use crate::ast::node::{ AnySpaceSuffixOperator, AssignmentStatement, BacktickString, BoundSuffixOperator, Call, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, DString, Expression, ExpressionList, ExpressionStatement, Function, FunctionAliasBody, FunctionBlockBody, FunctionBody, FunctionEqualsBody, GenericParameters, Identifier, IdentifierExpression, IdentifierOrFqn, LValue, LValueSuffix, Literal, ModuleLevelDeclaration, NoNewlineSuffixOperator, 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; 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::{ 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( compilation_unit: &mut CompilationUnit, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { for use_statement in compilation_unit.use_statements_mut() { na_p2_use_statement(use_statement, symbol_table, diagnostics); } for declaration in compilation_unit.module_level_declarations_mut() { na_p2_module_level_declaration(declaration, symbol_table, diagnostics); } } fn na_p2_use_statement( use_statement: &mut UseStatement, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { match use_statement { UseStatement::ConcreteUseStatement(concrete_use_statement) => { na_p2_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics); } UseStatement::StarUseStatement(star_use_statement) => { na_p2_star_use_statement(star_use_statement, symbol_table, diagnostics); } } } fn na_p2_concrete_use_statement( concrete_use_statement: &mut ConcreteUseStatement, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { let base_fqn_parts = concrete_use_statement .prefixes() .map(UseStatementPrefix::identifier) .map(Identifier::name) .map(Rc::from) .collect::>>(); match concrete_use_statement.suffix_mut() { ConcreteUseStatementSuffix::UseStatementIdentifier(use_statement_identifier) => { handle_concrete_use_statement_identifier( &base_fqn_parts, use_statement_identifier, symbol_table, diagnostics, ); } ConcreteUseStatementSuffix::UseList(use_list) => { for use_statement_identifier in use_list.identifiers_mut() { handle_concrete_use_statement_identifier( &base_fqn_parts, use_statement_identifier, symbol_table, diagnostics, ); } } } } fn handle_concrete_use_statement_identifier( base_fqn_parts: &[Rc], use_statement_identifier: &mut UseStatementIdentifier, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { let fqn_parts = { let mut all_parts = base_fqn_parts.to_vec(); all_parts.push(Rc::from(use_statement_identifier.identifier().name())); all_parts }; let maybe_usable_symbol = symbol_table.find_usable_symbol(&fqn_parts); if let Some(usable_symbol) = maybe_usable_symbol { use_statement_identifier .symbol_mut() .expect("Should have set the symbol on the use statement already.") .borrow_mut() .set_resolved_symbol(usable_symbol); } else { handle_lookup_error( SymbolLookupError::NoDefinition, &join_fqn_parts(&fqn_parts), use_statement_identifier.identifier().file_id(), use_statement_identifier.identifier().range(), diagnostics, ); } } fn na_p2_star_use_statement( star_use_statement: &mut StarUseStatement, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { let mut symbol_ref_mut = star_use_statement.symbol().unwrap().borrow_mut(); match symbol_table.find_usable_symbols_by_base_fqn(symbol_ref_mut.fqn_parts()) { Ok(usable_symbols) => { symbol_ref_mut.set_resolved_symbols(usable_symbols); } Err(symbol_lookup_error) => { handle_lookup_error( symbol_lookup_error, &join_fqn_parts(symbol_ref_mut.fqn_parts()), star_use_statement.file_id(), star_use_statement.range(), diagnostics, ); } } } fn na_p2_module_level_declaration( module_level_declaration: &mut ModuleLevelDeclaration, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { match module_level_declaration { ModuleLevelDeclaration::Module(module_declaration) => { todo!() } ModuleLevelDeclaration::Interface(interface) => { todo!() } ModuleLevelDeclaration::Class(class) => { todo!() } ModuleLevelDeclaration::Function(function) => { na_p2_function(function, symbol_table, diagnostics); } ModuleLevelDeclaration::PlatformFunction(platform_function) => { na_p2_platform_function(platform_function, symbol_table, diagnostics); } } } fn na_p2_function( function: &mut 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_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, 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, diagnostics: &mut Vec, ) { match function_body { FunctionBody::FunctionAliasBody(alias_body) => { na_p2_function_alias_body(alias_body, symbol_table, diagnostics); } 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); } } } fn na_p2_function_alias_body( function_alias_body: &mut FunctionAliasBody, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { 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); } Err(symbol_lookup_error) => { handle_lookup_error( symbol_lookup_error, function_alias_body.identifier().name(), function_alias_body.identifier().file_id(), function_alias_body.identifier().range(), diagnostics, ); } } } 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, ) { for statement in function_block_body.statements_mut() { na_p2_statement(statement, symbol_table, diagnostics); } } fn na_p2_statement( statement: &mut Statement, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { match statement { Statement::VariableDeclaration(variable_declaration) => { na_p2_variable_declaration(variable_declaration, symbol_table, diagnostics); } Statement::AssignmentStatement(assignment_statement) => { na_p2_assignment_statement(assignment_statement, symbol_table, diagnostics); } Statement::ExpressionStatement(expression_statement) => { na_p2_expression_statement(expression_statement, symbol_table, diagnostics); } Statement::UseStatement(use_statement) => { todo!() } Statement::IfStatement(if_statement) => { todo!() } Statement::WhileStatement(while_statement) => { todo!() } Statement::ForStatement(for_statement) => { todo!() } } } fn na_p2_variable_declaration( variable_declaration: &mut VariableDeclaration, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { // handle variable itself let to_insert = VariableSymbol::new( variable_declaration.identifier().name(), variable_declaration.is_mut(), Some(SourceDefinition::from_identifier( variable_declaration.identifier(), )), ); match symbol_table.insert_variable_symbol(to_insert) { Ok(variable_symbol) => { variable_declaration.set_variable_symbol(variable_symbol); } Err(symbol_insert_error) => { handle_insert_error( symbol_insert_error, variable_declaration.identifier().name(), variable_declaration.identifier().file_id(), variable_declaration.identifier().range(), diagnostics, ); } } // 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); } } fn na_p2_assignment_statement( assignment_statement: &mut AssignmentStatement, 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, ); } fn na_p2_l_value( l_value: &mut LValue, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { 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) // 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( expression_statement: &mut ExpressionStatement, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { na_p2_expression( expression_statement.expression_mut(), symbol_table, diagnostics, ); } fn na_p2_expression( expression: &mut Expression, symbol_table: &mut SymbolTable, diagnostics: &mut Vec, ) { match expression { Expression::Ternary(ternary) => { todo!() } Expression::Or(or) => { todo!() } Expression::And(and) => { todo!() } Expression::Comparison(comparison) => { todo!() } Expression::Shift(shift) => { todo!() } Expression::Additive(additive) => { todo!() } Expression::Multiplicative(multiplicative) => { todo!() } Expression::Prefix(prefix) => { todo!() } Expression::Suffix(suffix) => { na_p2_suffix_expression(suffix, symbol_table, diagnostics); } Expression::Literal(literal) => { na_p2_literal(literal, symbol_table, diagnostics); } Expression::Identifier(identifier_expression) => { na_p2_identifier_expression(identifier_expression, symbol_table, diagnostics); } Expression::Fqn(fqn) => { todo!() } Expression::Closure(closure) => { todo!() } Expression::List(list) => { todo!() } } } 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::BoundSuffixOperator(bound_suffix) => { match bound_suffix { BoundSuffixOperator::PlusPlus => { // no-op } BoundSuffixOperator::MinusMinus => { // no-op } } } SuffixOperator::NoNewlineSuffixOperator(no_newline_suffix) => match no_newline_suffix { NoNewlineSuffixOperator::ObjectIndex(object_index) => { na_p2_object_index(object_index, symbol_table, diagnostics); } NoNewlineSuffixOperator::Call(call) => { na_p2_call(call, symbol_table, diagnostics); } }, SuffixOperator::AnySpaceSuffixOperator(any_space_suffix) => { match any_space_suffix { AnySpaceSuffixOperator::ObjectProperty(_) => { // no-op; this is checked during type checking } } } } } 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); } } 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, ); } }