Add extern_function AST node.

This commit is contained in:
Jesse Brault 2026-03-02 12:19:38 -06:00
parent 35a849233c
commit b7b495178b
9 changed files with 198 additions and 55 deletions

View File

@ -1,29 +1,28 @@
use crate::asm::asm_function::AsmFunction; use crate::asm::asm_function::AsmFunction;
use crate::ast::assemble_context::AssembleContext; 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::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::Ir;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
pub struct CompilationUnit { pub struct CompilationUnit {
functions: Vec<Function>, declarations: Vec<ModuleLevelDeclaration>,
} }
impl CompilationUnit { impl CompilationUnit {
pub fn new(functions: Vec<Function>) -> Self { pub fn new(declarations: Vec<ModuleLevelDeclaration>) -> Self {
Self { functions } Self { declarations }
} }
pub fn functions(&self) -> Vec<&Function> { pub fn declarations(&self) -> &[ModuleLevelDeclaration] {
self.functions.iter().collect() &self.declarations
} }
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
symbol_table.push_scope("compilation_unit_scope"); symbol_table.push_scope("compilation_unit_scope");
for function in &mut self.functions { for declaration in &mut self.declarations {
diagnostics.append(&mut function.gather_declared_names(symbol_table)); diagnostics.append(&mut declaration.gather_declared_names(symbol_table));
} }
symbol_table.pop_scope(); symbol_table.pop_scope();
diagnostics diagnostics
@ -31,36 +30,28 @@ impl CompilationUnit {
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
for function in &mut self.functions { for declaration in &mut self.declarations {
diagnostics.append(&mut function.check_name_usages(symbol_table)); diagnostics.append(&mut declaration.check_name_usages(symbol_table));
} }
diagnostics diagnostics
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
for function in &mut self.functions { for declaration in &mut self.declarations {
diagnostics.append(&mut function.type_check(symbol_table)); diagnostics.append(&mut declaration.type_check(symbol_table));
} }
diagnostics diagnostics
} }
pub fn lower_to_ir(&self) -> Vec<Ir> {
let mut irs = vec![];
for function in &self.functions {
irs.append(&mut function.lower_to_ir());
}
irs
}
pub fn assemble( pub fn assemble(
&self, &self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable, constants_table: &mut ConstantsTable,
) -> Vec<AsmFunction> { ) -> Vec<AsmFunction> {
let mut context = AssembleContext::new(); let mut context = AssembleContext::new();
for function in &self.functions { for declaration in &self.declarations {
function.assemble(&mut context, symbol_table, constants_table); declaration.assemble(&mut context, symbol_table, constants_table);
} }
context.take_functions() context.take_functions()
} }

View File

@ -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<Diagnostic> {
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<Diagnostic> {
// no-op (for now)
vec![]
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
// no-op (for now)
vec![]
}
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) {
// no-op
}
}

View File

@ -43,6 +43,7 @@ impl Function {
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
self.declared_name(), self.declared_name(),
&vec![], // todo &vec![], // todo
false,
)); ));
if let Err(symbol_insert_error) = insert_result { if let Err(symbol_insert_error) = insert_result {
match symbol_insert_error { match symbol_insert_error {

View File

@ -3,19 +3,18 @@ pub mod call;
pub mod compilation_unit; pub mod compilation_unit;
pub mod expression; pub mod expression;
pub mod expression_statement; pub mod expression_statement;
pub mod extern_function;
pub mod fqn; pub mod fqn;
pub mod function; pub mod function;
pub mod identifier; pub mod identifier;
pub mod integer_literal; pub mod integer_literal;
pub mod let_statement; pub mod let_statement;
pub mod module_level_declaration;
pub mod statement; pub mod statement;
pub mod string_literal; pub mod string_literal;
#[cfg(test)] #[cfg(test)]
mod name_tests { 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::parser::parse_compilation_unit;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
@ -31,11 +30,6 @@ mod name_tests {
0 0
); );
assert_eq!(compilation_unit.check_name_usages(&symbol_table).len(), 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] #[test]
@ -45,20 +39,6 @@ mod name_tests {
parse_compilation_unit("fn println() end fn main() println(\"Hello, World!\") end"); parse_compilation_unit("fn println() end fn main() println(\"Hello, World!\") end");
compilation_unit.gather_declared_names(&mut symbol_table); compilation_unit.gather_declared_names(&mut symbol_table);
compilation_unit.check_name_usages(&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] #[test]

View File

@ -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<Diagnostic> {
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<Diagnostic> {
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<Diagnostic> {
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)
}
}
}
}

View File

@ -2,10 +2,12 @@ use crate::ast::call::Call;
use crate::ast::compilation_unit::CompilationUnit; use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::expression_statement::ExpressionStatement; use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::extern_function::ExternFunction;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::ast::identifier::Identifier; use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral; use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::let_statement::LetStatement; use crate::ast::let_statement::LetStatement;
use crate::ast::module_level_declaration::ModuleLevelDeclaration;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::ast::string_literal::StringLiteral; use crate::ast::string_literal::StringLiteral;
use crate::lexer::Lexer; use crate::lexer::Lexer;
@ -123,12 +125,21 @@ impl<'a> Parser<'a> {
} }
pub fn compilation_unit(&mut self) -> CompilationUnit { pub fn compilation_unit(&mut self) -> CompilationUnit {
let mut functions = vec![]; let mut declarations = vec![];
self.advance(); self.advance();
while self.current.is_some() { 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 { 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 { fn statement(&mut self) -> Statement {
let current = self.get_current(); let current = self.get_current();
match current.kind() { match current.kind() {
@ -241,9 +266,12 @@ mod smoke_tests {
#[test] #[test]
fn hello_world() { fn hello_world() {
let compilation_unit = parse_compilation_unit("fn main() println(\"Hello, World!\") end"); let compilation_unit = parse_compilation_unit("fn main() println(\"Hello, World!\") end");
let functions = compilation_unit.functions(); let declarations = compilation_unit.declarations();
assert_eq!(functions.len(), 1); assert_eq!(declarations.len(), 1);
let function = functions[0]; let function = match &declarations[0] {
ModuleLevelDeclaration::Function(function) => function,
_ => panic!(),
};
assert_eq!(function.declared_name(), "main"); assert_eq!(function.declared_name(), "main");
let statements = function.statements(); let statements = function.statements();
assert_eq!(statements.len(), 1); assert_eq!(statements.len(), 1);
@ -278,3 +306,20 @@ mod smoke_tests {
parse_compilation_unit("fn main() getCl()() end"); 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");
}
}

View File

@ -5,13 +5,15 @@ use std::rc::Rc;
pub struct FunctionSymbol { pub struct FunctionSymbol {
name: Rc<str>, name: Rc<str>,
parameters: Vec<Rc<ParameterSymbol>>, parameters: Vec<Rc<ParameterSymbol>>,
is_platform: bool,
} }
impl FunctionSymbol { impl FunctionSymbol {
pub fn new(name: &str, parameters: &[Rc<ParameterSymbol>]) -> Self { pub fn new(name: &str, parameters: &[Rc<ParameterSymbol>], is_platform: bool) -> Self {
Self { Self {
name: name.into(), name: name.into(),
parameters: parameters.into(), parameters: parameters.into(),
is_platform,
} }
} }

View File

@ -35,5 +35,5 @@ pub enum TokenKind {
IntegerLiteral, IntegerLiteral,
LongLiteral, LongLiteral,
String, String,
Extern Extern,
} }