From 0550df534e15061228e1ffb7064f1e71e4add8e6 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Sat, 1 Nov 2025 12:23:41 -0500 Subject: [PATCH] Move parameter gathering to second pass in order to match analysis of all locals there. --- src/bin/dmc/name_analysis.rs | 8 +- src/name_analysis/first_pass.rs | 249 +-------------- src/name_analysis/mod.rs | 129 ++++++-- src/name_analysis/second_pass.rs | 305 ++++++++++++++++--- src/name_analysis/symbol/parameter_symbol.rs | 8 + src/name_analysis/symbol_table/mod.rs | 4 +- src/parser/ast.yaml | 14 +- 7 files changed, 415 insertions(+), 302 deletions(-) diff --git a/src/bin/dmc/name_analysis.rs b/src/bin/dmc/name_analysis.rs index ddfd75c..9d88644 100644 --- a/src/bin/dmc/name_analysis.rs +++ b/src/bin/dmc/name_analysis.rs @@ -43,7 +43,7 @@ pub fn name_analysis(paths: &[PathBuf]) -> Result<(), Box let mut compilation_units = vec![]; let mut files: SimpleFiles<&str, &str> = SimpleFiles::new(); let mut parse_errors = vec![]; - + for (path, source) in &paths_and_sources { let parse_result = DeimosParser::parse(Rule::CompilationUnit, source); match parse_result { @@ -57,9 +57,11 @@ pub fn name_analysis(paths: &[PathBuf]) -> Result<(), Box } } } - + if !parse_errors.is_empty() { - return Err(Box::new(ParseErrors { errors: parse_errors })); + return Err(Box::new(ParseErrors { + errors: parse_errors, + })); } let mut symbol_table = SymbolTable::new(); diff --git a/src/name_analysis/first_pass.rs b/src/name_analysis/first_pass.rs index 4fabc83..0a36535 100644 --- a/src/name_analysis/first_pass.rs +++ b/src/name_analysis/first_pass.rs @@ -1,25 +1,20 @@ use crate::ast::node::{ - CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody, - GenericParameters, Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration, Parameter, - Parameters, PrimitiveType, ReturnType, StarUseStatement, TypeUse, TypedArray, UseStatement, + CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody + , Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration + , StarUseStatement, UseStatement, UseStatementIdentifier, UseStatementPrefix, }; use crate::diagnostic::DmDiagnostic; use crate::name_analysis::symbol::function_symbol::FunctionSymbol; -use crate::name_analysis::symbol::generic_type_symbol::GenericTypeSymbol; use crate::name_analysis::symbol::module_level_symbol::ModuleLevelSymbol; use crate::name_analysis::symbol::module_symbol::ModuleSymbol; -use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol; -use crate::name_analysis::symbol::primitive_type_symbol::PrimitiveTypeSymbol; use crate::name_analysis::symbol::source_definition::SourceDefinition; -use crate::name_analysis::symbol::type_symbol::TypeSymbol; use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol}; use crate::name_analysis::symbol_table::SymbolTable; use crate::name_analysis::util::{ - format_fqn, handle_insert_error, handle_lookup_error, join_fqn_parts, + handle_insert_error, join_fqn_parts, }; use std::cell::RefCell; -use std::fmt::format; use std::rc::Rc; pub fn na_p1_compilation_unit( @@ -250,7 +245,7 @@ fn na_p1_function( false, Some(SourceDefinition::from_identifier(function.identifier())), ); - let function_symbol = match symbol_table.insert_function_symbol(to_insert) { + 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( @@ -264,232 +259,14 @@ fn na_p1_function( } }; - if function_symbol.is_some() { - let mut as_ref_mut = function_symbol.as_ref().unwrap().borrow_mut(); - - // push a scope for this function - symbol_table.push_scope(&format!("FunctionScope {}", function.identifier().name())); - - // generics - na_p1_generic_parameters(function.generics_mut(), symbol_table, diagnostics); - - // parameters - as_ref_mut.set_parameter_symbols(na_p1_parameters( - function.parameters_mut(), - symbol_table, - diagnostics, - )); - - // return type - let return_type = na_p1_return_type(function.return_type_mut(), symbol_table, diagnostics); - if let Some(type_symbol) = return_type { - as_ref_mut.set_return_type(type_symbol); - } - - symbol_table.push_scope(&format!( - "FunctionBodyScope {}", - function.identifier().name() - )); - - na_p1_function_body( - function.function_body_mut(), - symbol_table, - diagnostics - ); - - symbol_table.pop_scope(); - symbol_table.pop_scope(); + 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()); + symbol_table.pop_scope(); - function_symbol -} - -fn na_p1_parameters( - parameters: &mut Parameters, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) -> Vec>> { - parameters - .parameters_mut() - .map(|parameter| na_p1_parameter(parameter, symbol_table, diagnostics)) - .filter(Option::is_some) - .map(Option::unwrap) - .collect() -} - -fn na_p1_parameter( - parameter: &mut Parameter, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) -> Option>> { - let parameter_type_symbol = na_p1_type_use(parameter.type_use_mut(), symbol_table, diagnostics); - let to_insert = ParameterSymbol::new( - parameter.identifier().name(), - Some(SourceDefinition::from_identifier(parameter.identifier())), - parameter_type_symbol, - ); - match symbol_table.insert_parameter_symbol(to_insert) { - Ok(parameter_symbol) => Some(parameter_symbol), - Err(symbol_insert_error) => { - handle_insert_error( - symbol_insert_error, - parameter.identifier().name(), - parameter.identifier().file_id(), - parameter.identifier().range(), - diagnostics, - ); - None - } - } -} - -fn na_p1_return_type( - return_type: &mut ReturnType, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) -> Option { - na_p1_type_use(return_type.type_use_mut(), symbol_table, diagnostics) -} - -fn na_p1_type_use( - type_use: &mut TypeUse, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) -> Option { - match type_use { - TypeUse::PrimitiveType(primitive_type) => { - Some(TypeSymbol::Primitive(match primitive_type { - PrimitiveType::Byte => PrimitiveTypeSymbol::Byte, - PrimitiveType::Short => PrimitiveTypeSymbol::Short, - PrimitiveType::Char => PrimitiveTypeSymbol::Char, - PrimitiveType::Int => PrimitiveTypeSymbol::Int, - PrimitiveType::Long => PrimitiveTypeSymbol::Long, - PrimitiveType::Double => PrimitiveTypeSymbol::Double, - PrimitiveType::Bool => PrimitiveTypeSymbol::Boolean, - PrimitiveType::String => PrimitiveTypeSymbol::String, - PrimitiveType::TypedArray(typed_array) => { - na_p1_typed_array(typed_array, symbol_table, diagnostics) - } - PrimitiveType::Any => PrimitiveTypeSymbol::Any, - PrimitiveType::Void => PrimitiveTypeSymbol::Void, - })) - } - TypeUse::InterfaceOrClassTypeUse(interface_or_class_type) => { - match interface_or_class_type.identifier_or_fqn() { - IdentifierOrFqn::Identifier(identifier) => { - match symbol_table.lookup_type(identifier.name()) { - Ok(type_symbol) => { - interface_or_class_type.set_type_symbol(type_symbol.clone()); - Some(type_symbol) - } - Err(symbol_lookup_error) => { - handle_lookup_error( - symbol_lookup_error, - identifier.name(), - identifier.file_id(), - identifier.range(), - diagnostics, - ); - None - } - } - } - IdentifierOrFqn::FullyQualifiedName(fqn) => { - let fqn_parts = fqn - .identifiers() - .map(Identifier::name) - .collect::>(); - match symbol_table.lookup_type_by_fqn(&fqn_parts) { - Ok(type_symbol) => { - interface_or_class_type.set_type_symbol(type_symbol.clone()); - Some(type_symbol) - } - Err(symbol_lookup_error) => { - handle_lookup_error( - symbol_lookup_error, - &format_fqn(&fqn_parts), - fqn.file_id(), - fqn.range(), - diagnostics, - ); - None - } - } - } - } - } - TypeUse::TupleTypeUse(tuple_type) => { - todo!() - } - TypeUse::FunctionTypeUse(function_type) => { - todo!() - } - } -} - -fn na_p1_typed_array( - typed_array: &mut TypedArray, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) -> PrimitiveTypeSymbol { - let inner_type_use = typed_array - .generic_arguments_mut() - .type_use_list_mut() - .type_uses_mut() - .next() - .unwrap(); - let inner_type_symbol = na_p1_type_use(inner_type_use, symbol_table, diagnostics); - PrimitiveTypeSymbol::TypedArray { - inner_type: inner_type_symbol.map(Box::from), - } -} - -fn na_p1_generic_parameters( - generic_parameters: &mut GenericParameters, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) { - for identifier in generic_parameters.identifier_list().identifiers() { - let generic_type_symbol = GenericTypeSymbol::new( - identifier.name(), - Some(SourceDefinition::from_identifier(identifier)), - ); - match symbol_table.insert_generic_type_symbol(generic_type_symbol) { - Ok(_) => {} - Err(symbol_insert_error) => { - handle_insert_error( - symbol_insert_error, - identifier.name(), - identifier.file_id(), - identifier.range(), - diagnostics, - ); - } - } - } -} - -fn na_p1_function_body( - function_body: &mut FunctionBody, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) { - match function_body { - FunctionBody::FunctionAliasBody(alias_body) => { - // set scope id for pass 2; see below - alias_body.set_scope_id(symbol_table.current_scope_id()); - } - FunctionBody::FunctionEqualsBody(equals_body) => { - // see below - equals_body.set_scope_id(symbol_table.current_scope_id()); - } - FunctionBody::FunctionBlockBody(block_body) => { - // we need to do all insertion/resolution in pass 2, because we - // might call functions/use classes/etc from the same compilation - // unit which haven't been defined yet. So the strategy is to set - // the scope id for the body and then in pass 2, set the symbol - // table's current scope to that id. - block_body.set_scope_id(symbol_table.current_scope_id()); - } - } + maybe_function_symbol } diff --git a/src/name_analysis/mod.rs b/src/name_analysis/mod.rs index a562acf..fdb7d9b 100644 --- a/src/name_analysis/mod.rs +++ b/src/name_analysis/mod.rs @@ -40,7 +40,7 @@ pub fn analyze_names< 'a, F: Files<'a, FileId = usize, Name = &'a str, Source = &'a str> + ?Sized, >( - compilation_units: &mut Vec, + compilation_units: &mut [CompilationUnit], files: &'a F, symbol_table: &mut SymbolTable, ) -> Vec { @@ -51,13 +51,13 @@ pub fn analyze_names< let file_name = files.name(compilation_unit.file_id()).unwrap(); na_p1_compilation_unit(file_name, compilation_unit, symbol_table, &mut diagnostics); } - + if !diagnostics.is_empty() { return diagnostics; } // resolve symbols - for compilation_unit in compilation_units { + for compilation_unit in compilation_units.iter_mut() { na_p2_compilation_unit(compilation_unit, symbol_table, &mut diagnostics); } @@ -68,6 +68,7 @@ pub fn analyze_names< mod tests { use super::*; use crate::ast::build::build_ast; + use crate::name_analysis::symbol::use_symbol::StarUseSymbol; use crate::parser::{DeimosParser, Rule}; use crate::std_core::add_std_core_symbols; use codespan_reporting::files::SimpleFiles; @@ -75,14 +76,17 @@ mod tests { use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use pest::Parser; use std::collections::HashMap; + use std::ops::Deref; use std::rc::Rc; - use crate::name_analysis::symbol::use_symbol::StarUseSymbol; + 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>, sources: HashMap<&'a str, &'a str>, ) -> Vec { - let mut compilation_units: Vec = vec![]; + let mut compilation_units = vec![]; for (file_name, source) in sources { let parse_result = DeimosParser::parse(Rule::CompilationUnit, source); @@ -96,7 +100,7 @@ mod tests { let file_id = files.add(file_name, source); compilation_units.push(build_ast(file_id, &mut pairs)); } - + compilation_units } @@ -104,11 +108,15 @@ mod tests { sources: HashMap<&'a str, &'a str>, symbol_table: &mut SymbolTable, number_of_diagnostics: usize, - ) -> Vec { + ) -> (Vec, SimpleFiles<&'a str, &'a str>) { let mut files = SimpleFiles::<&'a str, &'a str>::new(); - let mut compilation_units = parse_compilation_units(&mut files, sources); - - let diagnostics = analyze_names(&mut compilation_units, &files, symbol_table); + let mut to_analyze = parse_compilation_units(&mut files, sources); + + let diagnostics = analyze_names( + &mut to_analyze, + &files, + symbol_table + ); if diagnostics.len() != number_of_diagnostics { let writer = StandardStream::stderr(ColorChoice::Always); @@ -123,15 +131,30 @@ mod tests { assert_eq!(number_of_diagnostics, diagnostics.len()); - compilation_units + (to_analyze, files) } - fn assert_no_diagnostics( - sources: HashMap<&str, &str>, + fn assert_no_diagnostics<'a>( + sources: HashMap<&'a str, &'a str>, symbol_table: &mut SymbolTable, - ) -> Vec { + ) -> (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 + } #[test] fn params_seen() { @@ -144,7 +167,79 @@ mod tests { ", )]); - assert_no_diagnostics(sources, &mut SymbolTable::new()); + let mut symbol_table = SymbolTable::new(); + 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 { + Function(function) => { + if function.identifier().name() == "main" { + found_main = true; + let function_symbol_ref = function.function_symbol().unwrap().borrow(); + assert_eq!("main", function_symbol_ref.declared_name()); + let parameter_symbols = function_symbol_ref.parameter_symbols(); + assert_eq!(parameter_symbols.len(), 1); + let args_ref = parameter_symbols[0].borrow(); + assert_eq!(args_ref.declared_name(), "args"); + let args_type_symbol = args_ref.type_symbol().unwrap(); + match args_type_symbol { + TypeSymbol::Primitive(primitive_type_symbol) => { + match primitive_type_symbol { + PrimitiveTypeSymbol::TypedArray { inner_type } => { + match *inner_type.clone().unwrap() { + TypeSymbol::Primitive(primitive_type_symbol) => { + match primitive_type_symbol { + PrimitiveTypeSymbol::String => {} + _ => panic!("Expected String for args' inner type.") + } + } + _ => panic!("Expected a primitive type symbol for args' inner type.") + } + } + _ => panic!("Expected a TypeArray for args."), + } + } + _ => 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(), + "x" + ); + } + } + VariableUse(variable_use) => { + if variable_use.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" + ) + } + _ => panic!("Expected args variable use to be a parameter symbol.") + } + } + } + _ => {} + } + }); + + assert!(found_main); + assert!(found_x); + assert!(found_args_ref); } #[test] @@ -195,7 +290,7 @@ mod tests { )]); assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1); } - + #[test] fn sees_println() { let sources = HashMap::from([( @@ -213,7 +308,7 @@ mod tests { ); symbol_table.insert_star_use_symbol(global_std_core_use) .expect("Failed to insert star use symbol."); - + add_std_core_symbols(&mut symbol_table).expect("Failed to add 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 7a5f0cd..6f3af9d 100644 --- a/src/name_analysis/second_pass.rs +++ b/src/name_analysis/second_pass.rs @@ -1,9 +1,22 @@ -use crate::ast::node::{AssignmentStatement, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Expression, ExpressionStatement, Function, FunctionAliasBody, FunctionBlockBody, FunctionBody, Identifier, LValue, ModuleLevelDeclaration, StarUseStatement, Statement, TypeUse, UseStatement, UseStatementIdentifier, UseStatementPrefix, VariableDeclaration, VariableUse}; +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, +}; use crate::diagnostic::DmDiagnostic; use crate::name_analysis::symbol::source_definition::SourceDefinition; use crate::name_analysis::symbol::variable_symbol::VariableSymbol; +use crate::name_analysis::symbol::{ + GenericTypeSymbol, ParameterSymbol, PrimitiveTypeSymbol, TypeSymbol, +}; use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable}; -use crate::name_analysis::util::{handle_insert_error, handle_lookup_error, join_fqn_parts}; +use crate::name_analysis::util::{ + format_fqn, handle_insert_error, handle_lookup_error, join_fqn_parts, +}; +use std::cell::RefCell; use std::rc::Rc; pub fn na_p2_compilation_unit( @@ -149,9 +162,206 @@ fn na_p2_function( symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { + // change to this function's scope + symbol_table.set_current_scope(*function.scope_id().unwrap()); + + // generics + na_p2_generic_parameters(function.generics_mut(), symbol_table, diagnostics); + + // parameters + let parameter_symbols = na_p2_parameters(function.parameters_mut(), symbol_table, diagnostics); + function + .function_symbol() + .unwrap() + .borrow_mut() + .set_parameter_symbols(parameter_symbols); + + // return type + let maybe_return_type_symbol = + na_p2_return_type(function.return_type_mut(), symbol_table, diagnostics); + if let Some(return_type_symbol) = maybe_return_type_symbol { + function + .function_symbol() + .unwrap() + .borrow_mut() + .set_return_type(return_type_symbol); + } + + // push a scope for the body and check the body + symbol_table.push_scope(&format!( + "FunctionBodyScope {}", + function.identifier().name() + )); na_p2_function_body(function.function_body_mut(), symbol_table, diagnostics); + symbol_table.pop_scope(); } +fn na_p2_generic_parameters( + generic_parameters: &mut GenericParameters, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + for identifier in generic_parameters.identifier_list().identifiers() { + let generic_type_symbol = GenericTypeSymbol::new( + identifier.name(), + Some(SourceDefinition::from_identifier(identifier)), + ); + match symbol_table.insert_generic_type_symbol(generic_type_symbol) { + Ok(_) => {} + Err(symbol_insert_error) => { + handle_insert_error( + symbol_insert_error, + identifier.name(), + identifier.file_id(), + identifier.range(), + diagnostics, + ); + } + } + } +} + +fn na_p2_parameters( + parameters: &mut Parameters, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) -> Vec>> { + parameters + .parameters_mut() + .map(|parameter| na_p2_parameter(parameter, symbol_table, diagnostics)) + .filter(Option::is_some) + .map(Option::unwrap) + .collect() +} + +fn na_p2_parameter( + parameter: &mut Parameter, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) -> Option>> { + let parameter_type_symbol = na_p2_type_use(parameter.type_use_mut(), symbol_table, diagnostics); + let to_insert = ParameterSymbol::new( + parameter.identifier().name(), + Some(SourceDefinition::from_identifier(parameter.identifier())), + parameter_type_symbol, + ); + match symbol_table.insert_parameter_symbol(to_insert) { + Ok(parameter_symbol) => Some(parameter_symbol), + Err(symbol_insert_error) => { + handle_insert_error( + symbol_insert_error, + parameter.identifier().name(), + parameter.identifier().file_id(), + parameter.identifier().range(), + diagnostics, + ); + None + } + } +} + +fn na_p2_return_type( + return_type: &mut ReturnType, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) -> Option { + na_p2_type_use(return_type.type_use_mut(), symbol_table, diagnostics) +} + +fn na_p2_type_use( + type_use: &mut TypeUse, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) -> Option { + match type_use { + TypeUse::PrimitiveType(primitive_type) => { + Some(TypeSymbol::Primitive(match primitive_type { + PrimitiveType::Byte => PrimitiveTypeSymbol::Byte, + PrimitiveType::Short => PrimitiveTypeSymbol::Short, + PrimitiveType::Char => PrimitiveTypeSymbol::Char, + PrimitiveType::Int => PrimitiveTypeSymbol::Int, + PrimitiveType::Long => PrimitiveTypeSymbol::Long, + PrimitiveType::Double => PrimitiveTypeSymbol::Double, + PrimitiveType::Bool => PrimitiveTypeSymbol::Boolean, + PrimitiveType::String => PrimitiveTypeSymbol::String, + PrimitiveType::TypedArray(typed_array) => { + na_p2_typed_array(typed_array, symbol_table, diagnostics) + } + PrimitiveType::Any => PrimitiveTypeSymbol::Any, + PrimitiveType::Void => PrimitiveTypeSymbol::Void, + })) + } + TypeUse::InterfaceOrClassTypeUse(interface_or_class_type) => { + match interface_or_class_type.identifier_or_fqn() { + IdentifierOrFqn::Identifier(identifier) => { + match symbol_table.lookup_type(identifier.name()) { + Ok(type_symbol) => { + interface_or_class_type.set_type_symbol(type_symbol.clone()); + Some(type_symbol) + } + Err(symbol_lookup_error) => { + handle_lookup_error( + symbol_lookup_error, + identifier.name(), + identifier.file_id(), + identifier.range(), + diagnostics, + ); + None + } + } + } + IdentifierOrFqn::FullyQualifiedName(fqn) => { + let fqn_parts = fqn + .identifiers() + .map(Identifier::name) + .collect::>(); + match symbol_table.lookup_type_by_fqn(&fqn_parts) { + Ok(type_symbol) => { + interface_or_class_type.set_type_symbol(type_symbol.clone()); + Some(type_symbol) + } + Err(symbol_lookup_error) => { + handle_lookup_error( + symbol_lookup_error, + &format_fqn(&fqn_parts), + fqn.file_id(), + fqn.range(), + diagnostics, + ); + None + } + } + } + } + } + TypeUse::TupleTypeUse(tuple_type) => { + todo!() + } + TypeUse::FunctionTypeUse(function_type) => { + todo!() + } + } +} + +fn na_p2_typed_array( + typed_array: &mut TypedArray, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) -> PrimitiveTypeSymbol { + let inner_type_use = typed_array + .generic_arguments_mut() + .type_use_list_mut() + .type_uses_mut() + .next() + .unwrap(); + let inner_type_symbol = na_p2_type_use(inner_type_use, symbol_table, diagnostics); + PrimitiveTypeSymbol::TypedArray { + inner_type: inner_type_symbol.map(Box::from), + } +} + +/// **Note:** caller needs to push a scope before calling. fn na_p2_function_body( function_body: &mut FunctionBody, symbol_table: &mut SymbolTable, @@ -161,7 +371,9 @@ fn na_p2_function_body( FunctionBody::FunctionAliasBody(alias_body) => { na_p2_function_alias_body(alias_body, symbol_table, diagnostics); } - FunctionBody::FunctionEqualsBody(equals_body) => {} + FunctionBody::FunctionEqualsBody(equals_body) => { + na_p2_function_equals_body(equals_body, symbol_table, diagnostics); + } FunctionBody::FunctionBlockBody(block_body) => { na_p2_function_block_body(block_body, symbol_table, diagnostics); } @@ -173,10 +385,8 @@ fn na_p2_function_alias_body( symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { - let maybe_function_symbol = symbol_table.lookup_function_symbol( - function_alias_body.identifier().name(), - *function_alias_body.scope_id().unwrap(), - ); + let maybe_function_symbol = + symbol_table.lookup_function_symbol(function_alias_body.identifier().name()); match maybe_function_symbol { Ok(function_symbol) => { function_alias_body.set_resolved_function_symbol(function_symbol); @@ -193,12 +403,23 @@ fn na_p2_function_alias_body( } } +fn na_p2_function_equals_body( + function_equals_body: &mut FunctionEqualsBody, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + na_p2_expression( + function_equals_body.expression_mut(), + symbol_table, + diagnostics, + ); +} + fn na_p2_function_block_body( function_block_body: &mut FunctionBlockBody, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { - symbol_table.set_current_scope(*function_block_body.scope_id().unwrap()); for statement in function_block_body.statements_mut() { na_p2_statement(statement, symbol_table, diagnostics); } @@ -261,12 +482,12 @@ fn na_p2_variable_declaration( ); } } - + // type-use if let Some(type_use) = variable_declaration.type_use_mut() { na_p2_type_use(type_use, symbol_table, diagnostics); } - + // initializer if let Some(expression) = variable_declaration.expression_mut() { na_p2_expression(expression, symbol_table, diagnostics); @@ -278,8 +499,16 @@ fn na_p2_assignment_statement( symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { - na_p2_l_value(assignment_statement.l_value_mut(), symbol_table, diagnostics); - na_p2_expression(assignment_statement.expression_mut(), symbol_table, diagnostics); + na_p2_l_value( + assignment_statement.l_value_mut(), + symbol_table, + diagnostics, + ); + na_p2_expression( + assignment_statement.expression_mut(), + symbol_table, + diagnostics, + ); } fn na_p2_l_value( @@ -288,7 +517,29 @@ fn na_p2_l_value( diagnostics: &mut Vec, ) { na_p2_variable_use(l_value.variable_use_mut(), symbol_table, diagnostics); - // TODO: suffixes + // "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) + // which we do need to recurse into. + for l_value_suffix in l_value.suffixes_mut() { + na_p2_l_value_suffix(l_value_suffix, symbol_table, diagnostics); + } +} + +fn na_p2_l_value_suffix( + l_value_suffix: &mut LValueSuffix, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, +) { + match l_value_suffix { + LValueSuffix::ObjectProperty(_) => { + // no-op; properties are checked during type-checking for soundness + } + LValueSuffix::ObjectIndex(object_index) => { + // check inner expression + na_p2_expression(object_index.expression_mut(), symbol_table, diagnostics); + } + } } fn na_p2_expression_statement( @@ -296,7 +547,11 @@ fn na_p2_expression_statement( symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { - na_p2_expression(expression_statement.expression_mut(), symbol_table, diagnostics); + na_p2_expression( + expression_statement.expression_mut(), + symbol_table, + diagnostics, + ); } fn na_p2_variable_use( @@ -370,25 +625,3 @@ fn na_p2_expression( } } } - -fn na_p2_type_use( - type_use: &mut TypeUse, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, -) { - match type_use { - TypeUse::PrimitiveType(primitive_type_use) => { - todo!() - } - TypeUse::InterfaceOrClassTypeUse(interface_or_class_type_use) => { - todo!() - } - TypeUse::TupleTypeUse(tuple_type_use) => { - todo!() - } - TypeUse::FunctionTypeUse(function_type_use) => { - todo!() - } - } -} - diff --git a/src/name_analysis/symbol/parameter_symbol.rs b/src/name_analysis/symbol/parameter_symbol.rs index a616390..87a8df8 100644 --- a/src/name_analysis/symbol/parameter_symbol.rs +++ b/src/name_analysis/symbol/parameter_symbol.rs @@ -30,6 +30,14 @@ impl ParameterSymbol { pub fn declared_name_owned(&self) -> Rc { self.declared_name.clone() } + + pub fn type_symbol(&self) -> Option<&TypeSymbol> { + self.type_symbol.as_ref() + } + + pub fn type_symbol_mut(&mut self) -> Option<&mut TypeSymbol> { + self.type_symbol.as_mut() + } } impl Symbol for ParameterSymbol { diff --git a/src/name_analysis/symbol_table/mod.rs b/src/name_analysis/symbol_table/mod.rs index 42af0fe..b0ce529 100644 --- a/src/name_analysis/symbol_table/mod.rs +++ b/src/name_analysis/symbol_table/mod.rs @@ -267,8 +267,8 @@ impl SymbolTable { todo!() } - pub fn lookup_function_symbol(&self, declared_name: &str, scope_id: usize) -> Result>, SymbolLookupError> { - let mut current_scope: Option<&Scope> = self.scopes.get(scope_id); + 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) { return Ok(function_symbol.clone()); diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index ec8feef..45f3422 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -447,6 +447,12 @@ Function: node: or_else: void - function_body + fields: + - function_symbol: + kind: FunctionSymbol + wrap: rc_ref_cell + - scope_id: + kind: usize OperatorFunction: struct: children: @@ -609,9 +615,6 @@ FunctionEqualsBody: struct: children: - expression - fields: - - scope_id: - kind: usize FunctionAliasBody: struct: children: @@ -620,8 +623,6 @@ FunctionAliasBody: rule: Alias - identifier fields: - - scope_id: - kind: usize - resolved_function_symbol: kind: FunctionSymbol wrap: rc_ref_cell @@ -634,9 +635,6 @@ FunctionBlockBody: - end_kw: skip: rule: End - fields: - - scope_id: - kind: usize # Class constructs ClassConstructor: