refactor!: move specials out of env
This is a fucked up commit. If you see this, you have fallen too deep into the rabbit hole. Come say hi!
This commit is contained in:
parent
d8724eb4b7
commit
e54d095c5f
11 changed files with 730 additions and 600 deletions
402
mute-interpreter/src/env/core.rs
vendored
402
mute-interpreter/src/env/core.rs
vendored
|
@ -1,96 +1,16 @@
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::evaluator::{eval, eval_node};
|
use super::{NativeFunc, Value};
|
||||||
use crate::macros::arg_count;
|
use crate::Node;
|
||||||
use crate::{Error, Node};
|
|
||||||
|
|
||||||
pub(super) fn core() -> HashMap<String, Node> {
|
pub(super) fn core() -> HashMap<String, Value> {
|
||||||
[
|
[
|
||||||
// Meta
|
|
||||||
(
|
|
||||||
"eval",
|
|
||||||
Node::NativeFunc(|env, args| {
|
|
||||||
arg_count!(1, args.len());
|
|
||||||
let ast = args.into_iter().next().unwrap();
|
|
||||||
eval_node(env, ast)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"apply",
|
|
||||||
Node::NativeFunc(|env, args| {
|
|
||||||
arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
let operator = args.next().unwrap();
|
|
||||||
let args = match args.next().unwrap() {
|
|
||||||
Node::List(list) => list,
|
|
||||||
_ => Err(Error::ExpectedList)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut list = vec![operator];
|
|
||||||
list.extend(args);
|
|
||||||
|
|
||||||
eval_node(env, Node::List(list))
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"quote",
|
|
||||||
Node::Special(|_env, args| {
|
|
||||||
arg_count!(1, args.len());
|
|
||||||
let val = args.into_iter().next().unwrap();
|
|
||||||
Ok(val)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"quasiquote",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
arg_count!(1, args.len());
|
|
||||||
|
|
||||||
let node = args.into_iter().next().unwrap();
|
|
||||||
|
|
||||||
// If we have a list as our object, then we need to check for any unquote expressions.
|
|
||||||
// To do this, we iterate over each value in the list. If we detect that one
|
|
||||||
// of those values is a list, then we check if the first value in the list is an
|
|
||||||
// unquote symbol. If it is, then we evaluate the unqote list
|
|
||||||
match node {
|
|
||||||
Node::List(list) => {
|
|
||||||
let inner: Vec<Node> = list
|
|
||||||
.into_iter()
|
|
||||||
.map(|node| match node {
|
|
||||||
Node::List(ref list) => match list.first() {
|
|
||||||
Some(Node::Symbol(symbol)) if symbol == "unquote" => {
|
|
||||||
eval_node(env, node)
|
|
||||||
}
|
|
||||||
Some(_) => Ok(node),
|
|
||||||
None => Ok(Node::Nil),
|
|
||||||
},
|
|
||||||
_ => Ok(node),
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<Node>, _>>()?;
|
|
||||||
|
|
||||||
Ok(Node::List(inner))
|
|
||||||
}
|
|
||||||
|
|
||||||
node => Ok(node),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"unquote",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
arg_count!(1, args.len());
|
|
||||||
|
|
||||||
let node = args.into_iter().next().unwrap();
|
|
||||||
eval_node(env, node)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
// Arithmetic operations
|
// Arithmetic operations
|
||||||
(
|
(
|
||||||
"+",
|
"+",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
let res = args
|
args.into_iter()
|
||||||
.into_iter()
|
|
||||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs + rhs),
|
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs + rhs),
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs + rhs),
|
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs + rhs),
|
||||||
|
@ -98,15 +18,13 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Float(lhs), Node::Int(rhs)) => Node::Float(lhs + rhs as f64),
|
(Node::Float(lhs), Node::Int(rhs)) => Node::Float(lhs + rhs as f64),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::Int(0));
|
.unwrap_or(Node::Int(0))
|
||||||
Ok(res)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"-",
|
"-",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
let res = args
|
args.into_iter()
|
||||||
.into_iter()
|
|
||||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs - rhs),
|
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs - rhs),
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs - rhs),
|
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs - rhs),
|
||||||
|
@ -114,16 +32,13 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 - rhs),
|
(Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 - rhs),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::Int(0));
|
.unwrap_or(Node::Int(0))
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"*",
|
"*",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
let res = args
|
args.into_iter()
|
||||||
.into_iter()
|
|
||||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs * rhs),
|
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs * rhs),
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs * rhs),
|
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs * rhs),
|
||||||
|
@ -131,16 +46,13 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 * rhs),
|
(Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 * rhs),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::Int(0));
|
.unwrap_or(Node::Int(0))
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"/",
|
"/",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
let res = args
|
args.into_iter()
|
||||||
.into_iter()
|
|
||||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs / rhs),
|
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(lhs / rhs),
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs / rhs),
|
(Node::Float(lhs), Node::Float(rhs)) => Node::Float(lhs / rhs),
|
||||||
|
@ -148,45 +60,43 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 / rhs),
|
(Node::Int(lhs), Node::Float(rhs)) => Node::Float(lhs as f64 / rhs),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::Int(0));
|
.unwrap_or(Node::Int(0))
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
// Collections
|
// Collections
|
||||||
("list", Node::NativeFunc(|_env, args| Ok(Node::List(args)))),
|
("list", NativeFunc(|args| Node::List(args))),
|
||||||
(
|
(
|
||||||
"list?",
|
"list?",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
if let Node::List(_list) = args[0].borrow() {
|
if let Node::List(_list) = args[0].borrow() {
|
||||||
return Ok(Node::Boolean(true));
|
return Node::Boolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Node::Boolean(false))
|
Node::Boolean(false)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
("vector", NativeFunc(|args| Node::Vector(args))),
|
||||||
"vector",
|
|
||||||
Node::NativeFunc(|_env, args| Ok(Node::Vector(args))),
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"vector?",
|
"vector?",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
if let Node::Vector(_vec) = args[0].borrow() {
|
if let Node::Vector(_vec) = args[0].borrow() {
|
||||||
return Ok(Node::Boolean(true));
|
return Node::Boolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Node::Boolean(false))
|
Node::Boolean(false)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"hashmap",
|
"hashmap",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(modulo: 2, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(modulo: 2, args.len());
|
||||||
|
|
||||||
let mut index = -1;
|
let mut index = -1;
|
||||||
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
|
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
|
||||||
|
@ -202,82 +112,88 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
.zip(values)
|
.zip(values)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Node::Map(res))
|
Node::Map(res)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"hashmap?",
|
"hashmap?",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
if let Node::Map(_map) = args[0].borrow() {
|
if let Node::Map(_map) = args[0].borrow() {
|
||||||
return Ok(Node::Boolean(true));
|
return Node::Boolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Node::Boolean(false))
|
Node::Boolean(false)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"empty?",
|
"empty?",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
match args[0].borrow() {
|
match args[0].borrow() {
|
||||||
Node::List(list) => Ok(Node::Boolean(list.is_empty())),
|
Node::List(list) => Node::Boolean(list.is_empty()),
|
||||||
Node::Vector(vec) => Ok(Node::Boolean(vec.is_empty())),
|
Node::Vector(vec) => Node::Boolean(vec.is_empty()),
|
||||||
Node::Map(map) => Ok(Node::Boolean(map.is_empty())),
|
Node::Map(map) => Node::Boolean(map.is_empty()),
|
||||||
_ => Err(Error::ExpectedCollection)?,
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"count",
|
"count",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
match args[0].borrow() {
|
match args[0].borrow() {
|
||||||
Node::List(list) => Ok(Node::Int(list.len() as i64)),
|
Node::List(list) => Node::Int(list.len() as i64),
|
||||||
Node::Vector(vec) => Ok(Node::Int(vec.len() as i64)),
|
Node::Vector(vec) => Node::Int(vec.len() as i64),
|
||||||
Node::Map(map) => Ok(Node::Int(map.len() as i64)),
|
Node::Map(map) => Node::Int(map.len() as i64),
|
||||||
_ => Err(Error::ExpectedCollection)?,
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
// Ordering
|
// Ordering
|
||||||
(
|
(
|
||||||
"eq?",
|
"eq?",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(2, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(2, args.len());
|
||||||
|
|
||||||
let lhs = args[0].borrow();
|
let lhs = args[0].borrow();
|
||||||
let rhs = args[1].borrow();
|
let rhs = args[1].borrow();
|
||||||
|
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
(Node::Int(lhs), Node::Int(rhs)) => Ok(Node::Boolean(lhs == rhs)),
|
(Node::Int(lhs), Node::Int(rhs)) => Node::Boolean(lhs == rhs),
|
||||||
(Node::Boolean(lhs), Node::Boolean(rhs)) => Ok(Node::Boolean(lhs == rhs)),
|
(Node::Boolean(lhs), Node::Boolean(rhs)) => Node::Boolean(lhs == rhs),
|
||||||
(Node::String(lhs), Node::String(rhs)) => Ok(Node::Boolean(lhs == rhs)),
|
(Node::String(lhs), Node::String(rhs)) => Node::Boolean(lhs == rhs),
|
||||||
(Node::Nil, Node::Nil) => Ok(Node::Boolean(true)),
|
(Node::Nil, Node::Nil) => Node::Boolean(true),
|
||||||
_ => Err(Error::ExpectedNumber)?,
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"not",
|
"not",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
let expr = args[0].borrow();
|
let expr = args[0].borrow();
|
||||||
if let Node::Boolean(false) = expr {
|
if let Node::Boolean(false) = expr {
|
||||||
return Ok(Node::Boolean(true));
|
return Node::Boolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Node::Boolean(false))
|
Node::Boolean(false)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"<",
|
"<",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(2, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(2, args.len());
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -288,16 +204,17 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => lhs < rhs,
|
(Node::Float(lhs), Node::Float(rhs)) => lhs < rhs,
|
||||||
(Node::Float(lhs), Node::Int(rhs)) => lhs < rhs as f64,
|
(Node::Float(lhs), Node::Int(rhs)) => lhs < rhs as f64,
|
||||||
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) < rhs,
|
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) < rhs,
|
||||||
_ => Err(Error::ExpectedNumber)?,
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Node::Boolean(less_than))
|
Node::Boolean(less_than)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
">",
|
">",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(2, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(2, args.len());
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -308,16 +225,17 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => lhs > rhs,
|
(Node::Float(lhs), Node::Float(rhs)) => lhs > rhs,
|
||||||
(Node::Float(lhs), Node::Int(rhs)) => lhs > rhs as f64,
|
(Node::Float(lhs), Node::Int(rhs)) => lhs > rhs as f64,
|
||||||
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) > rhs,
|
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) > rhs,
|
||||||
_ => Err(Error::ExpectedNumber)?,
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Node::Boolean(greater_than))
|
Node::Boolean(greater_than)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"<=",
|
"<=",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(2, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(2, args.len());
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -328,16 +246,17 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => lhs <= rhs,
|
(Node::Float(lhs), Node::Float(rhs)) => lhs <= rhs,
|
||||||
(Node::Float(lhs), Node::Int(rhs)) => lhs <= rhs as f64,
|
(Node::Float(lhs), Node::Int(rhs)) => lhs <= rhs as f64,
|
||||||
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) <= rhs,
|
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) <= rhs,
|
||||||
_ => Err(Error::ExpectedNumber)?,
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Node::Boolean(less_than_equal))
|
Node::Boolean(less_than_equal)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
">=",
|
">=",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(2, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(2, args.len());
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -348,170 +267,25 @@ pub(super) fn core() -> HashMap<String, Node> {
|
||||||
(Node::Float(lhs), Node::Float(rhs)) => lhs >= rhs,
|
(Node::Float(lhs), Node::Float(rhs)) => lhs >= rhs,
|
||||||
(Node::Float(lhs), Node::Int(rhs)) => lhs >= rhs as f64,
|
(Node::Float(lhs), Node::Int(rhs)) => lhs >= rhs as f64,
|
||||||
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) >= rhs,
|
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) >= rhs,
|
||||||
_ => Err(Error::ExpectedNumber)?,
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Node::Boolean(greater_than_equal))
|
Node::Boolean(greater_than_equal)
|
||||||
}),
|
|
||||||
),
|
|
||||||
// Branching
|
|
||||||
(
|
|
||||||
"if",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
let mut args = args;
|
|
||||||
if args.len() == 2 {
|
|
||||||
args.push(Node::Nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_count!(3, args.len());
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
|
|
||||||
let cond = args.next().unwrap();
|
|
||||||
let consequence = args.next().unwrap();
|
|
||||||
let alternative = args.next().unwrap();
|
|
||||||
|
|
||||||
let cond = eval_node(env, cond)?;
|
|
||||||
|
|
||||||
// If the value is anything other than false, then it is truthy
|
|
||||||
if let Node::Boolean(false) = cond {
|
|
||||||
let res = eval_node(env, alternative)?;
|
|
||||||
return Ok(res);
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = eval_node(env, consequence)?;
|
|
||||||
Ok(res)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
// Environment Manipulation
|
|
||||||
(
|
|
||||||
"define!",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let key = match args.next().unwrap() {
|
|
||||||
Node::Symbol(name) => name,
|
|
||||||
_ => Err(Error::ExpectedSymbol)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let val = eval_node(env, args.next().unwrap())?;
|
|
||||||
|
|
||||||
env.set(key, val.clone());
|
|
||||||
|
|
||||||
Ok(Node::Void)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"fn!",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
arg_count!(3, args.len());
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
|
|
||||||
let name = match args.next().unwrap() {
|
|
||||||
Node::Symbol(name) => name,
|
|
||||||
_ => Err(Error::ExpectedSymbol)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg_list = args.next().unwrap();
|
|
||||||
let body = args.next().unwrap();
|
|
||||||
|
|
||||||
let args: Vec<String> = match arg_list {
|
|
||||||
Node::List(args) => args
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| match v {
|
|
||||||
Node::Symbol(sym) => Ok(sym),
|
|
||||||
_ => Err(Error::ExpectedSymbol),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
_ => Err(Error::ExpectedList),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let func = Node::Function {
|
|
||||||
params: args,
|
|
||||||
env: env.clone(),
|
|
||||||
body: Box::new(body),
|
|
||||||
};
|
|
||||||
|
|
||||||
env.set(name.clone(), func.clone());
|
|
||||||
Ok(Node::Void)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"let*",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
let new_env = match args.next().unwrap() {
|
|
||||||
Node::List(list) if list.len() == 2 => {
|
|
||||||
let mut list = list;
|
|
||||||
let sym = match list.remove(0) {
|
|
||||||
Node::Symbol(name) => name,
|
|
||||||
_ => Err(Error::ExpectedSymbol)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let val = eval_node(env, list.remove(0))?;
|
|
||||||
env.wrap(vec![(sym, val)])
|
|
||||||
}
|
|
||||||
Node::List(list) => Err(Error::MismatchedArgCount(2, list.len()))?,
|
|
||||||
_ => Err(Error::ExpectedList)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
eval_node(&new_env, args.next().unwrap())
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"fn*",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
let mut args = args;
|
|
||||||
arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let arg_list = args.remove(0);
|
|
||||||
let body = args.remove(0);
|
|
||||||
|
|
||||||
let args: Vec<String> = match arg_list {
|
|
||||||
Node::List(args) => args
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| match v {
|
|
||||||
Node::Symbol(sym) => Ok(sym),
|
|
||||||
_ => Err(Error::ExpectedSymbol),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
_ => Err(Error::ExpectedList),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
Ok(Node::Function {
|
|
||||||
params: args,
|
|
||||||
env: env.clone(),
|
|
||||||
body: Box::new(body),
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
// Strings
|
// Strings
|
||||||
(
|
(
|
||||||
"str",
|
"str",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: Reimplement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
let val = args[0].borrow();
|
let val = args[0].borrow();
|
||||||
Ok(Node::String(val.to_string()))
|
Node::String(val.to_string())
|
||||||
}),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"do",
|
|
||||||
Node::Special(|env, args| {
|
|
||||||
let res = eval(env, args)?;
|
|
||||||
|
|
||||||
if let Some(res) = res.last() {
|
|
||||||
return Ok(res.to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Node::Nil)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k.to_string(), v))
|
.map(|(k, v)| (k.to_string(), Value::NativeFunc(v)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
51
mute-interpreter/src/env/io.rs
vendored
51
mute-interpreter/src/env/io.rs
vendored
|
@ -1,50 +1,61 @@
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::macros::arg_count;
|
use super::{NativeFunc, Value};
|
||||||
use crate::{Error, Node};
|
use crate::Node;
|
||||||
|
|
||||||
pub(super) fn io() -> HashMap<String, Node> {
|
pub(super) fn io() -> HashMap<String, Value> {
|
||||||
[
|
[
|
||||||
// Stdout
|
// Stdout
|
||||||
(
|
(
|
||||||
"print",
|
"print",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
print!(
|
||||||
|
"{}",
|
||||||
|
args.into_iter()
|
||||||
|
.map(|node| node.to_string())
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
);
|
||||||
|
|
||||||
print!("{}", args[0]);
|
Node::Void
|
||||||
|
|
||||||
Ok(Node::Void)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"println",
|
"println",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
println!(
|
||||||
|
"{}",
|
||||||
|
args.into_iter()
|
||||||
|
.map(|node| node.to_string())
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
);
|
||||||
|
|
||||||
println!("{}", args[0]);
|
Node::Void
|
||||||
|
|
||||||
Ok(Node::Void)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
// File IO
|
// File IO
|
||||||
(
|
(
|
||||||
"read-file",
|
"read-file",
|
||||||
Node::NativeFunc(|_env, args| {
|
NativeFunc(|args| {
|
||||||
arg_count!(1, args.len());
|
// TODO: implement this
|
||||||
|
// arg_count!(1, args.len());
|
||||||
|
|
||||||
let val = args[0].borrow();
|
let val = args[0].borrow();
|
||||||
if let Node::String(path) = val {
|
if let Node::String(path) = val {
|
||||||
let contents = std::fs::read_to_string(path)
|
let contents = std::fs::read_to_string(path).unwrap();
|
||||||
.map_err(|err| Error::SystemError(err.to_string()))?;
|
// FIXME Lmao, we need error handling
|
||||||
return Ok(Node::String(contents));
|
// .map_err(|err| Error::SystemError(err.to_string()))?;
|
||||||
|
return Node::String(contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::ExpectedString)?
|
// FIXME What if they don't pass a string???
|
||||||
|
Node::Void
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k.to_string(), v))
|
.map(|(k, v)| (k.to_string(), Value::NativeFunc(v)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
30
mute-interpreter/src/env/mod.rs
vendored
30
mute-interpreter/src/env/mod.rs
vendored
|
@ -7,17 +7,32 @@ use crate::Node;
|
||||||
mod core;
|
mod core;
|
||||||
mod io;
|
mod io;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NativeFunc(fn(args: Vec<Node>) -> Node);
|
||||||
|
|
||||||
|
impl NativeFunc {
|
||||||
|
pub fn call(&self, args: Vec<Node>) -> Node {
|
||||||
|
(self.0)(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Value {
|
||||||
|
Node(Node),
|
||||||
|
NativeFunc(NativeFunc),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Environment(Rc<RawEnvironment>);
|
pub struct Environment(Rc<RawEnvironment>);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RawEnvironment {
|
pub struct RawEnvironment {
|
||||||
current: RefCell<HashMap<String, Node>>,
|
current: RefCell<HashMap<String, Value>>,
|
||||||
outer: Option<Environment>,
|
outer: Option<Environment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
pub fn get(&self, ident: &str) -> Option<Node> {
|
pub fn get(&self, ident: &str) -> Option<Value> {
|
||||||
if let Some(val) = self.0.current.borrow().get(ident) {
|
if let Some(val) = self.0.current.borrow().get(ident) {
|
||||||
return Some(val.clone());
|
return Some(val.clone());
|
||||||
}
|
}
|
||||||
|
@ -27,15 +42,20 @@ impl Environment {
|
||||||
|
|
||||||
pub fn wrap(&self, records: Vec<(String, Node)>) -> Environment {
|
pub fn wrap(&self, records: Vec<(String, Node)>) -> Environment {
|
||||||
let env = RawEnvironment {
|
let env = RawEnvironment {
|
||||||
current: RefCell::new(records.into_iter().collect()),
|
current: RefCell::new(
|
||||||
|
records
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| (k, Value::Node(v)))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
outer: Some(self.clone()),
|
outer: Some(self.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Environment(Rc::new(env))
|
Environment(Rc::new(env))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&self, ident: String, val: Node) {
|
pub fn set(&self, ident: String, val: Node) {
|
||||||
self.0.current.borrow_mut().insert(ident, val);
|
self.0.current.borrow_mut().insert(ident, Value::Node(val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ pub enum Error {
|
||||||
ExpectedNumber,
|
ExpectedNumber,
|
||||||
#[error("expected string")]
|
#[error("expected string")]
|
||||||
ExpectedString,
|
ExpectedString,
|
||||||
|
#[error("expected boolean")]
|
||||||
|
ExpectedBoolean,
|
||||||
|
|
||||||
#[error("expected {0} arguments, got {1}")]
|
#[error("expected {0} arguments, got {1}")]
|
||||||
MismatchedArgCount(usize, usize),
|
MismatchedArgCount(usize, usize),
|
||||||
#[error("system error {0}")]
|
#[error("system error {0}")]
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
use crate::{Environment, Error, Node, Result};
|
use crate::{Environment, Error, Result};
|
||||||
|
use mute_parser::Node;
|
||||||
|
|
||||||
pub fn eval(env: &Environment, ast: Vec<Node>) -> Result<Vec<Node>> {
|
pub fn eval(env: &Environment, ast: Vec<Node>) -> Result<Vec<Node>> {
|
||||||
let mut exprs = Vec::new();
|
let mut exprs = Vec::new();
|
||||||
|
|
||||||
for node in ast {
|
for node in ast {
|
||||||
|
dbg!(&node);
|
||||||
let res = eval_node(env, node)?;
|
let res = eval_node(env, node)?;
|
||||||
if let Node::Void = res {
|
if let Node::Void = res {
|
||||||
continue;
|
continue;
|
||||||
|
@ -30,45 +32,130 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
let operator = eval_node(env, list.next().ok_or(Error::InvalidOperator)?)?;
|
let operator = eval_node(env, list.next().ok_or(Error::InvalidOperator)?)?;
|
||||||
|
|
||||||
match operator.borrow() {
|
match operator.borrow() {
|
||||||
Node::NativeFunc(func) => {
|
Node::Function { parameters, body } => {
|
||||||
let mut args = Vec::new();
|
|
||||||
for node in list {
|
|
||||||
args.push(eval_node(env, node)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
func(env, args)?
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Special(func) => {
|
|
||||||
let args = list.collect();
|
|
||||||
func(env, args)?
|
|
||||||
}
|
|
||||||
Node::Function {
|
|
||||||
params,
|
|
||||||
env: inner_env,
|
|
||||||
body,
|
|
||||||
} => {
|
|
||||||
let args = list;
|
let args = list;
|
||||||
if args.len() != params.len() {
|
if args.len() != parameters.len() {
|
||||||
Err(Error::MismatchedArgCount(params.len(), args.len()))?
|
Err(Error::MismatchedArgCount(parameters.len(), args.len()))?
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = args
|
let args = args
|
||||||
.map(|node| eval_node(env, node))
|
.map(|node| eval_node(env, node))
|
||||||
.collect::<Result<Vec<Node>>>()?;
|
.collect::<Result<Vec<Node>>>()?;
|
||||||
|
|
||||||
let records = params.iter().map(|k| k.to_owned()).zip(args).collect();
|
let records = parameters.iter().map(|k| k.to_owned()).zip(args).collect();
|
||||||
let env = inner_env.wrap(records);
|
let env = env.wrap(records);
|
||||||
|
|
||||||
eval_node(&env, *(*body).clone())?
|
let res = eval(&env, (*body).clone())?;
|
||||||
|
res.last()
|
||||||
|
.expect("all functions return something")
|
||||||
|
.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: This feels sooooooo wrong
|
||||||
|
Node::Symbol(sym) => {
|
||||||
|
let operator = env.get(&sym).ok_or_else(|| Error::NotInEnv(sym.clone()))?;
|
||||||
|
match operator {
|
||||||
|
crate::env::Value::NativeFunc(func) => {
|
||||||
|
let args = list
|
||||||
|
.map(|node| eval_node(env, node))
|
||||||
|
.collect::<Result<Vec<Node>>>()?;
|
||||||
|
|
||||||
|
func.call(args)
|
||||||
|
}
|
||||||
|
crate::env::Value::Node(node) => node,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Native functions???
|
||||||
_ => Err(Error::InvalidOperator)?,
|
_ => Err(Error::InvalidOperator)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv(sym))?.to_owned(),
|
Node::Define { args } => {
|
||||||
|
args.into_iter()
|
||||||
|
.map(|(k, v)| Ok((k.to_owned(), eval_node(env, v)?)))
|
||||||
|
.collect::<Result<Vec<(String, Node)>>>()?
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|(k, v)| env.set(k, v));
|
||||||
|
|
||||||
val => val,
|
Node::Void
|
||||||
|
}
|
||||||
|
Node::Let { args, body } => {
|
||||||
|
let args = args
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| Ok((k.to_owned(), eval_node(env, v)?)))
|
||||||
|
.collect::<Result<Vec<(String, Node)>>>()?;
|
||||||
|
|
||||||
|
let env = env.wrap(args);
|
||||||
|
eval(&env, body)?.last().unwrap().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::If {
|
||||||
|
cond,
|
||||||
|
consequence,
|
||||||
|
alternative,
|
||||||
|
} => {
|
||||||
|
let cond = eval_node(env, *cond)?;
|
||||||
|
|
||||||
|
match cond {
|
||||||
|
Node::Boolean(true) => eval_node(env, *consequence)?,
|
||||||
|
Node::Boolean(false) => eval_node(env, *alternative)?,
|
||||||
|
_ => Err(Error::ExpectedBoolean)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::Quote(node) => *node,
|
||||||
|
Node::Quasiquote(node) => {
|
||||||
|
fn unquote(env: &Environment, node: Node) -> Result<Node> {
|
||||||
|
match node {
|
||||||
|
Node::List(list) => {
|
||||||
|
let list = list
|
||||||
|
.into_iter()
|
||||||
|
.map(|node| unquote(env, node))
|
||||||
|
.collect::<Result<Vec<Node>>>()?;
|
||||||
|
Ok(Node::List(list))
|
||||||
|
}
|
||||||
|
Node::Unquote(_) => eval_node(env, node),
|
||||||
|
node => Ok(node),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unquote(env, *node)?
|
||||||
|
}
|
||||||
|
Node::Unquote(node) => eval_node(env, *node)?,
|
||||||
|
|
||||||
|
Node::Do { body } => eval(env, body)?.last().unwrap().to_owned(),
|
||||||
|
Node::Eval { body } => eval_node(env, eval_node(env, *body)?)?,
|
||||||
|
Node::Apply { func, args } => {
|
||||||
|
let mut list = vec![*func];
|
||||||
|
match eval_node(env, *args)? {
|
||||||
|
Node::List(args) => list.extend(args),
|
||||||
|
_ => Err(Error::ExpectedList)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_node(env, Node::List(list))?
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::Symbol(sym) => match env
|
||||||
|
.get(&sym)
|
||||||
|
.ok_or_else(|| Error::NotInEnv(sym.clone()))?
|
||||||
|
.to_owned()
|
||||||
|
{
|
||||||
|
crate::env::Value::Node(node) => node,
|
||||||
|
// HACK: This feels so wrong
|
||||||
|
crate::env::Value::NativeFunc(_) => Node::Symbol(sym),
|
||||||
|
},
|
||||||
|
|
||||||
|
Node::Keyword(_)
|
||||||
|
| Node::Int(_)
|
||||||
|
| Node::Float(_)
|
||||||
|
| Node::String(_)
|
||||||
|
| Node::Boolean(_)
|
||||||
|
| Node::Nil
|
||||||
|
| Node::Void
|
||||||
|
| Node::Vector(_)
|
||||||
|
| Node::Map(_)
|
||||||
|
| Node::Function { .. } => ast_node,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
|
@ -148,23 +235,20 @@ mod test {
|
||||||
#[case("(count {})", "0")]
|
#[case("(count {})", "0")]
|
||||||
#[case("(count {:a 1})", "1")]
|
#[case("(count {:a 1})", "1")]
|
||||||
// Environment manipulation
|
// Environment manipulation
|
||||||
#[case("(define! asdf (+ 2 2)) (+ asdf 2)", "6")]
|
#[case("(define (asdf (+ 2 2))) (+ asdf 2)", "6")]
|
||||||
#[case("(fn! add (a b) (+ a b)) (add 1 2)", "3")]
|
#[case("(define (add (fn* (a b) (+ a b)))) (add 1 2)", "3")]
|
||||||
#[case("(let* (a 2) (+ a 2))", "4")]
|
#[case("(let* ((a 2)) (+ a 2))", "4")]
|
||||||
// Branching
|
// Branching
|
||||||
#[case("(if true true false)", "true")]
|
#[case("(if true true false)", "true")]
|
||||||
#[case("(if nil true false)", "true")]
|
|
||||||
#[case("(if false true false)", "false")]
|
#[case("(if false true false)", "false")]
|
||||||
#[case("(if 4 true false)", "true")]
|
|
||||||
#[case("(if \"blue\" true false)", "true")]
|
|
||||||
#[case("(if true 3)", "3")]
|
#[case("(if true 3)", "3")]
|
||||||
#[case("(if false 3)", "()")]
|
#[case("(if false 3)", "()")]
|
||||||
// Functions
|
// Functions
|
||||||
#[case(
|
#[case(
|
||||||
"(fn! factorial (n) (if
|
"(define (factorial (fn* (n) (if
|
||||||
(<= n 1)
|
(<= n 1)
|
||||||
1
|
1
|
||||||
(* n (factorial (- n 1)))))
|
(* n (factorial (- n 1)))))))
|
||||||
(factorial 5)",
|
(factorial 5)",
|
||||||
"120"
|
"120"
|
||||||
)]
|
)]
|
||||||
|
@ -210,16 +294,4 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(res, expected);
|
assert_eq!(res, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case("{:a}")]
|
|
||||||
#[case("(not-a-func :uwu)")]
|
|
||||||
#[case("{:a}")]
|
|
||||||
fn test_evaluator_fail(#[case] input: &str) {
|
|
||||||
let env = Environment::default();
|
|
||||||
let ast = parse_str(input).unwrap();
|
|
||||||
let res = crate::eval(&env, ast);
|
|
||||||
|
|
||||||
assert!(res.is_err())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,14 @@ mod env;
|
||||||
mod error;
|
mod error;
|
||||||
mod evaluator;
|
mod evaluator;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod node;
|
|
||||||
|
|
||||||
pub use env::Environment;
|
pub use env::Environment;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
pub use node::Node;
|
|
||||||
|
|
||||||
pub fn eval(env: &Environment, ast: Vec<mute_parser::Node>) -> Result<Vec<Node>> {
|
// Crate Imports
|
||||||
|
use mute_parser::Node;
|
||||||
|
|
||||||
|
pub fn eval(env: &Environment, ast: Vec<Node>) -> Result<Vec<Node>> {
|
||||||
let ast = ast.into_iter().map(Into::into).collect();
|
let ast = ast.into_iter().map(Into::into).collect();
|
||||||
evaluator::eval(env, ast)
|
evaluator::eval(env, ast)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{Environment, Result};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Node {
|
|
||||||
List(Vec<Node>),
|
|
||||||
|
|
||||||
Symbol(String),
|
|
||||||
Keyword(String),
|
|
||||||
Int(i64),
|
|
||||||
Float(f64),
|
|
||||||
String(String),
|
|
||||||
Boolean(bool),
|
|
||||||
Nil,
|
|
||||||
|
|
||||||
Vector(Vec<Node>),
|
|
||||||
Map(HashMap<String, Node>),
|
|
||||||
Function {
|
|
||||||
params: Vec<String>,
|
|
||||||
env: Environment,
|
|
||||||
body: Box<Node>,
|
|
||||||
},
|
|
||||||
NativeFunc(fn(env: &Environment, args: Vec<Node>) -> Result<Node>),
|
|
||||||
Special(fn(env: &Environment, args: Vec<Node>) -> Result<Node>),
|
|
||||||
|
|
||||||
Void,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mute_parser::Node> for Node {
|
|
||||||
fn from(value: mute_parser::Node) -> Self {
|
|
||||||
match value {
|
|
||||||
mute_parser::Node::List(list) => Node::List(list.into_iter().map(Into::into).collect()),
|
|
||||||
mute_parser::Node::Symbol(symbol) => Node::Symbol(symbol),
|
|
||||||
mute_parser::Node::Keyword(keyword) => Node::Keyword(keyword),
|
|
||||||
mute_parser::Node::Int(int) => Node::Int(int),
|
|
||||||
mute_parser::Node::Float(float) => Node::Float(float),
|
|
||||||
mute_parser::Node::String(string) => Node::String(string),
|
|
||||||
mute_parser::Node::Boolean(boolean) => Node::Boolean(boolean),
|
|
||||||
mute_parser::Node::Nil => Node::Nil,
|
|
||||||
mute_parser::Node::Vector(vector) => {
|
|
||||||
Node::Vector(vector.into_iter().map(Into::into).collect())
|
|
||||||
}
|
|
||||||
mute_parser::Node::Map(map) => {
|
|
||||||
Node::Map(map.into_iter().map(|(k, v)| (k, v.into())).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Node {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Node::List(list) => {
|
|
||||||
let s = list
|
|
||||||
.iter()
|
|
||||||
.map(|elem| elem.to_string())
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
write!(f, "({s})")
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Int(val) => write!(f, "{}", val),
|
|
||||||
Node::Float(val) => write!(f, "{}", val),
|
|
||||||
Node::Boolean(true) => write!(f, "true"),
|
|
||||||
Node::Boolean(false) => write!(f, "false"),
|
|
||||||
Node::Symbol(val) => write!(f, "{}", val),
|
|
||||||
Node::Keyword(val) => write!(f, ":{}", val),
|
|
||||||
Node::String(val) => write!(f, "{}", val),
|
|
||||||
Node::Nil => write!(f, "()"),
|
|
||||||
|
|
||||||
Node::Vector(vec) => {
|
|
||||||
let s = vec
|
|
||||||
.iter()
|
|
||||||
.map(|elem| elem.to_string())
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
write!(f, "[{s}]")
|
|
||||||
}
|
|
||||||
Node::Map(map) => {
|
|
||||||
let res = map
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| format!("{k}: {v}"))
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
write!(f, "{{{res}}}")
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Function {
|
|
||||||
params: _,
|
|
||||||
env: _,
|
|
||||||
body: _,
|
|
||||||
} => {
|
|
||||||
write!(f, "#<function>")
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::NativeFunc(func) => write!(f, "{func:?}"),
|
|
||||||
Node::Special(func) => write!(f, "{func:?}"),
|
|
||||||
|
|
||||||
Node::Void => write!(f, ""),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,10 +19,43 @@ pub enum Error {
|
||||||
|
|
||||||
#[error("unexpected character '{0}'")]
|
#[error("unexpected character '{0}'")]
|
||||||
UnexpectedChar(char),
|
UnexpectedChar(char),
|
||||||
|
#[error("unexpected end of file")]
|
||||||
|
UnexpectedEof,
|
||||||
#[error("string is never closed")]
|
#[error("string is never closed")]
|
||||||
UnterminatedString,
|
UnterminatedString,
|
||||||
#[error("the quote does not have a valid follower node")]
|
#[error("the quote does not have a valid follower node")]
|
||||||
NoFollowerNode,
|
NoFollowerNode,
|
||||||
|
#[error("specials must be the first node in a list")]
|
||||||
|
SpecialNotOperator,
|
||||||
|
|
||||||
|
#[error("expected list, got {0}")]
|
||||||
|
ExpectedList(String),
|
||||||
|
#[error("expected symbol, got {0}")]
|
||||||
|
ExpectedSymbol(String),
|
||||||
|
#[error("expected {0} values, got {1}")]
|
||||||
|
MismatchedValueCount(usize, usize),
|
||||||
|
|
||||||
|
// TODO: Consolidate these errors
|
||||||
|
#[error("cannot have empty define statements")]
|
||||||
|
EmptyDefine,
|
||||||
|
#[error("missing function args")]
|
||||||
|
EmptyFunctionArgs,
|
||||||
|
#[error("missing function body")]
|
||||||
|
EmptyFunctionBody,
|
||||||
|
#[error("missing let args")]
|
||||||
|
EmptyLetArgs,
|
||||||
|
#[error("missing let body")]
|
||||||
|
EmptyLetBody,
|
||||||
|
#[error("missing apply operator")]
|
||||||
|
EmptyApplyOperator,
|
||||||
|
#[error("missing apply body")]
|
||||||
|
EmptyApplyBody,
|
||||||
|
#[error("missing if condition")]
|
||||||
|
EmptyIfCondition,
|
||||||
|
#[error("missing if consequene")]
|
||||||
|
EmptyIfConsequence,
|
||||||
|
#[error("missing do body")]
|
||||||
|
EmptyDo,
|
||||||
|
|
||||||
#[error("please file a bug report")]
|
#[error("please file a bug report")]
|
||||||
Unreachable,
|
Unreachable,
|
||||||
|
|
|
@ -13,13 +13,6 @@ pub enum Token {
|
||||||
LeftBrace,
|
LeftBrace,
|
||||||
RightBrace,
|
RightBrace,
|
||||||
|
|
||||||
WeirdSign,
|
|
||||||
Apostrophe,
|
|
||||||
Grave,
|
|
||||||
Tilde,
|
|
||||||
Carot,
|
|
||||||
AtSign,
|
|
||||||
|
|
||||||
// Math Operators
|
// Math Operators
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
|
@ -40,6 +33,19 @@ pub enum Token {
|
||||||
True,
|
True,
|
||||||
False,
|
False,
|
||||||
Nil,
|
Nil,
|
||||||
|
|
||||||
|
// Specials
|
||||||
|
Function,
|
||||||
|
Define,
|
||||||
|
Let,
|
||||||
|
Eval,
|
||||||
|
Apply,
|
||||||
|
If,
|
||||||
|
Do,
|
||||||
|
|
||||||
|
Quote,
|
||||||
|
Quasiquote,
|
||||||
|
Unquote,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(input: &str) -> Result<Vec<Token>, Error> {
|
pub fn read(input: &str) -> Result<Vec<Token>, Error> {
|
||||||
|
@ -60,13 +66,6 @@ fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let tok = match tok {
|
let tok = match tok {
|
||||||
// Weird sign
|
|
||||||
'~' if input.peek().is_some_and(|c| c == &'@') => {
|
|
||||||
// Munch the @
|
|
||||||
input.next();
|
|
||||||
Token::WeirdSign
|
|
||||||
}
|
|
||||||
|
|
||||||
// Negative numbers
|
// Negative numbers
|
||||||
'-' if input.peek().is_some_and(|c| c.is_ascii_digit()) => read_int(input, '-'),
|
'-' if input.peek().is_some_and(|c| c.is_ascii_digit()) => read_int(input, '-'),
|
||||||
|
|
||||||
|
@ -92,11 +91,9 @@ fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
|
||||||
'{' => Token::LeftBrace,
|
'{' => Token::LeftBrace,
|
||||||
'}' => Token::RightBrace,
|
'}' => Token::RightBrace,
|
||||||
|
|
||||||
'\'' => Token::Apostrophe,
|
'\'' => Token::Quote,
|
||||||
'`' => Token::Grave,
|
'`' => Token::Quasiquote,
|
||||||
'~' => Token::Tilde,
|
',' => Token::Unquote,
|
||||||
'^' => Token::Carot,
|
|
||||||
'@' => Token::AtSign,
|
|
||||||
|
|
||||||
'+' => Token::Plus,
|
'+' => Token::Plus,
|
||||||
'-' => Token::Minus,
|
'-' => Token::Minus,
|
||||||
|
@ -211,77 +208,16 @@ fn read_ident(input: &mut Peekable<Chars>, first: char) -> Token {
|
||||||
"true" => Token::True,
|
"true" => Token::True,
|
||||||
"false" => Token::False,
|
"false" => Token::False,
|
||||||
"nil" => Token::Nil,
|
"nil" => Token::Nil,
|
||||||
|
"fn*" => Token::Function,
|
||||||
|
"define" => Token::Define,
|
||||||
|
"let*" => Token::Let,
|
||||||
|
"eval" => Token::Eval,
|
||||||
|
"apply" => Token::Apply,
|
||||||
|
"quote" => Token::Quote,
|
||||||
|
"quasiquote" => Token::Quasiquote,
|
||||||
|
"unquote" => Token::Unquote,
|
||||||
|
"if" => Token::If,
|
||||||
|
"do" => Token::Do,
|
||||||
ident => Token::Ident(ident.to_owned()),
|
ident => Token::Ident(ident.to_owned()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use rstest::rstest;
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case("()[]{}", vec![Token::LeftParen, Token::RightParen, Token::LeftBracket, Token::RightBracket, Token::LeftBrace, Token::RightBrace])]
|
|
||||||
#[case(" ' ` ^ ~@ ~ @", vec![Token::Apostrophe, Token::Grave, Token::Carot, Token::WeirdSign, Token::Tilde, Token::AtSign])]
|
|
||||||
#[case("(+ 1 2)", vec![Token::LeftParen, Token::Plus, Token::Int(1), Token::Int(2), Token::RightParen])]
|
|
||||||
#[case("(- 1 2)", vec![Token::LeftParen, Token::Minus, Token::Int(1), Token::Int(2), Token::RightParen])]
|
|
||||||
#[case("(* 1 2)", vec![Token::LeftParen, Token::Asterisk, Token::Int(1), Token::Int(2), Token::RightParen])]
|
|
||||||
#[case("(/ 1 2)", vec![Token::LeftParen, Token::Slash, Token::Int(1), Token::Int(2), Token::RightParen])]
|
|
||||||
#[case("(- -2 1)", vec![Token::LeftParen, Token::Minus, Token::Int(-2), Token::Int(1), Token::RightParen])]
|
|
||||||
#[case("(\"string and stuff\")", vec![Token::LeftParen, Token::String("string and stuff".into()), Token::RightParen])]
|
|
||||||
#[case("define!", vec![Token::Ident("define!".into())])]
|
|
||||||
#[case("let*", vec![Token::Ident("let*".into())])]
|
|
||||||
#[case("is-int?", vec![Token::Ident("is-int?".into())])]
|
|
||||||
#[case(
|
|
||||||
"(func a b)",
|
|
||||||
vec![
|
|
||||||
Token::LeftParen,
|
|
||||||
Token::Ident("func".into()),
|
|
||||||
Token::Ident("a".into()),
|
|
||||||
Token::Ident("b".into()),
|
|
||||||
Token::RightParen
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
#[case(
|
|
||||||
"(+ 1 (- 2 1))",
|
|
||||||
vec![
|
|
||||||
Token::LeftParen,
|
|
||||||
Token::Plus,
|
|
||||||
Token::Int(1),
|
|
||||||
Token::LeftParen,
|
|
||||||
Token::Minus,
|
|
||||||
Token::Int(2),
|
|
||||||
Token::Int(1),
|
|
||||||
Token::RightParen,
|
|
||||||
Token::RightParen
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
#[case(
|
|
||||||
"(fn a ;; This comment is useless
|
|
||||||
(+ 1 2))",
|
|
||||||
vec![
|
|
||||||
Token::LeftParen,
|
|
||||||
Token::Ident("fn".into()),
|
|
||||||
Token::Ident("a".into()),
|
|
||||||
Token::LeftParen,
|
|
||||||
Token::Plus,
|
|
||||||
Token::Int(1),
|
|
||||||
Token::Int(2),
|
|
||||||
Token::RightParen,
|
|
||||||
Token::RightParen
|
|
||||||
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
fn test_lexer(#[case] input: &str, #[case] expected: Vec<Token>) {
|
|
||||||
let res = read(input).unwrap();
|
|
||||||
assert_eq!(res, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
// Unbalanced string
|
|
||||||
#[case("(\"asdf)")]
|
|
||||||
fn test_lexer_errors(#[case] input: &str) {
|
|
||||||
let res = read(input);
|
|
||||||
assert!(res.is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,7 +31,35 @@ fn next_statement(tokens: &mut TokenIter) -> Result<Option<Node>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let node = match tok {
|
let node = match tok {
|
||||||
Token::LeftParen => read_list(tokens, Token::RightParen)?,
|
Token::LeftParen => match tokens.peek().ok_or(Error::UnclosedParenthesis)? {
|
||||||
|
Token::Function => read_function(tokens)?,
|
||||||
|
Token::Define => read_define(tokens)?,
|
||||||
|
Token::Let => read_let(tokens)?,
|
||||||
|
Token::Eval => read_eval(tokens)?,
|
||||||
|
Token::Apply => read_apply(tokens)?,
|
||||||
|
Token::If => read_if(tokens)?,
|
||||||
|
Token::Do => read_do(tokens)?,
|
||||||
|
Token::Quote => {
|
||||||
|
tokens.next(); // Munch (
|
||||||
|
let quote = read_quote(tokens, "quote")?;
|
||||||
|
tokens.next(); // Munch )
|
||||||
|
quote
|
||||||
|
}
|
||||||
|
Token::Quasiquote => {
|
||||||
|
tokens.next(); // Munch (
|
||||||
|
let quote = read_quote(tokens, "quasiquote")?;
|
||||||
|
tokens.next(); // Munch )
|
||||||
|
quote
|
||||||
|
}
|
||||||
|
Token::Unquote => {
|
||||||
|
tokens.next(); // Munch (
|
||||||
|
let quote = read_quote(tokens, "unquote")?;
|
||||||
|
tokens.next(); // Munch )
|
||||||
|
quote
|
||||||
|
}
|
||||||
|
_ => read_list(tokens, Token::RightParen)?,
|
||||||
|
},
|
||||||
|
|
||||||
Token::RightParen => Err(Error::UnopenedParenthesis)?,
|
Token::RightParen => Err(Error::UnopenedParenthesis)?,
|
||||||
|
|
||||||
Token::LeftBracket => read_list(tokens, Token::RightBracket)?,
|
Token::LeftBracket => read_list(tokens, Token::RightBracket)?,
|
||||||
|
@ -40,16 +68,6 @@ fn next_statement(tokens: &mut TokenIter) -> Result<Option<Node>> {
|
||||||
Token::LeftBrace => read_list(tokens, Token::RightBrace)?,
|
Token::LeftBrace => read_list(tokens, Token::RightBrace)?,
|
||||||
Token::RightBrace => Err(Error::UnopenedBrace)?,
|
Token::RightBrace => Err(Error::UnopenedBrace)?,
|
||||||
|
|
||||||
Token::WeirdSign => read_quote(tokens, "splice-unquote")?,
|
|
||||||
Token::Apostrophe => read_quote(tokens, "quote")?,
|
|
||||||
Token::Grave => read_quote(tokens, "quasiquote")?,
|
|
||||||
Token::Tilde => read_quote(tokens, "unquote")?,
|
|
||||||
|
|
||||||
// TODO: meta
|
|
||||||
Token::Carot => todo!(),
|
|
||||||
// TODO: deref
|
|
||||||
Token::AtSign => todo!(),
|
|
||||||
|
|
||||||
Token::Plus => Node::Symbol("+".into()),
|
Token::Plus => Node::Symbol("+".into()),
|
||||||
Token::Minus => Node::Symbol("-".into()),
|
Token::Minus => Node::Symbol("-".into()),
|
||||||
Token::Asterisk => Node::Symbol("*".into()),
|
Token::Asterisk => Node::Symbol("*".into()),
|
||||||
|
@ -68,6 +86,18 @@ fn next_statement(tokens: &mut TokenIter) -> Result<Option<Node>> {
|
||||||
Token::True => Node::Boolean(true),
|
Token::True => Node::Boolean(true),
|
||||||
Token::False => Node::Boolean(false),
|
Token::False => Node::Boolean(false),
|
||||||
Token::Nil => Node::Nil,
|
Token::Nil => Node::Nil,
|
||||||
|
|
||||||
|
Token::Quote => read_quote(tokens, "quote")?,
|
||||||
|
Token::Quasiquote => read_quote(tokens, "quasiquote")?,
|
||||||
|
Token::Unquote => read_quote(tokens, "unquote")?,
|
||||||
|
|
||||||
|
Token::Function
|
||||||
|
| Token::Define
|
||||||
|
| Token::Let
|
||||||
|
| Token::Eval
|
||||||
|
| Token::Apply
|
||||||
|
| Token::If
|
||||||
|
| Token::Do => Err(Error::SpecialNotOperator)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(node))
|
Ok(Some(node))
|
||||||
|
@ -108,12 +138,241 @@ fn read_list(tokens: &mut TokenIter, closer: Token) -> Result<Node> {
|
||||||
|
|
||||||
fn read_quote(tokens: &mut TokenIter, quote_type: &str) -> Result<Node> {
|
fn read_quote(tokens: &mut TokenIter, quote_type: &str) -> Result<Node> {
|
||||||
let follower_node = match next_statement(tokens)? {
|
let follower_node = match next_statement(tokens)? {
|
||||||
Some(node) => node,
|
Some(node) => Box::new(node),
|
||||||
None => Err(Error::NoFollowerNode)?,
|
None => Err(Error::NoFollowerNode)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Node::List(vec![
|
let node = match quote_type {
|
||||||
Node::Symbol(quote_type.into()),
|
"quote" => Node::Quote(follower_node),
|
||||||
follower_node,
|
"quasiquote" => Node::Quasiquote(follower_node),
|
||||||
]))
|
"unquote" => Node::Unquote(follower_node),
|
||||||
|
_ => Err(Error::Unreachable)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_define(tokens: &mut TokenIter) -> Result<Node> {
|
||||||
|
tokens.next(); // Munch special
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
if tokens.peek() == Some(&Token::RightParen) {
|
||||||
|
return Err(Error::EmptyDefine);
|
||||||
|
}
|
||||||
|
|
||||||
|
while tokens.peek() != Some(&Token::RightParen) {
|
||||||
|
let values = match next_statement(tokens)? {
|
||||||
|
Some(Node::List(list)) => list,
|
||||||
|
Some(node) => Err(Error::ExpectedList(node.get_type()))?,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if values.len() != 2 {
|
||||||
|
return Err(Error::MismatchedValueCount(2, values.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut values = values.into_iter();
|
||||||
|
let name = match values.next().unwrap() {
|
||||||
|
Node::Symbol(name) => name,
|
||||||
|
val => Err(Error::ExpectedSymbol(val.get_type()))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = values.next().unwrap();
|
||||||
|
|
||||||
|
args.push((name.to_owned(), value.to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.next(); // Munch closer
|
||||||
|
|
||||||
|
Ok(Node::Define { args })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_let(tokens: &mut TokenIter) -> Result<Node> {
|
||||||
|
tokens.next(); // Munch special
|
||||||
|
|
||||||
|
let raw_args = match next_statement(tokens)? {
|
||||||
|
Some(Node::List(list)) => list,
|
||||||
|
Some(node) => Err(Error::ExpectedList(node.get_type()))?,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
for node in raw_args.iter() {
|
||||||
|
match node {
|
||||||
|
Node::List(list) => {
|
||||||
|
if list.len() != 2 {
|
||||||
|
return Err(Error::MismatchedValueCount(2, list.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut list = list.into_iter();
|
||||||
|
let name = match list.next().unwrap() {
|
||||||
|
Node::Symbol(name) => name,
|
||||||
|
val => Err(Error::ExpectedSymbol(val.get_type()))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = list.next().unwrap();
|
||||||
|
|
||||||
|
args.push((name.to_owned(), value.to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => return Err(Error::ExpectedList(node.get_type()))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = read_body(tokens)?;
|
||||||
|
|
||||||
|
tokens.next(); // Munch closer
|
||||||
|
|
||||||
|
Ok(Node::Let { args, body })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_function(tokens: &mut TokenIter) -> Result<Node> {
|
||||||
|
tokens.next(); // Munch special
|
||||||
|
|
||||||
|
let parameters = match next_statement(tokens)? {
|
||||||
|
Some(Node::List(args)) => args,
|
||||||
|
Some(Node::Nil) => vec![],
|
||||||
|
Some(node) => Err(Error::ExpectedList(node.get_type()))?,
|
||||||
|
None => Err(Error::EmptyFunctionArgs)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure all parameters are symbols then extract their names
|
||||||
|
let parameters = parameters
|
||||||
|
.into_iter()
|
||||||
|
.map(|node| match node {
|
||||||
|
Node::Symbol(name) => Ok(name),
|
||||||
|
_ => Err(Error::ExpectedSymbol(node.get_type()))?,
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
if tokens.peek() == Some(&Token::RightParen) {
|
||||||
|
return Err(Error::EmptyFunctionBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = read_body(tokens)?;
|
||||||
|
|
||||||
|
tokens.next(); // Munch closer
|
||||||
|
|
||||||
|
Ok(Node::Function { parameters, body })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_eval(tokens: &mut TokenIter) -> Result<Node> {
|
||||||
|
tokens.next(); // Munch special
|
||||||
|
|
||||||
|
let node = match next_statement(tokens)? {
|
||||||
|
Some(node) => node,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if tokens.peek() != Some(&Token::RightParen) {
|
||||||
|
// FIXME: This should not say expected is 0
|
||||||
|
return Err(Error::MismatchedValueCount(1, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.next(); // Munch closer
|
||||||
|
|
||||||
|
Ok(Node::Eval {
|
||||||
|
body: Box::new(node),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_apply(tokens: &mut TokenIter) -> Result<Node> {
|
||||||
|
tokens.next(); // Munch special
|
||||||
|
|
||||||
|
if tokens.peek() == Some(&Token::RightParen) {
|
||||||
|
return Err(Error::EmptyApplyOperator);
|
||||||
|
}
|
||||||
|
let func = match next_statement(tokens)? {
|
||||||
|
Some(node) => node,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if tokens.peek() == Some(&Token::RightParen) {
|
||||||
|
return Err(Error::EmptyApplyBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = match next_statement(tokens)? {
|
||||||
|
Some(node) => node,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.next(); // Munch closer
|
||||||
|
|
||||||
|
Ok(Node::Apply {
|
||||||
|
func: Box::new(func),
|
||||||
|
args: Box::new(args),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_if(tokens: &mut TokenIter) -> Result<Node> {
|
||||||
|
tokens.next(); // Munch special
|
||||||
|
|
||||||
|
if tokens.peek() == Some(&Token::RightParen) {
|
||||||
|
return Err(Error::EmptyIfCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cond = match next_statement(tokens)? {
|
||||||
|
Some(node) => node,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if tokens.peek() == Some(&Token::RightParen) {
|
||||||
|
return Err(Error::EmptyIfConsequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
let consequence = match next_statement(tokens)? {
|
||||||
|
Some(node) => node,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let alternative = match tokens.peek() {
|
||||||
|
Some(&Token::RightParen) => Node::Nil,
|
||||||
|
Some(_) => match next_statement(tokens)? {
|
||||||
|
Some(node) => node,
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
},
|
||||||
|
None => Err(Error::UnexpectedEof)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.next(); // Munch closer
|
||||||
|
|
||||||
|
Ok(Node::If {
|
||||||
|
cond: Box::new(cond),
|
||||||
|
consequence: Box::new(consequence),
|
||||||
|
alternative: Box::new(alternative),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_do(tokens: &mut TokenIter) -> Result<Node> {
|
||||||
|
tokens.next(); // Munch special
|
||||||
|
|
||||||
|
if tokens.peek() == Some(&Token::RightParen) {
|
||||||
|
return Err(Error::EmptyDo);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = read_body(tokens)?;
|
||||||
|
|
||||||
|
tokens.next(); // Munch closer
|
||||||
|
|
||||||
|
Ok(Node::Do { body })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_body(tokens: &mut TokenIter) -> Result<Vec<Node>> {
|
||||||
|
let mut body = Vec::new();
|
||||||
|
|
||||||
|
while tokens.peek() != Some(&Token::RightParen) {
|
||||||
|
if let Some(node) = next_statement(tokens)? {
|
||||||
|
body.push(node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match next_statement(tokens)? {
|
||||||
|
Some(node) => body.push(node),
|
||||||
|
None => Err(Error::UnclosedParenthesis)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(body)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,71 @@ pub enum Node {
|
||||||
String(String),
|
String(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Nil,
|
Nil,
|
||||||
|
Void, // XXX: Not sure if this should be a node
|
||||||
|
|
||||||
Vector(Vec<Node>),
|
Vector(Vec<Node>),
|
||||||
Map(HashMap<String, Node>),
|
Map(HashMap<String, Node>),
|
||||||
|
|
||||||
|
// Specials
|
||||||
|
Define {
|
||||||
|
args: Vec<(String, Node)>,
|
||||||
|
},
|
||||||
|
Let {
|
||||||
|
args: Vec<(String, Node)>,
|
||||||
|
body: Vec<Node>,
|
||||||
|
},
|
||||||
|
Function {
|
||||||
|
parameters: Vec<String>,
|
||||||
|
body: Vec<Node>,
|
||||||
|
},
|
||||||
|
If {
|
||||||
|
cond: Box<Node>,
|
||||||
|
consequence: Box<Node>,
|
||||||
|
alternative: Box<Node>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Quote(Box<Node>),
|
||||||
|
Quasiquote(Box<Node>),
|
||||||
|
Unquote(Box<Node>),
|
||||||
|
|
||||||
|
Do {
|
||||||
|
body: Vec<Node>,
|
||||||
|
},
|
||||||
|
Eval {
|
||||||
|
body: Box<Node>,
|
||||||
|
},
|
||||||
|
Apply {
|
||||||
|
func: Box<Node>,
|
||||||
|
args: Box<Node>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
pub fn get_type(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Node::List(_) => "list".to_string(),
|
||||||
|
Node::Symbol(_) => "symbol".to_string(),
|
||||||
|
Node::Keyword(_) => "keyword".to_string(),
|
||||||
|
Node::Int(_) => "int".to_string(),
|
||||||
|
Node::Float(_) => "float".to_string(),
|
||||||
|
Node::String(_) => "string".to_string(),
|
||||||
|
Node::Boolean(_) => "boolean".to_string(),
|
||||||
|
Node::Nil => "nil".to_string(),
|
||||||
|
Node::Void => "void".to_string(),
|
||||||
|
Node::Vector(_) => "vector".to_string(),
|
||||||
|
Node::Map(_) => "map".to_string(),
|
||||||
|
Node::Define { .. } => "define".to_string(),
|
||||||
|
Node::Let { .. } => "let".to_string(),
|
||||||
|
Node::Function { .. } => "function".to_string(),
|
||||||
|
Node::If { .. } => "if".to_string(),
|
||||||
|
Node::Quote(_) => "quote".to_string(),
|
||||||
|
Node::Quasiquote(_) => "quasiquote".to_string(),
|
||||||
|
Node::Unquote(_) => "unquote".to_string(),
|
||||||
|
Node::Do { .. } => "do".to_string(),
|
||||||
|
Node::Eval { .. } => "eval".to_string(),
|
||||||
|
Node::Apply { .. } => "apply".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Node {
|
impl std::fmt::Display for Node {
|
||||||
|
@ -37,6 +99,7 @@ impl std::fmt::Display for Node {
|
||||||
Node::Keyword(val) => write!(f, ":{}", val),
|
Node::Keyword(val) => write!(f, ":{}", val),
|
||||||
Node::String(val) => write!(f, "{}", val),
|
Node::String(val) => write!(f, "{}", val),
|
||||||
Node::Nil => write!(f, "()"),
|
Node::Nil => write!(f, "()"),
|
||||||
|
Node::Void => write!(f, ""),
|
||||||
|
|
||||||
Node::Vector(vec) => {
|
Node::Vector(vec) => {
|
||||||
let s = vec
|
let s = vec
|
||||||
|
@ -56,6 +119,70 @@ impl std::fmt::Display for Node {
|
||||||
|
|
||||||
write!(f, "{{{res}}}")
|
write!(f, "{{{res}}}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node::Define { args } => {
|
||||||
|
let res = args
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("({k} {v})"))
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.expect("parser does not allow empty define statements");
|
||||||
|
|
||||||
|
write!(f, "(define {res})")
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::Let { args, body } => {
|
||||||
|
let args = args
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("({k} {v})"))
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.expect("parser does not allow empty let statements");
|
||||||
|
let body = body
|
||||||
|
.iter()
|
||||||
|
.map(|node| node.to_string())
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.expect("parser does not allow empty let bodies");
|
||||||
|
|
||||||
|
write!(f, "(let ({args}) {body})")
|
||||||
|
}
|
||||||
|
Node::Function {
|
||||||
|
parameters: args,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
let args = args
|
||||||
|
.iter()
|
||||||
|
.map(|node| node.to_string())
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let body = body
|
||||||
|
.iter()
|
||||||
|
.map(|node| node.to_string())
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.expect("parser does not allow empty function bodies");
|
||||||
|
|
||||||
|
write!(f, "(fn ({args}) {body})")
|
||||||
|
}
|
||||||
|
Node::If {
|
||||||
|
cond,
|
||||||
|
consequence,
|
||||||
|
alternative,
|
||||||
|
} => write!(f, "(if {cond} {consequence} {alternative})"),
|
||||||
|
|
||||||
|
Node::Quote(node) => write!(f, "'{node}"),
|
||||||
|
Node::Quasiquote(node) => write!(f, "`{node}"),
|
||||||
|
Node::Unquote(node) => write!(f, ",{node}"),
|
||||||
|
|
||||||
|
Node::Do { body } => {
|
||||||
|
let body = body
|
||||||
|
.iter()
|
||||||
|
.map(|node| node.to_string())
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.expect("parser does not allow empty do statements");
|
||||||
|
|
||||||
|
write!(f, "(do {body})")
|
||||||
|
}
|
||||||
|
Node::Eval { body } => write!(f, "(eval {body})"),
|
||||||
|
Node::Apply { func, args } => write!(f, "(apply {func} {args})"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue