Files
zern/src/codegen.rs
2025-05-29 20:57:07 +02:00

199 lines
6.0 KiB
Rust

use std::{collections::HashMap, error::Error, fmt::Write};
use crate::{
parser::{Expr, Stmt},
tokenizer::{MotError, TokenType, error},
};
struct Var {
var_type: String,
stack_offset: usize,
}
pub struct Env {
locals: HashMap<String, Var>,
}
impl Env {
pub fn new() -> Env {
Env {
locals: HashMap::new(),
}
}
}
pub struct Codegen {
output: String,
}
impl Codegen {
pub fn new() -> Codegen {
Codegen {
output: String::new(),
}
}
pub fn emit_prologue(&mut self) -> Result<(), Box<dyn Error>> {
writeln!(
&mut self.output,
"section .data
format db \"%d\", 10, 0
section .text
global main
main:
extern printf
push rbp
mov rbp, rsp
sub rsp, 256" // TODO
)?;
Ok(())
}
pub fn emit_epilogue(&mut self) -> Result<(), Box<dyn Error>> {
write!(
&mut self.output,
" mov rsp, rbp
pop rbp
mov rax, 0
ret
section .note.GNU-stack
db 0
"
)?;
Ok(())
}
pub fn get_output(self) -> String {
self.output
}
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,
var_type,
initializer,
} => {
// TODO
assert!(var_type.lexeme == "I64");
self.compile_expr(env, initializer)?;
let offset = (env.locals.len() + 1) * 8;
env.locals.insert(name.lexeme, Var {
var_type: var_type.lexeme,
stack_offset: offset,
});
writeln!(&mut self.output, " mov QWORD [rbp-{}], rax", offset)?;
}
Stmt::Block(statements) => {
let mut env = Env::new();
for stmt in statements {
self.compile_stmt(&mut env, stmt)?;
}
}
Stmt::If {
condition: _,
then_branch: _,
else_branch: _,
} => todo!(),
Stmt::While {
condition: _,
body: _,
} => todo!(),
}
Ok(())
}
pub fn compile_expr(&mut self, env: &mut Env, expr: Expr) -> Result<(), Box<dyn Error>> {
match expr {
Expr::Binary { left, op, right } => {
self.compile_expr(env, *left)?;
writeln!(&mut self.output, " push rax")?;
self.compile_expr(env, *right)?;
writeln!(&mut self.output, " mov rbx, rax")?;
writeln!(&mut self.output, " pop rax")?;
match op.token_type {
TokenType::Plus => writeln!(&mut self.output, " add rax, rbx")?,
TokenType::Minus => writeln!(&mut self.output, " sub rax, rbx")?,
TokenType::Star => writeln!(&mut self.output, " imul rax, rbx")?,
TokenType::Slash => {
writeln!(&mut self.output, " cqo")?;
writeln!(&mut self.output, " idiv rbx")?;
}
TokenType::Mod => {
writeln!(&mut self.output, " cqo")?;
writeln!(&mut self.output, " idiv rbx")?;
writeln!(&mut self.output, " mov rax, rdx")?;
}
TokenType::Xor => writeln!(&mut self.output, " xor rax, rbx")?,
TokenType::And => todo!(),
TokenType::Or => todo!(),
TokenType::DoubleEqual => todo!(),
TokenType::NotEqual => todo!(),
TokenType::Greater => todo!(),
TokenType::GreaterEqual => todo!(),
TokenType::Less => todo!(),
TokenType::LessEqual => todo!(),
_ => unreachable!(),
}
}
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(env, *right)?;
match op.token_type {
TokenType::Minus => writeln!(&mut self.output, " neg rax")?,
TokenType::Bang => todo!(),
_ => unreachable!(),
}
}
Expr::Variable(name) => {
let var = match env.locals.get(&name.lexeme) {
Some(x) => x,
None => {
return error!(name.loc, format!("undefined variable: {}", &name.lexeme));
}
};
writeln!(
&mut self.output,
" mov rax, QWORD [rbp-{}]",
var.stack_offset
)?
}
Expr::Assign { name, value } => {
self.compile_expr(env, *value)?;
let var = match env.locals.get(&name.lexeme) {
Some(x) => x,
None => {
return error!(name.loc, format!("undefined variable: {}", &name.lexeme));
}
};
writeln!(
&mut self.output,
" mov QWORD [rbp-{}], rax",
var.stack_offset
)?;
}
}
Ok(())
}
}