1 module zua.parser.analysis;
2 import zua.parser.ast;
3 import zua.diagnostic;
4 import std.variant;
5
6 private final class Environment {
7 Environment parent;
8 bool isVariadicContext;
9 bool inLoop;
10 bool laststatReached;
11
12 this(Environment parent) {
13 this.parent = parent;
14 if (parent) {
15 isVariadicContext = parent.isVariadicContext;
16 inLoop = parent.inLoop;
17 }
18 }
19 }
20
21 private final class SemanticAnalysis {
22
23 Diagnostic[]* diagnostics;
24 Environment env;
25
26 void walk(Stat stat) {
27 if (env.laststatReached) {
28 Diagnostic err;
29 err.type = DiagnosticType.Error;
30 err.message = "cannot have any statements after 'return' or 'break'";
31 err.add(stat.start, stat.end);
32 (*diagnostics) ~= err;
33 env.laststatReached = false; // we don't need to spam the user with messages
34 }
35 if (auto s = cast(AssignStat)stat) return walk(s);
36 if (auto s = cast(ExprStat)stat) return walk(s);
37 if (auto s = cast(Block)stat) return walk(s);
38 if (auto s = cast(DeclarationStat)stat) return walk(s);
39 if (auto s = cast(FunctionDeclarationStat)stat) return walk(s);
40 if (auto s = cast(WhileStat)stat) return walk(s);
41 if (auto s = cast(RepeatStat)stat) return walk(s);
42 if (auto s = cast(IfStat)stat) return walk(s);
43 if (auto s = cast(NumericForStat)stat) return walk(s);
44 if (auto s = cast(ForeachStat)stat) return walk(s);
45 if (auto s = cast(ReturnStat)stat) return walk(s);
46 if (auto s = cast(AtomicStat)stat) return walk(s);
47 assert(0);
48 }
49
50 void walk(Expr expr) {
51 if (auto e = cast(PrefixExpr)expr) return walk(e);
52 if (auto e = cast(AtomicExpr)expr) return walk(e);
53 if (auto e = cast(NumberExpr)expr) return walk(e);
54 if (auto e = cast(StringExpr)expr) return walk(e);
55 if (auto e = cast(FunctionExpr)expr) return walk(e);
56 if (auto e = cast(BinaryExpr)expr) return walk(e);
57 if (auto e = cast(UnaryExpr)expr) return walk(e);
58 if (auto e = cast(TableExpr)expr) return walk(e);
59 assert(0);
60 }
61
62 void walk(PrefixExpr expr) {
63 if (auto e = cast(LvalueExpr)expr) return walk(e);
64 if (auto e = cast(BracketExpr)expr) return walk(e);
65 if (auto e = cast(CallExpr)expr) return walk(e);
66 assert(0);
67 }
68
69 void walk(LvalueExpr expr) {
70 if (auto e = cast(VariableExpr)expr) return walk(e);
71 if (auto e = cast(IndexExpr)expr) return walk(e);
72 assert(0);
73 }
74
75 void walk(AssignStat stat) {
76 foreach (key; stat.keys) walk(key);
77 foreach (value; stat.values) walk(value);
78 }
79
80 void walk(ExprStat stat) {
81 walk(stat.expr);
82 }
83
84 void walk(Block stat) {
85 env = new Environment(env);
86 foreach (child; stat.body) walk(child);
87 env = env.parent;
88 }
89
90 void walk(DeclarationStat stat) {
91 foreach (value; stat.values) walk(value);
92 }
93
94 void walk(FunctionDeclarationStat stat) {
95 walk(stat.value);
96 }
97
98 void walk(WhileStat stat) {
99 env = new Environment(env);
100 env.inLoop = true;
101 walk(stat.cond);
102 walk(stat.body);
103 env = env.parent;
104 }
105
106 void walk(RepeatStat stat) {
107 env = new Environment(env);
108 env.inLoop = true;
109 walk(stat.body);
110 walk(stat.endCond);
111 env = env.parent;
112 }
113
114 void walk(IfStat stat) {
115 foreach (e; stat.entries) {
116 walk(e.cond);
117 walk(e.body);
118 }
119 if (!stat.elseBody.isNull) {
120 walk(stat.elseBody.get);
121 }
122 }
123
124 void walk(NumericForStat stat) {
125 env = new Environment(env);
126 env.inLoop = true;
127 walk(stat.low);
128 walk(stat.high);
129 if (!stat.step.isNull) {
130 walk(stat.step.get);
131 }
132 walk(stat.body);
133 env = env.parent;
134 }
135
136 void walk(ForeachStat stat) {
137 env = new Environment(env);
138 env.inLoop = true;
139 foreach (value; stat.iter) walk(value);
140 walk(stat.body);
141 env = env.parent;
142 }
143
144 void walk(ReturnStat stat) {
145 foreach (value; stat.values) walk(value);
146 env.laststatReached = true;
147 }
148
149 void walk(AtomicStat stat) {
150 if (stat.type == AtomicStatType.Break) {
151 if (!env.inLoop) {
152 Diagnostic err;
153 err.type = DiagnosticType.Error;
154 err.message = "cannot 'break' outside of a loop";
155 err.add(stat.start, stat.end);
156 (*diagnostics) ~= err;
157 }
158 else {
159 env.laststatReached = true;
160 }
161 }
162 }
163
164 void walk(VariableExpr expr) {
165
166 }
167
168 void walk(IndexExpr expr) {
169 walk(expr.base);
170 walk(expr.key);
171 }
172
173 void walk(BracketExpr expr) {
174 walk(expr.expr);
175 }
176
177 void walk(CallExpr expr) {
178 walk(expr.base);
179 foreach (arg; expr.args) walk(arg);
180 }
181
182 void walk(AtomicExpr expr) {
183 if (expr.type == AtomicExprType.VariadicTuple) {
184 if (!env.isVariadicContext) {
185 Diagnostic err;
186 err.type = DiagnosticType.Error;
187 err.message = "cannot close on variadic arguments";
188 err.add(expr.start, expr.end);
189 (*diagnostics) ~= err;
190 }
191 }
192 }
193
194 void walk(NumberExpr expr) {
195
196 }
197
198 void walk(StringExpr expr) {
199
200 }
201
202 void walk(FunctionExpr expr) {
203 env = new Environment(env);
204 env.isVariadicContext = expr.variadic;
205 walk(expr.body);
206 env = env.parent;
207 }
208
209 void walkToplevel(Block stat) {
210 env = new Environment(env);
211 env.isVariadicContext = true;
212 walk(stat);
213 env = env.parent;
214 }
215
216 void walk(BinaryExpr expr) {
217 walk(expr.lhs);
218 walk(expr.rhs);
219 }
220
221 void walk(UnaryExpr expr) {
222 walk(expr.expr);
223 }
224
225 void walk(TableExpr expr) {
226 foreach (e; expr.fields) {
227 e.visit!(
228 (TableField field) {
229 walk(field.key);
230 walk(field.value);
231 },
232 (Expr expr) {
233 walk(expr);
234 }
235 );
236 }
237 }
238
239 }
240
241 /** Perform semantic analysis on a block */
242 void performAnalysis(ref Diagnostic[] diagnostics, Block block) {
243 SemanticAnalysis analysis = new SemanticAnalysis;
244 analysis.diagnostics = &diagnostics;
245 analysis.walkToplevel(block);
246 }