Move parameter gathering to second pass in order to match analysis of all locals there.

This commit is contained in:
Jesse Brault 2025-11-01 12:23:41 -05:00
parent 13330300c1
commit 0550df534e
7 changed files with 415 additions and 302 deletions

View File

@ -43,7 +43,7 @@ pub fn name_analysis(paths: &[PathBuf]) -> Result<(), Box<dyn std::error::Error>
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<dyn std::error::Error>
}
}
}
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();

View File

@ -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<DmDiagnostic>,
) -> Vec<Rc<RefCell<ParameterSymbol>>> {
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<DmDiagnostic>,
) -> Option<Rc<RefCell<ParameterSymbol>>> {
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<DmDiagnostic>,
) -> Option<TypeSymbol> {
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<DmDiagnostic>,
) -> Option<TypeSymbol> {
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::<Vec<&str>>();
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<DmDiagnostic>,
) -> 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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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
}

View File

@ -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<CompilationUnit>,
compilation_units: &mut [CompilationUnit],
files: &'a F,
symbol_table: &mut SymbolTable,
) -> Vec<DmDiagnostic> {
@ -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<CompilationUnit> {
let mut compilation_units: Vec<CompilationUnit> = 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<CompilationUnit> {
) -> (Vec<CompilationUnit>, 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<CompilationUnit> {
) -> (Vec<CompilationUnit>, 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);
}

View File

@ -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<DmDiagnostic>,
) {
// 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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) -> Vec<Rc<RefCell<ParameterSymbol>>> {
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<DmDiagnostic>,
) -> Option<Rc<RefCell<ParameterSymbol>>> {
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<DmDiagnostic>,
) -> Option<TypeSymbol> {
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<DmDiagnostic>,
) -> Option<TypeSymbol> {
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::<Vec<&str>>();
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<DmDiagnostic>,
) -> 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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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<DmDiagnostic>,
) {
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!()
}
}
}

View File

@ -30,6 +30,14 @@ impl ParameterSymbol {
pub fn declared_name_owned(&self) -> Rc<str> {
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 {

View File

@ -267,8 +267,8 @@ impl SymbolTable {
todo!()
}
pub fn lookup_function_symbol(&self, declared_name: &str, scope_id: usize) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolLookupError> {
let mut current_scope: Option<&Scope> = self.scopes.get(scope_id);
pub fn lookup_function_symbol(&self, declared_name: &str) -> Result<Rc<RefCell<FunctionSymbol>>, 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());

View File

@ -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: