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::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<Function>,
declarations: Vec<ModuleLevelDeclaration>,
}
impl CompilationUnit {
pub fn new(functions: Vec<Function>) -> Self {
Self { functions }
pub fn new(declarations: Vec<ModuleLevelDeclaration>) -> 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<Diagnostic> {
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<Diagnostic> {
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<Diagnostic> {
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<Ir> {
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<AsmFunction> {
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()
}

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(
self.declared_name(),
&vec![], // todo
false,
));
if let Err(symbol_insert_error) = insert_result {
match symbol_insert_error {

View File

@ -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]

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::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");
}
}

View File

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

View File

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