fix block compilation; compile & run repeat
This commit is contained in:
parent
4fa2ce5e78
commit
cfe0b83192
|
@ -35,6 +35,12 @@ pub enum Op {
|
|||
PanicNoWhen,
|
||||
JumpIfNoMatch,
|
||||
PanicNoMatch,
|
||||
TypeOf,
|
||||
JumpBack,
|
||||
JumpIfZero,
|
||||
Duplicate,
|
||||
Decrement,
|
||||
Truncate,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Op {
|
||||
|
@ -67,6 +73,12 @@ impl std::fmt::Display for Op {
|
|||
PanicNoWhen => "panic_no_when",
|
||||
JumpIfNoMatch => "jump_if_no_match",
|
||||
PanicNoMatch => "panic_no_match",
|
||||
TypeOf => "type_of",
|
||||
JumpBack => "jump_back",
|
||||
JumpIfZero => "jump_if_zero",
|
||||
Decrement => "decrement",
|
||||
Truncate => "truncate",
|
||||
Duplicate => "duplicate",
|
||||
};
|
||||
write!(f, "{rep}")
|
||||
}
|
||||
|
@ -92,7 +104,8 @@ impl Chunk {
|
|||
use Op::*;
|
||||
match op {
|
||||
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch => {
|
||||
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch
|
||||
| TypeOf | Duplicate | Decrement | Truncate => {
|
||||
println!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
@ -101,12 +114,20 @@ impl Chunk {
|
|||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||
}
|
||||
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
|
||||
| JumpIfFalse | JumpIfNoMatch => {
|
||||
| JumpIfFalse | JumpIfNoMatch | JumpBack | JumpIfZero => {
|
||||
let next = self.bytecode[i + 1];
|
||||
println!("{i:04}: {:16} {next:04}", op.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kw_from(&self, kw: &str) -> Option<Value> {
|
||||
self.kw_index_from(kw).map(Value::Keyword)
|
||||
}
|
||||
|
||||
pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
|
||||
self.keywords.iter().position(|s| *s == kw)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Compiler {
|
||||
|
@ -226,6 +247,7 @@ impl Compiler {
|
|||
pub fn compile(&mut self) {
|
||||
use Ast::*;
|
||||
match self.ast {
|
||||
Error => unreachable!(),
|
||||
Nil => self.emit_op(Op::Nil),
|
||||
Number(n) => self.emit_constant(Value::Number(*n)),
|
||||
Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }),
|
||||
|
@ -253,13 +275,19 @@ impl Compiler {
|
|||
self.scope_depth += 1;
|
||||
for expr in lines.iter().take(lines.len() - 1) {
|
||||
if is_binding(expr) {
|
||||
self.visit(expr)
|
||||
self.visit(expr);
|
||||
} else {
|
||||
self.visit(expr);
|
||||
self.emit_op(Op::Pop);
|
||||
}
|
||||
}
|
||||
self.visit(lines.last().unwrap());
|
||||
let last_expr = lines.last().unwrap();
|
||||
if is_binding(last_expr) {
|
||||
self.visit(last_expr);
|
||||
self.emit_op(Op::Duplicate);
|
||||
} else {
|
||||
self.visit(last_expr);
|
||||
}
|
||||
self.emit_op(Op::Store);
|
||||
self.scope_depth -= 1;
|
||||
while let Some(binding) = self.bindings.last() {
|
||||
|
@ -270,6 +298,7 @@ impl Compiler {
|
|||
break;
|
||||
}
|
||||
}
|
||||
self.emit_op(Op::Pop);
|
||||
self.emit_op(Op::Load);
|
||||
}
|
||||
If(cond, then, r#else) => {
|
||||
|
@ -284,7 +313,7 @@ impl Compiler {
|
|||
self.visit(r#else);
|
||||
let end_idx = self.chunk.bytecode.len();
|
||||
let jif_offset = jump_idx - jif_idx;
|
||||
let jump_offset = end_idx - jump_idx;
|
||||
let jump_offset = end_idx - jump_idx - 2;
|
||||
self.chunk.bytecode[jif_idx + 1] = jif_offset as u8;
|
||||
self.chunk.bytecode[jump_idx + 1] = jump_offset as u8;
|
||||
}
|
||||
|
@ -430,6 +459,7 @@ impl Compiler {
|
|||
self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 1;
|
||||
}
|
||||
}
|
||||
WhenClause(..) => unreachable!(),
|
||||
Match(scrutinee, clauses) => {
|
||||
self.visit(scrutinee.as_ref());
|
||||
let mut jump_idxes = vec![];
|
||||
|
@ -464,6 +494,7 @@ impl Compiler {
|
|||
self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 2;
|
||||
}
|
||||
}
|
||||
MatchClause(..) => unreachable!(),
|
||||
Fn(name, body, doc) => {
|
||||
// first, declare the function
|
||||
// TODO: or, check if the function has already been declared!
|
||||
|
@ -498,7 +529,47 @@ impl Compiler {
|
|||
FnBody(clauses) => {
|
||||
self.emit_op(Op::ResetMatch);
|
||||
}
|
||||
_ => todo!(),
|
||||
Repeat(times, body) => {
|
||||
self.visit(times);
|
||||
self.emit_op(Op::Truncate);
|
||||
// skip the decrement the first time
|
||||
self.emit_op(Op::Jump);
|
||||
self.chunk.bytecode.push(1);
|
||||
// begin repeat
|
||||
self.emit_op(Op::Decrement);
|
||||
let repeat_begin = self.chunk.bytecode.len();
|
||||
self.emit_op(Op::Duplicate);
|
||||
self.emit_op(Op::JumpIfZero);
|
||||
self.chunk.bytecode.push(0xff);
|
||||
// compile the body
|
||||
self.visit(body);
|
||||
// pop whatever value the body returns
|
||||
self.emit_op(Op::Pop);
|
||||
self.emit_op(Op::JumpBack);
|
||||
// set jump points
|
||||
let repeat_end = self.chunk.bytecode.len();
|
||||
self.chunk.bytecode.push((repeat_end - repeat_begin) as u8);
|
||||
self.chunk.bytecode[repeat_begin + 2] = (repeat_end - repeat_begin - 2) as u8;
|
||||
// pop the counter
|
||||
self.emit_op(Op::Pop);
|
||||
// and emit nil
|
||||
self.emit_constant(Value::Nil);
|
||||
}
|
||||
Interpolated(..)
|
||||
| Arguments(..)
|
||||
| Placeholder
|
||||
| Panic(..)
|
||||
| Do(..)
|
||||
| Splat(..)
|
||||
| Loop(..)
|
||||
| Recur(..)
|
||||
| InterpolatedPattern(..)
|
||||
| AsPattern(..)
|
||||
| Splattern(..)
|
||||
| TuplePattern(..)
|
||||
| ListPattern(..)
|
||||
| PairPattern(..)
|
||||
| DictPattern(..) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,7 +582,8 @@ impl Compiler {
|
|||
use Op::*;
|
||||
match op {
|
||||
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||
| MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen | PanicNoMatch => {
|
||||
| MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen | PanicNoMatch
|
||||
| TypeOf | Duplicate | Truncate | Decrement => {
|
||||
println!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
@ -520,7 +592,7 @@ impl Compiler {
|
|||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||
}
|
||||
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
|
||||
| JumpIfFalse | JumpIfNoMatch => {
|
||||
| JumpIfFalse | JumpIfNoMatch | JumpBack | JumpIfZero => {
|
||||
let (_, next) = codes.next().unwrap();
|
||||
println!("{i:04}: {:16} {next:04}", op.to_string());
|
||||
}
|
||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -68,16 +68,9 @@ pub fn run(src: &'static str) {
|
|||
|
||||
pub fn main() {
|
||||
let src = "
|
||||
let foo = 42
|
||||
match foo with {
|
||||
:foo -> {
|
||||
1
|
||||
2
|
||||
x
|
||||
}
|
||||
2 -> :two
|
||||
42 -> :everything
|
||||
_ -> :something_else
|
||||
let foo = 4
|
||||
repeat foo {
|
||||
:foo
|
||||
}
|
||||
";
|
||||
run(src);
|
||||
|
|
19
src/value.rs
19
src/value.rs
|
@ -126,4 +126,23 @@ impl Value {
|
|||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_of(&self) -> &'static str {
|
||||
use Value::*;
|
||||
match self {
|
||||
Nil => "nil",
|
||||
True => "bool",
|
||||
False => "bool",
|
||||
Keyword(..) => "keyword",
|
||||
Interned(..) => "string",
|
||||
FnDecl(..) => "fn",
|
||||
String(..) => "string",
|
||||
Number(..) => "number",
|
||||
Tuple(..) => "tuple",
|
||||
List(..) => "list",
|
||||
Dict(..) => "dict",
|
||||
Box(..) => "box",
|
||||
Fn(..) => "fn",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
60
src/vm.rs
60
src/vm.rs
|
@ -57,6 +57,10 @@ impl<'a> Vm<'a> {
|
|||
self.stack.pop().unwrap()
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> &Value {
|
||||
self.stack.last().unwrap()
|
||||
}
|
||||
|
||||
fn print_stack(&self) {
|
||||
let inner = self
|
||||
.stack
|
||||
|
@ -68,8 +72,8 @@ impl<'a> Vm<'a> {
|
|||
}
|
||||
|
||||
fn print_debug(&self) {
|
||||
self.chunk.dissasemble_instr(self.ip);
|
||||
self.print_stack();
|
||||
self.chunk.dissasemble_instr(self.ip);
|
||||
}
|
||||
|
||||
pub fn interpret(&mut self) -> Result<Value, Panic> {
|
||||
|
@ -106,7 +110,12 @@ impl<'a> Vm<'a> {
|
|||
}
|
||||
Jump => {
|
||||
let jump_len = self.chunk.bytecode[self.ip + 1];
|
||||
self.ip += jump_len as usize;
|
||||
self.ip += jump_len as usize + 2;
|
||||
self.interpret()
|
||||
}
|
||||
JumpBack => {
|
||||
let jump_len = self.chunk.bytecode[self.ip + 1];
|
||||
self.ip -= jump_len as usize;
|
||||
self.interpret()
|
||||
}
|
||||
JumpIfFalse => {
|
||||
|
@ -123,6 +132,21 @@ impl<'a> Vm<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
JumpIfZero => {
|
||||
let jump_len = self.chunk.bytecode[self.ip + 1];
|
||||
let cond = self.pop();
|
||||
match cond {
|
||||
Value::Number(0.0) => {
|
||||
self.ip += jump_len as usize + 2;
|
||||
self.interpret()
|
||||
}
|
||||
Value::Number(..) => {
|
||||
self.ip += 2;
|
||||
self.interpret()
|
||||
}
|
||||
_ => Err(Panic("repeat requires a number")),
|
||||
}
|
||||
}
|
||||
Pop => {
|
||||
self.pop();
|
||||
self.ip += 1;
|
||||
|
@ -258,6 +282,38 @@ impl<'a> Vm<'a> {
|
|||
}
|
||||
self.interpret()
|
||||
}
|
||||
TypeOf => {
|
||||
let val = self.pop();
|
||||
let type_of = self.chunk.kw_from(val.type_of()).unwrap();
|
||||
self.push(type_of);
|
||||
self.ip += 1;
|
||||
self.interpret()
|
||||
}
|
||||
Truncate => {
|
||||
let val = self.pop();
|
||||
if let Value::Number(x) = val {
|
||||
self.push(Value::Number(x as usize as f64));
|
||||
self.ip += 1;
|
||||
self.interpret()
|
||||
} else {
|
||||
Err(Panic("repeat requires a number"))
|
||||
}
|
||||
}
|
||||
Decrement => {
|
||||
let val = self.pop();
|
||||
if let Value::Number(x) = val {
|
||||
self.push(Value::Number(x - 1.0));
|
||||
self.ip += 1;
|
||||
self.interpret()
|
||||
} else {
|
||||
Err(Panic("you may only decrement a number"))
|
||||
}
|
||||
}
|
||||
Duplicate => {
|
||||
self.push(self.peek().clone());
|
||||
self.ip += 1;
|
||||
self.interpret()
|
||||
}
|
||||
PanicNoWhen | PanicNoMatch => Err(Panic("no match")),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user