Generate a support module for each module that contain a class annotated with is actor

See nit/lib/actors/actors.nit for the abstraction on which the generated classes are based

Introduced classes

private class ActorPhase

nitc :: ActorPhase

Redefined classes

redef class MModule

nitc :: actors_generation_phase $ MModule

A Nit module is usually associated with a Nit source file.
redef class ToolContext

nitc :: actors_generation_phase $ ToolContext

Global context for tools

All class definitions

private class ActorPhase

nitc $ ActorPhase

redef class MModule

nitc :: actors_generation_phase $ MModule

A Nit module is usually associated with a Nit source file.
redef class ToolContext

nitc :: actors_generation_phase $ ToolContext

Global context for tools
package_diagram nitc::actors_generation_phase actors_generation_phase nitc::actors_injection_phase actors_injection_phase nitc::actors_generation_phase->nitc::actors_injection_phase nitc\>modelize\> modelize nitc::actors_generation_phase->nitc\>modelize\> gen_nit gen_nit nitc::actors_generation_phase->gen_nit nitc::modelize_property modelize_property nitc::actors_injection_phase->nitc::modelize_property nitc::parser_util parser_util nitc::actors_injection_phase->nitc::parser_util nitc nitc nitc\>modelize\>->nitc template template gen_nit->template ...nitc::modelize_property ... ...nitc::modelize_property->nitc::modelize_property ...nitc::parser_util ... ...nitc::parser_util->nitc::parser_util ...nitc ... ...nitc->nitc ...template ... ...template->template nitc::code_gen code_gen nitc::code_gen->nitc::actors_generation_phase nitc::nit nit nitc::nit->nitc::code_gen nitc::nitc nitc nitc::nitc->nitc::code_gen nitc::nitmetrics nitmetrics nitc::nitmetrics->nitc::code_gen nitc::nitvm nitvm nitc::nitvm->nitc::code_gen nitc::nit... ... nitc::nit...->nitc::nit nitc::nitc... ... nitc::nitc...->nitc::nitc nitc::nitmetrics... ... nitc::nitmetrics...->nitc::nitmetrics nitc::nitvm... ... nitc::nitvm...->nitc::nitvm

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module annotation

nitc :: annotation

Management and utilities on annotations
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module ini

ini :: ini

Read and write INI configuration files
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module literal

nitc :: literal

Parsing of literal values in the abstract syntax tree.
module loader

nitc :: loader

Loading of Nit source files
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module mmodule_data

nitc :: mmodule_data

Define and retrieve data in modules
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module modelize_class

nitc :: modelize_class

Analysis and verification of class definitions to instantiate model element
module modelize_property

nitc :: modelize_property

Analysis and verification of property definitions to instantiate model element
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_util

nitc :: parser_util

Utils and tools related to parsers and AST
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module phase

nitc :: phase

Phases of the processing of nit programs
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module actors_injection_phase

nitc :: actors_injection_phase

Injects model for the classes annotated with "is actor" so
module gen_nit

gen_nit :: gen_nit

Support to generate and otherwise manipulate Nit code
module modelize

nitc :: modelize

Create a model from nit source files

Children

module code_gen

nitc :: code_gen

Main frontend phases plus code generation phases

Descendants

module a_star-m

a_star-m

module nit

nitc :: nit

A naive Nit interpreter
module nitc

nitc :: nitc

A Nit compiler
module nitmetrics

nitc :: nitmetrics

A program that collects various metrics on nit programs and libraries
module nitvm

nitc :: nitvm

The Nit virtual machine launcher
module test_astbuilder

nitc :: test_astbuilder

Program used to test the clone method of the astbuilder tool
# Generate a support module for each module that contain a class annotated with `is actor`
# See `nit/lib/actors/actors.nit` for the abstraction on which the generated classes are based
module actors_generation_phase

import actors_injection_phase
import modelize
import gen_nit
import modelbuilder

