1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Serialization support compiler, a tool to support deserialization of live generic types
19 # Executing on the module `game_logic` will create the module `game_logic_serial`
20 # in the local directory. Mixing the generated module to the main module with
21 # `nitg game_logic.nit -m game_logic_serial` will create a program supporting
22 # deserialization of all generic types visible from the main module.
24 # Because the generation is limited to the visible types, a module author might want
25 # generate and include its own serialization support module.
29 import rapid_type_analysis
35 # TODO add more features and move to lib
39 var header
: nullable Streamable = null
44 # Imports from this module
45 var imports
= new Array[Streamable]
47 # Main content of this module
48 var content
= new Array[Streamable]
53 if header
!= null then add header
56 if name
!= null then add
"module {name}\n\n"
58 for i
in imports
do add
"import {i}\n"
61 for l
in content
do add
"{l}\n"
65 redef class ToolContext
66 # Where do we put a single result?
67 var opt_output
: OptionString = new OptionString("Output file (can also be 'stdout')", "-o", "--output")
69 # Where do we put the result?
70 var opt_dir
: OptionString = new OptionString("Output directory", "--dir")
74 option_context
.add_option
(opt_output
, opt_dir
)
80 # Get the type of the class `Serializable`
81 fun serializable_type
: MClassType is cached
do
82 return self.get_primitive_class
("Serializable").mclass_type
87 # Is this type fully visible from `mmodule`?
88 fun is_visible_from
(mmodule
: MModule): Bool is abstract
91 redef class MClassType
92 redef fun is_visible_from
(mmodule
) do
93 return mmodule
.is_visible
(mclass
.intro_mmodule
, public_visibility
)
97 redef class MNullableType
98 redef fun is_visible_from
(mmodule
) do return mtype
.is_visible_from
(mmodule
)
101 redef class MGenericType
102 redef fun is_visible_from
(mmodule
)
104 for arg_mtype
in arguments
do if not arg_mtype
.is_visible_from
(mmodule
) then return false
109 var toolcontext
= new ToolContext
110 toolcontext
.tooldescription
= """
111 Usage: nitserial [OPTION] program.nit [other_program.nit [...]]
112 Generates a serialization support module"""
114 toolcontext
.process_options
(args
)
115 var arguments
= toolcontext
.option_context
.rest
118 if toolcontext
.opt_output
.value
!= null and toolcontext
.opt_dir
.value
!= null then
119 print
"Error: cannot use both --dir and --output"
122 if arguments
.length
> 1 and toolcontext
.opt_output
.value
!= null then
123 print
"Error: --output needs a single source file. Do you prefer --dir?"
127 var model
= new Model
128 var modelbuilder
= new ModelBuilder(model
, toolcontext
)
130 var mmodules
= modelbuilder
.parse
(arguments
)
131 modelbuilder
.run_phases
133 # Create a distinct support module per targetted modules
134 for mmodule
in mmodules
do
135 var rta
= modelbuilder
.do_rapid_type_analysis
(mmodule
)
137 # Name of the support module
140 # Path to the support module
141 var module_path
= toolcontext
.opt_output
.value
142 if module_path
== null then
143 module_name
= "{mmodule.name}_serial"
144 module_path
= "{module_name}.nit"
146 var dir
= toolcontext
.opt_dir
.value
147 if dir
!= null then module_path
= dir
.join_path
(module_path
)
148 else if module_path
== "stdout" then
149 module_name
= "{mmodule.name}_serial"
151 else if module_path
.has_suffix
(".nit") then
152 module_name
= module_path
.basename
(".nit")
154 module_name
= module_path
.basename
("")
155 module_path
+= ".nit"
158 var nit_module
= new NitModule(module_name
)
159 nit_module
.header
= """
160 # This file is generated by nitserial
161 # Do not modify, but you can redef
164 nit_module
.imports
.add mmodule
.name
165 nit_module
.imports
.add
"serialization"
167 nit_module
.content
.add
"""
168 redef class Deserializer
169 redef fun deserialize_class(name)
172 var serializable_type
= mmodule
.serializable_type
173 for mtype
in rta
.live_types
do
174 # We are only interested in instanciated generics, subtypes of Serializable
175 # and which are visibles.
176 if mtype
isa MGenericType and
177 mtype
.is_subtype
(mmodule
, null, serializable_type
) and
178 mtype
.is_visible_from
(mmodule
) then
180 nit_module
.content
.add
"""
181 if name == \"{{{mtype}}}\" then return new {{{mtype}}}.from_deserializer(self)"""
185 nit_module
.content
.add
"""
190 # Compile support module
191 if module_path
!= null then
193 nit_module
.write_to_file module_path
196 nit_module
.write_to stdout