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 |