implement MemberAccess and assignment to MemberAccess and Index
This commit is contained in:
@@ -150,7 +150,8 @@ impl Analyzer {
|
||||
self.analyze_expr(right)?;
|
||||
}
|
||||
Expr::Variable(_) => {}
|
||||
Expr::Assign { name: _, value } => {
|
||||
Expr::Assign { left, op: _, value } => {
|
||||
self.analyze_expr(left)?;
|
||||
self.analyze_expr(value)?;
|
||||
}
|
||||
Expr::Call {
|
||||
@@ -202,6 +203,9 @@ impl Analyzer {
|
||||
self.analyze_expr(expr)?;
|
||||
}
|
||||
Expr::New(_) => {}
|
||||
Expr::MemberAccess { left, field: _ } => {
|
||||
self.analyze_expr(left)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Write};
|
||||
use crate::{
|
||||
analyzer::Analyzer,
|
||||
parser::{Expr, Stmt},
|
||||
tokenizer::{TokenType, ZernError, error},
|
||||
tokenizer::{Token, TokenType, ZernError, error},
|
||||
};
|
||||
|
||||
struct Var {
|
||||
@@ -245,6 +245,13 @@ _builtin_environ:
|
||||
emit!(&mut self.output, " sub rsp, 256"); // TODO
|
||||
|
||||
for (i, param) in params.iter().enumerate() {
|
||||
if !self.is_valid_type_name(¶m.var_type.lexeme) {
|
||||
return error!(
|
||||
&name.loc,
|
||||
"unrecognized type: ".to_owned() + ¶m.var_type.lexeme
|
||||
);
|
||||
}
|
||||
|
||||
let offset = env
|
||||
.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone());
|
||||
if let Some(reg) = REGISTERS.get(i) {
|
||||
@@ -511,14 +518,19 @@ _builtin_environ:
|
||||
);
|
||||
}
|
||||
}
|
||||
Expr::Assign { name, value } => {
|
||||
Expr::Assign { left, op, value } => {
|
||||
self.compile_expr(env, value)?;
|
||||
|
||||
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));
|
||||
return error!(
|
||||
name.loc,
|
||||
format!("undefined variable: {}", &name.lexeme)
|
||||
);
|
||||
}
|
||||
};
|
||||
emit!(
|
||||
@@ -527,6 +539,28 @@ _builtin_environ:
|
||||
var.stack_offset,
|
||||
);
|
||||
}
|
||||
Expr::Index { expr, index } => {
|
||||
emit!(&mut self.output, " push rax");
|
||||
self.compile_expr(env, expr)?;
|
||||
emit!(&mut self.output, " push rax");
|
||||
self.compile_expr(env, index)?;
|
||||
emit!(&mut self.output, " pop rbx");
|
||||
emit!(&mut self.output, " add rbx, rax");
|
||||
emit!(&mut self.output, " pop rax");
|
||||
emit!(&mut self.output, " mov BYTE [rbx], al");
|
||||
}
|
||||
Expr::MemberAccess { left, field } => {
|
||||
emit!(&mut self.output, " push rax");
|
||||
|
||||
let offset = self.get_field_offset(env, left, field)?;
|
||||
|
||||
self.compile_expr(env, left)?;
|
||||
emit!(&mut self.output, " pop rbx");
|
||||
emit!(&mut self.output, " mov QWORD [rax+{}], rbx", offset);
|
||||
}
|
||||
_ => return error!(&op.loc, "invalid assignment target"),
|
||||
};
|
||||
}
|
||||
Expr::Call {
|
||||
callee,
|
||||
paren: _,
|
||||
@@ -637,10 +671,13 @@ _builtin_environ:
|
||||
Expr::New(struct_name) => {
|
||||
let struct_fields = &self.analyzer.structs[&struct_name.lexeme];
|
||||
|
||||
let memory_size = struct_fields.len() * 8;
|
||||
|
||||
emit!(&mut self.output, " mov rdi, {}", memory_size);
|
||||
emit!(&mut self.output, " call malloc");
|
||||
emit!(&mut self.output, " mov rdi, {}", struct_fields.len() * 8);
|
||||
emit!(&mut self.output, " call mem.alloc");
|
||||
}
|
||||
Expr::MemberAccess { left, field } => {
|
||||
let offset = self.get_field_offset(env, left, field)?;
|
||||
self.compile_expr(env, left)?;
|
||||
emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -655,4 +692,40 @@ _builtin_environ:
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_field_offset(
|
||||
&self,
|
||||
env: &mut Env,
|
||||
left: &Expr,
|
||||
field: &Token,
|
||||
) -> Result<usize, ZernError> {
|
||||
let struct_name = match left {
|
||||
Expr::Variable(name) => match env.get_var(&name.lexeme) {
|
||||
Some(v) => v.var_type.clone(),
|
||||
None => {
|
||||
return error!(name.loc, format!("undefined variable: {}", &name.lexeme));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return error!(
|
||||
&field.loc,
|
||||
"cannot determine struct type for member assignment"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let fields = match self.analyzer.structs.get(&struct_name) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
return error!(&field.loc, format!("unknown struct type: {}", struct_name));
|
||||
}
|
||||
};
|
||||
|
||||
let offset = match fields.get(&field.lexeme) {
|
||||
Some(o) => *o,
|
||||
None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)),
|
||||
};
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ pub enum Expr {
|
||||
},
|
||||
Variable(Token),
|
||||
Assign {
|
||||
name: Token,
|
||||
left: Box<Expr>,
|
||||
op: Token,
|
||||
value: Box<Expr>,
|
||||
},
|
||||
Call {
|
||||
@@ -89,6 +90,10 @@ pub enum Expr {
|
||||
expr: Box<Expr>,
|
||||
},
|
||||
New(Token),
|
||||
MemberAccess {
|
||||
left: Box<Expr>,
|
||||
field: Token,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Parser {
|
||||
@@ -317,13 +322,11 @@ impl Parser {
|
||||
let equals = self.previous().clone();
|
||||
let value = self.assignment()?;
|
||||
|
||||
return match expr {
|
||||
Expr::Variable(name) => Ok(Expr::Assign {
|
||||
name,
|
||||
return Ok(Expr::Assign {
|
||||
left: Box::new(expr),
|
||||
op: equals,
|
||||
value: Box::new(value),
|
||||
}),
|
||||
_ => return error!(equals.loc, "invalid assignment target"),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Ok(expr)
|
||||
@@ -506,6 +509,13 @@ impl Parser {
|
||||
expr: Box::new(expr),
|
||||
index: Box::new(index),
|
||||
}
|
||||
} else if self.match_token(&[TokenType::Arrow]) {
|
||||
let field =
|
||||
self.consume(TokenType::Identifier, "expected field name after '->'")?;
|
||||
expr = Expr::MemberAccess {
|
||||
left: Box::new(expr),
|
||||
field,
|
||||
};
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ pub enum TokenType {
|
||||
DoubleDot,
|
||||
ShiftLeft,
|
||||
ShiftRight,
|
||||
Arrow,
|
||||
|
||||
Equal,
|
||||
DoubleEqual,
|
||||
@@ -155,10 +156,16 @@ impl Tokenizer {
|
||||
'+' => self.add_token(TokenType::Plus),
|
||||
'*' => self.add_token(TokenType::Star),
|
||||
',' => self.add_token(TokenType::Comma),
|
||||
'-' => self.add_token(TokenType::Minus),
|
||||
'%' => self.add_token(TokenType::Mod),
|
||||
'^' => self.add_token(TokenType::Xor),
|
||||
':' => self.add_token(TokenType::Colon),
|
||||
'-' => {
|
||||
if self.match_char('>') {
|
||||
self.add_token(TokenType::Arrow)
|
||||
} else {
|
||||
self.add_token(TokenType::Minus)
|
||||
}
|
||||
}
|
||||
'.' => {
|
||||
if self.match_char('.') {
|
||||
self.add_token(TokenType::DoubleDot)
|
||||
|
||||
Reference in New Issue
Block a user