Move parameter gathering to second pass in order to match analysis of all locals there.
This commit is contained in:
parent
13330300c1
commit
0550df534e
@ -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();
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user