private class ActorPhase
	super Phase

	var generated_actor_modules = new Array[String]

	# Source code of the actor classes to generate
	var actors = new Array[String]

	# Source code of the message classes to generate
	var messages = new Array[String]

	# Source code of the proxy classes to generate
	var proxys = new Array[String]

	# Redefinitions of annotated classes
	var redef_classes = new Array[String]

	fun generate_actor_classes(mclassdef: MClassDef, mmod: MModule) do
		if not mmod.generate_actor_submodule then mmod.generate_actor_submodule = true

		# Get the name of the annotated class
		var classname = mclassdef.name

		# Generate the actor class
		if mclassdef.is_intro then actors.add(
"""
class Actor{{{classname}}}
	super Actor
	redef type E: nullable {{{classname}}}
end
""")
		######## Generate the Messages classes ########

		# Get all the methods definitions
		var propdefs = new Array[MPropDef]
		for propdef in mclassdef.mpropdefs do
			if propdef.is_intro then propdefs.add(propdef)
		end

		var methods = new Array[MMethodDef]
		for p in propdefs do
			if p isa MMethodDef then
				# TODO: find a better way to exclude constructors,
				# getters/setters and the "async" (the actor)
				if p.name.has("=") or p.name.has("async") or p.mproperty.is_init then continue
				methods.add(p)
			end
		end

		# Generate the superclass for all Messages classes (each actor has its own Message super class)
		var msg_class_name = "Message" + classname

		if mclassdef.is_intro then messages.add(
"""
class {{{msg_class_name}}}
	super Message
	redef type E: {{{classname}}}
end
""")
		# Generate every Message class based on the methods of the annotated class
		var proxy_calls = new Array[String]
		for m in methods do
			# Signature of the method
			var signature = m.msignature

			# Name of the method
			var method_name = m.name

			# Attributes of the `Message` class if needed
			# Corresponds to the parameters of the proxied method
			var msg_attributes = new Array[String]

			# Signature of the proxy corresponding method
			var proxy_sign = ""

			# Values for the body of the `invoke` method of the generated Message class
			# Used if the call must return a value
			var return_value = ""
			var return_parenthesis = ""

			# Params to send to `instance` in the `invoke` method
			var params = ""

			# Values for the generated proxy method
			var return_signature = ""
			var return_statement = ""

			if signature != null then
				var proxy_params= new Array[String]

				# Deal with parameters
				var mparameters = signature.mparameters
				if mparameters.length > 0 then
					var parameters = new Array[String]
					proxy_sign += "("
					for p in mparameters do
						var n = p.name
						var t = p.mtype.name
						msg_attributes.add("var " + n + ": " + t)
						proxy_params.add(n + ": " + t)
						parameters.add(n)
					end
					proxy_sign += proxy_params.join(", ") + ")"
					params = "(" + parameters.join(", ") + ")"
				end

				# Deal with the return if any
				var ret_type = signature.return_mtype
				if ret_type != null then
					msg_attributes.add("var ret = new Future[{ret_type.name}]")
					return_value = "ret.set_value("
					return_parenthesis = ")"
					return_signature = ": Future[{ret_type.name}]"
					return_statement = "return msg.ret"
				end
			end

			# Name of the Message class
			var name = classname + "Message" + method_name

			# The effective Message Class
			messages.add(
"""
class {{{name}}}
	super {{{msg_class_name}}}

	{{{msg_attributes.join("\n")}}}

	redef fun invoke(instance) do {{{return_value}}}instance.{{{method_name}}}{{{params}}}{{{return_parenthesis}}}
end
""")


			# The actual proxy call
			proxy_calls.add(
"""
redef fun {{{method_name}}}{{{proxy_sign}}}{{{return_signature}}} do
	var msg = new {{{name}}}{{{params}}}
	actor.mailbox.push(msg)
	{{{return_statement}}}
end
""")
		end

		# At this point, all msg classes should be good
		# All of the functions of the proxy too

		# Let's generate the proxy class then

		var redef_virtual_type = ""
		if mclassdef.is_intro then redef_virtual_type = "redef type E: Actor{classname}"
		proxys.add(
"""
redef class Proxy{{{classname}}}

	{{{redef_virtual_type}}}

	init proxy(base_class: {{{classname}}}) do
		actor = new Actor{{{classname}}}(base_class)
		actor.start
	end

	{{{proxy_calls.join("\n\n")}}}
end
""")

	if mclassdef.is_intro then redef_classes.add(
"""
redef class {{{classname}}}
	var m = new Mutex
	var lazy_proxy: Proxy{{{classname}}} is lazy do return new Proxy{{{classname}}}.proxy(self)

	redef fun async: Proxy{{{classname}}} do
		m.lock
		var p = lazy_proxy
		m.unlock
		return p
	end
end
""")

	end

	redef fun process_nmodule(nmodule) do
		var mmod = nmodule.mmodule
		if mmod == null then return

		if generated_actor_modules.has(mmod.name) then return

		var mclasses_defs = mmod.mclassdefs
		for mclass_def in mclasses_defs do
			var mclass = mclass_def.mclass
			var actor = mclass.actor
			if actor != null then generate_actor_classes(mclass_def, mmod)
		end

	end

	redef fun process_annotated_node(nclass, nat)
	do
		if nat.n_atid.n_id.text != "actor" then return

		if not nclass isa AStdClassdef then
			toolcontext.error(nat.location, "Syntax Error: only a class can be annotated as an actor.")
			return
		end
	end

	redef fun process_nmodule_after(nmodule) do
		var first_mmodule = nmodule.mmodule
		if first_mmodule == null then return

		# Be careful not to generate useless submodules !
		if not first_mmodule.generate_actor_submodule then return

		# Name of the support module
		var module_name

		# Path to the support module
		module_name = "actors_{first_mmodule.name}"

		# We assume a module using actors has a `filepath` not null
		var mmodule_path = first_mmodule.filepath.as(not null).dirname

		var module_path = "{mmodule_path}/{module_name}.nit"

		var nit_module = new NitModule(module_name)
		nit_module.annotations.add "no_warning(\"missing-doc\")"

		nit_module.header = """
		# This file is generated by nitactors (threaded version)
		# Do not modify, instead use the generated services.
		"""

		# for mmod in mmodules do nit_module.imports.add mmod.name
		nit_module.imports.add first_mmodule.name

		generated_actor_modules.add(module_name)
		var idx = generated_actor_modules.index_of(module_name)
		for i in [0..idx[ do nit_module.imports.add(generated_actor_modules[i])

		nit_module.content.add "####################### Redef classes #########################"
		for c in redef_classes do nit_module.content.add( c + "\n\n" )

		nit_module.content.add "####################### Actor classes #########################"
		for c in actors do nit_module.content.add( c + "\n\n" )

		nit_module.content.add "####################### Messages classes ######################"
		for c in messages do nit_module.content.add( c + "\n\n" )

		nit_module.content.add "####################### Proxy classes #########################"
		for c in proxys do nit_module.content.add( c + "\n\n" )

		# Write support module
		nit_module.write_to_file module_path

		actors.clear
		messages.clear
		proxys.clear
		redef_classes.clear

		toolcontext.modelbuilder.inject_module_subimportation(first_mmodule, module_path)
	end
end

redef class MModule
	# Do we need to generate the actor submodule ?
	var generate_actor_submodule = false
end

redef class ToolContext
	# Generate actors
	var actor_phase: Phase = new ActorPhase(self, [modelize_class_phase, modelize_property_phase])
end
src/frontend/actors_generation_phase.nit:14,1--296,3