add builtins, fix basic synthetic expressions

This commit is contained in:
Scott Richmond 2025-05-27 14:15:12 -04:00
parent 74cc0025d6
commit 182b14e5f4
3 changed files with 168 additions and 16 deletions

View File

@ -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 => {

View File

@ -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
View File

@ -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;
}
} }
} }
} }