diff --git a/ast-generator/src/pretty_print.rs b/ast-generator/src/pretty_print.rs index 5698857..e90106e 100644 --- a/ast-generator/src/pretty_print.rs +++ b/ast-generator/src/pretty_print.rs @@ -11,6 +11,10 @@ use convert_case::{Case, Casing}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; +fn make_result() -> TokenStream { + quote! { std::fmt::Result } +} + fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream { let type_ident = format_ident!("{}", spec.name()); let type_string = spec.name(); @@ -44,9 +48,11 @@ fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> To }) .collect::>(); + let result = make_result(); + quote! { impl PrettyPrint for #type_ident { - fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + fn pretty_print(&self, writer: &mut IndentWriter) -> #result { writer.writeln_indented(#type_string)?; writer.increase_indent(); #(#child_print_statements)* @@ -70,9 +76,11 @@ fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream }) .collect::>(); + let result = make_result(); + quote! { impl PrettyPrint for #type_ident { - fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + fn pretty_print(&self, writer: &mut IndentWriter) -> #result { match self { #(#child_matchers,)* } @@ -94,9 +102,11 @@ fn make_leaf_enum_p2_impl(spec: &LeafEnumBuildSpec) -> TokenStream { }) .collect::>(); + let result = make_result(); + quote! { impl PrettyPrint for #type_ident { - fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + fn pretty_print(&self, writer: &mut IndentWriter) -> #result { match self { #(#child_matchers,)* } @@ -137,9 +147,11 @@ fn make_tree_enum_p2_impl(spec: &TreeEnumBuildSpec) -> TokenStream { .map(Option::unwrap) .collect::>(); + let result = make_result(); + quote! { impl PrettyPrint for #type_ident { - fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + fn pretty_print(&self, writer: &mut IndentWriter) -> #result { writer.writeln_indented(#type_str)?; writer.increase_indent(); match self { @@ -184,9 +196,11 @@ fn make_leaf_struct_p2_impl(leaf_struct_build_spec: &LeafStructBuildSpec) -> Tok .map(Option::unwrap) .collect::>(); + let result = make_result(); + quote! { impl PrettyPrint for #type_ident { - fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + fn pretty_print(&self, writer: &mut IndentWriter) -> #result { writer.writeln_indented(&format!(#format_string, #(#members),*)) } } @@ -232,7 +246,7 @@ fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream { }) } }, - StructChild::Special(_) => None + StructChild::Special(_) => None, }) .filter(Option::is_some) .map(Option::unwrap) @@ -241,9 +255,11 @@ fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream { let type_ident = format_ident!("{}", struct_build_spec.build()); let type_string = struct_build_spec.build(); + let result = make_result(); + quote! { impl PrettyPrint for #type_ident { - fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + fn pretty_print(&self, writer: &mut IndentWriter) -> #result { writer.writeln_indented(#type_string)?; writer.increase_indent(); #(#child_print_statements)* diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 3ba4999..b13c4f0 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -166,7 +166,7 @@ pub mod pretty_print { use crate::util::indent_writer::IndentWriter; pub trait PrettyPrint { - fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()>; + fn pretty_print(&self, writer: &mut IndentWriter) -> std::fmt::Result; } include!(concat!(env!("OUT_DIR"), "/src/ast/pretty_print.rs")); diff --git a/src/bin/dmc/p3.rs b/src/bin/dmc/p3.rs index d478f8f..ada0aba 100644 --- a/src/bin/dmc/p3.rs +++ b/src/bin/dmc/p3.rs @@ -11,10 +11,12 @@ pub fn pretty_print_parse(path: &PathBuf) { match parse_result { Ok(mut pairs) => { let compilation_unit = build_ast(0, &mut pairs); - let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout())); + let mut acc = String::new(); + let mut indent_writer = IndentWriter::new(0, " ", &mut acc); compilation_unit .pretty_print(&mut indent_writer) .expect("Unable to pretty-print."); + println!("{}", acc); } Err(e) => { eprintln!("{}", e); diff --git a/src/name_analysis/first_pass.rs b/src/name_analysis/first_pass.rs index 0a36535..33f54da 100644 --- a/src/name_analysis/first_pass.rs +++ b/src/name_analysis/first_pass.rs @@ -33,6 +33,8 @@ pub fn na_p1_compilation_unit( .set_current_fqn(&fqn.identifiers().map(Identifier::name).collect::>()); } } + } else { + symbol_table.set_current_fqn(&[]); } symbol_table.push_scope(&format!("FileScope {}", file_name)); diff --git a/src/name_analysis/mod.rs b/src/name_analysis/mod.rs index fdb7d9b..0aff365 100644 --- a/src/name_analysis/mod.rs +++ b/src/name_analysis/mod.rs @@ -67,20 +67,22 @@ pub fn analyze_names< #[cfg(test)] mod tests { use super::*; + use crate::ast::ast_node::AstNodeRef; use crate::ast::build::build_ast; + use crate::ast::pretty_print::PrettyPrint; + use crate::ast::walk::walk_depth_first; use crate::name_analysis::symbol::use_symbol::StarUseSymbol; + use crate::name_analysis::symbol::{ExpressibleSymbol, PrimitiveTypeSymbol, TypeSymbol}; + use crate::name_analysis::util::str_slice_to_rcs; use crate::parser::{DeimosParser, Rule}; use crate::std_core::add_std_core_symbols; + use crate::util::indent_writer::IndentWriter; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use pest::Parser; use std::collections::HashMap; - use std::ops::Deref; use std::rc::Rc; - use crate::ast::ast_node::AstNodeRef; - use crate::ast::walk::walk_depth_first; - use crate::name_analysis::symbol::{LVSymbol, PrimitiveTypeSymbol, TypeSymbol}; fn parse_compilation_units<'a>( files: &mut SimpleFiles<&'a str, &'a str>, @@ -100,7 +102,7 @@ mod tests { let file_id = files.add(file_name, source); compilation_units.push(build_ast(file_id, &mut pairs)); } - + compilation_units } @@ -110,13 +112,9 @@ mod tests { number_of_diagnostics: usize, ) -> (Vec, SimpleFiles<&'a str, &'a str>) { let mut files = SimpleFiles::<&'a str, &'a str>::new(); - let mut to_analyze = parse_compilation_units(&mut files, sources); - - let diagnostics = analyze_names( - &mut to_analyze, - &files, - symbol_table - ); + let mut compilation_units = parse_compilation_units(&mut files, sources); + + let diagnostics = analyze_names(&mut compilation_units, &files, symbol_table); if diagnostics.len() != number_of_diagnostics { let writer = StandardStream::stderr(ColorChoice::Always); @@ -127,11 +125,20 @@ mod tests { } eprintln!("{}", symbol_table); + + for compilation_unit in &compilation_units { + let mut acc = String::new(); + let mut indent_writer = IndentWriter::new(0, " ", &mut acc); + compilation_unit + .pretty_print(&mut indent_writer) + .expect("Pretty print failed"); + eprintln!("{}", acc); + } } assert_eq!(number_of_diagnostics, diagnostics.len()); - (to_analyze, files) + (compilation_units, files) } fn assert_no_diagnostics<'a>( @@ -140,19 +147,19 @@ mod tests { ) -> (Vec, SimpleFiles<&'a str, &'a str>) { assert_number_of_diagnostics(sources, symbol_table, 0) } - + fn make_names_to_cus_map<'a>( compilation_units: &'a [CompilationUnit], files: &SimpleFiles<&'a str, &str>, ) -> HashMap<&'a str, &'a CompilationUnit> { let mut map = HashMap::new(); - + for compilation_unit in compilation_units { let file_id = compilation_unit.file_id(); let name = files.name(file_id).unwrap(); map.insert(name, compilation_unit); } - + map } @@ -171,11 +178,11 @@ mod tests { let (cus, files) = assert_no_diagnostics(sources, &mut symbol_table); let results_map = make_names_to_cus_map(&cus, &files); let main_cu = *results_map.get("main.dm").unwrap(); - + let mut found_main = false; let mut found_x = false; let mut found_args_ref = false; - + walk_depth_first(main_cu, &mut |node| { use AstNodeRef::*; match node { @@ -209,34 +216,35 @@ mod tests { _ => panic!("Expected a primitive type symbol for args.") } } - }, + } VariableDeclaration(variable_declaration) => { if variable_declaration.identifier().name() == "x" { found_x = true; assert_eq!( - variable_declaration.variable_symbol().unwrap().borrow().declared_name(), + variable_declaration + .variable_symbol() + .unwrap() + .borrow() + .declared_name(), "x" ); } } - VariableUse(variable_use) => { - if variable_use.identifier().name() == "args" { + IdentifierExpression(identifier_expression) => { + if identifier_expression.identifier().name() == "args" { found_args_ref = true; - match variable_use.lv_symbol().unwrap() { - LVSymbol::Parameter(parameter_symbol) => { - assert_eq!( - parameter_symbol.borrow().declared_name(), - "args" - ) + match identifier_expression.expressible_symbol().unwrap() { + ExpressibleSymbol::Parameter(parameter_symbol) => { + assert_eq!(parameter_symbol.borrow().declared_name(), "args") } - _ => panic!("Expected args variable use to be a parameter symbol.") + _ => panic!("Expected args variable use to be a parameter symbol."), } } } _ => {} } }); - + assert!(found_main); assert!(found_x); assert!(found_args_ref); @@ -299,17 +307,24 @@ mod tests { fn main(args: Array) println args end - " + ", )]); let mut symbol_table = SymbolTable::new(); - let global_std_core_use = StarUseSymbol::new( - &[Rc::from("std"), Rc::from("core")], - None - ); - symbol_table.insert_star_use_symbol(global_std_core_use) + let global_std_core_star_use_to_insert = + StarUseSymbol::new(&[Rc::from("std"), Rc::from("core")], None); + let global_std_core_star_use = symbol_table + .insert_star_use_symbol(global_std_core_star_use_to_insert) .expect("Failed to insert star use symbol."); add_std_core_symbols(&mut symbol_table).expect("Failed to add std/core symbols."); + + let resolved_std_core_symbols = symbol_table + .find_usable_symbols_by_base_fqn(&str_slice_to_rcs(&["std", "core"])) + .unwrap(); + global_std_core_star_use + .borrow_mut() + .set_resolved_symbols(resolved_std_core_symbols); + assert_no_diagnostics(sources, &mut symbol_table); } } diff --git a/src/name_analysis/second_pass.rs b/src/name_analysis/second_pass.rs index 6f3af9d..8e9f212 100644 --- a/src/name_analysis/second_pass.rs +++ b/src/name_analysis/second_pass.rs @@ -1,10 +1,11 @@ use crate::ast::node::{ - AssignmentStatement, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, - Expression, ExpressionStatement, Function, FunctionAliasBody, FunctionBlockBody, FunctionBody, - FunctionEqualsBody, GenericParameters, Identifier, IdentifierOrFqn, LValue, LValueSuffix, - ModuleLevelDeclaration, Parameter, Parameters, PrimitiveType, ReturnType, StarUseStatement, - Statement, TypeUse, TypedArray, UseStatement, UseStatementIdentifier, UseStatementPrefix, - VariableDeclaration, VariableUse, + AssignmentStatement, Call, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, + Expression, ExpressionList, ExpressionStatement, Function, FunctionAliasBody, + FunctionBlockBody, FunctionBody, FunctionEqualsBody, GenericParameters, Identifier, + IdentifierExpression, IdentifierOrFqn, LValue, LValueSuffix, ModuleLevelDeclaration, + ObjectIndex, Parameter, Parameters, PrimitiveType, ReturnType, StarUseStatement, Statement, + SuffixExpression, SuffixOperator, TypeUse, TypedArray, UseStatement, UseStatementIdentifier, + UseStatementPrefix, VariableDeclaration, }; use crate::diagnostic::DmDiagnostic; use crate::name_analysis::symbol::source_definition::SourceDefinition; @@ -516,7 +517,20 @@ fn na_p2_l_value( symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { - na_p2_variable_use(l_value.variable_use_mut(), symbol_table, diagnostics); + match symbol_table.lookup_lv_symbol(l_value.identifier().name()) { + Ok(lv_symbol) => { + l_value.set_lv_symbol(lv_symbol); + } + Err(symbol_lookup_error) => { + handle_lookup_error( + symbol_lookup_error, + l_value.identifier().name(), + l_value.identifier().file_id(), + l_value.identifier().range(), + diagnostics, + ); + } + } // "Name analysis" of suffixes is done during type-checking. We don't want to deal with getting // out the different potential return types of things until that point, especially if we end up // adding dynamic objects to the language. However, suffixes may contain things (like arguments) @@ -554,27 +568,6 @@ fn na_p2_expression_statement( ); } -fn na_p2_variable_use( - variable_use: &mut VariableUse, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) { - match symbol_table.lookup_lv_symbol(variable_use.identifier().name()) { - Ok(lv_symbol) => { - variable_use.set_lv_symbol(lv_symbol); - } - Err(symbol_lookup_error) => { - handle_lookup_error( - symbol_lookup_error, - variable_use.identifier().name(), - variable_use.identifier().file_id(), - variable_use.identifier().range(), - diagnostics, - ); - } - } -} - fn na_p2_expression( expression: &mut Expression, symbol_table: &mut SymbolTable, @@ -606,13 +599,13 @@ fn na_p2_expression( todo!() } Expression::Suffix(suffix) => { - todo!() + na_p2_suffix_expression(suffix, symbol_table, diagnostics); } Expression::Literal(literal) => { todo!() } - Expression::VariableUse(variable_use) => { - na_p2_variable_use(variable_use, symbol_table, diagnostics); + Expression::Identifier(identifier_expression) => { + na_p2_identifier_expression(identifier_expression, symbol_table, diagnostics); } Expression::Fqn(fqn) => { todo!() @@ -625,3 +618,111 @@ fn na_p2_expression( } } } + +fn na_p2_suffix_expression( + suffix_expression: &mut SuffixExpression, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + na_p2_expression( + suffix_expression.expression_mut(), + symbol_table, + diagnostics, + ); + na_p2_suffix_operator(suffix_expression.operator_mut(), symbol_table, diagnostics); +} + +fn na_p2_suffix_operator( + suffix_operator: &mut SuffixOperator, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + match suffix_operator { + SuffixOperator::PlusPlus => { + // no-op + } + SuffixOperator::MinusMinus => { + // no-op + } + SuffixOperator::ObjectProperty(_) => { + // no-op; props checked during type checking + } + SuffixOperator::ObjectIndex(object_index) => { + na_p2_object_index(object_index, symbol_table, diagnostics); + } + SuffixOperator::Call(call) => { + na_p2_call(call, symbol_table, diagnostics); + } + } +} + +fn na_p2_object_index( + object_index: &mut ObjectIndex, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + na_p2_expression(object_index.expression_mut(), symbol_table, diagnostics); +} + +fn na_p2_call( + call: &mut Call, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + // TODO: modify the ast-gen so that we don't have to match on these. They should be the same. + match call { + Call::ParenthesesCall(parentheses_call) => { + if let Some(turbo_fish) = parentheses_call.turbo_fish_mut() { + todo!() + } + if let Some(expression_list) = parentheses_call.expression_list_mut() { + na_p2_expression_list(expression_list, symbol_table, diagnostics); + } + if let Some(closure) = parentheses_call.closure_mut() { + todo!() + } + } + Call::NonParenthesesCall(non_parentheses_call) => { + if let Some(turbo_fish) = non_parentheses_call.turbo_fish_mut() { + todo!() + } + if let Some(expression_list) = non_parentheses_call.expression_list_mut() { + na_p2_expression_list(expression_list, symbol_table, diagnostics); + } + if let Some(closure) = non_parentheses_call.closure_mut() { + todo!() + } + } + } +} + +fn na_p2_identifier_expression( + identifier_expression: &mut IdentifierExpression, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + match symbol_table.lookup_expressible_symbol(identifier_expression.identifier().name()) { + Ok(expressible_symbol) => { + identifier_expression.set_expressible_symbol(expressible_symbol); + } + Err(symbol_lookup_error) => { + handle_lookup_error( + symbol_lookup_error, + identifier_expression.identifier().name(), + identifier_expression.identifier().file_id(), + identifier_expression.identifier().range(), + diagnostics, + ); + } + } +} + +fn na_p2_expression_list( + expression_list: &mut ExpressionList, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + for expression in expression_list.expressions_mut() { + na_p2_expression(expression, symbol_table, diagnostics); + } +} diff --git a/src/name_analysis/symbol/expressible_symbol.rs b/src/name_analysis/symbol/expressible_symbol.rs new file mode 100644 index 0000000..ba0a5b5 --- /dev/null +++ b/src/name_analysis/symbol/expressible_symbol.rs @@ -0,0 +1,33 @@ +use crate::name_analysis::symbol::{ClassMemberSymbol, ClassSymbol, FunctionSymbol, ParameterSymbol, Symbol, VariableSymbol}; +use std::cell::RefCell; +use std::rc::Rc; + +pub enum ExpressibleSymbol { + Class(Rc>), + Function(Rc>), + ClassMember(Rc>), + Parameter(Rc>), + Variable(Rc>), +} + +impl ExpressibleSymbol { + pub fn to_symbol(self) -> Rc> { + match self { + ExpressibleSymbol::Class(class_symbol) => { + class_symbol as Rc> + } + ExpressibleSymbol::Function(function_symbol) => { + function_symbol as Rc> + } + ExpressibleSymbol::ClassMember(class_member_symbol) => { + class_member_symbol as Rc> + } + ExpressibleSymbol::Parameter(parameter_symbol) => { + parameter_symbol as Rc> + } + ExpressibleSymbol::Variable(variable_symbol) => { + variable_symbol as Rc> + } + } + } +} diff --git a/src/name_analysis/symbol/mod.rs b/src/name_analysis/symbol/mod.rs index c9f88e9..803c3d3 100644 --- a/src/name_analysis/symbol/mod.rs +++ b/src/name_analysis/symbol/mod.rs @@ -1,5 +1,6 @@ pub mod class_member_symbol; pub mod class_symbol; +pub mod expressible_symbol; pub mod function_symbol; pub mod generic_type_symbol; pub mod interface_symbol; @@ -18,10 +19,10 @@ use crate::name_analysis::symbol::source_definition::SourceDefinition; use std::fmt::Debug; pub use self::{ - class_member_symbol::*, class_symbol::*, function_symbol::*, function_symbol::*, - generic_type_symbol::*, interface_symbol::*, lv_symbol::*, module_level_symbol::*, - module_symbol::*, parameter_symbol::*, primitive_type_symbol::*, type_symbol::*, - usable_symbol::*, use_symbol::*, variable_symbol::*, + class_member_symbol::*, class_symbol::*, expressible_symbol::*, function_symbol::*, + function_symbol::*, generic_type_symbol::*, interface_symbol::*, lv_symbol::*, + module_level_symbol::*, module_symbol::*, parameter_symbol::*, primitive_type_symbol::*, + type_symbol::*, usable_symbol::*, use_symbol::*, variable_symbol::*, }; pub trait Symbol: Debug { diff --git a/src/name_analysis/symbol/parameter_symbol.rs b/src/name_analysis/symbol/parameter_symbol.rs index 87a8df8..f7a25d7 100644 --- a/src/name_analysis/symbol/parameter_symbol.rs +++ b/src/name_analysis/symbol/parameter_symbol.rs @@ -51,6 +51,7 @@ impl Debug for ParameterSymbol { f.debug_struct("ParameterSymbol") .field("declared_name", &self.declared_name) .field("source_definition", &self.source_definition) + .field("type_symbol", &self.type_symbol) .finish() } } diff --git a/src/name_analysis/symbol/use_symbol.rs b/src/name_analysis/symbol/use_symbol.rs index 391da08..0a5da5d 100644 --- a/src/name_analysis/symbol/use_symbol.rs +++ b/src/name_analysis/symbol/use_symbol.rs @@ -109,9 +109,10 @@ impl Symbol for StarUseSymbol { impl Debug for StarUseSymbol { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("StarUseStatementSymbol") + f.debug_struct("StarUseSymbol") .field("fqn", &join_fqn_parts(&self.fqn_parts)) .field("source_definition", &self.source_definition) + .field("resolved_symbols", &self.resolved_symbols) .finish() } } diff --git a/src/name_analysis/symbol_table/mod.rs b/src/name_analysis/symbol_table/mod.rs index b0ce529..edb9b7b 100644 --- a/src/name_analysis/symbol_table/mod.rs +++ b/src/name_analysis/symbol_table/mod.rs @@ -5,7 +5,8 @@ use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol; use crate::name_analysis::symbol::type_symbol::TypeSymbol; use crate::name_analysis::symbol::usable_symbol::UsableSymbol; use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol}; -use crate::name_analysis::symbol::{LVSymbol, Symbol}; +use crate::name_analysis::symbol::variable_symbol::VariableSymbol; +use crate::name_analysis::symbol::{ExpressibleSymbol, LVSymbol, Symbol}; use crate::name_analysis::symbol_table::fqn_context::FqnContext; use crate::name_analysis::symbol_table::symbol_tree::SymbolTree; use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined; @@ -14,7 +15,6 @@ use std::cell::RefCell; use std::fmt::Display; use std::ops::Deref; use std::rc::Rc; -use crate::name_analysis::symbol::variable_symbol::VariableSymbol; pub(self) mod fqn_context; mod scope; @@ -25,6 +25,7 @@ pub enum SymbolInsertError { SymbolAlreadyDefined(Rc>), } +#[derive(Debug)] pub enum SymbolLookupError { NoDefinition, NoSuchNamespace, @@ -44,7 +45,7 @@ impl SymbolTable { Self { scopes: vec![Scope::new(None, 0, String::from("GlobalScope"))], current_scope_id: 0, - symbol_tree: Box::new(SymbolTree::new()), + symbol_tree: Box::new(SymbolTree::new("")), fqn_context: Box::new(FqnContext::new()), } } @@ -68,7 +69,7 @@ impl SymbolTable { self.current_scope_id = parent_id; } } - + pub fn set_current_scope(&mut self, id: usize) { self.current_scope_id = id; } @@ -122,6 +123,15 @@ impl SymbolTable { self.symbol_tree.register_module_by_fqn_parts(fqn_parts); } + pub fn register_function_symbol( + &mut self, + function_symbol: FunctionSymbol, + ) -> Rc> { + let as_rc = Rc::new(RefCell::new(function_symbol)); + self.symbol_tree.register_function(as_rc.clone()); + as_rc + } + pub fn find_usable_symbols_by_base_fqn( &self, fqn_parts: &[Rc], @@ -231,7 +241,7 @@ impl SymbolTable { Ok(inserted) } } - + pub fn insert_variable_symbol( &mut self, variable_symbol: VariableSymbol, @@ -266,8 +276,11 @@ impl SymbolTable { pub fn lookup_type_by_fqn(&self, fqn_parts: &[&str]) -> Result { todo!() } - - pub fn lookup_function_symbol(&self, declared_name: &str) -> Result>, SymbolLookupError> { + + pub fn lookup_function_symbol( + &self, + declared_name: &str, + ) -> Result>, SymbolLookupError> { let mut current_scope: Option<&Scope> = Some(self.current_scope()); while let Some(scope) = current_scope.take() { if let Some(function_symbol) = scope.find_function_symbol(declared_name) { @@ -280,12 +293,29 @@ impl SymbolTable { } Err(SymbolLookupError::NoDefinition) } - + pub fn lookup_lv_symbol(&self, declared_name: &str) -> Result { let mut current_scope: Option<&Scope> = Some(self.current_scope()); while let Some(scope) = current_scope.take() { if let Some(lv_symbol) = scope.find_lv_symbol(declared_name) { - return Ok(lv_symbol) + return Ok(lv_symbol); + } else { + current_scope = scope + .parent() + .and_then(|parent_id| self.scopes.get(parent_id)); + } + } + Err(SymbolLookupError::NoDefinition) + } + + pub fn lookup_expressible_symbol( + &self, + declared_name: &str, + ) -> Result { + let mut current_scope: Option<&Scope> = Some(self.current_scope()); + while let Some(scope) = current_scope.take() { + if let Some(expressible_symbol) = scope.find_expressible_symbol(declared_name) { + return Ok(expressible_symbol); } else { current_scope = scope .parent() @@ -302,6 +332,8 @@ impl Display for SymbolTable { for scope in &self.scopes { writeln!(f, "{}", scope)?; } + writeln!(f, "---SymbolTable: SymbolTree---")?; + writeln!(f, "{}", self.symbol_tree)?; Ok(()) } } diff --git a/src/name_analysis/symbol_table/scope.rs b/src/name_analysis/symbol_table/scope.rs index 4ed39e7..e5da803 100644 --- a/src/name_analysis/symbol_table/scope.rs +++ b/src/name_analysis/symbol_table/scope.rs @@ -10,6 +10,7 @@ use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol; use crate::name_analysis::symbol::type_symbol::TypeSymbol; use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol}; use crate::name_analysis::symbol::variable_symbol::VariableSymbol; +use crate::name_analysis::symbol::{ExpressibleSymbol, UsableSymbol}; use std::cell::RefCell; use std::collections::HashMap; use std::fmt::{Display, Formatter}; @@ -142,7 +143,7 @@ impl Scope { let key = symbol.declared_name_owned(); insert_symbol!(self.parameter_symbols, symbol, key) } - + pub fn insert_variable_symbol( &mut self, symbol: VariableSymbol, @@ -203,11 +204,84 @@ impl Scope { .map(|variable_symbol| LVSymbol::Variable(variable_symbol.clone())) }) } - - pub fn find_function_symbol(&self, declared_name: &str) -> Option<&Rc>> { + + pub fn find_function_symbol( + &self, + declared_name: &str, + ) -> Option<&Rc>> { self.function_symbols.get(declared_name) } + pub fn find_expressible_symbol(&self, declared_name: &str) -> Option { + // First, try all the usual suspects: classes, functions, members, parameters, and variables + // which are in scope. If we don't find any of those, try looking through imports; start + // with concrete imports by declared name; if none of those match, check all resolved + // symbols of the star imports until one is found or none. + // !! Eventually consider registering star-imported symbols in a scope so that we can just + // check them via the hash maps instead of looping through the star symbols. + self.class_symbols + .get(declared_name) + .map(|class_symbol| ExpressibleSymbol::Class(class_symbol.clone())) + .or_else(|| { + self.function_symbols + .get(declared_name) + .map(|function_symbol| ExpressibleSymbol::Function(function_symbol.clone())) + }) + .or_else(|| { + self.class_member_symbols + .get(declared_name) + .map(|class_member_symbol| { + ExpressibleSymbol::ClassMember(class_member_symbol.clone()) + }) + }) + .or_else(|| { + self.parameter_symbols + .get(declared_name) + .map(|parameter_symbol| ExpressibleSymbol::Parameter(parameter_symbol.clone())) + }) + .or_else(|| { + self.variable_symbols + .get(declared_name) + .map(|variable_symbol| ExpressibleSymbol::Variable(variable_symbol.clone())) + }) + .or_else(|| { + if let Some(concrete_use_symbol) = self.concrete_use_symbols.get(declared_name) { + return match concrete_use_symbol.borrow().resolved_symbol().unwrap() { + UsableSymbol::Interface(_) => { + None + } + UsableSymbol::Class(class_symbol) => { + Some(ExpressibleSymbol::Class(class_symbol.clone())) + } + UsableSymbol::Function(function_symbol) => { + Some(ExpressibleSymbol::Function(function_symbol.clone())) + } + } + } + None + }) + .or_else(|| { + for star_use_symbol in self.star_use_symbols.values() { + for usable_symbol in star_use_symbol.borrow().resolved_symbols() { + match usable_symbol { + UsableSymbol::Class(class_symbol) => { + if class_symbol.borrow().declared_name() == declared_name { + return Some(ExpressibleSymbol::Class(class_symbol.clone())); + } + } + UsableSymbol::Function(function_symbol) => { + if function_symbol.borrow().declared_name() == declared_name { + return Some(ExpressibleSymbol::Function(function_symbol.clone())); + } + } + _ => continue, + } + } + } + None + }) + } + pub fn debug_name(&self) -> &str { &self.debug_name } diff --git a/src/name_analysis/symbol_table/symbol_tree.rs b/src/name_analysis/symbol_table/symbol_tree.rs index f0dbadc..659fde9 100644 --- a/src/name_analysis/symbol_table/symbol_tree.rs +++ b/src/name_analysis/symbol_table/symbol_tree.rs @@ -4,12 +4,15 @@ use crate::name_analysis::symbol::interface_symbol::InterfaceSymbol; use crate::name_analysis::symbol::module_symbol::ModuleSymbol; use crate::name_analysis::symbol::usable_symbol::UsableSymbol; use crate::name_analysis::symbol_table::SymbolLookupError; +use crate::util::indent_writer::IndentWriter; use std::cell::RefCell; use std::collections::HashMap; +use std::fmt::{Display, Formatter}; use std::rc::Rc; #[derive(Debug)] pub struct SymbolTree { + debug_name: String, children: Box, SymbolTree>>, classes: Box, Rc>>>, interfaces: Box, Rc>>>, @@ -17,8 +20,9 @@ pub struct SymbolTree { } impl SymbolTree { - pub fn new() -> Self { + pub fn new(debug_name: &str) -> Self { Self { + debug_name: debug_name.to_string(), children: Box::new(HashMap::new()), classes: Box::new(HashMap::new()), interfaces: Box::new(HashMap::new()), @@ -78,13 +82,13 @@ impl SymbolTree { } if fqn_parts.len() == 1 { self.children - .insert(fqn_parts[0].clone(), SymbolTree::new()); + .insert(fqn_parts[0].clone(), SymbolTree::new(fqn_parts[0].as_ref())); } else { if self.children.contains_key(fqn_parts[0].as_ref()) { let child = self.children.get_mut(fqn_parts[0].as_ref()).unwrap(); child.recurse_register_module(&fqn_parts[1..]); } else { - let mut child = SymbolTree::new(); + let mut child = SymbolTree::new(fqn_parts[0].as_ref()); child.recurse_register_module(&fqn_parts[1..]); self.children.insert(fqn_parts[0].clone(), child); } @@ -105,6 +109,12 @@ impl SymbolTree { panic!("Unable to register function fqn with no parts."); } if fqn_parts.len() == 1 { + if self.functions.contains_key(fqn_parts[0].as_ref()) { + panic!( + "There is already a function registered in this symbol tree with the name {}", + fqn_parts[0] + ); + } self.functions.insert(fqn_parts[0].clone(), function_symbol); } else { if self.children.contains_key(fqn_parts[0].as_ref()) { @@ -137,7 +147,7 @@ impl SymbolTree { _ => { if self.children.contains_key(fqn_parts[0].as_ref()) { let child = self.children.get(fqn_parts[0].as_ref()).unwrap(); - child.find_all_by_base_fqn(fqn_parts) + child.find_all_by_base_fqn(&fqn_parts[1..]) } else { Err(SymbolLookupError::NoSuchNamespace) } @@ -145,3 +155,55 @@ impl SymbolTree { } } } + +enum FormatAction<'a> { + PrintSymbolTree(&'a SymbolTree), + IncreaseIndent, + DecreaseIndent +} + +impl Display for SymbolTree { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut acc = String::new(); + let mut indent_writer = IndentWriter::new(0, " ", &mut acc); + let mut stack: Vec = vec![]; + stack.push(FormatAction::PrintSymbolTree(self)); + + while let Some(format_action) = stack.pop() { + match format_action { + FormatAction::PrintSymbolTree(symbol_tree) => { + // reverse order: start with decrease + stack.push(FormatAction::DecreaseIndent); + // add children + for child in symbol_tree.children.values() { + stack.push(FormatAction::PrintSymbolTree(child)); + } + // increase for the first child + stack.push(FormatAction::IncreaseIndent); + + indent_writer.writeln_indented(&format!("SymbolTree(debug_name = {})", symbol_tree.debug_name))?; + + indent_writer.increase_indent(); + for interface in symbol_tree.interfaces.values() { + indent_writer.writeln_indented(&format!("Interface({:?}", interface))?; + } + for class in symbol_tree.classes.values() { + indent_writer.writeln_indented(&format!("Class({:?})", class))?; + } + for function in symbol_tree.functions.values() { + indent_writer.writeln_indented(&format!("Function({:?})", function))?; + } + indent_writer.decrease_indent(); + } + FormatAction::IncreaseIndent => { + indent_writer.increase_indent(); + } + FormatAction::DecreaseIndent => { + indent_writer.decrease_indent(); + } + } + } + + writeln!(f, "{}", acc) + } +} diff --git a/src/name_analysis/util.rs b/src/name_analysis/util.rs index 0347fdf..8183400 100644 --- a/src/name_analysis/util.rs +++ b/src/name_analysis/util.rs @@ -50,12 +50,12 @@ pub fn handle_lookup_error( Label::primary(error_file_id, error_range).with_message("Symbol used here."), ); diagnostics.push(diagnostic); - }, + } SymbolLookupError::NoSuchNamespace => { let diagnostic = Diagnostic::error() .with_message(format!("No such namespace '{}' found", error_symbol_name)) .with_label( - Label::primary(error_file_id, error_range).with_message("Namespace used here.") + Label::primary(error_file_id, error_range).with_message("Namespace used here."), ); diagnostics.push(diagnostic); } @@ -69,3 +69,7 @@ pub fn format_fqn(parts: &[&str]) -> String { pub fn join_fqn_parts(parts: &[Rc]) -> String { format_fqn(&parts.iter().map(|part| part.as_ref()).collect::>()) } + +pub fn str_slice_to_rcs(str_slice: &[&str]) -> Vec> { + str_slice.iter().map(|s| Rc::from(*s)).collect::>() +} diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index 45f3422..0265072 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -785,25 +785,19 @@ ForStatement: LValue: struct: children: - - variable_use + - identifier - suffixes: vec: rule: LValueSuffix + fields: + - lv_symbol: + kind: LVSymbol LValueSuffix: tree_enum: rules: - ObjectProperty - ObjectIndex -# VariableUse -VariableUse: - struct: - children: - - identifier - fields: - - lv_symbol: - kind: LVSymbol - # Expressions Expression: polymorphic_type: @@ -838,9 +832,9 @@ Expression: - Literal: inner: kind: Literal - - VariableUse: + - Identifier: inner: - kind: VariableUse + kind: IdentifierExpression - Fqn: inner: kind: FullyQualifiedName @@ -1183,9 +1177,9 @@ PrimaryExpression: - Literal: inner: kind: Literal - - VariableUse: + - Identifier: inner: - kind: VariableUse + kind: IdentifierExpression - Fqn: inner: kind: FullyQualifiedName @@ -1198,6 +1192,13 @@ PrimaryExpression: - ParenthesizedExpression: pass_through: kind: Expression +IdentifierExpression: + struct: + children: + - identifier + fields: + - expressible_symbol: + kind: ExpressibleSymbol ListExpression: struct: children: diff --git a/src/parser/deimos.pest b/src/parser/deimos.pest index 6669d9d..edfd793 100644 --- a/src/parser/deimos.pest +++ b/src/parser/deimos.pest @@ -615,7 +615,7 @@ ForStatement = { // LValue LValue = { - VariableUse + Identifier ~ LValueSuffix* } @@ -624,12 +624,6 @@ LValueSuffix = { | ObjectIndex } -// Variable Use - -VariableUse = { - Identifier -} - // Expressions Expression = { @@ -778,13 +772,17 @@ ObjectIndex = { PrimaryExpression = { Literal - | VariableUse + | IdentifierExpression | FullyQualifiedName | Closure | ListExpression | ParenthesizedExpression } +IdentifierExpression = { + Identifier +} + ListExpression = { "[" ~ ExpressionList? diff --git a/src/std_core/mod.rs b/src/std_core/mod.rs index 13d8612..9f6cedf 100644 --- a/src/std_core/mod.rs +++ b/src/std_core/mod.rs @@ -26,7 +26,7 @@ pub fn add_std_core_symbols(symbol_table: &mut SymbolTable) -> Result<(), Symbol &vec![Rc::new(RefCell::new(println_msg_symbol))], None, ); - symbol_table.insert_function_symbol(println_symbol)?; + symbol_table.register_function_symbol(println_symbol); Ok(()) } diff --git a/src/util/indent_writer.rs b/src/util/indent_writer.rs index 544fbe3..2f777d9 100644 --- a/src/util/indent_writer.rs +++ b/src/util/indent_writer.rs @@ -1,15 +1,11 @@ -pub struct IndentWriter { +pub struct IndentWriter<'a> { indent_level: usize, indent_string: String, - out: Box, + out: &'a mut dyn std::fmt::Write, } -impl IndentWriter { - pub fn new( - start_level: usize, - indent_string: &str, - out: Box, - ) -> IndentWriter { +impl<'a> IndentWriter<'a> { + pub fn new(start_level: usize, indent_string: &str, out: &'a mut dyn std::fmt::Write) -> Self { IndentWriter { indent_level: start_level, indent_string: indent_string.to_string(), @@ -25,22 +21,22 @@ impl IndentWriter { self.indent_level -= 1; } - pub fn write(&mut self, s: &str) -> std::io::Result<()> { + pub fn write(&mut self, s: &str) -> std::fmt::Result { write!(self.out, "{}", s) } - pub fn writeln(&mut self, s: &str) -> std::io::Result<()> { + pub fn writeln(&mut self, s: &str) -> std::fmt::Result { self.write(&format!("{}\n", s)) } - pub fn write_indented(&mut self, s: &str) -> std::io::Result<()> { + pub fn write_indented(&mut self, s: &str) -> std::fmt::Result { for _ in 0..self.indent_level { write!(self.out, "{}", self.indent_string)?; } write!(self.out, "{}", s) } - pub fn writeln_indented(&mut self, s: &str) -> std::io::Result<()> { + pub fn writeln_indented(&mut self, s: &str) -> std::fmt::Result { self.write_indented(&format!("{}\n", s)) } }