@@ 0,0 1,242 @@
+use crate::{Lisp, Symbol};
+use std::{collections::HashMap, default::Default};
+
+pub struct Runtime<F> {
+ pub builtins: HashMap<Symbol, F, TheSymbolHasher>,
+}
+
+impl<F> Runtime<F>
+where
+ F: FnMut(&[Lisp]) -> Lisp,
+{
+ pub fn eval(&mut self, locals: &mut Locals, expr: &Lisp) -> Lisp {
+ match expr {
+ Lisp::Int(_) | Lisp::Float(_) | Lisp::String(_) => expr.clone(),
+ Lisp::Symbol(x) => match locals.get(x) {
+ Some(y) => y.clone(),
+ None => expr.clone(),
+ },
+ Lisp::List(xs) => {
+ let xs = xs.iter().map(|x| self.eval(locals, x)).collect::<Vec<_>>();
+ if let Some((f, args)) = xs.split_first() {
+ crate::match_expr!(f, {
+ ('fun, l(params), body) => {
+ locals.push_all(params.iter().zip(args).filter_map(|(p, a)| p.as_symbol().map(|p| (p, a.clone()))));
+ let y = self.eval(locals, body);
+ locals.pop_all(params.iter().filter_map(|p| p.as_symbol()));
+ y
+ },
+ sy(f) => {
+ if let Some(f) = self.builtins.get_mut(f) {
+ f(args)
+ } else {
+ Lisp::List(xs)
+ }
+ },
+ _ => Lisp::List(xs),
+ })
+ } else {
+ Lisp::List(xs)
+ }
+ }
+ }
+ }
+}
+
+impl Default for Runtime<fn(&[Lisp]) -> Lisp> {
+ fn default() -> Self {
+ Runtime {
+ builtins: [
+ ("+", builtins::add as _),
+ ("*", builtins::mul as _),
+ ("-", builtins::sub as _),
+ ("idiv", builtins::idiv as _),
+ ("fdiv", builtins::fdiv as _),
+ ("sin", builtins::sin as _),
+ ]
+ .into_iter()
+ .map(|(s, f)| (s.try_into().unwrap(), f))
+ .collect(),
+ }
+ }
+}
+
+pub mod builtins {
+ use crate::*;
+
+ pub fn add(xs: &[Lisp]) -> Lisp {
+ xs.iter().fold(Lisp::Int(0), |acc, x| match (acc, x) {
+ (Lisp::Int(acc), Lisp::Int(x)) => Lisp::Int(acc + x),
+ (Lisp::Float(acc), Lisp::Int(x)) => Lisp::Float(acc + *x as f64),
+ (Lisp::Int(acc), Lisp::Float(x)) => Lisp::Float(acc as f64 + x),
+ (Lisp::Float(acc), Lisp::Float(x)) => Lisp::Float(acc + x),
+ (acc, _) => acc,
+ })
+ }
+
+ pub fn mul(xs: &[Lisp]) -> Lisp {
+ xs.iter().fold(Lisp::Int(1), |acc, x| match (acc, x) {
+ (Lisp::Int(acc), Lisp::Int(x)) => Lisp::Int(acc * x),
+ (Lisp::Float(acc), Lisp::Int(x)) => Lisp::Float(acc * *x as f64),
+ (Lisp::Int(acc), Lisp::Float(x)) => Lisp::Float(acc as f64 * x),
+ (Lisp::Float(acc), Lisp::Float(x)) => Lisp::Float(acc * x),
+ (acc, _) => acc,
+ })
+ }
+
+ pub fn sub(xs: &[Lisp]) -> Lisp {
+ match xs.split_first() {
+ None => Lisp::Int(0),
+ Some((Lisp::Int(x), [])) => Lisp::Int(-x),
+ Some((Lisp::Float(x), [])) => Lisp::Float(-x),
+ Some((init, xs)) => xs.iter().fold(init.clone(), |acc, x| match (acc, x) {
+ (Lisp::Int(acc), Lisp::Int(x)) => Lisp::Int(acc - x),
+ (Lisp::Float(acc), Lisp::Int(x)) => Lisp::Float(acc - *x as f64),
+ (Lisp::Int(acc), Lisp::Float(x)) => Lisp::Float(acc as f64 - x),
+ (Lisp::Float(acc), Lisp::Float(x)) => Lisp::Float(acc - x),
+ (acc, _) => acc,
+ }),
+ }
+ }
+
+ pub fn idiv(xs: &[Lisp]) -> Lisp {
+ match xs.split_first() {
+ None => Lisp::Int(1),
+ Some((_, [])) => Lisp::Int(0),
+ Some((init, xs)) => xs.iter().fold(init.clone(), |acc, x| match (acc, x) {
+ (Lisp::Int(acc), Lisp::Int(x)) => Lisp::Int(acc / x),
+ (Lisp::Float(acc), Lisp::Int(x)) => Lisp::Int(acc as i64 / x),
+ (Lisp::Int(acc), Lisp::Float(x)) => Lisp::Int(acc - *x as i64),
+ (Lisp::Float(acc), Lisp::Float(x)) => Lisp::Int(acc as i64 - *x as i64),
+ (acc, _) => acc,
+ }),
+ }
+ }
+
+ pub fn fdiv(xs: &[Lisp]) -> Lisp {
+ match xs.split_first() {
+ None => Lisp::Int(1),
+ Some((Lisp::Int(x), [])) => Lisp::Float(1.0 / *x as f64),
+ Some((Lisp::Float(x), [])) => Lisp::Float(1.0 / x),
+ Some((init, xs)) => xs.iter().fold(init.clone(), |acc, x| match (acc, x) {
+ (Lisp::Int(acc), Lisp::Int(x)) =>
+ if acc % x == 0 {
+ Lisp::Int(acc / x)
+ } else {
+ Lisp::Float(acc as f64 / *x as f64)
+ },
+ (Lisp::Float(acc), Lisp::Int(x)) => Lisp::Float(acc / *x as f64),
+ (Lisp::Int(acc), Lisp::Float(x)) => Lisp::Float(acc as f64 / x),
+ (Lisp::Float(acc), Lisp::Float(x)) => Lisp::Float(acc / x),
+ (acc, _) => acc,
+ }),
+ }
+ }
+
+ pub fn sin(xs: &[Lisp]) -> Lisp {
+ match xs.first() {
+ Some(Lisp::Int(x)) => Lisp::Float((*x as f64).sin()),
+ Some(Lisp::Float(x)) => Lisp::Float(x.sin()),
+ _ => Lisp::Int(0),
+ }
+ }
+}
+
+pub struct Locals(HashMap<Symbol, Vec<Lisp>, TheSymbolHasher>);
+
+impl Locals {
+ pub fn new() -> Self {
+ Locals(HashMap::default())
+ }
+
+ pub fn get(&self, x: &Symbol) -> Option<&Lisp> {
+ self.0.get(x).and_then(|ys| ys.last())
+ }
+
+ pub fn push_all<I, S>(&mut self, defs: I)
+ where
+ S: TryInto<Symbol>,
+ <S as TryInto<Symbol>>::Error: std::fmt::Debug,
+ I: IntoIterator<Item = (S, Lisp)>,
+ {
+ for (lhs, rhs) in defs {
+ self.0.entry(lhs.try_into().unwrap()).or_insert(vec![]).push(rhs)
+ }
+ }
+
+ pub fn pop_all<I>(&mut self, vars: I)
+ where
+ I: IntoIterator<Item = Symbol>,
+ {
+ for var in vars {
+ self.0.get_mut(&var).unwrap().pop();
+ }
+ }
+}
+
+pub struct SymbolHasher(u128);
+pub struct TheSymbolHasher;
+
+impl std::hash::Hasher for SymbolHasher {
+ fn finish(&self) -> u64 {
+ (self.0 % (1 << 64)) as u64
+ }
+
+ fn write(&mut self, _bytes: &[u8]) {
+ panic!("Expected `write_u128` to be called, but `write` was called instead. `SymbolHasher` should only be used for `Symbol` keys.")
+ }
+
+ fn write_u128(&mut self, i: u128) {
+ self.0 = i;
+ }
+}
+
+impl std::hash::BuildHasher for TheSymbolHasher {
+ type Hasher = SymbolHasher;
+
+ fn build_hasher(&self) -> Self::Hasher {
+ SymbolHasher(0)
+ }
+}
+
+impl Default for TheSymbolHasher {
+ fn default() -> Self {
+ TheSymbolHasher
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn eval_expr() {
+ let mut rt = Runtime::default();
+
+ assert_eq!(rt.eval(&mut Locals::new(), &Lisp::Int(123)), Lisp::Int(123));
+
+ assert!(crate::match_expr!(rt.eval(&mut Locals::new(), &crate::parse(b"foo").unwrap()), {
+ 'foo => true,
+ _ => false
+ }));
+
+ assert!({
+ let mut env = Locals::new();
+ env.push_all([("foo", Lisp::Int(55)), ("bar", Lisp::Int(66))]);
+ crate::match_expr!(rt.eval(&mut env, &crate::parse(b"(foo bar)").unwrap()), {
+ ('foo, 'bar) => false,
+ (55, 66) => true,
+ _ => false
+ })
+ });
+
+ assert!({
+ let mut env = Locals::new();
+ env.push_all([("x", Lisp::Int(11)), ("y", Lisp::Int(22)), ("z", Lisp::Int(33))]);
+ crate::match_expr!(rt.eval(&mut env, &crate::parse(b"(+ x y z)").unwrap()), {
+ 66 => true,
+ _ => false
+ })
+ });
+ }
+}
@@ 1,5 1,9 @@
+#![allow(clippy::new_without_default)]
+
+mod eval;
mod matching;
+pub use eval::*;
use std::{
borrow::Cow,
cmp::{max, min},
@@ 19,7 23,7 @@ pub enum Lisp {
String(String),
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Symbol(u128);
pub fn parse(src: &[u8]) -> Result<Lisp, String> {
@@ 213,6 217,13 @@ impl Symbol {
}
}
+impl TryFrom<&str> for Symbol {
+ type Error = String;
+ fn try_from(s: &str) -> Result<Symbol, String> {
+ Symbol::pack_str(s).ok_or_else(|| format!("could not pack {s:?} into a symbol"))
+ }
+}
+
impl Lisp {
pub fn as_list(&self) -> Option<&[Lisp]> {
match self {
@@ 228,12 239,26 @@ impl Lisp {
}
}
+ pub fn as_float(&self) -> Option<f64> {
+ match *self {
+ Lisp::Float(x) => Some(x),
+ _ => None,
+ }
+ }
+
pub fn as_string(&self) -> Option<&str> {
match self {
Lisp::String(s) => Some(s),
_ => None,
}
}
+
+ pub fn as_symbol(&self) -> Option<Symbol> {
+ match *self {
+ Lisp::Symbol(x) => Some(x),
+ _ => None,
+ }
+ }
}
impl From<i64> for Lisp {