Much work to produce better errors during name analysis.
This commit is contained in:
parent
dda86f75e7
commit
f5a82c414c
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -148,6 +148,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
"log",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
]
|
||||
@ -190,6 +191,12 @@ version = "0.2.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
|
@ -16,3 +16,4 @@ pest = "2.7.14"
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
pest_derive = "2.7.14"
|
||||
codespan-reporting = "0.12.0"
|
||||
log = "0.4.27"
|
||||
|
@ -1,13 +1,13 @@
|
||||
ns greeter
|
||||
|
||||
fn main() {
|
||||
fn main(x: String) {
|
||||
let x = 'Hello';
|
||||
let y = 'World';
|
||||
{
|
||||
let x = 'Hello';
|
||||
let test = 'Test';
|
||||
let test = 'Again';
|
||||
let test = 'test';
|
||||
let x = 'x';
|
||||
let y = 'y';
|
||||
let z = 'z';
|
||||
let test = 'oops.';
|
||||
};
|
||||
x = y;
|
||||
let x = 'Hello!';
|
||||
}
|
506
src/ast/build.rs
506
src/ast/build.rs
File diff suppressed because it is too large
Load Diff
@ -55,15 +55,17 @@ pub enum SuffixUnaryOperator {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Identifier {
|
||||
pub name: String,
|
||||
pub file_id: usize,
|
||||
pub range: Range<usize>,
|
||||
scope_id: Option<usize>,
|
||||
symbol: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
pub fn new(name: &str, range: Range<usize>) -> Self {
|
||||
pub fn new(name: &str, file_id: usize, range: Range<usize>) -> Self {
|
||||
Identifier {
|
||||
name: name.to_string(),
|
||||
file_id,
|
||||
range,
|
||||
scope_id: None,
|
||||
symbol: None,
|
||||
@ -90,16 +92,18 @@ impl Identifier {
|
||||
#[derive(Debug)]
|
||||
pub struct FullyQualifiedName {
|
||||
pub identifiers: Vec<Identifier>,
|
||||
pub file_id: usize,
|
||||
pub range: Range<usize>,
|
||||
scope_id: Option<usize>,
|
||||
symbol: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl FullyQualifiedName {
|
||||
pub fn new(identifiers: Vec<Identifier>, range: Range<usize>) -> Self {
|
||||
pub fn new(identifiers: Vec<Identifier>, file_id: usize, range: Range<usize>) -> Self {
|
||||
FullyQualifiedName {
|
||||
identifiers,
|
||||
range,
|
||||
file_id,
|
||||
scope_id: None,
|
||||
symbol: None,
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ pub fn name_analysis(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
match parse_result {
|
||||
Ok(mut pairs) => {
|
||||
let compilation_unit_pair = pairs.next().unwrap();
|
||||
let mut compilation_unit = build_ast(compilation_unit_pair);
|
||||
let mut compilation_unit = build_ast(file_id, compilation_unit_pair);
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let diagnostics = analyze_names(file_id, &mut compilation_unit, &mut symbol_table);
|
||||
if diagnostics.is_empty() {
|
||||
|
@ -11,7 +11,7 @@ pub fn pretty_print_parse(path: &PathBuf) {
|
||||
match parse_result {
|
||||
Ok(mut pairs) => {
|
||||
let compilation_unit_pair = pairs.next().unwrap();
|
||||
let compilation_unit = build_ast(compilation_unit_pair);
|
||||
let compilation_unit = build_ast(0, compilation_unit_pair);
|
||||
let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
|
||||
compilation_unit
|
||||
.pretty_print(&mut indent_writer)
|
||||
|
@ -11,7 +11,7 @@ pub fn unparse(path: &PathBuf) {
|
||||
match parse_result {
|
||||
Ok(mut pairs) => {
|
||||
let compilation_unit_pair = pairs.next().unwrap();
|
||||
let compilation_unit = build_ast(compilation_unit_pair);
|
||||
let compilation_unit = build_ast(0, compilation_unit_pair);
|
||||
let mut writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
|
||||
compilation_unit
|
||||
.unparse(&mut writer)
|
||||
|
@ -1,10 +1,45 @@
|
||||
use crate::ast::named::Named;
|
||||
use crate::ast::*;
|
||||
use crate::name_analysis::fqn_context::FqnContext;
|
||||
use crate::name_analysis::symbol::{FunctionSymbol, Symbol, VariableSymbol};
|
||||
use crate::name_analysis::symbol_table::SymbolTable;
|
||||
use crate::name_analysis::symbol::{FunctionSymbol, ModuleSymbol, SourceDefinition, Symbol, VariableSymbol};
|
||||
use crate::name_analysis::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
use crate::name_analysis::DiagnosticsContainer;
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use std::range::Range;
|
||||
|
||||
fn handle_insert_error(
|
||||
err: SymbolInsertError,
|
||||
error_symbol_name: &str,
|
||||
error_file_id: usize,
|
||||
error_range: Range<usize>,
|
||||
symbol_types: &str,
|
||||
diagnostics: &mut DiagnosticsContainer,
|
||||
) {
|
||||
match err {
|
||||
SymbolInsertError::SymbolAlreadyDefined(s) => {
|
||||
let already_defined_definition = s.definition();
|
||||
|
||||
diagnostics.add(
|
||||
Diagnostic::error()
|
||||
.with_message(format!(
|
||||
"{} symbol '{}' already defined in the current scope.",
|
||||
symbol_types, error_symbol_name,
|
||||
))
|
||||
.with_label(
|
||||
Label::primary(error_file_id, error_range)
|
||||
.with_message("Symbol duplicated here."),
|
||||
)
|
||||
.with_label(
|
||||
Label::secondary(
|
||||
already_defined_definition.file_id(),
|
||||
already_defined_definition.range(),
|
||||
)
|
||||
.with_message("Symbol defined here."),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn gather_module_level_declaration(
|
||||
declaration: &mut ModuleLevelDeclaration,
|
||||
@ -14,6 +49,9 @@ pub(super) fn gather_module_level_declaration(
|
||||
) {
|
||||
use ModuleLevelDeclaration::*;
|
||||
match declaration {
|
||||
Module(module_declaration) => {
|
||||
gather_module_declaration(module_declaration, symbol_table, fqn_context, diagnostics);
|
||||
}
|
||||
Function(function_definition) => {
|
||||
gather_function_definition(function_definition, symbol_table, fqn_context, diagnostics)
|
||||
}
|
||||
@ -21,28 +59,78 @@ pub(super) fn gather_module_level_declaration(
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_module_declaration(
|
||||
declaration: &mut ModuleDeclaration,
|
||||
symbol_table: &mut SymbolTable,
|
||||
fqn_context: &mut FqnContext,
|
||||
diagnostics: &mut DiagnosticsContainer,
|
||||
) {
|
||||
// 1. Add mod identifier symbol
|
||||
// 2. Update fqn context
|
||||
// 3. Push scope
|
||||
// 4. Process declarations
|
||||
// 5. Pop scope
|
||||
|
||||
let module_name = declaration.identifier.name();
|
||||
|
||||
let insert_result = symbol_table.insert(
|
||||
&module_name,
|
||||
Symbol::Module(ModuleSymbol::new(
|
||||
&fqn_context.resolve(&module_name),
|
||||
&module_name,
|
||||
declaration.is_public,
|
||||
&declaration.identifier,
|
||||
)),
|
||||
);
|
||||
|
||||
if let Err(err) = insert_result {
|
||||
handle_insert_error(
|
||||
err,
|
||||
&module_name,
|
||||
declaration.identifier.file_id,
|
||||
declaration.identifier.range,
|
||||
"module/type",
|
||||
diagnostics,
|
||||
)
|
||||
}
|
||||
|
||||
fqn_context.push(module_name.to_string());
|
||||
|
||||
symbol_table.push_scope(&format!("Module '{}' Scope", module_name));
|
||||
for inner_declaration in &mut declaration.declarations {
|
||||
gather_module_level_declaration(inner_declaration, symbol_table, fqn_context, diagnostics);
|
||||
}
|
||||
symbol_table.pop_scope()
|
||||
}
|
||||
|
||||
fn gather_function_definition(
|
||||
function: &mut FunctionDefinition,
|
||||
symbol_table: &mut SymbolTable,
|
||||
fqn_context: &mut FqnContext,
|
||||
diagnostics: &mut DiagnosticsContainer,
|
||||
) {
|
||||
let declared_name = function.identifier.name().to_string();
|
||||
let declared_name = function.identifier.name();
|
||||
let resolved_name = fqn_context.resolve(&declared_name);
|
||||
|
||||
let insert_result = symbol_table.insert(
|
||||
declared_name.clone(),
|
||||
Symbol::Function(FunctionSymbol {
|
||||
fqn: resolved_name.clone(),
|
||||
declared_name,
|
||||
is_public: function.is_public,
|
||||
}),
|
||||
&declared_name,
|
||||
Symbol::Function(FunctionSymbol::new(
|
||||
&resolved_name,
|
||||
&declared_name,
|
||||
function.is_public,
|
||||
&function.identifier,
|
||||
)),
|
||||
);
|
||||
|
||||
if let Err(msg) = insert_result {
|
||||
diagnostics.add(|file_id| {
|
||||
Diagnostic::error().with_label(Label::primary(file_id, function.identifier.range))
|
||||
});
|
||||
if let Err(err) = insert_result {
|
||||
handle_insert_error(
|
||||
err,
|
||||
&declared_name,
|
||||
function.identifier.file_id,
|
||||
function.identifier.range,
|
||||
"function/variable",
|
||||
diagnostics,
|
||||
)
|
||||
}
|
||||
|
||||
function
|
||||
@ -80,18 +168,28 @@ fn gather_parameter(
|
||||
let parameter_name = parameter.identifier.name();
|
||||
|
||||
let insert_result = symbol_table.insert(
|
||||
parameter_name.to_string(),
|
||||
Symbol::Variable(VariableSymbol {
|
||||
name: parameter_name.to_string(),
|
||||
is_mutable: false,
|
||||
}),
|
||||
¶meter_name,
|
||||
Symbol::Variable(VariableSymbol::new(
|
||||
¶meter_name,
|
||||
false,
|
||||
SourceDefinition::from_identifier(¶meter.identifier),
|
||||
)),
|
||||
);
|
||||
|
||||
if let Err(msg) = insert_result {
|
||||
diagnostics.add_error_at_identifier(&msg, ¶meter.identifier);
|
||||
if let Err(err) = insert_result {
|
||||
handle_insert_error(
|
||||
err,
|
||||
¶meter_name,
|
||||
parameter.identifier.file_id,
|
||||
parameter.identifier.range,
|
||||
"function/variable",
|
||||
diagnostics,
|
||||
)
|
||||
}
|
||||
|
||||
parameter.identifier.set_scope_id(symbol_table.current_scope_id());
|
||||
parameter
|
||||
.identifier
|
||||
.set_scope_id(symbol_table.current_scope_id());
|
||||
}
|
||||
|
||||
fn gather_function_body(
|
||||
@ -114,7 +212,7 @@ fn gather_block_statement(
|
||||
diagnostics: &mut DiagnosticsContainer,
|
||||
) {
|
||||
symbol_table.push_scope("BlockStatementScope");
|
||||
gather_block_statement(block, symbol_table, fqn_context, diagnostics);
|
||||
gather_block_statement_inner(block, symbol_table, fqn_context, diagnostics);
|
||||
symbol_table.pop_scope();
|
||||
}
|
||||
|
||||
@ -158,18 +256,26 @@ fn gather_variable_declaration(
|
||||
symbol_table: &mut SymbolTable,
|
||||
diagnostics: &mut DiagnosticsContainer,
|
||||
) {
|
||||
let variable_name = variable_declaration.identifier.name().to_string();
|
||||
let variable_name = variable_declaration.identifier.name();
|
||||
|
||||
let insert_result = symbol_table.insert(
|
||||
variable_name.clone(),
|
||||
Symbol::Variable(VariableSymbol {
|
||||
name: variable_name,
|
||||
is_mutable: variable_declaration.is_mutable,
|
||||
}),
|
||||
&variable_name,
|
||||
Symbol::Variable(VariableSymbol::new(
|
||||
&variable_name,
|
||||
variable_declaration.is_mutable,
|
||||
SourceDefinition::from_identifier(&variable_declaration.identifier),
|
||||
)),
|
||||
);
|
||||
|
||||
if let Err(msg) = insert_result {
|
||||
diagnostics.add_error_at_identifier(&msg, &variable_declaration.identifier);
|
||||
if let Err(err) = insert_result {
|
||||
handle_insert_error(
|
||||
err,
|
||||
&variable_name,
|
||||
variable_declaration.identifier.file_id,
|
||||
variable_declaration.identifier.range,
|
||||
"function/variable",
|
||||
diagnostics,
|
||||
)
|
||||
}
|
||||
|
||||
variable_declaration
|
||||
|
@ -6,6 +6,7 @@ use crate::name_analysis::gather::gather_module_level_declaration;
|
||||
use crate::name_analysis::resolve::resolve_module_level_declaration;
|
||||
use crate::name_analysis::symbol_table::SymbolTable;
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use log::debug;
|
||||
|
||||
mod fqn_context;
|
||||
mod gather;
|
||||
@ -26,8 +27,8 @@ impl DiagnosticsContainer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, f: impl FnOnce(usize) -> DmDiagnostic) {
|
||||
self.diagnostics.push(f(self.file_id));
|
||||
pub fn add(&mut self, diagnostic: DmDiagnostic) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
pub fn add_error_at_identifier(&mut self, msg: &str, identifier: &Identifier) {
|
||||
@ -86,15 +87,16 @@ mod tests {
|
||||
use pest::Parser;
|
||||
|
||||
fn assert_no_diagnostics(src: &str) {
|
||||
let mut files = SimpleFiles::new();
|
||||
let test_file_id = files.add("test.dm", src);
|
||||
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, src);
|
||||
if let Err(err) = &parse_result {
|
||||
panic!("{:?}", err);
|
||||
}
|
||||
let compilation_unit_pair = parse_result.unwrap().next().unwrap();
|
||||
let mut ast = build_ast(compilation_unit_pair);
|
||||
|
||||
let mut files = SimpleFiles::new();
|
||||
let test_file_id = files.add("test.dm", src);
|
||||
let compilation_unit_pair = parse_result.unwrap().next().unwrap();
|
||||
let mut ast = build_ast(test_file_id, compilation_unit_pair);
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
|
||||
|
@ -128,10 +128,16 @@ fn resolve_fully_qualified_name(
|
||||
Ok(symbol) => {
|
||||
fully_qualified_name.set_symbol(symbol.clone());
|
||||
}
|
||||
Err(e) => diagnostics.add(|file_id| {
|
||||
Err(e) => diagnostics.add(
|
||||
Diagnostic::error()
|
||||
.with_message(e)
|
||||
.with_label(Label::primary(file_id, fully_qualified_name.range))
|
||||
}),
|
||||
.with_message(format!(
|
||||
"No symbol with name '{}' found in current scope.",
|
||||
fully_qualified_name.name()
|
||||
))
|
||||
.with_label(Label::primary(
|
||||
fully_qualified_name.file_id,
|
||||
fully_qualified_name.range,
|
||||
)),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,35 @@
|
||||
use crate::ast::Identifier;
|
||||
use std::fmt::Display;
|
||||
use std::range::Range;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SourceDefinition {
|
||||
file_id: usize,
|
||||
range: Range<usize>,
|
||||
}
|
||||
|
||||
impl SourceDefinition {
|
||||
pub fn from_identifier(identifier: &Identifier) -> Self {
|
||||
SourceDefinition {
|
||||
file_id: identifier.file_id,
|
||||
range: identifier.range,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_id(&self) -> usize {
|
||||
self.file_id
|
||||
}
|
||||
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.range.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Symbol {
|
||||
Function(FunctionSymbol),
|
||||
Variable(VariableSymbol),
|
||||
Module(ModuleSymbol),
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
@ -11,6 +37,15 @@ impl Symbol {
|
||||
match self {
|
||||
Symbol::Function(s) => s.declared_name.as_str(),
|
||||
Symbol::Variable(s) => s.name.as_str(),
|
||||
Symbol::Module(s) => s.declared_name.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn definition(&self) -> &SourceDefinition {
|
||||
match self {
|
||||
Symbol::Function(s) => s.definition(),
|
||||
Symbol::Module(s) => s.definition(),
|
||||
Symbol::Variable(s) => s.definition(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,6 +56,7 @@ impl Display for Symbol {
|
||||
match self {
|
||||
Function(function_symbol) => write!(f, "{}", function_symbol),
|
||||
Variable(variable_symbol) => write!(f, "{}", variable_symbol),
|
||||
Module(module_symbol) => write!(f, "{}", module_symbol),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,6 +66,27 @@ pub struct FunctionSymbol {
|
||||
pub fqn: String,
|
||||
pub declared_name: String,
|
||||
pub is_public: bool,
|
||||
definition: SourceDefinition,
|
||||
}
|
||||
|
||||
impl FunctionSymbol {
|
||||
pub fn new(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
identifier: &Identifier,
|
||||
) -> FunctionSymbol {
|
||||
FunctionSymbol {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
definition: SourceDefinition::from_identifier(identifier),
|
||||
}
|
||||
}
|
||||
|
||||
fn definition(&self) -> &SourceDefinition {
|
||||
&self.definition
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FunctionSymbol {
|
||||
@ -46,6 +103,21 @@ impl Display for FunctionSymbol {
|
||||
pub struct VariableSymbol {
|
||||
pub name: String,
|
||||
pub is_mutable: bool,
|
||||
definition: SourceDefinition,
|
||||
}
|
||||
|
||||
impl VariableSymbol {
|
||||
pub fn new(name: &str, is_mutable: bool, definition: SourceDefinition) -> Self {
|
||||
VariableSymbol {
|
||||
name: name.to_string(),
|
||||
is_mutable,
|
||||
definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn definition(&self) -> &SourceDefinition {
|
||||
&self.definition
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VariableSymbol {
|
||||
@ -57,3 +129,41 @@ impl Display for VariableSymbol {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleSymbol {
|
||||
pub fqn: String,
|
||||
pub declared_name: String,
|
||||
pub is_public: bool,
|
||||
definition: SourceDefinition,
|
||||
}
|
||||
|
||||
impl ModuleSymbol {
|
||||
pub fn new(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
identifier: &Identifier,
|
||||
) -> ModuleSymbol {
|
||||
ModuleSymbol {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
definition: SourceDefinition::from_identifier(identifier),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn definition(&self) -> &SourceDefinition {
|
||||
&self.definition
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModuleSymbol {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ModuleSymbol(name = {}, is_public = {})",
|
||||
self.fqn, self.is_public
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,35 @@
|
||||
use crate::name_analysis::symbol::Symbol;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined;
|
||||
use crate::name_analysis::symbol_table::SymbolLookupError::NoDefinition;
|
||||
|
||||
pub struct Scope {
|
||||
parent: Option<usize>,
|
||||
symbols: HashMap<String, Symbol>,
|
||||
function_and_variable_symbols: HashMap<String, Symbol>,
|
||||
type_and_module_symbols: HashMap<String, Symbol>,
|
||||
debug_name: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
impl Scope {
|
||||
pub fn new(parent: Option<usize>, debug_name: String) -> Scope {
|
||||
Scope {
|
||||
parent,
|
||||
function_and_variable_symbols: HashMap::new(),
|
||||
type_and_module_symbols: HashMap::new(),
|
||||
debug_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SymbolInsertError {
|
||||
SymbolAlreadyDefined(Symbol),
|
||||
}
|
||||
|
||||
pub enum SymbolLookupError {
|
||||
NoDefinition
|
||||
}
|
||||
|
||||
pub struct SymbolTable {
|
||||
scopes: Vec<Scope>,
|
||||
current_scope_id: usize,
|
||||
@ -17,13 +38,10 @@ pub struct SymbolTable {
|
||||
/// Contains a vec of scopes, like a flattened tree
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
let mut t = SymbolTable::default();
|
||||
t.scopes.push(Scope {
|
||||
parent: None,
|
||||
symbols: HashMap::new(),
|
||||
debug_name: String::from("Global Scope"),
|
||||
});
|
||||
t.current_scope_id = 0;
|
||||
let mut t = SymbolTable {
|
||||
scopes: vec![Scope::new(None, String::from("GlobalScope"))],
|
||||
current_scope_id: 0,
|
||||
};
|
||||
t
|
||||
}
|
||||
|
||||
@ -33,11 +51,8 @@ impl SymbolTable {
|
||||
|
||||
pub fn push_scope(&mut self, debug_name: &str) {
|
||||
let id = self.scopes.len();
|
||||
self.scopes.push(Scope {
|
||||
parent: Some(self.current_scope_id),
|
||||
symbols: HashMap::new(),
|
||||
debug_name: debug_name.to_string(),
|
||||
});
|
||||
self.scopes
|
||||
.push(Scope::new(Some(self.current_scope_id), debug_name.to_string()));
|
||||
self.current_scope_id = id;
|
||||
}
|
||||
|
||||
@ -47,24 +62,47 @@ impl SymbolTable {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, name: String, symbol: Symbol) -> Result<(), String> {
|
||||
if let Some(current_symbol) = self.scopes[self.current_scope_id].symbols.get(&name) {
|
||||
Err(format!(
|
||||
"Symbol '{}' already defined in current scope.",
|
||||
current_symbol.name()
|
||||
))
|
||||
pub fn insert(&mut self, name: &str, symbol: Symbol) -> Result<(), SymbolInsertError> {
|
||||
match symbol {
|
||||
Symbol::Function(_) | Symbol::Variable(_) => {
|
||||
self.insert_function_or_variable(name, symbol)
|
||||
}
|
||||
Symbol::Module(_) => self.insert_module_or_type(name, symbol),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_function_or_variable(&mut self, name: &str, symbol: Symbol) -> Result<(), SymbolInsertError> {
|
||||
if let Some(defined_symbol) = self.scopes[self.current_scope_id]
|
||||
.function_and_variable_symbols
|
||||
.get(name)
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol.clone()))
|
||||
} else {
|
||||
self.scopes[self.current_scope_id]
|
||||
.symbols
|
||||
.insert(name, symbol);
|
||||
.function_and_variable_symbols
|
||||
.insert(name.to_string(), symbol);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&self, name: &str, scope_id: usize) -> Result<&Symbol, String> {
|
||||
fn insert_module_or_type(&mut self, name: &str, symbol: Symbol) -> Result<(), SymbolInsertError> {
|
||||
if let Some(defined_symbol) = self.scopes[self.current_scope_id]
|
||||
.type_and_module_symbols
|
||||
.get(name)
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol.clone()))
|
||||
} else {
|
||||
self.scopes[self.current_scope_id]
|
||||
.type_and_module_symbols
|
||||
.insert(name.to_string(), symbol);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&self, name: &str, scope_id: usize) -> Result<&Symbol, SymbolLookupError> {
|
||||
let mut scope_opt = Some(&self.scopes[scope_id]);
|
||||
while let Some(scope) = scope_opt {
|
||||
if let Some(symbol) = scope.symbols.get(name) {
|
||||
if let Some(symbol) = scope.function_and_variable_symbols.get(name) {
|
||||
return Ok(symbol);
|
||||
}
|
||||
scope_opt = if let Some(parent_id) = scope.parent {
|
||||
@ -73,15 +111,16 @@ impl SymbolTable {
|
||||
None
|
||||
};
|
||||
}
|
||||
Err(format!("Symbol '{}' not found in current scope.", name))
|
||||
Err(NoDefinition)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SymbolTable {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
writeln!(f, "SymbolTable(current_scope = {})", self.current_scope_id)?;
|
||||
for (i, scope) in self.scopes.iter().enumerate() {
|
||||
writeln!(f, "Scope {} {}", i, scope.debug_name)?;
|
||||
for (name, symbol) in &scope.symbols {
|
||||
for (name, symbol) in &scope.function_and_variable_symbols {
|
||||
writeln!(f, " {}({})", name, symbol)?;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user