From a17ffa184a6ee6950c082d11d04e0cbd7cd91c89 Mon Sep 17 00:00:00 2001 From: Toni Date: Sat, 4 Apr 2026 13:14:58 +0200 Subject: [PATCH] _var_arg --- README.md | 2 +- src/codegen_x86_64.rs | 61 +++++++++++++++++++++++++++++++------------ src/parser.rs | 17 ++++++------ src/std/std.zr | 22 +++++++++------- src/symbol_table.rs | 4 +-- src/typechecker.rs | 6 ++--- test.zr | 21 ++++----------- 7 files changed, 75 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 27427fc..34ea29d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A very cool language * Growing [standard library](https://github.com/antpiasecki/zern/tree/main/src/std) * Produces tiny static executables (11KB for `hello.zr`) * No libc required! -* Has the pipe operator, dynamic arrays, hashmaps +* Has the pipe operator, variadics, dynamic arrays, hashmaps, etc. ## Syntax ```rust diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 1e9d601..21de96e 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -151,21 +151,6 @@ _builtin_syscall: mov r9, [rsp+8] syscall ret - -section .text.io.printf -io.printf: - push rbp - mov rbp, rsp - sub rsp, 48 - mov [rsp], rsi - mov [rsp + 8], rdx - mov [rsp + 16], rcx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - lea rsi, [rsp] - call io._printf_impl - leave - ret " ); @@ -337,7 +322,16 @@ _builtin_environ: } } } - Params::Variadic(_name) => todo!(), + Params::Variadic => { + emit!(&mut self.output, " sub rsp, 48"); + emit!(&mut self.output, " mov [rbp - 8], rdi"); + emit!(&mut self.output, " mov [rbp - 16], rsi"); + emit!(&mut self.output, " mov [rbp - 24], rdx"); + emit!(&mut self.output, " mov [rbp - 32], rcx"); + emit!(&mut self.output, " mov [rbp - 40], r8"); + emit!(&mut self.output, " mov [rbp - 48], r9"); + env.next_offset += 48; + } } self.compile_stmt(env, body)?; @@ -626,6 +620,12 @@ _builtin_environ: paren: _, args, } => { + if let Expr::Variable(callee_name) = &**callee + && callee_name.lexeme == "_var_arg" + { + return self.emit_var_arg(env, &args[0]); + } + for arg in args { self.compile_expr(env, arg)?; emit!(&mut self.output, " push rax"); @@ -795,4 +795,33 @@ _builtin_environ: Ok(field.offset) } + + fn emit_var_arg(&mut self, env: &mut Env, index_expr: &Expr) -> Result<(), ZernError> { + self.compile_expr(env, index_expr)?; + emit!(&mut self.output, " mov r10, rax"); + + let stack_label = self.label(); + let done_label = self.label(); + + emit!(&mut self.output, " cmp r10, 6"); + emit!(&mut self.output, " jge {}", stack_label); + + // < 6 + emit!(&mut self.output, " mov rax, r10"); + emit!(&mut self.output, " inc rax"); + emit!(&mut self.output, " shl rax, 3"); + emit!(&mut self.output, " neg rax"); + emit!(&mut self.output, " mov rax, [rbp + rax]"); + emit!(&mut self.output, " jmp {}", done_label); + + // >= 6 + emit!(&mut self.output, "{}:", stack_label); + emit!(&mut self.output, " mov rax, r10"); + emit!(&mut self.output, " sub rax, 6"); + emit!(&mut self.output, " shl rax, 3"); + emit!(&mut self.output, " mov rax, [rbp + 16 + rax]"); + + emit!(&mut self.output, "{}:", done_label); + Ok(()) + } } diff --git a/src/parser.rs b/src/parser.rs index 14b0001..3c96b89 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -9,7 +9,7 @@ pub struct Param { #[derive(Debug, Clone)] pub enum Params { Normal(Vec), - Variadic(Token), + Variadic, } #[derive(Debug, Clone)] @@ -170,12 +170,11 @@ 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 is_variadic = false; let mut params = vec![]; if !self.check(&TokenType::RightBracket) { if self.match_token(&[TokenType::DoubleDot]) { - variadic_param = - Some(self.consume(TokenType::Identifier, "expected variadic parameter name")?); + is_variadic = true; } else { loop { let var_name = @@ -201,13 +200,13 @@ 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, + params: if is_variadic { + Params::Variadic + } else { + Params::Normal(params) + }, return_type, body, exported, diff --git a/src/std/std.zr b/src/std/std.zr index 18b2088..3ce9cdf 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -264,24 +264,26 @@ func mem.write16be[x: ptr, d: i64] : void func mem.write64[x: ptr, d: any] : void _builtin_set64(x, d) -// see emit_prologue for the io.printf wrapper -func io._printf_impl[s: ptr, args: ptr] : void +func io.printf[..] : void + let s: ptr = _var_arg(0) as ptr + let i: i64 = 1 + while s[0] if s[0] == '%' s = s + 1 if s[0] == 'd' - io.print_i64(mem.read64(args)) - args = args + 8 + io.print_i64(_var_arg(i)) + i = i + 1 else if s[0] == 'x' - io.print_i64_hex(mem.read64(args)) - args = args + 8 + io.print_i64_hex(_var_arg(i)) + i = i + 1 else if s[0] == 's' - io.print(mem.read64(args) as str) - args = args + 8 + io.print(_var_arg(i) as str) + i = i + 1 else if s[0] == 'c' - io.print_char(mem.read64(args) as u8) - args = args + 8 + io.print_char(_var_arg(i) as u8) + i = i + 1 else if s[0] == '%' io.print_char('%') else if s[0] == 0 diff --git a/src/symbol_table.rs b/src/symbol_table.rs index 8887146..2f1abd0 100644 --- a/src/symbol_table.rs +++ b/src/symbol_table.rs @@ -54,8 +54,8 @@ impl SymbolTable { FnType::new("void", vec!["ptr", "i64"]), ), ("_builtin_syscall".into(), FnType::new_variadic("i64")), - ("io.printf".into(), FnType::new_variadic("void")), ("_builtin_environ".into(), FnType::new("ptr", vec![])), + ("_var_arg".into(), FnType::new("any", vec!["i64"])), ]), constants: HashMap::new(), structs: HashMap::new(), @@ -110,7 +110,7 @@ impl SymbolTable { ), }, ), - Params::Variadic(name) => self.functions.insert( + Params::Variadic => self.functions.insert( name.lexeme.clone(), FnType { return_type: return_type.lexeme.clone(), diff --git a/src/typechecker.rs b/src/typechecker.rs index f785b23..e9f9494 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -195,7 +195,7 @@ impl<'a> TypeChecker<'a> { } } } - Params::Variadic(_) => { + Params::Variadic => { return error!(&name.loc, "main function cannot be variadic"); } } @@ -228,9 +228,7 @@ impl<'a> TypeChecker<'a> { ); } } - Params::Variadic(name) => { - env.define_var(name.lexeme.clone(), "ptr".into()); - } + Params::Variadic => {} } self.typecheck_stmt(env, body)?; diff --git a/test.zr b/test.zr index 9e8d7a2..de7f608 100644 --- a/test.zr +++ b/test.zr @@ -4,22 +4,17 @@ func run_test[x: str] : void for i in 0..build_blacklist->size if str.equal(x, array.nth(build_blacklist, i)) - io.print("Skipping ") - io.print(x) - io.println("...") + io.printf("Skipping %s...\n", x) return 0 - io.print("Building ") - io.print(x) - io.print("... ") + io.printf("Building %s...\n", x) let build_start_time: i64 = os.time() if must(os.run_shell_command?(str.concat("./target/release/zern ", x))) != 0 os.exit(1) let build_end_time: i64 = os.time() - io.print_i64(build_end_time - build_start_time) - io.println("ms") + io.printf("%dms\n", build_end_time - build_start_time) for i in 0..run_blacklist->size if str.find(x, array.nth(run_blacklist, i)) != -1 @@ -28,9 +23,7 @@ func run_test[x: str] : void io.println("...") return 0 - io.print("Running ") - io.print(x) - io.println("...") + io.printf("Running %s...\n", x) let run_cmd: str = "./out" if str.equal(x, "examples/curl.zr") @@ -43,11 +36,7 @@ func run_test[x: str] : void os.exit(1) let run_end_time: i64 = os.time() - io.print("Running ") - io.print(x) - io.print(" took ") - io.print_i64(run_end_time - run_start_time) - io.println("ms") + io.printf("Running %s took %dms\n", x, run_end_time - run_start_time) func run_directory[dir: str] : void let files: Array = must(os.list_directory?(dir))