nitj: implement aborts
[nit.git] / src / compiler / java_compiler.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 # Compile Nit code to Java code
16 #
17 # 3 runtime structures are used to represent Nit instance in Java generated code:
18 # * `RTClass` to represent a class, it's super-type table and its VFT
19 # * `RTMethod` to reprensent a compiled method definition
20 # * `RTVal` to reprensent a Nit instance, the null value or a native value
21 #
22 # More details are given in the documentation of these 3 classes.
23 #
24 # TODO Factorize with `abstract_compiler`
25 module java_compiler
26
27 import rapid_type_analysis
28 import frontend
29
30 redef class ToolContext
31
32 # Where to output the generated binary
33 var opt_output = new OptionString("Output file", "-o", "--output")
34
35 # Where to output tmp files
36 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
37
38 redef init do
39 super
40 option_context.add_option(opt_output, opt_compile_dir)
41 end
42 end
43
44 redef class ModelBuilder
45
46 # Start the Java compiler
47 fun run_java_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis) do
48 var time0 = get_time
49 toolcontext.info("*** GENERATING JAVA ***", 1)
50
51 var compiler = new JavaCompiler(mainmodule, self, runtime_type_analysis)
52 compiler.do_compilation
53
54 var time1 = get_time
55 toolcontext.info("*** END GENERATING JAVA: {time1-time0} ***", 2)
56 write_and_make(compiler)
57 end
58
59 # Write Java code and compile it into an executable jar
60 fun write_and_make(compiler: JavaCompiler) do
61 var time0 = get_time
62 toolcontext.info("*** WRITING JAVA ***", 1)
63
64 compiler.compile_dir.mkdir
65
66 var jfiles = write_java_files(compiler)
67
68 var time1 = get_time
69 toolcontext.info("*** END WRITING JAVA: {time1-time0} ***", 2)
70
71 time0 = time1
72 toolcontext.info("*** COMPILING JAVA ***", 1)
73
74 build_with_make(compiler, jfiles)
75 write_shell_script(compiler)
76
77 time1 = get_time
78 toolcontext.info("*** END COMPILING JAVA: {time1-time0} ***", 2)
79 end
80
81 # Write files managed by `compiler` into concrete files
82 fun write_java_files(compiler: JavaCompiler): Array[String] do
83 var jfiles = new Array[String]
84 for f in compiler.files do
85 var file = new FileWriter.open("{compiler.compile_dir}/{f.filename}")
86 for line in f.lines do file.write(line)
87 file.close
88 jfiles.add(f.filename)
89 end
90 return jfiles
91 end
92
93 # Compile Java generated files using `make`
94 fun build_with_make(compiler: JavaCompiler, jfiles: Array[String]) do
95 write_manifest(compiler)
96 write_makefile(compiler, jfiles)
97 var compile_dir = compiler.compile_dir
98 var outname = compiler.outname.to_path.filename
99 toolcontext.info("make -N -C {compile_dir} -f {outname}.mk", 2)
100 var res
101 if toolcontext.verbose_level >= 3 then
102 res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1")
103 else
104 res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1 > /dev/null")
105 end
106 if res != 0 then toolcontext.error(null, "make failed! Error code: {res}.")
107 end
108
109 # Write the Makefile used to compile Java generated files into an executable jar
110 fun write_makefile(compiler: JavaCompiler, jfiles: Array[String]) do
111 # list class files from jfiles
112 var ofiles = new List[String]
113 for f in jfiles do ofiles.add(f.strip_extension(".java") + ".class")
114
115 var compile_dir = compiler.compile_dir
116 var outname = compiler.outname.to_path.filename
117 var outpath = (sys.getcwd / compiler.outname).simplify_path
118 var makename = "{compile_dir}/{outname}.mk"
119 var makefile = new FileWriter.open(makename)
120
121 makefile.write("JC = javac\n")
122 makefile.write("JAR = jar\n\n")
123
124 makefile.write("all: {outpath}.jar\n\n")
125
126 makefile.write("{outpath}.jar: {compiler.mainmodule.jname}_Main.class\n")
127 makefile.write("\t$(JAR) cfm {outpath}.jar {outname}.mf {ofiles.join(" ")}\n\n")
128
129 makefile.write("{compiler.mainmodule.jname}_Main.class:\n")
130 makefile.write("\t$(JC) {jfiles.join(" ")}\n\n")
131
132 makefile.write("clean:\n")
133 makefile.write("\trm {ofiles.join(" ")} 2>/dev/null\n\n")
134
135 makefile.close
136 toolcontext.info("Generated makefile: {makename}", 2)
137 end
138
139 # Write the Java manifest file
140 private fun write_manifest(compiler: JavaCompiler) do
141 var compile_dir = compiler.compile_dir
142 var outname = compiler.outname.to_path.filename
143 var maniffile = new FileWriter.open("{compile_dir}/{outname}.mf")
144 maniffile.write("Manifest-Version: 1.0\n")
145 maniffile.write("Main-Class: {compiler.mainmodule.jname}_Main\n")
146 maniffile.close
147 end
148
149 # Write a simple bash script that runs the jar like it was a binary generated by nitc
150 private fun write_shell_script(compiler: JavaCompiler) do
151 var outname = compiler.outname
152 var shfile = new FileWriter.open(outname)
153 shfile.write("#!/bin/bash\n")
154 shfile.write("java -jar {outname}.jar \"$@\"\n")
155 shfile.close
156 sys.system("chmod +x {outname}")
157 end
158 end
159
160 # Compiler that translates Nit code to Java code
161 class JavaCompiler
162 # The main module of the program currently compiled
163 var mainmodule: MModule
164
165 # Modelbuilder used to know the model and the AST
166 var modelbuilder: ModelBuilder
167
168 # The result of the RTA (used to know live types and methods)
169 var runtime_type_analysis: RapidTypeAnalysis
170
171 # Where to generate tmp files
172 var compile_dir: String is lazy do
173 var dir = modelbuilder.toolcontext.opt_compile_dir.value
174 if dir == null then dir = "nitj_compile"
175 return dir
176 end
177
178 # Name of the generated executable
179 var outname: String is lazy do
180 var name = modelbuilder.toolcontext.opt_output.value
181 if name == null then name = mainmodule.jname
182 return name
183 end
184
185 # The list of all associated files
186 # Used to generate .java files
187 var files: Array[JavaCodeFile] = new Array[JavaCodeFile]
188
189 # Force the creation of a new file
190 # The point is to avoid contamination between must-be-compiled-separately files
191 fun new_file(name: String): JavaCodeFile do
192 var file = new JavaCodeFile(name)
193 files.add(file)
194 return file
195 end
196
197 # Kind of visitor to use
198 type VISITOR: JavaCompilerVisitor
199
200 # Initialize a visitor specific for the compiler engine
201 fun new_visitor(filename: String): VISITOR do
202 return new JavaCompilerVisitor(self, new_file(filename))
203 end
204
205 # RuntimeModel representation
206 private var rt_model: JavaRuntimeModel is lazy do return new JavaRuntimeModel
207
208 # Compile Nit code to Java
209 fun do_compilation do
210 # compile java classes used to represents the runtime model of the program
211 rt_model.compile_rtmodel(self)
212
213 # compile class structures
214 compile_mclasses_to_java
215
216 # compile method structures
217 compile_mmethods_to_java
218
219 # TODO compile main
220 modelbuilder.toolcontext.info("NOT YET IMPLEMENTED", 0)
221 end
222
223 # Generate a `RTClass` for each `MClass` found in model
224 #
225 # This is a global phase because we need to know all the program to build
226 # attributes, fill vft and type table.
227 fun compile_mclasses_to_java do
228 for mclass in mainmodule.model.mclasses do
229 mclass.compile_to_java(new_visitor("{mclass.rt_name}.java"))
230 end
231 end
232
233 # Generate a `RTMethod` for each `MMethodDef` found in model
234 #
235 # This is a separate phase.
236 fun compile_mmethods_to_java do
237 for mmodule in mainmodule.in_importation.greaters do
238 for mclassdef in mmodule.mclassdefs do
239 for mdef in mclassdef.mpropdefs do
240 if mdef isa MMethodDef then
241 mdef.compile_to_java(new_visitor("{mdef.rt_name}.java"))
242 end
243 end
244 end
245 end
246 end
247 end
248
249 # The class visiting the AST
250 #
251 # A visitor is attached to one JavaCodeFile it writes into.
252 class JavaCompilerVisitor
253 super Visitor
254
255 # JavaCompiler used with this visitor
256 type COMPILER: JavaCompiler
257
258 # The associated compiler
259 var compiler: JavaCompiler
260
261 # The file to write generated code into
262 var file: JavaCodeFile
263
264 # Names handling
265
266 private var names = new HashSet[String]
267 private var last: Int = 0
268
269 # Return a new name based on `s` and unique in the visitor
270 fun get_name(s: String): String do
271 if not self.names.has(s) then
272 self.names.add(s)
273 return s
274 end
275 var i = self.last + 1
276 loop
277 var s2 = s + i.to_s
278 if not self.names.has(s2) then
279 self.last = i
280 self.names.add(s2)
281 return s2
282 end
283 i = i + 1
284 end
285 end
286
287 # Variables handling
288
289 # Registered variables
290 protected var variables = new HashMap[Variable, RuntimeVariable]
291
292 # Return the local RuntimeVariable associated to a Nit local variable
293 fun variable(variable: Variable): RuntimeVariable do
294 if variables.has_key(variable) then
295 return variables[variable]
296 else
297 var name = get_name("var_{variable.name}")
298 var mtype = variable.declared_type.as(not null)
299 # TODO mtype = self.anchor(mtype)
300 var res = decl_var(name, mtype)
301 variables[variable] = res
302 return res
303 end
304 end
305
306 # Return a new uninitialized local RuntimeVariable with `name`
307 fun decl_var(name: String, mtype: MType): RuntimeVariable do
308 var res = new RuntimeVariable(name, mtype, mtype)
309 add("{mtype.java_type} {name} /* : {mtype} */;")
310 return res
311 end
312
313 # Return a new uninitialized local RuntimeVariable
314 fun new_var(mtype: MType): RuntimeVariable do
315 # TODO mtype = self.anchor(mtype)
316 var name = self.get_name("var")
317 return decl_var(name, mtype)
318 end
319
320 # Calls handling
321
322 # The current `JavaStaticFrame`
323 var frame: nullable JavaStaticFrame = null is writable
324
325 # Code generation
326
327 # Add a line (will be suffixed by `\n`)
328 fun add(line: String) do file.lines.add("{line}\n")
329
330 # Add a new partial line (no `\n` suffix)
331 fun addn(line: String) do file.lines.add(line)
332
333 # Compile a statement (if any)
334 fun stmt(nexpr: nullable AExpr) do
335 if nexpr == null then return
336 var old = self.current_node
337 current_node = nexpr
338 nexpr.stmt(self)
339 current_node = old
340 end
341
342 # Compile an expression an return its result
343 # `mtype` is the expected return type, pass null if no specific type is expected.
344 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable do
345 var old = current_node
346 current_node = nexpr
347
348 var res = null
349 if nexpr.mtype != null then
350 res = nexpr.expr(self)
351 end
352 assert res != null
353 current_node = old
354 return res
355 end
356
357 # Correctly assign a left and a right value
358 # Boxing and unboxing is performed if required
359 fun assign(left, right: RuntimeVariable) do
360 # TODO right = autobox(right, left.mtype)
361 add("{left} = {right};")
362 end
363
364 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
365 #
366 # `mtype` is used for the Java return variable initialization.
367 fun new_expr(jexpr: String, mtype: MType): RuntimeVariable do
368 var res = new_var(mtype)
369 add("{res} = {jexpr};")
370 return res
371 end
372
373 # Generate generic abort
374 #
375 # Used by aborts, asserts, casts, etc.
376 fun add_abort(message: String) do
377 add("System.err.print(\"Runtime error: {message}\");")
378 var node = current_node
379 if node != null then
380 add("System.err.print(\" ({node.location.short_location})\");")
381 end
382 add("System.err.println(\"\");")
383 add("System.exit(1);")
384 end
385
386 # Display a info message
387 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
388 end
389
390 # A file containing Java code.
391 class JavaCodeFile
392
393 # File name
394 var filename: String
395
396 # Lines to write
397 var lines: List[String] = new List[String]
398 end
399
400 redef class MEntity
401 # A Java compatible name for `self`
402 private fun jname: String do return name.to_cmangle
403 end
404
405 # Handler for runtime classes generation
406 #
407 # We need 3 kinds of runtime structures:
408 # * `RTClass` to represent a global class
409 # * `RTMethod` to represent a method definition
410 # * `RTVal` to represent runtime variables
411 class JavaRuntimeModel
412
413 # Compile JavaRuntimeModel structures
414 fun compile_rtmodel(compiler: JavaCompiler) do
415 compile_rtclass(compiler)
416 compile_rtmethod(compiler)
417 compile_rtval(compiler)
418 end
419
420 # Compile the abstract runtime class structure
421 #
422 # Runtime classes have 3 attributes:
423 # * `class_name`: the class name as a String
424 # * `vft`: the virtual function table for the class (flattened)
425 # * `supers`: the super type table (used for type tests)
426 fun compile_rtclass(compiler: JavaCompiler) do
427 var v = compiler.new_visitor("RTClass.java")
428 v.add("import java.util.HashMap;")
429 v.add("public abstract class RTClass \{")
430 v.add(" public String class_name;")
431 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
432 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
433 v.add(" protected RTClass() \{\}")
434 v.add("\}")
435 end
436
437 # Compile the abstract runtime method structure
438 #
439 # Method body is executed through the `exec` method:
440 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
441 # * `exec` always returns a RTVal (or null if the Nit return type is void)
442 fun compile_rtmethod(compiler: JavaCompiler) do
443 var v = compiler.new_visitor("RTMethod.java")
444 v.add("public abstract class RTMethod \{")
445 v.add(" protected RTMethod() \{\}")
446 v.add(" public abstract RTVal exec(RTVal[] args);")
447 v.add("\}")
448 end
449
450 # Compile the runtime value structure
451 #
452 # RTVal both represents object instances and primitives values:
453 # * object instances:
454 # * `rtclass` the class of the RTVal is instance of
455 # * `attrs` contains the attributes of the instance
456 # * primitive values:
457 # * `rtclass` represents the class of the primitive value Nit type
458 # * `value` contains the primitive value of the instance
459 # * null values:
460 # * they must have both `rtclass` and `value` as null
461 fun compile_rtval(compiler: JavaCompiler) do
462 var v = compiler.new_visitor("RTVal.java")
463 v.add("import java.util.HashMap;")
464 v.add("public class RTVal \{")
465 v.add(" public RTClass rtclass;")
466 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
467 v.add(" Object value;")
468 v.add(" public RTVal(RTClass rtclass) \{")
469 v.add(" this.rtclass = rtclass;")
470 v.add(" \}")
471 v.add(" public RTVal(RTClass rtclass, Object value) \{")
472 v.add(" this.rtclass = rtclass;")
473 v.add(" this.value = value;")
474 v.add(" \}")
475 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
476 v.add("\}")
477 end
478 end
479
480 # A runtime variable hold a runtime value in Java.
481 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
482 class RuntimeVariable
483
484 # The name of the variable in the Java code
485 var name: String
486
487 # The static type of the variable (as declard in Java)
488 var mtype: MType
489
490 # The current casted type of the variable (as known in Nit)
491 var mcasttype: MType is writable
492
493 # If the variable exaclty a mcasttype?
494 # false (usual value) means that the variable is a mcasttype or a subtype.
495 var is_exact: Bool = false is writable
496
497 # Is this variable declared as a RTVal or a Java primitive one?
498 var is_boxed = false
499
500 redef fun to_s do return name
501
502 redef fun inspect
503 do
504 var exact_str
505 if self.is_exact then
506 exact_str = " exact"
507 else
508 exact_str = ""
509 end
510 var type_str
511 if self.mtype == self.mcasttype then
512 type_str = "{mtype}{exact_str}"
513 else
514 type_str = "{mtype}({mcasttype}{exact_str})"
515 end
516 return "<{name}:{type_str}>"
517 end
518 end
519
520 # The static context of a visited property in a `JavaCompilerVisitor`
521 class JavaStaticFrame
522 # The associated visitor
523 var visitor: JavaCompilerVisitor
524
525 # The executed property.
526 # A Method in case of a call, an attribute in case of a default initialization.
527 var mpropdef: MPropDef
528
529 # The static type of the receiver
530 var receiver: MClassType
531
532 # Arguments of the method (the first is the receiver)
533 var arguments: Array[RuntimeVariable]
534
535 # The runtime_variable associated to the return (in a function)
536 var returnvar: nullable RuntimeVariable = null is writable
537
538 # The label at the end of the property
539 var returnlabel: nullable String = null is writable
540 end
541
542 redef class Location
543 # Return a shortened version of the location with `"{file}:{line_start}"`
544 fun short_location: String do
545 var file = self.file
546 if file == null then return "<no file>:{line_start}"
547 return "{file.filename.escape_to_c}:{line_start}"
548 end
549 end
550
551 redef class MType
552 # Return the Java type associated to a given Nit static type
553 fun java_type: String do return "RTVal"
554
555 # Is the associated Java type a primitive one?
556 #
557 # ENSURE `result == (java_type != "Object")`
558 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
559 end
560
561 redef class MClassType
562
563 redef var java_type is lazy do
564 if mclass.name == "Int" then
565 return "int"
566 else if mclass.name == "Bool" then
567 return "boolean"
568 else if mclass.name == "Char" then
569 return "char"
570 else if mclass.name == "Float" then
571 return "double"
572 else if mclass.name == "Byte" then
573 return "byte"
574 else if mclass.name == "NativeString" then
575 return "String"
576 else if mclass.name == "NativeArray" then
577 return "Array"
578 end
579 return "RTVal"
580 end
581 end
582
583 redef class MClass
584
585 # Runtime name
586 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
587
588 # Generate a Java RTClass for a Nit MClass
589 fun compile_to_java(v: JavaCompilerVisitor) do
590 v.add("public class {rt_name} extends RTClass \{")
591 v.add(" protected static RTClass instance;")
592 v.add(" private {rt_name}() \{")
593 v.add(" this.class_name = \"{name}\";")
594 compile_vft(v)
595 compile_type_table(v)
596 v.add(" \}")
597 v.add(" public static RTClass get{rt_name}() \{")
598 v.add(" if(instance == null) \{")
599 v.add(" instance = new {rt_name}();")
600 v.add(" \}")
601 v.add(" return instance;")
602 v.add(" \}")
603 v.add("\}")
604 end
605
606 # Compile the virtual function table for the mclass
607 private fun compile_vft(v: JavaCompilerVisitor) do
608 # TODO handle generics
609 if mclass_type.need_anchor then return
610 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
611 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
612
613 var mainmodule = v.compiler.mainmodule
614 for mclassdef in mclassdefs.reversed do
615 for mprop in mclassdef.intro_mproperties do
616 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
617 if not mpropdef isa MMethodDef then continue
618 var rt_name = mpropdef.rt_name
619 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
620
621 # fill super next definitions
622 while mpropdef.has_supercall do
623 var prefix = mpropdef.full_name
624 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
625 rt_name = mpropdef.rt_name
626 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
627 end
628 end
629 end
630 end
631
632 # Compile the type table for the MClass
633 fun compile_type_table(v: JavaCompilerVisitor) do
634 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
635 if pclass == self then
636 v.add("supers.put(\"{pclass.jname}\", this);")
637 else
638 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
639 end
640 end
641 end
642 end
643
644 redef class MMethodDef
645
646 # Runtime name
647 private fun rt_name: String do
648 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
649 end
650
651 # Generate a Java RTMethod for `self`
652 fun compile_to_java(v: JavaCompilerVisitor) do
653 v.add("public class {rt_name} extends RTMethod \{")
654 v.add(" protected static RTMethod instance;")
655 v.add(" public static RTMethod get{rt_name}() \{")
656 v.add(" if(instance == null) \{")
657 v.add(" instance = new {rt_name}();")
658 v.add(" \}")
659 v.add(" return instance;")
660 v.add(" \}")
661 v.add(" @Override")
662 v.add(" public RTVal exec(RTVal[] args) \{")
663 compile_inside_to_java(v)
664 v.add(" \}")
665 v.add("\}")
666 end
667
668 # Compile the body of this function
669 fun compile_inside_to_java(v: JavaCompilerVisitor) do
670
671 var modelbuilder = v.compiler.modelbuilder
672 var node = modelbuilder.mpropdef2node(self)
673
674 if is_abstract then
675 # TODO compile abstract
676 v.info("NOT YET IMPLEMENTED call to abstract method")
677 v.add("return null;")
678 return
679 end
680
681 if node isa APropdef then
682 node.compile_to_java(v, self)
683 else if node isa AClassdef then
684 # TODO compile attributes
685 v.info("NOT YET IMPLEMENTED attribute handling")
686 v.add("return null;")
687 else
688 abort
689 end
690 end
691 end
692
693 redef class APropdef
694
695 # Compile that property definition to java code
696 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
697 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
698 end
699 end
700
701 redef class AMethPropdef
702 redef fun compile_to_java(v, mpropdef) do
703 # TODO Call the implicit super-init
704
705 # Compile intern methods
706 if mpropdef.is_intern then
707 v.info("NOT YET IMPLEMENTED {class_name}::compile_intern")
708 # TODO if compile_intern_to_java(v, mpropdef, arguments) then return
709 v.add("return null;")
710 return
711 end
712
713 # Compile block if any
714 var n_block = n_block
715 if n_block != null then
716 v.stmt(n_block)
717 return
718 end
719 end
720 end
721
722 redef class AExpr
723 # Try to compile self as an expression
724 # Do not call this method directly, use `v.expr` instead
725 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
726 v.info("NOT YET IMPLEMENTED {class_name}::expr")
727 return null
728 end
729
730 # Try to compile self as a statement
731 # Do not call this method directly, use `v.stmt` instead
732 private fun stmt(v: JavaCompilerVisitor) do expr(v)
733 end
734
735 redef class ABlockExpr
736 redef fun stmt(v)
737 do
738 for e in self.n_expr do v.stmt(e)
739 end
740 redef fun expr(v)
741 do
742 var last = self.n_expr.last
743 for e in self.n_expr do
744 if e == last then break
745 v.stmt(e)
746 end
747 return v.expr(last, null)
748 end
749 end
750
751 redef class AAbortExpr
752 redef fun stmt(v) do v.add_abort("Aborted")
753 end
754
755 redef class ADebugTypeExpr
756 redef fun stmt(v) do end # do nothing
757 end