| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library servicec.validator; | |
| 6 | |
| 7 import 'node.dart' show | |
| 8 CompilationUnitNode, | |
| 9 FormalNode, | |
| 10 FunctionNode, | |
| 11 IdentifierNode, | |
| 12 ListType, | |
| 13 MemberNode, | |
| 14 FieldNode, | |
| 15 Node, | |
| 16 PointerType, | |
| 17 RecursiveVisitor, | |
| 18 ServiceNode, | |
| 19 StructNode, | |
| 20 TopLevelNode, | |
| 21 TypeNode, | |
| 22 UnionNode; | |
| 23 | |
| 24 import 'errors.dart' show | |
| 25 BadFieldTypeError, | |
| 26 BadListTypeError, | |
| 27 BadPointerTypeError, | |
| 28 BadReturnTypeError, | |
| 29 BadSingleFormalError, | |
| 30 BadTypeParameterError, | |
| 31 CompilationError, | |
| 32 CyclicStructError, | |
| 33 ErrorNode, | |
| 34 ErrorTag, | |
| 35 MultipleDefinitionsError, | |
| 36 MultipleUnionsError, | |
| 37 NotPrimitiveFormalError, | |
| 38 SyntaxError, | |
| 39 ServiceStructNameClashError, | |
| 40 UndefinedServiceError; | |
| 41 | |
| 42 import 'dart:collection' show | |
| 43 Queue; | |
| 44 | |
| 45 import 'cycle_detection.dart' show | |
| 46 StructGraph; | |
| 47 | |
| 48 // Validation functions. | |
| 49 List<CompilationError> validate(CompilationUnitNode compilationUnit) { | |
| 50 Validator validator = new Validator(); | |
| 51 validator.process(compilationUnit); | |
| 52 return validator.errors; | |
| 53 } | |
| 54 | |
| 55 class Validator extends RecursiveVisitor { | |
| 56 List<CompilationError> errors; | |
| 57 Environment environment; | |
| 58 StructGraph structGraph; | |
| 59 | |
| 60 Validator() | |
| 61 : errors = <CompilationError>[], | |
| 62 environment = new Environment(), | |
| 63 structGraph = new StructGraph(), | |
| 64 super(); | |
| 65 | |
| 66 void process(CompilationUnitNode compilationUnit) { | |
| 67 visitCompilationUnit(compilationUnit); | |
| 68 errors.addAll(structGraph.findCycles()); | |
| 69 } | |
| 70 | |
| 71 // Visit methods. | |
| 72 void visitCompilationUnit(CompilationUnitNode compilationUnit) { | |
| 73 enterCompilationUnitScope(compilationUnit); | |
| 74 checkHasAtLeastOneService(compilationUnit); | |
| 75 super.visitCompilationUnit(compilationUnit); | |
| 76 leaveCompilationUnitScope(compilationUnit); | |
| 77 } | |
| 78 | |
| 79 void visitService(ServiceNode service) { | |
| 80 enterServiceScope(service); | |
| 81 checkIsNotError(service); | |
| 82 super.visitService(service); | |
| 83 leaveServiceScope(service); | |
| 84 } | |
| 85 | |
| 86 void visitStruct(StructNode struct) { | |
| 87 enterStructScope(struct); | |
| 88 checkIsNotError(struct); | |
| 89 checkHasAtMostOneUnion(struct); | |
| 90 super.visitStruct(struct); | |
| 91 structGraph.add(struct); | |
| 92 leaveStructScope(struct); | |
| 93 } | |
| 94 | |
| 95 void visitFunction(FunctionNode function) { | |
| 96 enterFunctionScope(function); | |
| 97 checkIsNotError(function); | |
| 98 | |
| 99 visitReturnType(function.returnType); | |
| 100 // Ensure formal parameters are either a single pointer to a user-defined | |
| 101 // type, or a list of primitives. | |
| 102 int length = function.formals.length; | |
| 103 if (length == 1) { | |
| 104 visitSingleFormal(function.formals[0]); | |
| 105 } else if (length > 1) { | |
| 106 for (FormalNode formal in function.formals) { | |
| 107 visitPrimitiveFormal(formal); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 leaveFunctionScope(function); | |
| 112 } | |
| 113 | |
| 114 void visitSingleFormal(FormalNode formal) { | |
| 115 checkIsNotError(formal); | |
| 116 visitType(formal.type); | |
| 117 checkSingleFormal(formal.type); | |
| 118 } | |
| 119 | |
| 120 void visitPrimitiveFormal(FormalNode formal) { | |
| 121 checkIsNotError(formal); | |
| 122 visitType(formal.type); | |
| 123 checkIsPrimitiveFormal(formal); | |
| 124 } | |
| 125 | |
| 126 void visitUnion(UnionNode union) { | |
| 127 // TODO(stanm): checkIsNotError(union); | |
| 128 super.visitUnion(union); | |
| 129 } | |
| 130 | |
| 131 void visitField(FieldNode field) { | |
| 132 checkIsNotError(field); | |
| 133 visitType(field.type); // resolve | |
| 134 if (field.type.isPointer()) { | |
| 135 checkPointeeTypeResolves(field.type); | |
| 136 } else if (field.type.isList()) { | |
| 137 ListType list = field.type; | |
| 138 visitListType(list); | |
| 139 } else { | |
| 140 checkFieldSimpleType(field.type); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 void visitReturnType(TypeNode type) { | |
| 145 visitType(type); | |
| 146 checkReturnType(type); | |
| 147 } | |
| 148 | |
| 149 void visitTypeParameter(TypeNode type) { | |
| 150 visitType(type); | |
| 151 checkTypeParameter(type); | |
| 152 } | |
| 153 | |
| 154 void visitPointerType(PointerType type) { | |
| 155 visitType(type); | |
| 156 checkPointeeTypeResolves(type); | |
| 157 } | |
| 158 | |
| 159 void visitListType(ListType type) { | |
| 160 visitType(type); | |
| 161 checkIsNotError(type); | |
| 162 checkIsListType(type); | |
| 163 super.visitListType(type); | |
| 164 } | |
| 165 | |
| 166 void visitType(TypeNode type) { | |
| 167 type.resolve(environment.structs); | |
| 168 } | |
| 169 | |
| 170 void visitError(ErrorNode error) { | |
| 171 errors.add(new SyntaxError(error)); | |
| 172 } | |
| 173 | |
| 174 // Checks. | |
| 175 void checkIsNotError(Node node) { | |
| 176 // Using var as a work-around for compiler warnings about ErrorNode not | |
| 177 // being in the node hierarchy. | |
| 178 var dummy = node; | |
| 179 if (dummy is ErrorNode) { | |
| 180 ErrorNode error = dummy; | |
| 181 visitError(error); | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 void checkHasAtLeastOneService(CompilationUnitNode compilationUnit) { | |
| 186 for (Node node in compilationUnit.topLevels) { | |
| 187 if (node is ServiceNode) return; | |
| 188 } | |
| 189 errors.add(new UndefinedServiceError()); | |
| 190 } | |
| 191 | |
| 192 void checkHasAtMostOneUnion(StructNode struct) { | |
| 193 int count = 0; | |
| 194 for (MemberNode member in struct.members) { | |
| 195 if (member is UnionNode && ++count > 1) { | |
| 196 errors.add(new MultipleUnionsError(struct)); | |
| 197 return; | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 void checkIsPointerOrPrimitive(TypeNode type, CompilationError error) { | |
| 203 if (type.isPrimitive()) return; | |
| 204 if (type.isPointer()) { | |
| 205 checkPointeeTypeResolves(type); | |
| 206 } else { | |
| 207 errors.add(error); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 void checkSingleFormal(TypeNode type) { | |
| 212 checkIsPointerOrPrimitive(type, new BadSingleFormalError(type)); | |
| 213 } | |
| 214 | |
| 215 void checkIsPrimitiveFormal(FormalNode formal) { | |
| 216 if (!formal.type.isPrimitive()) { | |
| 217 errors.add(new NotPrimitiveFormalError(formal)); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 void checkPointeeTypeResolves(PointerType type) { | |
| 222 if (!type.pointeeResolves()) { | |
| 223 errors.add(new BadPointerTypeError(type)); | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 void checkIsListType(ListType type) { | |
| 228 if (!type.isList()) { | |
| 229 errors.add(new BadListTypeError(type)); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 void checkReturnType(TypeNode type) { | |
| 234 checkIsPointerOrPrimitive(type, new BadReturnTypeError(type)); | |
| 235 } | |
| 236 | |
| 237 void checkTypeParameter(TypeNode type) { | |
| 238 if (!(type.isPrimitive() || type.isStruct())) { | |
| 239 errors.add(new BadTypeParameterError(type)); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 void checkFieldSimpleType(TypeNode type) { | |
| 244 if (!(type.isPrimitive() || type.isString() || type.isStruct())) { | |
| 245 errors.add(new BadFieldTypeError(type)); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 // Scope management. | |
| 250 void enterCompilationUnitScope(CompilationUnitNode compilationUnit) { | |
| 251 compilationUnit.topLevels.forEach(addTopLevelSymbol); | |
| 252 } | |
| 253 | |
| 254 void enterServiceScope(ServiceNode service) { | |
| 255 service.functions.forEach(addFunctionSymbol); | |
| 256 } | |
| 257 | |
| 258 void enterStructScope(StructNode struct) { | |
| 259 struct.members.forEach(addMemberSymbol); | |
| 260 } | |
| 261 | |
| 262 void enterFunctionScope(FunctionNode function) { | |
| 263 function.formals.forEach(addFormalSymbol); | |
| 264 } | |
| 265 | |
| 266 | |
| 267 void leaveCompilationUnitScope(CompilationUnitNode compilationUnit) { | |
| 268 compilationUnit.topLevels.forEach(removeTopLevelSymbol); | |
| 269 } | |
| 270 | |
| 271 void leaveServiceScope(ServiceNode service) { | |
| 272 service.functions.forEach(removeFunctionSymbol); | |
| 273 } | |
| 274 | |
| 275 void leaveStructScope(StructNode struct) { | |
| 276 struct.members.forEach(removeMemberSymbol); | |
| 277 } | |
| 278 | |
| 279 void leaveFunctionScope(FunctionNode function) { | |
| 280 function.formals.forEach(removeFormalSymbol); | |
| 281 } | |
| 282 | |
| 283 void addTopLevelSymbol(TopLevelNode node) { | |
| 284 if (node is ServiceNode) { | |
| 285 addServiceSymbol(node); | |
| 286 } else if (node is StructNode) { | |
| 287 addStructSymbol(node); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 void addServiceSymbol(ServiceNode service) { | |
| 292 checkIsNotNameClash(environment.structs.keys.toSet(), service.identifier); | |
| 293 addSymbol(environment.services, service.identifier); | |
| 294 } | |
| 295 | |
| 296 void addStructSymbol(StructNode struct) { | |
| 297 checkIsNotNameClash(environment.services, struct.identifier); | |
| 298 if (environment.structs.containsKey(struct.identifier)) { | |
| 299 IdentifierNode original = | |
| 300 environment.structs.keys.toSet().lookup(struct.identifier); | |
| 301 errors.add(new MultipleDefinitionsError(original, struct.identifier)); | |
| 302 } else { | |
| 303 environment.structs[struct.identifier] = struct; | |
| 304 } | |
| 305 } | |
| 306 | |
| 307 void addFunctionSymbol(FunctionNode function) { | |
| 308 addSymbol(environment.properties, function.identifier); | |
| 309 } | |
| 310 | |
| 311 void addMemberSymbol(MemberNode member) { | |
| 312 if (member is UnionNode) { | |
| 313 UnionNode union = member; | |
| 314 union.fields.forEach(addFieldSymbol); | |
| 315 } else { | |
| 316 addFieldSymbol(member); | |
| 317 } | |
| 318 } | |
| 319 | |
| 320 void addFieldSymbol(FieldNode field) { | |
| 321 addSymbol(environment.properties, field.identifier); | |
| 322 } | |
| 323 | |
| 324 void addFormalSymbol(FormalNode formal) { | |
| 325 addSymbol(environment.formals, formal.identifier); | |
| 326 } | |
| 327 | |
| 328 void removeTopLevelSymbol(TopLevelNode node) { | |
| 329 if (node is ServiceNode) { | |
| 330 removeServiceSymbol(node); | |
| 331 } else if (node is StructNode) { | |
| 332 removeStructSymbol(node); | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 void removeServiceSymbol(ServiceNode service) { | |
| 337 removeSymbol(environment.services, service.identifier); | |
| 338 } | |
| 339 | |
| 340 void removeStructSymbol(StructNode struct) { | |
| 341 environment.structs.remove(struct.identifier); | |
| 342 } | |
| 343 | |
| 344 void removeFunctionSymbol(FunctionNode function) { | |
| 345 removeSymbol(environment.properties, function.identifier); | |
| 346 } | |
| 347 | |
| 348 void removeMemberSymbol(MemberNode member) { | |
| 349 if (member is UnionNode) { | |
| 350 UnionNode union = member; | |
| 351 union.fields.forEach(removeFieldSymbol); | |
| 352 } else { | |
| 353 removeFieldSymbol(member); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 void removeFieldSymbol(FieldNode field) { | |
| 358 removeSymbol(environment.properties, field.identifier); | |
| 359 } | |
| 360 | |
| 361 void removeFormalSymbol(FormalNode formal) { | |
| 362 removeSymbol(environment.formals, formal.identifier); | |
| 363 } | |
| 364 | |
| 365 void checkIsNotNameClash(Set<IdentifierNode> symbols, | |
| 366 IdentifierNode identifier) { | |
| 367 IdentifierNode original = symbols.lookup(identifier); | |
| 368 if (null != original) { | |
| 369 errors.add(new ServiceStructNameClashError(original, identifier)); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 void addSymbol(Set<IdentifierNode> symbols, IdentifierNode identifier) { | |
| 374 IdentifierNode original = symbols.lookup(identifier); | |
| 375 if (null != original) { | |
| 376 errors.add(new MultipleDefinitionsError(original, identifier)); | |
| 377 } else { | |
| 378 symbols.add(identifier); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 void removeSymbol(Set<IdentifierNode> symbols, IdentifierNode identifier) { | |
| 383 symbols.remove(identifier); | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 class Environment { | |
| 388 Set<IdentifierNode> services; | |
| 389 Map<IdentifierNode, StructNode> structs; | |
| 390 Set<IdentifierNode> properties; | |
| 391 Set<IdentifierNode> formals; | |
| 392 | |
| 393 Environment() | |
| 394 : services = new Set<IdentifierNode>(), | |
| 395 structs = new Map<IdentifierNode, StructNode>(), | |
| 396 properties = new Set<IdentifierNode>(), | |
| 397 formals = new Set<IdentifierNode>(); | |
| 398 } | |
| 399 | |
| 400 | |
| OLD | NEW |