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::collections::HashMap;
|
||||
|
||||
use crate::evaluator::{eval, eval_node};
|
||||
use crate::macros::arg_count;
|
||||
use crate::{Error, Node};
|
||||
use super::{NativeFunc, Value};
|
||||
use crate::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
|
||||
(
|
||||
"+",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
let res = args
|
||||
.into_iter()
|
||||
NativeFunc(|args| {
|
||||
args.into_iter()
|
||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(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),
|
||||
_ => todo!(),
|
||||
})
|
||||
.unwrap_or(Node::Int(0));
|
||||
Ok(res)
|
||||
.unwrap_or(Node::Int(0))
|
||||
}),
|
||||
),
|
||||
(
|
||||
"-",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
let res = args
|
||||
.into_iter()
|
||||
NativeFunc(|args| {
|
||||
args.into_iter()
|
||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(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),
|
||||
_ => todo!(),
|
||||
})
|
||||
.unwrap_or(Node::Int(0));
|
||||
|
||||
Ok(res)
|
||||
.unwrap_or(Node::Int(0))
|
||||
}),
|
||||
),
|
||||
(
|
||||
"*",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
let res = args
|
||||
.into_iter()
|
||||
NativeFunc(|args| {
|
||||
args.into_iter()
|
||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(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),
|
||||
_ => todo!(),
|
||||
})
|
||||
.unwrap_or(Node::Int(0));
|
||||
|
||||
Ok(res)
|
||||
.unwrap_or(Node::Int(0))
|
||||
}),
|
||||
),
|
||||
(
|
||||
"/",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
let res = args
|
||||
.into_iter()
|
||||
NativeFunc(|args| {
|
||||
args.into_iter()
|
||||
.reduce(|lhs, rhs| match (lhs, rhs) {
|
||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Int(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),
|
||||
_ => todo!(),
|
||||
})
|
||||
.unwrap_or(Node::Int(0));
|
||||
|
||||
Ok(res)
|
||||
.unwrap_or(Node::Int(0))
|
||||
}),
|
||||
),
|
||||
// Collections
|
||||
("list", Node::NativeFunc(|_env, args| Ok(Node::List(args)))),
|
||||
("list", NativeFunc(|args| Node::List(args))),
|
||||
(
|
||||
"list?",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
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",
|
||||
Node::NativeFunc(|_env, args| Ok(Node::Vector(args))),
|
||||
),
|
||||
("vector", NativeFunc(|args| Node::Vector(args))),
|
||||
(
|
||||
"vector?",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
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",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(modulo: 2, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(modulo: 2, args.len());
|
||||
|
||||
let mut index = -1;
|
||||
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
|
||||
|
@ -202,82 +112,88 @@ pub(super) fn core() -> HashMap<String, Node> {
|
|||
.zip(values)
|
||||
.collect();
|
||||
|
||||
Ok(Node::Map(res))
|
||||
Node::Map(res)
|
||||
}),
|
||||
),
|
||||
(
|
||||
"hashmap?",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
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?",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
match args[0].borrow() {
|
||||
Node::List(list) => Ok(Node::Boolean(list.is_empty())),
|
||||
Node::Vector(vec) => Ok(Node::Boolean(vec.is_empty())),
|
||||
Node::Map(map) => Ok(Node::Boolean(map.is_empty())),
|
||||
_ => Err(Error::ExpectedCollection)?,
|
||||
Node::List(list) => Node::Boolean(list.is_empty()),
|
||||
Node::Vector(vec) => Node::Boolean(vec.is_empty()),
|
||||
Node::Map(map) => Node::Boolean(map.is_empty()),
|
||||
_ => todo!(),
|
||||
}
|
||||
}),
|
||||
),
|
||||
(
|
||||
"count",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
match args[0].borrow() {
|
||||
Node::List(list) => Ok(Node::Int(list.len() as i64)),
|
||||
Node::Vector(vec) => Ok(Node::Int(vec.len() as i64)),
|
||||
Node::Map(map) => Ok(Node::Int(map.len() as i64)),
|
||||
_ => Err(Error::ExpectedCollection)?,
|
||||
Node::List(list) => Node::Int(list.len() as i64),
|
||||
Node::Vector(vec) => Node::Int(vec.len() as i64),
|
||||
Node::Map(map) => Node::Int(map.len() as i64),
|
||||
_ => todo!(),
|
||||
}
|
||||
}),
|
||||
),
|
||||
// Ordering
|
||||
(
|
||||
"eq?",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(2, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(2, args.len());
|
||||
|
||||
let lhs = args[0].borrow();
|
||||
let rhs = args[1].borrow();
|
||||
|
||||
match (lhs, rhs) {
|
||||
(Node::Int(lhs), Node::Int(rhs)) => Ok(Node::Boolean(lhs == rhs)),
|
||||
(Node::Boolean(lhs), Node::Boolean(rhs)) => Ok(Node::Boolean(lhs == rhs)),
|
||||
(Node::String(lhs), Node::String(rhs)) => Ok(Node::Boolean(lhs == rhs)),
|
||||
(Node::Nil, Node::Nil) => Ok(Node::Boolean(true)),
|
||||
_ => Err(Error::ExpectedNumber)?,
|
||||
(Node::Int(lhs), Node::Int(rhs)) => Node::Boolean(lhs == rhs),
|
||||
(Node::Boolean(lhs), Node::Boolean(rhs)) => Node::Boolean(lhs == rhs),
|
||||
(Node::String(lhs), Node::String(rhs)) => Node::Boolean(lhs == rhs),
|
||||
(Node::Nil, Node::Nil) => Node::Boolean(true),
|
||||
_ => todo!(),
|
||||
}
|
||||
}),
|
||||
),
|
||||
(
|
||||
"not",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
let expr = args[0].borrow();
|
||||
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| {
|
||||
arg_count!(2, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(2, args.len());
|
||||
|
||||
let mut args = args.into_iter();
|
||||
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::Int(rhs)) => lhs < rhs as f64,
|
||||
(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| {
|
||||
arg_count!(2, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(2, args.len());
|
||||
|
||||
let mut args = args.into_iter();
|
||||
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::Int(rhs)) => lhs > rhs as f64,
|
||||
(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| {
|
||||
arg_count!(2, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(2, args.len());
|
||||
|
||||
let mut args = args.into_iter();
|
||||
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::Int(rhs)) => lhs <= rhs as f64,
|
||||
(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| {
|
||||
arg_count!(2, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(2, args.len());
|
||||
|
||||
let mut args = args.into_iter();
|
||||
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::Int(rhs)) => lhs >= rhs as f64,
|
||||
(Node::Int(lhs), Node::Float(rhs)) => (lhs as f64) >= rhs,
|
||||
_ => Err(Error::ExpectedNumber)?,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
Ok(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),
|
||||
})
|
||||
Node::Boolean(greater_than_equal)
|
||||
}),
|
||||
),
|
||||
// Strings
|
||||
(
|
||||
"str",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: Reimplement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
let val = args[0].borrow();
|
||||
Ok(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)
|
||||
Node::String(val.to_string())
|
||||
}),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v))
|
||||
.map(|(k, v)| (k.to_string(), Value::NativeFunc(v)))
|
||||
.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::collections::HashMap;
|
||||
|
||||
use crate::macros::arg_count;
|
||||
use crate::{Error, Node};
|
||||
use super::{NativeFunc, Value};
|
||||
use crate::Node;
|
||||
|
||||
pub(super) fn io() -> HashMap<String, Node> {
|
||||
pub(super) fn io() -> HashMap<String, Value> {
|
||||
[
|
||||
// Stdout
|
||||
(
|
||||
"print",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
print!(
|
||||
"{}",
|
||||
args.into_iter()
|
||||
.map(|node| node.to_string())
|
||||
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||
.unwrap_or_default()
|
||||
);
|
||||
|
||||
print!("{}", args[0]);
|
||||
|
||||
Ok(Node::Void)
|
||||
Node::Void
|
||||
}),
|
||||
),
|
||||
(
|
||||
"println",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
println!(
|
||||
"{}",
|
||||
args.into_iter()
|
||||
.map(|node| node.to_string())
|
||||
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||
.unwrap_or_default()
|
||||
);
|
||||
|
||||
println!("{}", args[0]);
|
||||
|
||||
Ok(Node::Void)
|
||||
Node::Void
|
||||
}),
|
||||
),
|
||||
// File IO
|
||||
(
|
||||
"read-file",
|
||||
Node::NativeFunc(|_env, args| {
|
||||
arg_count!(1, args.len());
|
||||
NativeFunc(|args| {
|
||||
// TODO: implement this
|
||||
// arg_count!(1, args.len());
|
||||
|
||||
let val = args[0].borrow();
|
||||
if let Node::String(path) = val {
|
||||
let contents = std::fs::read_to_string(path)
|
||||
.map_err(|err| Error::SystemError(err.to_string()))?;
|
||||
return Ok(Node::String(contents));
|
||||
let contents = std::fs::read_to_string(path).unwrap();
|
||||
// FIXME Lmao, we need error handling
|
||||
// .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()
|
||||
.map(|(k, v)| (k.to_string(), v))
|
||||
.map(|(k, v)| (k.to_string(), Value::NativeFunc(v)))
|
||||
.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 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)]
|
||||
pub struct Environment(Rc<RawEnvironment>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RawEnvironment {
|
||||
current: RefCell<HashMap<String, Node>>,
|
||||
current: RefCell<HashMap<String, Value>>,
|
||||
outer: Option<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) {
|
||||
return Some(val.clone());
|
||||
}
|
||||
|
@ -27,15 +42,20 @@ impl Environment {
|
|||
|
||||
pub fn wrap(&self, records: Vec<(String, Node)>) -> Environment {
|
||||
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()),
|
||||
};
|
||||
|
||||
Environment(Rc::new(env))
|
||||
}
|
||||
|
||||
fn set(&self, ident: String, val: Node) {
|
||||
self.0.current.borrow_mut().insert(ident, val);
|
||||
pub fn set(&self, ident: String, val: Node) {
|
||||
self.0.current.borrow_mut().insert(ident, Value::Node(val));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@ pub enum Error {
|
|||
ExpectedNumber,
|
||||
#[error("expected string")]
|
||||
ExpectedString,
|
||||
#[error("expected boolean")]
|
||||
ExpectedBoolean,
|
||||
|
||||
#[error("expected {0} arguments, got {1}")]
|
||||
MismatchedArgCount(usize, usize),
|
||||
#[error("system error {0}")]
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
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>> {
|
||||
let mut exprs = Vec::new();
|
||||
|
||||
for node in ast {
|
||||
dbg!(&node);
|
||||
let res = eval_node(env, node)?;
|
||||
if let Node::Void = res {
|
||||
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)?)?;
|
||||
|
||||
match operator.borrow() {
|
||||
Node::NativeFunc(func) => {
|
||||
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,
|
||||
} => {
|
||||
Node::Function { parameters, body } => {
|
||||
let args = list;
|
||||
if args.len() != params.len() {
|
||||
Err(Error::MismatchedArgCount(params.len(), args.len()))?
|
||||
if args.len() != parameters.len() {
|
||||
Err(Error::MismatchedArgCount(parameters.len(), args.len()))?
|
||||
}
|
||||
|
||||
let args = args
|
||||
.map(|node| eval_node(env, node))
|
||||
.collect::<Result<Vec<Node>>>()?;
|
||||
|
||||
let records = params.iter().map(|k| k.to_owned()).zip(args).collect();
|
||||
let env = inner_env.wrap(records);
|
||||
let records = parameters.iter().map(|k| k.to_owned()).zip(args).collect();
|
||||
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)?,
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -148,23 +235,20 @@ mod test {
|
|||
#[case("(count {})", "0")]
|
||||
#[case("(count {:a 1})", "1")]
|
||||
// Environment manipulation
|
||||
#[case("(define! asdf (+ 2 2)) (+ asdf 2)", "6")]
|
||||
#[case("(fn! add (a b) (+ a b)) (add 1 2)", "3")]
|
||||
#[case("(let* (a 2) (+ a 2))", "4")]
|
||||
#[case("(define (asdf (+ 2 2))) (+ asdf 2)", "6")]
|
||||
#[case("(define (add (fn* (a b) (+ a b)))) (add 1 2)", "3")]
|
||||
#[case("(let* ((a 2)) (+ a 2))", "4")]
|
||||
// Branching
|
||||
#[case("(if true true false)", "true")]
|
||||
#[case("(if nil true false)", "true")]
|
||||
#[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 false 3)", "()")]
|
||||
// Functions
|
||||
#[case(
|
||||
"(fn! factorial (n) (if
|
||||
"(define (factorial (fn* (n) (if
|
||||
(<= n 1)
|
||||
1
|
||||
(* n (factorial (- n 1)))))
|
||||
(* n (factorial (- n 1)))))))
|
||||
(factorial 5)",
|
||||
"120"
|
||||
)]
|
||||
|
@ -210,16 +294,4 @@ mod test {
|
|||
|
||||
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 evaluator;
|
||||
mod macros;
|
||||
mod node;
|
||||
|
||||
pub use env::Environment;
|
||||
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();
|
||||
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}'")]
|
||||
UnexpectedChar(char),
|
||||
#[error("unexpected end of file")]
|
||||
UnexpectedEof,
|
||||
#[error("string is never closed")]
|
||||
UnterminatedString,
|
||||
#[error("the quote does not have a valid follower node")]
|
||||
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")]
|
||||
Unreachable,
|
||||
|
|
|
@ -13,13 +13,6 @@ pub enum Token {
|
|||
LeftBrace,
|
||||
RightBrace,
|
||||
|
||||
WeirdSign,
|
||||
Apostrophe,
|
||||
Grave,
|
||||
Tilde,
|
||||
Carot,
|
||||
AtSign,
|
||||
|
||||
// Math Operators
|
||||
Plus,
|
||||
Minus,
|
||||
|
@ -40,6 +33,19 @@ pub enum Token {
|
|||
True,
|
||||
False,
|
||||
Nil,
|
||||
|
||||
// Specials
|
||||
Function,
|
||||
Define,
|
||||
Let,
|
||||
Eval,
|
||||
Apply,
|
||||
If,
|
||||
Do,
|
||||
|
||||
Quote,
|
||||
Quasiquote,
|
||||
Unquote,
|
||||
}
|
||||
|
||||
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 {
|
||||
// Weird sign
|
||||
'~' if input.peek().is_some_and(|c| c == &'@') => {
|
||||
// Munch the @
|
||||
input.next();
|
||||
Token::WeirdSign
|
||||
}
|
||||
|
||||
// Negative numbers
|
||||
'-' 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::RightBrace,
|
||||
|
||||
'\'' => Token::Apostrophe,
|
||||
'`' => Token::Grave,
|
||||
'~' => Token::Tilde,
|
||||
'^' => Token::Carot,
|
||||
'@' => Token::AtSign,
|
||||
'\'' => Token::Quote,
|
||||
'`' => Token::Quasiquote,
|
||||
',' => Token::Unquote,
|
||||
|
||||
'+' => Token::Plus,
|
||||
'-' => Token::Minus,
|
||||
|
@ -211,77 +208,16 @@ fn read_ident(input: &mut Peekable<Chars>, first: char) -> Token {
|
|||
"true" => Token::True,
|
||||
"false" => Token::False,
|
||||
"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()),
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
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::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::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::Minus => 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::False => Node::Boolean(false),
|
||||
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))
|
||||
|
@ -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> {
|
||||
let follower_node = match next_statement(tokens)? {
|
||||
Some(node) => node,
|
||||
Some(node) => Box::new(node),
|
||||
None => Err(Error::NoFollowerNode)?,
|
||||
};
|
||||
|
||||
Ok(Node::List(vec![
|
||||
Node::Symbol(quote_type.into()),
|
||||
follower_node,
|
||||
]))
|
||||
let node = match quote_type {
|
||||
"quote" => Node::Quote(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),
|
||||
Boolean(bool),
|
||||
Nil,
|
||||
Void, // XXX: Not sure if this should be a node
|
||||
|
||||
Vector(Vec<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 {
|
||||
|
@ -37,6 +99,7 @@ impl std::fmt::Display for Node {
|
|||
Node::Keyword(val) => write!(f, ":{}", val),
|
||||
Node::String(val) => write!(f, "{}", val),
|
||||
Node::Nil => write!(f, "()"),
|
||||
Node::Void => write!(f, ""),
|
||||
|
||||
Node::Vector(vec) => {
|
||||
let s = vec
|
||||
|
@ -56,6 +119,70 @@ impl std::fmt::Display for Node {
|
|||
|
||||
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