diff --git a/src/analyzer.rs b/src/analyzer.rs index c4d3d93..79591dd 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -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(()) } diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index beab783..74d1fb1 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -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,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 { + 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) + } } diff --git a/src/parser.rs b/src/parser.rs index 9452e04..da88eaf 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -71,7 +71,8 @@ pub enum Expr { }, Variable(Token), Assign { - name: Token, + left: Box, + op: Token, value: Box, }, Call { @@ -89,6 +90,10 @@ pub enum Expr { expr: Box, }, New(Token), + MemberAccess { + left: Box, + 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; } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 9dacccf..b31a68f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -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)