typecheck if and while conditions, disallow ptr multiplication
This commit is contained in:
@@ -120,6 +120,7 @@ impl Analyzer {
|
||||
}
|
||||
}
|
||||
Stmt::If {
|
||||
keyword: _,
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
@@ -128,7 +129,11 @@ impl Analyzer {
|
||||
self.analyze_stmt(then_branch)?;
|
||||
self.analyze_stmt(else_branch)?;
|
||||
}
|
||||
Stmt::While { condition, body } => {
|
||||
Stmt::While {
|
||||
keyword: _,
|
||||
condition,
|
||||
body,
|
||||
} => {
|
||||
self.analyze_expr(condition)?;
|
||||
self.analyze_stmt(body)?;
|
||||
}
|
||||
|
||||
@@ -260,6 +260,7 @@ _builtin_environ:
|
||||
env.pop_scope();
|
||||
}
|
||||
Stmt::If {
|
||||
keyword: _,
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
@@ -276,7 +277,11 @@ _builtin_environ:
|
||||
self.compile_stmt(env, else_branch)?;
|
||||
emit!(&mut self.output, "{}:", end_label);
|
||||
}
|
||||
Stmt::While { condition, body } => {
|
||||
Stmt::While {
|
||||
keyword: _,
|
||||
condition,
|
||||
body,
|
||||
} => {
|
||||
let old_loop_begin_label = env.loop_begin_label.clone();
|
||||
let old_loop_end_label = env.loop_end_label.clone();
|
||||
let old_loop_continue_label = env.loop_continue_label.clone();
|
||||
|
||||
@@ -20,11 +20,13 @@ pub enum Stmt {
|
||||
},
|
||||
Block(Vec<Stmt>),
|
||||
If {
|
||||
keyword: Token,
|
||||
condition: Expr,
|
||||
then_branch: Box<Stmt>,
|
||||
else_branch: Box<Stmt>,
|
||||
},
|
||||
While {
|
||||
keyword: Token,
|
||||
condition: Expr,
|
||||
body: Box<Stmt>,
|
||||
},
|
||||
@@ -280,6 +282,7 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn if_statement(&mut self) -> Result<Stmt, ZernError> {
|
||||
let keyword = self.previous().clone();
|
||||
let condition = self.expression()?;
|
||||
let then_branch = self.block()?;
|
||||
let else_branch = if self.match_token(&[TokenType::KeywordElse]) {
|
||||
@@ -292,6 +295,7 @@ impl Parser {
|
||||
Box::new(Stmt::Block(vec![]))
|
||||
};
|
||||
Ok(Stmt::If {
|
||||
keyword,
|
||||
condition,
|
||||
then_branch: Box::new(then_branch),
|
||||
else_branch,
|
||||
@@ -299,9 +303,11 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn while_statement(&mut self) -> Result<Stmt, ZernError> {
|
||||
let keyword = self.previous().clone();
|
||||
let condition = self.expression()?;
|
||||
let body = self.block()?;
|
||||
Ok(Stmt::While {
|
||||
keyword,
|
||||
condition,
|
||||
body: Box::new(body),
|
||||
})
|
||||
|
||||
@@ -53,7 +53,7 @@ func mem._split_block[blk: mem.Block, needed: i64] : void
|
||||
new_blk->next = blk->next
|
||||
new_blk->prev = blk
|
||||
|
||||
if blk->next
|
||||
if blk->next as ptr
|
||||
let next: mem.Block = blk->next
|
||||
next->prev = new_blk
|
||||
else
|
||||
@@ -83,7 +83,7 @@ func mem._request_space?[size: i64] : mem.Block
|
||||
blk->prev = 0 as mem.Block
|
||||
|
||||
let tail: mem.Block = mem.read64(_builtin_heap_tail()) as mem.Block
|
||||
if tail
|
||||
if tail as ptr
|
||||
tail->next = blk
|
||||
blk->prev = tail
|
||||
|
||||
@@ -99,7 +99,7 @@ func mem.alloc?[size: i64] : any
|
||||
size = mem._align(size)
|
||||
|
||||
let cur: mem.Block = mem.read64(_builtin_heap_head()) as mem.Block
|
||||
while cur
|
||||
while cur as ptr
|
||||
if cur->free && cur->size >= size
|
||||
mem._split_block(cur, size)
|
||||
cur->free = false
|
||||
@@ -133,7 +133,7 @@ func mem.free[x: any] : void
|
||||
if expected_next as ptr == next as ptr
|
||||
blk->size = blk->size + MEM_BLOCK_SIZE + next->size
|
||||
blk->next = next->next
|
||||
if next->next
|
||||
if next->next as ptr
|
||||
let b: mem.Block = next->next
|
||||
b->prev = blk
|
||||
if mem.read64(_builtin_heap_tail()) == next as ptr
|
||||
@@ -145,7 +145,7 @@ func mem.free[x: any] : void
|
||||
if expected_blk as ptr == blk as ptr
|
||||
prev->size = prev->size + MEM_BLOCK_SIZE + blk->size
|
||||
prev->next = blk->next
|
||||
if blk->next
|
||||
if blk->next as ptr
|
||||
let b: mem.Block = blk->next
|
||||
b->prev = prev
|
||||
if mem.read64(_builtin_heap_tail()) == blk as ptr
|
||||
@@ -154,12 +154,12 @@ func mem.free[x: any] : void
|
||||
|
||||
let block_total: i64 = blk->size + MEM_BLOCK_SIZE
|
||||
if (blk as i64 & 4095) == 0 && (block_total & 4095) == 0
|
||||
if blk->prev
|
||||
if blk->prev as ptr
|
||||
let b: mem.Block = blk->prev
|
||||
b->next = blk->next
|
||||
else
|
||||
mem.write64(_builtin_heap_head(), blk->next)
|
||||
if blk->next
|
||||
if blk->next as ptr
|
||||
let b: mem.Block = blk->next
|
||||
b->prev = blk->prev
|
||||
else
|
||||
@@ -194,7 +194,7 @@ func mem.realloc?[x: ptr, new_size: i64] : ptr
|
||||
if combined >= new_size
|
||||
blk->size = combined
|
||||
blk->next = next->next
|
||||
if next->next
|
||||
if next->next as ptr
|
||||
let b: mem.Block = next->next
|
||||
b->prev = blk
|
||||
if mem.read64(_builtin_heap_tail()) == next as ptr
|
||||
@@ -771,7 +771,7 @@ struct Array
|
||||
func array.new[] : Array
|
||||
return new Array
|
||||
|
||||
func array.new_with_size_zeroed[size: i64] : Array
|
||||
func array.new_preallocated_and_zeroed[size: i64] : Array
|
||||
let xs: Array = new Array
|
||||
if size > 0
|
||||
xs->data = must(mem.realloc?(xs->data, size * 8)) as ptr
|
||||
@@ -817,13 +817,13 @@ func array.slice[xs: Array, start: i64, length: i64] : Array
|
||||
if start < 0 || length < 0 || start + length > xs->size
|
||||
panic("array.slice out of bounds")
|
||||
|
||||
let new_array: Array = array.new_with_size_zeroed(length)
|
||||
let new_array: Array = array.new_preallocated_and_zeroed(length)
|
||||
for i in 0..length
|
||||
array.set(new_array, i, array.nth(xs, start + i))
|
||||
return new_array
|
||||
|
||||
func array.concat[a: Array, b: Array] : Array
|
||||
let new_array: Array = array.new_with_size_zeroed(a->size + b->size)
|
||||
let new_array: Array = array.new_preallocated_and_zeroed(a->size + b->size)
|
||||
for i in 0..a->size
|
||||
array.set(new_array, i, array.nth(a, i))
|
||||
for i in 0..b->size
|
||||
@@ -861,7 +861,7 @@ func alg.count[arr: Array, item: any] : i64
|
||||
return count
|
||||
|
||||
func alg.map[arr: Array, fn: fnptr] : Array
|
||||
let out: Array = array.new_with_size_zeroed(arr->size)
|
||||
let out: Array = array.new_preallocated_and_zeroed(arr->size)
|
||||
for i in 0..arr->size
|
||||
array.set(out, i, fn(array.nth(arr, i)))
|
||||
return out
|
||||
|
||||
@@ -51,6 +51,7 @@ impl Env {
|
||||
}
|
||||
|
||||
pub fn pop_scope(&mut self) {
|
||||
assert!(!self.scopes.is_empty());
|
||||
self.scopes.pop();
|
||||
}
|
||||
|
||||
@@ -119,15 +120,29 @@ impl TypeChecker {
|
||||
env.pop_scope();
|
||||
}
|
||||
Stmt::If {
|
||||
keyword,
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
} => {
|
||||
self.typecheck_expr(env, condition)?;
|
||||
expect_types!(
|
||||
self.typecheck_expr(env, condition)?,
|
||||
["i64", "u8", "ptr", "bool"],
|
||||
keyword.loc
|
||||
);
|
||||
self.typecheck_stmt(env, then_branch)?;
|
||||
self.typecheck_stmt(env, else_branch)?;
|
||||
}
|
||||
Stmt::While { condition, body } => {
|
||||
Stmt::While {
|
||||
keyword,
|
||||
condition,
|
||||
body,
|
||||
} => {
|
||||
expect_types!(
|
||||
self.typecheck_expr(env, condition)?,
|
||||
["i64", "u8", "ptr", "bool"],
|
||||
keyword.loc
|
||||
);
|
||||
self.typecheck_expr(env, condition)?;
|
||||
self.typecheck_stmt(env, body)?;
|
||||
}
|
||||
@@ -202,30 +217,43 @@ impl TypeChecker {
|
||||
match expr {
|
||||
Expr::Binary { left, op, right } => {
|
||||
let left_type = self.typecheck_expr(env, left)?;
|
||||
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 {
|
||||
TokenType::Plus
|
||||
| TokenType::Minus
|
||||
| TokenType::Star
|
||||
TokenType::Plus | TokenType::Minus => {
|
||||
expect_types!(left_type, ["i64", "ptr", "u8"], op.loc);
|
||||
expect_types!(
|
||||
self.typecheck_expr(env, right)?,
|
||||
["i64", "ptr", "u8"],
|
||||
op.loc
|
||||
);
|
||||
Ok(left_type)
|
||||
}
|
||||
TokenType::Star
|
||||
| TokenType::Slash
|
||||
| TokenType::Mod
|
||||
| TokenType::Xor
|
||||
| TokenType::BitAnd
|
||||
| TokenType::BitOr
|
||||
| TokenType::ShiftLeft
|
||||
| TokenType::ShiftRight => Ok(left_type),
|
||||
| TokenType::ShiftRight => {
|
||||
expect_types!(left_type, ["i64", "u8"], op.loc);
|
||||
expect_types!(self.typecheck_expr(env, right)?, ["i64", "u8"], op.loc);
|
||||
Ok(left_type)
|
||||
}
|
||||
TokenType::DoubleEqual
|
||||
| TokenType::NotEqual
|
||||
| TokenType::Greater
|
||||
| TokenType::GreaterEqual
|
||||
| TokenType::Less
|
||||
| TokenType::LessEqual => Ok("bool".into()),
|
||||
| TokenType::LessEqual => {
|
||||
expect_types!(left_type, ["i64", "ptr", "u8"], op.loc);
|
||||
expect_types!(
|
||||
self.typecheck_expr(env, right)?,
|
||||
["i64", "ptr", "u8"],
|
||||
op.loc
|
||||
);
|
||||
Ok("bool".into())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -347,6 +375,7 @@ impl TypeChecker {
|
||||
// its a function (defined/builtin/extern)
|
||||
if let Some(params) = fn_type.params.clone() {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
// arity is checked in the analyzer
|
||||
expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc);
|
||||
}
|
||||
} else {
|
||||
@@ -390,7 +419,7 @@ impl TypeChecker {
|
||||
expect_types!(self.typecheck_expr(env, index)?, ["i64", "u8"], bracket.loc);
|
||||
Ok("u8".into())
|
||||
}
|
||||
Expr::AddrOf { op, expr } => match *expr.clone() {
|
||||
Expr::AddrOf { op, expr } => match expr.as_ref() {
|
||||
Expr::Variable(name) => {
|
||||
if self.analyzer.borrow().functions.contains_key(&name.lexeme) {
|
||||
Ok("fnptr".into())
|
||||
|
||||
Reference in New Issue
Block a user