diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 94c0b11..05650a5 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -573,15 +573,9 @@ _builtin_environ: match left.as_ref() { Expr::Variable(name) => { - // TODO: move to analyzer let var = match env.get_var(&name.lexeme) { Some(x) => x, - None => { - return error!( - name.loc, - format!("undefined variable: {}", &name.lexeme) - ); - } + None => unreachable!(), }; emit!( &mut self.output, @@ -589,7 +583,11 @@ _builtin_environ: var.stack_offset, ); } - Expr::Index { expr, index } => { + Expr::Index { + expr, + bracket: _, + index, + } => { emit!(&mut self.output, " push rax"); self.compile_expr(env, expr)?; emit!(&mut self.output, " push rax"); @@ -688,7 +686,11 @@ _builtin_environ: } emit!(&mut self.output, " pop rax"); } - Expr::Index { expr, index } => { + Expr::Index { + expr, + bracket: _, + index, + } => { self.compile_expr(env, expr)?; emit!(&mut self.output, " push rax"); self.compile_expr(env, index)?; @@ -739,6 +741,9 @@ _builtin_environ: self.compile_expr(env, left)?; emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset); } + Expr::Cast { expr, type_name: _ } => { + self.compile_expr(env, expr)?; + } } Ok(()) } @@ -772,11 +777,11 @@ _builtin_environ: } }; - let offset = match fields.get(&field.lexeme) { - Some(o) => *o, + let field = match fields.get(&field.lexeme) { + Some(o) => o, None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)), }; - Ok(offset) + Ok(field.offset) } } diff --git a/src/parser.rs b/src/parser.rs index da88eaf..857fffb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -83,6 +83,7 @@ pub enum Expr { ArrayLiteral(Vec), Index { expr: Box, + bracket: Token, index: Box, }, AddrOf { @@ -94,6 +95,10 @@ pub enum Expr { left: Box, field: Token, }, + Cast { + expr: Box, + type_name: Token, + }, } pub struct Parser { @@ -438,7 +443,7 @@ impl Parser { } fn factor(&mut self) -> Result { - let mut expr = self.unary()?; + let mut expr = self.cast()?; while self.match_token(&[ TokenType::Star, @@ -459,6 +464,20 @@ impl Parser { Ok(expr) } + fn cast(&mut self) -> Result { + let mut expr = self.unary()?; + + while self.match_token(&[TokenType::KeywordAs]) { + let type_name = self.consume(TokenType::Identifier, "expected type after 'as'")?; + expr = Expr::Cast { + expr: Box::new(expr), + type_name, + }; + } + + Ok(expr) + } + fn unary(&mut self) -> Result { if self.match_token(&[TokenType::Xor]) { let op = self.previous().clone(); @@ -504,9 +523,10 @@ impl Parser { }; } else if self.match_token(&[TokenType::LeftBracket]) { let index = self.expression()?; - self.consume(TokenType::RightBracket, "expected ']' after index")?; + let bracket = self.consume(TokenType::RightBracket, "expected ']' after index")?; expr = Expr::Index { expr: Box::new(expr), + bracket, index: Box::new(index), } } else if self.match_token(&[TokenType::Arrow]) { diff --git a/src/std/std.zr b/src/std/std.zr index be155bf..9d54377 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -47,7 +47,7 @@ func mem._align[x: i64] : i64 func mem._split_block[blk: mem.Block, needed: i64] : void if blk->size >= needed + MEM_BLOCK_SIZE + 8 - let new_blk: mem.Block = blk + MEM_BLOCK_SIZE + needed + let new_blk: mem.Block = (blk as ptr + MEM_BLOCK_SIZE + needed) as mem.Block new_blk->size = blk->size - needed - MEM_BLOCK_SIZE new_blk->free = true new_blk->next = blk->next @@ -70,17 +70,19 @@ func mem._request_space?[size: i64] : mem.Block // PROT_READ | PROT_WRITE = 3 // MAP_PRIVATE | MAP_ANONYMOUS = 34 // fd = -1, offset = 0 - let blk: mem.Block = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0) - if blk == -1 + 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 + + let blk: mem.Block = blk_ptr as mem.Block blk->size = alloc_size - MEM_BLOCK_SIZE blk->free = false - blk->next = 0 - blk->prev = 0 + blk->next = 0 as mem.Block + blk->prev = 0 as mem.Block - let tail: mem.Block = mem.read64(_builtin_heap_tail()) + let tail: mem.Block = mem.read64(_builtin_heap_tail()) as mem.Block if tail tail->next = blk blk->prev = tail diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 3056a7a..445c93d 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -55,6 +55,7 @@ pub enum TokenType { KeywordExport, KeywordStruct, KeywordNew, + KeywordAs, Indent, Dedent, @@ -371,6 +372,7 @@ impl Tokenizer { "export" => TokenType::KeywordExport, "struct" => TokenType::KeywordStruct, "new" => TokenType::KeywordNew, + "as" => TokenType::KeywordAs, "true" => TokenType::True, "false" => TokenType::False, _ => TokenType::Identifier, diff --git a/src/typechecker.rs b/src/typechecker.rs index ac87ea3..06b5398 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -22,6 +22,21 @@ macro_rules! expect_type { }; } +macro_rules! expect_types { + ($expr_type:expr, [$( $expected:expr ),+], $loc:expr) => { + if $( $expr_type != $expected )&&+ { + return error!( + $loc, + format!( + "expected one of [{}], got '{}'", + [$( $expected ),+].join(", "), + $expr_type + ) + ); + } + }; +} + // TODO: currently they are all just 64 bit values static BUILTIN_TYPES: [&str; 8] = ["void", "u8", "i64", "str", "bool", "ptr", "fnptr", "any"]; @@ -119,6 +134,8 @@ impl TypeChecker { body, exported, } => { + env.push_scope(); + if !self.is_valid_type_name(&return_type.lexeme) { return error!( &return_type.loc, @@ -138,6 +155,8 @@ impl TypeChecker { } self.typecheck_stmt(env, body)?; + + env.pop_scope(); } Stmt::Return(expr) => { // TODO @@ -153,11 +172,31 @@ impl TypeChecker { pub fn typecheck_expr(&mut self, env: &mut Env, expr: &Expr) -> Result { match expr { Expr::Binary { left, op, right } => { - expect_type!(self.typecheck_expr(env, left)?, "i64", op.loc); - Ok("i64".into()) + expect_types!(self.typecheck_expr(env, left)?, ["i64", "ptr"], op.loc); + expect_types!(self.typecheck_expr(env, right)?, ["i64", "ptr"], op.loc); + + match op.token_type { + TokenType::Plus + | TokenType::Minus + | TokenType::Star + | TokenType::Slash + | TokenType::Mod + | TokenType::Xor + | TokenType::BitAnd + | TokenType::BitOr + | TokenType::ShiftLeft + | TokenType::ShiftRight => Ok("i64".into()), + TokenType::DoubleEqual + | TokenType::NotEqual + | TokenType::Greater + | TokenType::GreaterEqual + | TokenType::Less + | TokenType::LessEqual => Ok("bool".into()), + _ => unreachable!(), + } } Expr::Logical { left, op, right } => todo!(), - Expr::Grouping(expr) => todo!(), + Expr::Grouping(expr) => self.typecheck_expr(env, expr), Expr::Literal(token) => match token.token_type { TokenType::Number => Ok("i64".into()), TokenType::Char => Ok("u8".into()), @@ -166,10 +205,23 @@ impl TypeChecker { TokenType::False => Ok("bool".into()), _ => unreachable!(), }, - Expr::Unary { op, right } => todo!(), + Expr::Unary { op, right } => { + let right_type = self.typecheck_expr(env, right)?; + match op.token_type { + TokenType::Minus => { + expect_type!(right_type, "i64", op.loc); + Ok("i64".into()) + } + TokenType::Bang => { + expect_type!(right_type, "bool", op.loc); + Ok("bool".into()) + } + _ => unreachable!(), + } + } Expr::Variable(name) => { if self.analyzer.borrow().constants.contains_key(&name.lexeme) { - Ok("fnptr".into()) + Ok("i64".into()) } else { match env.get_var_type(&name.lexeme) { Some(x) => Ok(x.clone()), @@ -177,7 +229,60 @@ impl TypeChecker { } } } - Expr::Assign { left, op, value } => todo!(), + Expr::Assign { left, op, value } => { + let value_type = self.typecheck_expr(env, value)?; + + match left.as_ref() { + Expr::Variable(name) => { + let var_type = match env.get_var_type(&name.lexeme) { + Some(x) => x, + None => { + return error!( + name.loc, + format!("undefined variable: {}", &name.lexeme) + ); + } + }; + expect_type!(*var_type, value_type, name.loc); + } + Expr::Index { + expr, + bracket, + index, + } => { + expect_type!(self.typecheck_expr(env, expr)?, "ptr", bracket.loc); + expect_type!(self.typecheck_expr(env, index)?, "u8", bracket.loc); + } + Expr::MemberAccess { left, field } => { + let left_type = self.typecheck_expr(env, left)?; + + let analyzer = self.analyzer.borrow(); + let fields = match analyzer.structs.get(&left_type) { + Some(f) => f, + None => { + return error!( + &field.loc, + format!("unknown struct type: {}", left_type) + ); + } + }; + + let f = match fields.get(&field.lexeme) { + Some(o) => o, + None => { + return error!( + &field.loc, + format!("unknown field: {}", &field.lexeme) + ); + } + }; + + expect_type!(value_type, f.field_type, field.loc); + } + _ => return error!(&op.loc, "invalid assignment target"), + } + Ok(value_type) + } Expr::Call { callee, paren, @@ -201,6 +306,7 @@ impl TypeChecker { } for arg in args { + // TODO: actually check against the function we're calling self.typecheck_expr(env, arg)?; } @@ -208,14 +314,34 @@ impl TypeChecker { Ok("any".into()) } Expr::ArrayLiteral(exprs) => todo!(), - Expr::Index { expr, index } => todo!(), + Expr::Index { + expr, + bracket, + index, + } => todo!(), Expr::AddrOf { op, expr } => todo!(), Expr::New(token) => todo!(), Expr::MemberAccess { left, field } => { let left_type = self.typecheck_expr(env, left)?; - // TODO: actually look up left_type->field type - Ok("any".into()) + let analyzer = self.analyzer.borrow(); + let fields = match analyzer.structs.get(&left_type) { + Some(f) => f, + None => { + return error!(&field.loc, format!("unknown struct type: {}", left_type)); + } + }; + + let field = match fields.get(&field.lexeme) { + Some(o) => o, + None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)), + }; + + Ok(field.field_type.clone()) + } + Expr::Cast { expr, type_name } => { + self.typecheck_expr(env, expr)?; + Ok(type_name.lexeme.clone()) } } }