X-Git-Url: http://nitlanguage.org diff --git a/src/nitserial.nit b/src/nitserial.nit index 2fd0f82..2b347f1 100644 --- a/src/nitserial.nit +++ b/src/nitserial.nit @@ -18,49 +18,18 @@ # # Executing on the module `game_logic` will create the module `game_logic_serial` # in the local directory. Mixing the generated module to the main module with -# `nitg game_logic.nit -m game_logic_serial` will create a program supporting +# `nitc game_logic.nit -m game_logic_serial` will create a program supporting # deserialization of all generic types visible from the main module. # # Because the generation is limited to the visible types, a module author might want # generate and include its own serialization support module. module nitserial -import frontend -import rapid_type_analysis -import model_utils import template +import gen_nit -# A Nit module -# -# TODO add more features and move to lib -class NitModule - super Template - - var header: nullable Streamable = null - - # The module's name - var name: Streamable - - # Imports from this module - var imports = new Array[Streamable] - - # Main content of this module - var content = new Array[Streamable] - - redef fun rendering - do - var header = header - if header != null then add header - - var name = name - add "module {name}\n\n" - - for i in imports do add "import {i}\n" - add "\n" - - for l in content do add "{l}\n" - end -end +import frontend +import rapid_type_analysis redef class ToolContext # Where do we put a single result? @@ -69,40 +38,43 @@ redef class ToolContext # Where do we put the result? var opt_dir: OptionString = new OptionString("Output directory", "--dir") + # Depth of the visit and generation + var opt_depth = new OptionEnum(["module", "group", "package"], + "Depth of the visit and generation", 0, "-d", "--depth") + redef init do - option_context.add_option(opt_output, opt_dir) + option_context.add_option(opt_output, opt_dir, opt_depth) super end end redef class MModule # Get the type of the class `Serializable` - fun serializable_type: MClassType is cached do + var serializable_type: MClassType is lazy do return self.get_primitive_class("Serializable").mclass_type end end redef class MType - # Is this type fully visible from `mmodule`? - fun is_visible_from(mmodule: MModule): Bool is abstract + # List classes composing this type + private fun related_mclasses(mmodule: MModule): Array[MClass] is abstract end redef class MClassType - redef fun is_visible_from(mmodule) do - return mmodule.is_visible(mclass.intro_mmodule, public_visibility) - end + redef fun related_mclasses(mmodule) do return [mclass] end -redef class MNullableType - redef fun is_visible_from(mmodule) do return mtype.is_visible_from(mmodule) +redef class MProxyType + redef fun related_mclasses(mmodule) do return undecorate.related_mclasses(mmodule) end redef class MGenericType - redef fun is_visible_from(mmodule) + redef fun related_mclasses(mmodule) do - for arg_mtype in arguments do if not arg_mtype.is_visible_from(mmodule) then return false - return super + var mods = super + for arg_mtype in arguments do mods.add_all(arg_mtype.related_mclasses(mmodule)) + return mods end end @@ -127,13 +99,11 @@ end var model = new Model var modelbuilder = new ModelBuilder(model, toolcontext) -var mmodules = modelbuilder.parse(arguments) +var mmodules = modelbuilder.parse_full(arguments) modelbuilder.run_phases -# Create a distinct support module per targetted modules +# Create a distinct support module per target modules for mmodule in mmodules do - var rta = modelbuilder.do_rapid_type_analysis(mmodule) - # Name of the support module var module_name @@ -151,17 +121,53 @@ for mmodule in mmodules do else if module_path.has_suffix(".nit") then module_name = module_path.basename(".nit") else - module_name = module_path.basename("") + module_name = module_path.basename module_path += ".nit" end + var target_modules = null + var importations = null + var mgroup = mmodule.mgroup + if toolcontext.opt_depth.value == 1 and mgroup != null then + modelbuilder.scan_group mgroup + target_modules = mgroup.mmodules + else if toolcontext.opt_depth.value == 2 then + # package + target_modules = new Array[MModule] + importations = new Array[MModule] + if mgroup != null then + for g in mgroup.mpackage.mgroups do + target_modules.add_all g.mmodules + end + + for g in mgroup.in_nesting.direct_smallers do + var dm = g.default_mmodule + if dm != null then + importations.add dm + end + end + + for m in mgroup.mmodules do + importations.add m + end + end + end + + if target_modules == null then target_modules = [mmodule] + if importations == null then importations = target_modules + var nit_module = new NitModule(module_name) + nit_module.annotations.add """generated""" + nit_module.annotations.add """no_warning("property-conflict")""" nit_module.header = """ # This file is generated by nitserial # Do not modify, but you can redef """ - nit_module.imports.add mmodule.name + for importation in importations do + nit_module.imports.add importation.name + end + nit_module.imports.add "serialization" nit_module.content.add """ @@ -170,15 +176,41 @@ redef class Deserializer do""" var serializable_type = mmodule.serializable_type - for mtype in rta.live_types do - # We are only interested in instanciated generics, subtypes of Serializable - # and which are visibles. - if mtype isa MGenericType and - mtype.is_subtype(mmodule, null, serializable_type) and - mtype.is_visible_from(mmodule) then - - nit_module.content.add """ + var compiled_types = new Array[MType] + for m in target_modules do + nit_module.content.add """ + # Module: {{{m.to_s}}}""" + + var rta = modelbuilder.do_rapid_type_analysis(m) + + for mtype in rta.live_types do + # We are only interested in instanciated generics, subtypes of Serializable + # and which are visible. + if mtype isa MGenericType and + mtype.is_subtype(m, null, serializable_type) and + mtype.mclass.kind == concrete_kind and + not compiled_types.has(mtype) then + + # Intrude import the modules declaring private classes + var related_mclasses = mtype.related_mclasses(mmodule) + for mclass in related_mclasses do + if not mmodule.is_visible(mclass.intro_mmodule, mclass.visibility) then + var intro_mmodule = mclass.intro_mmodule + var intro_mgroup = intro_mmodule.mgroup + + var to_import = intro_mmodule.full_name + if intro_mgroup == null or intro_mgroup.default_mmodule == intro_mmodule then + to_import = intro_mmodule.name + end + + nit_module.imports.add "intrude import {to_import}" + end + end + + compiled_types.add mtype + nit_module.content.add """ if name == \"{{{mtype}}}\" then return new {{{mtype}}}.from_deserializer(self)""" + end end end