casts, typecheck struct field access
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user