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:
Roman Godmaire 2024-05-12 15:00:06 -04:00
parent 02463197c3
commit 6c08af02a1
6 changed files with 54 additions and 43 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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