adding loops

This commit is contained in:
mlokr 2024-05-11 18:16:27 +02:00
parent 7f32e7775c
commit a3c4b878b2
20 changed files with 151 additions and 10 deletions

View file

@ -6,6 +6,7 @@ main := ||: int {
fib := |x: int|: int {
if x <= 2 {
return 1;
} else {
return fib(x - 1) + fib(x - 2);
}
return fib(x - 1) + fib(x - 2);
}

19
hblang/examples/loops.hb Normal file
View file

@ -0,0 +1,19 @@
main := ||: int {
return fib(10);
}
fib := |n: int|: int {
a := 0;
b := 1;
loop {
c := a + b;
a = b;
b = c;
n = n - 1;
if n == 0 {
break;
}
continue;
}
return a;
}

View file

@ -179,6 +179,11 @@ struct RetReloc {
size: u16,
}
struct Loop {
offset: u32,
relocs: Vec<RetReloc>,
}
pub struct Codegen<'a> {
path: &'a std::path::Path,
ret: Expr<'a>,
@ -190,6 +195,7 @@ pub struct Codegen<'a> {
vars: Vec<Variable<'a>>,
stack_relocs: Vec<StackReloc>,
ret_relocs: Vec<RetReloc>,
loops: Vec<Loop>,
}
impl<'a> Codegen<'a> {
@ -206,6 +212,7 @@ impl<'a> Codegen<'a> {
stack_relocs: Default::default(),
ret_relocs: Default::default(),
loops: Default::default(),
}
}
@ -368,7 +375,7 @@ impl<'a> Codegen<'a> {
ty: expeted.unwrap_or(Expr::Ident { name: "int" }),
loc: Loc::Imm(value),
}),
E::If { cond, then } => {
E::If { cond, then, else_ } => {
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;
@ -377,13 +384,74 @@ impl<'a> Codegen<'a> {
self.gpa.free(reg);
self.expr(then, None);
let jump = self.code.code.len() as i16 - jump_offset as i16;
let jump;
if let Some(else_) = else_ {
let else_jump_offset = self.code.code.len() as u32;
println!("jump_offset: {:02x}", jump_offset);
self.code.encode(instrs::jmp(0));
jump = self.code.code.len() as i16 - jump_offset as i16;
self.expr(else_, None);
let jump = self.code.code.len() as i32 - else_jump_offset as i32;
println!("jump: {:02x}", jump);
self.code.code[else_jump_offset as usize + 1..][..4]
.copy_from_slice(&jump.to_ne_bytes());
} else {
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::Loop { body } => {
let loop_start = self.code.code.len() as u32;
self.loops.push(Loop {
offset: loop_start,
relocs: Default::default(),
});
self.expr(body, None);
let loop_end = self.code.code.len();
self.code
.encode(instrs::jmp(loop_start as i32 - loop_end as i32));
let loop_end = self.code.code.len() as u32;
let loop_ = self.loops.pop().unwrap();
for reloc in loop_.relocs {
let dest = &mut self.code.code
[reloc.offset as usize + reloc.instr_offset as usize..]
[..reloc.size as usize];
let offset = loop_end as i32 - reloc.offset as i32;
dest.copy_from_slice(&offset.to_ne_bytes());
}
None
}
E::Break => {
let loop_ = self.loops.last_mut().unwrap();
let offset = self.code.code.len() as u32;
self.code.encode(instrs::jmp(0));
loop_.relocs.push(RetReloc {
offset,
instr_offset: 1,
size: 4,
});
None
}
E::Continue => {
let loop_ = self.loops.last().unwrap();
let offset = self.code.code.len() as u32;
self.code
.encode(instrs::jmp(loop_.offset as i32 - offset as i32));
None
}
E::BinOp { left, op, right } => {
let left = self.expr(left, expeted).unwrap();
let right = self.expr(right, Some(left.ty)).unwrap();
@ -404,6 +472,16 @@ impl<'a> Codegen<'a> {
loc: Loc::Reg(lhs),
});
}
T::Eq => {
self.code.encode(instrs::cmpu(lhs, lhs, rhs));
self.gpa.free(rhs);
self.code.encode(instrs::cmpui(lhs, lhs, 0));
self.code.encode(instrs::not(lhs, lhs));
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),
@ -635,5 +713,6 @@ mod tests {
variables => include_str!("../examples/variables.hb");
functions => include_str!("../examples/functions.hb");
if_statements => include_str!("../examples/if_statement.hb");
loops => include_str!("../examples/loops.hb");
}
}

View file

@ -30,11 +30,16 @@ pub enum TokenKind {
Bor,
Or,
Le,
Eq,
Semi,
Colon,
Comma,
Return,
If,
Else,
Loop,
Break,
Continue,
Eof,
Error,
}
@ -60,11 +65,16 @@ impl std::fmt::Display for TokenKind {
T::Bor => "|",
T::Or => "||",
T::Le => "<=",
T::Eq => "==",
T::Semi => ";",
T::Colon => ":",
T::Comma => ",",
T::Return => "return",
T::If => "if",
T::Else => "else",
T::Loop => "loop",
T::Break => "break",
T::Continue => "continue",
T::Eof => "<eof>",
T::Error => "<error>",
};
@ -76,7 +86,7 @@ impl TokenKind {
pub fn precedence(&self) -> Option<u8> {
Some(match self {
Self::Assign => 1,
Self::Le => 21,
Self::Le | Self::Eq => 21,
Self::Plus | Self::Minus => 23,
Self::Star | Self::FSlash => 24,
_ => return None,
@ -167,6 +177,10 @@ impl<'a> Iterator for Lexer<'a> {
match ident {
b"return" => T::Return,
b"if" => T::If,
b"else" => T::Else,
b"loop" => T::Loop,
b"break" => T::Break,
b"continue" => T::Continue,
_ => T::Ident,
}
}
@ -176,7 +190,10 @@ impl<'a> Iterator for Lexer<'a> {
},
b',' => T::Comma,
b';' => T::Semi,
b'=' => T::Assign,
b'=' => match self.advance_if(b'=') {
true => T::Eq,
false => T::Assign,
},
b'<' => match self.advance_if(b'=') {
true => T::Le,
false => T::Error,

View file

@ -84,8 +84,14 @@ impl<'a, 'b> Parser<'a, 'b> {
TokenKind::If => {
let cond = self.ptr_expr();
let then = self.ptr_expr();
Expr::If { cond, then }
let else_ = self.advance_if(TokenKind::Else).then(|| self.ptr_expr());
Expr::If { cond, then, else_ }
}
TokenKind::Loop => Expr::Loop {
body: self.ptr_expr(),
},
TokenKind::Break => Expr::Break,
TokenKind::Continue => Expr::Continue,
TokenKind::Return => Expr::Return {
val: (self.token.kind != TokenKind::Semi).then(|| self.ptr_expr()),
},
@ -189,6 +195,8 @@ impl<'a, 'b> Parser<'a, 'b> {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Expr<'a> {
Break,
Continue,
Decl {
name: &'a str,
val: &'a Expr<'a>,
@ -220,8 +228,12 @@ pub enum Expr<'a> {
right: &'a Expr<'a>,
},
If {
cond: &'a Expr<'a>,
then: &'a Expr<'a>,
cond: &'a Expr<'a>,
then: &'a Expr<'a>,
else_: Option<&'a Expr<'a>>,
},
Loop {
body: &'a Expr<'a>,
},
}
@ -232,7 +244,16 @@ impl<'a> std::fmt::Display for Expr<'a> {
}
match *self {
Self::If { cond, then } => write!(f, "if {} {}", cond, then),
Self::Break => write!(f, "break;"),
Self::Continue => write!(f, "continue;"),
Self::If { cond, then, else_ } => {
write!(f, "if {} {}", cond, then)?;
if let Some(else_) = else_ {
write!(f, " else {}", else_)?;
}
Ok(())
}
Self::Loop { body } => write!(f, "loop {}", body),
Self::Decl { name, val } => write!(f, "{} := {}", name, val),
Self::Closure { ret, body, args } => {
write!(f, "|")?;

View file

@ -12,7 +12,7 @@ pub fn run_test(name: &'static str, input: &'static str, test: fn(&'static str,
test(input, &mut output);
let mut root = PathBuf::from(std::env::var("PT_TEST_ROOT").unwrap_or("tests".to_string()));
root.push(name);
root.push(name.replace("::", "_"));
root.set_extension("txt");
let expected = std::fs::read_to_string(&root).unwrap_or_default();

Binary file not shown.

View file

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

View file

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