1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Example implementations about _expression_.

use gcmodule::Trace;
use std::borrow::Cow;
use std::fmt;

/// S-expression.
#[derive(Clone)]
pub enum Sexp<V> {
    /// A symbol that needs to be resolved in an environment.
    Symbol(Cow<'static, str>),

    /// A list, such as `(+ 1 2)`.
    Compound(Vec<Sexp<V>>),

    /// An inlined value.
    ///
    /// Pre-resolved values.
    /// Be-careful: self-referral inlined values can cause Display or Debug
    /// to stack overflow.
    Inlined(V),
}

impl<V: 'static> Trace for Sexp<V> {
    fn is_type_tracked() -> bool {
        false
    }
    fn as_any(&self) -> Option<&dyn std::any::Any> {
        Some(self)
    }
}

impl<V: fmt::Display> fmt::Display for Sexp<V> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt_expr(self, f, V::fmt)
    }
}

impl<V: fmt::Debug> fmt::Debug for Sexp<V> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt_expr(self, f, V::fmt)
    }
}

fn fmt_expr<V>(
    expr: &Sexp<V>,
    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 } ) };
}