103 lines
3.3 KiB
Rust
103 lines
3.3 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) => format!("Node::List({})", reduce_list(v)),
|
|
|
|
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(_) => todo!(),
|
|
Node::Map(_) => todo!(),
|
|
|
|
// Quoting
|
|
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)),
|
|
|
|
// Specials
|
|
Node::Define(body) => format!("Node::Define({})", reduce_list(body)),
|
|
Node::Let(body) => format!("Node::Let({})", reduce_list(body)),
|
|
Node::Function(body) => format!("Node::Function({})", reduce_list(body)),
|
|
Node::Macro(body) => format!("Node::Macro({})", reduce_list(body)),
|
|
Node::If(body) => format!("Node::If({})", reduce_list(body)),
|
|
Node::Do(body) => format!("Node::Do({})", reduce_list(body)),
|
|
Node::Eval(body) => format!("Node::Eval({})", reduce_list(body)),
|
|
Node::Apply(body) => format!("Node::Apply({})", reduce_list(body),),
|
|
}
|
|
}
|
|
|
|
fn reduce_list<T: IntoIterator<Item = Node>>(node: T) -> String {
|
|
let vec = node
|
|
.into_iter()
|
|
.map(format_node)
|
|
.reduce(|lhs, rhs| format!("{lhs}, {rhs}"))
|
|
.unwrap_or_default();
|
|
|
|
format!("vec![{vec}].into()")
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
}
|