Compare commits
5 Commits
ecd16e3aef
...
a5f2e2a9bd
Author | SHA1 | Date | |
---|---|---|---|
|
a5f2e2a9bd | ||
|
a0ef6d2777 | ||
|
d727096422 | ||
|
979afdbcb9 | ||
|
316e8a6a58 |
135
src/compiler.rs
135
src/compiler.rs
|
@ -261,13 +261,20 @@ impl Chunk {
|
||||||
*i += 1;
|
*i += 1;
|
||||||
}
|
}
|
||||||
PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple
|
PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple
|
||||||
| PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | JumpIfMatch
|
| PushBox | MatchDepth | PopN | StoreAt | Call | SetUpvalue | GetUpvalue | Partial
|
||||||
| JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call | SetUpvalue
|
| MatchString | PushStringMatches => {
|
||||||
| GetUpvalue | Partial | MatchString | PushStringMatches => {
|
|
||||||
let next = self.bytecode[*i + 1];
|
let next = self.bytecode[*i + 1];
|
||||||
println!("{i:04}: {:16} {next:03}", op.to_string());
|
println!("{i:04}: {:16} {next:03}", op.to_string());
|
||||||
*i += 1;
|
*i += 1;
|
||||||
}
|
}
|
||||||
|
Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | JumpIfMatch | JumpBack
|
||||||
|
| JumpIfZero => {
|
||||||
|
let high = self.bytecode[*i + 1];
|
||||||
|
let low = self.bytecode[*i + 2];
|
||||||
|
let len = ((high as u16) << 8) + low as u16;
|
||||||
|
println!("{i:04}: {:16} {len:05}", op.to_string());
|
||||||
|
*i += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,6 +402,29 @@ impl<'a> Compiler<'a> {
|
||||||
self.span = root_span;
|
self.span = root_span;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn jump(&mut self, op: Op, len: usize) {
|
||||||
|
let low = len as u8;
|
||||||
|
let high = (len >> 8) as u8;
|
||||||
|
self.emit_op(op);
|
||||||
|
self.chunk.bytecode.push(high);
|
||||||
|
self.chunk.bytecode.push(low);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump_stub(&mut self, op: Op) -> usize {
|
||||||
|
let out = self.chunk.bytecode.len();
|
||||||
|
self.emit_op(op);
|
||||||
|
self.emit_byte(0xff);
|
||||||
|
self.emit_byte(0xff);
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn patch_jump(&mut self, i: usize, len: usize) {
|
||||||
|
let low = len as u8;
|
||||||
|
let high = (len >> 8) as u8;
|
||||||
|
self.chunk.bytecode[i + 1] = high;
|
||||||
|
self.chunk.bytecode[i + 2] = low;
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_constant(&mut self, val: Value) {
|
fn emit_constant(&mut self, val: Value) {
|
||||||
let const_idx = if let Some(idx) = self.chunk.constants.iter().position(|v| *v == val) {
|
let const_idx = if let Some(idx) = self.chunk.constants.iter().position(|v| *v == val) {
|
||||||
idx
|
idx
|
||||||
|
@ -568,18 +598,26 @@ impl<'a> Compiler<'a> {
|
||||||
}
|
}
|
||||||
Keyword(s) => self.emit_constant(Value::Keyword(s)),
|
Keyword(s) => self.emit_constant(Value::Keyword(s)),
|
||||||
Block(lines) => {
|
Block(lines) => {
|
||||||
|
// increase the scope
|
||||||
self.scope_depth += 1;
|
self.scope_depth += 1;
|
||||||
|
// stash the stack depth
|
||||||
let stack_depth = self.stack_depth;
|
let stack_depth = self.stack_depth;
|
||||||
|
// evaluate all the lines but the last
|
||||||
for expr in lines.iter().take(lines.len() - 1) {
|
for expr in lines.iter().take(lines.len() - 1) {
|
||||||
if is_binding(expr) {
|
// evaluate the expression
|
||||||
self.visit(expr);
|
|
||||||
} else {
|
|
||||||
self.visit(expr);
|
self.visit(expr);
|
||||||
|
// if it doesn't bind a name, pop the result from the stack
|
||||||
|
if !is_binding(expr) {
|
||||||
self.pop();
|
self.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now, evaluate the last expression in the block
|
||||||
let last_expr = lines.last().unwrap();
|
let last_expr = lines.last().unwrap();
|
||||||
match last_expr {
|
match last_expr {
|
||||||
|
// if the last expression is a let form,
|
||||||
|
// return the evaluated rhs instead of whatever is last on the stack
|
||||||
|
// we do this by pretending it's a binding
|
||||||
(Let(patt, expr), _) => {
|
(Let(patt, expr), _) => {
|
||||||
self.match_depth = 0;
|
self.match_depth = 0;
|
||||||
self.emit_op(Op::ResetMatch);
|
self.emit_op(Op::ResetMatch);
|
||||||
|
@ -591,14 +629,20 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit_byte(expr_pos);
|
self.emit_byte(expr_pos);
|
||||||
self.stack_depth += 1;
|
self.stack_depth += 1;
|
||||||
}
|
}
|
||||||
|
// otherwise, just evaluate it and leave the value on the stack
|
||||||
_ => {
|
_ => {
|
||||||
self.visit(last_expr);
|
self.visit(last_expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.stack_depth += 1;
|
|
||||||
self.emit_op(Op::Store);
|
|
||||||
self.scope_depth -= 1;
|
|
||||||
|
|
||||||
|
// we've made a new value, so increase the stack level in the compiler
|
||||||
|
self.stack_depth += 1;
|
||||||
|
|
||||||
|
// store the value in the return register
|
||||||
|
self.emit_op(Op::Store);
|
||||||
|
|
||||||
|
// reset the scope
|
||||||
|
self.scope_depth -= 1;
|
||||||
while let Some(binding) = self.bindings.last() {
|
while let Some(binding) = self.bindings.last() {
|
||||||
if binding.depth > self.scope_depth {
|
if binding.depth > self.scope_depth {
|
||||||
self.bindings.pop();
|
self.bindings.pop();
|
||||||
|
@ -606,29 +650,24 @@ impl<'a> Compiler<'a> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// reset stack
|
// reset the stack
|
||||||
while self.stack_depth > stack_depth + 1 {
|
self.pop_n(self.stack_depth - stack_depth - 1);
|
||||||
self.pop();
|
// load the value from the return register
|
||||||
}
|
|
||||||
self.emit_op(Op::Load);
|
self.emit_op(Op::Load);
|
||||||
}
|
}
|
||||||
If(cond, then, r#else) => {
|
If(cond, then, r#else) => {
|
||||||
self.visit(cond);
|
self.visit(cond);
|
||||||
let jif_idx = self.len();
|
let jif_idx = self.jump_stub(Op::JumpIfFalse);
|
||||||
self.emit_op(Op::JumpIfFalse);
|
|
||||||
self.emit_byte(0xff);
|
|
||||||
self.stack_depth -= 1;
|
self.stack_depth -= 1;
|
||||||
self.visit(then);
|
self.visit(then);
|
||||||
let jump_idx = self.len();
|
let jump_idx = self.jump_stub(Op::Jump);
|
||||||
self.emit_op(Op::Jump);
|
|
||||||
self.emit_byte(0xff);
|
|
||||||
self.visit(r#else);
|
self.visit(r#else);
|
||||||
self.stack_depth -= 1;
|
self.stack_depth -= 1;
|
||||||
let end_idx = self.len();
|
let end_idx = self.len();
|
||||||
let jif_offset = jump_idx - jif_idx;
|
let jif_offset = jump_idx - jif_idx;
|
||||||
let jump_offset = end_idx - jump_idx - 2;
|
let jump_offset = end_idx - jump_idx - 1;
|
||||||
self.chunk.bytecode[jif_idx + 1] = jif_offset as u8;
|
self.patch_jump(jif_idx, jif_offset);
|
||||||
self.chunk.bytecode[jump_idx + 1] = jump_offset as u8;
|
self.patch_jump(jump_idx, jump_offset);
|
||||||
}
|
}
|
||||||
Let(patt, expr) => {
|
Let(patt, expr) => {
|
||||||
self.match_depth = 0;
|
self.match_depth = 0;
|
||||||
|
@ -679,36 +718,54 @@ impl<'a> Compiler<'a> {
|
||||||
self.match_constant(Value::Interned(s));
|
self.match_constant(Value::Interned(s));
|
||||||
}
|
}
|
||||||
TuplePattern(members) => {
|
TuplePattern(members) => {
|
||||||
|
// first, test the tuple against length
|
||||||
self.emit_op(Op::MatchTuple);
|
self.emit_op(Op::MatchTuple);
|
||||||
self.emit_byte(members.len());
|
self.emit_byte(members.len());
|
||||||
self.emit_op(Op::JumpIfNoMatch);
|
// skip everything if tuple lengths don't match
|
||||||
let before_load_tup_idx = self.len();
|
let before_load_tup_idx = self.jump_stub(Op::JumpIfNoMatch);
|
||||||
self.emit_byte(0xff);
|
|
||||||
|
// set up the per-member conditional logic
|
||||||
let mut jump_idxes = vec![];
|
let mut jump_idxes = vec![];
|
||||||
self.match_depth += members.len();
|
// stash match_depth, and set it to the tuple len
|
||||||
|
let match_depth = self.match_depth;
|
||||||
|
self.match_depth = members.len();
|
||||||
|
|
||||||
|
// load the tuple and update the stack len
|
||||||
self.emit_op(Op::LoadTuple);
|
self.emit_op(Op::LoadTuple);
|
||||||
self.stack_depth += members.len();
|
self.stack_depth += members.len();
|
||||||
|
|
||||||
|
// visit each member
|
||||||
for member in members {
|
for member in members {
|
||||||
|
// reduce the match depth to start
|
||||||
self.match_depth -= 1;
|
self.match_depth -= 1;
|
||||||
self.emit_op(Op::MatchDepth);
|
self.emit_op(Op::MatchDepth);
|
||||||
self.emit_byte(self.match_depth);
|
self.emit_byte(self.match_depth);
|
||||||
|
// visit the pattern member
|
||||||
self.visit(member);
|
self.visit(member);
|
||||||
self.emit_op(Op::JumpIfNoMatch);
|
// and jump if there's no match
|
||||||
jump_idxes.push(self.len());
|
jump_idxes.push(self.jump_stub(Op::JumpIfNoMatch));
|
||||||
self.emit_byte(0xff);
|
|
||||||
}
|
}
|
||||||
self.emit_op(Op::Jump);
|
|
||||||
let jump_idx = self.len();
|
// if we get here--not having jumped on no match--we're matched; jump the "no match" code
|
||||||
self.emit_byte(0xff);
|
self.match_depth = match_depth + members.len();
|
||||||
|
let jump_idx = self.jump_stub(Op::Jump);
|
||||||
|
|
||||||
|
// patch up the previous no match jumps to jump to clean-up code
|
||||||
for idx in jump_idxes {
|
for idx in jump_idxes {
|
||||||
self.chunk.bytecode[idx] = (self.len() - idx) as u8 - 1;
|
self.patch_jump(idx, self.len() - idx - 2)
|
||||||
}
|
}
|
||||||
for _ in 0..members.len() {
|
// pop everything that was pushed
|
||||||
self.emit_op(Op::Pop);
|
// don't change the compiler stack representation, tho
|
||||||
}
|
// we need this as cleanup code with no matches
|
||||||
self.chunk.bytecode[before_load_tup_idx] =
|
// the compiler should still have access to the bindings in this pattern
|
||||||
(self.len() - before_load_tup_idx) as u8 - 1;
|
self.emit_op(Op::PopN);
|
||||||
self.chunk.bytecode[jump_idx] = (self.len() - jump_idx) as u8 - 1;
|
self.emit_byte(members.len());
|
||||||
|
|
||||||
|
// patch up the tuple length match jump
|
||||||
|
self.patch_jump(before_load_tup_idx, self.len() - before_load_tup_idx - 3);
|
||||||
|
|
||||||
|
// patch up the yes-matches unconditional jump
|
||||||
|
self.patch_jump(jump_idx, self.len() - jump_idx - 3);
|
||||||
}
|
}
|
||||||
ListPattern(members) => {
|
ListPattern(members) => {
|
||||||
self.emit_op(Op::MatchList);
|
self.emit_op(Op::MatchList);
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -20,7 +20,6 @@ mod validator;
|
||||||
mod compiler;
|
mod compiler;
|
||||||
use crate::compiler::Compiler;
|
use crate::compiler::Compiler;
|
||||||
|
|
||||||
mod pattern;
|
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
mod vm;
|
mod vm;
|
||||||
|
@ -75,23 +74,8 @@ pub fn run(src: &'static str) {
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
// env::set_var("RUST_BACKTRACE", "1");
|
// env::set_var("RUST_BACKTRACE", "1");
|
||||||
// let src = "
|
let src = "
|
||||||
// let x = {
|
let ((1)) = ((1))
|
||||||
// match #{:a 1, :b 2, :c 3} with {
|
";
|
||||||
// #{a} -> :one
|
run(src);
|
||||||
// #{a, b, :c 3} -> :two
|
|
||||||
// #{a, b, c} -> :three
|
|
||||||
// (1, 2, 3) -> :thing
|
|
||||||
// (4, 5, (6, 7, a)) -> if or (true, false, false, true) then :thing_1 else :thing_2
|
|
||||||
// ([:a, :b, :c, [:d, [:e, (:f, :g)]]]) -> if or (true, false, false, true) then :thing_1 else :thing_2
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ";
|
|
||||||
// run(src);
|
|
||||||
let a: u16 = 14261;
|
|
||||||
let b_high: u8 = (a >> 8) as u8;
|
|
||||||
let b_low: u8 = a as u8;
|
|
||||||
let c: u16 = ((b_high as u16) << 8) + b_low as u16;
|
|
||||||
println!("{a} // {b_high}/{b_low} // {c}");
|
|
||||||
}
|
}
|
||||||
|
|
66
src/vm.rs
66
src/vm.rs
|
@ -57,7 +57,7 @@ impl CallFrame {
|
||||||
let Value::Fn(ref function) = self.function else {
|
let Value::Fn(ref function) = self.function else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
&function.chunk(self.arity)
|
function.chunk(self.arity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,11 @@ impl fmt::Display for CallFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn combine_bytes(high: u8, low: u8) -> usize {
|
||||||
|
let out = ((high as u16) << 8) + low as u16;
|
||||||
|
out as usize
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
pub stack: Vec<Value>,
|
pub stack: Vec<Value>,
|
||||||
pub call_stack: Vec<CallFrame>,
|
pub call_stack: Vec<CallFrame>,
|
||||||
|
@ -205,46 +210,61 @@ impl Vm {
|
||||||
self.ip += 2;
|
self.ip += 2;
|
||||||
}
|
}
|
||||||
Jump => {
|
Jump => {
|
||||||
let jump_len = self.chunk().bytecode[self.ip + 1];
|
let high = self.chunk().bytecode[self.ip + 1];
|
||||||
self.ip += jump_len as usize + 2;
|
let low = self.chunk().bytecode[self.ip + 2];
|
||||||
|
let jump_len = combine_bytes(high, low);
|
||||||
|
// let jump_len = self.chunk().bytecode[self.ip + 1];
|
||||||
|
self.ip += jump_len + 3;
|
||||||
}
|
}
|
||||||
JumpBack => {
|
JumpBack => {
|
||||||
let jump_len = self.chunk().bytecode[self.ip + 1];
|
let high = self.chunk().bytecode[self.ip + 1];
|
||||||
self.ip -= jump_len as usize;
|
let low = self.chunk().bytecode[self.ip + 2];
|
||||||
|
let jump_len = combine_bytes(high, low);
|
||||||
|
// let jump_len = self.chunk().bytecode[self.ip + 1];
|
||||||
|
self.ip -= jump_len;
|
||||||
}
|
}
|
||||||
JumpIfFalse => {
|
JumpIfFalse => {
|
||||||
let jump_len = self.chunk().bytecode[self.ip + 1];
|
let high = self.chunk().bytecode[self.ip + 1];
|
||||||
|
let low = self.chunk().bytecode[self.ip + 2];
|
||||||
|
let jump_len = combine_bytes(high, low);
|
||||||
|
// let jump_len = self.chunk().bytecode[self.ip + 1];
|
||||||
let cond = self.pop();
|
let cond = self.pop();
|
||||||
match cond {
|
match cond {
|
||||||
Value::Nil | Value::False => {
|
Value::Nil | Value::False => {
|
||||||
self.ip += jump_len as usize + 2;
|
self.ip += jump_len + 3;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.ip += 2;
|
self.ip += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JumpIfTrue => {
|
JumpIfTrue => {
|
||||||
let jump_len = self.chunk().bytecode[self.ip + 1];
|
let high = self.chunk().bytecode[self.ip + 1];
|
||||||
|
let low = self.chunk().bytecode[self.ip + 2];
|
||||||
|
let jump_len = combine_bytes(high, low);
|
||||||
|
// let jump_len = self.chunk().bytecode[self.ip + 1];
|
||||||
let cond = self.pop();
|
let cond = self.pop();
|
||||||
match cond {
|
match cond {
|
||||||
Value::Nil | Value::False => {
|
Value::Nil | Value::False => {
|
||||||
self.ip += 2;
|
self.ip += 3;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.ip += jump_len as usize + 2;
|
self.ip += jump_len + 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JumpIfZero => {
|
JumpIfZero => {
|
||||||
let jump_len = self.chunk().bytecode[self.ip + 1];
|
let high = self.chunk().bytecode[self.ip + 1];
|
||||||
|
let low = self.chunk().bytecode[self.ip + 2];
|
||||||
|
let jump_len = combine_bytes(high, low);
|
||||||
|
// let jump_len = self.chunk().bytecode[self.ip + 1];
|
||||||
let cond = self.pop();
|
let cond = self.pop();
|
||||||
match cond {
|
match cond {
|
||||||
Value::Number(x) if x <= 0.0 => {
|
Value::Number(x) if x <= 0.0 => {
|
||||||
self.ip += jump_len as usize + 2;
|
self.ip += jump_len as usize + 3;
|
||||||
}
|
}
|
||||||
Value::Number(..) => {
|
Value::Number(..) => {
|
||||||
self.ip += 2;
|
self.ip += 3;
|
||||||
}
|
}
|
||||||
_ => return self.panic("repeat requires a number"),
|
_ => return self.panic("repeat requires a number"),
|
||||||
}
|
}
|
||||||
|
@ -542,19 +562,25 @@ impl Vm {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
}
|
}
|
||||||
JumpIfNoMatch => {
|
JumpIfNoMatch => {
|
||||||
let jump_len = self.chunk().bytecode[self.ip + 1] as usize;
|
let high = self.chunk().bytecode[self.ip + 1];
|
||||||
|
let low = self.chunk().bytecode[self.ip + 2];
|
||||||
|
let jump_len = combine_bytes(high, low);
|
||||||
|
// let jump_len = self.chunk().bytecode[self.ip + 1] as usize;
|
||||||
if !self.matches {
|
if !self.matches {
|
||||||
self.ip += jump_len + 2;
|
self.ip += jump_len + 3;
|
||||||
} else {
|
} else {
|
||||||
self.ip += 2;
|
self.ip += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JumpIfMatch => {
|
JumpIfMatch => {
|
||||||
let jump_len = self.chunk().bytecode[self.ip + 1] as usize;
|
let high = self.chunk().bytecode[self.ip + 1];
|
||||||
|
let low = self.chunk().bytecode[self.ip + 2];
|
||||||
|
let jump_len = combine_bytes(high, low);
|
||||||
|
// let jump_len = self.chunk().bytecode[self.ip + 1] as usize;
|
||||||
if self.matches {
|
if self.matches {
|
||||||
self.ip += jump_len + 2;
|
self.ip += jump_len + 3;
|
||||||
} else {
|
} else {
|
||||||
self.ip += 2;
|
self.ip += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypeOf => {
|
TypeOf => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user