diff --git a/hblang/examples/if_statement.hb b/hblang/examples/if_statement.hb new file mode 100644 index 00000000..f4e3784e --- /dev/null +++ b/hblang/examples/if_statement.hb @@ -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); +} diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 205702d1..c0da7544 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -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::() - // ); + 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::() + ); 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"); } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index 3cdbeaf1..7518f47d 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -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 => "", T::Error => "", }; @@ -72,8 +76,9 @@ impl TokenKind { pub fn precedence(&self) -> Option { 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, diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index 267dbd85..8bbaa27f 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -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(&mut self, mut f: impl FnMut(&mut Self) -> Option) -> Slice<'a, T> { + fn collect(&mut self, mut f: impl FnMut(&mut Self) -> Option) -> &'a [T] { let vec = std::iter::from_fn(|| f(self)).collect::>(); 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>>, + 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, "|")?; diff --git a/hblang/test.bin b/hblang/test.bin new file mode 100644 index 00000000..e9cab906 Binary files /dev/null and b/hblang/test.bin differ diff --git a/hblang/tests/hblang::codegen::tests::functions.txt b/hblang/tests/hblang::codegen::tests::functions.txt index e69de29b..fc2740da 100644 --- a/hblang/tests/hblang::codegen::tests::functions.txt +++ b/hblang/tests/hblang::codegen::tests::functions.txt @@ -0,0 +1,2 @@ +ret: 33 +status: Ok(()) diff --git a/hblang/tests/hblang::codegen::tests::if_statements.txt b/hblang/tests/hblang::codegen::tests::if_statements.txt new file mode 100644 index 00000000..e69de29b