adding loops
This commit is contained in:
parent
7f32e7775c
commit
a3c4b878b2
|
@ -6,6 +6,7 @@ main := ||: int {
|
|||
fib := |x: int|: int {
|
||||
if x <= 2 {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return fib(x - 1) + fib(x - 2);
|
||||
}
|
||||
}
|
||||
|
|
19
hblang/examples/loops.hb
Normal file
19
hblang/examples/loops.hb
Normal 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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>,
|
||||
|
@ -222,6 +230,10 @@ pub enum Expr<'a> {
|
|||
If {
|
||||
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, "|")?;
|
||||
|
|
|
@ -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();
|
||||
|
|
BIN
hblang/test.bin
BIN
hblang/test.bin
Binary file not shown.
2
hblang/tests/hblang_codegen_tests_if_statements.txt
Normal file
2
hblang/tests/hblang_codegen_tests_if_statements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
ret: 55
|
||||
status: Ok(())
|
2
hblang/tests/hblang_codegen_tests_loops.txt
Normal file
2
hblang/tests/hblang_codegen_tests_loops.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
ret: 55
|
||||
status: Ok(())
|
Loading…
Reference in a new issue