179 lines
5.5 KiB
Rust
179 lines
5.5 KiB
Rust
|
use mute_parser::{parse_str, Node};
|
||
|
use proc_macro::TokenStream;
|
||
|
|
||
|
#[proc_macro]
|
||
|
pub fn inline(input: TokenStream) -> TokenStream {
|
||
|
let ast = parse_str(input.to_string().as_str()).unwrap();
|
||
|
if ast.len() != 1 {
|
||
|
panic!("Expected a single expression, got multiple");
|
||
|
}
|
||
|
|
||
|
ast.into_iter()
|
||
|
.map(format_node)
|
||
|
.reduce(|lhs, rhs| format!("{lhs}\n{rhs}"))
|
||
|
.unwrap_or("Node::Nil".to_string())
|
||
|
.parse::<TokenStream>()
|
||
|
.unwrap()
|
||
|
}
|
||
|
|
||
|
fn format_node(node: Node) -> String {
|
||
|
match node {
|
||
|
Node::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::Keyword(k) => format!("Node::Keyword(\"{k}\".to_string())"),
|
||
|
Node::Int(i) => format!("Node::Int({i})"),
|
||
|
Node::Float(f) => format!("Node::Float({f})"),
|
||
|
Node::String(s) => format!("Node::String(\"{s}\".to_string())"),
|
||
|
Node::Boolean(b) => format!("Node::Bool({b})"),
|
||
|
Node::Nil => "Node::Nil".to_string(),
|
||
|
Node::Void => "Node::Void".to_string(),
|
||
|
|
||
|
Node::Error(e) => format!("Node::Error(\"{e}\".to_string())"),
|
||
|
|
||
|
Node::Vector(v) => {
|
||
|
let vec = v
|
||
|
.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)
|
||
|
),
|
||
|
|
||
|
Node::Quote(node) => format!("Node::Quote(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::Do { body } => {
|
||
|
let vec = body
|
||
|
.into_iter()
|
||
|
.map(format_node)
|
||
|
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
||
|
.unwrap_or_default();
|
||
|
|
||
|
format!("Node::Do(vec![{vec}])")
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
use super::*;
|
||
|
use rstest::rstest;
|
||
|
|
||
|
macro_rules! set_snapshot_suffix {
|
||
|
($($expr:expr),*) => {
|
||
|
let mut settings = insta::Settings::clone_current();
|
||
|
settings.set_snapshot_suffix(format!($($expr,)*));
|
||
|
let _guard = settings.bind_to_scope();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[rstest]
|
||
|
// Basic values
|
||
|
#[case("()")]
|
||
|
#[case("(+ 1 2 3)")]
|
||
|
#[case("1")]
|
||
|
#[case("1.0")]
|
||
|
#[case("\"uwu\"")]
|
||
|
#[case("true")]
|
||
|
#[case("false")]
|
||
|
#[case("asdf")]
|
||
|
#[case("nil")]
|
||
|
#[case("[1 2 3]")]
|
||
|
#[case("{:a 1 :b 2 :c 3}")]
|
||
|
// Specials
|
||
|
#[case("(define (a 1))")]
|
||
|
#[case("(define (a 1) (b 2))")]
|
||
|
#[case("(fn* () (+ 1 1))")]
|
||
|
#[case("(fn* (a b) (+ a b))")]
|
||
|
#[case("(if true 1 2)")]
|
||
|
fn test_format_node(#[case] input: &str) {
|
||
|
set_snapshot_suffix!("{}", input);
|
||
|
|
||
|
let ast = parse_str(input).unwrap();
|
||
|
let res = format_node(ast[0].clone());
|
||
|
insta::assert_snapshot!(res);
|
||
|
}
|
||
|
}
|