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)?;