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 }