Compare commits

..

3 Commits

Author SHA1 Message Date
Jesse Brault
6ab9efa8fd Add codespan-reporting to project for awesome error reporting. 2025-05-16 09:09:25 -05:00
Jesse Brault
2b4e042602 Change unparse to work with IndentWriter. 2025-05-16 08:05:59 -05:00
Jesse Brault
ce20cece21 Work on basic name analysis. 2025-05-15 21:13:43 -05:00
15 changed files with 1240 additions and 424 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"
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]]
name = "colorchoice"
version = "1.0.3"
@ -136,6 +147,7 @@ name = "deimos"
version = "0.1.0"
dependencies = [
"clap",
"codespan-reporting",
"pest",
"pest_derive",
]
@ -253,6 +265,26 @@ dependencies = [
"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]]
name = "sha2"
version = "0.10.8"
@ -281,6 +313,15 @@ dependencies = [
"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]]
name = "thiserror"
version = "1.0.69"
@ -319,6 +360,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "utf8parse"
version = "0.2.2"
@ -331,6 +378,15 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "windows-sys"
version = "0.59.0"

View File

@ -15,3 +15,4 @@ path = "src/bin/dmc/main.rs"
pest = "2.7.14"
clap = { version = "4.5.23", features = ["derive"] }
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

@ -0,0 +1,13 @@
ns greeter
fn main() {
let x = 'Hello';
let y = 'World';
{
let x = 'Hello';
let test = 'Test';
let test = 'Again';
};
x = y;
let x = 'Hello!';
}

View File

@ -14,19 +14,29 @@ pub fn build_ast(compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
}
fn build_identifier(identifier_pair: Pair<Rule>) -> Identifier {
Identifier {
name: identifier_pair.as_str().to_string(),
}
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 {
FullyQualifiedName(
let as_span = fqn_pair.as_span();
FullyQualifiedName::new(
fqn_pair
.into_inner()
.map(|identifier_pair| {
expect_and_use(identifier_pair, Rule::Identifier, build_identifier)
})
.collect(),
Range {
start: as_span.start(),
end: as_span.end(),
},
)
}
@ -745,14 +755,10 @@ fn build_call_statement(call_statement_pair: Pair<Rule>) -> CallStatement {
while let Some(inner_pair) = inner.next() {
match inner_pair.as_rule() {
Rule::ObjectAccess => {
result = Expression::ObjectAccess(build_object_access(
result, inner_pair,
));
result = Expression::ObjectAccess(build_object_access(result, inner_pair));
}
Rule::ParenthesesCall => {
result = Expression::Call(build_call_expression(
result, inner_pair,
));
result = Expression::Call(build_call_expression(result, inner_pair));
}
Rule::PlusPlus => {
result = Expression::UnarySuffix(SuffixExpression {
@ -971,16 +977,10 @@ fn build_suffix_expression(suffix_pair: Pair<Rule>) -> Expression {
while let Some(suffix_pair) = inner.next() {
match suffix_pair.as_rule() {
Rule::ObjectAccess => {
result = Expression::ObjectAccess(build_object_access(
result,
suffix_pair,
))
result = Expression::ObjectAccess(build_object_access(result, suffix_pair))
}
Rule::ParenthesesCall => {
result = Expression::Call(build_call_expression(
result,
suffix_pair,
))
result = Expression::Call(build_call_expression(result, suffix_pair))
}
Rule::PlusPlus => {
result = Expression::UnarySuffix(SuffixExpression {

View File

@ -1,6 +1,9 @@
use crate::compile::name_analysis::Symbol;
use pest::Parser;
use std::range::Range;
pub mod build;
pub mod named;
pub mod pretty_print;
pub mod unparse;
// Operators
@ -52,10 +55,72 @@ pub enum SuffixUnaryOperator {
#[derive(Debug, Clone)]
pub struct Identifier {
pub name: String,
pub range: Range<usize>,
scope_id: Option<usize>,
symbol: Option<Symbol>,
}
impl Identifier {
pub fn new(name: &str, range: Range<usize>) -> Self {
Identifier {
name: name.to_string(),
range,
scope_id: None,
symbol: None,
}
}
pub fn set_scope_id(&mut self, id: usize) {
self.scope_id = Some(id);
}
pub fn scope_id(&self) -> Option<usize> {
self.scope_id
}
pub fn set_symbol(&mut self, symbol: Symbol) {
self.symbol = Some(symbol);
}
pub fn symbol(&self) -> &Option<Symbol> {
&self.symbol
}
}
#[derive(Debug)]
pub struct FullyQualifiedName(pub Vec<Identifier>);
pub struct FullyQualifiedName {
pub identifiers: Vec<Identifier>,
pub range: Range<usize>,
scope_id: Option<usize>,
symbol: Option<Symbol>,
}
impl FullyQualifiedName {
pub fn new(identifiers: Vec<Identifier>, range: Range<usize>) -> Self {
FullyQualifiedName {
identifiers,
range,
scope_id: None,
symbol: None,
}
}
pub fn set_scope_id(&mut self, scope_id: usize) {
self.scope_id = Some(scope_id);
}
pub fn scope_id(&self) -> Option<usize> {
self.scope_id
}
pub fn set_symbol(&mut self, symbol: Symbol) {
self.symbol = Some(symbol);
}
pub fn symbol(&self) -> &Option<Symbol> {
&self.symbol
}
}
/* Type Use */
@ -400,10 +465,10 @@ pub enum Statement {
#[derive(Debug)]
pub struct VariableDeclarationStatement {
is_mutable: bool,
identifier: Identifier,
declared_type: Option<TypeUse>,
initializer: Option<Expression>,
pub is_mutable: bool,
pub identifier: Identifier,
pub declared_type: Option<TypeUse>,
pub initializer: Option<Expression>,
}
#[derive(Debug)]

29
src/ast/named.rs Normal file
View File

@ -0,0 +1,29 @@
use crate::ast::{FullyQualifiedName, Identifier};
use std::borrow::Cow;
pub trait Named {
fn name(&self) -> Cow<'_, str>;
}
impl Named for Identifier {
fn name(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.name)
}
}
impl Named for FullyQualifiedName {
fn name(&self) -> Cow<'_, str> {
if self.identifiers.len() == 1 {
self.identifiers[0].name()
} else {
let mut acc = String::new();
for (i, identifier) in self.identifiers.iter().enumerate() {
acc += &identifier.name();
if i < self.identifiers.len() - 1 {
acc += "::";
}
}
Cow::Owned(acc)
}
}
}

View File

@ -74,7 +74,7 @@ impl PrettyPrint for FullyQualifiedName {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.writeln_indented("FullyQualifiedName")?;
writer.increase_indent();
for identifier in &self.0 {
for identifier in &self.identifiers {
identifier.pretty_print(writer)?;
}
writer.decrease_indent();

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,10 @@
mod name_analysis;
mod p3;
mod unparse;
use std::path::PathBuf;
use crate::name_analysis::name_analysis;
use crate::p3::pretty_print_parse;
use crate::unparse::unparse;
use clap::{Parser, Subcommand};
@ -24,6 +26,9 @@ enum Commands {
P3 {
paths: Vec<PathBuf>,
},
NameAnalysis {
paths: Vec<PathBuf>,
},
}
fn main() {
@ -39,5 +44,13 @@ fn main() {
pretty_print_parse(&path)
}
}
Commands::NameAnalysis { paths } => {
for path in paths {
let result = name_analysis(&path);
if let Err(e) = result {
eprintln!("{}", e);
}
}
}
}
}

View File

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

View File

@ -1,8 +1,9 @@
use std::path::PathBuf;
use pest::Parser;
use deimos::ast::build::build_ast;
use deimos::ast::unparse::Unparse;
use deimos::parser::{DeimosParser, Rule};
use deimos::util::indent_writer::IndentWriter;
use pest::Parser;
use std::path::PathBuf;
pub fn unparse(path: &PathBuf) {
let src = std::fs::read_to_string(path).expect(&format!("Could not read {:?}", path));
@ -11,10 +12,11 @@ pub fn unparse(path: &PathBuf) {
Ok(mut pairs) => {
let compilation_unit_pair = pairs.next().unwrap();
let compilation_unit = build_ast(compilation_unit_pair);
let mut out = String::new();
compilation_unit.unparse(&mut out).expect("Failed to write to string.");
println!("{}", out);
},
let mut writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
compilation_unit
.unparse(&mut writer)
.expect("Failed to write to string.");
}
Err(e) => {
eprintln!("{}", e);
}

1
src/compile/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod name_analysis;

View File

@ -0,0 +1,540 @@
use crate::ast::named::Named;
use crate::ast::*;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use std::collections::HashMap;
use std::fmt::Display;
#[derive(Debug, Clone)]
pub enum Symbol {
Function(FunctionSymbol),
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 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Symbol::*;
match self {
Function(function_symbol) => write!(f, "{}", function_symbol),
Variable(variable_symbol) => write!(f, "{}", variable_symbol),
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionSymbol {
pub fqn: String,
pub declared_name: String,
pub is_public: bool,
}
impl Display for FunctionSymbol {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"FunctionSymbol(fqn = {}, declared_name = {}, is_public = {})",
self.fqn, self.declared_name, self.is_public
)
}
}
#[derive(Debug, Clone)]
pub struct VariableSymbol {
pub name: String,
pub is_mutable: bool,
}
impl Display for VariableSymbol {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"VariableSymbol(name = {}, is_mutable = {})",
self.name, self.is_mutable
)
}
}
#[derive(Default)]
pub struct Scope {
parent: Option<usize>,
symbols: HashMap<String, Symbol>,
debug_name: String,
}
#[derive(Default)]
pub struct SymbolTable {
scopes: Vec<Scope>,
current_scope_id: usize,
}
/// Contains a vec of scopes, like a flattened tree
impl SymbolTable {
pub fn new() -> Self {
let mut t = SymbolTable::default();
t.scopes.push(Scope::default());
t.current_scope_id = 0;
t
}
pub fn current_scope_id(&self) -> usize {
self.current_scope_id
}
pub fn push_scope(&mut self, debug_name: &str) {
let id = self.scopes.len();
self.scopes.push(Scope {
symbols: HashMap::new(),
parent: Some(self.current_scope_id),
debug_name: debug_name.to_string(),
});
self.current_scope_id = id;
}
pub fn pop_scope(&mut self) {
if let Some(parent_id) = self.scopes[self.current_scope_id].parent {
self.current_scope_id = parent_id;
}
}
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()
))
} else {
self.scopes[self.current_scope_id]
.symbols
.insert(name, symbol);
Ok(())
}
}
pub fn lookup(&self, name: &str, scope_id: usize) -> Result<&Symbol, String> {
let mut scope_opt = Some(&self.scopes[scope_id]);
while let Some(scope) = scope_opt {
if let Some(symbol) = scope.symbols.get(name) {
return Ok(symbol);
}
scope_opt = if let Some(parent_id) = scope.parent {
Some(&self.scopes[parent_id])
} else {
None
};
}
Err(format!("Symbol '{}' not found in current scope.", name))
}
}
impl Display for SymbolTable {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for (i, scope) in self.scopes.iter().enumerate() {
writeln!(f, "Scope {} {}", i, scope.debug_name)?;
for (name, symbol) in &scope.symbols {
writeln!(f, " {}({})", name, symbol)?;
}
}
Ok(())
}
}
struct FqnContext {
stack: Vec<String>,
}
impl FqnContext {
fn new() -> Self {
FqnContext { stack: Vec::new() }
}
fn push(&mut self, name: String) {
self.stack.push(name);
}
fn pop(&mut self) {
self.stack.pop();
}
fn current(&self) -> String {
let mut acc = String::new();
for (i, name) in self.stack.iter().enumerate() {
acc.push_str(name);
if i != self.stack.len() - 1 {
acc.push_str("::")
}
}
acc
}
fn resolve(&self, name: &str) -> String {
let mut acc = String::new();
if !self.stack.is_empty() {
acc.push_str(&self.current());
acc.push_str("::");
}
acc.push_str(name);
acc
}
}
pub fn analyze_names(
file_id: usize,
compilation_unit: &mut CompilationUnit,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
let mut fqn_context = FqnContext::new();
if let Some(namespace) = &compilation_unit.namespace {
fqn_context.push(namespace.name().to_string());
}
for declaration in &mut compilation_unit.declarations {
diagnostics.extend(gather_module_level_declaration(
file_id,
declaration,
symbol_table,
&mut fqn_context,
));
}
assert_eq!(symbol_table.current_scope_id, 0);
for declaration in &mut compilation_unit.declarations {
diagnostics.extend(resolve_module_level_declaration(file_id, declaration, symbol_table));
}
diagnostics
}
fn gather_module_level_declaration(
file_id: usize,
declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext,
) -> Vec<Diagnostic<usize>> {
use ModuleLevelDeclaration::*;
match declaration {
Function(function_definition) => {
gather_function_definition(file_id, function_definition, symbol_table, fqn_context)
}
_ => todo!(),
}
}
fn gather_function_definition(
file_id: usize,
function: &mut FunctionDefinition,
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext,
) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
let declared_name = function.identifier.name().to_string();
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,
}),
);
if let Err(msg) = insert_result {
diagnostics.push(
Diagnostic::error()
.with_message(msg)
.with_label(Label::primary(file_id, function.identifier.range)),
)
}
function
.identifier
.set_scope_id(symbol_table.current_scope_id());
symbol_table.push_scope(&format!("FunctionScope({})", resolved_name));
// TODO: params
diagnostics.extend(gather_function_body(
file_id,
&mut function.body,
symbol_table,
fqn_context,
));
symbol_table.pop_scope();
diagnostics
}
fn gather_function_body(
file_id: usize,
function_body: &mut FunctionBody,
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext,
) -> Vec<Diagnostic<usize>> {
use FunctionBody::*;
match function_body {
Block(block) => gather_block_statement(file_id, block, symbol_table, fqn_context),
_ => todo!(),
}
}
fn gather_block_statement(
file_id: usize,
block: &mut BlockStatement,
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext,
) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
symbol_table.push_scope("BlockStatementScope");
for statement in &mut block.statements {
diagnostics.extend(gather_statement(
file_id,
statement,
symbol_table,
fqn_context,
));
}
symbol_table.pop_scope();
diagnostics
}
fn gather_statement(
file_id: usize,
statement: &mut Statement,
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext,
) -> Vec<Diagnostic<usize>> {
use Statement::*;
match statement {
BlockStatement(block) => gather_block_statement(file_id, block, symbol_table, fqn_context),
VariableDeclarationStatement(variable_declaration) => {
gather_variable_declaration(file_id, variable_declaration, symbol_table)
}
AssignStatement(assign_statement) => {
gather_assign_statement(assign_statement, symbol_table, fqn_context)
}
_ => todo!(),
}
}
fn gather_variable_declaration(
file_id: usize,
variable_declaration: &mut VariableDeclarationStatement,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
let variable_name = variable_declaration.identifier.name().to_string();
let insert_result = symbol_table.insert(
variable_name.clone(),
Symbol::Variable(VariableSymbol {
name: variable_name,
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
.identifier
.set_scope_id(symbol_table.current_scope_id());
diagnostics
}
fn gather_assign_statement(
assign_statement: &mut AssignStatement,
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext,
) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
diagnostics.extend(gather_expression(
&mut assign_statement.lhs,
symbol_table,
fqn_context,
));
diagnostics.extend(gather_expression(
&mut assign_statement.rhs,
symbol_table,
fqn_context,
));
diagnostics
}
fn gather_expression(
expression: &mut Expression,
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext,
) -> Vec<Diagnostic<usize>> {
use Expression::*;
match expression {
FullyQualifiedName(fully_qualified_name) => {
gather_fully_qualified_name(fully_qualified_name, symbol_table)
}
_ => {
vec![]
}
}
}
fn gather_fully_qualified_name(
fully_qualified_name: &mut FullyQualifiedName,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
fully_qualified_name.set_scope_id(symbol_table.current_scope_id());
vec![]
}
/* Resolve */
fn resolve_module_level_declaration(
file_id: usize,
declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
use ModuleLevelDeclaration::*;
match declaration {
Function(function_definition) => {
resolve_function_definition(file_id, function_definition, symbol_table)
}
_ => todo!(),
}
}
fn resolve_function_definition(
file_id: usize,
function_definition: &mut FunctionDefinition,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
resolve_function_body(file_id, &mut function_definition.body, symbol_table)
}
fn resolve_function_body(
file_id: usize,
function_body: &mut FunctionBody,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
use FunctionBody::*;
match function_body {
Block(block) => resolve_block(file_id, block, symbol_table),
_ => todo!(),
}
}
fn resolve_block(
file_id: usize,
block_statement: &mut BlockStatement,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
block_statement
.statements
.iter_mut()
.flat_map(|statement| resolve_statement(file_id, statement, symbol_table))
.collect()
}
fn resolve_statement(
file_id: usize,
statement: &mut Statement,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
use Statement::*;
match statement {
BlockStatement(block) => resolve_block(file_id, block, symbol_table),
VariableDeclarationStatement(variable_declaration) => {
resolve_variable_declaration(file_id, variable_declaration, symbol_table)
}
AssignStatement(assign_statement) => {
resolve_assign_statement(file_id, assign_statement, symbol_table)
}
CallStatement(call_statement) => resolve_call_statement(file_id, call_statement, symbol_table),
_ => todo!(),
}
}
fn resolve_variable_declaration(
file_id: usize,
variable_declaration: &mut VariableDeclarationStatement,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
if let Some(initializer) = &mut variable_declaration.initializer {
resolve_expression(file_id, initializer, symbol_table)
} else {
vec![]
}
}
fn resolve_assign_statement(
file_id: usize,
assign_statement: &mut AssignStatement,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
let mut diagnostics = vec![];
diagnostics.extend(resolve_expression(file_id, &mut assign_statement.lhs, symbol_table));
diagnostics.extend(resolve_expression(file_id, &mut assign_statement.rhs, symbol_table));
diagnostics
}
fn resolve_call_statement(
file_id: usize,
call_statement: &mut CallStatement,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
resolve_expression(file_id, &mut call_statement.0, symbol_table)
}
fn resolve_expression(
file_id: usize,
expression: &mut Expression,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
use Expression::*;
match expression {
FullyQualifiedName(fqn) => resolve_fully_qualified_name(file_id, fqn, symbol_table),
Literal(_) => vec![],
_ => todo!(),
}
}
fn resolve_fully_qualified_name(
file_id: usize,
fully_qualified_name: &mut FullyQualifiedName,
symbol_table: &mut SymbolTable,
) -> Vec<Diagnostic<usize>> {
let lookup_result = symbol_table.lookup(
fully_qualified_name.name().as_ref(),
fully_qualified_name.scope_id().expect(&format!(
"FullyQualifiedName has no scope_id set: {:?}",
fully_qualified_name
)),
);
match lookup_result {
Ok(symbol) => {
fully_qualified_name.set_symbol(symbol.clone());
vec![]
}
Err(e) => vec![
Diagnostic::error()
.with_message(e)
.with_label(Label::primary(file_id, fully_qualified_name.range))
],
}
}

View File

@ -1,5 +1,7 @@
#![feature(new_range_api)]
#![allow(warnings)]
pub mod ast;
pub mod compile;
pub mod module;
pub mod object_file;
pub mod parser;