Moving the astvalidation module in astbuilder
[nit.git] / src / astbuilder.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 # Instantiation and transformation of semantic nodes in the AST of expressions and statements
16 module astbuilder
17
18 intrude import semantize::typing
19 intrude import literal
20 intrude import parser
21 intrude import semantize::scope
22
23 # General factory to build semantic nodes in the AST of expressions
24 class ASTBuilder
25 # The module used as reference for the building
26 # It is used to gather types and other stuff
27 var mmodule: MModule
28
29 # The anchor used for some mechanism relying on types
30 var anchor: nullable MClassType
31
32 # Make a new Int literal
33 fun make_int(value: Int): AIntegerExpr
34 do
35 return new AIntegerExpr.make(value, mmodule.int_type)
36 end
37
38 # Make a new instantiation
39 fun make_new(callsite: CallSite, args: nullable Array[AExpr]): ANewExpr
40 do
41 return new ANewExpr.make(callsite, args)
42 end
43
44 # Make a new message send
45 fun make_call(recv: AExpr, callsite: CallSite, args: nullable Array[AExpr]): ACallExpr
46 do
47 return new ACallExpr.make(recv, callsite, args)
48 end
49
50 # Make a new, empty, sequence of statements
51 fun make_block: ABlockExpr
52 do
53 return new ABlockExpr.make
54 end
55
56 # Make a new, empty, loop of statements
57 fun make_loop: ALoopExpr
58 do
59 return new ALoopExpr.make
60 end
61
62 # Make a new variable read
63 fun make_var_read(variable: Variable, mtype: MType): AVarExpr
64 do
65 return new AVarExpr.make(variable, mtype)
66 end
67
68 # Make a new variable assignment
69 fun make_var_assign(variable: Variable, value: AExpr): AVarAssignExpr
70 do
71 return new AVarAssignExpr.make(variable, value)
72 end
73
74 # Make a new attribute read
75 fun make_attr_read(recv: AExpr, attribute: MAttribute): AAttrExpr
76 do
77 var mtype = attribute.intro.static_mtype.resolve_for(recv.mtype.as(not null), anchor, mmodule, true)
78 return new AAttrExpr.make(recv, attribute, mtype)
79 end
80
81 # Make a new attribute assignment
82 fun make_attr_assign(recv: AExpr, attribute: MAttribute, value: AExpr): AAttrAssignExpr
83 do
84 return new AAttrAssignExpr.make(recv, attribute, value)
85 end
86
87 # Make a new escapable block
88 fun make_do: ADoExpr
89 do
90 return new ADoExpr.make
91 end
92
93 # Make a new break for a given escapemark
94 fun make_break(escapemark: EscapeMark): ABreakExpr
95 do
96 return new ABreakExpr.make(escapemark)
97 end
98
99 # Make a new conditional
100 # `mtype` is the return type of the whole if, in case of a ternary operator.
101 fun make_if(condition: AExpr, mtype: nullable MType): AIfExpr
102 do
103 return new AIfExpr.make(condition, mtype)
104 end
105 end
106
107 redef class AExpr
108 # Return a new variable read that contains the value of the expression
109 # This method take care efficiently of creating and initalising an anonymous local variable
110 #
111 # Note: since this method do side-effects (AST replacement), there could be unexpected effects when used as
112 # argument of other methods related to AST transformations.
113 fun make_var_read: AVarExpr
114 do
115 var variable = self.variable_cache
116 if variable == null then
117 assert parent != null
118 var place = detach_with_placeholder
119 variable = new Variable("")
120 variable.declared_type = self.mtype
121 var nvar = new AVarAssignExpr.make(variable, self)
122 place.replace_with(nvar)
123 self.variable_cache = variable
124 end
125 return new AVarExpr.make(variable, variable.declared_type.as(not null))
126 end
127
128 private var variable_cache: nullable Variable
129
130 # The `detach` method completely remove the node in the parent.
131 # However, sometime, it is useful to keep the emplacement of the removed child.
132 #
133 # The standard use case is the insertion of a node between a parent `p` and a child `p.c`.
134 # To create the new node `n`, we need to attach the child to it.
135 # But, to put `n` where `c` was in `p`, the place has to be remembered.
136 #
137 # ~~~nitish
138 # var p: AExpr
139 # var c = p.c
140 # var h = c.detach_with_placeholder
141 # var n = astbuilder.make_XXX(c)
142 # h.replace_with(n)
143 # ~~~
144 fun detach_with_placeholder: AExpr
145 do
146 var h = new APlaceholderExpr.make
147 self.replace_with(h)
148 return h
149 end
150
151
152 # Add `expr` at the end of the block
153 #
154 # REQUIRE: self isa ABlockExpr
155 #
156 # Note: this method, aimed to `ABlockExpr` is promoted to `AExpr` because of the limitations of the hierarchies generated by sablecc3
157 fun add(expr: AExpr)
158 do
159 print "add not implemented in {inspect}"
160 abort
161 end
162
163 redef fun accept_ast_validation(v)
164 do
165 super
166 if mtype == null and not is_typed then
167 #debug "TYPING: untyped expression"
168 end
169 end
170 end
171
172 # A placeholder for a `AExpr` node
173 # Instances are transiantly used to mark some specific emplacements in the AST
174 # during complex transformations.
175 #
176 # Their must not appear in a valid AST
177 #
178 # @see AExpr::detach_with_placeholder
179 class APlaceholderExpr
180 super AExpr
181 private init make
182 do
183 end
184
185 redef fun accept_ast_validation(v)
186 do
187 super
188 debug "PARENT: remaining placeholder"
189 end
190 end
191
192 redef class ABlockExpr
193 private init make
194 do
195 self.is_typed = true
196 end
197
198 redef fun add(expr)
199 do
200 n_expr.add expr
201 end
202 end
203
204 redef class ALoopExpr
205 private init make
206 do
207 _n_kwloop = new TKwloop
208 self.is_typed = true
209 n_block = new ABlockExpr
210 n_block.is_typed = true
211 end
212
213 redef fun add(expr)
214 do
215 n_block.add expr
216 end
217 end
218
219 redef class ADoExpr
220 private init make
221 do
222 _n_kwdo = new TKwdo
223 self.is_typed = true
224 n_block = new ABlockExpr
225 n_block.is_typed = true
226 end
227
228 # Make a new break expression of the given do
229 fun make_break: ABreakExpr
230 do
231 var escapemark = self.break_mark
232 if escapemark == null then
233 escapemark = new EscapeMark(null)
234 self.break_mark = escapemark
235 end
236 return new ABreakExpr.make(escapemark)
237 end
238
239 redef fun add(expr)
240 do
241 n_block.add expr
242 end
243 end
244
245 redef class ABreakExpr
246 private init make(escapemark: EscapeMark)
247 do
248 _n_kwbreak = new TKwbreak
249 self.escapemark = escapemark
250 escapemark.escapes.add self
251 self.is_typed = true
252 end
253 end
254
255 redef class AIfExpr
256 private init make(condition: AExpr, mtype: nullable MType)
257 do
258 _n_kwif = new TKwif
259 _n_expr = condition
260 _n_expr.parent = self
261 _n_kwthen = new TKwthen
262 _n_then = new ABlockExpr.make
263 _n_kwelse = new TKwelse
264 _n_else = new ABlockExpr.make
265 self.mtype = mtype
266 self.is_typed = true
267 end
268 end
269
270 redef class AType
271 private init make
272 do
273 var n_id = new TClassid
274 var n_qid = new AQclassid
275 n_qid.n_id = n_id
276 _n_qid = n_qid
277 end
278 end
279
280 redef class AIntegerExpr
281 private init make(value: Int, t: MType)
282 do
283 self.value = value
284 self._n_integer = new TInteger # dummy
285 self.mtype = t
286 end
287 end
288
289 redef class ANewExpr
290 private init make(callsite: CallSite, args: nullable Array[AExpr])
291 do
292 _n_kwnew = new TKwnew
293 _n_type = new AType.make
294 _n_args = new AListExprs
295 if args != null then
296 n_args.n_exprs.add_all(args)
297 end
298 self.callsite = callsite
299 self.recvtype = callsite.recv.as(MClassType)
300 if callsite.mproperty.is_new then
301 self.mtype = callsite.msignature.return_mtype
302 else
303 self.mtype = callsite.recv
304 end
305 self.is_typed = true
306 end
307 end
308
309 redef class ACallExpr
310 private init make(recv: AExpr, callsite: CallSite, args: nullable Array[AExpr])
311 do
312 self._n_expr = recv
313 _n_args = new AListExprs
314 _n_qid = new AQid
315 _n_qid.n_id = new TId
316 if args != null then
317 self.n_args.n_exprs.add_all(args)
318 end
319 self.callsite = callsite
320 self.mtype = callsite.msignature.return_mtype
321 self.is_typed = true
322 end
323 end
324
325 redef class AAttrExpr
326 private init make(recv: AExpr, attribute: MAttribute, t: MType)
327 do
328 _n_expr = recv
329 recv.parent = self
330 _n_id = new TAttrid
331 mproperty = attribute
332 mtype = t
333 end
334 end
335
336 redef class AAttrAssignExpr
337 private init make(recv: AExpr, attribute: MAttribute, value: AExpr)
338 do
339 _n_expr = recv
340 recv.parent = self
341 _n_id = new TAttrid
342 _n_value = value
343 value.parent = self
344 _n_assign = new TAssign
345 mproperty = attribute
346 mtype = value.mtype
347 end
348 end
349
350 redef class AVarExpr
351 private init make(v: Variable, mtype: MType)
352 do
353 _n_id = new TId
354 variable = v
355 self.mtype = mtype
356 end
357 end
358
359 redef class AVarAssignExpr
360 private init make(v: Variable, value: AExpr)
361 do
362 _n_id = new TId
363 _n_value = value
364 value.parent = self
365 _n_assign = new TAssign
366 variable = v
367 mtype = value.mtype
368 end
369 end
370
371 # Check the consitency of AST
372 class ASTValidationVisitor
373 super Visitor
374 redef fun visit(node)
375 do
376 node.accept_ast_validation(self)
377 end
378 private var path = new CircularArray[ANode]
379 private var seen = new HashSet[ANode]
380 end
381
382 redef class ANode
383 # Recursively validate a AST node.
384 # This ensure that location and parenting are defined and coherent.
385 #
386 # After complex low-level AST manipulation and construction,
387 # it is recommended to call it.
388 #
389 # Note: this just instantiate and run an `ASTValidationVisitor`.
390 fun validate
391 do
392 (new ASTValidationVisitor).enter_visit(self)
393 end
394
395 private fun accept_ast_validation(v: ASTValidationVisitor)
396 do
397 var parent = self.parent
398 var path = v.path
399
400 if path.length > 0 then
401 var path_parent = v.path.first
402 if parent == null then
403 self.parent = path_parent
404 #debug "PARENT: expected parent: {path_parent}"
405 v.seen.add(self)
406 else if parent != path_parent then
407 self.parent = path_parent
408 if v.seen.has(self) then
409 debug "DUPLICATE (NOTATREE): already seen node with parent {parent} now with {path_parent}."
410 else
411 v.seen.add(self)
412 debug "PARENT: expected parent: {path_parent}, got {parent}"
413 end
414 end
415 end
416
417 if not isset _location then
418 #debug "LOCATION: unlocated node {v.path.join(", ")}"
419 _location = self.parent.location
420 end
421
422 path.unshift(self)
423 visit_all(v)
424 path.shift
425 end
426 end
427
428 redef class AAnnotation
429 redef fun accept_ast_validation(v)
430 do
431 # Do not enter in annotations
432 end
433 end