statements and local variables

This commit is contained in:
2025-05-29 19:48:09 +02:00
parent f0938ce0a3
commit 0c38b0b6a0
4 changed files with 113 additions and 24 deletions

View File

@@ -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<String, i32>,
}
impl Env {
pub fn new() -> Env {
Env {
locals: HashMap::new(),
}
}
}
pub struct Codegen { pub struct Codegen {
output: String, output: String,
@@ -21,7 +36,10 @@ format db \"%d\", 10, 0
section .text section .text
global main global main
main: main:
extern printf" extern printf
push rbp
mov rbp, rsp
sub rsp, 256" // TODO
)?; )?;
Ok(()) Ok(())
} }
@@ -29,12 +47,8 @@ main:
pub fn emit_epilogue(&mut self) -> Result<(), Box<dyn Error>> { pub fn emit_epilogue(&mut self) -> Result<(), Box<dyn Error>> {
write!( write!(
&mut self.output, &mut self.output,
" " mov rsp, rbp
mov rdi, format pop rbp
mov rsi, rax
xor rax, rax
call printf
mov rax, 0 mov rax, 0
ret ret
@@ -49,12 +63,36 @@ section .note.GNU-stack
self.output self.output
} }
pub fn compile_expr(&mut self, expr: Expr) -> Result<(), Box<dyn Error>> { pub fn compile_stmt(&mut self, env: &mut Env, stmt: Stmt) -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
match expr { match expr {
Expr::Binary { left, op, right } => { Expr::Binary { left, op, right } => {
self.compile_expr(*left)?; self.compile_expr(env, *left)?;
writeln!(&mut self.output, " push rax")?; 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, " mov rbx, rax")?;
writeln!(&mut self.output, " pop rax")?; writeln!(&mut self.output, " pop rax")?;
@@ -83,20 +121,25 @@ section .note.GNU-stack
_ => unreachable!(), _ => unreachable!(),
} }
} }
Expr::Grouping(expr) => self.compile_expr(*expr)?, Expr::Grouping(expr) => self.compile_expr(env, *expr)?,
Expr::Literal(token) => match token.token_type { Expr::Literal(token) => match token.token_type {
TokenType::Number => writeln!(&mut self.output, " mov rax, {}", token.lexeme)?, TokenType::Number => writeln!(&mut self.output, " mov rax, {}", token.lexeme)?,
TokenType::String => todo!(), TokenType::String => todo!(),
_ => unreachable!(), _ => unreachable!(),
}, },
Expr::Unary { op, right } => { Expr::Unary { op, right } => {
self.compile_expr(*right)?; self.compile_expr(env, *right)?;
match op.token_type { match op.token_type {
TokenType::Minus => writeln!(&mut self.output, " neg rax")?, TokenType::Minus => writeln!(&mut self.output, " neg rax")?,
TokenType::Bang => todo!(), TokenType::Bang => todo!(),
_ => unreachable!(), _ => 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(()) Ok(())
} }

View File

@@ -17,17 +17,22 @@ fn compile_file(path: String) -> Result<(), Box<dyn Error>> {
let tokens = tokenizer.tokenize()?; let tokens = tokenizer.tokenize()?;
let parser = parser::Parser::new(tokens); let parser = parser::Parser::new(tokens);
let expr = parser.parse()?; let statements = parser.parse()?;
let mut codegen = codegen::Codegen::new(); let mut codegen = codegen::Codegen::new();
codegen.emit_prologue()?; 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()?; codegen.emit_epilogue()?;
fs::write("out.s", codegen.get_output())?; fs::write("out.s", codegen.get_output())?;
Command::new("nasm") Command::new("nasm")
.args(["-f", "elf64", "-o", "out.o", "out.s"]) .args(["-f", "elf64", "-o", "out.o", "out.s"])
.output()?; .status()?;
Command::new("ld") Command::new("ld")
.args([ .args([
@@ -39,7 +44,7 @@ fn compile_file(path: String) -> Result<(), Box<dyn Error>> {
"out", "out",
"out.o", "out.o",
]) ])
.output()?; .status()?;
Ok(()) Ok(())
} }

View File

@@ -1,5 +1,12 @@
use crate::tokenizer::{MotError, Token, TokenType, error}; 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)] #[derive(Debug, Clone)]
pub enum Expr { pub enum Expr {
Binary { Binary {
@@ -13,6 +20,7 @@ pub enum Expr {
op: Token, op: Token,
right: Box<Expr>, right: Box<Expr>,
}, },
Variable(Token),
} }
pub struct Parser { pub struct Parser {
@@ -25,11 +33,37 @@ impl Parser {
Parser { tokens, current: 0 } Parser { tokens, current: 0 }
} }
pub fn parse(mut self) -> Result<Expr, MotError> { pub fn parse(mut self) -> Result<Vec<Stmt>, MotError> {
self.expression() let mut statements = vec![];
while !self.eof() {
statements.push(self.declaration()?);
}
Ok(statements)
} }
fn declaration(&mut self) -> Result<Stmt, MotError> {
// TODO: synchronization after parse error // TODO: synchronization after parse error
if self.match_token(&[TokenType::KeywordLet]) {
self.let_declaration()
} else {
self.statement()
}
}
fn let_declaration(&mut self) -> Result<Stmt, MotError> {
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<Stmt, MotError> {
if self.match_token(&[TokenType::KeywordPrint]) {
Ok(Stmt::Print(self.expression()?))
} else {
Ok(Stmt::Expression(self.expression()?))
}
}
fn expression(&mut self) -> Result<Expr, MotError> { fn expression(&mut self) -> Result<Expr, MotError> {
self.equality() self.equality()
@@ -124,6 +158,8 @@ impl Parser {
let expr = self.expression()?; let expr = self.expression()?;
self.consume(TokenType::RightParen, "expected ')' after expression")?; self.consume(TokenType::RightParen, "expected ')' after expression")?;
Ok(Expr::Grouping(Box::new(expr))) Ok(Expr::Grouping(Box::new(expr)))
} else if self.match_token(&[TokenType::Identifier]) {
Ok(Expr::Variable(self.previous().clone()))
} else { } else {
error!(self.peek().loc, "expected expression") error!(self.peek().loc, "expected expression")
} }

View File

@@ -30,6 +30,9 @@ pub enum TokenType {
String, String,
Number, Number,
KeywordPrint,
KeywordLet,
Eof, Eof,
} }
@@ -228,9 +231,11 @@ impl Tokenizer {
} }
let lexeme: String = self.source[self.start..self.current].iter().collect(); let lexeme: String = self.source[self.start..self.current].iter().collect();
match lexeme.as_str() { self.add_token(match lexeme.as_str() {
_ => self.add_token(TokenType::Identifier), "print" => TokenType::KeywordPrint,
} "let" => TokenType::KeywordLet,
_ => TokenType::Identifier,
})
} }
fn match_char(&mut self, expected: char) -> bool { fn match_char(&mut self, expected: char) -> bool {