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
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.
nitc :: actors_injection_phase
Injects model for the classes annotated with "is actor" sonitc :: astbuilder
Instantiation and transformation of semantic nodes in the AST of expressions and statementsnitc :: i18n_phase
Basic support of internationalization through the generation of id-to-string tablesSerializable::inspect
to show more useful information
nitc :: modelbuilder
more_collections :: more_collections
Highly specific, but useful, collections-related classes.threaded
annotation
serialization :: serialization_core
Abstract services to serialize Nit objects to different formatsnitc :: serialization_model_phase
Phase generating methods (model-only) to serialize Nit objectsnitc :: toolcontext
Common command-line tool infrastructure than handle options and error messagescore :: union_find
union–find algorithm using an efficient disjoint-set data structure
# Serialization support compiler, a tool to support deserialization of live generic types
#
# 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
# `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 template
import gen_nit
import frontend
import rapid_type_analysis
redef class ToolContext
# Where do we put a single result?
var opt_output: OptionString = new OptionString("Output file (can also be 'stdout')", "-o", "--output")
# 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, opt_depth)
super
end
end
redef class MModule
# Get the type of the class `Serializable`
var serializable_type: MClassType is lazy do
return self.get_primitive_class("Serializable").mclass_type
end
end
redef class MType
end
redef class MClassType
end
redef class MProxyType
end
redef class MGenericType
end
var toolcontext = new ToolContext
toolcontext.tooldescription = """
Usage: nitserial [OPTION] program.nit [other_program.nit [...]]
Generates a serialization support module"""
toolcontext.process_options(args)
var arguments = toolcontext.option_context.rest
# Check options
if toolcontext.opt_output.value != null and toolcontext.opt_dir.value != null then
print "Error: cannot use both --dir and --output"
exit 1
end
if arguments.length > 1 and toolcontext.opt_output.value != null then
print "Error: --output needs a single source file. Do you prefer --dir?"
exit 1
end
var model = new Model
var modelbuilder = new ModelBuilder(model, toolcontext)
var mmodules = modelbuilder.parse_full(arguments)
modelbuilder.run_phases
# Create a distinct support module per target modules
for mmodule in mmodules do
# Name of the support module
var module_name
# Path to the support module
var module_path = toolcontext.opt_output.value
if module_path == null then
module_name = "{mmodule.name}_serial"
module_path = "{module_name}.nit"
var dir = toolcontext.opt_dir.value
if dir != null then module_path = dir.join_path(module_path)
else if module_path == "stdout" then
module_name = "{mmodule.name}_serial"
module_path = null
else if module_path.has_suffix(".nit") then
module_name = module_path.basename(".nit")
else
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
"""
for importation in importations do
nit_module.imports.add importation.name
end
nit_module.imports.add "serialization"
nit_module.content.add """
redef class Deserializer
redef fun deserialize_class(name)
do"""
var serializable_type = mmodule.serializable_type
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
nit_module.content.add """
return super
end
end"""
# Compile support module
if module_path != null then
# To file
nit_module.write_to_file module_path
else
# To stdout
nit_module.write_to stdout
end
end
src/nitserial.nit:17,1--230,3