Merge: new option --define
authorJean Privat <jean@pryen.org>
Sat, 11 Oct 2014 12:24:15 +0000 (08:24 -0400)
committerJean Privat <jean@pryen.org>
Sat, 11 Oct 2014 12:24:15 +0000 (08:24 -0400)
It is standard in compiled programs to be able to setup some program configuration at compile-time.

Eg. C compilers accept a command-line option `-D` to define macros that will be used inside programs.
It is useful for configuring some string (like `-D PREFIX=/opt/nit/`, or `-D PORT=8081`) or activate some parts (conditional compilation, eg `-D WITH_SSL`).

This PR brings an equivalent capability to Nit engines through the new `-D` (`--define`) option.

The design behind the -D it to enable specific refinement of top-level functions at link-time.
Thus, basically

~~~sh
$ cat my_foo.nit
import foo
redef fun prefix do return "/opt/nit/"
$ nitg my_foo.nit
~~~

can be simplified into

~~~sh
$ nitg foo.nit -D prefix=/opt/nit/
~~~

Like `-m`, the `-D` creates a fictive module that refines the main module of the program.

`-D` also use the return type of the refined method to know how to interpret the text of the value.
Currently only Int, String and Bool is supported.

~~~nit
module foo
fun str: String do return "test"
fun num: Int do return 1
fun flag: Bool do return false
print str
print num
print flag
~~~

~~~sh
$ nitg foo.nit
$ ./foo
test
1
false
$ nitg foo.nit -D str=hello -D num=42 -D flag
$ ./foo
hello
42
true
~~~

The code of the PR is quite straightforward and show again that the new model is quite robust.

As usual, the first commits are some cleanup. The fun stuff is in the latter commits.

Pull-Request: #815
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

1  2 
src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit
src/rapid_type_analysis.nit

@@@ -22,6 -22,7 +22,7 @@@ import semantiz
  import platform
  import c_tools
  private import annotation
+ import mixin
  
  # Add compiling options
  redef class ToolContext
@@@ -310,7 -311,16 +311,16 @@@ class MakefileToolchai
  
        fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
  
-       fun default_outname(mainmodule: MModule): String do return mainmodule.name
+       fun default_outname(mainmodule: MModule): String
+       do
+               # Search a non fictive module
+               var res = mainmodule.name
+               while mainmodule.is_fictive do
+                       mainmodule = mainmodule.in_importation.direct_greaters.first
+                       res = mainmodule.name
+               end
+               return res
+       end
  
        # Combine options and platform informations to get the final path of the outfile
        fun outfile(mainmodule: MModule): String
