making if statements without else branch work

This commit is contained in:
mlokr 2024-05-11 17:05:22 +02:00
parent 1d74f27b0e
commit 7f32e7775c
7 changed files with 93 additions and 36 deletions

View file

@ -0,0 +1,11 @@
main := ||: int {
return fib(10);
}
fib := |x: int|: int {
if x <= 2 {
return 1;
}
return fib(x - 1) + fib(x - 2);
}

View file

@ -54,17 +54,18 @@ impl Func {
} }
fn encode(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) { fn encode(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) {
// let name = instrs::NAMES[instr[0] as usize]; let name = instrs::NAMES[instr[0] as usize];
// println!( println!(
// "{}: {}", "{:08x}: {}: {}",
// name, self.code.len(),
// instr name,
// .iter() instr
// .take(len) .iter()
// .skip(1) .take(len)
// .map(|b| format!("{:02x}", b)) .skip(1)
// .collect::<String>() .map(|b| format!("{:02x}", b))
// ); .collect::<String>()
);
self.code.extend_from_slice(&instr[..len]); self.code.extend_from_slice(&instr[..len]);
} }
@ -367,22 +368,47 @@ impl<'a> Codegen<'a> {
ty: expeted.unwrap_or(Expr::Ident { name: "int" }), ty: expeted.unwrap_or(Expr::Ident { name: "int" }),
loc: Loc::Imm(value), loc: Loc::Imm(value),
}), }),
E::If { cond, then } => {
let cond = self.expr(cond, Some(Expr::Ident { name: "bool" })).unwrap();
let reg = self.loc_to_reg(cond.loc);
let jump_offset = self.code.code.len() as u32;
println!("jump_offset: {:02x}", jump_offset);
self.code.encode(instrs::jeq(reg, 0, 0));
self.gpa.free(reg);
self.expr(then, None);
let jump = self.code.code.len() as i16 - jump_offset as i16;
println!("jump: {:02x}", jump);
self.code.code[jump_offset as usize + 3..][..2]
.copy_from_slice(&jump.to_ne_bytes());
None
}
E::BinOp { left, op, right } => { E::BinOp { left, op, right } => {
let left = self.expr(left, expeted).unwrap(); let left = self.expr(left, expeted).unwrap();
let right = self.expr(right, Some(left.ty)).unwrap(); let right = self.expr(right, Some(left.ty)).unwrap();
let lhs = self.loc_to_reg(left.loc);
let rhs = self.loc_to_reg(right.loc);
let op = match op { let op = match op {
T::Plus => instrs::add64, T::Plus => instrs::add64,
T::Minus => instrs::sub64, T::Minus => instrs::sub64,
T::Star => instrs::mul64, T::Star => instrs::mul64,
T::Le => {
self.code.encode(instrs::cmpu(lhs, lhs, rhs));
self.gpa.free(rhs);
self.code.encode(instrs::cmpui(lhs, lhs, 1));
return Some(Value {
ty: Expr::Ident { name: "bool" },
loc: Loc::Reg(lhs),
});
}
T::FSlash => |reg0, reg1, reg2| instrs::diru64(reg0, ZERO, reg1, reg2), T::FSlash => |reg0, reg1, reg2| instrs::diru64(reg0, ZERO, reg1, reg2),
T::Assign => return self.assign(left, right), T::Assign => return self.assign(left, right),
_ => unimplemented!("{:#?}", op), _ => unimplemented!("{:#?}", op),
}; };
let lhs = self.loc_to_reg(left.loc);
let rhs = self.loc_to_reg(right.loc);
self.code.encode(op(lhs, lhs, rhs)); self.code.encode(op(lhs, lhs, rhs));
self.gpa.free(rhs); self.gpa.free(rhs);
@ -476,8 +502,6 @@ impl<'a> Codegen<'a> {
} }
pub fn dump(mut self, out: &mut impl std::io::Write) -> std::io::Result<()> { pub fn dump(mut self, out: &mut impl std::io::Write) -> std::io::Result<()> {
assert!(self.labels.iter().filter(|l| l.offset == 0).count() == 1);
self.temp.prelude(self.get_label("main")); self.temp.prelude(self.get_label("main"));
self.temp self.temp
.relocate(&self.labels, self.temp.code.len() as i64); .relocate(&self.labels, self.temp.code.len() as i64);
@ -501,6 +525,7 @@ pub struct Value<'a> {
loc: Loc, loc: Loc,
} }
#[derive(Clone, Copy)]
pub enum Loc { pub enum Loc {
Reg(Reg), Reg(Reg),
Imm(u64), Imm(u64),
@ -578,6 +603,7 @@ mod tests {
let mut out = Vec::new(); let mut out = Vec::new();
codegen.dump(&mut out).unwrap(); codegen.dump(&mut out).unwrap();
std::fs::write("test.bin", &out).unwrap();
use std::fmt::Write; use std::fmt::Write;
let mut stack = [0_u64; 1024]; let mut stack = [0_u64; 1024];
@ -608,5 +634,6 @@ mod tests {
arithmetic => include_str!("../examples/arithmetic.hb"); arithmetic => include_str!("../examples/arithmetic.hb");
variables => include_str!("../examples/variables.hb"); variables => include_str!("../examples/variables.hb");
functions => include_str!("../examples/functions.hb"); functions => include_str!("../examples/functions.hb");
if_statements => include_str!("../examples/if_statement.hb");
} }
} }

View file

@ -29,10 +29,12 @@ pub enum TokenKind {
FSlash, FSlash,
Bor, Bor,
Or, Or,
Le,
Semi, Semi,
Colon, Colon,
Comma, Comma,
Return, Return,
If,
Eof, Eof,
Error, Error,
} }
@ -57,10 +59,12 @@ impl std::fmt::Display for TokenKind {
T::FSlash => "/", T::FSlash => "/",
T::Bor => "|", T::Bor => "|",
T::Or => "||", T::Or => "||",
T::Le => "<=",
T::Semi => ";", T::Semi => ";",
T::Colon => ":", T::Colon => ":",
T::Comma => ",", T::Comma => ",",
T::Return => "return", T::Return => "return",
T::If => "if",
T::Eof => "<eof>", T::Eof => "<eof>",
T::Error => "<error>", T::Error => "<error>",
}; };
@ -72,8 +76,9 @@ impl TokenKind {
pub fn precedence(&self) -> Option<u8> { pub fn precedence(&self) -> Option<u8> {
Some(match self { Some(match self {
Self::Assign => 1, Self::Assign => 1,
Self::Plus | Self::Minus => 2, Self::Le => 21,
Self::Star | Self::FSlash => 3, Self::Plus | Self::Minus => 23,
Self::Star | Self::FSlash => 24,
_ => return None, _ => return None,
}) })
} }
@ -161,6 +166,7 @@ impl<'a> Iterator for Lexer<'a> {
let ident = &self.bytes[start as usize..self.pos as usize]; let ident = &self.bytes[start as usize..self.pos as usize];
match ident { match ident {
b"return" => T::Return, b"return" => T::Return,
b"if" => T::If,
_ => T::Ident, _ => T::Ident,
} }
} }
@ -171,6 +177,10 @@ impl<'a> Iterator for Lexer<'a> {
b',' => T::Comma, b',' => T::Comma,
b';' => T::Semi, b';' => T::Semi,
b'=' => T::Assign, b'=' => T::Assign,
b'<' => match self.advance_if(b'=') {
true => T::Le,
false => T::Error,
},
b'+' => T::Plus, b'+' => T::Plus,
b'-' => T::Minus, b'-' => T::Minus,
b'*' => T::Star, b'*' => T::Star,

View file

@ -2,9 +2,6 @@ use std::{cell::Cell, ops::Not, ptr::NonNull};
use crate::lexer::{Lexer, Token, TokenKind}; use crate::lexer::{Lexer, Token, TokenKind};
type Ptr<'a, T> = &'a T;
type Slice<'a, T> = &'a [T];
pub struct Parser<'a, 'b> { pub struct Parser<'a, 'b> {
path: &'a std::path::Path, path: &'a std::path::Path,
lexer: Lexer<'a>, lexer: Lexer<'a>,
@ -32,7 +29,7 @@ impl<'a, 'b> Parser<'a, 'b> {
} }
} }
pub fn file(&mut self) -> Slice<'a, Expr<'a>> { pub fn file(&mut self) -> &'a [Expr<'a>] {
self.collect(|s| (s.token.kind != TokenKind::Eof).then(|| s.expr())) self.collect(|s| (s.token.kind != TokenKind::Eof).then(|| s.expr()))
} }
@ -40,7 +37,7 @@ impl<'a, 'b> Parser<'a, 'b> {
std::mem::replace(&mut self.token, self.lexer.next()) std::mem::replace(&mut self.token, self.lexer.next())
} }
fn ptr_expr(&mut self) -> Ptr<'a, Expr<'a>> { fn ptr_expr(&mut self) -> &'a Expr<'a> {
self.arena.alloc(self.expr()) self.arena.alloc(self.expr())
} }
@ -84,6 +81,11 @@ impl<'a, 'b> Parser<'a, 'b> {
Expr::Ident { name } Expr::Ident { name }
} }
} }
TokenKind::If => {
let cond = self.ptr_expr();
let then = self.ptr_expr();
Expr::If { cond, then }
}
TokenKind::Return => Expr::Return { TokenKind::Return => Expr::Return {
val: (self.token.kind != TokenKind::Semi).then(|| self.ptr_expr()), val: (self.token.kind != TokenKind::Semi).then(|| self.ptr_expr()),
}, },
@ -154,7 +156,7 @@ impl<'a, 'b> Parser<'a, 'b> {
expr expr
} }
fn collect<T: Copy>(&mut self, mut f: impl FnMut(&mut Self) -> Option<T>) -> Slice<'a, T> { fn collect<T: Copy>(&mut self, mut f: impl FnMut(&mut Self) -> Option<T>) -> &'a [T] {
let vec = std::iter::from_fn(|| f(self)).collect::<Vec<_>>(); let vec = std::iter::from_fn(|| f(self)).collect::<Vec<_>>();
self.arena.alloc_slice(&vec) self.arena.alloc_slice(&vec)
} }
@ -188,34 +190,38 @@ impl<'a, 'b> Parser<'a, 'b> {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Expr<'a> { pub enum Expr<'a> {
Decl { Decl {
name: Ptr<'a, str>, name: &'a str,
val: Ptr<'a, Expr<'a>>, val: &'a Expr<'a>,
}, },
Closure { Closure {
args: Slice<'a, (Ptr<'a, str>, Expr<'a>)>, args: &'a [(&'a str, Expr<'a>)],
ret: Ptr<'a, Expr<'a>>, ret: &'a Expr<'a>,
body: Ptr<'a, Expr<'a>>, body: &'a Expr<'a>,
}, },
Call { Call {
func: Ptr<'a, Expr<'a>>, func: &'a Expr<'a>,
args: Slice<'a, Expr<'a>>, args: &'a [Expr<'a>],
}, },
Return { Return {
val: Option<Ptr<'a, Expr<'a>>>, val: Option<&'a Expr<'a>>,
}, },
Ident { Ident {
name: Ptr<'a, str>, name: &'a str,
}, },
Block { Block {
stmts: Slice<'a, Expr<'a>>, stmts: &'a [Expr<'a>],
}, },
Number { Number {
value: u64, value: u64,
}, },
BinOp { BinOp {
left: Ptr<'a, Expr<'a>>, left: &'a Expr<'a>,
op: TokenKind, op: TokenKind,
right: Ptr<'a, Expr<'a>>, right: &'a Expr<'a>,
},
If {
cond: &'a Expr<'a>,
then: &'a Expr<'a>,
}, },
} }
@ -226,6 +232,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
} }
match *self { match *self {
Self::If { cond, then } => write!(f, "if {} {}", cond, then),
Self::Decl { name, val } => write!(f, "{} := {}", name, val), Self::Decl { name, val } => write!(f, "{} := {}", name, val),
Self::Closure { ret, body, args } => { Self::Closure { ret, body, args } => {
write!(f, "|")?; write!(f, "|")?;

BIN
hblang/test.bin Normal file

Binary file not shown.

View file

@ -0,0 +1,2 @@
ret: 33
status: Ok(())