diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 31e1e6dca5..e04910b706 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -23,6 +23,7 @@ import platform import c_tools private import annotation import mixin +import define import counter import pkgconfig private import explain_assert_api diff --git a/src/define.nit b/src/define.nit new file mode 100644 index 0000000000..d7ee14fb70 --- /dev/null +++ b/src/define.nit @@ -0,0 +1,103 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Top-level function refinements at link-time. +module define + +import mixin + +redef class ToolContext + # --define + var opt_defines = new OptionArray("Define a specific property", "-D", "--define") + + redef init + do + super + option_context.add_option(opt_defines) + end + + redef fun make_main_module(mmodules) + do + var mainmodule = super + + var defines = opt_defines.value + if not defines.is_empty then + var location = mainmodule.location + var model = mainmodule.model + + # Create a fictive module if needed + if mainmodule == mmodules.first then + mainmodule = new MModule(model, null, mainmodule.name + "-d", location) + mainmodule.set_imported_mmodules(mmodules) + mainmodule.is_fictive = true + mainmodule.first_real_mmodule = mmodules.first + end + + var recv = mainmodule.sys_type + assert recv != null + var mclassdef = new MClassDef(mainmodule, recv, location) + mclassdef.add_in_hierarchy + + for define in defines do + var spl = define.split_once_on('=') + var name = spl.first + var val = null + if spl.length > 1 then val = spl[1] + var prop = mainmodule.try_get_primitive_method(name, recv.mclass) + if prop == null then + error(null, "Error: --define: no top-level function `{name}`.") + continue + end + var ret = prop.intro.msignature.return_mtype + var v + if ret == null then + error(null, "Error: --define: method `{prop}` is not a function.") + continue + else if ret.to_s == "Bool" then + if val == null or val == "true" then + v = true + else if val == "false" then + v = false + else + error(null, "Error: --define: method `{prop}` needs a Bool.") + continue + end + else if ret.to_s == "Int" then + if val != null and val.is_numeric then + v = val.to_i + else + error(null, "Error: --define: method `{prop}` needs a Int.") + continue + end + else if ret.to_s == "String" then + if val != null then + v = val + else + error(null, "Error: --define: method `{prop}` needs a String.") + continue + end + else + error(null, "Error: --define: method `{prop}` returns an unsupported type `{ret}`.") + continue + end + var pd = new MMethodDef(mclassdef, prop, location) + pd.msignature = prop.intro.msignature + pd.constant_value = v + end + check_errors + end + + return mainmodule + end +end diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index e5adfe68f6..468a2e29f5 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -21,6 +21,7 @@ import literal import semantize private import parser::tables import mixin +import define private import model::serialize_model private import frontend::explain_assert_api diff --git a/src/mixin.nit b/src/mixin.nit index 1e39970417..3a9f5af3ef 100644 --- a/src/mixin.nit +++ b/src/mixin.nit @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Loading and additional module refinements at link-time. +# Loading of additional modules at link-time. # # Used to factorize some code used by the engines. module mixin @@ -22,13 +22,11 @@ import modelbuilder redef class ToolContext # --mixin var opt_mixins = new OptionArray("Additional module to mix-in", "-m", "--mixin") - # --define - var opt_defines = new OptionArray("Define a specific property", "-D", "--define") redef init do super - option_context.add_option(opt_mixins, opt_defines) + option_context.add_option(opt_mixins) end redef fun make_main_module(mmodules) @@ -38,76 +36,6 @@ redef class ToolContext mmodules.add_all modelbuilder.parse(opt_mixins.value) modelbuilder.run_phases end - - var mainmodule = super - - var defines = opt_defines.value - if not defines.is_empty then - var location = mainmodule.location - var model = mainmodule.model - - # Create a fictive module if needed - if mainmodule == mmodules.first then - mainmodule = new MModule(model, null, mainmodule.name + "-d", location) - mainmodule.set_imported_mmodules(mmodules) - mainmodule.is_fictive = true - mainmodule.first_real_mmodule = mmodules.first - end - - var recv = mainmodule.sys_type - assert recv != null - var mclassdef = new MClassDef(mainmodule, recv, location) - mclassdef.add_in_hierarchy - - for define in defines do - var spl = define.split_once_on('=') - var name = spl.first - var val = null - if spl.length > 1 then val = spl[1] - var prop = mainmodule.try_get_primitive_method(name, recv.mclass) - if prop == null then - error(null, "Error: --define: no top-level function `{name}`.") - continue - end - var ret = prop.intro.msignature.return_mtype - var v - if ret == null then - error(null, "Error: --define: method `{prop}` is not a function.") - continue - else if ret.to_s == "Bool" then - if val == null or val == "true" then - v = true - else if val == "false" then - v = false - else - error(null, "Error: --define: method `{prop}` needs a Bool.") - continue - end - else if ret.to_s == "Int" then - if val != null and val.is_numeric then - v = val.to_i - else - error(null, "Error: --define: method `{prop}` needs a Int.") - continue - end - else if ret.to_s == "String" then - if val != null then - v = val - else - error(null, "Error: --define: method `{prop}` needs a String.") - continue - end - else - error(null, "Error: --define: method `{prop}` returns an unsupported type `{ret}`.") - continue - end - var pd = new MMethodDef(mclassdef, prop, location) - pd.msignature = prop.intro.msignature - pd.constant_value = v - end - check_errors - end - - return mainmodule + return super end end diff --git a/src/modelbuilder.nit b/src/modelbuilder.nit index b44270432a..2f679eaf84 100644 --- a/src/modelbuilder.nit +++ b/src/modelbuilder.nit @@ -36,21 +36,11 @@ redef class ToolContext # Combine module to make a single one if required. fun make_main_module(mmodules: Array[MModule]): MModule do - assert not mmodules.is_empty - var mainmodule - # We need a main module, so we build it by importing all modules - mainmodule = new MModule(modelbuilder.model, null, mmodules.first.name + "-m", new Location(mmodules.first.location.file, 0, 0, 0, 0)) - mainmodule.is_fictive = true - mainmodule.first_real_mmodule = mmodules.first.first_real_mmodule - mainmodule.set_imported_mmodules(mmodules) - modelbuilder.apply_conditional_importations(mainmodule) - if mainmodule.in_importation.direct_greaters.length == 1 and mainmodule.in_importation.direct_greaters.first == mmodules.first then - # Drop the fictive module if not needed - mainmodule = mmodules.first - else - # Or else run phases on it - modelbuilder.run_phases - end + var mainmodule = modelbuilder.combine_main_module(mmodules) + + # Run phases on the fictive module if we created one + if mainmodule.is_fictive then modelbuilder.run_phases + return mainmodule end @@ -89,6 +79,27 @@ end redef class ModelBuilder + # Combine `mmodules` into the main module of a program and apply conditional importations + # + # Create a fictive module to import all the modules, unless the first one already + # imports the others, then we can reuse it. + fun combine_main_module(mmodules: Array[MModule]): MModule + do + assert not mmodules.is_empty + var mainmodule + # We need a main module, so we build it by importing all modules + mainmodule = new MModule(model, null, mmodules.first.name + "-m", new Location(mmodules.first.location.file, 0, 0, 0, 0)) + mainmodule.is_fictive = true + mainmodule.first_real_mmodule = mmodules.first.first_real_mmodule + mainmodule.set_imported_mmodules(mmodules) + apply_conditional_importations(mainmodule) + if mainmodule.in_importation.direct_greaters.length == 1 and mainmodule.in_importation.direct_greaters.first == mmodules.first then + # Drop the fictive module if not needed + mainmodule = mmodules.first + end + return mainmodule + end + # Run phases on all loaded modules fun run_phases do diff --git a/src/nitls.nit b/src/nitls.nit index b716b02c4a..60be1fee0c 100644 --- a/src/nitls.nit +++ b/src/nitls.nit @@ -18,6 +18,7 @@ module nitls import modelbuilder +import mixin import loader_header_only import ordered_tree import console @@ -154,9 +155,10 @@ if sum == 0 then end var mmodules = mb.scan_full(files) +var mixins = mb.scan_full(tc.opt_mixins.value) # Load modules to get more informations -for mmodule in mmodules do +for mmodule in mmodules + mixins do if not opt_paths.value or opt_depends.value then var ast = mmodule.parse(mb) if ast != null and opt_depends.value then @@ -167,6 +169,9 @@ end #tc.check_errors if opt_depends.value then + # Apply the conditional importations + for mmodule in mmodules do mb.combine_main_module([mmodule] + mixins) + # Extends the list of module with the loaded ones mmodules = mb.parsed_modules.to_a end diff --git a/tests/nitls.args b/tests/nitls.args index 28b5957d38..f34e2789ec 100644 --- a/tests/nitls.args +++ b/tests/nitls.args @@ -12,3 +12,8 @@ test_prog/game/excluded.nit test_prog/game/excluded_dir/more.nit -t --no-color -M nitls_header_redef.nit -M error_mod_unk.nit --no-color -M nitls_error_header.nit +-M base_module_conditional.nit +-M base_module_conditional_client.nit +-M base_module_conditional.nit module_2.nit +-M base_module_conditional.nit -m module_2.nit +-M base_module_conditional.nit module_0.nit -m module_2.nit diff --git a/tests/sav/nitls_args10.res b/tests/sav/nitls_args10.res index a5ab79683f..ce78089855 100644 --- a/tests/sav/nitls_args10.res +++ b/tests/sav/nitls_args10.res @@ -1,2 +1,3 @@ base_simple.nit +base_simple_import.nit nitls_header_annotations.nit diff --git a/tests/sav/nitls_args15.res b/tests/sav/nitls_args15.res new file mode 100644 index 0000000000..c8869d728a --- /dev/null +++ b/tests/sav/nitls_args15.res @@ -0,0 +1,2 @@ +base_module_conditional.nit +module_0.nit diff --git a/tests/sav/nitls_args16.res b/tests/sav/nitls_args16.res new file mode 100644 index 0000000000..972462f7cf --- /dev/null +++ b/tests/sav/nitls_args16.res @@ -0,0 +1,5 @@ +base_module_conditional.nit +base_module_conditional_client.nit +module_0.nit +module_1.nit +module_2.nit diff --git a/tests/sav/nitls_args17.res b/tests/sav/nitls_args17.res new file mode 100644 index 0000000000..928f1f27fc --- /dev/null +++ b/tests/sav/nitls_args17.res @@ -0,0 +1,3 @@ +base_module_conditional.nit +module_0.nit +module_2.nit diff --git a/tests/sav/nitls_args18.res b/tests/sav/nitls_args18.res new file mode 100644 index 0000000000..41f89a5a42 --- /dev/null +++ b/tests/sav/nitls_args18.res @@ -0,0 +1,4 @@ +base_module_conditional.nit +module_0.nit +module_1.nit +module_2.nit diff --git a/tests/sav/nitls_args19.res b/tests/sav/nitls_args19.res new file mode 100644 index 0000000000..41f89a5a42 --- /dev/null +++ b/tests/sav/nitls_args19.res @@ -0,0 +1,4 @@ +base_module_conditional.nit +module_0.nit +module_1.nit +module_2.nit