diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 1c30b41..1e9d601 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, fmt::Write}; use crate::{ - parser::{Expr, Stmt}, + parser::{Expr, Params, Stmt}, symbol_table::SymbolTable, tokenizer::{Token, TokenType, ZernError, error}, }; @@ -317,20 +317,27 @@ _builtin_environ: emit!(&mut self.output, " mov rbp, rsp"); emit!(&mut self.output, " sub rsp, 256"); // TODO: eww - for (i, param) in params.iter().enumerate() { - let offset = env - .define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone()); - if let Some(reg) = REGISTERS.get(i) { - emit!(&mut self.output, " mov QWORD [rbp-{}], {}", offset, reg); - } else { - let stack_offset = 16 + 8 * (i - REGISTERS.len()); - emit!( - &mut self.output, - " mov rax, QWORD [rbp+{}]", - stack_offset - ); - emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset); + match params { + Params::Normal(params) => { + for (i, param) in params.iter().enumerate() { + let offset = env.define_var( + param.var_name.lexeme.clone(), + param.var_type.lexeme.clone(), + ); + if let Some(reg) = REGISTERS.get(i) { + emit!(&mut self.output, " mov QWORD [rbp-{}], {}", offset, reg); + } else { + let stack_offset = 16 + 8 * (i - REGISTERS.len()); + emit!( + &mut self.output, + " mov rax, QWORD [rbp+{}]", + stack_offset + ); + emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset); + } + } } + Params::Variadic(_name) => todo!(), } self.compile_stmt(env, body)?; diff --git a/src/parser.rs b/src/parser.rs index 142f52c..14b0001 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -6,6 +6,12 @@ pub struct Param { pub var_name: Token, } +#[derive(Debug, Clone)] +pub enum Params { + Normal(Vec), + Variadic(Token), +} + #[derive(Debug, Clone)] pub enum Stmt { Expression(Expr), @@ -38,7 +44,7 @@ pub enum Stmt { }, Function { name: Token, - params: Vec, + params: Params, return_type: Token, body: Box, exported: bool, @@ -164,17 +170,25 @@ impl Parser { let name = self.consume(TokenType::Identifier, "expected function name")?; self.consume(TokenType::LeftBracket, "expected '[' after function name")?; + let mut variadic_param: Option = None; let mut params = vec![]; if !self.check(&TokenType::RightBracket) { - loop { - let var_name = self.consume(TokenType::Identifier, "expected parameter name")?; - self.consume(TokenType::Colon, "expected ':' after parameter name")?; + if self.match_token(&[TokenType::DoubleDot]) { + variadic_param = + Some(self.consume(TokenType::Identifier, "expected variadic parameter name")?); + } else { + loop { + let var_name = + self.consume(TokenType::Identifier, "expected 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 }); - if !self.match_token(&[TokenType::Comma]) { - break; + params.push(Param { var_type, var_name }); + if !self.match_token(&[TokenType::Comma]) { + break; + } } } } @@ -187,6 +201,10 @@ impl Parser { let body = Box::new(self.block()?); self.is_inside_function = false; + let params = match variadic_param { + Some(name) => Params::Variadic(name), + None => Params::Normal(params), + }; Ok(Stmt::Function { name, params, diff --git a/src/std/std.zr b/src/std/std.zr index 1dea503..18b2088 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -1007,7 +1007,7 @@ func os.urandom?[n: i64]: ptr let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0) if fd < 0 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 let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n) diff --git a/src/symbol_table.rs b/src/symbol_table.rs index f38d10b..8887146 100644 --- a/src/symbol_table.rs +++ b/src/symbol_table.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::{ - parser::Stmt, + parser::{Params, Stmt}, tokenizer::{ZernError, error}, }; @@ -100,13 +100,24 @@ impl SymbolTable { if self.is_name_defined(&name.lexeme) { return error!(name.loc, format!("tried to redefine '{}'", name.lexeme)); } - self.functions.insert( - name.lexeme.clone(), - FnType { - return_type: return_type.lexeme.clone(), - params: Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()), - }, - ); + match params { + Params::Normal(params) => self.functions.insert( + name.lexeme.clone(), + FnType { + return_type: return_type.lexeme.clone(), + 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 } => { if self.is_name_defined(&name.lexeme) { diff --git a/src/typechecker.rs b/src/typechecker.rs index ce9b358..f785b23 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::{ - parser::{Expr, Stmt}, + parser::{Expr, Params, Stmt}, symbol_table::{SymbolTable, Type}, tokenizer::{TokenType, ZernError, error}, }; @@ -171,22 +171,32 @@ impl<'a> TypeChecker<'a> { return error!(&name.loc, "main function must return i64"); } - if !params.is_empty() && params.len() != 2 { - return error!(&name.loc, "main function must accept 0 or 2 parameters"); - } + match params { + Params::Normal(params) => { + if !params.is_empty() && params.len() != 2 { + return error!( + &name.loc, + "main function must accept 0 or 2 parameters" + ); + } - if params.len() == 2 { - if params[0].var_type.lexeme != "i64" { - return error!( - &name.loc, - "first parameter of the main function must be a i64" - ); + if params.len() == 2 { + if params[0].var_type.lexeme != "i64" { + return error!( + &name.loc, + "first parameter of the main function must be a i64" + ); + } + if params[1].var_type.lexeme != "ptr" { + return error!( + &name.loc, + "second parameter of the main function must be a ptr" + ); + } + } } - if params[1].var_type.lexeme != "ptr" { - return error!( - &name.loc, - "second parameter of the main function must be a ptr" - ); + Params::Variadic(_) => { + return error!(&name.loc, "main function cannot be variadic"); } } } @@ -202,15 +212,25 @@ impl<'a> TypeChecker<'a> { env.push_scope(); - for param in params { - if !self.is_valid_type_name(¶m.var_type.lexeme) { - return error!( - ¶m.var_name.loc, - "unrecognized type: ".to_owned() + ¶m.var_type.lexeme - ); - } + match params { + Params::Normal(params) => { + for param in params { + if !self.is_valid_type_name(¶m.var_type.lexeme) { + return error!( + ¶m.var_name.loc, + "unrecognized type: ".to_owned() + ¶m.var_type.lexeme + ); + } - 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)?;