diff --git a/src/name_analysis/first_pass.rs b/src/name_analysis/first_pass.rs index 33f54da..f8f775e 100644 --- a/src/name_analysis/first_pass.rs +++ b/src/name_analysis/first_pass.rs @@ -1,8 +1,7 @@ use crate::ast::node::{ - CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody - , Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration - , StarUseStatement, UseStatement, - UseStatementIdentifier, UseStatementPrefix, + CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, Identifier, + IdentifierOrFqn, Module, ModuleLevelDeclaration, PlatformFunction, StarUseStatement, + UseStatement, UseStatementIdentifier, UseStatementPrefix, }; use crate::diagnostic::DmDiagnostic; use crate::name_analysis::symbol::function_symbol::FunctionSymbol; @@ -11,10 +10,9 @@ use crate::name_analysis::symbol::module_symbol::ModuleSymbol; use crate::name_analysis::symbol::source_definition::SourceDefinition; use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol}; use crate::name_analysis::symbol_table::SymbolTable; -use crate::name_analysis::util::{ - handle_insert_error, join_fqn_parts, -}; +use crate::name_analysis::util::{handle_insert_error, join_fqn_parts}; use std::cell::RefCell; +use std::process::id; use std::rc::Rc; pub fn na_p1_compilation_unit( @@ -26,13 +24,26 @@ pub fn na_p1_compilation_unit( if let Some(namespace) = compilation_unit.namespace() { match namespace.identifier_or_fqn() { IdentifierOrFqn::Identifier(identifier) => { - symbol_table.set_current_fqn(&[identifier.name()]) + symbol_table.set_current_fqn(&[identifier.name()]); } IdentifierOrFqn::FullyQualifiedName(fqn) => { symbol_table .set_current_fqn(&fqn.identifiers().map(Identifier::name).collect::>()); } } + let fqn_parts = match namespace.identifier_or_fqn() { + IdentifierOrFqn::Identifier(identifier) => { + vec![identifier.name()] + } + IdentifierOrFqn::FullyQualifiedName(fqn) => { + let mut parts = vec![]; + for identifier in fqn.identifiers() { + parts.push(identifier.name()); + } + parts + } + }; + symbol_table.register_module(&fqn_parts); } else { symbol_table.set_current_fqn(&[]); } @@ -187,7 +198,9 @@ fn na_p1_module_level_declaration( .map(|function_symbol| ModuleLevelSymbol::Function(function_symbol)) } ModuleLevelDeclaration::PlatformFunction(platform_function) => { - todo!() + na_p1_platform_function(platform_function, symbol_table, diagnostics).map( + |platform_function_symbol| ModuleLevelSymbol::Function(platform_function_symbol), + ) } } } @@ -264,7 +277,7 @@ fn na_p1_function( if maybe_function_symbol.is_some() { function.set_function_symbol(maybe_function_symbol.as_ref().unwrap().clone()); } - + // create a scope for this function symbol_table.push_scope(&format!("FunctionScope {}", function.identifier().name())); function.set_scope_id(symbol_table.current_scope_id()); @@ -272,3 +285,45 @@ fn na_p1_function( maybe_function_symbol } + +fn na_p1_platform_function( + platform_function: &mut PlatformFunction, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) -> Option>> { + let to_insert = FunctionSymbol::new( + &symbol_table.resolve_fqn(Rc::from(platform_function.identifier().name())), + platform_function.is_public(), + true, + Some(SourceDefinition::from_identifier( + platform_function.identifier(), + )), + ); + let maybe_function_symbol = match symbol_table.insert_function_symbol(to_insert) { + Ok(function_symbol) => Some(function_symbol), + Err(symbol_insert_error) => { + handle_insert_error( + symbol_insert_error, + platform_function.identifier().name(), + platform_function.identifier().file_id(), + platform_function.identifier().range(), + diagnostics, + ); + None + } + }; + + if maybe_function_symbol.is_some() { + platform_function.set_function_symbol(maybe_function_symbol.as_ref().unwrap().clone()); + } + + // create a scope for later + symbol_table.push_scope(&format!( + "PlatformFunctionScope {}", + platform_function.identifier().name() + )); + platform_function.set_scope_id(symbol_table.current_scope_id()); + symbol_table.pop_scope(); + + maybe_function_symbol +} diff --git a/src/name_analysis/mod.rs b/src/name_analysis/mod.rs index 0aff365..37c41ae 100644 --- a/src/name_analysis/mod.rs +++ b/src/name_analysis/mod.rs @@ -46,7 +46,9 @@ pub fn analyze_names< ) -> Vec { let mut diagnostics = vec![]; - // gather symbols + // first pass + // generally, gather symbols from declarations + // don't go into functions for compilation_unit in compilation_units.iter_mut() { let file_name = files.name(compilation_unit.file_id()).unwrap(); na_p1_compilation_unit(file_name, compilation_unit, symbol_table, &mut diagnostics); @@ -56,7 +58,11 @@ pub fn analyze_names< return diagnostics; } - // resolve symbols + // resolve global imports + symbol_table.resolve_global_imports(); + + // second pass + // resolve referenced symbols and go into functions for compilation_unit in compilation_units.iter_mut() { na_p2_compilation_unit(compilation_unit, symbol_table, &mut diagnostics); } @@ -318,12 +324,39 @@ mod tests { 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); + } + + #[test] + fn with_std_core_source() { + let sources = HashMap::from([ + ( + "std/core/print.dm", + " + ns std::core + + pub platform fn println(msg: Any) -> Void + + pub platform fn print(msg: Any) -> Void + ", + ), + ( + "main.dm", + " + fn main() + println('A line') + print('Not a line') + end + ", + ), + ]); + let mut symbol_table = SymbolTable::new(); + let global_star_use = symbol_table + .insert_star_use_symbol(StarUseSymbol::new( + &str_slice_to_rcs(&["std", "core"]), + None, + )) + .expect("Failed to insert star use symbol."); assert_no_diagnostics(sources, &mut symbol_table); } diff --git a/src/name_analysis/second_pass.rs b/src/name_analysis/second_pass.rs index 8e9f212..2dd582d 100644 --- a/src/name_analysis/second_pass.rs +++ b/src/name_analysis/second_pass.rs @@ -1,11 +1,11 @@ use crate::ast::node::{ - 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, + AssignmentStatement, BacktickString, Call, CompilationUnit, ConcreteUseStatement, + ConcreteUseStatementSuffix, DString, Expression, ExpressionList, ExpressionStatement, Function, + FunctionAliasBody, FunctionBlockBody, FunctionBody, FunctionEqualsBody, GenericParameters, + Identifier, IdentifierExpression, IdentifierOrFqn, LValue, LValueSuffix, Literal, + ModuleLevelDeclaration, ObjectIndex, Parameter, Parameters, PlatformFunction, PrimitiveType, + ReturnType, StarUseStatement, Statement, SuffixExpression, SuffixOperator, TypeUse, TypedArray, + UseStatement, UseStatementIdentifier, UseStatementPrefix, VariableDeclaration, }; use crate::diagnostic::DmDiagnostic; use crate::name_analysis::symbol::source_definition::SourceDefinition; @@ -153,7 +153,7 @@ fn na_p2_module_level_declaration( na_p2_function(function, symbol_table, diagnostics); } ModuleLevelDeclaration::PlatformFunction(platform_function) => { - todo!() + na_p2_platform_function(platform_function, symbol_table, diagnostics); } } } @@ -197,6 +197,44 @@ fn na_p2_function( symbol_table.pop_scope(); } +fn na_p2_platform_function( + platform_function: &mut PlatformFunction, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + // change to this function's scope + symbol_table.set_current_scope(*platform_function.scope_id().unwrap()); + + // generics + na_p2_generic_parameters(platform_function.generics_mut(), symbol_table, diagnostics); + + // parameters + let parameter_symbols = na_p2_parameters( + platform_function.parameters_mut(), + symbol_table, + diagnostics, + ); + platform_function + .function_symbol_mut() + .unwrap() + .borrow_mut() + .set_parameter_symbols(parameter_symbols); + + // return_type + let maybe_return_type_symbol = na_p2_return_type( + platform_function.return_type_mut(), + symbol_table, + diagnostics, + ); + if let Some(return_type_symbol) = maybe_return_type_symbol { + platform_function + .function_symbol_mut() + .unwrap() + .borrow_mut() + .set_return_type(return_type_symbol); + } +} + fn na_p2_generic_parameters( generic_parameters: &mut GenericParameters, symbol_table: &mut SymbolTable, @@ -602,7 +640,7 @@ fn na_p2_expression( na_p2_suffix_expression(suffix, symbol_table, diagnostics); } Expression::Literal(literal) => { - todo!() + na_p2_literal(literal, symbol_table, diagnostics); } Expression::Identifier(identifier_expression) => { na_p2_identifier_expression(identifier_expression, symbol_table, diagnostics); @@ -726,3 +764,49 @@ fn na_p2_expression_list( na_p2_expression(expression, symbol_table, diagnostics); } } + +fn na_p2_literal( + literal: &mut Literal, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + match literal { + Literal::DString(d_string) => { + na_p2_d_string(d_string, symbol_table, diagnostics); + } + Literal::BacktickString(backtick_string) => { + na_p2_backtick_string(backtick_string, symbol_table, diagnostics); + } + _ => { + // no-op, nothing to check. + } + } +} + +fn na_p2_d_string( + d_string: &mut DString, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + for d_string_expression in d_string.expressions_mut() { + na_p2_expression( + d_string_expression.expression_mut(), + symbol_table, + diagnostics, + ); + } +} + +fn na_p2_backtick_string( + backtick_string: &mut BacktickString, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + for d_string_expression in backtick_string.expressions_mut() { + na_p2_expression( + d_string_expression.expression_mut(), + symbol_table, + diagnostics, + ); + } +} diff --git a/src/name_analysis/symbol_table/mod.rs b/src/name_analysis/symbol_table/mod.rs index edb9b7b..6d841f6 100644 --- a/src/name_analysis/symbol_table/mod.rs +++ b/src/name_analysis/symbol_table/mod.rs @@ -10,6 +10,7 @@ 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; +use crate::name_analysis::util::join_fqn_parts; use scope::Scope; use std::cell::RefCell; use std::fmt::Display; @@ -132,6 +133,45 @@ impl SymbolTable { as_rc } + pub fn resolve_global_imports(&mut self) { + let global_scope = &self.scopes[0]; + for concrete_use_symbol in global_scope.concrete_use_symbols() { + match self + .symbol_tree + .find_usable_symbol(concrete_use_symbol.borrow().fqn_parts()) + { + Ok(usable_symbol) => { + concrete_use_symbol.borrow_mut().set_resolved_symbol(usable_symbol); + } + Err(symbol_lookup_error) => { + // panic because this is definitely a compiler/configuration error + panic!( + "{:?}: Cannot resolve global import {}", + symbol_lookup_error, + join_fqn_parts(concrete_use_symbol.borrow().fqn_parts()) + ); + } + } + } + + for star_use_symbol in global_scope.star_use_symbols() { + let mut star_use_symbol_ref_mut = star_use_symbol.borrow_mut(); + match self.symbol_tree.find_all_by_base_fqn(star_use_symbol_ref_mut.fqn_parts()) { + Ok(usable_symbols) => { + star_use_symbol_ref_mut.set_resolved_symbols(usable_symbols); + } + Err(symbol_lookup_error) => { + // again, panic because this is a compiler/configuration error + panic!( + "{:?}: Cannot resolve global star import {}::*", + symbol_lookup_error, + join_fqn_parts(star_use_symbol.borrow().fqn_parts()) + ); + } + } + } + } + pub fn find_usable_symbols_by_base_fqn( &self, fqn_parts: &[Rc], diff --git a/src/name_analysis/symbol_table/scope.rs b/src/name_analysis/symbol_table/scope.rs index e5da803..7cb37e7 100644 --- a/src/name_analysis/symbol_table/scope.rs +++ b/src/name_analysis/symbol_table/scope.rs @@ -69,6 +69,14 @@ impl Scope { pub fn id(&self) -> usize { self.id } + + pub fn concrete_use_symbols(&self) -> impl Iterator>> { + self.concrete_use_symbols.values() + } + + pub fn star_use_symbols(&self) -> impl Iterator>> { + self.star_use_symbols.values() + } pub fn insert_concrete_use_symbol( &mut self, diff --git a/src/name_analysis/symbol_table/symbol_tree.rs b/src/name_analysis/symbol_table/symbol_tree.rs index 659fde9..df31106 100644 --- a/src/name_analysis/symbol_table/symbol_tree.rs +++ b/src/name_analysis/symbol_table/symbol_tree.rs @@ -81,8 +81,10 @@ impl SymbolTree { panic!("Unable to register module fqn with no parts.") } if fqn_parts.len() == 1 { - self.children - .insert(fqn_parts[0].clone(), SymbolTree::new(fqn_parts[0].as_ref())); + if !self.children.contains_key(&fqn_parts[0]) { + self.children + .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(); @@ -154,12 +156,44 @@ impl SymbolTree { } } } + + pub fn find_usable_symbol( + &self, + fqn_parts: &[Rc], + ) -> Result { + match fqn_parts.len() { + 0 => panic!("Cannot resolve a usable symbol with no fqn part."), + 1 => self + .find_interface(fqn_parts) + .map(|interface_symbol| UsableSymbol::Interface(interface_symbol.clone())) + .or_else(|| { + self.find_class(fqn_parts) + .map(|class_symbol| UsableSymbol::Class(class_symbol.clone())) + }) + .or_else(|| { + self.find_function(fqn_parts) + .map(|function_symbol| UsableSymbol::Function(function_symbol)) + }) + .map_or_else( + || Err(SymbolLookupError::NoDefinition), + |usable_symbol| Ok(usable_symbol), + ), + _ => { + if self.children.contains_key(fqn_parts[0].as_ref()) { + let child = self.children.get(fqn_parts[0].as_ref()).unwrap(); + child.find_usable_symbol(&fqn_parts[1..]) + } else { + Err(SymbolLookupError::NoSuchNamespace) + } + } + } + } } enum FormatAction<'a> { PrintSymbolTree(&'a SymbolTree), IncreaseIndent, - DecreaseIndent + DecreaseIndent, } impl Display for SymbolTree { @@ -168,7 +202,7 @@ impl Display for SymbolTree { 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) => { @@ -181,7 +215,10 @@ impl Display for SymbolTree { // increase for the first child stack.push(FormatAction::IncreaseIndent); - indent_writer.writeln_indented(&format!("SymbolTree(debug_name = {})", symbol_tree.debug_name))?; + indent_writer.writeln_indented(&format!( + "SymbolTree(debug_name = {})", + symbol_tree.debug_name + ))?; indent_writer.increase_indent(); for interface in symbol_tree.interfaces.values() { @@ -203,7 +240,7 @@ impl Display for SymbolTree { } } } - + writeln!(f, "{}", acc) } } diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index 0265072..af47676 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -503,6 +503,12 @@ PlatformFunction: - identifier - parameters - return_type + fields: + - function_symbol: + kind: FunctionSymbol + wrap: rc_ref_cell + - scope_id: + kind: usize PlatformOperatorFunction: struct: children: diff --git a/src/parser/deimos.pest b/src/parser/deimos.pest index edfd793..753be0e 100644 --- a/src/parser/deimos.pest +++ b/src/parser/deimos.pest @@ -188,8 +188,8 @@ FullyQualifiedName = { } IdentifierOrFqn = { - Identifier - | FullyQualifiedName + FullyQualifiedName + | Identifier } // Common lists