misc: Chesterton's Fence
Everything is a list in lisp for a reason 🙃
This commit is contained in:
parent
047db2b188
commit
02463197c3
13 changed files with 245 additions and 486 deletions
7
mute-interpreter/src/env/core.rs
vendored
7
mute-interpreter/src/env/core.rs
vendored
|
@ -54,6 +54,13 @@ macro_rules! ordering {
|
||||||
|
|
||||||
pub(super) fn core() -> HashMap<String, Value> {
|
pub(super) fn core() -> HashMap<String, Value> {
|
||||||
[
|
[
|
||||||
|
(
|
||||||
|
"dbg",
|
||||||
|
NativeFunc(|args| {
|
||||||
|
dbg!(args);
|
||||||
|
Node::Void
|
||||||
|
}),
|
||||||
|
),
|
||||||
// Arithmetic operations
|
// Arithmetic operations
|
||||||
("+", arithmetic!(+)),
|
("+", arithmetic!(+)),
|
||||||
("-", arithmetic!(-)),
|
("-", arithmetic!(-)),
|
||||||
|
|
|
@ -26,6 +26,8 @@ pub enum Error {
|
||||||
#[error("system error {0}")]
|
#[error("system error {0}")]
|
||||||
SystemError(String),
|
SystemError(String),
|
||||||
|
|
||||||
|
#[error("INTERNAL: Incorrect macro expansion, please file a bug report")]
|
||||||
|
MacroExpansion,
|
||||||
#[error("please file a bug report")]
|
#[error("please file a bug report")]
|
||||||
Unreachable,
|
Unreachable,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,29 @@ 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 {
|
let mut ast = ast.into_iter();
|
||||||
|
loop {
|
||||||
|
let node = match ast.next() {
|
||||||
|
Some(node) => node,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Node::List(list) = &node {
|
||||||
|
if let Some(Node::Macro(body)) = list.first() {
|
||||||
|
let mut body = body.iter();
|
||||||
|
let parameters = body.next().ok_or_else(|| todo!())?;
|
||||||
|
let parameters = read_parameters(parameters.clone())?;
|
||||||
|
let args = list.clone().into_iter().skip(1).collect::<Vec<Node>>();
|
||||||
|
|
||||||
|
ast = expand_macro(env, parameters.to_owned(), args, body.cloned().collect())?
|
||||||
|
.into_iter()
|
||||||
|
.chain(ast)
|
||||||
|
.collect::<Vec<Node>>()
|
||||||
|
.into_iter();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let res = eval_node(env, node)?;
|
let res = eval_node(env, node)?;
|
||||||
if let Node::Void = res {
|
if let Node::Void = res {
|
||||||
continue;
|
continue;
|
||||||
|
@ -31,7 +53,11 @@ 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::Function { parameters, body } => {
|
Node::Function(body) => {
|
||||||
|
let mut body = body.iter();
|
||||||
|
let parameters = body.next().ok_or_else(|| todo!())?;
|
||||||
|
let parameters = read_parameters(parameters.clone())?;
|
||||||
|
|
||||||
let args = list;
|
let args = list;
|
||||||
if args.len() != parameters.len() {
|
if args.len() != parameters.len() {
|
||||||
Err(Error::MismatchedArgCount(parameters.len(), args.len()))?
|
Err(Error::MismatchedArgCount(parameters.len(), args.len()))?
|
||||||
|
@ -44,13 +70,30 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
let records = parameters.iter().map(|k| k.to_owned()).zip(args).collect();
|
let records = parameters.iter().map(|k| k.to_owned()).zip(args).collect();
|
||||||
let env = env.wrap(records);
|
let env = env.wrap(records);
|
||||||
|
|
||||||
let res = eval(&env, (*body).clone())?;
|
let res = eval(&env, body.cloned().collect::<Vec<Node>>())?;
|
||||||
match res.last() {
|
match res.last() {
|
||||||
Some(node) => node.to_owned(),
|
Some(node) => node.to_owned(),
|
||||||
None => Node::Void,
|
None => Node::Void,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node::Macro(body) => {
|
||||||
|
let mut body = body.iter();
|
||||||
|
let parameters = body.next().ok_or_else(|| todo!())?;
|
||||||
|
let parameters = read_parameters(parameters.clone())?;
|
||||||
|
|
||||||
|
let args = list;
|
||||||
|
match expand_macro(
|
||||||
|
env,
|
||||||
|
parameters.clone(),
|
||||||
|
args.collect(),
|
||||||
|
body.cloned().collect::<Vec<Node>>(),
|
||||||
|
)? {
|
||||||
|
nodes if nodes.len() == 1 => nodes[0].to_owned(),
|
||||||
|
_ => Err(Error::MacroExpansion)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HACK: This feels sooooooo wrong but it works
|
// HACK: This feels sooooooo wrong but it works
|
||||||
// Native Functions /should/ be their own type, but they're not.
|
// Native Functions /should/ be their own type, but they're not.
|
||||||
Node::Symbol(sym) => {
|
Node::Symbol(sym) => {
|
||||||
|
@ -71,35 +114,68 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::Define { args } => {
|
Node::Define(body) => {
|
||||||
args.into_iter()
|
body.into_iter()
|
||||||
.map(|(k, v)| Ok((k.to_owned(), eval_node(env, v)?)))
|
.map(|n| match n {
|
||||||
|
Node::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())),
|
||||||
|
Node::List(list) => Err(Error::MismatchedArgCount(2, list.len()))?,
|
||||||
|
_ => todo!(),
|
||||||
|
})
|
||||||
|
.map(|n| match n {
|
||||||
|
Ok((Node::Symbol(k), v)) => Ok((k.to_owned(), eval_node(env, v)?)),
|
||||||
|
Ok(_) => todo!(),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
})
|
||||||
.collect::<Result<Vec<(String, Node)>>>()?
|
.collect::<Result<Vec<(String, Node)>>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.for_each(|(k, v)| env.set(k, v));
|
.for_each(|(k, v)| env.set(k, v));
|
||||||
|
|
||||||
Node::Void
|
Node::Void
|
||||||
}
|
}
|
||||||
Node::Let { args, body } => {
|
Node::Let(body) => {
|
||||||
let args = args
|
let mut body = body.into_iter();
|
||||||
.into_iter()
|
let args = match body.next().ok_or_else(|| todo!())? {
|
||||||
.map(|(k, v)| Ok((k.to_owned(), eval_node(env, v)?)))
|
Node::List(list) => list
|
||||||
.collect::<Result<Vec<(String, Node)>>>()?;
|
.into_iter()
|
||||||
|
.map(|n| match n {
|
||||||
|
Node::List(list) if list.len() == 2 => {
|
||||||
|
Ok((list[0].clone(), list[1].clone()))
|
||||||
|
}
|
||||||
|
Node::List(list) => Err(Error::MismatchedArgCount(2, list.len()))?,
|
||||||
|
_ => todo!(),
|
||||||
|
})
|
||||||
|
.map(|n| match n {
|
||||||
|
Ok((Node::Symbol(k), v)) => Ok((k.to_owned(), eval_node(env, v)?)),
|
||||||
|
Ok(_) => todo!(),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<(String, Node)>>>()?,
|
||||||
|
_ => Err(Error::MismatchedArgCount(1, 1))?,
|
||||||
|
};
|
||||||
|
let body = body.collect();
|
||||||
|
|
||||||
let env = env.wrap(args);
|
let env = env.wrap(args);
|
||||||
eval(&env, body)?.last().unwrap().to_owned()
|
eval(&env, body)?.last().unwrap().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::If {
|
Node::If(body) => {
|
||||||
cond,
|
if body.len() != 2 && body.len() != 3 {
|
||||||
consequence,
|
Err(Error::MismatchedArgCount(3, body.len()))?
|
||||||
alternative,
|
}
|
||||||
} => {
|
|
||||||
let cond = eval_node(env, *cond)?;
|
let mut body = body.iter();
|
||||||
|
let cond = body.next().expect("arg count verified above").clone();
|
||||||
|
let consequence = body.next().expect("arg count verified above").clone();
|
||||||
|
let alternative = match body.next() {
|
||||||
|
Some(node) => node.clone(),
|
||||||
|
None => Node::Nil,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cond = eval_node(env, cond)?;
|
||||||
|
|
||||||
match cond {
|
match cond {
|
||||||
Node::Boolean(true) => eval_node(env, *consequence)?,
|
Node::Boolean(true) => eval_node(env, consequence)?,
|
||||||
Node::Boolean(false) => eval_node(env, *alternative)?,
|
Node::Boolean(false) => eval_node(env, alternative)?,
|
||||||
_ => Err(Error::ExpectedBoolean)?,
|
_ => Err(Error::ExpectedBoolean)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,16 +200,25 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
}
|
}
|
||||||
Node::Unquote(node) => eval_node(env, *node)?,
|
Node::Unquote(node) => eval_node(env, *node)?,
|
||||||
|
|
||||||
Node::Do { body } => eval(env, body)?.last().unwrap().to_owned(),
|
Node::Do(body) => eval(env, body)?.last().unwrap().to_owned(),
|
||||||
Node::Eval { body } => eval_node(env, eval_node(env, *body)?)?,
|
// FIXME: check for empty body
|
||||||
Node::Apply { func, args } => {
|
Node::Eval(body) => eval_node(env, eval_node(env, body[0].clone())?)?,
|
||||||
let mut list = vec![*func];
|
Node::Apply(body) => {
|
||||||
match eval_node(env, *args)? {
|
let mut body = body.into_iter();
|
||||||
Node::List(args) => list.extend(args),
|
let operator = eval_node(env, body.next().ok_or(Error::InvalidOperator)?)?;
|
||||||
_ => Err(Error::ExpectedList)?,
|
|
||||||
}
|
|
||||||
|
|
||||||
eval_node(env, Node::List(list))?
|
let args = match body.next() {
|
||||||
|
Some(node) => match eval_node(env, node)? {
|
||||||
|
Node::List(list) => list,
|
||||||
|
_ => todo!(),
|
||||||
|
},
|
||||||
|
None => return Err(Error::MismatchedArgCount(2, 1)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut nodes = vec![operator];
|
||||||
|
nodes.extend(args);
|
||||||
|
|
||||||
|
eval_node(env, Node::List(nodes))?
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::Symbol(sym) => match env
|
Node::Symbol(sym) => match env
|
||||||
|
@ -163,6 +248,39 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_parameters(node: Node) -> Result<Vec<String>> {
|
||||||
|
match node {
|
||||||
|
Node::List(parameters) => parameters
|
||||||
|
.iter()
|
||||||
|
.map(|node| match node {
|
||||||
|
Node::Symbol(name) => Ok(name.clone()),
|
||||||
|
_ => todo!(),
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<String>>>(),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_macro(
|
||||||
|
env: &Environment,
|
||||||
|
parameters: Vec<String>,
|
||||||
|
args: Vec<Node>,
|
||||||
|
body: Vec<Node>,
|
||||||
|
) -> Result<Vec<Node>> {
|
||||||
|
if args.len() != parameters.len() {
|
||||||
|
Err(Error::MismatchedArgCount(parameters.len(), args.len()))?
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = parameters
|
||||||
|
.into_iter()
|
||||||
|
.map(|param| param.to_owned())
|
||||||
|
.zip(args)
|
||||||
|
.collect::<Vec<(String, Node)>>();
|
||||||
|
|
||||||
|
let env = env.wrap(args);
|
||||||
|
eval(&env, body)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use mute_parser::parse_str;
|
use mute_parser::parse_str;
|
||||||
|
@ -194,6 +312,8 @@ mod test {
|
||||||
#[case("(quote (1 2 (4 5) 6 7))", "(1 2 (4 5) 6 7)")]
|
#[case("(quote (1 2 (4 5) 6 7))", "(1 2 (4 5) 6 7)")]
|
||||||
#[case("(quasiquote (1 2 (+ 3 4) 5 6))", "(1 2 (+ 3 4) 5 6)")]
|
#[case("(quasiquote (1 2 (+ 3 4) 5 6))", "(1 2 (+ 3 4) 5 6)")]
|
||||||
#[case("(quasiquote (1 2 (unquote(+ 3 4)) 5 6))", "(1 2 7 5 6)")]
|
#[case("(quasiquote (1 2 (unquote(+ 3 4)) 5 6))", "(1 2 7 5 6)")]
|
||||||
|
// Macros
|
||||||
|
#[case("((macro* (var) `(str ,var)) 2)", "2")]
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
#[case("(+ 1 2)", "3")]
|
#[case("(+ 1 2)", "3")]
|
||||||
#[case("(- 5 1)", "4")]
|
#[case("(- 5 1)", "4")]
|
||||||
|
@ -289,6 +409,7 @@ mod test {
|
||||||
#[case("(str (list 1 2 3))", "(1 2 3)")]
|
#[case("(str (list 1 2 3))", "(1 2 3)")]
|
||||||
fn test_evaluator(#[case] input: &str, #[case] expected: &str) {
|
fn test_evaluator(#[case] input: &str, #[case] expected: &str) {
|
||||||
dbg!(input);
|
dbg!(input);
|
||||||
|
|
||||||
let env = Environment::default();
|
let env = Environment::default();
|
||||||
let ast = parse_str(input).unwrap();
|
let ast = parse_str(input).unwrap();
|
||||||
let res = crate::eval(&env, ast)
|
let res = crate::eval(&env, ast)
|
||||||
|
|
|
@ -18,15 +18,7 @@ pub fn inline(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
fn format_node(node: Node) -> String {
|
fn format_node(node: Node) -> String {
|
||||||
match node {
|
match node {
|
||||||
Node::List(v) => {
|
Node::List(v) => format!("Node::List({})", reduce_list(v)),
|
||||||
let vec = v
|
|
||||||
.into_iter()
|
|
||||||
.map(format_node)
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
format!("Node::List(vec![{vec}])")
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Symbol(s) => format!("Node::Symbol(\"{s}\".to_string())"),
|
Node::Symbol(s) => format!("Node::Symbol(\"{s}\".to_string())"),
|
||||||
Node::Keyword(k) => format!("Node::Keyword(\"{k}\".to_string())"),
|
Node::Keyword(k) => format!("Node::Keyword(\"{k}\".to_string())"),
|
||||||
|
@ -39,103 +31,36 @@ fn format_node(node: Node) -> String {
|
||||||
|
|
||||||
Node::Error(e) => format!("Node::Error(\"{e}\".to_string())"),
|
Node::Error(e) => format!("Node::Error(\"{e}\".to_string())"),
|
||||||
|
|
||||||
Node::Vector(v) => {
|
Node::Vector(_) => todo!(),
|
||||||
let vec = v
|
Node::Map(_) => todo!(),
|
||||||
.into_iter()
|
|
||||||
.map(format_node)
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
format!("Node::Vector(vec![{vec}])")
|
|
||||||
}
|
|
||||||
Node::Map(v) => {
|
|
||||||
let vec = v
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, format_node(v)))
|
|
||||||
.map(|(k, v)| format!("({k}, {v})"))
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
format!("Node::Hashmap(vec![{vec}].into())")
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Define { args } => {
|
|
||||||
let args = args
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| format!("(\"{}\".to_string(), {})", k, format_node(v)))
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
format!("Node::Define(vec![{args}])")
|
|
||||||
}
|
|
||||||
Node::Let { args, body } => {
|
|
||||||
let args = args
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, format_node(v)))
|
|
||||||
.fold(String::new(), |lhs, (k, v)| {
|
|
||||||
format!("{lhs}, ({k}.to_string(), {v})")
|
|
||||||
});
|
|
||||||
|
|
||||||
let body = body
|
|
||||||
.into_iter()
|
|
||||||
.map(format_node)
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
format!("Node::Let(vec![{}], vec![{}]", args, body)
|
|
||||||
}
|
|
||||||
Node::Function { parameters, body } => {
|
|
||||||
let parameters = parameters
|
|
||||||
.into_iter()
|
|
||||||
.map(|param| format!("\"{}\".to_string()", param))
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let body = body
|
|
||||||
.into_iter()
|
|
||||||
.map(format_node)
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
format!(
|
|
||||||
"Node::Function {{ parameters: vec![{}], body: vec![{}] }}",
|
|
||||||
parameters, body
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Node::Macro { .. } => todo!(),
|
|
||||||
Node::If {
|
|
||||||
cond,
|
|
||||||
consequence,
|
|
||||||
alternative,
|
|
||||||
} => format!(
|
|
||||||
"Node::If(Box::new({}), Box::new({}), Box::new({}))",
|
|
||||||
format_node(*cond),
|
|
||||||
format_node(*consequence),
|
|
||||||
format_node(*alternative)
|
|
||||||
),
|
|
||||||
|
|
||||||
|
// Quoting
|
||||||
Node::Quote(node) => format!("Node::Quote(Box::new({}))", format_node(*node)),
|
Node::Quote(node) => format!("Node::Quote(Box::new({}))", format_node(*node)),
|
||||||
Node::Quasiquote(node) => format!("Node::Quasiquote(Box::new({}))", format_node(*node)),
|
Node::Quasiquote(node) => format!("Node::Quasiquote(Box::new({}))", format_node(*node)),
|
||||||
Node::Unquote(node) => format!("Node::Unquote(Box::new({}))", format_node(*node)),
|
Node::Unquote(node) => format!("Node::Unquote(Box::new({}))", format_node(*node)),
|
||||||
|
|
||||||
Node::Do { body } => {
|
// Specials
|
||||||
let vec = body
|
Node::Define(body) => format!("Node::Define({})", reduce_list(body)),
|
||||||
.into_iter()
|
Node::Let(body) => format!("Node::Let({})", reduce_list(body)),
|
||||||
.map(format_node)
|
Node::Function(body) => format!("Node::Function({})", reduce_list(body)),
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
Node::Macro(body) => format!("Node::Macro({})", reduce_list(body)),
|
||||||
.unwrap_or_default();
|
Node::If(body) => format!("Node::If({})", reduce_list(body)),
|
||||||
|
Node::Do(body) => format!("Node::Do({})", reduce_list(body)),
|
||||||
format!("Node::Do(vec![{vec}])")
|
Node::Eval(body) => format!("Node::Eval({})", reduce_list(body)),
|
||||||
}
|
Node::Apply(body) => format!("Node::Apply({})", reduce_list(body),),
|
||||||
|
|
||||||
Node::Eval { body } => format!("Node::Eval(Box::new({}))", format_node(*body)),
|
|
||||||
Node::Apply { func, args } => format!(
|
|
||||||
"Node::Apply(Box::new({}), Box::new({}))",
|
|
||||||
format_node(*func),
|
|
||||||
format_node(*args)
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reduce_list(node: Vec<Node>) -> String {
|
||||||
|
let vec = node
|
||||||
|
.into_iter()
|
||||||
|
.map(format_node)
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
format!("vec![{vec}]")
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
source: mute-macros/src/lib.rs
|
source: mute-macros/src/lib.rs
|
||||||
expression: res
|
expression: res
|
||||||
---
|
---
|
||||||
Node::Define(vec![("a".to_string(), Node::Int(1)), ("b".to_string(), Node::Int(2))])
|
Node::Define(vec![Node::List(vec![Node::Symbol("a".to_string()), Node::Int(1)]), Node::List(vec![Node::Symbol("b".to_string()), Node::Int(2)])])
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
source: mute-macros/src/lib.rs
|
source: mute-macros/src/lib.rs
|
||||||
expression: res
|
expression: res
|
||||||
---
|
---
|
||||||
Node::Define(vec![("a".to_string(), Node::Int(1))])
|
Node::Define(vec![Node::List(vec![Node::Symbol("a".to_string()), Node::Int(1)])])
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
source: mute-macros/src/lib.rs
|
source: mute-macros/src/lib.rs
|
||||||
expression: res
|
expression: res
|
||||||
---
|
---
|
||||||
Node::Function { parameters: vec![], body: vec![Node::List(vec![Node::Symbol("+".to_string()), Node::Int(1), Node::Int(1)])] }
|
Node::Function(vec![Node::List(vec![]), Node::List(vec![Node::Symbol("+".to_string()), Node::Int(1), Node::Int(1)])])
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
source: mute-macros/src/lib.rs
|
source: mute-macros/src/lib.rs
|
||||||
expression: res
|
expression: res
|
||||||
---
|
---
|
||||||
Node::Function { parameters: vec!["a".to_string(), "b".to_string()], body: vec![Node::List(vec![Node::Symbol("+".to_string()), Node::Symbol("a".to_string()), Node::Symbol("b".to_string())])] }
|
Node::Function(vec![Node::List(vec![Node::Symbol("a".to_string()), Node::Symbol("b".to_string())]), Node::List(vec![Node::Symbol("+".to_string()), Node::Symbol("a".to_string()), Node::Symbol("b".to_string())])])
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
source: mute-macros/src/lib.rs
|
source: mute-macros/src/lib.rs
|
||||||
expression: res
|
expression: res
|
||||||
---
|
---
|
||||||
Node::If(Box::new(Node::Bool(true)), Box::new(Node::Int(1)), Box::new(Node::Int(2)))
|
Node::If(vec![Node::Bool(true), Node::Int(1), Node::Int(2)])
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub enum Token {
|
||||||
Nil,
|
Nil,
|
||||||
|
|
||||||
// Specials
|
// Specials
|
||||||
|
Macro,
|
||||||
Function,
|
Function,
|
||||||
Define,
|
Define,
|
||||||
Let,
|
Let,
|
||||||
|
@ -209,6 +210,7 @@ fn read_ident(input: &mut Peekable<Chars>, first: char) -> Token {
|
||||||
"false" => Token::False,
|
"false" => Token::False,
|
||||||
"nil" => Token::Nil,
|
"nil" => Token::Nil,
|
||||||
"fn*" => Token::Function,
|
"fn*" => Token::Function,
|
||||||
|
"macro*" => Token::Macro,
|
||||||
"define" => Token::Define,
|
"define" => Token::Define,
|
||||||
"let*" => Token::Let,
|
"let*" => Token::Let,
|
||||||
"eval" => Token::Eval,
|
"eval" => Token::Eval,
|
||||||
|
|
|
@ -30,33 +30,30 @@ fn next_statement(tokens: &mut TokenIter) -> Result<Option<Node>> {
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
macro_rules! read_quote_operator {
|
||||||
|
($type:expr) => {{
|
||||||
|
tokens.next(); // Munch opened
|
||||||
|
let quote = read_quote(tokens, $type)?;
|
||||||
|
tokens.next(); // Munch closed
|
||||||
|
quote
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
let node = match tok {
|
let node = match tok {
|
||||||
Token::LeftParen => match tokens.peek().ok_or(Error::UnclosedParenthesis)? {
|
Token::LeftParen => match tokens.peek().ok_or(Error::UnclosedParenthesis)? {
|
||||||
Token::Function => read_function(tokens)?,
|
Token::Function => Node::Function(read_body(tokens)?),
|
||||||
Token::Define => read_define(tokens)?,
|
Token::Macro => Node::Macro(read_body(tokens)?),
|
||||||
Token::Let => read_let(tokens)?,
|
Token::Define => Node::Define(read_body(tokens)?),
|
||||||
Token::Eval => read_eval(tokens)?,
|
Token::Let => Node::Let(read_body(tokens)?),
|
||||||
Token::Apply => read_apply(tokens)?,
|
Token::Eval => Node::Eval(read_body(tokens)?),
|
||||||
Token::If => read_if(tokens)?,
|
Token::Apply => Node::Apply(read_body(tokens)?),
|
||||||
Token::Do => read_do(tokens)?,
|
Token::If => Node::If(read_body(tokens)?),
|
||||||
Token::Quote => {
|
Token::Do => Node::Do(read_body(tokens)?),
|
||||||
tokens.next(); // Munch (
|
|
||||||
let quote = read_quote(tokens, "quote")?;
|
Token::Quote => read_quote_operator!("quote"),
|
||||||
tokens.next(); // Munch )
|
Token::Quasiquote => read_quote_operator!("quasiquote"),
|
||||||
quote
|
Token::Unquote => read_quote_operator!("unquote"),
|
||||||
}
|
|
||||||
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)?,
|
_ => read_list(tokens, Token::RightParen)?,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -92,6 +89,7 @@ fn next_statement(tokens: &mut TokenIter) -> Result<Option<Node>> {
|
||||||
Token::Unquote => read_quote(tokens, "unquote")?,
|
Token::Unquote => read_quote(tokens, "unquote")?,
|
||||||
|
|
||||||
Token::Function
|
Token::Function
|
||||||
|
| Token::Macro
|
||||||
| Token::Define
|
| Token::Define
|
||||||
| Token::Let
|
| Token::Let
|
||||||
| Token::Eval
|
| Token::Eval
|
||||||
|
@ -152,216 +150,10 @@ fn read_quote(tokens: &mut TokenIter, quote_type: &str) -> Result<Node> {
|
||||||
Ok(node)
|
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.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>> {
|
fn read_body(tokens: &mut TokenIter) -> Result<Vec<Node>> {
|
||||||
let mut body = Vec::new();
|
let mut body = Vec::new();
|
||||||
|
|
||||||
|
tokens.next(); // Munch special
|
||||||
while tokens.peek() != Some(&Token::RightParen) {
|
while tokens.peek() != Some(&Token::RightParen) {
|
||||||
if let Some(node) = next_statement(tokens)? {
|
if let Some(node) = next_statement(tokens)? {
|
||||||
body.push(node);
|
body.push(node);
|
||||||
|
@ -373,6 +165,7 @@ fn read_body(tokens: &mut TokenIter) -> Result<Vec<Node>> {
|
||||||
None => Err(Error::UnclosedParenthesis)?,
|
None => Err(Error::UnclosedParenthesis)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tokens.next(); // Munch closing parenthesis
|
||||||
|
|
||||||
Ok(body)
|
Ok(body)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,41 +19,20 @@ pub enum Node {
|
||||||
Map(HashMap<String, Node>),
|
Map(HashMap<String, Node>),
|
||||||
|
|
||||||
// Specials
|
// Specials
|
||||||
Define {
|
// TODO: Specials should point to a LinkedList
|
||||||
args: Vec<(String, Node)>,
|
Define(Vec<Node>),
|
||||||
},
|
Let(Vec<Node>),
|
||||||
Let {
|
Function(Vec<Node>),
|
||||||
args: Vec<(String, Node)>,
|
Macro(Vec<Node>),
|
||||||
body: Vec<Node>,
|
If(Vec<Node>),
|
||||||
},
|
|
||||||
Function {
|
|
||||||
parameters: Vec<String>,
|
|
||||||
body: Vec<Node>,
|
|
||||||
},
|
|
||||||
Macro {
|
|
||||||
parameters: Vec<String>,
|
|
||||||
body: Vec<Node>,
|
|
||||||
},
|
|
||||||
If {
|
|
||||||
cond: Box<Node>,
|
|
||||||
consequence: Box<Node>,
|
|
||||||
alternative: Box<Node>,
|
|
||||||
},
|
|
||||||
|
|
||||||
Quote(Box<Node>),
|
Quote(Box<Node>),
|
||||||
Quasiquote(Box<Node>),
|
Quasiquote(Box<Node>),
|
||||||
Unquote(Box<Node>),
|
Unquote(Box<Node>),
|
||||||
|
|
||||||
Do {
|
Do(Vec<Node>),
|
||||||
body: Vec<Node>,
|
Eval(Vec<Node>),
|
||||||
},
|
Apply(Vec<Node>),
|
||||||
Eval {
|
|
||||||
body: Box<Node>,
|
|
||||||
},
|
|
||||||
Apply {
|
|
||||||
func: Box<Node>,
|
|
||||||
args: Box<Node>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
|
@ -111,106 +90,36 @@ impl std::fmt::Display for Node {
|
||||||
|
|
||||||
Node::Error(val) => write!(f, "error: {}", val),
|
Node::Error(val) => write!(f, "error: {}", val),
|
||||||
|
|
||||||
Node::Vector(vec) => {
|
Node::Vector(body) => write!(f, "[{}]", reduce_list(body)),
|
||||||
let s = vec
|
Node::Map(body) => write!(
|
||||||
.iter()
|
f,
|
||||||
.map(|elem| elem.to_string())
|
"{{{}}}",
|
||||||
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
body.iter()
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
write!(f, "[{s}]")
|
|
||||||
}
|
|
||||||
Node::Map(map) => {
|
|
||||||
let res = map
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| format!("{k}: {v}"))
|
.map(|(k, v)| format!("{k}: {v}"))
|
||||||
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
write!(f, "{{{res}}}")
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Define { args } => {
|
|
||||||
let res = args
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| format!("({k} {v})"))
|
|
||||||
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
.expect("parser does not allow empty define statements");
|
.unwrap_or_default()
|
||||||
|
),
|
||||||
|
|
||||||
write!(f, "(define {res})")
|
Node::Define(body) => write!(f, "(define! {})", reduce_list(body)),
|
||||||
}
|
Node::Let(body) => write!(f, "(let* {})", reduce_list(body)),
|
||||||
|
Node::Macro(body) => write!(f, "(macro* {})", reduce_list(body)),
|
||||||
Node::Let { args, body } => {
|
Node::Function(body) => write!(f, "(fn* {})", reduce_list(body)),
|
||||||
let args = args
|
Node::If(body) => write!(f, "(if {})", reduce_list(body)),
|
||||||
.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::Macro {
|
|
||||||
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::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, "(defmacro ({args}) {body})")
|
|
||||||
}
|
|
||||||
Node::If {
|
|
||||||
cond,
|
|
||||||
consequence,
|
|
||||||
alternative,
|
|
||||||
} => write!(f, "(if {cond} {consequence} {alternative})"),
|
|
||||||
|
|
||||||
Node::Quote(node) => write!(f, "'{node}"),
|
Node::Quote(node) => write!(f, "'{node}"),
|
||||||
Node::Quasiquote(node) => write!(f, "`{node}"),
|
Node::Quasiquote(node) => write!(f, "`{node}"),
|
||||||
Node::Unquote(node) => write!(f, ",{node}"),
|
Node::Unquote(node) => write!(f, ",{node}"),
|
||||||
|
|
||||||
Node::Do { body } => {
|
Node::Do(body) => write!(f, "(do {})", reduce_list(body)),
|
||||||
let body = body
|
Node::Eval(body) => write!(f, "(eval {})", reduce_list(body)),
|
||||||
.iter()
|
Node::Apply(body) => write!(f, "(apply {})", reduce_list(body)),
|
||||||
.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})"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reduce_list(list: &Vec<Node>) -> String {
|
||||||
|
list.iter()
|
||||||
|
.map(|n| n.to_string())
|
||||||
|
.reduce(|lhs, rhs| format!("{lhs} {rhs}"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ fn repl() {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("error: {}", err);
|
println!("error: {}", err);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let res = eval(&env, ast);
|
let res = eval(&env, ast);
|
||||||
|
|
Loading…
Reference in a new issue