Compare commits
6 Commits
d50cc24d0a
...
d5bfe9ad28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5bfe9ad28 | ||
|
|
0b270c186b | ||
|
|
4dd3140538 | ||
|
|
639d052e9c | ||
|
|
34480a0870 | ||
|
|
9d3e906957 |
@ -12,6 +12,7 @@ use crate::ir::ir_assign::IrAssign;
|
|||||||
use crate::ir::ir_set_field::IrSetField;
|
use crate::ir::ir_set_field::IrSetField;
|
||||||
use crate::ir::ir_statement::IrStatement;
|
use crate::ir::ir_statement::IrStatement;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
use crate::symbol::class_symbol::ClassSymbol;
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
@ -36,6 +37,10 @@ impl AssignStatement {
|
|||||||
&self.destination
|
&self.destination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> &Expression {
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
self.destination.init_scopes(symbol_table, container_scope);
|
self.destination.init_scopes(symbol_table, container_scope);
|
||||||
self.value.init_scopes(symbol_table, container_scope);
|
self.value.init_scopes(symbol_table, container_scope);
|
||||||
@ -226,6 +231,7 @@ impl AssignStatement {
|
|||||||
(nodes_to_types, diagnostics)
|
(nodes_to_types, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn type_check(
|
pub fn type_check(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
@ -358,6 +364,7 @@ impl AssignStatement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn to_ir(
|
pub fn to_ir(
|
||||||
&self,
|
&self,
|
||||||
builder: &mut IrBuilder,
|
builder: &mut IrBuilder,
|
||||||
@ -399,6 +406,60 @@ impl AssignStatement {
|
|||||||
|
|
||||||
builder.current_block_mut().add_statement(ir_statement);
|
builder.current_block_mut().add_statement(ir_statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) {
|
||||||
|
let destination_expressible_symbol = match self.destination.as_ref() {
|
||||||
|
Expression::Identifier(identifier) => nodes_to_symbols
|
||||||
|
.get(&identifier.scope_id())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_expressible_symbol(),
|
||||||
|
_ => unreachable!("Destination must be a mutable L value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ir_statement = match destination_expressible_symbol {
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
let field_type = symbols_to_types
|
||||||
|
.get(&Symbol::Field(field_symbol.clone()))
|
||||||
|
.unwrap();
|
||||||
|
let mut_field_pointer_variable =
|
||||||
|
get_or_init_mut_field_pointer_variable(builder, &field_symbol, field_type)
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let ir_set_field = IrSetField::new(
|
||||||
|
&mut_field_pointer_variable,
|
||||||
|
self.value.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
IrStatement::SetField(ir_set_field)
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
let ir_variable = builder.local_variables().get(&variable_symbol).unwrap();
|
||||||
|
let ir_assign = IrAssign::new(
|
||||||
|
ir_variable.clone(),
|
||||||
|
self.value.lower_to_ir_operation(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
IrStatement::Assign(ir_assign)
|
||||||
|
}
|
||||||
|
_ => unreachable!("Destination must be a mutable L value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.current_block_mut().add_statement(ir_statement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -17,7 +17,6 @@ use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
|||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use crate::type_info::TypeInfo;
|
use crate::type_info::TypeInfo;
|
||||||
use crate::types_table::TypesTable;
|
use crate::types_table::TypesTable;
|
||||||
use std::thread::Builder;
|
|
||||||
|
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
|
|||||||
@ -540,12 +540,22 @@ impl Class {
|
|||||||
nodes_to_symbols: &NodesToSymbols,
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
symbols_to_types: &SymbolsToTypes,
|
symbols_to_types: &SymbolsToTypes,
|
||||||
nodes_to_types: &NodesToTypes,
|
nodes_to_types: &NodesToTypes,
|
||||||
function_return_types: &FunctionReturnTypes,
|
|
||||||
) -> (IrClass, Vec<IrFunction>) {
|
) -> (IrClass, Vec<IrFunction>) {
|
||||||
let mut ir_functions = Vec::new();
|
let mut ir_functions = Vec::new();
|
||||||
|
|
||||||
|
let self_class_symbol = nodes_to_symbols
|
||||||
|
.get(&self.node_id)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_class_symbol();
|
||||||
|
|
||||||
if let Some(constructor) = &self.constructor {
|
if let Some(constructor) = &self.constructor {
|
||||||
// todo
|
ir_functions.push(constructor.lower_to_ir(
|
||||||
|
self_class_symbol,
|
||||||
|
&self.fields,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for function in &self.functions {
|
for function in &self.functions {
|
||||||
@ -553,15 +563,9 @@ impl Class {
|
|||||||
nodes_to_symbols,
|
nodes_to_symbols,
|
||||||
symbols_to_types,
|
symbols_to_types,
|
||||||
nodes_to_types,
|
nodes_to_types,
|
||||||
function_return_types,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let self_class_symbol = nodes_to_symbols
|
|
||||||
.get(&self.node_id)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap_class_symbol();
|
|
||||||
|
|
||||||
let ir_class = IrClass::new(
|
let ir_class = IrClass::new(
|
||||||
self_class_symbol.declared_name_owned(),
|
self_class_symbol.declared_name_owned(),
|
||||||
fqn_parts_to_string(self_class_symbol.fqn_parts()).into(),
|
fqn_parts_to_string(self_class_symbol.fqn_parts()).into(),
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use crate::ast::function::Function;
|
|||||||
use crate::ast::helpers::{
|
use crate::ast::helpers::{
|
||||||
collect_diagnostics_into_mut, insert_declared_types_into, insert_resolved_types_into,
|
collect_diagnostics_into_mut, insert_declared_types_into, insert_resolved_types_into,
|
||||||
};
|
};
|
||||||
use crate::ast::{FunctionReturnTypes, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
use crate::ast::{NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
use crate::compile_pipeline::FileId;
|
use crate::compile_pipeline::FileId;
|
||||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::ir::ir_class::IrClass;
|
use crate::ir::ir_class::IrClass;
|
||||||
@ -159,17 +159,26 @@ impl CompilationUnit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Associate each declared symbol with a TypeInfo.
|
/// Associate each declared symbol with a TypeInfo.
|
||||||
pub fn declared_types(&self, names_table: &NodesToSymbols) -> (SymbolsToTypes, Diagnostics) {
|
pub fn declared_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
) -> (SymbolsToTypes, Diagnostics) {
|
||||||
let mut diagnostics = Diagnostics::new();
|
let mut diagnostics = Diagnostics::new();
|
||||||
let mut declared_types = SymbolsToTypes::new();
|
let mut symbols_to_types = SymbolsToTypes::new();
|
||||||
|
|
||||||
for function in &self.functions {
|
for function in &self.functions {
|
||||||
let (dts, mut ds) = function.declared_types(names_table);
|
let (sts, mut ds) = function.declared_types(nodes_to_symbols);
|
||||||
insert_declared_types_into(dts, &mut declared_types);
|
insert_declared_types_into(sts, &mut symbols_to_types);
|
||||||
diagnostics.append(&mut ds);
|
diagnostics.append(&mut ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
(declared_types, diagnostics)
|
for extern_function in &self.extern_functions {
|
||||||
|
let (sts, mut ds) = extern_function.declared_types(nodes_to_symbols);
|
||||||
|
insert_declared_types_into(sts, &mut symbols_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(symbols_to_types, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve types of all nodes that have an implicit (perhaps not declared) type, checking that
|
/// Resolve types of all nodes that have an implicit (perhaps not declared) type, checking that
|
||||||
@ -250,7 +259,6 @@ impl CompilationUnit {
|
|||||||
nodes_to_symbols: &NodesToSymbols,
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
symbols_to_types: &SymbolsToTypes,
|
symbols_to_types: &SymbolsToTypes,
|
||||||
nodes_to_types: &NodesToTypes,
|
nodes_to_types: &NodesToTypes,
|
||||||
function_return_types: &FunctionReturnTypes,
|
|
||||||
) -> (Vec<IrClass>, Vec<IrFunction>) {
|
) -> (Vec<IrClass>, Vec<IrFunction>) {
|
||||||
let mut ir_classes = Vec::new();
|
let mut ir_classes = Vec::new();
|
||||||
let mut ir_functions = Vec::new();
|
let mut ir_functions = Vec::new();
|
||||||
@ -260,17 +268,12 @@ impl CompilationUnit {
|
|||||||
nodes_to_symbols,
|
nodes_to_symbols,
|
||||||
symbols_to_types,
|
symbols_to_types,
|
||||||
nodes_to_types,
|
nodes_to_types,
|
||||||
function_return_types,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for class in &self.classes {
|
for class in &self.classes {
|
||||||
let (ir_class, mut class_ir_functions) = class.lower_to_ir(
|
let (ir_class, mut class_ir_functions) =
|
||||||
nodes_to_symbols,
|
class.lower_to_ir(nodes_to_symbols, symbols_to_types, nodes_to_types);
|
||||||
symbols_to_types,
|
|
||||||
nodes_to_types,
|
|
||||||
function_return_types,
|
|
||||||
);
|
|
||||||
ir_classes.push(ir_class);
|
ir_classes.push(ir_class);
|
||||||
ir_functions.append(&mut class_ir_functions);
|
ir_functions.append(&mut class_ir_functions);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -425,7 +425,7 @@ impl Constructor {
|
|||||||
|
|
||||||
// PART 3. Constructor statements
|
// PART 3. Constructor statements
|
||||||
for statement in &self.statements {
|
for statement in &self.statements {
|
||||||
statement.lower(
|
statement.lower_to_ir(
|
||||||
&mut ir_builder,
|
&mut ir_builder,
|
||||||
nodes_to_symbols,
|
nodes_to_symbols,
|
||||||
symbols_to_types,
|
symbols_to_types,
|
||||||
|
|||||||
@ -140,4 +140,25 @@ impl ExpressionStatement {
|
|||||||
.add_statement(IrStatement::Return(IrReturn::new(ir_expression)));
|
.add_statement(IrStatement::Return(IrReturn::new(ir_expression)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
is_return_statement: bool,
|
||||||
|
) {
|
||||||
|
let ir_expression = self.expression.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
if is_return_statement {
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Return(IrReturn::new(Some(ir_expression))));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::ast::helpers::{
|
|||||||
};
|
};
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
use crate::ast::{NodeId, NodesToSymbols};
|
use crate::ast::{NodeId, NodesToSymbols, SymbolsToTypes};
|
||||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol::Symbol;
|
use crate::symbol::Symbol;
|
||||||
@ -42,10 +42,30 @@ impl ExternFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn node_id(&self) -> NodeId {
|
||||||
|
self.node_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn declared_name(&self) -> &str {
|
pub fn declared_name(&self) -> &str {
|
||||||
&self.declared_name
|
&self.declared_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
self.declared_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_source_range(&self) -> SourceRange {
|
||||||
|
self.declared_name_source_range.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parameters(&self) -> &[Parameter] {
|
||||||
|
&self.parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn return_type(&self) -> &TypeUse {
|
||||||
|
&self.return_type
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
self.scope_id = Some(container_scope);
|
self.scope_id = Some(container_scope);
|
||||||
|
|
||||||
@ -105,6 +125,23 @@ impl ExternFunction {
|
|||||||
(names_table, diagnostics)
|
(names_table, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declared_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
) -> (SymbolsToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut symbols_to_types = SymbolsToTypes::new();
|
||||||
|
|
||||||
|
for parameter in &self.parameters {
|
||||||
|
let (type_info, mut ds) = parameter.declared_type(nodes_to_symbols);
|
||||||
|
let parameter_symbol = nodes_to_symbols.get(¶meter.node_id()).unwrap();
|
||||||
|
symbols_to_types.insert(parameter_symbol.clone(), type_info);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(symbols_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||||
for parameter in &self.parameters {
|
for parameter in &self.parameters {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::ast::ir_builder::IrBuilder;
|
|||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
use crate::ast::{FunctionReturnTypes, NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::ir::ir_function::IrFunction;
|
use crate::ir::ir_function::IrFunction;
|
||||||
use crate::ir::ir_parameter::IrParameter;
|
use crate::ir::ir_parameter::IrParameter;
|
||||||
@ -69,6 +69,22 @@ impl Function {
|
|||||||
&self.declared_name
|
&self.declared_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
self.declared_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_source_range(&self) -> SourceRange {
|
||||||
|
self.declared_name_source_range.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parameters(&self) -> &[Parameter] {
|
||||||
|
&self.parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn return_type(&self) -> Option<&TypeUse> {
|
||||||
|
self.return_type.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn statements(&self) -> Vec<&Statement> {
|
pub fn statements(&self) -> Vec<&Statement> {
|
||||||
self.statements.iter().collect()
|
self.statements.iter().collect()
|
||||||
}
|
}
|
||||||
@ -150,6 +166,12 @@ impl Function {
|
|||||||
diagnostics.append(&mut ds);
|
diagnostics.append(&mut ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert self function symbol with this node
|
||||||
|
let function_symbol = symbol_table
|
||||||
|
.get_function_symbol_owned(self.container_scope_id.unwrap(), &self.declared_name)
|
||||||
|
.unwrap();
|
||||||
|
nodes_to_symbols.insert(self.node_id, Symbol::Function(function_symbol));
|
||||||
|
|
||||||
(nodes_to_symbols, diagnostics)
|
(nodes_to_symbols, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,18 +238,33 @@ impl Function {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declared_types(&self, names_table: &NodesToSymbols) -> (SymbolsToTypes, Diagnostics) {
|
pub fn declared_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
) -> (SymbolsToTypes, Diagnostics) {
|
||||||
let mut diagnostics = Diagnostics::new();
|
let mut diagnostics = Diagnostics::new();
|
||||||
let mut declared_types = SymbolsToTypes::new();
|
let mut symbols_to_types = SymbolsToTypes::new();
|
||||||
|
|
||||||
for parameter in &self.parameters {
|
for parameter in &self.parameters {
|
||||||
let symbol = names_table.get(¶meter.node_id()).unwrap();
|
let symbol = nodes_to_symbols.get(¶meter.node_id()).unwrap();
|
||||||
let (type_info, mut ds) = parameter.declared_type(names_table);
|
let (type_info, mut ds) = parameter.declared_type(nodes_to_symbols);
|
||||||
declared_types.insert(symbol.clone(), type_info);
|
symbols_to_types.insert(symbol.clone(), type_info);
|
||||||
diagnostics.append(&mut ds);
|
diagnostics.append(&mut ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
(declared_types, diagnostics)
|
// Insert return type as the type of this function symbol
|
||||||
|
let symbol = nodes_to_symbols.get(&self.node_id).unwrap().clone();
|
||||||
|
let type_info = match &self.return_type {
|
||||||
|
None => TypeInfo::Void,
|
||||||
|
Some(type_use) => {
|
||||||
|
let (type_info, mut ds) = type_use.declared_type(nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
type_info
|
||||||
|
}
|
||||||
|
};
|
||||||
|
symbols_to_types.insert(symbol, type_info);
|
||||||
|
|
||||||
|
(symbols_to_types, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_types(
|
pub fn resolve_types(
|
||||||
@ -462,7 +499,6 @@ impl Function {
|
|||||||
nodes_to_symbols: &NodesToSymbols,
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
symbols_to_types: &SymbolsToTypes,
|
symbols_to_types: &SymbolsToTypes,
|
||||||
nodes_to_types: &NodesToTypes,
|
nodes_to_types: &NodesToTypes,
|
||||||
function_return_types: &FunctionReturnTypes,
|
|
||||||
) -> IrFunction {
|
) -> IrFunction {
|
||||||
let mut builder = IrBuilder::new();
|
let mut builder = IrBuilder::new();
|
||||||
|
|
||||||
@ -488,11 +524,13 @@ impl Function {
|
|||||||
let entry_block_id = builder.new_block();
|
let entry_block_id = builder.new_block();
|
||||||
|
|
||||||
// lower statements
|
// lower statements
|
||||||
let return_type_info = function_return_types.get(function_symbol).unwrap();
|
let return_type_info = symbols_to_types
|
||||||
|
.get(&Symbol::Function(function_symbol.clone()))
|
||||||
|
.unwrap();
|
||||||
let should_return_value = !matches!(return_type_info, TypeInfo::Void);
|
let should_return_value = !matches!(return_type_info, TypeInfo::Void);
|
||||||
for (i, statement) in self.statements.iter().enumerate() {
|
for (i, statement) in self.statements.iter().enumerate() {
|
||||||
let is_last = i == self.statements.len() - 1;
|
let is_last = i == self.statements.len() - 1;
|
||||||
statement.lower(
|
statement.lower_to_ir(
|
||||||
&mut builder,
|
&mut builder,
|
||||||
nodes_to_symbols,
|
nodes_to_symbols,
|
||||||
symbols_to_types,
|
symbols_to_types,
|
||||||
|
|||||||
@ -75,6 +75,7 @@ impl Identifier {
|
|||||||
(names_table, diagnostics)
|
(names_table, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the name inside an initializer for a field *outside* a constructor.
|
||||||
pub fn resolve_name_field_init(
|
pub fn resolve_name_field_init(
|
||||||
&self,
|
&self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
@ -125,6 +126,7 @@ impl Identifier {
|
|||||||
(names_table, diagnostics)
|
(names_table, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the name as used in a rhs expression in a constructor.
|
||||||
pub fn resolve_name_ctor(
|
pub fn resolve_name_ctor(
|
||||||
&self,
|
&self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
@ -173,6 +175,7 @@ impl Identifier {
|
|||||||
(names_table, diagnostics)
|
(names_table, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the name as an LValue in a constructor.
|
||||||
pub fn resolve_name_ctor_destination(
|
pub fn resolve_name_ctor_destination(
|
||||||
&self,
|
&self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
@ -509,11 +512,11 @@ impl Identifier {
|
|||||||
|
|
||||||
pub fn resolve_type(
|
pub fn resolve_type(
|
||||||
&self,
|
&self,
|
||||||
resolved_symbols: &NodesToSymbols,
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
resolved_symbol_type_infos: &SymbolsToTypes,
|
symbols_to_types: &SymbolsToTypes,
|
||||||
) -> (NodesToTypes, Diagnostics) {
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
let self_symbol = resolved_symbols.get(&self.node_id).unwrap();
|
let self_symbol = nodes_to_symbols.get(&self.node_id).unwrap();
|
||||||
let type_info = resolved_symbol_type_infos.get(self_symbol).unwrap();
|
let type_info = symbols_to_types.get(self_symbol).unwrap();
|
||||||
|
|
||||||
let mut resolved_types = NodesToTypes::new();
|
let mut resolved_types = NodesToTypes::new();
|
||||||
resolved_types.insert(self.node_id, type_info.clone());
|
resolved_types.insert(self.node_id, type_info.clone());
|
||||||
@ -650,3 +653,51 @@ impl Identifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ast::identifier::Identifier;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::variable_symbol::VariableSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inits_scope_id() {
|
||||||
|
let mut identifier = Identifier::new(0, "foo", SourceRange::new(0, 0));
|
||||||
|
identifier.init_scope_id(42);
|
||||||
|
assert_eq!(identifier.scope_id(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolves_static_foo() {
|
||||||
|
let mut identifier = Identifier::new(0, "foo", SourceRange::new(0, 0));
|
||||||
|
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
symbol_table.push_module_scope("foo module");
|
||||||
|
symbol_table.push_function_scope("foo function");
|
||||||
|
let scope_id = symbol_table.push_block_scope("foo block");
|
||||||
|
identifier.init_scope_id(scope_id);
|
||||||
|
|
||||||
|
let variable_symbol = Rc::new(VariableSymbol::new(
|
||||||
|
&"foo".into(),
|
||||||
|
&SourceRange::new(0, 0),
|
||||||
|
false,
|
||||||
|
scope_id,
|
||||||
|
));
|
||||||
|
symbol_table.insert_variable_symbol(variable_symbol.clone());
|
||||||
|
|
||||||
|
let (nodes_to_symbols, diagnostics) = identifier.resolve_name_static(&symbol_table);
|
||||||
|
|
||||||
|
assert_eq!(diagnostics.len(), 0);
|
||||||
|
assert_eq!(nodes_to_symbols.len(), 1);
|
||||||
|
let symbol = nodes_to_symbols.get(&identifier.node_id()).unwrap();
|
||||||
|
match symbol {
|
||||||
|
Symbol::Variable(matched_variable_symbol) => {
|
||||||
|
assert_eq!(&variable_symbol, matched_variable_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -44,10 +44,22 @@ impl LetStatement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn node_id(&self) -> NodeId {
|
||||||
|
self.node_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn declared_name(&self) -> &str {
|
pub fn declared_name(&self) -> &str {
|
||||||
&self.declared_name
|
&self.declared_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
self.declared_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_source_range(&self) -> SourceRange {
|
||||||
|
self.declared_name_source_range.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn initializer(&self) -> &Expression {
|
pub fn initializer(&self) -> &Expression {
|
||||||
&self.initializer
|
&self.initializer
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,14 @@ impl Parameter {
|
|||||||
&self.declared_name
|
&self.declared_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
self.declared_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_source_range(&self) -> SourceRange {
|
||||||
|
self.declared_name_source_range.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scope_id(&self) -> usize {
|
pub fn scope_id(&self) -> usize {
|
||||||
self.scope_id.unwrap()
|
self.scope_id.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -199,7 +199,7 @@ impl Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lower(
|
pub fn lower_to_ir(
|
||||||
&self,
|
&self,
|
||||||
builder: &mut IrBuilder,
|
builder: &mut IrBuilder,
|
||||||
nodes_to_symbols: &NodesToSymbols,
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
@ -211,8 +211,23 @@ impl Statement {
|
|||||||
Statement::Let(let_statement) => {
|
Statement::Let(let_statement) => {
|
||||||
let_statement.lower(builder, nodes_to_symbols, symbols_to_types, nodes_to_types);
|
let_statement.lower(builder, nodes_to_symbols, symbols_to_types, nodes_to_types);
|
||||||
}
|
}
|
||||||
Statement::Expression(expression_statement) => {}
|
Statement::Expression(expression_statement) => {
|
||||||
Statement::Assign(assign_statement) => {}
|
expression_statement.lower_to_ir(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
is_return_statement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
assign_statement.lower_to_ir(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,10 @@ impl TypeUse {
|
|||||||
&self.declared_name
|
&self.declared_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn node_id(&self) -> NodeId {
|
||||||
|
self.node_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
self.scope_id = Some(container_scope);
|
self.scope_id = Some(container_scope);
|
||||||
for type_use in &mut self.generic_arguments {
|
for type_use in &mut self.generic_arguments {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
use crate::ast::compilation_unit::CompilationUnit;
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
use crate::diagnostic::Diagnostics;
|
use crate::diagnostic::Diagnostics;
|
||||||
|
use crate::ir::ir_class::IrClass;
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
use crate::parser::parse_compilation_unit;
|
use crate::parser::parse_compilation_unit;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use crate::symbol_table::util::try_insert_symbols_into;
|
use crate::symbol_table::util::try_insert_symbols_into;
|
||||||
@ -26,14 +28,15 @@ fn parse_compilation_units(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_compilation_units(inputs: &HashMap<FileId, &str>) -> Result<(), Diagnostics> {
|
pub fn compile_compilation_units(
|
||||||
|
inputs: &HashMap<FileId, &str>,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<(Vec<IrClass>, Vec<IrFunction>), Diagnostics> {
|
||||||
let mut compilation_units = parse_compilation_units(inputs)?;
|
let mut compilation_units = parse_compilation_units(inputs)?;
|
||||||
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
|
|
||||||
// init scopes
|
// init scopes
|
||||||
for compilation_unit in compilation_units.values_mut() {
|
for compilation_unit in compilation_units.values_mut() {
|
||||||
compilation_unit.init_scopes(&mut symbol_table);
|
compilation_unit.init_scopes(symbol_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
// gather unordered symbols
|
// gather unordered symbols
|
||||||
@ -41,12 +44,14 @@ pub fn compile_compilation_units(inputs: &HashMap<FileId, &str>) -> Result<(), D
|
|||||||
.values()
|
.values()
|
||||||
.flat_map(|compilation_unit| compilation_unit.declared_symbols())
|
.flat_map(|compilation_unit| compilation_unit.declared_symbols())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
try_insert_symbols_into(all_symbols, &mut symbol_table)?;
|
try_insert_symbols_into(all_symbols, symbol_table)?;
|
||||||
|
|
||||||
// now we can just finish each compilation unit, since we have the symbols
|
// now we can just finish each compilation unit, since we have the symbols
|
||||||
|
let mut ir_classes = Vec::new();
|
||||||
|
let mut ir_functions = Vec::new();
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
for compilation_unit in compilation_units.values() {
|
for compilation_unit in compilation_units.values() {
|
||||||
let (nodes_to_symbols, mut ds) = compilation_unit.resolve_names(&mut symbol_table);
|
let (nodes_to_symbols, mut ds) = compilation_unit.resolve_names(symbol_table);
|
||||||
|
|
||||||
// in the future, we'll ideally be able to *actually* continue with the following steps
|
// in the future, we'll ideally be able to *actually* continue with the following steps
|
||||||
// instead of aborting here, but this needs to be tested :)
|
// instead of aborting here, but this needs to be tested :)
|
||||||
@ -61,7 +66,7 @@ pub fn compile_compilation_units(inputs: &HashMap<FileId, &str>) -> Result<(), D
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sts, resolved_types, mut ds) =
|
let (sts, nodes_to_types, mut ds) =
|
||||||
compilation_unit.resolve_types(&nodes_to_symbols, &symbols_to_types);
|
compilation_unit.resolve_types(&nodes_to_symbols, &symbols_to_types);
|
||||||
if !ds.is_empty() {
|
if !ds.is_empty() {
|
||||||
diagnostics.append(&mut ds);
|
diagnostics.append(&mut ds);
|
||||||
@ -72,7 +77,70 @@ pub fn compile_compilation_units(inputs: &HashMap<FileId, &str>) -> Result<(), D
|
|||||||
for (symbol, type_info) in sts {
|
for (symbol, type_info) in sts {
|
||||||
symbols_to_types.insert(symbol, type_info);
|
symbols_to_types.insert(symbol, type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (mut classes, mut functions) =
|
||||||
|
compilation_unit.lower_to_ir(&nodes_to_symbols, &symbols_to_types, &nodes_to_types);
|
||||||
|
ir_classes.append(&mut classes);
|
||||||
|
ir_functions.append(&mut functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
if diagnostics.is_empty() {
|
||||||
|
Ok((ir_classes, ir_functions))
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
|
||||||
|
fn prepare_symbol_table(symbol_table: &mut SymbolTable) {
|
||||||
|
let global_scope = symbol_table.push_module_scope("global scope");
|
||||||
|
let any_symbol = ClassSymbol::new(
|
||||||
|
&"Any".into(),
|
||||||
|
None,
|
||||||
|
vec!["Any".into()],
|
||||||
|
false,
|
||||||
|
global_scope,
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
symbol_table.insert_class_symbol(Rc::new(any_symbol));
|
||||||
|
|
||||||
|
let void_symbol = ClassSymbol::new(
|
||||||
|
&"Void".into(),
|
||||||
|
None,
|
||||||
|
vec!["Void".into()],
|
||||||
|
false,
|
||||||
|
global_scope,
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
symbol_table.insert_class_symbol(Rc::new(void_symbol));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hello_world() -> Result<(), Diagnostics> {
|
||||||
|
let input = "
|
||||||
|
extern fn println(msg: Any) -> Void
|
||||||
|
|
||||||
|
fn main()
|
||||||
|
println(\"Hello, World!\")
|
||||||
|
end
|
||||||
|
";
|
||||||
|
let mut inputs = HashMap::new();
|
||||||
|
inputs.insert(0, input);
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
prepare_symbol_table(&mut symbol_table);
|
||||||
|
let (ir_classes, ir_functions) = compile_compilation_units(&inputs, &mut symbol_table)?;
|
||||||
|
assert_eq!(ir_classes.len(), 0);
|
||||||
|
assert_eq!(ir_functions.len(), 1);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ pub mod lexer;
|
|||||||
pub mod offset_counter;
|
pub mod offset_counter;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
|
pub mod semantic_analysis;
|
||||||
pub mod source_range;
|
pub mod source_range;
|
||||||
pub mod symbol;
|
pub mod symbol;
|
||||||
pub mod symbol_table;
|
pub mod symbol_table;
|
||||||
|
|||||||
207
dmc-lib/src/semantic_analysis/collect_scopes.rs
Normal file
207
dmc-lib/src/semantic_analysis/collect_scopes.rs
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
use crate::ast::NodeId;
|
||||||
|
use crate::ast::assign_statement::AssignStatement;
|
||||||
|
use crate::ast::binary_expression::BinaryExpression;
|
||||||
|
use crate::ast::call::Call;
|
||||||
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::expression_statement::ExpressionStatement;
|
||||||
|
use crate::ast::extern_function::ExternFunction;
|
||||||
|
use crate::ast::function::Function;
|
||||||
|
use crate::ast::identifier::Identifier;
|
||||||
|
use crate::ast::let_statement::LetStatement;
|
||||||
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
|
use crate::ast::statement::Statement;
|
||||||
|
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct ScopeCollectionContext {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
current_scope_id: Option<ScopeId>,
|
||||||
|
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScopeCollectionContext {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
scopes: Vec::new(),
|
||||||
|
current_scope_id: None,
|
||||||
|
nodes_to_scopes: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_scope(&mut self, scope: Scope) -> ScopeId {
|
||||||
|
self.scopes.push(scope);
|
||||||
|
self.current_scope_id = Some(self.scopes.len() - 1);
|
||||||
|
self.current_scope_id.unwrap() // guaranteed because we just set it
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_scope(&mut self) {
|
||||||
|
let popped_scope = self.scopes.pop().unwrap();
|
||||||
|
self.current_scope_id = popped_scope.parent_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_scope_id(&self) -> Option<ScopeId> {
|
||||||
|
self.current_scope_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes_to_scopes(&self) -> &HashMap<NodeId, ScopeId> {
|
||||||
|
&self.nodes_to_scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes_to_scopes_mut(&mut self) -> &mut HashMap<NodeId, ScopeId> {
|
||||||
|
&mut self.nodes_to_scopes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_scopes(compilation_unit: &CompilationUnit) -> ScopeCollectionContext {
|
||||||
|
let mut ctx = ScopeCollectionContext::new();
|
||||||
|
ctx.push_scope(Scope::new(None));
|
||||||
|
for function in compilation_unit.functions() {
|
||||||
|
collect_scopes_function(function, &mut ctx);
|
||||||
|
}
|
||||||
|
for extern_function in compilation_unit.extern_functions() {
|
||||||
|
collect_scopes_extern_function(extern_function, &mut ctx);
|
||||||
|
}
|
||||||
|
ctx.pop_scope();
|
||||||
|
ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext) {
|
||||||
|
// get containing scope id
|
||||||
|
let containing_scope_id = ctx.current_scope_id().unwrap(); // guaranteed because functions are in modules
|
||||||
|
|
||||||
|
// save function's containing scope id
|
||||||
|
ctx.nodes_to_scopes_mut()
|
||||||
|
.insert(function.node_id(), containing_scope_id);
|
||||||
|
|
||||||
|
// push function scope
|
||||||
|
let function_scope = Scope::new(Some(containing_scope_id));
|
||||||
|
let function_scope_id = ctx.push_scope(function_scope);
|
||||||
|
|
||||||
|
// save return-type and parameters' scope id
|
||||||
|
for parameter in function.parameters() {
|
||||||
|
ctx.nodes_to_scopes_mut()
|
||||||
|
.insert(parameter.node_id(), function_scope_id);
|
||||||
|
}
|
||||||
|
match function.return_type() {
|
||||||
|
None => {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
Some(type_use) => {
|
||||||
|
ctx.nodes_to_scopes_mut()
|
||||||
|
.insert(type_use.node_id(), function_scope_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push block scope for body
|
||||||
|
let block_scope = Scope::new(Some(function_scope_id));
|
||||||
|
ctx.push_scope(block_scope);
|
||||||
|
|
||||||
|
for statement in function.statements() {
|
||||||
|
collect_scopes_statement(statement, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.pop_scope(); // block
|
||||||
|
ctx.pop_scope(); // function
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_extern_function(
|
||||||
|
extern_function: &ExternFunction,
|
||||||
|
ctx: &mut ScopeCollectionContext,
|
||||||
|
) {
|
||||||
|
let containing_scope_id = ctx.current_scope_id().unwrap();
|
||||||
|
|
||||||
|
ctx.nodes_to_scopes_mut()
|
||||||
|
.insert(extern_function.node_id(), containing_scope_id);
|
||||||
|
|
||||||
|
let function_scope = Scope::new(Some(containing_scope_id));
|
||||||
|
let function_scope_id = ctx.push_scope(function_scope);
|
||||||
|
|
||||||
|
for parameter in extern_function.parameters() {
|
||||||
|
ctx.nodes_to_scopes_mut()
|
||||||
|
.insert(parameter.node_id(), function_scope_id);
|
||||||
|
}
|
||||||
|
ctx.nodes_to_scopes_mut()
|
||||||
|
.insert(extern_function.return_type().node_id(), function_scope_id);
|
||||||
|
|
||||||
|
ctx.pop_scope();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_statement(statement: &Statement, ctx: &mut ScopeCollectionContext) {
|
||||||
|
match statement {
|
||||||
|
Statement::Let(let_statement) => collect_scopes_let_statement(let_statement, ctx),
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
collect_scopes_expression_statement(expression_statement, ctx)
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
collect_scopes_assign_statement(assign_statement, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_let_statement(let_statement: &LetStatement, ctx: &mut ScopeCollectionContext) {
|
||||||
|
collect_scopes_expression(let_statement.initializer(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_expression_statement(
|
||||||
|
expression_statement: &ExpressionStatement,
|
||||||
|
ctx: &mut ScopeCollectionContext,
|
||||||
|
) {
|
||||||
|
collect_scopes_expression(expression_statement.expression(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_assign_statement(
|
||||||
|
assign_statement: &AssignStatement,
|
||||||
|
ctx: &mut ScopeCollectionContext,
|
||||||
|
) {
|
||||||
|
collect_scopes_expression(assign_statement.value(), ctx);
|
||||||
|
collect_scopes_expression(assign_statement.destination(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_expression(expression: &Expression, ctx: &mut ScopeCollectionContext) {
|
||||||
|
match expression {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
collect_scopes_binary_expression(binary_expression, ctx);
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
collect_scopes_negative_expression(negative_expression, ctx);
|
||||||
|
}
|
||||||
|
Expression::Call(call) => {
|
||||||
|
collect_scopes_call(call, ctx);
|
||||||
|
}
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
collect_scopes_identifier(identifier, ctx);
|
||||||
|
}
|
||||||
|
Expression::Integer(_) => {}
|
||||||
|
Expression::Double(_) => {}
|
||||||
|
Expression::String(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_binary_expression(
|
||||||
|
binary_expression: &BinaryExpression,
|
||||||
|
ctx: &mut ScopeCollectionContext,
|
||||||
|
) {
|
||||||
|
collect_scopes_expression(binary_expression.lhs(), ctx);
|
||||||
|
collect_scopes_expression(binary_expression.rhs(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_negative_expression(
|
||||||
|
negative_expression: &NegativeExpression,
|
||||||
|
ctx: &mut ScopeCollectionContext,
|
||||||
|
) {
|
||||||
|
collect_scopes_expression(negative_expression.operand(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_call(call: &Call, ctx: &mut ScopeCollectionContext) {
|
||||||
|
for argument in call.arguments() {
|
||||||
|
collect_scopes_expression(argument, ctx);
|
||||||
|
}
|
||||||
|
collect_scopes_expression(call.callee(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut ScopeCollectionContext) {
|
||||||
|
let current_scope_id = ctx.current_scope_id().unwrap(); // if this fails, we tried to do this without any context
|
||||||
|
ctx.nodes_to_scopes_mut()
|
||||||
|
.insert(identifier.node_id(), current_scope_id);
|
||||||
|
}
|
||||||
122
dmc-lib/src/semantic_analysis/collect_symbols.rs
Normal file
122
dmc-lib/src/semantic_analysis/collect_symbols.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
use crate::ast::NodeId;
|
||||||
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
|
use crate::ast::extern_function::ExternFunction;
|
||||||
|
use crate::ast::function::Function;
|
||||||
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::diagnostic::Diagnostics;
|
||||||
|
use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared;
|
||||||
|
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
||||||
|
use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct SymbolCollectionContext {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||||
|
symbols: Vec<Symbol>,
|
||||||
|
diagnostics: Diagnostics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolCollectionContext {
|
||||||
|
pub fn new(scopes: Vec<Scope>, nodes_to_scopes: HashMap<NodeId, ScopeId>) -> Self {
|
||||||
|
Self {
|
||||||
|
scopes,
|
||||||
|
nodes_to_scopes,
|
||||||
|
symbols: Vec::new(),
|
||||||
|
diagnostics: Diagnostics::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_symbol_in_scope_for(&self, name: &str, node_id: NodeId) -> Option<&Symbol> {
|
||||||
|
let scope_id = self.nodes_to_scopes[&node_id];
|
||||||
|
let scope = &self.scopes[scope_id];
|
||||||
|
if let Some(symbol_id) = scope.symbols().get(name) {
|
||||||
|
Some(&self.symbols[*symbol_id])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_symbol(&mut self, symbol: Symbol, node_id: NodeId) {
|
||||||
|
let declared_name = symbol.declared_name_owned();
|
||||||
|
self.symbols.push(symbol);
|
||||||
|
let symbol_id = self.symbols.len() - 1;
|
||||||
|
let scope_id = self.nodes_to_scopes[&node_id];
|
||||||
|
let scope = &mut self.scopes[scope_id];
|
||||||
|
scope.symbols_mut().insert(declared_name, symbol_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diagnostics_mut(&mut self) -> &mut Diagnostics {
|
||||||
|
&mut self.diagnostics
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_symbols(compilation_unit: &CompilationUnit, ctx: &mut SymbolCollectionContext) {
|
||||||
|
for function in compilation_unit.functions() {
|
||||||
|
collect_symbols_function(function, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for extern_function in compilation_unit.extern_functions() {
|
||||||
|
collect_symbols_extern_function(extern_function, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionContext) {
|
||||||
|
// function itself
|
||||||
|
let function_symbol = Symbol::Function(FunctionSymbol::new(
|
||||||
|
function.declared_name_owned(),
|
||||||
|
Some(function.declared_name_source_range()),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
|
||||||
|
// insert
|
||||||
|
if let Some(already_declared) =
|
||||||
|
ctx.find_symbol_in_scope_for(function.declared_name(), function.node_id())
|
||||||
|
{
|
||||||
|
let diagnostic = symbol_already_declared(already_declared, &function_symbol);
|
||||||
|
ctx.diagnostics_mut().push(diagnostic);
|
||||||
|
} else {
|
||||||
|
ctx.insert_symbol(function_symbol, function.node_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameters
|
||||||
|
for parameter in function.parameters() {
|
||||||
|
collect_symbols_parameter(parameter, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// n.b. do not do statements yet, because variables are declared and resolved in the resolution pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_symbols_extern_function(
|
||||||
|
extern_function: &ExternFunction,
|
||||||
|
ctx: &mut SymbolCollectionContext,
|
||||||
|
) {
|
||||||
|
// function itself
|
||||||
|
let function_symbol = Symbol::Function(FunctionSymbol::new(
|
||||||
|
extern_function.declared_name_owned(),
|
||||||
|
Some(extern_function.declared_name_source_range()),
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
|
||||||
|
// insert function symbol
|
||||||
|
if let Some(already_declared) =
|
||||||
|
ctx.find_symbol_in_scope_for(extern_function.declared_name(), extern_function.node_id())
|
||||||
|
{
|
||||||
|
let diagnostic = symbol_already_declared(already_declared, &function_symbol);
|
||||||
|
ctx.diagnostics_mut().push(diagnostic);
|
||||||
|
} else {
|
||||||
|
ctx.insert_symbol(function_symbol, extern_function.node_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameters
|
||||||
|
for parameter in extern_function.parameters() {
|
||||||
|
collect_symbols_parameter(parameter, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_symbols_parameter(parameter: &Parameter, ctx: &mut SymbolCollectionContext) {
|
||||||
|
let parameter_symbol = ParameterSymbol::new(
|
||||||
|
parameter.declared_name_owned(),
|
||||||
|
Some(parameter.declared_name_source_range()),
|
||||||
|
);
|
||||||
|
ctx.insert_symbol(Symbol::Parameter(parameter_symbol), parameter.node_id());
|
||||||
|
}
|
||||||
30
dmc-lib/src/semantic_analysis/diagnostic_helpers.rs
Normal file
30
dmc-lib/src/semantic_analysis/diagnostic_helpers.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::diagnostic::{Diagnostic, SecondaryLabel};
|
||||||
|
use crate::semantic_analysis::symbol::Symbol;
|
||||||
|
|
||||||
|
pub fn symbol_already_declared(already_declared: &Symbol, would_insert: &Symbol) -> Diagnostic {
|
||||||
|
let secondary_label = if let Some(source_range) = already_declared.source_range() {
|
||||||
|
Some(SecondaryLabel::new(
|
||||||
|
source_range.start(),
|
||||||
|
source_range.start(),
|
||||||
|
Some("Symbol already declared here".to_string()),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let would_insert_source_range = would_insert.source_range().unwrap();
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Symbol {} already declared in current scope.",
|
||||||
|
would_insert.declared_name()
|
||||||
|
),
|
||||||
|
would_insert_source_range.start(),
|
||||||
|
would_insert_source_range.end(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(secondary_label) = secondary_label {
|
||||||
|
diagnostic.with_secondary_labels(&[secondary_label])
|
||||||
|
} else {
|
||||||
|
diagnostic
|
||||||
|
}
|
||||||
|
}
|
||||||
7
dmc-lib/src/semantic_analysis/mod.rs
Normal file
7
dmc-lib/src/semantic_analysis/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mod collect_scopes;
|
||||||
|
mod collect_symbols;
|
||||||
|
mod diagnostic_helpers;
|
||||||
|
mod resolve_names;
|
||||||
|
mod scope;
|
||||||
|
mod semantic_context;
|
||||||
|
mod symbol;
|
||||||
266
dmc-lib/src/semantic_analysis/resolve_names.rs
Normal file
266
dmc-lib/src/semantic_analysis/resolve_names.rs
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
use crate::ast::NodeId;
|
||||||
|
use crate::ast::assign_statement::AssignStatement;
|
||||||
|
use crate::ast::binary_expression::BinaryExpression;
|
||||||
|
use crate::ast::call::Call;
|
||||||
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::expression_statement::ExpressionStatement;
|
||||||
|
use crate::ast::function::Function;
|
||||||
|
use crate::ast::identifier::Identifier;
|
||||||
|
use crate::ast::let_statement::LetStatement;
|
||||||
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
|
use crate::ast::statement::Statement;
|
||||||
|
use crate::diagnostic::Diagnostics;
|
||||||
|
use crate::diagnostic_factories::symbol_not_found;
|
||||||
|
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
||||||
|
use crate::semantic_analysis::symbol::{Symbol, SymbolId, VariableSymbol};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct NameResolutionContext {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
symbols: Vec<Symbol>,
|
||||||
|
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||||
|
nodes_to_symbols: HashMap<NodeId, SymbolId>,
|
||||||
|
diagnostics: Diagnostics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NameResolutionContext {
|
||||||
|
pub fn new(
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
symbols: Vec<Symbol>,
|
||||||
|
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
scopes,
|
||||||
|
symbols,
|
||||||
|
nodes_to_scopes,
|
||||||
|
nodes_to_symbols: HashMap::new(),
|
||||||
|
diagnostics: Diagnostics::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scopes(&self) -> &[Scope] {
|
||||||
|
&self.scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scopes_mut(&mut self) -> &mut Vec<Scope> {
|
||||||
|
&mut self.scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbols(&self) -> &[Symbol] {
|
||||||
|
&self.symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbols_mut(&mut self) -> &mut Vec<Symbol> {
|
||||||
|
&mut self.symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes_to_scopes(&self) -> &HashMap<NodeId, ScopeId> {
|
||||||
|
&self.nodes_to_scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes_to_symbols(&self) -> &HashMap<NodeId, SymbolId> {
|
||||||
|
&self.nodes_to_symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes_to_symbols_mut(&mut self) -> &mut HashMap<NodeId, SymbolId> {
|
||||||
|
&mut self.nodes_to_symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diagnostics_mut(&mut self) -> &mut Diagnostics {
|
||||||
|
&mut self.diagnostics
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StatementResolutionPhase {
|
||||||
|
Static,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum ExpressionResolutionPhase {
|
||||||
|
Static,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(compilation_unit: &CompilationUnit, ctx: &mut NameResolutionContext) {
|
||||||
|
for function in compilation_unit.functions() {
|
||||||
|
resolve_names_function(function, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_function(function: &Function, ctx: &mut NameResolutionContext) {
|
||||||
|
for statement in function.statements() {
|
||||||
|
resolve_names_statement(statement, ctx, StatementResolutionPhase::Static);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_statement(
|
||||||
|
statement: &Statement,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
phase: StatementResolutionPhase,
|
||||||
|
) {
|
||||||
|
match statement {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
resolve_names_let_statement(let_statement, ctx, phase);
|
||||||
|
}
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
resolve_names_expression_statement(expression_statement, ctx);
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
resolve_names_assign_statement(assign_statement, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_let_statement(
|
||||||
|
let_statement: &LetStatement,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
phase: StatementResolutionPhase,
|
||||||
|
) {
|
||||||
|
match phase {
|
||||||
|
StatementResolutionPhase::Static => {
|
||||||
|
// first, resolve the initializer expression
|
||||||
|
resolve_names_expression(
|
||||||
|
let_statement.initializer(),
|
||||||
|
ctx,
|
||||||
|
ExpressionResolutionPhase::Static,
|
||||||
|
);
|
||||||
|
// now add the declared name to the ctx so later usages can access it
|
||||||
|
let scope_id = ctx.nodes_to_scopes()[&let_statement.node_id()];
|
||||||
|
let symbol = Symbol::Variable(VariableSymbol::new(
|
||||||
|
let_statement.declared_name_owned(),
|
||||||
|
Some(let_statement.declared_name_source_range()),
|
||||||
|
));
|
||||||
|
// the following could be a method, but this is probably the only place in this phase
|
||||||
|
// where we are pushing symbols still
|
||||||
|
ctx.symbols_mut().push(symbol);
|
||||||
|
let symbol_id = ctx.symbols().len() - 1;
|
||||||
|
let scope = &mut ctx.scopes_mut()[scope_id];
|
||||||
|
scope
|
||||||
|
.symbols_mut()
|
||||||
|
.insert(let_statement.declared_name_owned(), symbol_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_expression_statement(
|
||||||
|
expression_statement: &ExpressionStatement,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
) {
|
||||||
|
resolve_names_expression(
|
||||||
|
expression_statement.expression(),
|
||||||
|
ctx,
|
||||||
|
ExpressionResolutionPhase::Static,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_assign_statement(
|
||||||
|
assign_statement: &AssignStatement,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
) {
|
||||||
|
resolve_names_expression(
|
||||||
|
assign_statement.value(),
|
||||||
|
ctx,
|
||||||
|
ExpressionResolutionPhase::Static,
|
||||||
|
);
|
||||||
|
resolve_names_expression(
|
||||||
|
assign_statement.destination(),
|
||||||
|
ctx,
|
||||||
|
ExpressionResolutionPhase::Static,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_expression(
|
||||||
|
expression: &Expression,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
phase: ExpressionResolutionPhase,
|
||||||
|
) {
|
||||||
|
match expression {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
resolve_names_binary_expression(binary_expression, ctx, phase);
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
resolve_names_negative_expression(negative_expression, ctx, phase);
|
||||||
|
}
|
||||||
|
Expression::Call(call) => {
|
||||||
|
resolve_names_call(call, ctx, phase);
|
||||||
|
}
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
resolve_names_identifier(identifier, ctx, phase);
|
||||||
|
}
|
||||||
|
Expression::Integer(_) => {}
|
||||||
|
Expression::Double(_) => {}
|
||||||
|
Expression::String(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_binary_expression(
|
||||||
|
binary_expression: &BinaryExpression,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
phase: ExpressionResolutionPhase,
|
||||||
|
) {
|
||||||
|
resolve_names_expression(binary_expression.lhs(), ctx, phase);
|
||||||
|
resolve_names_expression(binary_expression.rhs(), ctx, phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_negative_expression(
|
||||||
|
negative_expression: &NegativeExpression,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
phase: ExpressionResolutionPhase,
|
||||||
|
) {
|
||||||
|
resolve_names_expression(negative_expression.operand(), ctx, phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_call(
|
||||||
|
call: &Call,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
phase: ExpressionResolutionPhase,
|
||||||
|
) {
|
||||||
|
for argument in call.arguments() {
|
||||||
|
resolve_names_expression(argument, ctx, phase);
|
||||||
|
}
|
||||||
|
resolve_names_expression(call.callee(), ctx, phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_identifier(
|
||||||
|
identifier: &Identifier,
|
||||||
|
ctx: &mut NameResolutionContext,
|
||||||
|
phase: ExpressionResolutionPhase,
|
||||||
|
) {
|
||||||
|
match phase {
|
||||||
|
ExpressionResolutionPhase::Static => {
|
||||||
|
let scope_id = ctx.nodes_to_scopes()[&identifier.scope_id()];
|
||||||
|
let mut maybe_scope = Some(&ctx.scopes()[scope_id]);
|
||||||
|
let mut found_symbol_id: Option<SymbolId> = None;
|
||||||
|
while let Some(scope) = maybe_scope {
|
||||||
|
let maybe_symbol_id = scope.symbols().get(identifier.name()).cloned(); // cloned because ctx cannot be borrowed both immutably and mutably
|
||||||
|
match maybe_symbol_id {
|
||||||
|
None => {
|
||||||
|
maybe_scope = match scope.parent_id() {
|
||||||
|
None => None,
|
||||||
|
Some(parent_id) => Some(&ctx.scopes()[parent_id]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(symbol_id) => {
|
||||||
|
found_symbol_id = Some(symbol_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match found_symbol_id {
|
||||||
|
None => {
|
||||||
|
ctx.diagnostics_mut().push(symbol_not_found(
|
||||||
|
identifier.name(),
|
||||||
|
identifier.source_range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Some(symbol_id) => {
|
||||||
|
// In the future, we may want to differentiate between different types of
|
||||||
|
// symbols, but for now, let's just assume that we always refer to the
|
||||||
|
// nearest symbol in the ancestor scope chain.
|
||||||
|
ctx.nodes_to_symbols_mut()
|
||||||
|
.insert(identifier.node_id(), symbol_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
dmc-lib/src/semantic_analysis/scope.rs
Normal file
31
dmc-lib/src/semantic_analysis/scope.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use crate::semantic_analysis::symbol::SymbolId;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub type ScopeId = usize;
|
||||||
|
|
||||||
|
pub struct Scope {
|
||||||
|
parent_id: Option<ScopeId>,
|
||||||
|
symbols: HashMap<Rc<str>, SymbolId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
pub fn new(parent_id: Option<ScopeId>) -> Self {
|
||||||
|
Self {
|
||||||
|
parent_id,
|
||||||
|
symbols: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent_id(&self) -> Option<ScopeId> {
|
||||||
|
self.parent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbols(&self) -> &HashMap<Rc<str>, SymbolId> {
|
||||||
|
&self.symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbols_mut(&mut self) -> &mut HashMap<Rc<str>, SymbolId> {
|
||||||
|
&mut self.symbols
|
||||||
|
}
|
||||||
|
}
|
||||||
10
dmc-lib/src/semantic_analysis/semantic_context.rs
Normal file
10
dmc-lib/src/semantic_analysis/semantic_context.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
use crate::ast::NodeId;
|
||||||
|
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct SemanticContext {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
node_scopes: HashMap<NodeId, ScopeId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SemanticContext {}
|
||||||
110
dmc-lib/src/semantic_analysis/symbol.rs
Normal file
110
dmc-lib/src/semantic_analysis/symbol.rs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub type SymbolId = usize;
|
||||||
|
|
||||||
|
pub enum Symbol {
|
||||||
|
Function(FunctionSymbol),
|
||||||
|
Parameter(ParameterSymbol),
|
||||||
|
Variable(VariableSymbol),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Symbol {
|
||||||
|
pub fn declared_name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Symbol::Function(function_symbol) => function_symbol.declared_name(),
|
||||||
|
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name(),
|
||||||
|
Symbol::Variable(variable_symbol) => variable_symbol.declared_name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
match self {
|
||||||
|
Symbol::Function(function_symbol) => function_symbol.declared_name_owned(),
|
||||||
|
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name_owned(),
|
||||||
|
Symbol::Variable(variable_symbol) => variable_symbol.declared_name_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||||
|
match self {
|
||||||
|
Symbol::Function(function_symbol) => function_symbol.source_range(),
|
||||||
|
Symbol::Parameter(parameter_symbol) => parameter_symbol.source_range(),
|
||||||
|
Symbol::Variable(variable_symbol) => variable_symbol.source_range(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FunctionSymbol {
|
||||||
|
name: Rc<str>,
|
||||||
|
source_range: Option<SourceRange>,
|
||||||
|
is_extern: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionSymbol {
|
||||||
|
pub fn new(name: Rc<str>, source_range: Option<SourceRange>, is_extern: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
source_range,
|
||||||
|
is_extern,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||||
|
self.source_range.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParameterSymbol {
|
||||||
|
name: Rc<str>,
|
||||||
|
source_range: Option<SourceRange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParameterSymbol {
|
||||||
|
pub fn new(name: Rc<str>, source_range: Option<SourceRange>) -> Self {
|
||||||
|
Self { name, source_range }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||||
|
self.source_range.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VariableSymbol {
|
||||||
|
name: Rc<str>,
|
||||||
|
source_range: Option<SourceRange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableSymbol {
|
||||||
|
pub fn new(name: Rc<str>, source_range: Option<SourceRange>) -> Self {
|
||||||
|
Self { name, source_range }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||||
|
self.source_range.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -59,3 +60,13 @@ impl Hash for VariableSymbol {
|
|||||||
self.scope_id.hash(state);
|
self.scope_id.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for VariableSymbol {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"VariableSymbol({:?}, {})",
|
||||||
|
self.declared_name, self.scope_id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user