diff --git a/src/codegen.rs b/src/codegen.rs index e49aa8d..bf3f788 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,6 +1,21 @@ -use std::{error::Error, fmt::Write}; +use std::{collections::HashMap, error::Error, fmt::Write}; -use crate::{parser::Expr, tokenizer::TokenType}; +use crate::{ + parser::{Expr, Stmt}, + tokenizer::TokenType, +}; + +pub struct Env { + locals: HashMap, +} + +impl Env { + pub fn new() -> Env { + Env { + locals: HashMap::new(), + } + } +} pub struct Codegen { output: String, @@ -21,7 +36,10 @@ format db \"%d\", 10, 0 section .text global main main: - extern printf" + extern printf + push rbp + mov rbp, rsp + sub rsp, 256" // TODO )?; Ok(()) } @@ -29,12 +47,8 @@ main: pub fn emit_epilogue(&mut self) -> Result<(), Box> { write!( &mut self.output, - " - mov rdi, format - mov rsi, rax - xor rax, rax - call printf - + " mov rsp, rbp + pop rbp mov rax, 0 ret @@ -49,12 +63,36 @@ section .note.GNU-stack self.output } - pub fn compile_expr(&mut self, expr: Expr) -> Result<(), Box> { + pub fn compile_stmt(&mut self, env: &mut Env, stmt: Stmt) -> Result<(), Box> { + match stmt { + Stmt::Expression(expr) => self.compile_expr(env, expr)?, + Stmt::Print(expr) => { + self.compile_expr(env, expr)?; + writeln!( + &mut self.output, + " + mov rdi, format + mov rsi, rax + xor rax, rax + call printf" + )?; + } + Stmt::Var { name, initializer } => { + self.compile_expr(env, initializer)?; + let offset = (env.locals.len() as i32 + 1) * 8; + env.locals.insert(name.lexeme, offset); + writeln!(&mut self.output, " mov QWORD [rbp-{}], rax", offset)?; + } + } + Ok(()) + } + + pub fn compile_expr(&mut self, env: &mut Env, expr: Expr) -> Result<(), Box> { match expr { Expr::Binary { left, op, right } => { - self.compile_expr(*left)?; + self.compile_expr(env, *left)?; writeln!(&mut self.output, " push rax")?; - self.compile_expr(*right)?; + self.compile_expr(env, *right)?; writeln!(&mut self.output, " mov rbx, rax")?; writeln!(&mut self.output, " pop rax")?; @@ -83,20 +121,25 @@ section .note.GNU-stack _ => unreachable!(), } } - Expr::Grouping(expr) => self.compile_expr(*expr)?, + Expr::Grouping(expr) => self.compile_expr(env, *expr)?, Expr::Literal(token) => match token.token_type { TokenType::Number => writeln!(&mut self.output, " mov rax, {}", token.lexeme)?, TokenType::String => todo!(), _ => unreachable!(), }, Expr::Unary { op, right } => { - self.compile_expr(*right)?; + self.compile_expr(env, *right)?; match op.token_type { TokenType::Minus => writeln!(&mut self.output, " neg rax")?, TokenType::Bang => todo!(), _ => unreachable!(), } } + Expr::Variable(name) => { + // TODO: handle error + let offset = env.locals.get(&name.lexeme).unwrap(); + writeln!(&mut self.output, " mov rax, QWORD [rbp-{}]", offset)? + } } Ok(()) } diff --git a/src/main.rs b/src/main.rs index 6aa37b0..bddccca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,17 +17,22 @@ fn compile_file(path: String) -> Result<(), Box> { let tokens = tokenizer.tokenize()?; let parser = parser::Parser::new(tokens); - let expr = parser.parse()?; + let statements = parser.parse()?; let mut codegen = codegen::Codegen::new(); codegen.emit_prologue()?; - codegen.compile_expr(expr)?; + + let mut env = codegen::Env::new(); + for stmt in statements { + codegen.compile_stmt(&mut env, stmt)?; + } + codegen.emit_epilogue()?; fs::write("out.s", codegen.get_output())?; Command::new("nasm") .args(["-f", "elf64", "-o", "out.o", "out.s"]) - .output()?; + .status()?; Command::new("ld") .args([ @@ -39,7 +44,7 @@ fn compile_file(path: String) -> Result<(), Box> { "out", "out.o", ]) - .output()?; + .status()?; Ok(()) } diff --git a/src/parser.rs b/src/parser.rs index 13ed650..c21523c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,5 +1,12 @@ use crate::tokenizer::{MotError, Token, TokenType, error}; +#[derive(Debug, Clone)] +pub enum Stmt { + Expression(Expr), + Print(Expr), + Var { name: Token, initializer: Expr }, +} + #[derive(Debug, Clone)] pub enum Expr { Binary { @@ -13,6 +20,7 @@ pub enum Expr { op: Token, right: Box, }, + Variable(Token), } pub struct Parser { @@ -25,11 +33,37 @@ impl Parser { Parser { tokens, current: 0 } } - pub fn parse(mut self) -> Result { - self.expression() + pub fn parse(mut self) -> Result, MotError> { + let mut statements = vec![]; + while !self.eof() { + statements.push(self.declaration()?); + } + Ok(statements) } - // TODO: synchronization after parse error + fn declaration(&mut self) -> Result { + // TODO: synchronization after parse error + if self.match_token(&[TokenType::KeywordLet]) { + self.let_declaration() + } else { + self.statement() + } + } + + fn let_declaration(&mut self) -> Result { + let name = self.consume(TokenType::Identifier, "expected variable name")?; + self.consume(TokenType::Equal, "expected '=' after variable name")?; + let initializer = self.expression()?; + Ok(Stmt::Var { name, initializer }) + } + + fn statement(&mut self) -> Result { + if self.match_token(&[TokenType::KeywordPrint]) { + Ok(Stmt::Print(self.expression()?)) + } else { + Ok(Stmt::Expression(self.expression()?)) + } + } fn expression(&mut self) -> Result { self.equality() @@ -124,6 +158,8 @@ impl Parser { let expr = self.expression()?; self.consume(TokenType::RightParen, "expected ')' after expression")?; Ok(Expr::Grouping(Box::new(expr))) + } else if self.match_token(&[TokenType::Identifier]) { + Ok(Expr::Variable(self.previous().clone())) } else { error!(self.peek().loc, "expected expression") } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 49086bf..b0e64b5 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -30,6 +30,9 @@ pub enum TokenType { String, Number, + KeywordPrint, + KeywordLet, + Eof, } @@ -228,9 +231,11 @@ impl Tokenizer { } let lexeme: String = self.source[self.start..self.current].iter().collect(); - match lexeme.as_str() { - _ => self.add_token(TokenType::Identifier), - } + self.add_token(match lexeme.as_str() { + "print" => TokenType::KeywordPrint, + "let" => TokenType::KeywordLet, + _ => TokenType::Identifier, + }) } fn match_char(&mut self, expected: char) -> bool {