do lots of work
This commit is contained in:
parent
d4342b0623
commit
d943185db8
|
@ -131,3 +131,4 @@ That's probably the thing to do. Jesus, Scott.
|
||||||
|
|
||||||
And **another** thing worth internalizing: every single instruction that's not an explicit push or pop should leave the stack length unchanged.
|
And **another** thing worth internalizing: every single instruction that's not an explicit push or pop should leave the stack length unchanged.
|
||||||
So store and load need always to swap in a `nil`
|
So store and load need always to swap in a `nil`
|
||||||
|
|
||||||
|
|
236
src/compiler.rs
236
src/compiler.rs
|
@ -7,6 +7,9 @@ use num_traits::FromPrimitive;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
|
Nil,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
Constant,
|
Constant,
|
||||||
Jump,
|
Jump,
|
||||||
JumpIfFalse,
|
JumpIfFalse,
|
||||||
|
@ -14,20 +17,52 @@ pub enum Op {
|
||||||
PushBinding,
|
PushBinding,
|
||||||
Store,
|
Store,
|
||||||
Load,
|
Load,
|
||||||
|
ResetMatch,
|
||||||
|
MatchNil,
|
||||||
|
MatchTrue,
|
||||||
|
MatchFalse,
|
||||||
|
MatchWord,
|
||||||
|
PanicIfNoMatch,
|
||||||
|
MatchConstant,
|
||||||
|
MatchTuple,
|
||||||
|
PushTuple,
|
||||||
|
PushList,
|
||||||
|
PushDict,
|
||||||
|
PushBox,
|
||||||
|
GetKey,
|
||||||
|
PanicNoWhen,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Op {
|
impl std::fmt::Display for Op {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
use Op::*;
|
use Op::*;
|
||||||
match self {
|
let rep = match self {
|
||||||
Constant => write!(f, "constant"),
|
Nil => "nil",
|
||||||
Jump => write!(f, "jump"),
|
True => "true",
|
||||||
JumpIfFalse => write!(f, "jump_if_false"),
|
False => "false",
|
||||||
Pop => write!(f, "pop"),
|
Constant => "constant",
|
||||||
PushBinding => write!(f, "push_binding"),
|
Jump => "jump",
|
||||||
Store => write!(f, "store"),
|
JumpIfFalse => "jump_if_false",
|
||||||
Load => write!(f, "load"),
|
Pop => "pop",
|
||||||
}
|
PushBinding => "push_binding",
|
||||||
|
Store => "store",
|
||||||
|
Load => "load",
|
||||||
|
MatchNil => "match_nil",
|
||||||
|
MatchTrue => "match_true",
|
||||||
|
MatchFalse => "match_false",
|
||||||
|
MatchWord => "match_word",
|
||||||
|
ResetMatch => "reset_match",
|
||||||
|
PanicIfNoMatch => "panic_if_no_match",
|
||||||
|
MatchConstant => "match_constant",
|
||||||
|
MatchTuple => "match_tuple",
|
||||||
|
PushTuple => "push_tuple",
|
||||||
|
PushList => "push_list",
|
||||||
|
PushDict => "push_dict",
|
||||||
|
PushBox => "push_box",
|
||||||
|
GetKey => "get_key",
|
||||||
|
PanicNoWhen => "panic_no_when",
|
||||||
|
};
|
||||||
|
write!(f, "{rep}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,10 +121,11 @@ impl<'a> Chunk<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kw_from(&self, kw: &str) -> Option<Value> {
|
pub fn kw_from(&self, kw: &str) -> Option<Value> {
|
||||||
self.keywords
|
self.kw_index_from(kw).map(Value::Keyword)
|
||||||
.iter()
|
}
|
||||||
.position(|s| *s == kw)
|
|
||||||
.map(Value::Keyword)
|
pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
|
||||||
|
self.keywords.iter().position(|s| *s == kw)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit(&mut self, node: &'a Spanned<Ast>) {
|
pub fn visit(&mut self, node: &'a Spanned<Ast>) {
|
||||||
|
@ -118,6 +154,27 @@ impl<'a> Chunk<'a> {
|
||||||
self.spans.push(self.span);
|
self.spans.push(self.span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn match_constant(&mut self, val: Value) {
|
||||||
|
let constant_index = match self.constants.iter().position(|v| *v == val) {
|
||||||
|
Some(idx) => idx,
|
||||||
|
None => self.constants.len(),
|
||||||
|
};
|
||||||
|
if constant_index > u8::MAX as usize {
|
||||||
|
panic!(
|
||||||
|
"internal Ludus compiler error: too many constants in chunk:{}:: {}",
|
||||||
|
self.span, self.ast
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if constant_index == self.constants.len() {
|
||||||
|
self.constants.push(val);
|
||||||
|
}
|
||||||
|
self.bytecode.push(Op::MatchConstant as u8);
|
||||||
|
self.spans.push(self.span);
|
||||||
|
self.bytecode.push(constant_index as u8);
|
||||||
|
self.spans.push(self.span);
|
||||||
|
self.bind("*constant");
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_op(&mut self, op: Op) {
|
fn emit_op(&mut self, op: Op) {
|
||||||
self.bytecode.push(op as u8);
|
self.bytecode.push(op as u8);
|
||||||
self.spans.push(self.span);
|
self.spans.push(self.span);
|
||||||
|
@ -133,11 +190,15 @@ impl<'a> Chunk<'a> {
|
||||||
pub fn compile(&mut self) {
|
pub fn compile(&mut self) {
|
||||||
use Ast::*;
|
use Ast::*;
|
||||||
match self.ast {
|
match self.ast {
|
||||||
Nil => self.emit_constant(Value::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_constant(if *b { Value::True } else { Value::False }),
|
Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }),
|
||||||
String(s) => {
|
String(s) => {
|
||||||
let str_index = self.strings.len();
|
let existing_str = self.strings.iter().position(|e| e == s);
|
||||||
|
let str_index = match existing_str {
|
||||||
|
Some(idx) => idx,
|
||||||
|
None => self.strings.len(),
|
||||||
|
};
|
||||||
self.strings.push(s);
|
self.strings.push(s);
|
||||||
self.emit_constant(Value::Interned(str_index));
|
self.emit_constant(Value::Interned(str_index));
|
||||||
}
|
}
|
||||||
|
@ -147,7 +208,9 @@ impl<'a> Chunk<'a> {
|
||||||
Some(index) => index,
|
Some(index) => index,
|
||||||
None => self.keywords.len(),
|
None => self.keywords.len(),
|
||||||
};
|
};
|
||||||
self.keywords.push(s);
|
if kw_index == self.keywords.len() {
|
||||||
|
self.keywords.push(s);
|
||||||
|
}
|
||||||
self.emit_constant(Value::Keyword(kw_index));
|
self.emit_constant(Value::Keyword(kw_index));
|
||||||
}
|
}
|
||||||
Block(lines) => {
|
Block(lines) => {
|
||||||
|
@ -190,10 +253,13 @@ impl<'a> Chunk<'a> {
|
||||||
self.bytecode[jump_idx + 1] = jump_offset as u8;
|
self.bytecode[jump_idx + 1] = jump_offset as u8;
|
||||||
}
|
}
|
||||||
Let(patt, expr) => {
|
Let(patt, expr) => {
|
||||||
|
self.emit_op(Op::ResetMatch);
|
||||||
self.visit(expr);
|
self.visit(expr);
|
||||||
self.visit(patt);
|
self.visit(patt);
|
||||||
|
self.emit_op(Op::PanicIfNoMatch);
|
||||||
}
|
}
|
||||||
WordPattern(name) => {
|
WordPattern(name) => {
|
||||||
|
self.emit_op(Op::MatchWord);
|
||||||
self.bind(name);
|
self.bind(name);
|
||||||
}
|
}
|
||||||
Word(name) => {
|
Word(name) => {
|
||||||
|
@ -207,8 +273,126 @@ impl<'a> Chunk<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PlaceholderPattern => {
|
PlaceholderPattern => {
|
||||||
|
self.emit_op(Op::MatchWord);
|
||||||
self.bind("_");
|
self.bind("_");
|
||||||
}
|
}
|
||||||
|
NilPattern => {
|
||||||
|
self.emit_op(Op::MatchNil);
|
||||||
|
self.bind("nil");
|
||||||
|
}
|
||||||
|
BooleanPattern(b) => {
|
||||||
|
if *b {
|
||||||
|
self.emit_op(Op::MatchTrue);
|
||||||
|
self.bind("true");
|
||||||
|
} else {
|
||||||
|
self.emit_op(Op::MatchFalse);
|
||||||
|
self.bind("false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberPattern(n) => {
|
||||||
|
self.match_constant(Value::Number(*n));
|
||||||
|
}
|
||||||
|
KeywordPattern(s) => {
|
||||||
|
let existing_kw = self.keywords.iter().position(|kw| kw == s);
|
||||||
|
let kw_index = match existing_kw {
|
||||||
|
Some(index) => index,
|
||||||
|
None => self.keywords.len(),
|
||||||
|
};
|
||||||
|
if kw_index == self.keywords.len() {
|
||||||
|
self.keywords.push(s);
|
||||||
|
}
|
||||||
|
self.match_constant(Value::Keyword(kw_index));
|
||||||
|
}
|
||||||
|
StringPattern(s) => {
|
||||||
|
let existing_str = self.strings.iter().position(|e| e == s);
|
||||||
|
let str_index = match existing_str {
|
||||||
|
Some(idx) => idx,
|
||||||
|
None => self.strings.len(),
|
||||||
|
};
|
||||||
|
if str_index == self.strings.len() {
|
||||||
|
self.strings.push(s)
|
||||||
|
}
|
||||||
|
self.match_constant(Value::Interned(str_index));
|
||||||
|
}
|
||||||
|
Tuple(members) => {
|
||||||
|
for member in members {
|
||||||
|
self.visit(member);
|
||||||
|
}
|
||||||
|
self.emit_op(Op::PushTuple);
|
||||||
|
self.bytecode.push(members.len() as u8);
|
||||||
|
}
|
||||||
|
List(members) => {
|
||||||
|
for member in members {
|
||||||
|
self.visit(member);
|
||||||
|
}
|
||||||
|
self.emit_op(Op::PushList);
|
||||||
|
self.bytecode.push(members.len() as u8);
|
||||||
|
}
|
||||||
|
LBox(name, expr) => {
|
||||||
|
self.visit(expr);
|
||||||
|
self.emit_op(Op::PushBox);
|
||||||
|
self.bind(name);
|
||||||
|
}
|
||||||
|
Dict(pairs) => {
|
||||||
|
for pair in pairs {
|
||||||
|
self.visit(pair);
|
||||||
|
}
|
||||||
|
self.emit_op(Op::PushDict);
|
||||||
|
self.bytecode.push(pairs.len() as u8);
|
||||||
|
}
|
||||||
|
Pair(key, value) => {
|
||||||
|
let existing_kw = self.keywords.iter().position(|kw| kw == key);
|
||||||
|
let kw_index = match existing_kw {
|
||||||
|
Some(index) => index,
|
||||||
|
None => self.keywords.len(),
|
||||||
|
};
|
||||||
|
if kw_index == self.keywords.len() {
|
||||||
|
self.keywords.push(key);
|
||||||
|
}
|
||||||
|
self.emit_constant(Value::Keyword(kw_index));
|
||||||
|
self.visit(value);
|
||||||
|
}
|
||||||
|
Synthetic(first, second, rest) => {
|
||||||
|
match (&first.0, &second.0) {
|
||||||
|
(Word(_), Keyword(_)) => {
|
||||||
|
self.visit(first);
|
||||||
|
self.visit(second);
|
||||||
|
self.emit_op(Op::GetKey);
|
||||||
|
}
|
||||||
|
(Keyword(_), Arguments(args)) => {
|
||||||
|
self.visit(&args[0]);
|
||||||
|
self.visit(first);
|
||||||
|
self.emit_op(Op::GetKey);
|
||||||
|
}
|
||||||
|
(Word(_), Arguments(_)) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
for term in rest {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
When(clauses) => {
|
||||||
|
let mut jump_idxes = vec![];
|
||||||
|
let mut clauses = clauses.iter();
|
||||||
|
while let Some((WhenClause(cond, body), _)) = clauses.next() {
|
||||||
|
self.visit(cond.as_ref());
|
||||||
|
self.emit_op(Op::JumpIfFalse);
|
||||||
|
let jif_jump_idx = self.bytecode.len();
|
||||||
|
self.bytecode.push(0xff);
|
||||||
|
self.visit(body);
|
||||||
|
self.emit_op(Op::Jump);
|
||||||
|
jump_idxes.push(self.bytecode.len());
|
||||||
|
self.bytecode.push(0xff);
|
||||||
|
self.bytecode[jif_jump_idx] =
|
||||||
|
self.bytecode.len() as u8 - jif_jump_idx as u8 - 1;
|
||||||
|
}
|
||||||
|
self.emit_op(Op::PanicNoWhen);
|
||||||
|
for idx in jump_idxes {
|
||||||
|
self.bytecode[idx] = self.bytecode.len() as u8 - idx as u8 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,13 +405,16 @@ impl<'a> Chunk<'a> {
|
||||||
let op = Op::from_u8(*byte).unwrap();
|
let op = Op::from_u8(*byte).unwrap();
|
||||||
use Op::*;
|
use Op::*;
|
||||||
match op {
|
match op {
|
||||||
Pop | Store | Load => println!("{i:04}: {op}"),
|
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||||
Constant => {
|
| MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen => {
|
||||||
|
println!("{i:04}: {op}")
|
||||||
|
}
|
||||||
|
Constant | MatchConstant => {
|
||||||
let (_, next) = codes.next().unwrap();
|
let (_, next) = codes.next().unwrap();
|
||||||
let value = &self.constants[*next as usize].show(self);
|
let value = &self.constants[*next as usize].show(self);
|
||||||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||||
}
|
}
|
||||||
PushBinding => {
|
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox => {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
@ -243,13 +430,16 @@ impl<'a> Chunk<'a> {
|
||||||
let op = Op::from_u8(self.bytecode[i]).unwrap();
|
let op = Op::from_u8(self.bytecode[i]).unwrap();
|
||||||
use Op::*;
|
use Op::*;
|
||||||
match op {
|
match op {
|
||||||
Pop | Store | Load => println!("{i:04}: {op}"),
|
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||||
Constant => {
|
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen => {
|
||||||
|
println!("{i:04}: {op}")
|
||||||
|
}
|
||||||
|
Constant | MatchConstant => {
|
||||||
let next = self.bytecode[i + 1];
|
let next = self.bytecode[i + 1];
|
||||||
let value = &self.constants[next as usize].show(self);
|
let value = &self.constants[next as usize].show(self);
|
||||||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||||
}
|
}
|
||||||
PushBinding => {
|
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox => {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -64,17 +64,12 @@ pub fn run(src: &'static str) {
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let src = "
|
let src = "
|
||||||
let foo = :let_foo
|
let foo = 42
|
||||||
|
when {
|
||||||
let bar = if true
|
foo -> :two
|
||||||
then {
|
false -> :four
|
||||||
let baz = :baz
|
:else -> :thing
|
||||||
}
|
}
|
||||||
else :whatever
|
|
||||||
|
|
||||||
foo
|
|
||||||
|
|
||||||
bar
|
|
||||||
";
|
";
|
||||||
run(src);
|
run(src);
|
||||||
}
|
}
|
||||||
|
|
41
src/value.rs
41
src/value.rs
|
@ -5,12 +5,6 @@ use imbl::{HashMap, Vector};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct LBox {
|
|
||||||
pub name: usize,
|
|
||||||
pub cell: RefCell<Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct LFn {
|
pub struct LFn {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
|
@ -27,17 +21,15 @@ pub enum Value {
|
||||||
Nil,
|
Nil,
|
||||||
True,
|
True,
|
||||||
False,
|
False,
|
||||||
Keyword(usize), // use an idx, rather than a raw index
|
Keyword(usize),
|
||||||
Interned(usize),
|
Interned(usize),
|
||||||
FnDecl(usize),
|
FnDecl(usize),
|
||||||
String(Rc<String>),
|
String(Rc<String>),
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Tuple(Rc<Vec<Value>>),
|
Tuple(Rc<Vec<Value>>),
|
||||||
TupleStart { len: u8, size: u16 },
|
|
||||||
TupleEnd { len: u8, size: u16 },
|
|
||||||
List(Box<Vector<Value>>),
|
List(Box<Vector<Value>>),
|
||||||
Dict(Box<HashMap<usize, Value>>),
|
Dict(Box<HashMap<usize, Value>>),
|
||||||
Box(Rc<LBox>),
|
Box(Rc<RefCell<Value>>),
|
||||||
Fn(Rc<RefCell<LFn>>),
|
Fn(Rc<RefCell<LFn>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +43,34 @@ impl std::fmt::Display for Value {
|
||||||
Keyword(idx) => write!(f, ":{idx}"),
|
Keyword(idx) => write!(f, ":{idx}"),
|
||||||
Interned(idx) => write!(f, "\"@{idx}\""),
|
Interned(idx) => write!(f, "\"@{idx}\""),
|
||||||
Number(n) => write!(f, "{n}"),
|
Number(n) => write!(f, "{n}"),
|
||||||
|
Tuple(members) => write!(
|
||||||
|
f,
|
||||||
|
"({})",
|
||||||
|
members
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
List(members) => write!(
|
||||||
|
f,
|
||||||
|
"[{}]",
|
||||||
|
members
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
Dict(members) => write!(
|
||||||
|
f,
|
||||||
|
"#{{{}}}",
|
||||||
|
members
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{k} {v}"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
Box(value) => write!(f, "box {}", value.as_ref().borrow()),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +113,7 @@ impl Value {
|
||||||
format!("#{{{members}}}")
|
format!("#{{{members}}}")
|
||||||
}
|
}
|
||||||
String(s) => s.as_ref().clone(),
|
String(s) => s.as_ref().clone(),
|
||||||
|
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show(ctx)),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
148
src/vm.rs
148
src/vm.rs
|
@ -3,18 +3,22 @@ use crate::parser::Ast;
|
||||||
use crate::spans::Spanned;
|
use crate::spans::Spanned;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use chumsky::prelude::SimpleSpan;
|
use chumsky::prelude::SimpleSpan;
|
||||||
|
use imbl::{HashMap, Vector};
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::mem::swap;
|
use std::mem::swap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Panic {
|
// pub struct Panic {
|
||||||
pub input: &'static str,
|
// pub input: &'static str,
|
||||||
pub src: &'static str,
|
// pub src: &'static str,
|
||||||
pub msg: String,
|
// pub msg: String,
|
||||||
pub span: SimpleSpan,
|
// pub span: SimpleSpan,
|
||||||
pub trace: Vec<Trace>,
|
// pub trace: Vec<Trace>,
|
||||||
pub extra: String,
|
// pub extra: String,
|
||||||
}
|
// }
|
||||||
|
pub struct Panic(&'static str);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Trace {
|
pub struct Trace {
|
||||||
|
@ -31,6 +35,7 @@ pub struct Vm<'a> {
|
||||||
pub chunk: &'a Chunk<'a>,
|
pub chunk: &'a Chunk<'a>,
|
||||||
pub ip: usize,
|
pub ip: usize,
|
||||||
pub return_register: Value,
|
pub return_register: Value,
|
||||||
|
pub matches: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Vm<'a> {
|
impl<'a> Vm<'a> {
|
||||||
|
@ -40,6 +45,7 @@ impl<'a> Vm<'a> {
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
ip: 0,
|
ip: 0,
|
||||||
return_register: Value::Nil,
|
return_register: Value::Nil,
|
||||||
|
matches: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +82,21 @@ impl<'a> Vm<'a> {
|
||||||
let op = Op::from_u8(*byte).unwrap();
|
let op = Op::from_u8(*byte).unwrap();
|
||||||
use Op::*;
|
use Op::*;
|
||||||
match op {
|
match op {
|
||||||
|
Nil => {
|
||||||
|
self.push(Value::Nil);
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
True => {
|
||||||
|
self.push(Value::True);
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
False => {
|
||||||
|
self.push(Value::False);
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
Constant => {
|
Constant => {
|
||||||
let const_idx = self.chunk.bytecode[self.ip + 1];
|
let const_idx = self.chunk.bytecode[self.ip + 1];
|
||||||
let value = self.chunk.constants[const_idx as usize].clone();
|
let value = self.chunk.constants[const_idx as usize].clone();
|
||||||
|
@ -122,20 +143,113 @@ impl<'a> Vm<'a> {
|
||||||
}
|
}
|
||||||
Load => {
|
Load => {
|
||||||
let mut value = Value::Nil;
|
let mut value = Value::Nil;
|
||||||
// println!(
|
|
||||||
// "before swap, return register holds: {}",
|
|
||||||
// self.return_register
|
|
||||||
// );
|
|
||||||
swap(&mut self.return_register, &mut value);
|
swap(&mut self.return_register, &mut value);
|
||||||
// println!(
|
|
||||||
// "before swap, return register holds: {}",
|
|
||||||
// self.return_register
|
|
||||||
// );
|
|
||||||
// println!("now local value holds {value}");
|
|
||||||
self.push(value);
|
self.push(value);
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
self.interpret()
|
self.interpret()
|
||||||
}
|
}
|
||||||
|
ResetMatch => {
|
||||||
|
self.matches = false;
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
MatchWord => {
|
||||||
|
self.matches = true;
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
MatchNil => {
|
||||||
|
if *self.stack.last().unwrap() == Value::Nil {
|
||||||
|
self.matches = true;
|
||||||
|
};
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
MatchTrue => {
|
||||||
|
if *self.stack.last().unwrap() == Value::True {
|
||||||
|
self.matches = true;
|
||||||
|
};
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
MatchFalse => {
|
||||||
|
if *self.stack.last().unwrap() == Value::False {
|
||||||
|
self.matches = true;
|
||||||
|
}
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
PanicIfNoMatch => {
|
||||||
|
if !self.matches {
|
||||||
|
Err(Panic("no match"))
|
||||||
|
} else {
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MatchConstant => {
|
||||||
|
let const_idx = self.chunk.bytecode[self.ip + 1];
|
||||||
|
let value = self.stack.last().unwrap();
|
||||||
|
self.matches = *value == self.chunk.constants[const_idx as usize];
|
||||||
|
self.ip += 2;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
PushTuple => {
|
||||||
|
let tuple_len = self.chunk.bytecode[self.ip + 1];
|
||||||
|
let tuple_members = self.stack.split_off(self.stack.len() - tuple_len as usize);
|
||||||
|
let tuple = Value::Tuple(Rc::new(tuple_members));
|
||||||
|
self.stack.push(tuple);
|
||||||
|
self.ip += 2;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
PushList => {
|
||||||
|
let list_len = self.chunk.bytecode[self.ip + 1];
|
||||||
|
let list_members = self.stack.split_off(self.stack.len() - list_len as usize);
|
||||||
|
let list = Value::List(Box::new(Vector::from(list_members)));
|
||||||
|
self.stack.push(list);
|
||||||
|
self.ip += 2;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
PushDict => {
|
||||||
|
let dict_len = self.chunk.bytecode[self.ip + 1] as usize * 2;
|
||||||
|
let dict_members = self.stack.split_off(self.stack.len() - dict_len);
|
||||||
|
let mut dict = HashMap::new();
|
||||||
|
let mut dict_iter = dict_members.iter();
|
||||||
|
while let Some(kw) = dict_iter.next() {
|
||||||
|
let Value::Keyword(key) = kw else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let value = dict_iter.next().unwrap();
|
||||||
|
dict.insert(*key, value.clone());
|
||||||
|
}
|
||||||
|
self.stack.push(Value::Dict(Box::new(dict)));
|
||||||
|
self.ip += 2;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
PushBox => {
|
||||||
|
let val = self.pop();
|
||||||
|
self.stack.push(Value::Box(Rc::new(RefCell::new(val))));
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
GetKey => {
|
||||||
|
let key = self.pop();
|
||||||
|
let Value::Keyword(idx) = key else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let dict = self.pop();
|
||||||
|
let value = match dict {
|
||||||
|
Value::Dict(d) => d.as_ref().get(&idx).unwrap_or(&Value::Nil).clone(),
|
||||||
|
_ => Value::Nil,
|
||||||
|
};
|
||||||
|
self.push(value);
|
||||||
|
self.ip += 1;
|
||||||
|
self.interpret()
|
||||||
|
}
|
||||||
|
MatchTuple => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
PanicNoWhen => Err(Panic("no match")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user