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)?; 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(())
} }

View File

@@ -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(&param.var_type.lexeme) {
return error!(
&name.loc,
"unrecognized type: ".to_owned() + &param.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)
}
} }

View File

@@ -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;
} }

View File

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