statements and local variables
This commit is contained in:
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: synchronization after parse error
|
fn declaration(&mut self) -> Result<Stmt, MotError> {
|
||||||
|
// 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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user