astvalidation: add `ANode::validate` to simplify validation
[nit.git] / src / astvalidation.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Check the consitency of AST
16 module astvalidation
17
18 intrude import parser
19 import astbuilder
20
21 class ASTValidationVisitor
22 super Visitor
23 redef fun visit(node)
24 do
25 node.accept_ast_validation(self)
26 end
27 private var path = new List[ANode]
28 private var seen = new HashSet[ANode]
29 end
30
31 redef class ANode
32 # Recursively validate a AST node.
33 # This ensure that location and parenting are defined and coherent.
34 #
35 # After complex low-level AST manipulation and construction,
36 # it is recommended to call it.
37 #
38 # Note: this just instantiate and run an `ASTValidationVisitor`.
39 fun validate
40 do
41 (new ASTValidationVisitor).enter_visit(self)
42 end
43
44 private fun accept_ast_validation(v: ASTValidationVisitor)
45 do
46 var parent = self.parent
47 var path = v.path
48
49 if path.length > 0 then
50 var path_parent = v.path.first
51 if parent == null then
52 self.parent = path_parent
53 #debug "PARENT: expected parent: {path_parent}"
54 v.seen.add(self)
55 else if parent != path_parent then
56 self.parent = path_parent
57 if v.seen.has(self) then
58 debug "DUPLICATE (NOTATREE): already seen node with parent {parent} now with {path_parent}."
59 else
60 v.seen.add(self)
61 debug "PARENT: expected parent: {path_parent}, got {parent}"
62 end
63 end
64 end
65
66 if not isset _location then
67 #debug "LOCATION: unlocated node {v.path.join(", ")}"
68 _location = self.parent.location
69 end
70
71 path.unshift(self)
72 visit_all(v)
73 path.shift
74 end
75 end
76
77 redef class AAnnotation
78 redef fun accept_ast_validation(v)
79 do
80 # Do not enter in annotations
81 end
82 end
83
84 redef class AExpr
85 redef fun accept_ast_validation(v)
86 do
87 super
88 if mtype == null and not is_typed then
89 #debug "TYPING: untyped expression"
90 end
91 end
92 end
93
94 redef class APlaceholderExpr
95 redef fun accept_ast_validation(v)
96 do
97 super
98 debug "PARENT: remaining placeholder"
99 end
100 end