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 }