parse variadic functions

This commit is contained in:
2026-04-04 12:17:44 +02:00
parent 86de997be3
commit bb42e1fcf7
5 changed files with 110 additions and 54 deletions

View File

@@ -1,7 +1,7 @@
use std::{collections::HashMap, fmt::Write}; use std::{collections::HashMap, fmt::Write};
use crate::{ use crate::{
parser::{Expr, Stmt}, parser::{Expr, Params, Stmt},
symbol_table::SymbolTable, symbol_table::SymbolTable,
tokenizer::{Token, TokenType, ZernError, error}, tokenizer::{Token, TokenType, ZernError, error},
}; };
@@ -317,9 +317,13 @@ _builtin_environ:
emit!(&mut self.output, " mov rbp, rsp"); emit!(&mut self.output, " mov rbp, rsp");
emit!(&mut self.output, " sub rsp, 256"); // TODO: eww emit!(&mut self.output, " sub rsp, 256"); // TODO: eww
match params {
Params::Normal(params) => {
for (i, param) in params.iter().enumerate() { for (i, param) in params.iter().enumerate() {
let offset = env let offset = env.define_var(
.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone()); param.var_name.lexeme.clone(),
param.var_type.lexeme.clone(),
);
if let Some(reg) = REGISTERS.get(i) { if let Some(reg) = REGISTERS.get(i) {
emit!(&mut self.output, " mov QWORD [rbp-{}], {}", offset, reg); emit!(&mut self.output, " mov QWORD [rbp-{}], {}", offset, reg);
} else { } else {
@@ -332,6 +336,9 @@ _builtin_environ:
emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset); emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset);
} }
} }
}
Params::Variadic(_name) => todo!(),
}
self.compile_stmt(env, body)?; self.compile_stmt(env, body)?;

View File

