From b7b495178b2c7444b685d4d73647fffb300195bd Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Mon, 2 Mar 2026 12:19:38 -0600 Subject: [PATCH] Add extern_function AST node. --- dmc-lib/src/ast/compilation_unit.rs | 37 +++++------- dmc-lib/src/ast/extern_function.rs | 66 +++++++++++++++++++++ dmc-lib/src/ast/function.rs | 1 + dmc-lib/src/ast/mod.rs | 24 +------- dmc-lib/src/ast/module_level_declaration.rs | 58 ++++++++++++++++++ dmc-lib/src/lexer.rs | 4 +- dmc-lib/src/parser.rs | 57 ++++++++++++++++-- dmc-lib/src/symbol.rs | 4 +- dmc-lib/src/token.rs | 2 +- 9 files changed, 198 insertions(+), 55 deletions(-) create mode 100644 dmc-lib/src/ast/extern_function.rs create mode 100644 dmc-lib/src/ast/module_level_declaration.rs diff --git a/dmc-lib/src/ast/compilation_unit.rs b/dmc-lib/src/ast/compilation_unit.rs index 7f3aa75..48a371e 100644 --- a/dmc-lib/src/ast/compilation_unit.rs +++ b/dmc-lib/src/ast/compilation_unit.rs @@ -1,29 +1,28 @@ use crate::asm::asm_function::AsmFunction; use crate::ast::assemble_context::AssembleContext; -use crate::ast::function::Function; +use crate::ast::module_level_declaration::ModuleLevelDeclaration; use crate::constants_table::ConstantsTable; use crate::diagnostic::Diagnostic; -use crate::ir::Ir; use crate::symbol_table::SymbolTable; pub struct CompilationUnit { - functions: Vec, + declarations: Vec, } impl CompilationUnit { - pub fn new(functions: Vec) -> Self { - Self { functions } + pub fn new(declarations: Vec) -> Self { + Self { declarations } } - pub fn functions(&self) -> Vec<&Function> { - self.functions.iter().collect() + pub fn declarations(&self) -> &[ModuleLevelDeclaration] { + &self.declarations } pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { let mut diagnostics = vec![]; symbol_table.push_scope("compilation_unit_scope"); - for function in &mut self.functions { - diagnostics.append(&mut function.gather_declared_names(symbol_table)); + for declaration in &mut self.declarations { + diagnostics.append(&mut declaration.gather_declared_names(symbol_table)); } symbol_table.pop_scope(); diagnostics @@ -31,36 +30,28 @@ impl CompilationUnit { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { let mut diagnostics = vec![]; - for function in &mut self.functions { - diagnostics.append(&mut function.check_name_usages(symbol_table)); + for declaration in &mut self.declarations { + diagnostics.append(&mut declaration.check_name_usages(symbol_table)); } diagnostics } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { let mut diagnostics = vec![]; - for function in &mut self.functions { - diagnostics.append(&mut function.type_check(symbol_table)); + for declaration in &mut self.declarations { + diagnostics.append(&mut declaration.type_check(symbol_table)); } diagnostics } - pub fn lower_to_ir(&self) -> Vec { - let mut irs = vec![]; - for function in &self.functions { - irs.append(&mut function.lower_to_ir()); - } - irs - } - pub fn assemble( &self, symbol_table: &SymbolTable, constants_table: &mut ConstantsTable, ) -> Vec { let mut context = AssembleContext::new(); - for function in &self.functions { - function.assemble(&mut context, symbol_table, constants_table); + for declaration in &self.declarations { + declaration.assemble(&mut context, symbol_table, constants_table); } context.take_functions() } diff --git a/dmc-lib/src/ast/extern_function.rs b/dmc-lib/src/ast/extern_function.rs new file mode 100644 index 0000000..04db97c --- /dev/null +++ b/dmc-lib/src/ast/extern_function.rs @@ -0,0 +1,66 @@ +use crate::ast::assemble_context::AssembleContext; +use crate::constants_table::ConstantsTable; +use crate::diagnostic::Diagnostic; +use crate::source_range::SourceRange; +use crate::symbol::FunctionSymbol; +use crate::symbol_table::{SymbolInsertError, SymbolTable}; + +pub struct ExternFunction { + declared_name: String, + declared_name_source_range: SourceRange, +} + +impl ExternFunction { + pub fn new(name: &str, declared_name_source_range: SourceRange) -> ExternFunction { + ExternFunction { + declared_name: name.into(), + declared_name_source_range, + } + } + + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( + &self.declared_name, + &vec![], + true, + )); + match insert_result { + Ok(_) => vec![], + Err(symbol_insert_error) => match symbol_insert_error { + SymbolInsertError::AlreadyDeclared(already_declared) => { + vec![Diagnostic::new( + &format!( + "Function {} already declared in current scope.", + already_declared.name() + ), + self.declared_name_source_range.start(), + self.declared_name_source_range.end(), + )] + } + }, + } + } + + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + // no-op (for now) + vec![] + } + + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { + // no-op (for now) + vec![] + } + + pub fn assemble( + &self, + context: &mut AssembleContext, + symbol_table: &SymbolTable, + constants_table: &mut ConstantsTable, + ) { + // no-op + } +} diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index 2645e51..61f1a7a 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -43,6 +43,7 @@ impl Function { let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( self.declared_name(), &vec![], // todo + false, )); if let Err(symbol_insert_error) = insert_result { match symbol_insert_error { diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index a9f637e..8b912b1 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -3,19 +3,18 @@ pub mod call; pub mod compilation_unit; pub mod expression; pub mod expression_statement; +pub mod extern_function; pub mod fqn; pub mod function; pub mod identifier; pub mod integer_literal; pub mod let_statement; +pub mod module_level_declaration; pub mod statement; pub mod string_literal; #[cfg(test)] mod name_tests { - use crate::constants_table::ConstantsTable; - use crate::ir::Ir; - use crate::ir::assemble_context::AssembleContext; use crate::parser::parse_compilation_unit; use crate::symbol_table::SymbolTable; @@ -31,11 +30,6 @@ mod name_tests { 0 ); assert_eq!(compilation_unit.check_name_usages(&symbol_table).len(), 0); - let mut constants_table = ConstantsTable::new(); - let asm_functions = compilation_unit.assemble(&symbol_table, &mut constants_table); - for asm_function in &asm_functions { - println!("{:#?}", asm_function); - } } #[test] @@ -45,20 +39,6 @@ mod name_tests { parse_compilation_unit("fn println() end fn main() println(\"Hello, World!\") end"); compilation_unit.gather_declared_names(&mut symbol_table); compilation_unit.check_name_usages(&symbol_table); - compilation_unit.type_check(&symbol_table); - let irs = compilation_unit.lower_to_ir(); - for ir in &irs { - println!("{:#?}", ir); - } - for ir in &irs { - match ir { - Ir::Function(ir_function) => { - let asm_function = ir_function.assemble(&mut AssembleContext::new()); - println!("{:#?}", asm_function); - } - _ => {} - } - } } #[test] diff --git a/dmc-lib/src/ast/module_level_declaration.rs b/dmc-lib/src/ast/module_level_declaration.rs new file mode 100644 index 0000000..e7da720 --- /dev/null +++ b/dmc-lib/src/ast/module_level_declaration.rs @@ -0,0 +1,58 @@ +use crate::ast::assemble_context::AssembleContext; +use crate::ast::extern_function::ExternFunction; +use crate::ast::function::Function; +use crate::constants_table::ConstantsTable; +use crate::diagnostic::Diagnostic; +use crate::symbol_table::SymbolTable; + +pub enum ModuleLevelDeclaration { + Function(Function), + ExternFunction(ExternFunction), +} + +impl ModuleLevelDeclaration { + pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + match self { + ModuleLevelDeclaration::Function(function) => { + function.gather_declared_names(symbol_table) + } + ModuleLevelDeclaration::ExternFunction(extern_function) => { + extern_function.gather_declared_names(symbol_table) + } + } + } + + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + match self { + ModuleLevelDeclaration::Function(function) => function.check_name_usages(symbol_table), + ModuleLevelDeclaration::ExternFunction(extern_function) => { + extern_function.check_name_usages(symbol_table) + } + } + } + + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { + match self { + ModuleLevelDeclaration::Function(function) => function.type_check(symbol_table), + ModuleLevelDeclaration::ExternFunction(extern_function) => { + extern_function.type_check(symbol_table) + } + } + } + + pub fn assemble( + &self, + context: &mut AssembleContext, + symbol_table: &SymbolTable, + constants_table: &mut ConstantsTable, + ) { + match self { + ModuleLevelDeclaration::Function(function) => { + function.assemble(context, symbol_table, constants_table) + } + ModuleLevelDeclaration::ExternFunction(extern_function) => { + extern_function.assemble(context, symbol_table, constants_table) + } + } + } +} diff --git a/dmc-lib/src/lexer.rs b/dmc-lib/src/lexer.rs index d8ba93f..c7d4606 100644 --- a/dmc-lib/src/lexer.rs +++ b/dmc-lib/src/lexer.rs @@ -157,14 +157,14 @@ mod tests { assert_next(&mut lexer, TokenKind::End, 3); assert_eq!(lexer.next(), None); } - + #[test] fn blank_after_last_token_returns_none() { let mut lexer = Lexer::new("fn "); assert_next(&mut lexer, TokenKind::Fn, 2); assert_eq!(lexer.next(), None); } - + #[test] fn extern_returned() { let mut lexer = Lexer::new("extern"); diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index 8dc568c..0aabd1b 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -2,10 +2,12 @@ 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::integer_literal::IntegerLiteral; use crate::ast::let_statement::LetStatement; +use crate::ast::module_level_declaration::ModuleLevelDeclaration; use crate::ast::statement::Statement; use crate::ast::string_literal::StringLiteral; use crate::lexer::Lexer; @@ -123,12 +125,21 @@ impl<'a> Parser<'a> { } pub fn compilation_unit(&mut self) -> CompilationUnit { - let mut functions = vec![]; + let mut declarations = vec![]; self.advance(); while self.current.is_some() { - functions.push(self.function()); + declarations.push(self.module_level_declaration()); + } + CompilationUnit::new(declarations) + } + + fn module_level_declaration(&mut self) -> ModuleLevelDeclaration { + let current = self.get_current(); + match current.kind() { + TokenKind::Fn => ModuleLevelDeclaration::Function(self.function()), + TokenKind::Extern => ModuleLevelDeclaration::ExternFunction(self.extern_function()), + _ => panic!(), } - CompilationUnit::new(functions) } fn function(&mut self) -> Function { @@ -149,6 +160,20 @@ impl<'a> Parser<'a> { ) } + fn extern_function(&mut self) -> ExternFunction { + self.expect_advance(TokenKind::Extern); + self.expect_advance(TokenKind::Fn); + let identifier_token = self.expect_advance(TokenKind::Identifier); + self.expect_advance(TokenKind::LeftParentheses); + // params + self.expect_advance(TokenKind::RightParentheses); + // return type + ExternFunction::new( + self.token_text(&identifier_token), + SourceRange::new(identifier_token.start(), identifier_token.end()), + ) + } + fn statement(&mut self) -> Statement { let current = self.get_current(); match current.kind() { @@ -241,9 +266,12 @@ mod smoke_tests { #[test] fn hello_world() { let compilation_unit = parse_compilation_unit("fn main() println(\"Hello, World!\") end"); - let functions = compilation_unit.functions(); - assert_eq!(functions.len(), 1); - let function = functions[0]; + let declarations = compilation_unit.declarations(); + assert_eq!(declarations.len(), 1); + let function = match &declarations[0] { + ModuleLevelDeclaration::Function(function) => function, + _ => panic!(), + }; assert_eq!(function.declared_name(), "main"); let statements = function.statements(); assert_eq!(statements.len(), 1); @@ -278,3 +306,20 @@ mod smoke_tests { parse_compilation_unit("fn main() getCl()() end"); } } + +#[cfg(test)] +mod concrete_tests { + use super::*; + + #[test] + fn parses_extern_fn() { + let cu = parse_compilation_unit("extern fn println()"); + let declarations = cu.declarations(); + assert_eq!(declarations.len(), 1); + let extern_function = match &declarations[0] { + ModuleLevelDeclaration::ExternFunction(extern_function) => extern_function, + _ => panic!(), + }; + assert_eq!(extern_function.declared_name(), "println"); + } +} diff --git a/dmc-lib/src/symbol.rs b/dmc-lib/src/symbol.rs index 597eb84..93fcc0c 100644 --- a/dmc-lib/src/symbol.rs +++ b/dmc-lib/src/symbol.rs @@ -5,13 +5,15 @@ use std::rc::Rc; pub struct FunctionSymbol { name: Rc, parameters: Vec>, + is_platform: bool, } impl FunctionSymbol { - pub fn new(name: &str, parameters: &[Rc]) -> Self { + pub fn new(name: &str, parameters: &[Rc], is_platform: bool) -> Self { Self { name: name.into(), parameters: parameters.into(), + is_platform, } } diff --git a/dmc-lib/src/token.rs b/dmc-lib/src/token.rs index a5cfcf2..fe41073 100644 --- a/dmc-lib/src/token.rs +++ b/dmc-lib/src/token.rs @@ -35,5 +35,5 @@ pub enum TokenKind { IntegerLiteral, LongLiteral, String, - Extern + Extern, }