typecheck function calls
This commit is contained in:
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
120
src/std/std.zr
120
src/std/std.zr
@@ -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 '..'
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
// its a function (defined/builtin/extern)
|
let params = self.analyzer.borrow().functions[&callee_name.lexeme].clone();
|
||||||
|
if let (_, Some(params)) = params {
|
||||||
|
// 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)?;
|
}
|
||||||
|
Ok("any".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: actually look up return type
|
|
||||||
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)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user