diff --git a/multiply_add.sus b/multiply_add.sus index 9ada263..eb8086e 100644 --- a/multiply_add.sus +++ b/multiply_add.sus @@ -156,3 +156,7 @@ module test_exists : -> int result { int x = exists(5); int b = doesnt_exist(3); } + +module exists : duplicate a { + // Should be a duplicate of previous exists +} diff --git a/src/ast.rs b/src/ast.rs index f142472..661114d 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,9 +1,9 @@ use num_bigint::BigUint; -use crate::{tokenizer::TokenTypeIdx, linker::GlobalValueUUID, linker::GlobalTypeUUID}; +use crate::{tokenizer::TokenTypeIdx, linker::ValueUUID, linker::TypeUUID}; use core::ops::Range; -use std::rc::Rc; +use std::{rc::Rc, path::Path}; // Token span. Indices are INCLUSIVE #[derive(Clone,Copy,Debug,PartialEq,Eq)] @@ -105,7 +105,7 @@ pub enum Statement { TimelineStage(usize) } -pub type FileName = Rc; +pub type FileName = Rc; #[derive(Debug)] pub struct Location { @@ -118,8 +118,8 @@ pub struct Dependencies { pub global_references : Vec, pub type_references : Vec, - pub resolved_globals : Vec, - pub resolved_types : Vec + pub resolved_globals : Vec, + pub resolved_types : Vec } #[derive(Debug)] diff --git a/src/dev_aid/lsp.rs b/src/dev_aid/lsp.rs index 918d42d..6518aa6 100644 --- a/src/dev_aid/lsp.rs +++ b/src/dev_aid/lsp.rs @@ -276,7 +276,7 @@ fn convert_diagnostic(err : ParsingError, severity : DiagnosticSeverity, token_p let mut related_info = Vec::new(); for info in err.infos { let info_pos = cvt_span_to_lsp_range(info.position, token_positions); - let location = Location{uri : Url::parse(&info.file_name).unwrap(), range : info_pos}; + let location = Location{uri : Url::from_file_path(info.file_name).unwrap(), range : info_pos}; related_info.push(DiagnosticRelatedInformation { location, message: info.info }); } Diagnostic::new(error_pos, Some(severity), None, None, err.reason, Some(related_info), None) @@ -339,8 +339,7 @@ fn main_loop( let path : PathBuf = params.text_document.uri.to_file_path().unwrap(); let file_data : Rc = file_cache.get(&path); - let file_uri_text = params.text_document.uri.to_string(); - let (full_parse, errors) = perform_full_semantic_parse(&file_data.file_text, Rc::from(file_uri_text)); + let (full_parse, errors) = perform_full_semantic_parse(&file_data.file_text, Rc::from(path)); let (syntax_highlight, token_positions) = do_syntax_highlight(&file_data, &full_parse); diff --git a/src/dev_aid/syntax_highlighting.rs b/src/dev_aid/syntax_highlighting.rs index fb78b0d..3dcebd6 100644 --- a/src/dev_aid/syntax_highlighting.rs +++ b/src/dev_aid/syntax_highlighting.rs @@ -1,8 +1,9 @@ -use std::{ops::Range, rc::Rc}; +use std::{ops::Range, rc::Rc, collections::HashMap}; use crate::{ast::*, tokenizer::*, parser::*, linker::Linker}; +use ariadne::FileCache; use console::Style; @@ -178,13 +179,16 @@ fn generate_character_offsets(file_text : &str, tokens : &[Token]) -> Vec file_text, - Err(reason) => panic!("Could not open file '{file_path}' for syntax highlighting because {reason}") + Err(reason) => { + let file_path_disp = file_path.display(); + panic!("Could not open file '{file_path_disp}' for syntax highlighting because {reason}") + } }; let (full_parse, errors) = perform_full_semantic_parse(&file_text, Rc::from(file_path.to_owned())); @@ -198,16 +202,19 @@ pub fn syntax_highlight_file(file_paths : &[String]) { println!("{:?}", full_parse.ast); - file_list.push(linker.add_file(file_text, full_parse.ast, errors, (full_parse.tokens, ide_tokens))); + let file_data = linker.add_file(file_text, full_parse.ast, errors, (full_parse.tokens, ide_tokens)); + file_list.insert(file_path.clone(), file_data); } let linked = linker.link_all(file_list); - for f in &linked.files { + let mut file_cache : FileCache = Default::default(); + + for (_file_name, f) in &linked.files { let token_offsets = generate_character_offsets(&f.file_text, &f.extra_data.0); for err in &f.errors.errors { - err.pretty_print_error(&f.errors.main_file, &f.file_text, &token_offsets); + err.pretty_print_error(&f.errors.main_file, &token_offsets, &mut file_cache); } } } diff --git a/src/errors.rs b/src/errors.rs index 19b1086..d4f0095 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,6 +1,6 @@ -use std::ops::Range; +use std::{ops::Range, path::Path}; use crate::ast::{Span, FileName}; use ariadne::*; @@ -19,27 +19,40 @@ pub struct ParsingError { pub infos : Vec } +struct CustomSpan<'a> { + file : &'a Path, + span : Range +} +impl<'a> ariadne::Span for CustomSpan<'a> { + type SourceId = Path; + + fn source(&self) -> &Self::SourceId { &self.file } + fn start(&self) -> usize { self.span.start } + fn end(&self) -> usize { self.span.end } +} + impl ParsingError { // Requires that character_ranges.len() == tokens.len() + 1 to include EOF token - pub fn pretty_print_error(&self, main_file_name : &str, file_text : &str, character_ranges : &[Range]) { + pub fn pretty_print_error(&self, main_file : &Path, character_ranges : &[Range], file_cache : &mut FileCache) { // Generate & choose some colours for each of our elements let err_color = Color::Red; let info_color = Color::Blue; let error_span = self.position.to_range(character_ranges); - let mut report = Report::build(ReportKind::Error, main_file_name, error_span.start) + + let mut report: ReportBuilder<'_, CustomSpan> = Report::build(ReportKind::Error, main_file, error_span.start); + report = report .with_message(&self.reason) .with_label( - Label::new((main_file_name, error_span)) + Label::new(CustomSpan{file : main_file, span : error_span}) .with_message(&self.reason) .with_color(err_color) ); for info in &self.infos { let info_span = info.position.to_range(character_ranges); - let info_file : &str = &info.file_name; report = report.with_label( - Label::new((info_file, info_span)) + Label::new(CustomSpan{file : &info.file_name, span : info_span}) .with_message(&info.info) .with_color(info_color) ) @@ -49,7 +62,7 @@ impl ParsingError { "match".fg(out) ))*/ report.finish() - .print((main_file_name, Source::from(file_text))) + .eprint(file_cache) .unwrap(); } } diff --git a/src/linker.rs b/src/linker.rs index 57d5d5d..7c07f6e 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; -use crate::{ast::{Module, ASTRoot, Span, Location, Dependencies}, errors::{ErrorCollector, error_info}}; +use crate::{ast::{Module, ASTRoot, Span, Location, Dependencies, FileName}, errors::{ErrorCollector, error_info}}; -#[derive(Debug, Clone, Copy)] -pub struct GlobalValueUUID(usize); -#[derive(Debug, Clone, Copy)] -pub struct GlobalTypeUUID(usize); +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ValueUUID(usize); +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TypeUUID(usize); const INVALID_UUID : usize = usize::MAX; @@ -91,16 +91,16 @@ impl Linkable for NamedType { #[derive(Debug, Clone, Copy)] pub enum GlobalNamespaceNode { - Value(GlobalValueUUID), - Type(GlobalTypeUUID), + Value(ValueUUID), + Type(TypeUUID), } pub struct FileData { pub file_text : String, pub extra_data : T, pub errors : ErrorCollector, - associated_values : Vec, - associated_types : Vec + associated_values : Vec, + associated_types : Vec } // All modules in the workspace @@ -113,17 +113,17 @@ pub struct Linker { // All modules in the workspace pub struct FullyLinked { pub data : Linker, - pub files : Vec> + pub files : HashMap> } impl Linker { // Returns None for builtins fn get_location(&self, node : GlobalNamespaceNode) -> Option<&Location> { match node { - GlobalNamespaceNode::Value(GlobalValueUUID(pos)) => { + GlobalNamespaceNode::Value(ValueUUID(pos)) => { self.values[pos].get_location() }, - GlobalNamespaceNode::Type(GlobalTypeUUID(pos)) => { + GlobalNamespaceNode::Type(TypeUUID(pos)) => { self.types[pos].get_location() }, } @@ -136,7 +136,7 @@ impl Linker { let mut global_namespace = HashMap::new(); for name in BUILTIN_TYPES { - let success = global_namespace.insert(name.to_owned(), GlobalNamespaceNode::Type(GlobalTypeUUID(named_types.len()))).is_none(); + let success = global_namespace.insert(name.to_owned(), GlobalNamespaceNode::Type(TypeUUID(named_types.len()))).is_none(); assert!(success); named_types.push(NamedType::Builtin(name)); } @@ -159,8 +159,8 @@ impl Linker { } } std::collections::hash_map::Entry::Vacant(vac) => { - vac.insert(GlobalNamespaceNode::Value(GlobalValueUUID(self.values.len()))); - associated_values.push(GlobalValueUUID(self.values.len())); + vac.insert(GlobalNamespaceNode::Value(ValueUUID(self.values.len()))); + associated_values.push(ValueUUID(self.values.len())); self.values.push(NamedValue::Module(md)); } } @@ -168,15 +168,15 @@ impl Linker { FileData{file_text, errors, associated_values, associated_types: Vec::new(), extra_data} } - fn link_dependencies(&self, file_text : &str, deps : &Dependencies, errors : &mut ErrorCollector) -> (Vec, Vec) { - let value_references : Vec = deps.global_references.iter().map(|reference| { + fn link_dependencies(&self, file_text : &str, deps : &Dependencies, errors : &mut ErrorCollector) -> (Vec, Vec) { + let value_references : Vec = deps.global_references.iter().map(|reference| { let reference_span = Span(reference.last().unwrap().position, reference[0].position); let reference_name_str = &file_text[reference[0].text.clone()]; match self.global_namespace.get(reference_name_str) { Some(GlobalNamespaceNode::Value(v)) => { *v } - Some(GlobalNamespaceNode::Type(GlobalTypeUUID(t))) => { + Some(GlobalNamespaceNode::Type(TypeUUID(t))) => { let found_instead = &self.types[*t]; let found_full_name = found_instead.get_full_name(); let infos = if let Some(loc) = found_instead.get_location() { @@ -185,23 +185,23 @@ impl Linker { vec![] }; errors.error_with_info(reference_span, format!("No Module or Constant of the name '{reference_name_str}' was found. Found type '{found_full_name}'"), infos); - GlobalValueUUID(INVALID_UUID) + ValueUUID(INVALID_UUID) } None => { errors.error_basic(reference_span, format!("No Module or Constant of the name '{reference_name_str}' was found. Did you forget to import it?")); - GlobalValueUUID(INVALID_UUID) + ValueUUID(INVALID_UUID) } } }).collect(); - let type_references : Vec = deps.type_references.iter().map(|reference| { + let type_references : Vec = deps.type_references.iter().map(|reference| { let reference_span = Span(reference.last().unwrap().position, reference[0].position); let reference_name_str = &file_text[reference[0].text.clone()]; match self.global_namespace.get(reference_name_str) { Some(GlobalNamespaceNode::Type(v)) => { *v } - Some(GlobalNamespaceNode::Value(GlobalValueUUID(idx))) => { + Some(GlobalNamespaceNode::Value(ValueUUID(idx))) => { let found_instead = &self.values[*idx]; let found_full_name = found_instead.get_full_name(); let infos = if let Some(loc) = found_instead.get_location() { @@ -210,11 +210,11 @@ impl Linker { vec![] }; errors.error_with_info(reference_span, format!("No Type of the name '{reference_name_str}' was found. Found type '{found_full_name}'"), infos); - GlobalTypeUUID(INVALID_UUID) + TypeUUID(INVALID_UUID) } None => { errors.error_basic(reference_span, format!("No Type of the name '{reference_name_str}' was found. Did you forget to import it?")); - GlobalTypeUUID(INVALID_UUID) + TypeUUID(INVALID_UUID) } } }).collect(); @@ -223,16 +223,16 @@ impl Linker { } // This should be called once all modules have been added. Adds errors for globals it couldn't match - pub fn link_all(mut self, mut files : Vec>) -> FullyLinked { - for file in &mut files { - for GlobalValueUUID(idx) in &file.associated_values { + pub fn link_all(mut self, mut files : HashMap>) -> FullyLinked { + for (_file_name, file) in &mut files { + for ValueUUID(idx) in &file.associated_values { let deps = self.values[*idx].get_dependencies(); let (vals_this_refers_to, types_this_refers_to) = self.link_dependencies(&file.file_text, deps, &mut file.errors); let deps_mut = self.values[*idx].get_dependencies_mut(); deps_mut.resolved_globals = vals_this_refers_to; deps_mut.resolved_types = types_this_refers_to; } - for GlobalTypeUUID(idx) in &file.associated_types { + for TypeUUID(idx) in &file.associated_types { let deps = self.types[*idx].get_dependencies(); let (vals_this_refers_to, types_this_refers_to) = self.link_dependencies(&file.file_text, deps, &mut file.errors); let deps_mut = self.types[*idx].get_dependencies_mut(); diff --git a/src/main.rs b/src/main.rs index 91caea5..2ca91ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,9 @@ mod linker; use std::env; use std::error::Error; +use std::path::PathBuf; +use std::rc::Rc; +use ast::FileName; use dev_aid::syntax_highlighting::*; @@ -18,13 +21,13 @@ fn main() -> Result<(), Box> { let _executable_path = args.next(); - let mut file_paths : Vec = Vec::new(); + let mut file_paths : Vec = Vec::new(); let mut is_lsp : bool = false; for arg in args { if arg == "--lsp" { is_lsp = true; } else { - file_paths.push(arg); + file_paths.push(Rc::from(PathBuf::from(arg))); } } #[cfg(feature = "lsp")] @@ -33,7 +36,7 @@ fn main() -> Result<(), Box> { } if file_paths.len() == 0 { // Quick debug file - file_paths.push("multiply_add.sus".to_owned()); + file_paths.push(Rc::from(PathBuf::from("multiply_add.sus"))); } syntax_highlight_file(&file_paths); diff --git a/src/parser.rs b/src/parser.rs index 5f53e0f..7d7c300 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -672,7 +672,7 @@ impl<'g, 'file> ASTParserContext<'g, 'file> { type_references : replace(&mut self.type_references, Vec::new()), ..Default::default() }; - let full_name : String = [self.errors.main_file.as_ref(), &self.file_text[name.text.clone()]].join("::"); + let full_name : String = [&self.errors.main_file.as_ref().to_string_lossy(), &self.file_text[name.text.clone()]].join("::"); Some(Module{name, declarations, code, full_name, location : Location{span, file_name : self.errors.main_file.clone()}, dependencies}) }