casts, typecheck struct field access

This commit is contained in:
2026-03-17 15:25:28 +01:00
parent 13fa93c611
commit 5846f7e730
5 changed files with 184 additions and 29 deletions

View File

@@ -573,15 +573,9 @@ _builtin_environ:
match left.as_ref() { match left.as_ref() {
Expr::Variable(name) => { Expr::Variable(name) => {
// TODO: move to analyzer
let var = match env.get_var(&name.lexeme) { let var = match env.get_var(&name.lexeme) {
Some(x) => x, Some(x) => x,
None => { None => unreachable!(),
return error!(
name.loc,
format!("undefined variable: {}", &name.lexeme)
);
}
}; };
emit!( emit!(
&mut self.output, &mut self.output,
@@ -589,7 +583,11 @@ _builtin_environ:
var.stack_offset, var.stack_offset,
); );
} }
Expr::Index { expr, index } => { Expr::Index {
expr,
bracket: _,
index,
} => {
emit!(&mut self.output, " push rax"); emit!(&mut self.output, " push rax");
self.compile_expr(env, expr)?; self.compile_expr(env, expr)?;
emit!(&mut self.output, " push rax"); emit!(&mut self.output, " push rax");
@@ -688,7 +686,11 @@ _builtin_environ:
} }
emit!(&mut self.output, " pop rax"); emit!(&mut self.output, " pop rax");
} }
Expr::Index { expr, index } => { Expr::Index {
expr,
bracket: _,
index,
} => {
self.compile_expr(env, expr)?; self.compile_expr(env, expr)?;
emit!(&mut self.output, " push rax"); emit!(&mut self.output, " push rax");
self.compile_expr(env, index)?; self.compile_expr(env, index)?;
@@ -739,6 +741,9 @@ _builtin_environ:
self.compile_expr(env, left)?; self.compile_expr(env, left)?;
emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset); emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset);
} }
Expr::Cast { expr, type_name: _ } => {
self.compile_expr(env, expr)?;
}
} }
Ok(()) Ok(())
} }
@@ -772,11 +777,11 @@ _builtin_environ:
} }
}; };
let offset = match fields.get(&field.lexeme) { let field = match fields.get(&field.lexeme) {
Some(o) => *o, Some(o) => o,
None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)), None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)),
}; };
Ok(offset) Ok(field.offset)
} }
} }

View File

