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 {
|
||||
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> {
|
||||
if let Some(val) = self.0.current.borrow().get(ident) {
|
||||
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;
|
||||
|
||||
pub(super) fn standard() -> HashMap<String, Value> {
|
||||
[(
|
||||
"test",
|
||||
inline! {
|
||||
(fn* () (println "Hello, world!"))
|
||||
},
|
||||
)]
|
||||
[
|
||||
(
|
||||
"defmacro",
|
||||
inline! {
|
||||
(macro* (name args body) ~(define (,name (macro* ,args ,body))))
|
||||
},
|
||||
),
|
||||
(
|
||||
"defun",
|
||||
inline! {
|
||||
(macro* (name args body) ~(define (,name (fn* ,args ,body))) )
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), Value::Node(v)))
|
||||
.collect()
|
||||
|
|
|
@ -14,7 +14,15 @@ pub fn eval(env: &Environment, ast: Vec<Node>) -> Result<Vec<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 parameters = body.next().ok_or_else(|| todo!())?;
|
||||
let parameters = read_parameters(parameters.clone())?;
|
||||
|
@ -89,7 +97,7 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
|||
args.collect(),
|
||||
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)?,
|
||||
}
|
||||
}
|
||||
|
@ -183,14 +191,26 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
|||
Node::Quote(node) => *node,
|
||||
Node::Quasiquote(node) => {
|
||||
fn unquote(env: &Environment, node: Node) -> Result<Node> {
|
||||
match node {
|
||||
Node::List(list) => {
|
||||
let list = list
|
||||
macro_rules! unquote {
|
||||
($variant:ident, $list:ident) => {{
|
||||
let list = $list
|
||||
.into_iter()
|
||||
.map(|node| unquote(env, 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 => Ok(node),
|
||||
}
|
||||
|
@ -221,15 +241,10 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
|||
eval_node(env, Node::List(nodes))?
|
||||
}
|
||||
|
||||
Node::Symbol(sym) => match env
|
||||
.get(&sym)
|
||||
Node::Symbol(sym) => env
|
||||
.get_node(&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),
|
||||
},
|
||||
.to_owned(),
|
||||
|
||||
Node::Keyword(_)
|
||||
| Node::Int(_)
|
||||
|
|
|
@ -94,6 +94,7 @@ fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
|
|||
|
||||
'\'' => Token::Quote,
|
||||
'`' => Token::Quasiquote,
|
||||
'~' => Token::Quasiquote,
|
||||
',' => Token::Unquote,
|
||||
|
||||
'+' => Token::Plus,
|
||||
|
@ -215,9 +216,6 @@ fn read_ident(input: &mut Peekable<Chars>, first: char) -> Token {
|
|||
"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()),
|
||||
|
|
|
@ -30,15 +30,6 @@ fn next_statement(tokens: &mut TokenIter) -> Result<Option<Node>> {
|
|||
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 {
|
||||
Token::LeftParen => match tokens.peek().ok_or(Error::UnclosedParenthesis)? {
|
||||
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::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)?,
|
||||
},
|
||||
|
||||
|
@ -155,11 +142,6 @@ fn read_body(tokens: &mut TokenIter) -> Result<Vec<Node>> {
|
|||
|
||||
tokens.next(); // Munch special
|
||||
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)?,
|
||||
|
|
|
@ -100,7 +100,7 @@ impl std::fmt::Display for Node {
|
|||
.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::Macro(body) => write!(f, "(macro* {})", reduce_list(body)),
|
||||
Node::Function(body) => write!(f, "(fn* {})", reduce_list(body)),
|
||||
|
|
Loading…
Reference in a new issue