add builtins, fix basic synthetic expressions
This commit is contained in:
parent
74cc0025d6
commit
182b14e5f4
|
@ -5,6 +5,7 @@ use chumsky::prelude::SimpleSpan;
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||||
|
@ -51,6 +52,17 @@ pub enum Op {
|
||||||
Decrement,
|
Decrement,
|
||||||
Truncate,
|
Truncate,
|
||||||
MatchDepth,
|
MatchDepth,
|
||||||
|
|
||||||
|
Eq,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mult,
|
||||||
|
Div,
|
||||||
|
Unbox,
|
||||||
|
BoxStore,
|
||||||
|
Assert,
|
||||||
|
Get,
|
||||||
|
At,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Op {
|
impl std::fmt::Display for Op {
|
||||||
|
@ -99,6 +111,17 @@ impl std::fmt::Display for Op {
|
||||||
Truncate => "truncate",
|
Truncate => "truncate",
|
||||||
Duplicate => "duplicate",
|
Duplicate => "duplicate",
|
||||||
MatchDepth => "match_depth",
|
MatchDepth => "match_depth",
|
||||||
|
|
||||||
|
Eq => "eq",
|
||||||
|
Add => "add",
|
||||||
|
Sub => "sub",
|
||||||
|
Mult => "mult",
|
||||||
|
Div => "div",
|
||||||
|
Unbox => "unbox",
|
||||||
|
BoxStore => "box_store",
|
||||||
|
Assert => "assert",
|
||||||
|
Get => "get",
|
||||||
|
At => "at",
|
||||||
};
|
};
|
||||||
write!(f, "{rep}")
|
write!(f, "{rep}")
|
||||||
}
|
}
|
||||||
|
@ -126,7 +149,8 @@ impl Chunk {
|
||||||
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 | Noop | LoadTuple | LoadList => {
|
| TypeOf | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq
|
||||||
|
| Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At => {
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -164,6 +188,23 @@ impl LoopInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_builtin(name: &str, arity: usize) -> Option<Op> {
|
||||||
|
match (name, arity) {
|
||||||
|
("type", 1) => Some(Op::TypeOf),
|
||||||
|
("eq?", 2) => Some(Op::Eq),
|
||||||
|
("add", 2) => Some(Op::Add),
|
||||||
|
("sub", 2) => Some(Op::Sub),
|
||||||
|
("mult", 2) => Some(Op::Mult),
|
||||||
|
("div", 2) => Some(Op::Div),
|
||||||
|
("unbox", 1) => Some(Op::Unbox),
|
||||||
|
("store!", 2) => Some(Op::BoxStore),
|
||||||
|
("assert!", 1) => Some(Op::Assert),
|
||||||
|
("get", 2) => Some(Op::Get),
|
||||||
|
("at", 2) => Some(Op::At),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
pub chunk: Chunk,
|
pub chunk: Chunk,
|
||||||
|
@ -612,7 +653,6 @@ impl Compiler {
|
||||||
LBox(name, expr) => {
|
LBox(name, expr) => {
|
||||||
self.visit(expr);
|
self.visit(expr);
|
||||||
self.emit_op(Op::PushBox);
|
self.emit_op(Op::PushBox);
|
||||||
self.stack_depth += 1;
|
|
||||||
self.bind(name);
|
self.bind(name);
|
||||||
}
|
}
|
||||||
Dict(pairs) => {
|
Dict(pairs) => {
|
||||||
|
@ -649,15 +689,24 @@ impl Compiler {
|
||||||
self.visit(first);
|
self.visit(first);
|
||||||
self.visit(second);
|
self.visit(second);
|
||||||
self.emit_op(Op::GetKey);
|
self.emit_op(Op::GetKey);
|
||||||
|
self.stack_depth -= 1;
|
||||||
}
|
}
|
||||||
(Keyword(_), Arguments(args)) => {
|
(Keyword(_), Arguments(args)) => {
|
||||||
self.visit(&args[0]);
|
self.visit(&args[0]);
|
||||||
self.visit(first);
|
self.visit(first);
|
||||||
self.emit_op(Op::GetKey);
|
self.emit_op(Op::GetKey);
|
||||||
|
self.stack_depth -= 1;
|
||||||
}
|
}
|
||||||
(Word(_), Arguments(_)) => {
|
(Word(fn_name), Arguments(args)) => match get_builtin(fn_name, args.len()) {
|
||||||
todo!()
|
Some(code) => {
|
||||||
|
for arg in args {
|
||||||
|
self.visit(arg);
|
||||||
}
|
}
|
||||||
|
self.emit_op(code);
|
||||||
|
self.stack_depth -= args.len() - 1;
|
||||||
|
}
|
||||||
|
None => todo!(),
|
||||||
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
// TODO: implement longer synthetic expressions
|
// TODO: implement longer synthetic expressions
|
||||||
|
@ -941,7 +990,7 @@ impl Compiler {
|
||||||
Noop | Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue
|
Noop | Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue
|
||||||
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
||||||
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple
|
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple
|
||||||
| LoadList => {
|
| LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At => {
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -74,16 +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 = "
|
||||||
loop (true) with {
|
let foo = #{:a 1, :b 2}
|
||||||
(false) -> :done
|
:a (foo)
|
||||||
(true) -> recur (42)
|
|
||||||
(42) -> recur (:thingie)
|
|
||||||
(:thingie) -> {
|
|
||||||
let x = false
|
|
||||||
12
|
|
||||||
recur (x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
";
|
";
|
||||||
run(src);
|
run(src);
|
||||||
}
|
}
|
||||||
|
|
111
src/vm.rs
111
src/vm.rs
|
@ -419,6 +419,117 @@ impl<'a> Vm<'a> {
|
||||||
self.interpret()
|
self.interpret()
|
||||||
}
|
}
|
||||||
PanicNoWhen | PanicNoMatch => self.panic("no match"),
|
PanicNoWhen | PanicNoMatch => self.panic("no match"),
|
||||||
|
|
||||||
|
Eq => {
|
||||||
|
let first = self.stack.pop().unwrap();
|
||||||
|
let second = self.stack.pop().unwrap();
|
||||||
|
if first == second {
|
||||||
|
self.stack.push(Value::True)
|
||||||
|
} else {
|
||||||
|
self.stack.push(Value::False)
|
||||||
|
}
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Add => {
|
||||||
|
let first = self.stack.pop().unwrap();
|
||||||
|
let second = self.stack.pop().unwrap();
|
||||||
|
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||||
|
self.stack.push(Value::Number(x + y))
|
||||||
|
} else {
|
||||||
|
self.panic("`add` requires two numbers")
|
||||||
|
}
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Sub => {
|
||||||
|
let first = self.stack.pop().unwrap();
|
||||||
|
let second = self.stack.pop().unwrap();
|
||||||
|
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||||
|
self.stack.push(Value::Number(y - x))
|
||||||
|
} else {
|
||||||
|
self.panic("`sub` requires two numbers")
|
||||||
|
}
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Mult => {
|
||||||
|
let first = self.stack.pop().unwrap();
|
||||||
|
let second = self.stack.pop().unwrap();
|
||||||
|
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||||
|
self.stack.push(Value::Number(x * y))
|
||||||
|
} else {
|
||||||
|
self.panic("`mult` requires two numbers")
|
||||||
|
}
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Div => {
|
||||||
|
let first = self.stack.pop().unwrap();
|
||||||
|
let second = self.stack.pop().unwrap();
|
||||||
|
if let (Value::Number(x), Value::Number(y)) = (first, second) {
|
||||||
|
if x == 0.0 {
|
||||||
|
self.panic("division by 0")
|
||||||
|
}
|
||||||
|
self.stack.push(Value::Number(y / x))
|
||||||
|
} else {
|
||||||
|
self.panic("`div` requires two numbers")
|
||||||
|
}
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Unbox => {
|
||||||
|
let the_box = self.stack.pop().unwrap();
|
||||||
|
let inner = if let Value::Box(b) = the_box {
|
||||||
|
b.borrow().clone()
|
||||||
|
} else {
|
||||||
|
return self.panic("`unbox` requires a box");
|
||||||
|
};
|
||||||
|
self.stack.push(inner);
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
BoxStore => {
|
||||||
|
let new_value = self.stack.pop().unwrap();
|
||||||
|
let the_box = self.stack.pop().unwrap();
|
||||||
|
if let Value::Box(b) = the_box {
|
||||||
|
b.replace(new_value.clone());
|
||||||
|
} else {
|
||||||
|
return self.panic("`store` requires a box");
|
||||||
|
}
|
||||||
|
self.stack.push(new_value);
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Assert => {
|
||||||
|
let value = self.stack.last().unwrap();
|
||||||
|
if let Value::Nil | Value::False = value {
|
||||||
|
return self.panic("asserted falsy value");
|
||||||
|
}
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Get => {
|
||||||
|
let key = self.stack.pop().unwrap();
|
||||||
|
let dict = self.stack.pop().unwrap();
|
||||||
|
let value = match (key, dict) {
|
||||||
|
(Value::Keyword(k), Value::Dict(d)) => {
|
||||||
|
d.as_ref().get(&k).unwrap_or(&Value::Nil).clone()
|
||||||
|
}
|
||||||
|
(Value::Keyword(_), _) => Value::Nil,
|
||||||
|
_ => return self.panic("keys must be keywords"),
|
||||||
|
};
|
||||||
|
self.stack.push(value);
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
At => {
|
||||||
|
let idx = self.stack.pop().unwrap();
|
||||||
|
let ordered = self.stack.pop().unwrap();
|
||||||
|
let value = match (ordered, idx) {
|
||||||
|
(Value::List(l), Value::Number(i)) => {
|
||||||
|
l.get(i as usize).unwrap_or(&Value::Nil).clone()
|
||||||
|
}
|
||||||
|
(Value::Tuple(t), Value::Number(i)) => {
|
||||||
|
t.get(i as usize).unwrap_or(&Value::Nil).clone()
|
||||||
|
}
|
||||||
|
(_, Value::Number(_)) => Value::Nil,
|
||||||
|
_ => return self.panic("indexes must be numbers"),
|
||||||
|
};
|
||||||
|
self.stack.push(value);
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user