diff --git a/examples/udp_server.zr b/examples/udp_server.zr index e784f5d..1ec4c65 100644 --- a/examples/udp_server.zr +++ b/examples/udp_server.zr @@ -1,5 +1,5 @@ func main[] : i64 - let s: net.UDPSocket = must(net.udp_listen?(net.pack_addr(127, 0, 0, 1), 8000)) + let s: net.UDPSocket = must(net.create_udp_server?(net.pack_addr(127, 0, 0, 1), 8000)) io.println("Listening on port 8000...") while true diff --git a/src/analyzer.rs b/src/analyzer.rs index 6e4c84c..113b0d8 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -51,9 +51,9 @@ impl Analyzer { ("_builtin_read64".into(), FnType::new("i64", vec!["ptr"])), ( "_builtin_set64".into(), - FnType::new("void", vec!["ptr", "any"]), + FnType::new("void", vec!["ptr", "i64"]), ), - ("_builtin_syscall".into(), FnType::new_variadic("any")), + ("_builtin_syscall".into(), FnType::new_variadic("i64")), ("io.printf".into(), FnType::new_variadic("void")), ("_builtin_environ".into(), FnType::new("ptr", vec![])), ]), @@ -145,7 +145,7 @@ impl Analyzer { self.analyze_stmt(body)?; } - Stmt::Return(expr) => { + Stmt::Return { expr, keyword: _ } => { self.analyze_expr(expr)?; } Stmt::For { diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 05650a5..3f0a3be 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -339,7 +339,7 @@ _builtin_environ: emit!(&mut self.output, " ret"); } } - Stmt::Return(expr) => { + Stmt::Return { expr, keyword: _ } => { self.compile_expr(env, expr)?; emit!(&mut self.output, " mov rsp, rbp"); emit!(&mut self.output, " pop rbp"); diff --git a/src/parser.rs b/src/parser.rs index 857fffb..a49f53d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -41,7 +41,10 @@ pub enum Stmt { body: Box, exported: bool, }, - Return(Expr), + Return { + expr: Expr, + keyword: Token, + }, Break, Continue, Extern(Token), @@ -262,7 +265,11 @@ impl Parser { } else if self.match_token(&[TokenType::KeywordFor]) { self.for_statement() } else if self.match_token(&[TokenType::KeywordReturn]) { - Ok(Stmt::Return(self.expression()?)) + let keyword = self.previous().clone(); + Ok(Stmt::Return { + expr: self.expression()?, + keyword, + }) } else if self.match_token(&[TokenType::KeywordBreak]) { Ok(Stmt::Break) } else if self.match_token(&[TokenType::KeywordContinue]) { diff --git a/src/std/net.zr b/src/std/net.zr index 1a6bbca..ac3e7f1 100644 --- a/src/std/net.zr +++ b/src/std/net.zr @@ -82,7 +82,7 @@ func net.close[s: i64] : void _builtin_syscall(SYS_close, s) func net.pack_addr[a: u8, b: u8, c: u8, d: u8] : i64 - return (a << 24) | (b << 16) | (c << 8) | d + return (a as i64 << 24) | (b << 16) | (c << 8) | d struct net.UDPSocket fd: i64 @@ -101,7 +101,7 @@ func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket if s->fd < 0 mem.free(s) err.set(ERR_SYSCALL_FAILED, "net.create_udp_client?: failed to create a socket") - return 0 + return 0 as net.UDPSocket s->addr = mem.alloc(16) mem.zero(s->addr, 16) @@ -115,16 +115,16 @@ func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket s->addr[7] = packed_host & 255 return s -func net.udp_listen?[packed_host: i64, port: i64] : net.UDPSocket +func net.create_udp_server?[packed_host: i64, port: i64] : net.UDPSocket err.clear() let s: net.UDPSocket = net.create_udp_client?(packed_host, port) if err.check() - return 0 + return 0 as net.UDPSocket if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0 net.UDPSocket.close_and_free(s) - err.set(ERR_SYSCALL_FAILED, "net.udp_listen?: failed to bind") - return 0 + err.set(ERR_SYSCALL_FAILED, "net.create_udp_server?: failed to bind") + return 0 as net.UDPSocket return s diff --git a/src/std/std.zr b/src/std/std.zr index 165eafe..b9afe49 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -10,7 +10,7 @@ func err.code[] : i64 return mem.read64(_builtin_err_code()) func err.msg[] : str - return mem.read64(_builtin_err_msg()) + return mem.read64(_builtin_err_msg()) as str func err.check[] : bool return err.code() != 0 @@ -73,7 +73,7 @@ func mem._request_space?[size: i64] : mem.Block let blk_ptr: ptr = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0) as ptr if blk_ptr == -1 err.set(ERR_ALLOC_FAILED, "mem._request_space: mmap failed") - return 0 + return 0 as mem.Block let blk: mem.Block = blk_ptr as mem.Block @@ -173,11 +173,11 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr if new_size < 0 err.set(ERR_ALLOC_FAILED, "mem.realloc? called with negative size") - return 0 + return 0 as ptr if new_size == 0 mem.free(x) - return 0 + return 0 as ptr new_size = mem._align(new_size) @@ -204,7 +204,7 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr let new_ptr: ptr = mem.alloc?(new_size) if err.check() - return 0 + return 0 as ptr mem.copy(x, new_ptr, math.min(blk->size, new_size)) mem.free(x) @@ -232,16 +232,16 @@ func mem.read8[x: ptr] : u8 return x[0] func mem.read16[x: ptr] : i64 - return x[0] | (x[1] << 8) + return x[0] as i64 | (x[1] << 8) func mem.read16be[x: ptr] : i64 - return (x[0] << 8) | x[1] + return (x[0] << 8) as i64 | x[1] func mem.read32[x: ptr] : i64 - return x[0] | (x[1] << 8) | (x[2] << 16) | (x[3] << 24) + return x[0] as i64 | (x[1] << 8) | (x[2] << 16) | (x[3] << 24) func mem.read32be[x: ptr] : i64 - return (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3] + return (x[0] as i64 << 24) | (x[1] << 16) | (x[2] << 8) | x[3] func mem.read64[x: ptr] : i64 return _builtin_read64(x) @@ -349,7 +349,7 @@ func io.Buffer.alloc?[size: i64] : io.Buffer buffer->data = mem.alloc?(size) if err.check() mem.free(buffer) - return 0 + return 0 as io.Buffer return buffer func io.Buffer.free[buf: io.Buffer] : void @@ -364,7 +364,7 @@ func io.read_text_file?[path: str] : str let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0) if fd < 0 err.set(ERR_OPEN_FAILED, "io.read_text_file?: failed to open file") - return 0 + return 0 as str let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2) _builtin_syscall(SYS_lseek, fd, 0, 0) @@ -372,7 +372,7 @@ func io.read_text_file?[path: str] : str let buffer: str = mem.alloc?(size + 1) as str if err.check() _builtin_syscall(SYS_close, fd) - return 0 + return 0 as str let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size) _builtin_syscall(SYS_close, fd) @@ -380,7 +380,7 @@ func io.read_text_file?[path: str] : str if n != size mem.free(buffer) err.set(ERR_READ_FAILED, "io.read_text_file?: failed to read file") - return 0 + return 0 as str buffer[n] = 0 return buffer @@ -390,7 +390,7 @@ func io.read_binary_file?[path: str] : io.Buffer let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0) if fd < 0 err.set(ERR_OPEN_FAILED, "io.read_binary_file?: failed to open file") - return 0 + return 0 as io.Buffer let buf: io.Buffer = new io.Buffer buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2) @@ -400,7 +400,7 @@ func io.read_binary_file?[path: str] : io.Buffer if err.check() io.Buffer.free(buf) _builtin_syscall(SYS_close, fd) - return 0 + return 0 as io.Buffer let n: i64 = _builtin_syscall(SYS_read, fd, buf->data, buf->size) _builtin_syscall(SYS_close, fd) @@ -408,7 +408,7 @@ func io.read_binary_file?[path: str] : io.Buffer if n != buf->size io.Buffer.free(buf) err.set(ERR_READ_FAILED, "io.read_binary_file?: failed to read file") - return 0 + return 0 as io.Buffer return buf @@ -664,7 +664,7 @@ func str.hex_encode[s: str, s_len: i64] : str out[j] = 0 return out -func str._hex_digit_to_int[d: u8] : i64 +func str._hex_digit_to_int[d: u8] : u8 if d >= 'a' && d <= 'f' return d - 'a' + 10 if d >= 'A' && d <= 'F' @@ -902,13 +902,13 @@ func os.urandom?[n: i64]: ptr err.clear() let buffer: ptr = mem.alloc(n) if err.check() - return 0 + return 0 as 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") - return 0 + return 0 as ptr let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n) _builtin_syscall(SYS_close, fd) @@ -916,7 +916,7 @@ func os.urandom?[n: i64]: ptr if n != bytes_read mem.free(buffer) err.set(ERR_READ_FAILED, "os.urandom?: failed to read /dev/urandom") - return 0 + return 0 as ptr return buffer @@ -968,7 +968,7 @@ func os.list_directory?[path: str] : Array let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0) if fd < 0 err.set(ERR_OPEN_FAILED, "os.list_directory?: failed to open dir") - return 0 + return 0 as Array let files: Array = [] let buf: ptr = mem.alloc(1024) @@ -983,7 +983,7 @@ func os.list_directory?[path: str] : Array _builtin_syscall(SYS_close, fd) err.set(ERR_SYSCALL_FAILED, "os.list_directory?: getdents64() failed") - return 0 + return 0 as Array else if n == 0 break diff --git a/src/typechecker.rs b/src/typechecker.rs index d772ffe..54beae8 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,6 +1,3 @@ -#![allow(dead_code)] -#![allow(unused_variables)] - use std::{cell::RefCell, collections::HashMap, rc::Rc}; use crate::{ @@ -72,11 +69,15 @@ impl Env { pub struct TypeChecker { analyzer: Rc>, + current_function_return_type: String, } impl TypeChecker { pub fn new(analyzer: Rc>) -> TypeChecker { - TypeChecker { analyzer } + TypeChecker { + analyzer, + current_function_return_type: String::new(), + } } pub fn typecheck_stmt(&mut self, env: &mut Env, stmt: &Stmt) -> Result<(), ZernError> { @@ -106,11 +107,15 @@ impl TypeChecker { env.define_var(name.lexeme.clone(), actual_type); } - Stmt::Const { name, value } => {} + Stmt::Const { name: _, value: _ } => { + // handled in the analyzer + } Stmt::Block(stmts) => { + env.push_scope(); for stmt in stmts { self.typecheck_stmt(env, stmt)?; } + env.pop_scope(); } Stmt::If { condition, @@ -140,14 +145,12 @@ impl TypeChecker { env.pop_scope(); } Stmt::Function { - name, + name: _, params, return_type, body, - exported, + exported: _, } => { - env.push_scope(); - if !self.is_valid_type_name(&return_type.lexeme) { return error!( &return_type.loc, @@ -155,7 +158,11 @@ impl TypeChecker { ); } - for (i, param) in params.iter().enumerate() { + self.current_function_return_type = return_type.lexeme.clone(); + + env.push_scope(); + + for param in params { if !self.is_valid_type_name(¶m.var_type.lexeme) { return error!( ¶m.var_name.loc, @@ -170,15 +177,22 @@ impl TypeChecker { env.pop_scope(); } - Stmt::Return(expr) => { - // TODO + Stmt::Return { expr, keyword } => { + let expected = if self.current_function_return_type == "void" { + "i64".into() + } else { + self.current_function_return_type.clone() + }; + expect_type!(self.typecheck_expr(env, expr)?, expected, keyword.loc); } Stmt::Break => {} Stmt::Continue => {} Stmt::Extern(_) => { // handled in the analyzer } - Stmt::Struct { name, fields } => {} + Stmt::Struct { name: _, fields: _ } => { + // TODO: validate types + } } Ok(()) } @@ -265,7 +279,7 @@ impl TypeChecker { match left.as_ref() { Expr::Variable(name) => { - let var_type = match env.get_var_type(&name.lexeme) { + let existing_var_type = match env.get_var_type(&name.lexeme) { Some(x) => x, None => { return error!( @@ -274,7 +288,7 @@ impl TypeChecker { ); } }; - expect_type!(*var_type, value_type, name.loc); + expect_type!(value_type, *existing_var_type, name.loc); } Expr::Index { expr, @@ -403,6 +417,7 @@ impl TypeChecker { } Expr::Cast { expr, type_name } => { self.typecheck_expr(env, expr)?; + // TODO: validate target type Ok(type_name.lexeme.clone()) } }