@@ -83,6 +83,7 @@ pub enum Expr {
ArrayLiteral(Vec<Expr>), ArrayLiteral(Vec<Expr>),
Index { Index {
expr: Box<Expr>, expr: Box<Expr>,
bracket: Token,
index: Box<Expr>, index: Box<Expr>,
}, },
AddrOf { AddrOf {
@@ -94,6 +95,10 @@ pub enum Expr {
left: Box<Expr>, left: Box<Expr>,
field: Token, field: Token,
}, },
Cast {
expr: Box<Expr>,
type_name: Token,
},
} }
pub struct Parser { pub struct Parser {
@@ -438,7 +443,7 @@ impl Parser {
} }
fn factor(&mut self) -> Result<Expr, ZernError> { fn factor(&mut self) -> Result<Expr, ZernError> {
let mut expr = self.unary()?; let mut expr = self.cast()?;
while self.match_token(&[ while self.match_token(&[
TokenType::Star, TokenType::Star,
@@ -459,6 +464,20 @@ impl Parser {
Ok(expr) Ok(expr)
} }
fn cast(&mut self) -> Result<Expr, ZernError> {
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<Expr, ZernError> { fn unary(&mut self) -> Result<Expr, ZernError> {
if self.match_token(&[TokenType::Xor]) { if self.match_token(&[TokenType::Xor]) {
let op = self.previous().clone(); let op = self.previous().clone();
@@ -504,9 +523,10 @@ impl Parser {
}; };
} else if self.match_token(&[TokenType::LeftBracket]) { } else if self.match_token(&[TokenType::LeftBracket]) {
let index = self.expression()?; let index = self.expression()?;
self.consume(TokenType::RightBracket, "expected ']' after index")?; let bracket = self.consume(TokenType::RightBracket, "expected ']' after index")?;
expr = Expr::Index { expr = Expr::Index {
expr: Box::new(expr), expr: Box::new(expr),
bracket,
index: Box::new(index), index: Box::new(index),
} }
} else if self.match_token(&[TokenType::Arrow]) { } else if self.match_token(&[TokenType::Arrow]) {

View File

@@ -47,7 +47,7 @@ func mem._align[x: i64] : i64
func mem._split_block[blk: mem.Block, needed: i64] : void func mem._split_block[blk: mem.Block, needed: i64] : void
if blk->size >= needed + MEM_BLOCK_SIZE + 8 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->size = blk->size - needed - MEM_BLOCK_SIZE
new_blk->free = true new_blk->free = true
new_blk->next = blk->next new_blk->next = blk->next
@@ -70,17 +70,19 @@ func mem._request_space?[size: i64] : mem.Block
// PROT_READ | PROT_WRITE = 3 // PROT_READ | PROT_WRITE = 3
// MAP_PRIVATE | MAP_ANONYMOUS = 34 // MAP_PRIVATE | MAP_ANONYMOUS = 34
// fd = -1, offset = 0 // fd = -1, offset = 0
let blk: mem.Block = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0) let blk_ptr: ptr = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0) as ptr
if blk == -1 if blk_ptr == -1
err.set(ERR_ALLOC_FAILED, "mem._request_space: mmap failed") err.set(ERR_ALLOC_FAILED, "mem._request_space: mmap failed")
return 0 return 0
let blk: mem.Block = blk_ptr as mem.Block
blk->size = alloc_size - MEM_BLOCK_SIZE blk->size = alloc_size - MEM_BLOCK_SIZE
blk->free = false blk->free = false
blk->next = 0 blk->next = 0 as mem.Block
blk->prev = 0 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 if tail
tail->next = blk tail->next = blk
blk->prev = tail blk->prev = tail

View File

@@ -55,6 +55,7 @@ pub enum TokenType {
KeywordExport, KeywordExport,
KeywordStruct, KeywordStruct,
KeywordNew, KeywordNew,
KeywordAs,
Indent, Indent,
Dedent, Dedent,
@@ -371,6 +372,7 @@ impl Tokenizer {
"export" => TokenType::KeywordExport, "export" => TokenType::KeywordExport,
"struct" => TokenType::KeywordStruct, "struct" => TokenType::KeywordStruct,
"new" => TokenType::KeywordNew, "new" => TokenType::KeywordNew,
"as" => TokenType::KeywordAs,
"true" => TokenType::True, "true" => TokenType::True,
"false" => TokenType::False, "false" => TokenType::False,
_ => TokenType::Identifier, _ => TokenType::Identifier,

View File

@@ -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 // TODO: currently they are all just 64 bit values
static BUILTIN_TYPES: [&str; 8] = ["void", "u8", "i64", "str", "bool", "ptr", "fnptr", "any"]; static BUILTIN_TYPES: [&str; 8] = ["void", "u8", "i64", "str", "bool", "ptr", "fnptr", "any"];
@@ -119,6 +134,8 @@ impl TypeChecker {
body, body,
exported, exported,
} => { } => {
env.push_scope();
if !self.is_valid_type_name(&return_type.lexeme) { if !self.is_valid_type_name(&return_type.lexeme) {
return error!( return error!(
&return_type.loc, &return_type.loc,
@@ -138,6 +155,8 @@ impl TypeChecker {
} }
self.typecheck_stmt(env, body)?; self.typecheck_stmt(env, body)?;
env.pop_scope();
} }
Stmt::Return(expr) => { Stmt::Return(expr) => {
// TODO // TODO
@@ -153,11 +172,31 @@ 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_type!(self.typecheck_expr(env, left)?, "i64", op.loc); expect_types!(self.typecheck_expr(env, left)?, ["i64", "ptr"], op.loc);
Ok("i64".into()) 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::Logical { left, op, right } => todo!(),
Expr::Grouping(expr) => todo!(), 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()),
TokenType::Char => Ok("u8".into()), TokenType::Char => Ok("u8".into()),
@@ -166,10 +205,23 @@ impl TypeChecker {
TokenType::False => Ok("bool".into()), TokenType::False => Ok("bool".into()),
_ => unreachable!(), _ => 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) => { Expr::Variable(name) => {
if self.analyzer.borrow().constants.contains_key(&name.lexeme) { if self.analyzer.borrow().constants.contains_key(&name.lexeme) {
Ok("fnptr".into()) Ok("i64".into())
} else { } else {
match env.get_var_type(&name.lexeme) { match env.get_var_type(&name.lexeme) {
Some(x) => Ok(x.clone()), 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 { Expr::Call {
callee, callee,
paren, paren,
@@ -201,6 +306,7 @@ impl TypeChecker {
} }
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)?;
} }
@@ -208,14 +314,34 @@ impl TypeChecker {
Ok("any".into()) Ok("any".into())
} }
Expr::ArrayLiteral(exprs) => todo!(), Expr::ArrayLiteral(exprs) => todo!(),
Expr::Index { expr, index } => todo!(), Expr::Index {
expr,
bracket,
index,
} => todo!(),
Expr::AddrOf { op, expr } => todo!(), Expr::AddrOf { op, expr } => todo!(),
Expr::New(token) => todo!(), Expr::New(token) => todo!(),
Expr::MemberAccess { left, field } => { Expr::MemberAccess { left, field } => {
let left_type = self.typecheck_expr(env, left)?; let left_type = self.typecheck_expr(env, left)?;
// TODO: actually look up left_type->field type let analyzer = self.analyzer.borrow();
Ok("any".into()) 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())
} }
} }
} }