summaryrefslogtreecommitdiff
path: root/src/chomp/cst.rs
blob: 8e3163b9ad0f0f9c3513106506771d5a07fcaa7f (plain)
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
use std::rc::Rc;

use super::Context;

#[derive(Debug)]
pub struct Name;

#[derive(Debug)]
pub enum Shape {
    Epsilon,
    Literal(String),
    Variable(usize),
    Cat(Vec<Rc<Shape>>),
    Alt(Vec<Rc<Shape>>),
    Fix(Rc<Shape>),
    Call(Vec<Rc<Shape>>),
    Lambda(Vec<Name>, Rc<Shape>),
    Let(Name, Rc<Shape>, Rc<Shape>),
}

pub(crate) fn as_tree(ctx: &mut Context, shape: &Rc<Shape>) -> Rc<super::ast::Tree> {
    fn lambda_as_tree(ctx: &mut Context, count: usize, inner: &Rc<Shape>) -> Rc<super::ast::Tree> {
        if count == 0 {
            ctx.as_tree(inner)
        } else {
            ctx.new_lambda(|ctx| lambda_as_tree(ctx, count - 1, inner))
        }
    }

    match shape.as_ref() {
        Shape::Epsilon => ctx.tree_interner.epsilon(),
        Shape::Literal(l) => ctx.tree_interner.literal(l.to_owned()),
        Shape::Variable(idx) => ctx.lookup_tree_variable(*idx),
        Shape::Cat(v) => {
            let epsilon = ctx.tree_interner.epsilon();
            v.into_iter().fold(epsilon, |front, back| {
                let back = ctx.as_tree(back);
                ctx.tree_interner.cat(front, back)
            })
        }
        Shape::Alt(v) => {
            let bottom = ctx.tree_interner.bottom();
            v.into_iter().fold(bottom, |left, right| {
                let right = ctx.as_tree(right);
                ctx.tree_interner.alt(left, right)
            })
        }
        Shape::Fix(inner) => {
            let inner = ctx.as_tree(inner);
            ctx.tree_interner.fix(inner)
        }
        Shape::Call(v) => {
            let identity = ctx.tree_interner.identity();
            v.into_iter().fold(identity, |fun, arg| {
                let arg = ctx.as_tree(arg);
                ctx.tree_interner.call(fun, arg)
            })
        }
        Shape::Lambda(names, inner) => lambda_as_tree(ctx, names.len(), inner),
        Shape::Let(_, bind, inner) => {
            let bind = ctx.as_tree(bind);
            ctx.new_let(bind, |ctx| ctx.as_tree(inner))
        }
    }
}