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:
Roman Godmaire 2024-05-10 11:21:46 -04:00
parent d8724eb4b7
commit e54d095c5f
11 changed files with 730 additions and 600 deletions

View file

@ -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()
}

View file

@ -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()
}

View file

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

View file

@ -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}")]

View file

@ -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())
}
}

View file

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

View file

@ -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, ""),
}
}
}

View file

@ -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,

View file

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

View file

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

View file

@ -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})"),
}
}
}