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])) {
// let name = instrs::NAMES[instr[0] as usize];
// println!(
// "{}: {}",
// name,
// instr
// .iter()
// .take(len)
// .skip(1)
// .map(|b| format!("{:02x}", b))
// .collect::<String>()
// );
let name = instrs::NAMES[instr[0] as usize];
println!(
"{:08x}: {}: {}",
self.code.len(),
name,
instr
.iter()
.take(len)
.skip(1)
.map(|b| format!("{:02x}", b))
.collect::<String>()
);
self.code.extend_from_slice(&instr[..len]);
}
@ -367,22 +368,47 @@ impl<'a> Codegen<'a> {
ty: expeted.unwrap_or(Expr::Ident { name: "int" }),
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 } => {
let left = self.expr(left, expeted).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 {
T::Plus => instrs::add64,
T::Minus => instrs::sub64,
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::Assign => return self.assign(left, right),
_ => 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.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<()> {
assert!(self.labels.iter().filter(|l| l.offset == 0).count() == 1);
self.temp.prelude(self.get_label("main"));
self.temp
.relocate(&self.labels, self.temp.code.len() as i64);
@ -501,6 +525,7 @@ pub struct Value<'a> {
loc: Loc,
}
#[derive(Clone, Copy)]
pub enum Loc {
Reg(Reg),
Imm(u64),
@ -578,6 +603,7 @@ mod tests {
let mut out = Vec::new();
codegen.dump(&mut out).unwrap();
std::fs::write("test.bin", &out).unwrap();
use std::fmt::Write;
let mut stack = [0_u64; 1024];
@ -608,5 +634,6 @@ mod tests {
arithmetic => include_str!("../examples/arithmetic.hb");
variables => include_str!("../examples/variables.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,
Bor,
Or,
Le,
Semi,
Colon,
Comma,
Return,
If,
Eof,
Error,
}
@ -57,10 +59,12 @@ impl std::fmt::Display for TokenKind {
T::FSlash => "/",
T::Bor => "|",
T::Or => "||",
T::Le => "<=",
T::Semi => ";",
T::Colon => ":",
T::Comma => ",",
T::Return => "return",
T::If => "if",
T::Eof => "<eof>",
T::Error => "<error>",
};
@ -72,8 +76,9 @@ impl TokenKind {
pub fn precedence(&self) -> Option<u8> {
Some(match self {
Self::Assign => 1,
Self::Plus | Self::Minus => 2,
Self::Star | Self::FSlash => 3,
Self::Le => 21,
Self::Plus | Self::Minus => 23,
Self::Star | Self::FSlash => 24,
_ => return None,
})
}
@ -161,6 +166,7 @@ impl<'a> Iterator for Lexer<'a> {
let ident = &self.bytes[start as usize..self.pos as usize];
match ident {
b"return" => T::Return,
b"if" => T::If,
_ => T::Ident,
}
}
@ -171,6 +177,10 @@ impl<'a> Iterator for Lexer<'a> {
b',' => T::Comma,
b';' => T::Semi,
b'=' => T::Assign,
b'<' => match self.advance_if(b'=') {
true => T::Le,
false => T::Error,
},
b'+' => T::Plus,
b'-' => T::Minus,
b'*' => T::Star,

View file

@ -2,9 +2,6 @@ use std::{cell::Cell, ops::Not, ptr::NonNull};
use crate::lexer::{Lexer, Token, TokenKind};
type Ptr<'a, T> = &'a T;
type Slice<'a, T> = &'a [T];
pub struct Parser<'a, 'b> {
path: &'a std::path::Path,
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()))
}
@ -40,7 +37,7 @@ impl<'a, 'b> Parser<'a, 'b> {
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())
}
@ -84,6 +81,11 @@ impl<'a, 'b> Parser<'a, 'b> {
Expr::Ident { name }
}
}
TokenKind::If => {
let cond = self.ptr_expr();
let then = self.ptr_expr();
Expr::If { cond, then }
}
TokenKind::Return => Expr::Return {
val: (self.token.kind != TokenKind::Semi).then(|| self.ptr_expr()),
},
@ -154,7 +156,7 @@ impl<'a, 'b> Parser<'a, 'b> {
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<_>>();
self.arena.alloc_slice(&vec)
}
@ -188,34 +190,38 @@ impl<'a, 'b> Parser<'a, 'b> {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Expr<'a> {
Decl {
name: Ptr<'a, str>,
val: Ptr<'a, Expr<'a>>,
name: &'a str,
val: &'a Expr<'a>,
},
Closure {
args: Slice<'a, (Ptr<'a, str>, Expr<'a>)>,
ret: Ptr<'a, Expr<'a>>,
body: Ptr<'a, Expr<'a>>,
args: &'a [(&'a str, Expr<'a>)],
ret: &'a Expr<'a>,
body: &'a Expr<'a>,
},
Call {
func: Ptr<'a, Expr<'a>>,
args: Slice<'a, Expr<'a>>,
func: &'a Expr<'a>,
args: &'a [Expr<'a>],
},
Return {
val: Option<Ptr<'a, Expr<'a>>>,
val: Option<&'a Expr<'a>>,
},
Ident {
name: Ptr<'a, str>,
name: &'a str,
},
Block {
stmts: Slice<'a, Expr<'a>>,
stmts: &'a [Expr<'a>],
},
Number {
value: u64,
},
BinOp {
left: Ptr<'a, Expr<'a>>,
left: &'a Expr<'a>,
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 {
Self::If { cond, then } => write!(f, "if {} {}", cond, then),
Self::Decl { name, val } => write!(f, "{} := {}", name, val),
Self::Closure { ret, body, args } => {
write!(f, "|")?;

BIN
hblang/test.bin Normal file

Binary file not shown.

View file

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