1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 # Generate a support module for each module that contain a class annotated with `is actor`
15 # See `nit/lib/actors/actors.nit` for the abstraction on which the generated classes are based
16 module actors_generation_phase
22 private class ActorPhase
25 # Source code of the actor classes to generate
26 var actors
= new Array[String]
28 # Source code of the message classes to generate
29 var messages
= new Array[String]
31 # Source code of the proxy classes to generate
32 var proxys
= new Array[String]
34 # Redefinitions of annotated classes
35 var redef_classes
= new Array[String]
37 redef fun process_annotated_node
(nclass
, nat
)
39 if nat
.n_atid
.n_id
.text
!= "actor" then return
41 if not nclass
isa AStdClassdef then
42 toolcontext
.error
(nat
.location
, "Syntax Error: only a class can be annotated as an actor.")
46 # Get the module associated with this class
47 var mclassdef
= nclass
.mclassdef
48 assert mclassdef
!= null
50 var mmod
= mclassdef
.mmodule
51 if not mmod
.generate_actor_submodule
then mmod
.generate_actor_submodule
= true
53 # Get the name of the annotated class
54 var classname
= mclassdef
.name
56 # Generate the actor class
59 class Actor{{{classname}}}
61 redef type E: nullable {{{classname}}}
64 ######## Generate the Messages classes ########
66 # Get all the methods definitions
67 var propdefs
= mclassdef
.mpropdefs
68 var methods
= new Array[MMethodDef]
70 if p
isa MMethodDef then
71 # TODO: find a better way to exclude constructors,
72 # getters/setters and the "async" (the actor)
73 if p
.name
.has
("=") or p
.name
.has
("async") or p
.mproperty
.is_init
then continue
78 # Generate the superclass for all Messages classes (each actor has its own Message super class)
79 var msg_class_name
= "Message" + classname
82 class {{{msg_class_name}}}
84 redef type E: {{{classname}}}
87 # Generate every Message class based on the methods of the annotated class
88 var proxy_calls
= new Array[String]
90 # Signature of the method
91 var signature
= m
.msignature
94 var method_name
= m
.name
96 # Attributes of the `Message` class if needed
97 # Corresponds to the parameters of the proxied method
98 var msg_attributes
= new Array[String]
100 # Signature of the proxy corresponding method
103 # Values for the body of the `invoke` method of the generated Message class
104 # Used if the call must return a value
105 var return_value
= ""
106 var return_parenthesis
= ""
108 # Params to send to `instance` in the `invoke` method
111 # Values for the generated proxy method
112 var return_signature
= ""
113 var return_statement
= ""
115 if signature
!= null then
116 var proxy_params
= new Array[String]
118 # Deal with parameters
119 var mparameters
= signature
.mparameters
120 if mparameters
.length
> 0 then
121 var parameters
= new Array[String]
123 for p
in mparameters
do
126 msg_attributes
.add
("var " + n
+ ": " + t
)
127 proxy_params
.add
(n
+ ": " + t
)
130 proxy_sign
+= proxy_params
.join
(", ") + ")"
131 params
= "(" + parameters
.join
(", ") + ")"
134 # Deal with the return if any
135 var ret_type
= signature
.return_mtype
136 if ret_type
!= null then
137 msg_attributes
.add
("var ret = new Future[{ret_type.name}]")
138 return_value
= "ret.set_value("
139 return_parenthesis
= ")"
140 return_signature
= ": Future[{ret_type.name}]"
141 return_statement
= "return msg.ret"
145 # Name of the Message class
146 var name
= classname
+ "Message" + method_name
148 # The effective Message Class
152 super {{{msg_class_name}}}
154 {{{msg_attributes.join("\n")}}}
156 redef fun invoke(instance) do {{{return_value}}}instance.{{{method_name}}}{{{params}}}{{{return_parenthesis}}}
161 # The actual proxy call
164 redef fun {{{method_name}}}{{{proxy_sign}}}{{{return_signature}}} do
165 var msg = new {{{name}}}{{{params}}}
166 actor.mailbox.push(msg)
167 {{{return_statement}}}
172 # At this point, all msg classes should be good
173 # All of the functions of the proxy too
175 # Let's generate the proxy class then
178 redef class Proxy{{{classname}}}
180 redef type E: Actor{{{classname}}}
181 #var actor: Actor{{{classname}}} is noinit
183 init proxy(base_class: {{{classname}}}) do
184 actor = new Actor{{{classname}}}(base_class)
188 {{{proxy_calls.join("\n\n")}}}
194 redef class {{{classname}}}
195 redef var async is lazy do return new Proxy{{{classname}}}.proxy(self)
200 redef fun process_nmodule_after
(nmodule
) do
201 var first_mmodule
= nmodule
.mmodule
202 if first_mmodule
== null then return
204 # Be careful not to generate useless submodules !
205 if not first_mmodule
.generate_actor_submodule
then return
207 # Name of the support module
210 # Path to the support module
211 module_name
= "actors_{first_mmodule.name}"
213 # We assume a module using actors has a `filepath` not null
214 var mmodule_path
= first_mmodule
.filepath
.as(not null).dirname
216 var module_path
= "{mmodule_path}/{module_name}.nit"
218 var nit_module
= new NitModule(module_name
)
219 nit_module
.annotations
.add
"no_warning(\"missing-doc\
")"
221 nit_module
.header
= """
222 # This file is generated by nitactors (threaded version)
223 # Do not modify, instead use the generated services.
226 # for mmod in mmodules do nit_module.imports.add mmod.name
227 nit_module
.imports
.add first_mmodule
.name
229 nit_module
.content
.add
"####################### Redef classes #########################"
230 for c
in redef_classes
do nit_module
.content
.add
( c
+ "\n\n" )
232 nit_module
.content
.add
"####################### Actor classes #########################"
233 for c
in actors
do nit_module
.content
.add
( c
+ "\n\n" )
235 nit_module
.content
.add
"####################### Messages classes ######################"
236 for c
in messages
do nit_module
.content
.add
( c
+ "\n\n" )
238 nit_module
.content
.add
"####################### Proxy classes #########################"
239 for c
in proxys
do nit_module
.content
.add
( c
+ "\n\n" )
241 # Write support module
242 nit_module
.write_to_file module_path
244 actors
= new Array[String]
245 messages
= new Array[String]
246 proxys
= new Array[String]
247 redef_classes
= new Array[String]
249 toolcontext
.modelbuilder
.inject_module_subimportation
(first_mmodule
, module_path
)
254 # Do we need to generate the actor submodule ?
255 var generate_actor_submodule
= false
258 redef class ToolContext
260 var actor_phase
: Phase = new ActorPhase(self, [modelize_class_phase
])