//! Example implementations about _expression_. use gcmodule::Trace; use std::borrow::Cow; use std::fmt; /// S-expression. #[derive(Clone)] pub enum Sexp { /// A symbol that needs to be resolved in an environment. Symbol(Cow<'static, str>), /// A list, such as `(+ 1 2)`. Compound(Vec>), /// An inlined value. /// /// Pre-resolved values. /// Be-careful: self-referral inlined values can cause Display or Debug /// to stack overflow. Inlined(V), } impl Trace for Sexp { fn is_type_tracked() -> bool { false } fn as_any(&self) -> Option<&dyn std::any::Any> { Some(self) } } impl fmt::Display for Sexp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt_expr(self, f, V::fmt) } } impl fmt::Debug for Sexp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt_expr(self, f, V::fmt) } } fn fmt_expr( expr: &Sexp, f: &mut fmt::Formatter, fmt_inlined: fn(&V, &mut fmt::Formatter) -> fmt::Result, ) -> fmt::Result { match expr { Sexp::Symbol(s) => write!(f, "{}", &s)?, Sexp::Compound(v) => { write!(f, "(")?; let mut padding = ""; for expr in v.iter() { write!(f, "{}", padding)?; fmt_expr(expr, f, fmt_inlined)?; padding = " "; } write!(f, ")")?; } Sexp::Inlined(v) => fmt_inlined(v, f)?, } Ok(()) } /// Macro for creating S-expression. #[macro_export] macro_rules! sexp { // { } is used to escape native Rust expression. ( { $x: expr } ) => { $crate::expr::Sexp::Inlined($crate::value::Value::new($x)) }; ( + ) => { $crate::expr::Sexp::Symbol("+".into()) }; ( - ) => { $crate::expr::Sexp::Symbol("-".into()) }; ( * ) => { $crate::expr::Sexp::Symbol("*".into()) }; ( / ) => { $crate::expr::Sexp::Symbol("/".into()) }; ( $x: ident ) => { $crate::expr::Sexp::Symbol(stringify!($x).into()) }; ( ( $( $x: tt ) * ) ) => { { #[allow(unused_mut)] let mut vec = Vec::new(); $( vec.push(sexp!($x)); )* $crate::expr::Sexp::Compound(vec) } }; // Symbol like "set!" can be written as ["set!"]. ( [ $x: tt ] ) => { $crate::expr::Sexp::Symbol($x.into()) }; // Sugar: Fallback to { x } for resolving Rust expression like 123, "abc". ( $x: expr ) => { sexp!( { $x } ) }; }