Add codespan-reporting to project for awesome error reporting.

This commit is contained in:
Jesse Brault 2025-05-16 09:06:44 -05:00
parent 2b4e042602
commit 6ab9efa8fd
10 changed files with 266 additions and 92 deletions

56
Cargo.lock generated
View File

@ -106,6 +106,17 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "codespan-reporting"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [
"serde",
"termcolor",
"unicode-width",
]
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.3" version = "1.0.3"
@ -136,6 +147,7 @@ name = "deimos"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"codespan-reporting",
"pest", "pest",
"pest_derive", "pest_derive",
] ]
@ -253,6 +265,26 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.8" version = "0.10.8"
@ -281,6 +313,15 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@ -319,6 +360,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -331,6 +378,15 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"

View File

@ -15,3 +15,4 @@ path = "src/bin/dmc/main.rs"
pest = "2.7.14" pest = "2.7.14"
clap = { version = "4.5.23", features = ["derive"] } clap = { version = "4.5.23", features = ["derive"] }
pest_derive = "2.7.14" pest_derive = "2.7.14"
codespan-reporting = "0.12.0"

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View File

@ -4,7 +4,10 @@ fn main() {
let x = 'Hello'; let x = 'Hello';
let y = 'World'; let y = 'World';
{ {
let x = 'Hello';
let test = 'Test'; let test = 'Test';
let test = 'Again';
}; };
x = y; x = y;
let x = 'Hello!';
} }

View File

@ -14,10 +14,18 @@ pub fn build_ast(compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
} }
fn build_identifier(identifier_pair: Pair<Rule>) -> Identifier { fn build_identifier(identifier_pair: Pair<Rule>) -> Identifier {
Identifier::new(identifier_pair.as_span().as_str()) let as_span = identifier_pair.as_span();
Identifier::new(
as_span.as_str(),
Range {
start: as_span.start(),
end: as_span.end(),
},
)
} }
fn build_fqn(fqn_pair: Pair<Rule>) -> FullyQualifiedName { fn build_fqn(fqn_pair: Pair<Rule>) -> FullyQualifiedName {
let as_span = fqn_pair.as_span();
FullyQualifiedName::new( FullyQualifiedName::new(
fqn_pair fqn_pair
.into_inner() .into_inner()
@ -25,6 +33,10 @@ fn build_fqn(fqn_pair: Pair<Rule>) -> FullyQualifiedName {
expect_and_use(identifier_pair, Rule::Identifier, build_identifier) expect_and_use(identifier_pair, Rule::Identifier, build_identifier)
}) })
.collect(), .collect(),
Range {
start: as_span.start(),
end: as_span.end(),
},
) )
} }

View File

