implement MemberAccess and assignment to MemberAccess and Index

This commit is contained in:
2026-03-11 15:31:08 +01:00
parent d1b65dc1ed
commit 930d7b56cc
4 changed files with 120 additions and 26 deletions

View File

@@ -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(())
}

View File

@@ -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(&param.var_type.lexeme) {
return error!(
&name.loc,
"unrecognized type: ".to_owned() + &param.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,21 +518,48 @@ _builtin_environ:
);
}
}
Expr::Assign { name, value } => {
Expr::Assign { left, op, value } => {
self.compile_expr(env, value)?;
// 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));
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)
);
}
};
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 {
callee,
@@ -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)
}
}

View File

@@ -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,
value: Box::new(value),
}),
_ => return error!(equals.loc, "invalid assignment target"),
};
return Ok(Expr::Assign {
left: Box::new(expr),
op: equals,
value: Box::new(value),
});
}
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;
}

View File

@@ -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)