libunseemly/
ast.rs

1//! This abstract syntax tree is *really* abstract.
2//! It makes binding explicit, but everything else about the language is hidden inside `Node`;
3//!  Their meaning comes from `Form`.
4
5#![macro_use]
6
7use crate::{
8    beta::{Beta, ExportBeta},
9    form::Form,
10    name::*,
11    util::mbe::EnvMBE,
12};
13use std::{fmt, iter, rc::Rc};
14
15// TODO: This really ought to be an `Rc` around an `enum`
16#[derive(Clone, PartialEq)]
17pub enum AstContents {
18    Trivial,
19    /// Typically, a binder
20    Atom(Name),
21    VariableReference(Name),
22
23    /// Shift environment to quote more (the `bool` indicates whether it's positive or negative)
24    QuoteMore(Ast, bool),
25    /// Shift environment to quote less (the `u8` indicates the number of steps out)
26    QuoteLess(Ast, u8),
27
28    /// A meaningful chunk of syntax, governed by a form, containing an environment,
29    ///  potentially exporting some names.
30    Node(std::rc::Rc<Form>, EnvMBE<Ast>, ExportBeta),
31
32    /// For parsing purposes.
33    IncompleteNode(EnvMBE<Ast>),
34    /// For parsing purposes.
35    Shape(Vec<Ast>),
36
37    /// Variable binding
38    ExtendEnv(Ast, Beta),
39    /// Variable binding, affects all phases.
40    /// This is weird, but needed for marcos, it seems.
41    ExtendEnvPhaseless(Ast, Beta),
42}
43
44#[derive(Clone, PartialEq)]
45pub struct LocatedAst {
46    /// "contents"
47    pub c: AstContents,
48    pub file_id: usize,
49    pub begin: usize,
50    pub end: usize,
51}
52
53#[derive(Clone)]
54pub struct Ast(pub Rc<LocatedAst>);
55
56// For testing purposes, we want to ignore span information
57impl PartialEq for Ast {
58    fn eq(&self, other: &Ast) -> bool { self.c() == other.c() }
59}
60
61// Reification macros would totally work for this,
62//  but it's worth having a special case in `Value` in order to make this faster.
63impl crate::runtime::reify::Reifiable for Ast {
64    fn ty_name() -> Name { n("Ast") }
65
66    fn reify(&self) -> crate::runtime::eval::Value {
67        crate::runtime::eval::Value::AbstractSyntax(self.clone())
68    }
69
70    fn reflect(v: &crate::runtime::eval::Value) -> Ast {
71        extract!((v) crate::runtime::eval::Value::AbstractSyntax = (ref ast) => ast.clone())
72    }
73}
74
75pub use self::AstContents::*;
76
77impl fmt::Debug for Ast {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#?}", self.c()) }
79}
80
81impl fmt::Debug for AstContents {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        match self {
84            Trivial => write!(f, "⨉"),
85            Atom(n) => write!(f, "∘{}∘", n.print()),
86            VariableReference(v) => write!(f, "{}", v.print()),
87            Shape(v) => {
88                write!(f, "(")?;
89                let mut first = true;
90                for elt in v {
91                    if !first {
92                        write!(f, " ")?
93                    }
94                    elt.fmt(f)?;
95                    first = false;
96                }
97                write!(f, ")")
98            }
99            Node(form, body, export) => {
100                write!(f, "{{ ({}); {:#?}", form.name.sp(), body)?;
101                match *export {
102                    crate::beta::ExportBeta::Nothing => {}
103                    _ => write!(f, " ⇑{:#?}", export)?,
104                }
105                write!(f, "}}")
106            }
107            QuoteMore(body, pos) => {
108                if *pos {
109                    write!(f, "pos``{:#?}``", body)
110                } else {
111                    write!(f, "neg``{:#?}``", body)
112                }
113            }
114            QuoteLess(body, depth) => write!(f, ",,({}){:#?},,", depth, body),
115            IncompleteNode(body) => write!(f, "{{ INCOMPLETE; {:#?} }}", body),
116            ExtendEnv(body, beta) => write!(f, "{:#?}↓{:#?}", body, beta),
117            ExtendEnvPhaseless(body, beta) => write!(f, "{:#?}±↓{:#?}", body, beta),
118        }
119    }
120}
121
122// Warning: this assumes the core language! To properly display an `Ast`, you need the `SynEnv`.
123impl fmt::Display for Ast {
124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.c()) }
125}
126
127impl fmt::Display for AstContents {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        match self {
130            Atom(ref n) => write!(f, "{}", n.print()),
131            VariableReference(ref v) => write!(f, "{}", v.print()),
132            Node(ref form, ref body, _) => {
133                let s = crate::unparse::unparse_mbe(
134                    &form.grammar,
135                    self,
136                    body,
137                    &crate::core_forms::get_core_forms(),
138                );
139                write!(f, "{}", s)
140            }
141            Shape(ref v) => {
142                write!(f, "(")?;
143                let mut first = true;
144                for elt in v {
145                    if !first {
146                        write!(f, " ")?
147                    }
148                    elt.fmt(f)?;
149                    first = false;
150                }
151                write!(f, ")")
152            }
153            ExtendEnv(ref body, _) => write!(f, "{}↓", body),
154            ExtendEnvPhaseless(ref body, _) => write!(f, "{}±↓", body),
155            QuoteMore(body, _) => {
156                write!(f, "``{}``", body)
157            }
158            QuoteLess(body, _) => write!(f, ",,{},,", body),
159            _ => write!(f, "{:#?}", self),
160        }
161    }
162}
163
164impl Ast {
165    /// "Contents". Ignore location information and get the interesting bits
166    pub fn c(&self) -> &AstContents { &self.0.c }
167
168    /// Replace the contents.
169    pub fn c_map(&self, f: &dyn Fn(&AstContents) -> AstContents) -> Ast { self.with_c(f(self.c())) }
170
171    /// Keep the location information, but replace the contents.
172    pub fn with_c(&self, c: AstContents) -> Ast {
173        Ast(Rc::new(LocatedAst {
174            c: c,
175            file_id: self.0.file_id,
176            begin: self.0.begin,
177            end: self.0.end,
178        }))
179    }
180
181    // TODO: this ought to at least warn if we're losing anything other than `Shape`
182    pub fn flatten(&self) -> EnvMBE<Ast> {
183        match self.c() {
184            Trivial | Atom(_) => EnvMBE::new(),
185            VariableReference(_) => EnvMBE::new(),
186            Shape(ref v) => {
187                let mut accum = EnvMBE::new();
188                for sub_a in v {
189                    accum = accum.combine_overriding(&sub_a.flatten())
190                }
191                accum
192            }
193            IncompleteNode(ref env) => env.clone(),
194            Node(ref _f, ref _body, ref _export) => {
195                // TODO: think about what should happen when
196                //  `Scope` contains a `Scope` without an intervening `Named`
197                panic!("I don't know what to do with {:#?}!", self)
198            }
199            QuoteMore(ref body, _) => body.flatten(),
200            QuoteLess(ref body, _) => body.flatten(),
201            ExtendEnv(ref body, _) | ExtendEnvPhaseless(ref body, _) => body.flatten(),
202        }
203    }
204
205    // TODO: use `Mode::context_match` whenever possible
206    pub fn destructure(
207        &self,
208        expd_form: std::rc::Rc<Form>,
209    ) -> Option<crate::util::mbe::EnvMBE<Ast>> {
210        if let Node(ref f, ref parts, _) = self.c() {
211            if f == &expd_form {
212                return Some(parts.clone());
213            }
214        }
215        None
216    }
217
218    pub fn is_node(&self) -> bool {
219        match self.c() {
220            Node(_, _, _) => true,
221            _ => false,
222        }
223    }
224
225    // TODO: I think we have a lot of places where we ought to use this function:
226    pub fn node_parts(&self) -> &EnvMBE<Ast> {
227        match self.c() {
228            Node(_, ref body, _) => body,
229            _ => icp!(),
230        }
231    }
232
233    pub fn maybe_node_parts(&self) -> Option<&EnvMBE<Ast>> {
234        match self.c() {
235            Node(_, ref body, _) => Some(body),
236            _ => None,
237        }
238    }
239
240    pub fn node_form(&self) -> &Form {
241        match self.c() {
242            Node(ref form, _, _) => form,
243            _ => icp!(),
244        }
245    }
246
247    pub fn free_vrs(&self) -> Vec<Name> {
248        match self.c() {
249            Trivial | Atom(_) => vec![],
250            VariableReference(v) => vec![*v],
251            Shape(_) | IncompleteNode(_) => unimplemented!("TODO"),
252            QuoteLess(_, _) | QuoteMore(_, _) => unimplemented!("TODO"),
253            // This one is actually encounterable by real-world code
254            //  (if a ∀ somehow ends up underneath a `*` in syntax.)
255            // And we need to take a LazyWalkReses to do this right.
256            ExtendEnv(_, _) | ExtendEnvPhaseless(_, _) => unimplemented!("TODO"),
257            Node(_, ref body, _) => body.map_reduce(
258                &|a| a.free_vrs(),
259                &|v0, v1| {
260                    let mut res = v0.clone();
261                    res.append(&mut v1.clone());
262                    res
263                },
264                vec![],
265            ),
266        }
267    }
268
269    pub fn to_name(&self) -> Name {
270        match self.c() {
271            Atom(n) => *n,
272            _ => icp!("{:#?} is not an atom", self),
273        }
274    }
275
276    pub fn vr_to_name(&self) -> Name {
277        match self.c() {
278            VariableReference(n) => *n,
279            _ => icp!("{:#?} is not an atom", self),
280        }
281    }
282
283    pub fn orig_str<'a, 'b>(&'a self, prog: &'b str) -> &'b str { &prog[self.0.begin..self.0.end] }
284}
285
286// This is used by combine::many, which is used by the Star parser
287impl iter::FromIterator<Ast> for Ast {
288    fn from_iter<I: IntoIterator<Item = Ast>>(i: I) -> Self {
289        raw_ast!(IncompleteNode(EnvMBE::new_from_anon_repeat(
290            i.into_iter().map(|a| a.flatten()).collect()
291        )))
292    }
293}
294
295// This is also sort of a test of MBE, since we need `Ast`s to make them with the macros
296//
297// Suppose we have the following series of `Ast`s:
298// [b = 8] [a = [1 2], b = 8] [a = [3 4 5], b = 8]
299//
300// We should turn them into the following `Ast`
301// [a = [[] [1 2] [3 4 5]], b = [8 8 8]]
302#[test]
303fn combine_from_kleene_star() {
304    use std::iter::FromIterator;
305
306    let parse_parts = vec![
307        ast!({ - "b" => "8.0"}),
308        ast!({ - "a" => ["1", "2"], "b" => "8.1"}),
309        ast!({ - "a" => ["1", "2", "3"], "b" => "8.2"}),
310    ];
311    let parsed = Ast::from_iter(parse_parts);
312
313    let mut expected_mbe = mbe!("a" => [@"triple" [], ["1", "2"], ["1", "2", "3"]],
314             "b" => [@"triple" "8.0", "8.1", "8.2"]);
315    expected_mbe.anonimize_repeat(n("triple"));
316
317    assert_eq!(parsed.c(), &IncompleteNode(expected_mbe));
318}
319
320#[test]
321fn star_construction() {
322    let env = mbe!( "a" => ["1", "2"]);
323
324    assert_eq!(
325        ast!( { - "x" => [* env =>("a") env : (, env.get_leaf_or_panic(&n("a")).clone())]} ),
326        ast!( { - "x" => ["1", "2"] })
327    );
328
329    let env = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);
330
331    assert_eq!(
332        ast!( { - "x" => [* env =>("a", "b") env :
333                            ((, env.get_leaf_or_panic(&n("b")).clone())
334                             (, env.get_leaf_or_panic(&n("a")).clone()))]} ),
335        ast!( { - "x" => [("11" "1"), ("22" "2")] })
336    );
337}
338
339#[test]
340fn mbe_r_and_r_roundtrip() {
341    use crate::runtime::reify::Reifiable;
342    let mbe1 = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);
343    assert_eq!(mbe1, EnvMBE::<Ast>::reflect(&mbe1.reify()));
344}