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_traits::FromPrimitive;
|
||||
use std::cell::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
|
@ -51,6 +52,17 @@ pub enum Op {
|
|||
Decrement,
|
||||
Truncate,
|
||||
MatchDepth,
|
||||
|
||||
Eq,
|
||||
Add,
|
||||
Sub,
|
||||
Mult,
|
||||
Div,
|
||||
Unbox,
|
||||
BoxStore,
|
||||
Assert,
|
||||
Get,
|
||||
At,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Op {
|
||||
|
@ -99,6 +111,17 @@ impl std::fmt::Display for Op {
|
|||
Truncate => "truncate",
|
||||
Duplicate => "duplicate",
|
||||
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}")
|
||||
}
|
||||
|
@ -126,7 +149,8 @@ impl Chunk {
|
|||
match op {
|
||||
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||
| 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}")
|
||||
}
|
||||
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)]
|
||||
pub struct Compiler {
|
||||
pub chunk: Chunk,
|
||||
|
@ -612,7 +653,6 @@ impl Compiler {
|
|||
LBox(name, expr) => {
|
||||
self.visit(expr);
|
||||
self.emit_op(Op::PushBox);
|
||||
self.stack_depth += 1;
|
||||
self.bind(name);
|
||||
}
|
||||
Dict(pairs) => {
|
||||
|
@ -649,15 +689,24 @@ impl Compiler {
|
|||
self.visit(first);
|
||||
self.visit(second);
|
||||
self.emit_op(Op::GetKey);
|
||||
self.stack_depth -= 1;
|
||||
}
|
||||
(Keyword(_), Arguments(args)) => {
|
||||
self.visit(&args[0]);
|
||||
self.visit(first);
|
||||
self.emit_op(Op::GetKey);
|
||||
self.stack_depth -= 1;
|
||||
}
|
||||
(Word(_), Arguments(_)) => {
|
||||
todo!()
|
||||
}
|
||||
(Word(fn_name), Arguments(args)) => match get_builtin(fn_name, args.len()) {
|
||||
Some(code) => {
|
||||
for arg in args {
|
||||
self.visit(arg);
|
||||
}
|
||||
self.emit_op(code);
|
||||
self.stack_depth -= args.len() - 1;
|
||||
}
|
||||
None => todo!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// TODO: implement longer synthetic expressions
|
||||
|
@ -941,7 +990,7 @@ impl Compiler {
|
|||
Noop | Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue
|
||||
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
||||
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple
|
||||
| LoadList => {
|
||||
| LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At => {
|
||||
println!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -74,16 +74,8 @@ pub fn run(src: &'static str) {
|
|||
pub fn main() {
|
||||
env::set_var("RUST_BACKTRACE", "1");
|
||||
let src = "
|
||||
loop (true) with {
|
||||
(false) -> :done
|
||||
(true) -> recur (42)
|
||||
(42) -> recur (:thingie)
|
||||
(:thingie) -> {
|
||||
let x = false
|
||||
12
|
||||
recur (x)
|
||||
}
|
||||
}
|
||||
let foo = #{:a 1, :b 2}
|
||||
:a (foo)
|
||||
";
|
||||
run(src);
|
||||
}
|
||||
|
|
111
src/vm.rs
111
src/vm.rs
|
@ -419,6 +419,117 @@ impl<'a> Vm<'a> {
|
|||
self.interpret()
|
||||
}
|
||||
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