@@ -6,6 +6,12 @@ pub struct Param {
pub var_name: Token, pub var_name: Token,
} }
#[derive(Debug, Clone)]
pub enum Params {
Normal(Vec<Param>),
Variadic(Token),
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Stmt { pub enum Stmt {
Expression(Expr), Expression(Expr),
@@ -38,7 +44,7 @@ pub enum Stmt {
}, },
Function { Function {
name: Token, name: Token,
params: Vec<Param>, params: Params,
return_type: Token, return_type: Token,
body: Box<Stmt>, body: Box<Stmt>,
exported: bool, exported: bool,
@@ -164,13 +170,20 @@ impl Parser {
let name = self.consume(TokenType::Identifier, "expected function name")?; let name = self.consume(TokenType::Identifier, "expected function name")?;
self.consume(TokenType::LeftBracket, "expected '[' after function name")?; self.consume(TokenType::LeftBracket, "expected '[' after function name")?;
let mut variadic_param: Option<Token> = None;
let mut params = vec![]; let mut params = vec![];
if !self.check(&TokenType::RightBracket) { if !self.check(&TokenType::RightBracket) {
if self.match_token(&[TokenType::DoubleDot]) {
variadic_param =
Some(self.consume(TokenType::Identifier, "expected variadic parameter name")?);
} else {
loop { loop {
let var_name = self.consume(TokenType::Identifier, "expected parameter name")?; let var_name =
self.consume(TokenType::Identifier, "expected parameter name")?;
self.consume(TokenType::Colon, "expected ':' after parameter name")?; self.consume(TokenType::Colon, "expected ':' after parameter name")?;
let var_type = self.consume(TokenType::Identifier, "expected parameter type")?; let var_type =
self.consume(TokenType::Identifier, "expected parameter type")?;
params.push(Param { var_type, var_name }); params.push(Param { var_type, var_name });
if !self.match_token(&[TokenType::Comma]) { if !self.match_token(&[TokenType::Comma]) {
@@ -178,6 +191,7 @@ impl Parser {
} }
} }
} }
}
self.consume(TokenType::RightBracket, "expected ']' after arguments")?; self.consume(TokenType::RightBracket, "expected ']' after arguments")?;
self.consume(TokenType::Colon, "expected ':' after '['")?; self.consume(TokenType::Colon, "expected ':' after '['")?;
@@ -187,6 +201,10 @@ impl Parser {
let body = Box::new(self.block()?); let body = Box::new(self.block()?);
self.is_inside_function = false; self.is_inside_function = false;
let params = match variadic_param {
Some(name) => Params::Variadic(name),
None => Params::Normal(params),
};
Ok(Stmt::Function { Ok(Stmt::Function {
name, name,
params, params,

View File

@@ -1007,7 +1007,7 @@ func os.urandom?[n: i64]: ptr
let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0) let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0)
if fd < 0 if fd < 0
mem.free(buffer) mem.free(buffer)
err.set(ERR_READ_FAILED, "os.urandom?: failed to open /dev/urandom") err.set(ERR_OPEN_FAILED, "os.urandom?: failed to open /dev/urandom")
return 0 as ptr return 0 as ptr
let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n) let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n)

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
parser::Stmt, parser::{Params, Stmt},
tokenizer::{ZernError, error}, tokenizer::{ZernError, error},
}; };
@@ -100,13 +100,24 @@ impl SymbolTable {
if self.is_name_defined(&name.lexeme) { if self.is_name_defined(&name.lexeme) {
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme)); return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
} }
self.functions.insert( match params {
Params::Normal(params) => self.functions.insert(
name.lexeme.clone(), name.lexeme.clone(),
FnType { FnType {
return_type: return_type.lexeme.clone(), return_type: return_type.lexeme.clone(),
params: Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()), params: Some(
params.iter().map(|x| x.var_type.lexeme.clone()).collect(),
),
}, },
); ),
Params::Variadic(name) => self.functions.insert(
name.lexeme.clone(),
FnType {
return_type: return_type.lexeme.clone(),
params: None,
},
),
};
} }
Stmt::Struct { name, fields } => { Stmt::Struct { name, fields } => {
if self.is_name_defined(&name.lexeme) { if self.is_name_defined(&name.lexeme) {

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
parser::{Expr, Stmt}, parser::{Expr, Params, Stmt},
symbol_table::{SymbolTable, Type}, symbol_table::{SymbolTable, Type},
tokenizer::{TokenType, ZernError, error}, tokenizer::{TokenType, ZernError, error},
}; };
@@ -171,8 +171,13 @@ impl<'a> TypeChecker<'a> {
return error!(&name.loc, "main function must return i64"); return error!(&name.loc, "main function must return i64");
} }
match params {
Params::Normal(params) => {
if !params.is_empty() && params.len() != 2 { if !params.is_empty() && params.len() != 2 {
return error!(&name.loc, "main function must accept 0 or 2 parameters"); return error!(
&name.loc,
"main function must accept 0 or 2 parameters"
);
} }
if params.len() == 2 { if params.len() == 2 {
@@ -190,6 +195,11 @@ impl<'a> TypeChecker<'a> {
} }
} }
} }
Params::Variadic(_) => {
return error!(&name.loc, "main function cannot be variadic");
}
}
}
if !self.is_valid_type_name(&return_type.lexeme) { if !self.is_valid_type_name(&return_type.lexeme) {
return error!( return error!(
@@ -202,6 +212,8 @@ impl<'a> TypeChecker<'a> {
env.push_scope(); env.push_scope();
match params {
Params::Normal(params) => {
for param in params { for param in params {
if !self.is_valid_type_name(&param.var_type.lexeme) { if !self.is_valid_type_name(&param.var_type.lexeme) {
return error!( return error!(
@@ -210,7 +222,15 @@ impl<'a> TypeChecker<'a> {
); );
} }
env.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone()); env.define_var(
param.var_name.lexeme.clone(),
param.var_type.lexeme.clone(),
);
}
}
Params::Variadic(name) => {
env.define_var(name.lexeme.clone(), "ptr".into());
}
} }
self.typecheck_stmt(env, body)?; self.typecheck_stmt(env, body)?;