fix: unquote not being truly recursive
Before we were skipping checks for unquotes within specials, which themselves contain lists. Quote is still broken, funnily enough.
This commit is contained in:
parent
02463197c3
commit
6c08af02a1
6 changed files with 54 additions and 43 deletions
8
mute-interpreter/src/env/mod.rs
vendored
8
mute-interpreter/src/env/mod.rs
vendored
|
@ -34,6 +34,14 @@ pub struct RawEnvironment {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
|
pub fn get_node(&self, ident: &str) -> Option<Node> {
|
||||||
|
match self.get(ident) {
|
||||||
|
Some(Value::NativeFunc(_)) => Some(Node::Symbol(ident.to_string())),
|
||||||
|
Some(Value::Node(val)) => Some(val.clone()),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, ident: &str) -> Option<Value> {
|
pub fn get(&self, ident: &str) -> Option<Value> {
|
||||||
if let Some(val) = self.0.current.borrow().get(ident) {
|
if let Some(val) = self.0.current.borrow().get(ident) {
|
||||||
return Some(val.clone());
|
return Some(val.clone());
|
||||||
|
|
20
mute-interpreter/src/env/standard.rs
vendored
20
mute-interpreter/src/env/standard.rs
vendored
|
@ -6,12 +6,20 @@ use mute_parser::Node;
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
pub(super) fn standard() -> HashMap<String, Value> {
|
pub(super) fn standard() -> HashMap<String, Value> {
|
||||||
[(
|
[
|
||||||
"test",
|
(
|
||||||
inline! {
|
"defmacro",
|
||||||
(fn* () (println "Hello, world!"))
|
inline! {
|
||||||
},
|
(macro* (name args body) ~(define (,name (macro* ,args ,body))))
|
||||||
)]
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"defun",
|
||||||
|
inline! {
|
||||||
|
(macro* (name args body) ~(define (,name (fn* ,args ,body))) )
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k.to_string(), Value::Node(v)))
|
.map(|(k, v)| (k.to_string(), Value::Node(v)))
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -14,7 +14,15 @@ pub fn eval(env: &Environment, ast: Vec<Node>) -> Result<Vec<Node>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Node::List(list) = &node {
|
if let Node::List(list) = &node {
|
||||||
if let Some(Node::Macro(body)) = list.first() {
|
let first = match list.first() {
|
||||||
|
Some(Node::Symbol(ident)) => match env.get_node(ident) {
|
||||||
|
m @ Some(Node::Macro(_)) => m,
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
node => node.cloned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(Node::Macro(body)) = first {
|
||||||
let mut body = body.iter();
|
let mut body = body.iter();
|
||||||
let parameters = body.next().ok_or_else(|| todo!())?;
|
let parameters = body.next().ok_or_else(|| todo!())?;
|
||||||
let parameters = read_parameters(parameters.clone())?;
|
let parameters = read_parameters(parameters.clone())?;
|
||||||
|
@ -89,7 +97,7 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
args.collect(),
|
args.collect(),
|
||||||
body.cloned().collect::<Vec<Node>>(),
|
body.cloned().collect::<Vec<Node>>(),
|
||||||
)? {
|
)? {
|
||||||
nodes if nodes.len() == 1 => nodes[0].to_owned(),
|
nodes if nodes.len() == 1 => eval_node(env, nodes[0].to_owned())?,
|
||||||
_ => Err(Error::MacroExpansion)?,
|
_ => Err(Error::MacroExpansion)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,14 +191,26 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
Node::Quote(node) => *node,
|
Node::Quote(node) => *node,
|
||||||
Node::Quasiquote(node) => {
|
Node::Quasiquote(node) => {
|
||||||
fn unquote(env: &Environment, node: Node) -> Result<Node> {
|
fn unquote(env: &Environment, node: Node) -> Result<Node> {
|
||||||
match node {
|
macro_rules! unquote {
|
||||||
Node::List(list) => {
|
($variant:ident, $list:ident) => {{
|
||||||
let list = list
|
let list = $list
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|node| unquote(env, node))
|
.map(|node| unquote(env, node))
|
||||||
.collect::<Result<Vec<Node>>>()?;
|
.collect::<Result<Vec<Node>>>()?;
|
||||||
Ok(Node::List(list))
|
Ok(Node::$variant(list))
|
||||||
}
|
}};
|
||||||
|
}
|
||||||
|
match node {
|
||||||
|
Node::List(list) => unquote!(List, list),
|
||||||
|
Node::Define(list) => unquote!(Define, list),
|
||||||
|
Node::Let(list) => unquote!(Let, list),
|
||||||
|
Node::Function(list) => unquote!(Function, list),
|
||||||
|
Node::Macro(list) => unquote!(Macro, list),
|
||||||
|
Node::If(list) => unquote!(If, list),
|
||||||
|
Node::Do(list) => unquote!(Do, list),
|
||||||
|
Node::Eval(list) => unquote!(Do, list),
|
||||||
|
Node::Apply(list) => unquote!(Apply, list),
|
||||||
|
|
||||||
Node::Unquote(_) => eval_node(env, node),
|
Node::Unquote(_) => eval_node(env, node),
|
||||||
node => Ok(node),
|
node => Ok(node),
|
||||||
}
|
}
|
||||||
|
@ -221,15 +241,10 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
eval_node(env, Node::List(nodes))?
|
eval_node(env, Node::List(nodes))?
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::Symbol(sym) => match env
|
Node::Symbol(sym) => env
|
||||||
.get(&sym)
|
.get_node(&sym)
|
||||||
.ok_or_else(|| Error::NotInEnv(sym.clone()))?
|
.ok_or_else(|| Error::NotInEnv(sym.clone()))?
|
||||||
.to_owned()
|
.to_owned(),
|
||||||
{
|
|
||||||
crate::env::Value::Node(node) => node,
|
|
||||||
// HACK: This feels so wrong
|
|
||||||
crate::env::Value::NativeFunc(_) => Node::Symbol(sym),
|
|
||||||
},
|
|
||||||
|
|
||||||
Node::Keyword(_)
|
Node::Keyword(_)
|
||||||
| Node::Int(_)
|
| Node::Int(_)
|
||||||
|
|
|
@ -94,6 +94,7 @@ fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
|
||||||
|
|
||||||
'\'' => Token::Quote,
|
'\'' => Token::Quote,
|
||||||
'`' => Token::Quasiquote,
|
'`' => Token::Quasiquote,
|
||||||
|
'~' => Token::Quasiquote,
|
||||||
',' => Token::Unquote,
|
',' => Token::Unquote,
|
||||||
|
|
||||||
'+' => Token::Plus,
|
'+' => Token::Plus,
|
||||||
|
@ -215,9 +216,6 @@ fn read_ident(input: &mut Peekable<Chars>, first: char) -> Token {
|
||||||
"let*" => Token::Let,
|
"let*" => Token::Let,
|
||||||
"eval" => Token::Eval,
|
"eval" => Token::Eval,
|
||||||
"apply" => Token::Apply,
|
"apply" => Token::Apply,
|
||||||
"quote" => Token::Quote,
|
|
||||||
"quasiquote" => Token::Quasiquote,
|
|
||||||
"unquote" => Token::Unquote,
|
|
||||||
"if" => Token::If,
|
"if" => Token::If,
|
||||||
"do" => Token::Do,
|
"do" => Token::Do,
|
||||||
ident => Token::Ident(ident.to_owned()),
|
ident => Token::Ident(ident.to_owned()),
|
||||||
|
|
|
@ -30,15 +30,6 @@ 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 => Node::Function(read_body(tokens)?),
|
Token::Function => Node::Function(read_body(tokens)?),
|
||||||
|
@ -50,10 +41,6 @@ fn next_statement(tokens: &mut TokenIter) -> Result<Option<Node>> {
|
||||||
Token::If => Node::If(read_body(tokens)?),
|
Token::If => Node::If(read_body(tokens)?),
|
||||||
Token::Do => Node::Do(read_body(tokens)?),
|
Token::Do => Node::Do(read_body(tokens)?),
|
||||||
|
|
||||||
Token::Quote => read_quote_operator!("quote"),
|
|
||||||
Token::Quasiquote => read_quote_operator!("quasiquote"),
|
|
||||||
Token::Unquote => read_quote_operator!("unquote"),
|
|
||||||
|
|
||||||
_ => read_list(tokens, Token::RightParen)?,
|
_ => read_list(tokens, Token::RightParen)?,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -155,11 +142,6 @@ fn read_body(tokens: &mut TokenIter) -> Result<Vec<Node>> {
|
||||||
|
|
||||||
tokens.next(); // Munch special
|
tokens.next(); // Munch special
|
||||||
while tokens.peek() != Some(&Token::RightParen) {
|
while tokens.peek() != Some(&Token::RightParen) {
|
||||||
if let Some(node) = next_statement(tokens)? {
|
|
||||||
body.push(node);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match next_statement(tokens)? {
|
match next_statement(tokens)? {
|
||||||
Some(node) => body.push(node),
|
Some(node) => body.push(node),
|
||||||
None => Err(Error::UnclosedParenthesis)?,
|
None => Err(Error::UnclosedParenthesis)?,
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl std::fmt::Display for Node {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
),
|
),
|
||||||
|
|
||||||
Node::Define(body) => write!(f, "(define! {})", reduce_list(body)),
|
Node::Define(body) => write!(f, "(define {})", reduce_list(body)),
|
||||||
Node::Let(body) => write!(f, "(let* {})", reduce_list(body)),
|
Node::Let(body) => write!(f, "(let* {})", reduce_list(body)),
|
||||||
Node::Macro(body) => write!(f, "(macro* {})", reduce_list(body)),
|
Node::Macro(body) => write!(f, "(macro* {})", reduce_list(body)),
|
||||||
Node::Function(body) => write!(f, "(fn* {})", reduce_list(body)),
|
Node::Function(body) => write!(f, "(fn* {})", reduce_list(body)),
|
||||||
|
|
Loading…
Reference in a new issue