diff --git a/hblang/examples/arithmetic.hb b/hblang/examples/arithmetic.hb index 52394e6..6a9289b 100644 --- a/hblang/examples/arithmetic.hb +++ b/hblang/examples/arithmetic.hb @@ -1,3 +1,3 @@ main := ||: int { - return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4; + return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1; } diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 26bc312..9abc906 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -1,5 +1,8 @@ use { - crate::parser::{self, Expr}, + crate::{ + lexer, + parser::{self, Expr}, + }, std::rc::Rc, }; @@ -66,6 +69,10 @@ impl Func { self.tx(); } + fn div64(&mut self, reg0: Reg, reg1: Reg, reg2: Reg) { + self.diru64(reg0, ZERO, reg1, reg2); + } + fn relocate(&mut self, labels: &[Label], shift: i64) { for reloc in self.relocs.drain(..) { let label = &labels[reloc.id as usize]; @@ -158,13 +165,14 @@ impl<'a> Codegen<'a> { } fn expr(&mut self, expr: &'a parser::Expr<'a>, expeted: Option>) -> Option> { - use parser::Expr as E; + use {lexer::TokenKind as T, parser::Expr as E}; match *expr { E::Decl { name, val: E::Closure { ret, body }, } => { let frame = self.add_label(name); + self.gpa.init_caller(); self.ret = **ret; self.expr(body, None); self.write_fn_prelude(frame); @@ -194,7 +202,48 @@ impl<'a> Codegen<'a> { ty: expeted.unwrap_or(Expr::Ident { name: "int" }), loc: Loc::Imm(value), }), - ast => unimplemented!("{:?}", ast), + E::BinOp { left, op, right } => { + let left = self.expr(left, expeted).unwrap(); + let right = self.expr(right, Some(left.ty)).unwrap(); + + type Op = fn(&mut Func, u8, u8, u8); + type ImmOp = fn(&mut Func, u8, u8, u64); + + let op = match op { + T::Plus => Func::add64 as Op, + T::Minus => Func::sub64 as Op, + T::Star => Func::mul64 as Op, + T::FSlash => Func::div64 as Op, + _ => unimplemented!("{:#?}", op), + }; + + let lhs = match left.loc { + Loc::Reg(reg) => reg, + Loc::Imm(imm) => { + let reg = self.gpa.allocate(); + self.code.li64(reg, imm); + reg + } + }; + + let rhs = match right.loc { + Loc::Reg(reg) => reg, + Loc::Imm(imm) => { + let reg = self.gpa.allocate(); + self.code.li64(reg, imm); + reg + } + }; + + op(&mut self.code, lhs, lhs, rhs); + self.gpa.free(rhs); + + Some(Value { + ty: left.ty, + loc: Loc::Reg(lhs), + }) + } + ast => unimplemented!("{:#?}", ast), } } @@ -328,14 +377,6 @@ mod tests { let mut stack = [0_u64; 1024]; - for (i, b) in out.iter().enumerate() { - write!(output, "{:02x}", b).unwrap(); - if (i + 1) % 4 == 0 { - writeln!(output).unwrap(); - } - } - writeln!(output).unwrap(); - let mut vm = unsafe { hbvm::Vm::::new(TestMem, hbvm::mem::Address::new(out.as_ptr() as u64)) }; @@ -356,5 +397,6 @@ mod tests { crate::run_tests! { generate: example => include_str!("../examples/main_fn.hb"); + arithmetic => include_str!("../examples/arithmetic.hb"); } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index 490db40..1de5aef 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -11,7 +11,7 @@ impl Token { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum TokenKind { Ident, Number, @@ -22,6 +22,10 @@ pub enum TokenKind { LBrack, RBrack, Decl, + Plus, + Minus, + Star, + FSlash, Or, Semi, Colon, @@ -30,6 +34,44 @@ pub enum TokenKind { Error, } +impl std::fmt::Display for TokenKind { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use TokenKind as T; + let s = match self { + T::Ident => "", + T::Number => "", + T::LParen => "(", + T::RParen => ")", + T::LBrace => "{", + T::RBrace => "}", + T::LBrack => "[", + T::RBrack => "]", + T::Decl => ":=", + T::Plus => "+", + T::Minus => "-", + T::Star => "*", + T::FSlash => "/", + T::Or => "||", + T::Semi => ";", + T::Colon => ":", + T::Return => "return", + T::Eof => "", + T::Error => "", + }; + write!(f, "{}", s) + } +} + +impl TokenKind { + pub fn precedence(&self) -> Option { + match self { + Self::Plus | Self::Minus => Some(2), + Self::Star | Self::FSlash => Some(3), + _ => None, + } + } +} + pub struct Lexer<'a> { pos: u32, bytes: &'a [u8], @@ -120,6 +162,10 @@ impl<'a> Iterator for Lexer<'a> { false => T::Colon, }, b';' => T::Semi, + b'+' => T::Plus, + b'-' => T::Minus, + b'*' => T::Star, + b'/' => T::FSlash, b'|' => match self.advance_if(b'|') { true => T::Or, false => T::Error, diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index 3036a94..d603026 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -1,4 +1,5 @@ #![feature(noop_waker)] +#![feature(let_chains)] #![feature(non_null_convenience)] #![allow(dead_code)] #![feature(const_mut_refs)] diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index e2b2cf2..b83fbd2 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -45,6 +45,34 @@ impl<'a, 'b> Parser<'a, 'b> { } fn expr(&mut self) -> Expr<'a> { + let left = self.unit_expr(); + self.bin_expr(left, 0) + } + + fn bin_expr(&mut self, mut left: Expr<'a>, min_prec: u8) -> Expr<'a> { + loop { + let Some(prec) = self.token.kind.precedence() else { + break; + }; + + if prec < min_prec { + break; + } + + let op = self.next().kind; + let right = self.unit_expr(); + let right = self.bin_expr(right, prec); + left = Expr::BinOp { + left: self.arena.alloc(left), + right: self.arena.alloc(right), + op, + }; + } + + left + } + + fn unit_expr(&mut self) -> Expr<'a> { let token = self.next(); let expr = match token.kind { TokenKind::Ident => { @@ -74,6 +102,11 @@ impl<'a, 'b> Parser<'a, 'b> { Err(e) => self.report(format_args!("invalid number: {e}")), }, }, + TokenKind::LParen => { + let expr = self.expr(); + self.expect_advance(TokenKind::RParen); + expr + } tok => self.report(format_args!("unexpected token: {tok:?}")), }; @@ -140,6 +173,11 @@ pub enum Expr<'a> { Number { value: u64, }, + BinOp { + left: Ptr<'a, Expr<'a>>, + op: TokenKind, + right: Ptr<'a, Expr<'a>>, + }, } impl<'a> std::fmt::Display for Expr<'a> { @@ -148,7 +186,7 @@ impl<'a> std::fmt::Display for Expr<'a> { static INDENT: Cell = Cell::new(0); } - match self { + match *self { Self::Decl { name, val } => write!(f, "{} := {}", name, val), Self::Closure { ret, body } => write!(f, "||: {} {}", ret, body), Self::Return { val: Some(val) } => write!(f, "return {};", val), @@ -158,7 +196,7 @@ impl<'a> std::fmt::Display for Expr<'a> { writeln!(f, "{{")?; INDENT.with(|i| i.set(i.get() + 1)); let res = crate::try_block(|| { - for stmt in *stmts { + for stmt in stmts { for _ in 0..INDENT.with(|i| i.get()) { write!(f, " ")?; } @@ -171,6 +209,21 @@ impl<'a> std::fmt::Display for Expr<'a> { res } Self::Number { value } => write!(f, "{}", value), + Self::BinOp { left, right, op } => { + let display_branch = |f: &mut std::fmt::Formatter, expr: &Self| { + if let Self::BinOp { op: lop, .. } = expr + && op.precedence() > lop.precedence() + { + write!(f, "({})", expr) + } else { + write!(f, "{}", expr) + } + }; + + display_branch(f, left)?; + write!(f, " {} ", op)?; + display_branch(f, right) + } } } } @@ -360,5 +413,6 @@ mod tests { crate::run_tests! { parse: example => include_str!("../examples/main_fn.hb"); + arithmetic => include_str!("../examples/arithmetic.hb"); } } diff --git a/hblang/tests/hblang::codegen::tests::arithmetic.txt b/hblang/tests/hblang::codegen::tests::arithmetic.txt new file mode 100644 index 0000000..5fe5101 --- /dev/null +++ b/hblang/tests/hblang::codegen::tests::arithmetic.txt @@ -0,0 +1,2 @@ +ret: 1 +status: Ok(()) diff --git a/hblang/tests/hblang::codegen::tests::example.txt b/hblang/tests/hblang::codegen::tests::example.txt index eed3d92..5fe5101 100644 --- a/hblang/tests/hblang::codegen::tests::example.txt +++ b/hblang/tests/hblang::codegen::tests::example.txt @@ -1,10 +1,2 @@ -541f0005 -00000001 -4b010100 -00000000 -00005500 -1f000000 -00000000 -00 ret: 1 status: Ok(()) diff --git a/hblang/tests/hblang::lexer::tests::arithmetic.txt b/hblang/tests/hblang::lexer::tests::arithmetic.txt new file mode 100644 index 0000000..e44cf7d --- /dev/null +++ b/hblang/tests/hblang::lexer::tests::arithmetic.txt @@ -0,0 +1,29 @@ +Ident "main" +Decl ":=" +Or "||" +Colon ":" +Ident "int" +LBrace "{" +Return "return" +Number "10" +Minus "-" +Number "20" +FSlash "/" +Number "2" +Plus "+" +Number "4" +Star "*" +LParen "(" +Number "2" +Plus "+" +Number "2" +RParen ")" +Minus "-" +Number "4" +Star "*" +Number "4" +Plus "+" +Number "1" +Semi ";" +RBrace "}" +Eof "" diff --git a/hblang/tests/hblang::parser::tests::arithmetic.txt b/hblang/tests/hblang::parser::tests::arithmetic.txt new file mode 100644 index 0000000..73a1788 --- /dev/null +++ b/hblang/tests/hblang::parser::tests::arithmetic.txt @@ -0,0 +1,3 @@ +main := ||: int { + return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1; +}