fix block compilation; compile & run repeat
This commit is contained in:
parent
4fa2ce5e78
commit
cfe0b83192
|
@ -35,6 +35,12 @@ pub enum Op {
|
||||||
PanicNoWhen,
|
PanicNoWhen,
|
||||||
JumpIfNoMatch,
|
JumpIfNoMatch,
|
||||||
PanicNoMatch,
|
PanicNoMatch,
|
||||||
|
TypeOf,
|
||||||
|
JumpBack,
|
||||||
|
JumpIfZero,
|
||||||
|
Duplicate,
|
||||||
|
Decrement,
|
||||||
|
Truncate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Op {
|
impl std::fmt::Display for Op {
|
||||||
|
@ -67,6 +73,12 @@ impl std::fmt::Display for Op {
|
||||||
PanicNoWhen => "panic_no_when",
|
PanicNoWhen => "panic_no_when",
|
||||||
JumpIfNoMatch => "jump_if_no_match",
|
JumpIfNoMatch => "jump_if_no_match",
|
||||||
PanicNoMatch => "panic_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}")
|
write!(f, "{rep}")
|
||||||
}
|
}
|
||||||
|
@ -92,7 +104,8 @@ impl Chunk {
|
||||||
use Op::*;
|
use Op::*;
|
||||||
match op {
|
match op {
|
||||||
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
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}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -101,12 +114,20 @@ impl Chunk {
|
||||||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||||
}
|
}
|
||||||
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
|
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
|
||||||
| JumpIfFalse | JumpIfNoMatch => {
|
| JumpIfFalse | JumpIfNoMatch | JumpBack | JumpIfZero => {
|
||||||
let next = self.bytecode[i + 1];
|
let next = self.bytecode[i + 1];
|
||||||
println!("{i:04}: {:16} {next:04}", op.to_string());
|
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 {
|
pub struct Compiler {
|
||||||
|
@ -226,6 +247,7 @@ impl Compiler {
|
||||||
pub fn compile(&mut self) {
|
pub fn compile(&mut self) {
|
||||||
use Ast::*;
|
use Ast::*;
|
||||||
match self.ast {
|
match self.ast {
|
||||||
|
Error => unreachable!(),
|
||||||
Nil => self.emit_op(Op::Nil),
|
Nil => self.emit_op(Op::Nil),
|
||||||
Number(n) => self.emit_constant(Value::Number(*n)),
|
Number(n) => self.emit_constant(Value::Number(*n)),
|
||||||
Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }),
|
Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }),
|
||||||
|
@ -253,13 +275,19 @@ impl Compiler {
|
||||||
self.scope_depth += 1;
|
self.scope_depth += 1;
|
||||||
for expr in lines.iter().take(lines.len() - 1) {
|
for expr in lines.iter().take(lines.len() - 1) {
|
||||||
if is_binding(expr) {
|
if is_binding(expr) {
|
||||||
self.visit(expr)
|
self.visit(expr);
|
||||||
} else {
|
} else {
|
||||||
self.visit(expr);
|
self.visit(expr);
|
||||||
self.emit_op(Op::Pop);
|
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.emit_op(Op::Store);
|
||||||
self.scope_depth -= 1;
|
self.scope_depth -= 1;
|
||||||
while let Some(binding) = self.bindings.last() {
|
while let Some(binding) = self.bindings.last() {
|
||||||
|
@ -270,6 +298,7 @@ impl Compiler {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.emit_op(Op::Pop);
|
||||||
self.emit_op(Op::Load);
|
self.emit_op(Op::Load);
|
||||||
}
|
}
|
||||||
If(cond, then, r#else) => {
|
If(cond, then, r#else) => {
|
||||||
|
@ -284,7 +313,7 @@ impl Compiler {
|
||||||
self.visit(r#else);
|
self.visit(r#else);
|
||||||
let end_idx = self.chunk.bytecode.len();
|
let end_idx = self.chunk.bytecode.len();
|
||||||
let jif_offset = jump_idx - jif_idx;
|
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[jif_idx + 1] = jif_offset as u8;
|
||||||
self.chunk.bytecode[jump_idx + 1] = jump_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;
|
self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WhenClause(..) => unreachable!(),
|
||||||
Match(scrutinee, clauses) => {
|
Match(scrutinee, clauses) => {
|
||||||
self.visit(scrutinee.as_ref());
|
self.visit(scrutinee.as_ref());
|
||||||
let mut jump_idxes = vec![];
|
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;
|
self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MatchClause(..) => unreachable!(),
|
||||||
Fn(name, body, doc) => {
|
Fn(name, body, doc) => {
|
||||||
// first, declare the function
|
// first, declare the function
|
||||||
// TODO: or, check if the function has already been declared!
|
// TODO: or, check if the function has already been declared!
|
||||||
|
@ -498,7 +529,47 @@ impl Compiler {
|
||||||
FnBody(clauses) => {
|
FnBody(clauses) => {
|
||||||
self.emit_op(Op::ResetMatch);
|
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::*;
|
use Op::*;
|
||||||
match op {
|
match op {
|
||||||
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
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}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -520,7 +592,7 @@ impl Compiler {
|
||||||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||||
}
|
}
|
||||||
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
|
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
|
||||||
| JumpIfFalse | JumpIfNoMatch => {
|
| JumpIfFalse | JumpIfNoMatch | JumpBack | JumpIfZero => {
|
||||||
let (_, next) = codes.next().unwrap();
|
let (_, next) = codes.next().unwrap();
|
||||||
println!("{i:04}: {:16} {next:04}", op.to_string());
|
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() {
|
pub fn main() {
|
||||||
let src = "
|
let src = "
|
||||||
let foo = 42
|
let foo = 4
|
||||||
match foo with {
|
repeat foo {
|
||||||
:foo -> {
|
:foo
|
||||||
1
|
|
||||||
2
|
|
||||||
x
|
|
||||||
}
|
|
||||||
2 -> :two
|
|
||||||
42 -> :everything
|
|
||||||
_ -> :something_else
|
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
run(src);
|
run(src);
|
||||||
|
|
19
src/value.rs
19
src/value.rs
|
@ -126,4 +126,23 @@ impl Value {
|
||||||
_ => todo!(),
|
_ => 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()
|
self.stack.pop().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn peek(&self) -> &Value {
|
||||||
|
self.stack.last().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn print_stack(&self) {
|
fn print_stack(&self) {
|
||||||
let inner = self
|
let inner = self
|
||||||
.stack
|
.stack
|
||||||
|
@ -68,8 +72,8 @@ impl<'a> Vm<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_debug(&self) {
|
fn print_debug(&self) {
|
||||||
self.chunk.dissasemble_instr(self.ip);
|
|
||||||
self.print_stack();
|
self.print_stack();
|
||||||
|
self.chunk.dissasemble_instr(self.ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(&mut self) -> Result<Value, Panic> {
|
pub fn interpret(&mut self) -> Result<Value, Panic> {
|
||||||
|
@ -106,7 +110,12 @@ impl<'a> Vm<'a> {
|
||||||
}
|
}
|
||||||
Jump => {
|
Jump => {
|
||||||
let jump_len = self.chunk.bytecode[self.ip + 1];
|
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()
|
self.interpret()
|
||||||
}
|
}
|
||||||
JumpIfFalse => {
|
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 => {
|
Pop => {
|
||||||
self.pop();
|
self.pop();
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
|
@ -258,6 +282,38 @@ impl<'a> Vm<'a> {
|
||||||
}
|
}
|
||||||
self.interpret()
|
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")),
|
PanicNoWhen | PanicNoMatch => Err(Panic("no match")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user