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