forked from AbleOS/holey-bytes
adding loops
This commit is contained in:
parent
7f32e7775c
commit
a3c4b878b2
|
@ -6,6 +6,7 @@ main := ||: int {
|
||||||
fib := |x: int|: int {
|
fib := |x: int|: int {
|
||||||
if x <= 2 {
|
if x <= 2 {
|
||||||
return 1;
|
return 1;
|
||||||
|
} else {
|
||||||
|
return fib(x - 1) + fib(x - 2);
|
||||||
}
|
}
|
||||||
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,
|
size: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Loop {
|
||||||
|
offset: u32,
|
||||||
|
relocs: Vec<RetReloc>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Codegen<'a> {
|
pub struct Codegen<'a> {
|
||||||
path: &'a std::path::Path,
|
path: &'a std::path::Path,
|
||||||
ret: Expr<'a>,
|
ret: Expr<'a>,
|
||||||
|
@ -190,6 +195,7 @@ pub struct Codegen<'a> {
|
||||||
vars: Vec<Variable<'a>>,
|
vars: Vec<Variable<'a>>,
|
||||||
stack_relocs: Vec<StackReloc>,
|
stack_relocs: Vec<StackReloc>,
|
||||||
ret_relocs: Vec<RetReloc>,
|
ret_relocs: Vec<RetReloc>,
|
||||||
|
loops: Vec<Loop>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Codegen<'a> {
|
impl<'a> Codegen<'a> {
|
||||||
|
@ -206,6 +212,7 @@ impl<'a> Codegen<'a> {
|
||||||
|
|
||||||
stack_relocs: Default::default(),
|
stack_relocs: Default::default(),
|
||||||
ret_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" }),
|
ty: expeted.unwrap_or(Expr::Ident { name: "int" }),
|
||||||
loc: Loc::Imm(value),
|
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 cond = self.expr(cond, Some(Expr::Ident { name: "bool" })).unwrap();
|
||||||
let reg = self.loc_to_reg(cond.loc);
|
let reg = self.loc_to_reg(cond.loc);
|
||||||
let jump_offset = self.code.code.len() as u32;
|
let jump_offset = self.code.code.len() as u32;
|
||||||
|
@ -377,13 +384,74 @@ impl<'a> Codegen<'a> {
|
||||||
self.gpa.free(reg);
|
self.gpa.free(reg);
|
||||||
|
|
||||||
self.expr(then, None);
|
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);
|
println!("jump: {:02x}", jump);
|
||||||
self.code.code[jump_offset as usize + 3..][..2]
|
self.code.code[jump_offset as usize + 3..][..2]
|
||||||
.copy_from_slice(&jump.to_ne_bytes());
|
.copy_from_slice(&jump.to_ne_bytes());
|
||||||
|
|
||||||
None
|
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 } => {
|
E::BinOp { left, op, right } => {
|
||||||
let left = self.expr(left, expeted).unwrap();
|
let left = self.expr(left, expeted).unwrap();
|
||||||
let right = self.expr(right, Some(left.ty)).unwrap();
|
let right = self.expr(right, Some(left.ty)).unwrap();
|
||||||
|
@ -404,6 +472,16 @@ impl<'a> Codegen<'a> {
|
||||||
loc: Loc::Reg(lhs),
|
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::FSlash => |reg0, reg1, reg2| instrs::diru64(reg0, ZERO, reg1, reg2),
|
||||||
T::Assign => return self.assign(left, right),
|
T::Assign => return self.assign(left, right),
|
||||||
_ => unimplemented!("{:#?}", op),
|
_ => unimplemented!("{:#?}", op),
|
||||||
|
@ -635,5 +713,6 @@ mod tests {
|
||||||
variables => include_str!("../examples/variables.hb");
|
variables => include_str!("../examples/variables.hb");
|
||||||
functions => include_str!("../examples/functions.hb");
|
functions => include_str!("../examples/functions.hb");
|
||||||
if_statements => include_str!("../examples/if_statement.hb");
|
if_statements => include_str!("../examples/if_statement.hb");
|
||||||
|
loops => include_str!("../examples/loops.hb");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,16 @@ pub enum TokenKind {
|
||||||
Bor,
|
Bor,
|
||||||
Or,
|
Or,
|
||||||
Le,
|
Le,
|
||||||
|
Eq,
|
||||||
Semi,
|
Semi,
|
||||||
Colon,
|
Colon,
|
||||||
Comma,
|
Comma,
|
||||||
Return,
|
Return,
|
||||||
If,
|
If,
|
||||||
|
Else,
|
||||||
|
Loop,
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
Eof,
|
Eof,
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
@ -60,11 +65,16 @@ impl std::fmt::Display for TokenKind {
|
||||||
T::Bor => "|",
|
T::Bor => "|",
|
||||||
T::Or => "||",
|
T::Or => "||",
|
||||||
T::Le => "<=",
|
T::Le => "<=",
|
||||||
|
T::Eq => "==",
|
||||||
T::Semi => ";",
|
T::Semi => ";",
|
||||||
T::Colon => ":",
|
T::Colon => ":",
|
||||||
T::Comma => ",",
|
T::Comma => ",",
|
||||||
T::Return => "return",
|
T::Return => "return",
|
||||||
T::If => "if",
|
T::If => "if",
|
||||||
|
T::Else => "else",
|
||||||
|
T::Loop => "loop",
|
||||||
|
T::Break => "break",
|
||||||
|
T::Continue => "continue",
|
||||||
T::Eof => "<eof>",
|
T::Eof => "<eof>",
|
||||||
T::Error => "<error>",
|
T::Error => "<error>",
|
||||||
};
|
};
|
||||||
|
@ -76,7 +86,7 @@ impl TokenKind {
|
||||||
pub fn precedence(&self) -> Option<u8> {
|
pub fn precedence(&self) -> Option<u8> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Self::Assign => 1,
|
Self::Assign => 1,
|
||||||
Self::Le => 21,
|
Self::Le | Self::Eq => 21,
|
||||||
Self::Plus | Self::Minus => 23,
|
Self::Plus | Self::Minus => 23,
|
||||||
Self::Star | Self::FSlash => 24,
|
Self::Star | Self::FSlash => 24,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
|
@ -167,6 +177,10 @@ impl<'a> Iterator for Lexer<'a> {
|
||||||
match ident {
|
match ident {
|
||||||
b"return" => T::Return,
|
b"return" => T::Return,
|
||||||
b"if" => T::If,
|
b"if" => T::If,
|
||||||
|
b"else" => T::Else,
|
||||||
|
b"loop" => T::Loop,
|
||||||
|
b"break" => T::Break,
|
||||||
|
b"continue" => T::Continue,
|
||||||
_ => T::Ident,
|
_ => T::Ident,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +190,10 @@ impl<'a> Iterator for Lexer<'a> {
|
||||||
},
|
},
|
||||||
b',' => T::Comma,
|
b',' => T::Comma,
|
||||||
b';' => T::Semi,
|
b';' => T::Semi,
|
||||||
b'=' => T::Assign,
|
b'=' => match self.advance_if(b'=') {
|
||||||
|
true => T::Eq,
|
||||||
|
false => T::Assign,
|
||||||
|
},
|
||||||
b'<' => match self.advance_if(b'=') {
|
b'<' => match self.advance_if(b'=') {
|
||||||
true => T::Le,
|
true => T::Le,
|
||||||
false => T::Error,
|
false => T::Error,
|
||||||
|
|
|
@ -84,8 +84,14 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
TokenKind::If => {
|
TokenKind::If => {
|
||||||
let cond = self.ptr_expr();
|
let cond = self.ptr_expr();
|
||||||
let then = 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 {
|
TokenKind::Return => Expr::Return {
|
||||||
val: (self.token.kind != TokenKind::Semi).then(|| self.ptr_expr()),
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Expr<'a> {
|
pub enum Expr<'a> {
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
Decl {
|
Decl {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
val: &'a Expr<'a>,
|
val: &'a Expr<'a>,
|
||||||
|
@ -220,8 +228,12 @@ pub enum Expr<'a> {
|
||||||
right: &'a Expr<'a>,
|
right: &'a Expr<'a>,
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
cond: &'a Expr<'a>,
|
cond: &'a Expr<'a>,
|
||||||
then: &'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 {
|
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::Decl { name, val } => write!(f, "{} := {}", name, val),
|
||||||
Self::Closure { ret, body, args } => {
|
Self::Closure { ret, body, args } => {
|
||||||
write!(f, "|")?;
|
write!(f, "|")?;
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub fn run_test(name: &'static str, input: &'static str, test: fn(&'static str,
|
||||||
test(input, &mut output);
|
test(input, &mut output);
|
||||||
|
|
||||||
let mut root = PathBuf::from(std::env::var("PT_TEST_ROOT").unwrap_or("tests".to_string()));
|
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");
|
root.set_extension("txt");
|
||||||
|
|
||||||
let expected = std::fs::read_to_string(&root).unwrap_or_default();
|
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