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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#[macro_export]
macro_rules! analyzed_tree {
    ((None)) => { None };
    ((Some($($node:tt)*))) => { Some(analyzed_tree!(($($node)*))) };

    ((
        Program,
        globals: [$($global:tt),* $(,)?],
        functions: [$($func:tt),* $(,)?],
        main_fn: $main_fn:tt,
        used_builtins: [$($name:expr),* $(,)?] $(,)?
    )) => {
        AnalyzedProgram {
            globals: vec![$(analyzed_tree!($global)),*],
            functions: vec![$(analyzed_tree!($func)),*],
            main_fn: analyzed_tree!($main_fn),
            used_builtins: HashSet::from([$($name),*]),
        }
    };
    ((
        FunctionDefinition,
        used: $used:expr,
        name: $name:expr,
        params: [$($param:tt),* $(,)?],
        return_type: $return_type:expr,
        block: $block:tt $(,)?
    )) => {
        AnalyzedFunctionDefinition {
            used: $used,
            name: $name,
            params: vec![$(analyzed_tree!($param)),*],
            return_type: $return_type,
            block: analyzed_tree!($block),
        }
    };
    ((
        Parameter,
        mutable: $mutable:expr,
        name: $name:expr,
        type: $type:expr $(,)?
    )) => {
        AnalyzedParameter {
            mutable: $mutable,
            name: $name,
            type_: $type,
        }
    };

    ((
        Let,
        name: $name:expr,
        expr: $expr:tt $(,)?
    )) => {
        AnalyzedLetStmt {
            name: $name,
            expr: analyzed_tree!($expr),
        }
    };
    ((LetStmt $($rest:tt)*)) => {
        AnalyzedStatement::Let(analyzed_tree!((Let $($rest)*)))
    };
    ((ReturnStmt, $expr:tt $(,)?)) => {
        AnalyzedStatement::Return(analyzed_tree!($expr))
    };
    ((ExprStmt, $expr:tt $(,)?)) => {
        AnalyzedStatement::Expr(analyzed_tree!($expr))
    };

    ((
        Block -> $result_type:expr,
        stmts: [$($stmt:tt),* $(,)?],
        expr: $expr:tt $(,)?
    )) => {
        AnalyzedBlock {
            result_type: $result_type,
            stmts: vec![$(analyzed_tree!($stmt)),*],
            expr: analyzed_tree!($expr),
        }
    };
    ((BlockExpr $($rest:tt)*)) => {
        AnalyzedExpression::Block(analyzed_tree!((Block $($rest)*)).into())
    };
    ((
        IfExpr -> $result_type:expr,
        cond: $cond:tt,
        then_block: $then_block:tt,
        else_block: $else_block:tt $(,)?
    )) => {
        AnalyzedExpression::If(AnalyzedIfExpr {
            result_type: $result_type,
            cond: analyzed_tree!($cond),
            then_block: analyzed_tree!($then_block),
            else_block: analyzed_tree!($else_block),
        }.into())
    };
    ((Int $expr:expr)) => { AnalyzedExpression::Int($expr) };
    ((Float $expr:expr)) => { AnalyzedExpression::Float($expr) };
    ((Bool $expr:expr)) => { AnalyzedExpression::Bool($expr) };
    ((Char $expr:expr)) => { AnalyzedExpression::Char($expr) };
    ((Ident -> $type:expr, $ident:expr)) => {
        AnalyzedExpression::Ident(AnalyzedIdentExpr {
            result_type: $type,
            ident: $ident,
        })
    };
    ((Grouped $expr:tt)) => {
        AnalyzedExpression::Grouped(analyzed_tree!($expr).into())
    };
    ((
        PrefixExpr -> $result_type:expr,
        op: $op:expr,
        expr: $expr:tt $(,)?
    )) => {
        AnalyzedExpression::Prefix(AnalyzedPrefixExpr {
            result_type: $result_type,
            op: $op,
            expr: analyzed_tree!($expr),
        }.into())
    };
    ((
        InfixExpr -> $result_type:expr,
        lhs: $lhs:tt,
        op: $op:expr,
        rhs: $rhs:tt $(,)?
    )) => {
        AnalyzedExpression::Infix(AnalyzedInfixExpr {
            result_type: $result_type,
            lhs: analyzed_tree!($lhs),
            op: $op,
            rhs: analyzed_tree!($rhs),
        }.into())
    };
    ((
        AssignExpr -> $result_type:expr,
        assignee: $assignee:expr,
        op: $op:expr,
        expr: $expr:tt $(,)?
    )) => {
        AnalyzedExpression::Assign(AnalyzedAssignExpr {
            result_type: $result_type,
            assignee: $assignee,
            op: $op,
            expr: analyzed_tree!($expr),
        }.into())
    };
    ((
        CallExpr -> $result_type:expr,
        func: $func:expr,
        args: [$($arg:tt),* $(,)?] $(,)?
    )) => {
        AnalyzedExpression::Call(AnalyzedCallExpr {
            result_type: $result_type,
            func: $func,
            args: vec![$(analyzed_tree!($arg)),*],
        }.into())
    };
    ((
        CastExpr -> $result_type:expr,
        expr: $expr:tt,
        type: $type:expr $(,)?
    )) => {
        AnalyzedExpression::Cast(AnalyzedCastExpr {
            result_type: $result_type,
            expr: analyzed_tree!($expr),
            type_: $type,
        }.into())
    };
}