typecheck function calls

This commit is contained in:
2026-03-17 16:10:28 +01:00
parent 5846f7e730
commit a3f480dc1d
4 changed files with 206 additions and 113 deletions

View File

@@ -5,25 +5,36 @@ use crate::{
tokenizer::{ZernError, error}, tokenizer::{ZernError, error},
}; };
pub struct StructField {
pub offset: usize,
pub field_type: String,
}
pub struct Analyzer { pub struct Analyzer {
pub functions: HashMap<String, i32>, pub functions: HashMap<String, (String, Option<Vec<String>>)>,
pub constants: HashMap<String, u64>, pub constants: HashMap<String, u64>,
pub structs: HashMap<String, HashMap<String, usize>>, pub structs: HashMap<String, HashMap<String, StructField>>,
} }
impl Analyzer { impl Analyzer {
pub fn new() -> Analyzer { pub fn new() -> Analyzer {
Analyzer { Analyzer {
functions: HashMap::from([ functions: HashMap::from([
("_builtin_heap_head".into(), 0), ("_builtin_heap_head".into(), ("ptr".into(), Some(vec![]))),
("_builtin_heap_tail".into(), 0), ("_builtin_heap_tail".into(), ("ptr".into(), Some(vec![]))),
("_builtin_err_code".into(), 0), ("_builtin_err_code".into(), ("ptr".into(), Some(vec![]))),
("_builtin_err_msg".into(), 0), ("_builtin_err_msg".into(), ("ptr".into(), Some(vec![]))),
("_builtin_read64".into(), 1), (
("_builtin_set64".into(), 2), "_builtin_read64".into(),
("_builtin_syscall".into(), -1), ("i64".into(), Some(vec!["ptr".into()])),
("io.printf".into(), -1), ),
("_builtin_environ".into(), 0), (
"_builtin_set64".into(),
("void".into(), Some(vec!["ptr".into(), "i64".into()])),
),
("_builtin_syscall".into(), ("any".into(), None)),
("io.printf".into(), ("void".into(), None)),
("_builtin_environ".into(), ("ptr".into(), Some(vec![]))),
]), ]),
constants: HashMap::new(), constants: HashMap::new(),
structs: HashMap::new(), structs: HashMap::new(),
@@ -34,7 +45,7 @@ impl Analyzer {
if let Stmt::Function { if let Stmt::Function {
name, name,
params, params,
return_type: _, return_type,
body: _, body: _,
exported: _, exported: _,
} = stmt } = stmt
@@ -42,8 +53,13 @@ impl Analyzer {
if self.functions.contains_key(&name.lexeme) { if self.functions.contains_key(&name.lexeme) {
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme)); return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
} }
self.functions self.functions.insert(
.insert(name.lexeme.clone(), params.len() as i32); name.lexeme.clone(),
(
return_type.lexeme.clone(),
Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()),
),
);
} }
Ok(()) Ok(())
} }
@@ -127,14 +143,21 @@ impl Analyzer {
if self.functions.contains_key(&name.lexeme) { if self.functions.contains_key(&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(name.lexeme.clone(), -1); self.functions
.insert(name.lexeme.clone(), ("any".into(), None));
} }
Stmt::Struct { name, fields } => { Stmt::Struct { name, fields } => {
let mut fields_map: HashMap<String, usize> = HashMap::new(); let mut fields_map: HashMap<String, StructField> = HashMap::new();
let mut offset: usize = 0; let mut offset: usize = 0;
for field in fields { for field in fields {
fields_map.insert(field.var_name.lexeme.clone(), offset); fields_map.insert(
field.var_name.lexeme.clone(),
StructField {
offset,
field_type: field.var_type.lexeme.clone(),
},
);
offset += 8; offset += 8;
} }
@@ -172,11 +195,18 @@ impl Analyzer {
if let Expr::Variable(callee_name) = *callee.clone() { if let Expr::Variable(callee_name) = *callee.clone() {
if self.functions.contains_key(&callee_name.lexeme) { if self.functions.contains_key(&callee_name.lexeme) {
// its a function (defined/builtin/extern) // its a function (defined/builtin/extern)
if let Some(arity) = self.functions.get(&callee_name.lexeme) { if let Some(params) = self.functions.get(&callee_name.lexeme) {
if *arity >= 0 && *arity != args.len() as i32 { // if its None, its variadic
if let (_, Some(params)) = params
&& params.len() != args.len()
{
return error!( return error!(
&paren.loc, &paren.loc,
format!("expected {} arguments, got {}", arity, args.len()) format!(
"expected {} arguments, got {}",
params.len(),
args.len()
)
); );
} }
} else { } else {
@@ -203,7 +233,11 @@ impl Analyzer {
self.analyze_expr(expr)?; self.analyze_expr(expr)?;
} }
} }
Expr::Index { expr, index } => { Expr::Index {
expr,
bracket: _,
index,
} => {
self.analyze_expr(expr)?; self.analyze_expr(expr)?;
self.analyze_expr(index)?; self.analyze_expr(index)?;
} }
@@ -214,6 +248,9 @@ impl Analyzer {
Expr::MemberAccess { left, field: _ } => { Expr::MemberAccess { left, field: _ } => {
self.analyze_expr(left)?; self.analyze_expr(left)?;
} }
Expr::Cast { expr, type_name: _ } => {
self.analyze_expr(expr)?;
}
} }
Ok(()) Ok(())
} }

View File

@@ -81,7 +81,7 @@ func net.read[s: i64, buffer: ptr, size: i64] : i64
func net.close[s: i64] : void func net.close[s: i64] : void
_builtin_syscall(SYS_close, s) _builtin_syscall(SYS_close, s)
func net.pack_addr[a: i64, b: i64, c: i64, d: i64] : i64 func net.pack_addr[a: u8, b: u8, c: u8, d: u8] : i64
return (a << 24) | (b << 16) | (c << 8) | d return (a << 24) | (b << 16) | (c << 8) | d
struct net.UDPSocket struct net.UDPSocket
@@ -166,7 +166,7 @@ func net.encode_dns_name[domain: str] : io.Buffer
let part_len: i64 = i - part_start let part_len: i64 = i - part_start
buf->data[out_pos] = part_len & 0xff buf->data[out_pos] = part_len & 0xff
out_pos = out_pos + 1 out_pos = out_pos + 1
mem.copy(domain + part_start, buf->data + out_pos, part_len) mem.copy(domain as ptr + part_start, buf->data + out_pos, part_len)
out_pos = out_pos + part_len out_pos = out_pos + part_len
part_start = i + 1 part_start = i + 1
i = i + 1 i = i + 1
@@ -201,7 +201,7 @@ func net.resolve?[domain: str] : i64
let query: io.Buffer = net.build_dns_query(domain, DNS_TYPE_A) let query: io.Buffer = net.build_dns_query(domain, DNS_TYPE_A)
// TODO: this probably shouldnt be hardcoded // TODO: this probably shouldnt be hardcoded
let s: net.UDPSocket = net.create_udp_client?(net.pack_addr(8, 8, 8, 8), 53) let s: net.UDPSocket = net.create_udp_client?(net.pack_addr(8 as u8, 8 as u8, 8 as u8, 8 as u8), 53)
if err.check() if err.check()
return -1 return -1

View File

@@ -16,11 +16,11 @@ func err.check[] : bool
return err.code() != 0 return err.code() != 0
func err.clear[] : void func err.clear[] : void
err.set(0, 0) err.set(0, 0 as str)
func err.set[code: i64, msg: str] : void func err.set[code: i64, msg: str] : void
mem.write64(_builtin_err_code(), code) mem.write64(_builtin_err_code(), code)
mem.write64(_builtin_err_msg(), msg) mem.write64(_builtin_err_msg(), msg as ptr as i64)
func must[x: any] : any func must[x: any] : any
if err.check() if err.check()
@@ -57,7 +57,7 @@ func mem._split_block[blk: mem.Block, needed: i64] : void
let next: mem.Block = blk->next let next: mem.Block = blk->next
next->prev = new_blk next->prev = new_blk
else else
mem.write64(_builtin_heap_tail(), new_blk) mem.write64(_builtin_heap_tail(), new_blk as ptr as i64)
blk->size = needed blk->size = needed
blk->next = new_blk blk->next = new_blk
@@ -87,7 +87,7 @@ func mem._request_space?[size: i64] : mem.Block
tail->next = blk tail->next = blk
blk->prev = tail blk->prev = tail
mem.write64(_builtin_heap_tail(), blk) mem.write64(_builtin_heap_tail(), blk as ptr as i64)
return blk return blk
func mem.alloc?[size: i64] : ptr func mem.alloc?[size: i64] : ptr
@@ -98,7 +98,7 @@ func mem.alloc?[size: i64] : ptr
size = mem._align(size) size = mem._align(size)
let cur: mem.Block = mem.read64(_builtin_heap_head()) let cur: mem.Block = mem.read64(_builtin_heap_head()) as mem.Block
while cur while cur
if cur->free && cur->size >= size if cur->free && cur->size >= size
mem._split_block(cur, size) mem._split_block(cur, size)
@@ -111,7 +111,7 @@ func mem.alloc?[size: i64] : ptr
return 0 return 0
if !mem.read64(_builtin_heap_head()) if !mem.read64(_builtin_heap_head())
mem.write64(_builtin_heap_head(), blk) mem.write64(_builtin_heap_head(), blk as ptr as i64)
mem._split_block(blk, size) mem._split_block(blk, size)
@@ -120,50 +120,50 @@ func mem.alloc?[size: i64] : ptr
func mem.alloc[size: i64] : ptr func mem.alloc[size: i64] : ptr
return must(mem.alloc?(size)) return must(mem.alloc?(size))
func mem.free[x: ptr] : void func mem.free[x: any] : void
if !x if !(x as ptr)
return 0 return 0
let blk: mem.Block = x - MEM_BLOCK_SIZE let blk: mem.Block = (x as ptr - MEM_BLOCK_SIZE) as mem.Block
blk->free = true blk->free = true
let next: mem.Block = blk->next let next: mem.Block = blk->next
if next && next->free if next as ptr && next->free
let expected_next: mem.Block = blk + MEM_BLOCK_SIZE + blk->size let expected_next: mem.Block = (blk as ptr + MEM_BLOCK_SIZE + blk->size) as mem.Block
if expected_next == next if expected_next as ptr == next as ptr
blk->size = blk->size + MEM_BLOCK_SIZE + next->size blk->size = blk->size + MEM_BLOCK_SIZE + next->size
blk->next = next->next blk->next = next->next
if next->next if next->next
let b: mem.Block = next->next let b: mem.Block = next->next
b->prev = blk b->prev = blk
if mem.read64(_builtin_heap_tail()) == next if mem.read64(_builtin_heap_tail()) == next as ptr
mem.write64(_builtin_heap_tail(), blk) mem.write64(_builtin_heap_tail(), blk as ptr as i64)
let prev: mem.Block = blk->prev let prev: mem.Block = blk->prev
if prev && prev->free if prev as ptr && prev->free
let expected_blk: mem.Block = prev + MEM_BLOCK_SIZE + prev->size let expected_blk: mem.Block = (prev as ptr + MEM_BLOCK_SIZE + prev->size) as mem.Block
if expected_blk == blk if expected_blk as ptr == blk as ptr
prev->size = prev->size + MEM_BLOCK_SIZE + blk->size prev->size = prev->size + MEM_BLOCK_SIZE + blk->size
prev->next = blk->next prev->next = blk->next
if blk->next if blk->next
let b: mem.Block = blk->next let b: mem.Block = blk->next
b->prev = prev b->prev = prev
if mem.read64(_builtin_heap_tail()) == blk if mem.read64(_builtin_heap_tail()) == blk as ptr
mem.write64(_builtin_heap_tail(), prev) mem.write64(_builtin_heap_tail(), prev as ptr as i64)
blk = prev blk = prev
let block_total: i64 = blk->size + MEM_BLOCK_SIZE let block_total: i64 = blk->size + MEM_BLOCK_SIZE
if (blk & 4095) == 0 && (block_total & 4095) == 0 if (blk as i64 & 4095) == 0 && (block_total & 4095) == 0
if blk->prev if blk->prev
let b: mem.Block = blk->prev let b: mem.Block = blk->prev
b->next = blk->next b->next = blk->next
else else
mem.write64(_builtin_heap_head(), blk->next) mem.write64(_builtin_heap_head(), blk->next as ptr as i64)
if blk->next if blk->next
let b: mem.Block = blk->next let b: mem.Block = blk->next
b->prev = blk->prev b->prev = blk->prev
else else
mem.write64(_builtin_heap_tail(), blk->prev) mem.write64(_builtin_heap_tail(), blk->prev as ptr as i64)
_builtin_syscall(SYS_munmap, blk, block_total) _builtin_syscall(SYS_munmap, blk, block_total)
func mem.realloc?[x: ptr, new_size: i64] : ptr func mem.realloc?[x: ptr, new_size: i64] : ptr
@@ -181,15 +181,15 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
new_size = mem._align(new_size) new_size = mem._align(new_size)
let blk: mem.Block = x - MEM_BLOCK_SIZE let blk: mem.Block = (x - MEM_BLOCK_SIZE) as mem.Block
if blk->size >= new_size if blk->size >= new_size
mem._split_block(blk, new_size) mem._split_block(blk, new_size)
return x return x
let next: mem.Block = blk->next let next: mem.Block = blk->next
let expected_next: mem.Block = blk + MEM_BLOCK_SIZE + blk->size let expected_next: mem.Block = (blk as ptr + MEM_BLOCK_SIZE + blk->size) as mem.Block
if next && next->free && expected_next == next if next as ptr && next->free && expected_next as ptr == next as ptr
let combined: i64 = blk->size + MEM_BLOCK_SIZE + next->size let combined: i64 = blk->size + MEM_BLOCK_SIZE + next->size
if combined >= new_size if combined >= new_size
blk->size = combined blk->size = combined
@@ -197,8 +197,8 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
if next->next if next->next
let b: mem.Block = next->next let b: mem.Block = next->next
b->prev = blk b->prev = blk
if mem.read64(_builtin_heap_tail()) == next if mem.read64(_builtin_heap_tail()) == next as ptr
mem.write64(_builtin_heap_tail(), blk) mem.write64(_builtin_heap_tail(), blk as ptr as i64)
mem._split_block(blk, new_size) mem._split_block(blk, new_size)
return x return x
@@ -216,7 +216,7 @@ func mem.zero_and_free[x: ptr, size: i64] : void
func mem.zero[x: ptr, size: i64] : void func mem.zero[x: ptr, size: i64] : void
for i in 0..size for i in 0..size
x[i] = 0 x[i] = 0 as u8
func mem.copy[src: ptr, dst: ptr, n: i64] : void func mem.copy[src: ptr, dst: ptr, n: i64] : void
if dst > src if dst > src
@@ -264,7 +264,7 @@ func mem.write64[x: ptr, d: i64] : void
_builtin_set64(x, d) _builtin_set64(x, d)
// see emit_prologue for the io.printf wrapper // see emit_prologue for the io.printf wrapper
func io._printf_impl[s: str, args: ptr] : void func io._printf_impl[s: ptr, args: ptr] : void
while s[0] while s[0]
if s[0] == '%' if s[0] == '%'
s = s + 1 s = s + 1
@@ -276,10 +276,10 @@ func io._printf_impl[s: str, args: ptr] : void
io.print_i64_hex(mem.read64(args)) io.print_i64_hex(mem.read64(args))
args = args + 8 args = args + 8
else if s[0] == 's' else if s[0] == 's'
io.print(mem.read64(args)) io.print(mem.read64(args) as str)
args = args + 8 args = args + 8
else if s[0] == 'c' else if s[0] == 'c'
io.print_char(mem.read64(args)) io.print_char(mem.read64(args) as u8)
args = args + 8 args = args + 8
else if s[0] == '%' else if s[0] == '%'
io.print_char('%') io.print_char('%')
@@ -300,7 +300,7 @@ func io.println[x: str] : void
io.print("\n") io.print("\n")
func io.print_char[x: u8] : void func io.print_char[x: u8] : void
io.print_sized(^x, 1) io.print_sized(^x as str, 1)
func io.print_bool[x: bool] : void func io.print_bool[x: bool] : void
if x if x
@@ -324,18 +324,18 @@ func io.println_i64[x: i64] : void
mem.free(s) mem.free(s)
func io.read_char[] : u8 func io.read_char[] : u8
let c: u8 = 0 let c: u8 = 0 as u8
_builtin_syscall(SYS_read, 0, ^c, 1) _builtin_syscall(SYS_read, 0, ^c, 1)
return c return c
func io.read_line[]: str func io.read_line[]: str
let MAX_SIZE = 60000 let MAX_SIZE = 60000
let buffer: str = mem.alloc(MAX_SIZE + 1) let buffer: str = mem.alloc(MAX_SIZE + 1) as str
let n: i64 = _builtin_syscall(SYS_read, 0, buffer, MAX_SIZE) let n: i64 = _builtin_syscall(SYS_read, 0, buffer, MAX_SIZE)
if n < 0 if n < 0
n = 0 n = 0
buffer[n] = 0 buffer[n] = 0
buffer = must(mem.realloc?(buffer, n + 1)) buffer = must(mem.realloc?(buffer, n + 1) as str)
return buffer return buffer
struct io.Buffer struct io.Buffer
@@ -369,7 +369,7 @@ func io.read_text_file?[path: str] : str
let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2) let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2)
_builtin_syscall(SYS_lseek, fd, 0, 0) _builtin_syscall(SYS_lseek, fd, 0, 0)
let buffer: str = mem.alloc?(size + 1) let buffer: str = mem.alloc?(size + 1) as str
if err.check() if err.check()
_builtin_syscall(SYS_close, fd) _builtin_syscall(SYS_close, fd)
return 0 return 0
@@ -413,7 +413,7 @@ func io.read_binary_file?[path: str] : io.Buffer
return buf return buf
func io.write_file?[path: str, content: str] : void func io.write_file?[path: str, content: str] : void
io.write_binary_file?(path, content, str.len(content)) io.write_binary_file?(path, content as ptr, str.len(content))
func io.write_binary_file?[path: str, content: ptr, size: i64] : void func io.write_binary_file?[path: str, content: ptr, size: i64] : void
err.clear() err.clear()
@@ -436,8 +436,8 @@ func str.len[s: str] : i64
func str.make_copy[s: str] : str func str.make_copy[s: str] : str
let size: i64 = str.len(s) + 1 let size: i64 = str.len(s) + 1
let dup: str = mem.alloc(size) let dup: str = mem.alloc(size) as str
mem.copy(s, dup, size) mem.copy(s as ptr, dup as ptr, size)
return dup return dup
func str.equal[a: str, b: str] : bool func str.equal[a: str, b: str] : bool
@@ -472,9 +472,9 @@ func str.is_alphanumeric[x: u8] : bool
func str.concat[a: str, b: str] : str func str.concat[a: str, b: str] : str
let a_len: i64 = str.len(a) let a_len: i64 = str.len(a)
let b_len: i64 = str.len(b) let b_len: i64 = str.len(b)
let out: str = mem.alloc(a_len + b_len + 1) let out: str = mem.alloc(a_len + b_len + 1) as str
mem.copy(a, out, a_len) mem.copy(a as ptr, out as ptr, a_len)
mem.copy(b, out + a_len, b_len) mem.copy(b as ptr, out as ptr + a_len, b_len)
out[a_len + b_len] = 0 out[a_len + b_len] = 0
return out return out
@@ -505,15 +505,15 @@ func str.substr[s: str, start: i64, length: i64] : str
if start < 0 || length < 0 || start + length > str.len(s) if start < 0 || length < 0 || start + length > str.len(s)
panic("str.substr out of bounds") panic("str.substr out of bounds")
let out: str = mem.alloc(length + 1) let out: str = mem.alloc(length + 1) as str
mem.copy(s + start, out, length) mem.copy(s as ptr + start, out as ptr, length)
out[length] = 0 out[length] = 0
return out return out
func str.trim[s: str] : str func str.trim[s: str] : str
let len: i64 = str.len(s) let len: i64 = str.len(s)
if len == 0 if len == 0
let out: str = mem.alloc(1) let out: str = mem.alloc(1) as str
out[0] = 0 out[0] = 0
return out return out
@@ -562,7 +562,7 @@ func str.split[haystack: str, needle: str]: Array
func str.reverse[s: str] : str func str.reverse[s: str] : str
let len: i64 = str.len(s) let len: i64 = str.len(s)
let out: str = mem.alloc(len + 1) let out: str = mem.alloc(len + 1) as str
for i in 0..len for i in 0..len
out[i] = s[len - i - 1] out[i] = s[len - i - 1]
@@ -571,7 +571,7 @@ func str.reverse[s: str] : str
func str.from_i64[n: i64] : str func str.from_i64[n: i64] : str
if n == 0 if n == 0
let out: str = mem.alloc(2) let out: str = mem.alloc(2) as str
out[0] = '0' out[0] = '0'
out[1] = 0 out[1] = 0
return out return out
@@ -582,7 +582,7 @@ func str.from_i64[n: i64] : str
if n == -9223372036854775808 if n == -9223372036854775808
return str.make_copy("-9223372036854775808") return str.make_copy("-9223372036854775808")
n = -n n = -n
let buf: str = mem.alloc(21) // enough to fit -MAX_I64 let buf: str = mem.alloc(21) as str // enough to fit -MAX_I64
let end = 20 let end = 20
buf[end] = 0 buf[end] = 0
end = end - 1 end = end - 1
@@ -593,7 +593,7 @@ func str.from_i64[n: i64] : str
if neg if neg
buf[end] = '-' buf[end] = '-'
end = end - 1 end = end - 1
let s: str = str.make_copy(buf + end + 1) let s: str = str.make_copy((buf as ptr + end + 1) as str)
mem.free(buf) mem.free(buf)
return s return s
@@ -601,13 +601,13 @@ func str.hex_from_i64[n: i64] : str
let hex_chars: str = "0123456789abcdef" let hex_chars: str = "0123456789abcdef"
if n == 0 if n == 0
let out: str = mem.alloc(2) let out: str = mem.alloc(2) as str
out[0] = '0' out[0] = '0'
out[1] = 0 out[1] = 0
return out return out
let mask: i64 = (1 << 60) - 1 let mask: i64 = (1 << 60) - 1
let buf: str = mem.alloc(17) let buf: str = mem.alloc(17) as str
let len = 0 let len = 0
while n != 0 while n != 0
@@ -615,7 +615,7 @@ func str.hex_from_i64[n: i64] : str
n = (n >> 4) & mask n = (n >> 4) & mask
len = len + 1 len = len + 1
let out: str = mem.alloc(len + 1) let out: str = mem.alloc(len + 1) as str
let j = 0 let j = 0
while j < len while j < len
out[j] = buf[len - 1 - j] out[j] = buf[len - 1 - j]
@@ -626,7 +626,7 @@ func str.hex_from_i64[n: i64] : str
return out return out
func str.from_char[c: u8] : str func str.from_char[c: u8] : str
let s: str = mem.alloc(2) let s: str = mem.alloc(2) as str
s[0] = c s[0] = c
s[1] = 0 s[1] = 0
return s return s
@@ -652,7 +652,7 @@ func str.parse_i64[s: str] : i64
func str.hex_encode[s: str, s_len: i64] : str func str.hex_encode[s: str, s_len: i64] : str
let hex_chars: str = "0123456789abcdef" let hex_chars: str = "0123456789abcdef"
let j = 0 let j = 0
let out: str = mem.alloc(s_len * 2 + 1) let out: str = mem.alloc(s_len * 2 + 1) as str
for i in 0..s_len for i in 0..s_len
let high: u8 = (s[i] >> 4) & 15 let high: u8 = (s[i] >> 4) & 15
@@ -678,7 +678,7 @@ func str.hex_decode[s: str] : str
let i = 0 let i = 0
let j = 0 let j = 0
let out: str = mem.alloc(s_len / 2 + 1) let out: str = mem.alloc(s_len / 2 + 1) as str
while i < s_len while i < s_len
out[j] = str._hex_digit_to_int(s[i]) * 16 + str._hex_digit_to_int(s[i + 1]) out[j] = str._hex_digit_to_int(s[i]) * 16 + str._hex_digit_to_int(s[i + 1])
@@ -774,7 +774,7 @@ func array.new[] : Array
func array.new_with_size_zeroed[size: i64] : Array func array.new_with_size_zeroed[size: i64] : Array
let xs: Array = new Array let xs: Array = new Array
if size > 0 if size > 0
xs->data = must(mem.realloc?(xs->data, size * 8)) xs->data = must(mem.realloc?(xs->data, size * 8)) as ptr
mem.zero(xs->data, size * 8) mem.zero(xs->data, size * 8)
xs->size = size xs->size = size
xs->capacity = size xs->capacity = size
@@ -843,7 +843,7 @@ func alg._partition[arr: Array, low: i64, high: i64] : i64
let pivot: i64 = array.nth(arr, high) let pivot: i64 = array.nth(arr, high)
let i: i64 = low - 1 let i: i64 = low - 1
for j in (low)..high for j in (low)..high
if array.nth(arr, j) <= pivot if array.nth(arr, j) as i64 <= pivot
i = i + 1 i = i + 1
let temp: i64 = array.nth(arr ,i) let temp: i64 = array.nth(arr ,i)
array.set(arr, i, array.nth(arr, j)) array.set(arr, i, array.nth(arr, j))
@@ -866,14 +866,14 @@ func alg.map[arr: Array, fn: ptr] : Array
array.set(out, i, fn(array.nth(arr, i))) array.set(out, i, fn(array.nth(arr, i)))
return out return out
func alg.filter[arr: Array, fn: ptr] : Array func alg.filter[arr: Array, fn: fnptr] : Array
let out: Array = [] let out: Array = []
for i in 0..arr->size for i in 0..arr->size
if fn(array.nth(arr, i)) if fn(array.nth(arr, i))
array.push(out, array.nth(arr, i)) array.push(out, array.nth(arr, i))
return out return out
func alg.reduce[arr: Array, fn: ptr, acc: any] : any func alg.reduce[arr: Array, fn: fnptr, acc: any] : any
for i in 0..arr->size for i in 0..arr->size
acc = fn(acc, array.nth(arr, i)) acc = fn(acc, array.nth(arr, i))
return acc return acc
@@ -990,7 +990,7 @@ func os.list_directory?[path: str] : Array
let pos = 0 let pos = 0
while pos < n while pos < n
let len: i64 = mem.read16(buf + pos + 16) let len: i64 = mem.read16(buf + pos + 16)
let name: str = buf + pos + 19 let name: ptr = buf + pos + 19
if name[0] if name[0]
let skip: bool = false let skip: bool = false
// skip if name is exactly '.' or '..' // skip if name is exactly '.' or '..'

View File

@@ -13,7 +13,7 @@ type Type = String;
macro_rules! expect_type { macro_rules! expect_type {
($expr_type:expr, $expected:expr, $loc:expr) => { ($expr_type:expr, $expected:expr, $loc:expr) => {
if $expr_type != $expected { if $expected != "any" && $expr_type != "any" && $expr_type != $expected {
return error!( return error!(
$loc, $loc,
format!("expected type '{}', got '{}'", $expected, $expr_type) format!("expected type '{}', got '{}'", $expected, $expr_type)
@@ -24,7 +24,7 @@ macro_rules! expect_type {
macro_rules! expect_types { macro_rules! expect_types {
($expr_type:expr, [$( $expected:expr ),+], $loc:expr) => { ($expr_type:expr, [$( $expected:expr ),+], $loc:expr) => {
if $( $expr_type != $expected )&&+ { if $expr_type != "any" && $( $expr_type != $expected )&&+ {
return error!( return error!(
$loc, $loc,
format!( format!(
@@ -92,7 +92,7 @@ impl TypeChecker {
var_type, var_type,
initializer, initializer,
} => { } => {
let inferred = self.typecheck_expr(env, initializer)?; let mut actual_type = self.typecheck_expr(env, initializer)?;
if let Some(var_type) = var_type { if let Some(var_type) = var_type {
if !self.is_valid_type_name(&var_type.lexeme) { if !self.is_valid_type_name(&var_type.lexeme) {
return error!( return error!(
@@ -100,10 +100,14 @@ impl TypeChecker {
"unrecognized type: ".to_owned() + &var_type.lexeme "unrecognized type: ".to_owned() + &var_type.lexeme
); );
} }
expect_type!(inferred, var_type.lexeme, var_type.loc); expect_type!(actual_type, var_type.lexeme, var_type.loc);
if actual_type == "any" {
actual_type = var_type.lexeme.clone();
}
} }
env.define_var(name.lexeme.clone(), inferred); env.define_var(name.lexeme.clone(), actual_type);
} }
Stmt::Const { name, value } => {} Stmt::Const { name, value } => {}
Stmt::Block(stmts) => { Stmt::Block(stmts) => {
@@ -120,13 +124,24 @@ impl TypeChecker {
self.typecheck_stmt(env, then_branch)?; self.typecheck_stmt(env, then_branch)?;
self.typecheck_stmt(env, else_branch)?; self.typecheck_stmt(env, else_branch)?;
} }
Stmt::While { condition, body } => todo!(), Stmt::While { condition, body } => {
self.typecheck_expr(env, condition)?;
self.typecheck_stmt(env, body)?;
}
Stmt::For { Stmt::For {
var, var,
start, start,
end, end,
body, body,
} => todo!(), } => {
expect_type!(self.typecheck_expr(env, start)?, "i64", var.loc);
expect_type!(self.typecheck_expr(env, end)?, "i64", var.loc);
env.push_scope();
env.define_var(var.lexeme.clone(), "i64".into());
self.typecheck_stmt(env, body)?;
env.pop_scope();
}
Stmt::Function { Stmt::Function {
name, name,
params, params,
@@ -161,9 +176,11 @@ impl TypeChecker {
Stmt::Return(expr) => { Stmt::Return(expr) => {
// TODO // TODO
} }
Stmt::Break => todo!(), Stmt::Break => {}
Stmt::Continue => todo!(), Stmt::Continue => {}
Stmt::Extern(token) => todo!(), Stmt::Extern(_) => {
// handled in the analyzer
}
Stmt::Struct { name, fields } => {} Stmt::Struct { name, fields } => {}
} }
Ok(()) Ok(())
@@ -172,8 +189,13 @@ impl TypeChecker {
pub fn typecheck_expr(&mut self, env: &mut Env, expr: &Expr) -> Result<Type, ZernError> { pub fn typecheck_expr(&mut self, env: &mut Env, expr: &Expr) -> Result<Type, ZernError> {
match expr { match expr {
Expr::Binary { left, op, right } => { Expr::Binary { left, op, right } => {
expect_types!(self.typecheck_expr(env, left)?, ["i64", "ptr"], op.loc); let left_type = self.typecheck_expr(env, left)?;
expect_types!(self.typecheck_expr(env, right)?, ["i64", "ptr"], op.loc); expect_types!(left_type, ["i64", "ptr", "u8"], op.loc);
expect_types!(
self.typecheck_expr(env, right)?,
["i64", "ptr", "u8"],
op.loc
);
match op.token_type { match op.token_type {
TokenType::Plus TokenType::Plus
@@ -185,7 +207,7 @@ impl TypeChecker {
| TokenType::BitAnd | TokenType::BitAnd
| TokenType::BitOr | TokenType::BitOr
| TokenType::ShiftLeft | TokenType::ShiftLeft
| TokenType::ShiftRight => Ok("i64".into()), | TokenType::ShiftRight => Ok(left_type),
TokenType::DoubleEqual TokenType::DoubleEqual
| TokenType::NotEqual | TokenType::NotEqual
| TokenType::Greater | TokenType::Greater
@@ -195,7 +217,19 @@ impl TypeChecker {
_ => unreachable!(), _ => unreachable!(),
} }
} }
Expr::Logical { left, op, right } => todo!(), Expr::Logical { left, op, right } => {
expect_types!(
self.typecheck_expr(env, left)?,
["bool", "i64", "ptr"],
op.loc
);
expect_types!(
self.typecheck_expr(env, right)?,
["bool", "i64", "ptr"],
op.loc
);
Ok("bool".into())
}
Expr::Grouping(expr) => self.typecheck_expr(env, expr), Expr::Grouping(expr) => self.typecheck_expr(env, expr),
Expr::Literal(token) => match token.token_type { Expr::Literal(token) => match token.token_type {
TokenType::Number => Ok("i64".into()), TokenType::Number => Ok("i64".into()),
@@ -213,7 +247,7 @@ impl TypeChecker {
Ok("i64".into()) Ok("i64".into())
} }
TokenType::Bang => { TokenType::Bang => {
expect_type!(right_type, "bool", op.loc); expect_types!(right_type, ["bool", "i64", "ptr"], op.loc);
Ok("bool".into()) Ok("bool".into())
} }
_ => unreachable!(), _ => unreachable!(),
@@ -250,8 +284,9 @@ impl TypeChecker {
bracket, bracket,
index, index,
} => { } => {
expect_type!(self.typecheck_expr(env, expr)?, "ptr", bracket.loc); expect_types!(self.typecheck_expr(env, expr)?, ["ptr", "str"], bracket.loc);
expect_type!(self.typecheck_expr(env, index)?, "u8", bracket.loc); expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc);
expect_types!(value_type, ["u8", "i64"], bracket.loc);
} }
Expr::MemberAccess { left, field } => { Expr::MemberAccess { left, field } => {
let left_type = self.typecheck_expr(env, left)?; let left_type = self.typecheck_expr(env, left)?;
@@ -295,32 +330,53 @@ impl TypeChecker {
.functions .functions
.contains_key(&callee_name.lexeme) .contains_key(&callee_name.lexeme)
{ {
let params = self.analyzer.borrow().functions[&callee_name.lexeme].clone();
if let (_, Some(params)) = params {
// its a function (defined/builtin/extern) // its a function (defined/builtin/extern)
for (i, arg) in args.iter().enumerate() {
expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc);
}
}
Ok(params.0)
} else { } else {
// its a variable containing function address // its a variable containing function address
expect_type!(self.typecheck_expr(env, callee)?, "fnptr", paren.loc); expect_type!(self.typecheck_expr(env, callee)?, "fnptr", paren.loc);
for arg in args {
self.typecheck_expr(env, arg)?;
}
Ok("any".into())
} }
} else { } else {
// its an expression that evalutes to function address // its an expression that evalutes to function address
expect_type!(self.typecheck_expr(env, callee)?, "fnptr", paren.loc); expect_type!(self.typecheck_expr(env, callee)?, "fnptr", paren.loc);
}
for arg in args { for arg in args {
// TODO: actually check against the function we're calling
self.typecheck_expr(env, arg)?; self.typecheck_expr(env, arg)?;
} }
// TODO: actually look up return type
Ok("any".into()) Ok("any".into())
} }
Expr::ArrayLiteral(exprs) => todo!(), }
Expr::ArrayLiteral(exprs) => {
for expr in exprs {
self.typecheck_expr(env, expr)?;
}
Ok("Array".into())
}
Expr::Index { Expr::Index {
expr, expr,
bracket, bracket,
index, index,
} => todo!(), } => {
Expr::AddrOf { op, expr } => todo!(), expect_types!(self.typecheck_expr(env, expr)?, ["ptr", "str"], bracket.loc);
Expr::New(token) => todo!(), expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc);
Ok("u8".into())
}
Expr::AddrOf { op, expr } => {
self.typecheck_expr(env, expr)?;
Ok("ptr".into())
}
Expr::New(struct_name) => Ok(struct_name.lexeme.clone()),
Expr::MemberAccess { left, field } => { Expr::MemberAccess { left, field } => {
let left_type = self.typecheck_expr(env, left)?; let left_type = self.typecheck_expr(env, left)?;