@@@ -891,6 -901,7 +901,7 @@@ extern void nitni_global_ref_decr( stru
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@@ -1044,7 -1056,7 +1056,7 @@@ abstract class AbstractCompilerVisito
        fun get_property(name: String, recv: MType): MMethod
        do
                assert recv isa MClassType
-               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
+               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
        end
  
        fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
                return res
        end
  
+       # Generate an integer value
+       fun bool_instance(value: Bool): RuntimeVariable
+       do
+               var res = self.new_var(self.get_class("Bool").mclass_type)
+               if value then
+                       self.add("{res} = 1;")
+               else
+                       self.add("{res} = 0;")
+               end
+               return res
+       end
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
                return res
        end
  
+       fun value_instance(object: Object): RuntimeVariable
+       do
+               if object isa Int then
+                       return int_instance(object)
+               else if object isa Bool then
+                       return bool_instance(object)
+               else if object isa String then
+                       return string_instance(object)
+               else
+                       abort
+               end
+       end
        # Generate an array value
        fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
  
@@@ -1813,6 -1850,7 +1850,7 @@@ redef class MMethodDe
        fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var modelbuilder = v.compiler.modelbuilder
+               var val = constant_value
                if modelbuilder.mpropdef2npropdef.has_key(self) then
                        var npropdef = modelbuilder.mpropdef2npropdef[self]
                        var oldnode = v.current_node
                        self.compile_parameter_check(v, arguments)
                        nclassdef.compile_to_c(v, self, arguments)
                        v.current_node = oldnode
+               else if val != null then
+                       v.ret(v.value_instance(val))
                else
                        abort
                end
@@@ -2601,12 -2641,6 +2641,12 @@@ redef class AForExp
                v.compile_callsite(next_meth, [it])
                v.add("\}")
                v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
 +
 +              var method_finish = self.method_finish
 +              if method_finish != null then
 +                      # TODO: Find a way to call this also in long escape (e.g. return)
 +                      v.compile_callsite(method_finish, [it])
 +              end
        end
  end
  
@@@ -3025,9 -3059,6 +3065,6 @@@ en
  # Create a tool context to handle options and paths
  var toolcontext = new ToolContext
  
- var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
- toolcontext.option_context.add_option(opt_mixins)
  toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
  
  # We do not add other options, so process them now!
@@@ -3046,7 -3077,6 +3083,6 @@@ en
  
  # Here we load an process all modules passed on the command line
  var mmodules = modelbuilder.parse(arguments)
- var mixins = modelbuilder.parse(opt_mixins.value)
  
  if mmodules.is_empty then return
  modelbuilder.run_phases
  for mmodule in mmodules do
        toolcontext.info("*** PROCESS {mmodule} ***", 1)
        var ms = [mmodule]
-       if not mixins.is_empty then
-               ms.add_all mixins
-       end
        toolcontext.run_global_phases(ms)
  end
@@@ -20,6 -20,7 +20,7 @@@ module naive_interprete
  import literal
  import semantize
  private import parser::tables
+ import mixin
  
  redef class ToolContext
        # --discover-call-trace
@@@ -247,6 -248,19 +248,19 @@@ class NaiveInterprete
                return res
        end
  
+       fun value_instance(object: Object): Instance
+       do
+               if object isa Int then
+                       return int_instance(object)
+               else if object isa Bool then
+                       return bool_instance(object)
+               else if object isa String then
+                       return string_instance(object)
+               else
+                       abort
+               end
+       end
        # Return a new native string initialized with `txt`
        fun native_string_instance(txt: String): Instance
        do
                return new PrimitiveInstance[Buffer](ic.mclass_type, val)
        end
  
+       # Return a new String instance for `txt`
+       fun string_instance(txt: String): Instance
+       do
+               var nat = native_string_instance(txt)
+               var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.length)])
+               assert res != null
+               return res
+       end
        # The current frame used to store local variables of the current method executed
        fun frame: Frame do return frames.first
  
  
                # Look for the AST node that implements the property
                var mproperty = mpropdef.mproperty
+               var val = mpropdef.constant_value
                if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then
                        var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef]
                        self.parameter_check(npropdef, mpropdef, args)
                        var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef]
                        self.parameter_check(nclassdef, mpropdef, args)
                        return nclassdef.call(self, mpropdef, args)
+               else if val != null then
+                       return value_instance(val)
                else
                        fatal("Fatal Error: method {mpropdef} not found in the AST")
                        abort
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@@ -1330,7 -1357,7 +1357,7 @@@ redef class AForExp
                #self.debug("iter {iter}")
                loop
                        var isok = v.callsite(method_is_ok, [iter]).as(not null)
 -                      if not isok.is_true then return
 +                      if not isok.is_true then break
                        if self.variables.length == 1 then
                                var item = v.callsite(method_item, [iter]).as(not null)
                                #self.debug("item {item}")
                                abort
                        end
                        v.stmt(self.n_block)
 -                      if v.is_break(self.escapemark) then return
 +                      if v.is_break(self.escapemark) then break
                        v.is_continue(self.escapemark) # Clear the break
 -                      if v.is_escaping then return
 +                      if v.is_escaping then break
                        v.callsite(method_next, [iter])
                end
 +              var method_finish = self.method_finish
 +              if method_finish != null then
 +                      v.callsite(method_finish, [iter])
 +              end
        end
  end
  
@@@ -1464,9 -1487,7 +1491,7 @@@ redef class AStringFormExp
        redef fun expr(v)
        do
                var txt = self.value.as(not null)
-               var nat = v.native_string_instance(txt)
-               var res = v.send(v.force_get_primitive_method("to_s", nat.mtype), [nat]).as(not null)
-               return res
+               return v.string_instance(txt)
        end
  end
  
@@@ -244,6 -244,9 +244,9 @@@ class RapidTypeAnalysi
                                        if mmethoddef.mproperty.is_root_init and not mmethoddef.is_intro then
                                                self.add_super_send(v.receiver, mmethoddef)
                                        end
+                               else if mmethoddef.constant_value != null then
+                                       # Make the return type live
+                                       v.add_type(mmethoddef.msignature.return_mtype.as(MClassType))
                                else
                                        abort
                                end
@@@ -675,8 -678,6 +678,8 @@@ redef class AForExp
                        abort
                end
                v.add_callsite(self.method_next)
 +              var mf = self.method_finish
 +              if mf != null then v.add_callsite(mf)
        end
  end