@ -1,5 +1,6 @@
use crate::compile::name_analysis::Symbol; use crate::compile::name_analysis::Symbol;
use pest::Parser; use pest::Parser;
use std::range::Range;
pub mod build; pub mod build;
pub mod named; pub mod named;
@ -54,14 +55,16 @@ pub enum SuffixUnaryOperator {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Identifier { pub struct Identifier {
pub name: String, pub name: String,
pub range: Range<usize>,
scope_id: Option<usize>, scope_id: Option<usize>,
symbol: Option<Symbol>, symbol: Option<Symbol>,
} }
impl Identifier { impl Identifier {
pub fn new(name: &str) -> Self { pub fn new(name: &str, range: Range<usize>) -> Self {
Identifier { Identifier {
name: name.to_string(), name: name.to_string(),
range,
scope_id: None, scope_id: None,
symbol: None, symbol: None,
} }
@ -87,14 +90,16 @@ impl Identifier {
#[derive(Debug)] #[derive(Debug)]
pub struct FullyQualifiedName { pub struct FullyQualifiedName {
pub identifiers: Vec<Identifier>, pub identifiers: Vec<Identifier>,
pub range: Range<usize>,
scope_id: Option<usize>, scope_id: Option<usize>,
symbol: Option<Symbol>, symbol: Option<Symbol>,
} }
impl FullyQualifiedName { impl FullyQualifiedName {
pub fn new(identifiers: Vec<Identifier>) -> Self { pub fn new(identifiers: Vec<Identifier>, range: Range<usize>) -> Self {
FullyQualifiedName { FullyQualifiedName {
identifiers, identifiers,
range,
scope_id: None, scope_id: None,
symbol: None, symbol: None,
} }

View File

@ -46,7 +46,10 @@ fn main() {
} }
Commands::NameAnalysis { paths } => { Commands::NameAnalysis { paths } => {
for path in paths { for path in paths {
name_analysis(&path) let result = name_analysis(&path);
if let Err(e) = result {
eprintln!("{}", e);
}
} }
} }
} }

View File

@ -1,36 +1,37 @@
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use deimos::ast::build::build_ast; use deimos::ast::build::build_ast;
use deimos::compile::name_analysis::{analyze_names, SymbolTable}; use deimos::compile::name_analysis::{analyze_names, SymbolTable};
use deimos::parser::{DeimosParser, Rule}; use deimos::parser::{DeimosParser, Rule};
use pest::Parser; use pest::Parser;
use std::path::Path; use std::path::Path;
pub fn name_analysis(path: &Path) { pub fn name_analysis(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let src = std::fs::read_to_string(path).unwrap(); let src = std::fs::read_to_string(path).unwrap();
let parse_result = DeimosParser::parse( let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
Rule::CompilationUnit, let mut files = SimpleFiles::new();
&src let file_id = files.add(path.display().to_string(), &src);
);
match parse_result { match parse_result {
Ok(mut pairs) => { Ok(mut pairs) => {
let compilation_unit_pair = pairs.next().unwrap(); let compilation_unit_pair = pairs.next().unwrap();
let mut compilation_unit = build_ast(compilation_unit_pair); let mut compilation_unit = build_ast(compilation_unit_pair);
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
let name_analysis_result = analyze_names( let diagnostics = analyze_names(file_id, &mut compilation_unit, &mut symbol_table);
&mut compilation_unit, if diagnostics.is_empty() {
&mut symbol_table, println!("Name analysis complete.");
); println!("Symbol table\n-------\n{}", symbol_table);
match name_analysis_result { Ok(())
Err(e) => { } else {
eprintln!("{}", e); let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
for diagnostic in diagnostics {
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
} }
Ok(_) => { Ok(())
println!("name_analysis complete");
println!("{}", symbol_table);
} }
} }
} Err(e) => Err(e.into()),
Err(e) => {
eprintln!("{}", e);
}
} }
} }

View File

@ -1,5 +1,6 @@
use crate::ast::named::Named; use crate::ast::named::Named;
use crate::ast::*; use crate::ast::*;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display; use std::fmt::Display;
@ -9,6 +10,15 @@ pub enum Symbol {
Variable(VariableSymbol), Variable(VariableSymbol),
} }
impl Symbol {
pub fn name(&self) -> &str {
match self {
Symbol::Function(s) => s.declared_name.as_str(),
Symbol::Variable(s) => s.name.as_str(),
}
}
}
impl Display for Symbol { impl Display for Symbol {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Symbol::*; use Symbol::*;
@ -96,7 +106,10 @@ impl SymbolTable {
pub fn insert(&mut self, name: String, symbol: Symbol) -> Result<(), String> { 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) { if let Some(current_symbol) = self.scopes[self.current_scope_id].symbols.get(&name) {
Err(format!("Symbol '{}' already defined", current_symbol)) Err(format!(
"Symbol '{}' already defined in current scope.",
current_symbol.name()
))
} else { } else {
self.scopes[self.current_scope_id] self.scopes[self.current_scope_id]
.symbols .symbols
@ -117,7 +130,7 @@ impl SymbolTable {
None None
}; };
} }
Err(format!("Symbol '{}' not found", name)) Err(format!("Symbol '{}' not found in current scope.", name))
} }
} }
@ -173,101 +186,138 @@ impl FqnContext {
} }
pub fn analyze_names( pub fn analyze_names(
file_id: usize,
compilation_unit: &mut CompilationUnit, compilation_unit: &mut CompilationUnit,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
let mut fqn_context = FqnContext::new(); let mut fqn_context = FqnContext::new();
if let Some(namespace) = &compilation_unit.namespace { if let Some(namespace) = &compilation_unit.namespace {
fqn_context.push(namespace.name().to_string()); fqn_context.push(namespace.name().to_string());
} }
for declaration in &mut compilation_unit.declarations { for declaration in &mut compilation_unit.declarations {
gather_module_level_declaration(declaration, symbol_table, &mut fqn_context)?; diagnostics.extend(gather_module_level_declaration(
file_id,
declaration,
symbol_table,
&mut fqn_context,
));
} }
assert_eq!(symbol_table.current_scope_id, 0); assert_eq!(symbol_table.current_scope_id, 0);
for declaration in &mut compilation_unit.declarations { for declaration in &mut compilation_unit.declarations {
resolve_module_level_declaration(declaration, symbol_table)?; diagnostics.extend(resolve_module_level_declaration(file_id, declaration, symbol_table));
} }
Ok(()) diagnostics
} }
fn gather_module_level_declaration( fn gather_module_level_declaration(
file_id: usize,
declaration: &mut ModuleLevelDeclaration, declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use ModuleLevelDeclaration::*; use ModuleLevelDeclaration::*;
match declaration { match declaration {
Function(function_definition) => { Function(function_definition) => {
gather_function_definition(function_definition, symbol_table, fqn_context) gather_function_definition(file_id, function_definition, symbol_table, fqn_context)
} }
_ => todo!(), _ => todo!(),
} }
} }
fn gather_function_definition( fn gather_function_definition(
file_id: usize,
function: &mut FunctionDefinition, function: &mut FunctionDefinition,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
let declared_name = function.identifier.name().to_string(); let declared_name = function.identifier.name().to_string();
let resolved_name = fqn_context.resolve(&declared_name); let resolved_name = fqn_context.resolve(&declared_name);
symbol_table.insert(
let insert_result = symbol_table.insert(
declared_name.clone(), declared_name.clone(),
Symbol::Function(FunctionSymbol { Symbol::Function(FunctionSymbol {
fqn: resolved_name.clone(), fqn: resolved_name.clone(),
declared_name, declared_name,
is_public: function.is_public, is_public: function.is_public,
}), }),
)?; );
if let Err(msg) = insert_result {
diagnostics.push(
Diagnostic::error()
.with_message(msg)
.with_label(Label::primary(file_id, function.identifier.range)),
)
}
function function
.identifier .identifier
.set_scope_id(symbol_table.current_scope_id()); .set_scope_id(symbol_table.current_scope_id());
symbol_table.push_scope(&format!("FunctionScope({})", resolved_name)); symbol_table.push_scope(&format!("FunctionScope({})", resolved_name));
// TODO: params // TODO: params
gather_function_body(&mut function.body, symbol_table, fqn_context)?; diagnostics.extend(gather_function_body(
file_id,
&mut function.body,
symbol_table,
fqn_context,
));
symbol_table.pop_scope(); symbol_table.pop_scope();
Ok(())
diagnostics
} }
fn gather_function_body( fn gather_function_body(
file_id: usize,
function_body: &mut FunctionBody, function_body: &mut FunctionBody,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use FunctionBody::*; use FunctionBody::*;
match function_body { match function_body {
Block(block) => gather_block_statement(block, symbol_table, fqn_context), Block(block) => gather_block_statement(file_id, block, symbol_table, fqn_context),
_ => todo!(), _ => todo!(),
} }
} }
fn gather_block_statement( fn gather_block_statement(
file_id: usize,
block: &mut BlockStatement, block: &mut BlockStatement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
symbol_table.push_scope("BlockStatementScope"); symbol_table.push_scope("BlockStatementScope");
for statement in &mut block.statements { for statement in &mut block.statements {
gather_statement(statement, symbol_table, fqn_context)?; diagnostics.extend(gather_statement(
file_id,
statement,
symbol_table,
fqn_context,
));
} }
symbol_table.pop_scope(); symbol_table.pop_scope();
Ok(()) diagnostics
} }
fn gather_statement( fn gather_statement(
file_id: usize,
statement: &mut Statement, statement: &mut Statement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use Statement::*; use Statement::*;
match statement { match statement {
BlockStatement(block) => gather_block_statement(block, symbol_table, fqn_context), BlockStatement(block) => gather_block_statement(file_id, block, symbol_table, fqn_context),
VariableDeclarationStatement(variable_declaration) => { VariableDeclarationStatement(variable_declaration) => {
gather_variable_declaration(variable_declaration, symbol_table, fqn_context) gather_variable_declaration(file_id, variable_declaration, symbol_table)
} }
AssignStatement(assign_statement) => { AssignStatement(assign_statement) => {
gather_assign_statement(assign_statement, symbol_table, fqn_context) gather_assign_statement(assign_statement, symbol_table, fqn_context)
@ -277,162 +327,198 @@ fn gather_statement(
} }
fn gather_variable_declaration( fn gather_variable_declaration(
file_id: usize,
variable_declaration: &mut VariableDeclarationStatement, variable_declaration: &mut VariableDeclarationStatement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, ) -> Vec<Diagnostic<usize>> {
) -> Result<(), String> { let mut diagnostics = vec![];
let variable_name = variable_declaration.identifier.name().to_string(); let variable_name = variable_declaration.identifier.name().to_string();
symbol_table.insert(
let insert_result = symbol_table.insert(
variable_name.clone(), variable_name.clone(),
Symbol::Variable(VariableSymbol { Symbol::Variable(VariableSymbol {
name: variable_name, name: variable_name,
is_mutable: variable_declaration.is_mutable, is_mutable: variable_declaration.is_mutable,
}), }),
)?; );
if let Err(msg) = insert_result {
diagnostics.push(
Diagnostic::error()
.with_message(msg)
.with_label(Label::primary(
file_id,
variable_declaration.identifier.range,
)),
);
}
variable_declaration variable_declaration
.identifier .identifier
.set_scope_id(symbol_table.current_scope_id()); .set_scope_id(symbol_table.current_scope_id());
Ok(())
diagnostics
} }
fn gather_assign_statement( fn gather_assign_statement(
assign_statement: &mut AssignStatement, assign_statement: &mut AssignStatement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
gather_expression(&mut assign_statement.lhs, symbol_table, fqn_context)?; let mut diagnostics = vec![];
gather_expression(&mut assign_statement.rhs, symbol_table, fqn_context)?; diagnostics.extend(gather_expression(
Ok(()) &mut assign_statement.lhs,
symbol_table,
fqn_context,
));
diagnostics.extend(gather_expression(
&mut assign_statement.rhs,
symbol_table,
fqn_context,
));
diagnostics
} }
fn gather_expression( fn gather_expression(
expression: &mut Expression, expression: &mut Expression,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use Expression::*; use Expression::*;
match expression { match expression {
FullyQualifiedName(fully_qualified_name) => { FullyQualifiedName(fully_qualified_name) => {
gather_fully_qualified_name(fully_qualified_name, symbol_table, fqn_context)?; gather_fully_qualified_name(fully_qualified_name, symbol_table)
}
_ => {
vec![]
} }
_ => {}
} }
Ok(())
} }
fn gather_fully_qualified_name( fn gather_fully_qualified_name(
fully_qualified_name: &mut FullyQualifiedName, fully_qualified_name: &mut FullyQualifiedName,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, ) -> Vec<Diagnostic<usize>> {
) -> Result<(), String> {
fully_qualified_name.set_scope_id(symbol_table.current_scope_id()); fully_qualified_name.set_scope_id(symbol_table.current_scope_id());
Ok(()) vec![]
} }
/* Resolve */ /* Resolve */
fn resolve_module_level_declaration( fn resolve_module_level_declaration(
file_id: usize,
declaration: &mut ModuleLevelDeclaration, declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use ModuleLevelDeclaration::*; use ModuleLevelDeclaration::*;
match declaration { match declaration {
Function(function_definition) => { Function(function_definition) => {
resolve_function_definition(function_definition, symbol_table) resolve_function_definition(file_id, function_definition, symbol_table)
} }
_ => todo!(), _ => todo!(),
} }
} }
fn resolve_function_definition( fn resolve_function_definition(
file_id: usize,
function_definition: &mut FunctionDefinition, function_definition: &mut FunctionDefinition,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
resolve_function_body(&mut function_definition.body, symbol_table) resolve_function_body(file_id, &mut function_definition.body, symbol_table)
} }
fn resolve_function_body( fn resolve_function_body(
file_id: usize,
function_body: &mut FunctionBody, function_body: &mut FunctionBody,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use FunctionBody::*; use FunctionBody::*;
match function_body { match function_body {
Block(block) => resolve_block(block, symbol_table), Block(block) => resolve_block(file_id, block, symbol_table),
_ => todo!(), _ => todo!(),
} }
} }
fn resolve_block( fn resolve_block(
file_id: usize,
block_statement: &mut BlockStatement, block_statement: &mut BlockStatement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
for statement in &mut block_statement.statements { block_statement
resolve_statement(statement, symbol_table)?; .statements
} .iter_mut()
Ok(()) .flat_map(|statement| resolve_statement(file_id, statement, symbol_table))
.collect()
} }
fn resolve_statement( fn resolve_statement(
file_id: usize,
statement: &mut Statement, statement: &mut Statement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use Statement::*; use Statement::*;
match statement { match statement {
BlockStatement(block) => resolve_block(block, symbol_table), BlockStatement(block) => resolve_block(file_id, block, symbol_table),
VariableDeclarationStatement(variable_declaration) => { VariableDeclarationStatement(variable_declaration) => {
resolve_variable_declaration(variable_declaration, symbol_table) resolve_variable_declaration(file_id, variable_declaration, symbol_table)
} }
AssignStatement(assign_statement) => { AssignStatement(assign_statement) => {
resolve_assign_statement(assign_statement, symbol_table) resolve_assign_statement(file_id, assign_statement, symbol_table)
} }
CallStatement(call_statement) => resolve_call_statement(call_statement, symbol_table), CallStatement(call_statement) => resolve_call_statement(file_id, call_statement, symbol_table),
_ => todo!(), _ => todo!(),
} }
} }
fn resolve_variable_declaration( fn resolve_variable_declaration(
file_id: usize,
variable_declaration: &mut VariableDeclarationStatement, variable_declaration: &mut VariableDeclarationStatement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
if let Some(initializer) = &mut variable_declaration.initializer { if let Some(initializer) = &mut variable_declaration.initializer {
resolve_expression(initializer, symbol_table) resolve_expression(file_id, initializer, symbol_table)
} else { } else {
Ok(()) vec![]
} }
} }
fn resolve_assign_statement( fn resolve_assign_statement(
file_id: usize,
assign_statement: &mut AssignStatement, assign_statement: &mut AssignStatement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
resolve_expression(&mut assign_statement.lhs, symbol_table)?; let mut diagnostics = vec![];
resolve_expression(&mut assign_statement.rhs, symbol_table)?; diagnostics.extend(resolve_expression(file_id, &mut assign_statement.lhs, symbol_table));
Ok(()) diagnostics.extend(resolve_expression(file_id, &mut assign_statement.rhs, symbol_table));
diagnostics
} }
fn resolve_call_statement( fn resolve_call_statement(
file_id: usize,
call_statement: &mut CallStatement, call_statement: &mut CallStatement,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
resolve_expression(&mut call_statement.0, symbol_table) resolve_expression(file_id, &mut call_statement.0, symbol_table)
} }
fn resolve_expression( fn resolve_expression(
file_id: usize,
expression: &mut Expression, expression: &mut Expression,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
use Expression::*; use Expression::*;
match expression { match expression {
FullyQualifiedName(fqn) => resolve_fully_qualified_name(fqn, symbol_table), FullyQualifiedName(fqn) => resolve_fully_qualified_name(file_id, fqn, symbol_table),
Literal(_) => Ok(()), Literal(_) => vec![],
_ => todo!(), _ => todo!(),
} }
} }
fn resolve_fully_qualified_name( fn resolve_fully_qualified_name(
file_id: usize,
fully_qualified_name: &mut FullyQualifiedName, fully_qualified_name: &mut FullyQualifiedName,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), String> { ) -> Vec<Diagnostic<usize>> {
let lookup_result = symbol_table.lookup( let lookup_result = symbol_table.lookup(
fully_qualified_name.name().as_ref(), fully_qualified_name.name().as_ref(),
fully_qualified_name.scope_id().expect(&format!( fully_qualified_name.scope_id().expect(&format!(
@ -443,8 +529,12 @@ fn resolve_fully_qualified_name(
match lookup_result { match lookup_result {
Ok(symbol) => { Ok(symbol) => {
fully_qualified_name.set_symbol(symbol.clone()); fully_qualified_name.set_symbol(symbol.clone());
Ok(()) vec![]
} }
Err(e) => Err(e), Err(e) => vec![
Diagnostic::error()
.with_message(e)
.with_label(Label::primary(file_id, fully_qualified_name.range))
],
} }
} }

View File

@ -1,3 +1,4 @@
#![feature(new_range_api)]
#![allow(warnings)] #![allow(warnings)]
pub mod ast; pub mod ast;
pub mod compile; pub mod compile;