Original in Russian: http://18delphi.blogspot.ru/2013/10/blog-post_4433.html
Introduction
So, this is an “example”. We’ll go back a long way and think up a subject domain. First, we’ll describe it (create a meta-model), then draw a model and generate an “artifact” with it: a thought-up description. Next we’ll invent and describe a language and finally “adopt” the language to the subject area. As a result, we’ll generate the source code.
From the outset, please forgive me for the totally hypothetical character of the subject domain and my poor language (I used to have problems with natural languages since I was a child:) )
From the outset, please forgive me for the totally hypothetical character of the subject domain and my poor language (I used to have problems with natural languages since I was a child:) )
Subject domain
Let’s describe the living form. Suppose, we have an entity – an Organism consisting of parts. Each part presents an entity of some type. So, we have two types of entities. To make it less complex, we will not enter properties or parameters except the name and the description (any entity has them in our tool since creation). Let’s write it as a template determining the meta-model:
: Body Part::Class
? Body Part (can join with other body part)
: Organism::Class
? organism (consists of parts)
> Body Part::Class
: Body Part::Class::Attribute
c {}
r {%T%f_kind_of("Body Part")=false}: {Body parts can only join with each other}
? Body Part (can join with other body part)
: Organism::Class
? organism (consists of parts)
> Body Part::Class
: Body Part::Class::Attribute
c {}
r {%T%f_kind_of("Body Part")=false}: {Body parts can only join with each other}
Let me explain.
: X::Y - determines the stereotype Х for the elements class Y (Class is a usual class from specifications to UML); for now two possible classes stereotypes (Body Part and Organism) have been determined.
? is just a description of the stereotype for information (you’ll see it in model draw tool).
> determines the elements of the stereotypes to be nested in the given one. I mean that Parts can be nested in the Organism.
: X::Y::Z::Y2 determine valid stereotypes Z of the nested UML-elements Y2 (attributes, operations, relations). In this case, we determine if Body Parts have attributes. Z is not specified, i.e. we use the default (empty) stereotype.
At last, we can see a constraint (two lines beginning with the letters "c" and "r") that ensures that these attributes are targeted at another Body Part.
The meta-model is finished, so we may try to draw the Model. I omit some boring technical preparations like special template header. The only special thing to do is determine the root stereotype of the folder we’ll create the model in. As a rule, the root folder determines the specific project.
: Life Forms::Category
M
> Organism::Class
М determines stereotype as a root one, i.e. it may give basis for the Model
: X::Y - determines the stereotype Х for the elements class Y (Class is a usual class from specifications to UML); for now two possible classes stereotypes (Body Part and Organism) have been determined.
? is just a description of the stereotype for information (you’ll see it in model draw tool).
> determines the elements of the stereotypes to be nested in the given one. I mean that Parts can be nested in the Organism.
: X::Y::Z::Y2 determine valid stereotypes Z of the nested UML-elements Y2 (attributes, operations, relations). In this case, we determine if Body Parts have attributes. Z is not specified, i.e. we use the default (empty) stereotype.
At last, we can see a constraint (two lines beginning with the letters "c" and "r") that ensures that these attributes are targeted at another Body Part.
The meta-model is finished, so we may try to draw the Model. I omit some boring technical preparations like special template header. The only special thing to do is determine the root stereotype of the folder we’ll create the model in. As a rule, the root folder determines the specific project.
: Life Forms::Category
M
> Organism::Class
М determines stereotype as a root one, i.e. it may give basis for the Model
After opening a new model and linking it to our “template”, we get an empty screen which is obvious since we did not CREATE anything. Actually, the blank screen is the top level in our model, we can create an element of one type (stereotype) of the Life Form:
Now we have a project package, we open it and see the empty diagram. Similarly, we create the first instance of the Organism in it and call it "Snoopy the Dog”.
Next we’ll write the Snoopy, wherefore we create the nested classes with the stereotype “Body Part” within the class called "Snoopy the Dog" (other classes creation is not allowed). Steps are the same, I’ll give the final diagram:
We created three component of "Snoopy" and linked them to each other as it is in real life. We need only to draw the “feet”. But suppose that limbs is a special body part for all Organisms and we want to have a separate stereotype for them. So, we update the meta-model:
: Foot::Class
= Body Part::Class
: Hand::Class
= Body Part::Class
= <stereotype name> determines the classic object-orientated inheritance (possibly, multiple inheritance).
Let’s add them to the list of valid stereotypes of the Organism:
: Organism::Class
? organism (consists of parts)
> Body Part::Class
> Hand::Class
> Foot::Class
Now they are available from the list of possible stereotypes in the Organism and we can finish to draw the Snoopy:
So, we described a simple meta-model of the subject domain and drew it of two subjects. Now let’s generate something from it. Suppose, we need to generate the description on the pseudo-human language. For example, “The world consists of...".
To do it, first of all we need to define the GENERATOR, i.e. specify the type of the artifacts derived from the model. There are no limits for the number of generators. There exist generators for different programming languages, documentation and so on. At this stage, we need only one generator:
Generator definitions:
group "All" {
generator "output" {
Description: World description
}
}
output is the identifier of our generator.
Now we can specify a template for this generator in each stereotype. We’ll start with the root one:
: Life Forms::Category
M
> Organism::Class
+ output
C /%SN
O %SN.txt
The world consists of: <%CX
>
+output begins the template for generator output.
С determines the generation path for the element in question and the nested elements
O specifies the name of the output file. If the stereotype generator does not specify the output file, then its template is generated into a parent.
%SN is a wonderful minimalistic syntax (the first sight sends shiver down the spine but if you understand and get used to it, you find lots of advantages in minimalism)
%S (Self) is the access to the current element of the model, N ourputs the name (Name) of the element on the model.
So, the two commands described above define the output file with the following path: <generation folder>/<root package name>/<root package name>.txt. The following is the template that outputs the constant string and launches the generators of the children.
%C (Child) is the child of the current element
<... > is the cycle (in this case on %C - the children). X launches the current generator on the element.
Now let’s describe the template for the Organisms, i.e. “the children”:
: Organism::Class
+ output
%SN[, %SD,] is <{; }%CX>.
%SN is the name of the Organism
%SD (Documentation) is the documentation of the element on the model
[...] is the condition, in this case an “empty” one, that specifies that comas and space are only output when the documentation of the element is not empty.
Finally, the cycle on the children of the Organism. You can see a parameter in brace brackets that defines the elements separator string.
At last, the template for the Body Part:
: Body Part::Class
+ output
%SN[ %SD][ with <{, }[%CN ]%C%TN>]
Similarly, we output the name and the documentation, upon which we output the name and type of relation (attribute) if the body part is connected to the other one, i.e. the name of the part specified.
%T (Target) is the target of the element like attribute type or the type of operation value returned, relation type and so on. Thus, the binding %C%T is the access to the target of the current child.
Now we can launch the generation:
Here is the output file (you may have guessed):
The world consists of Snoopy the Dog has Head with teeth; Trundle Tail; Spotty Body with the Head, Right Front Foot, Left Front Foot, Left Rear Foot, Right Rear Foot, Tail.
If we extent the model, so will the description do. Suppose, we need to describe a new life form - a Human. Suppose, we understand there’ll be many humans (in contrast to the Snoopy who is the only one) and we sure don't want to draw humans and their parts one by one. That is why we use summoning, i.e. creates the elements on the model. To begin with, let’s define a new stereotype Human (and the God “to join the company”, humans can not do without the God ;)) on the meta-model:
: Human::Class
= Organism::Class
: God::Class
= Human::Class
Next, as we discussed above we need to describe the parts of ANY human and the God for once so that not to do it several times manually:
: Human::Class
= Organism::Class
+ spell
%S%f_add_class(HEAD,Body Part,Head,TMP)
%S%{HEAD}%f_set_documentation(fair)
%S%f_add_class(L_HAND,Hand,Left Hand,TMP)
%S%f_add_class(R_HAND,Hand,Right Hand,TMP)
%S%f_add_class(L_FOOT,Foot,Left Foot,TMP)
%S%f_add_class(R_FOOT,Foot,Right Foot,TMP)
%{TMP}%f_set_documentation(is a bit shorter than the left one)
: God::Class
= Human::Class
+ spell
%S%f_add_class(SECOND_HEAD,Body Part,Second Head,TMP)
%{TMP}%f_set_documentation(is better than the first one)
= Human::Class;spell
+ spell is the identifier of a special system generator for summoning.
%S%f_add_class(X,Y,Z,V) calls the function that adds a new class of the specified stereotype in the current element (%S).
Y is the stereotype of the created class
Z is the name of the created class
X is the unique identifier and the variable name in the current element the created element will be connected with. V is almost the same but globally. We use these variables to access the elements created.
%S%{HEAD} accesses the variable HEAD (created in the previous step) on the current element. In this case, %{TMP} is equivalent (the duality was formed historically, though it is still useful sometimes)
%f_set_documentation sets the documentation field
So, we have summoned a typical human and the God. Now the stereotype is ready for use and thus we avoid drawing manually on the model. Though, it is still possible for us to specify the “contents” of a specific human if needed by adding something manually.
Let us update the model:
The result of the generation is predictably as follows:
The world consists of Snoopy the Dog has Head with teeth; Trundle Tail; Spotty Body with the Head, Right Front Foot, Left Front Foot, Left Rear Foot, Right Rear Foot, Tail.
John has fair Hair; Right Hand; Left Hand; Left Foot; Right Foot is a bit shorter than the left one.
Buddha has Second Head is better than the first one; Light Hair; Left Hand; Right Hand; Left Foot; Right Foot is a bit shorter than the left one.
Mary has fair Hair; Left Hand; Right Hand; Left Foot; Right Foot is a bit shorter than the left one.
Naturally, this “putting” one stereotypes into others may have any nesting level and be more or less complex. In this way we also can define the stereotype... say... Family and summon its contents automatically or… say, Village of 50 families and so on. The language options are wide enough for summoning code (as well as the code of the usual generation template) to have quite a free structure with conditions, cycles, cases, inheritance and polymorphism and in order for us to express any rule and algorithm.
Let’s extend the meta-model by adding an option of relation between Organisms:
: Organism::Class::ClassDependency
: Organism::Class::loves::ClassDependency
= Organism::Class::ClassDependency
: Organism::Class::trusts in::ClassDependency
= Organism::Class::ClassDependency
We have added abstract relation and defined the two specific relations – “loves” and “trusts in”. They are now available on the model:
: Organism::Class
+ output
%SN[, %SD,] has <{; }%CX>.<{}{%CC=Dependency} %SN %CS %C%TN.>
Now we output the description of these dependencies.
<..> is the cycle on children with condition to select the children with class type (%CС - Class) Dependency and to output its name, their stereotype (%СS - Stereotype) and the target name.
The changes after generation are as follows:
The world consists of Snoopy the Dog has Head with teeth; Trundle Tail; Spotty Body with the Head, Right Front Foot, Left Front Foot, Left Rear Foot, Right Rear Foot, Tail. Snoopy the Dog trusts in John.
John has fair Hair; Right Hand; Left Hand; Left Foot; Right Foot is a bit shorter than the left one. John loves Snoopy the Dog. John loves Mary. John trusts in Buddha.
Buddha has Second Head is better than the first one; Light Hair; Left Hand; Right Hand; Left Foot; Right Foot is a bit shorter than the left one.
Mary has fair Hair; Left Hand; Right Hand; Left Foot; Right Foot is a bit shorter than the left one.
Programming Language
We have described the subject domain, drawn the model and generated its description. Let us suppose that we need to get the code on “our” programming language that would display it. To begin with, we define the meta-model of the language, then convert the subject domain into our language (using summoning).
Let our programming language have a minimal set of items (this is an unreal example, I long ago forgot Delphi and java, I only started to learn С++ and I am afraid that my syntax on Objective C will drive you crazy), sort of java, all in one file:
module M1 {
class C1 ---> BaseClass {
property propA: TypeA;
operation opB (arg1: Type1, arg2: Type2): TypeRet {
// method’s body
}
}
}
Thus we will need the following items in the language meta-model (let’s call it X language) module, class, property and constructor.
Let us write it on a template. To begin with, we’ll update the description of our generators:
Generator definitions:
group "All" {
generator "output" {
Description: World description
}
generator "langx" {
Description: Here’s the code
}
}
Next we add new stereotypes and the empty language generator:
: ModuleX::Category
> ClassX::Class
+ langx
O src/%SN.langx
: ClassX::Class
d
+ langx
: ClassX::Class::property::Attribute
+ langx
: ClassX::Class::Operation
a ra
m t
+ langx
: ClassX::Class::ctor::Operation
= ClassX::Class::Operation
T
+ langx
So, first we work with definitions for the module. ModuleХ may have elements of ClassХ and will create a file in src subdirectory named <module name>.langx.
We define (d) for ClassХ that it has to work with derived properties (operations and/or attributes), i.e. the meta-model will allow the element to implement the abstract children of its ancestor and/or override them (of course, the specific ideas of these items depend solely on the template. However, in this case I mean classic inheritance, implementation and overriding).
On ClassХ we set a property, an operation without stereotype and a constructor as an operation with stereotype ctor. Operations can be (r) – regular or (a) – abstract and available for implementation and overriding in derived elements (m t). Moreover, constructors can have no target (T), i.e. we do not have to specify the type of result to return.
At this point, we already can form the model on our language, however it only generates an empty .langx file. Thus, next we define the generation template.
Keeping in mind the desired result we can find principles and common features of different elements’ generation. Our imaginary example has a minimum of them and in real life there can be many. Suppose all our elements have documentation that has to be output in the same way. This means we can determine the template parts that are common for all elements by defining the specific features of each stereotype separately. To do it, we add one more abstract stereotype LangX and derive the rest from it:
: LangX::Class
+ langx
// %SD
%S%f_element_definition()
%f _element_definition
/ empty
: ClassX::Class
= LangX::Class
: ClassX::Class::Operation
= LangX::Class
...
As a base stereotype, LangX determines the common template that outputs documentation in the required format (in this case it goes after the comment characters) and then calls the stereotype method _element_definition that does nothing in the base implementation.
Let’s determine this method for the real stereotypes:
: ClassX::Class
%f _element_definition
class %SN[ --\> <{, }%GN>] {
<%CX
>\
<%oX
>};
According to our syntax, the Class template outputs its name, specifies the base class (%G - Generalization) if any and then starts to generate its own children and the abstract children inherited from the ancestors (%o) implemented by this class.
: ClassX::Class::Operation
%f _element_definition
operation %f_to_javaex(%SN) (<{, }%CN:%C%TN>): %TN {
}
The operation is always output in small letters solidly (like on java) which is controlled by the global function of string conversion _to_javaex. The rest is controlled by the syntax defined previously. For now we do not output the body of the operation.
: ClassX::Class::property::Attribute
%f _element_definition
property %f_to_javaex(%SN): %TN;
It is the same for the property, even simpler.
: ModuleX::Category
%f _element_definition
module %SN {
<{\n\n}%CX>
}
Finally, the module outputs its header and opens all its children similarly to the class.
We have forgotten about the generator that has a definite type of result (in contrast to usual operation). We will show it on a template using summoning:
: ClassX::Class::ctor::Operation
= ClassX::Class::Operation
T
+ spell
%S%f_set_target(%PU)
This means that we set targets to all constructors implicitly by specifying the parent class (%P - Parent, U – guide of the element; actually name can be used here as well, but it can be not clear and guide will sure point at the element we need).
Let us get back to the subject domain and the model. Suppose we want to dispay it on our language by simple rules: every instant of the entity has to correspond with the program class and the parts of the organism have to be available as its properties. Each organism must have its special constructor that possesses the instants of other organisms related to the current one.
In real life such dispay will base on some framework, at least display classes instances will most probably be the subclasses of this framework. That is why we will “think up” a primitive framework and use it for display and specify that all Body Parts should be inherited from the base class BodyPartsBase and all organisms – from the OrganismBase. Every organism has to also implement the default parameterless constructor from base class.
First we create our framework. Formally it may be outside the model, so we will need at least two placeholders for framework elements that we’ll use in templates for reference. We also can draw it as a part of our model or export from the other that we already have. In our example we’ll use the third way – summoning. Technically, it is the same to drawing it but it is more interesting and sometimes more simple. To do it we’ll define a new function _summon_framework and call it in the spell of the project:
: Life Forms::Category
+ spell
%S%f_summon_framework()
<%CX>
%f _summon_framework
%S%f_add_category(XWF,ModuleX,XFramework,FW)
%{FW}%f_set_documentation(Framework for Life Forms)
%{FW}%f_add_class(BP_BASE,ClassX,BodyPartsBase,ADDS)
%{ADDS}%f_set_documentation(Base class for Body Parts)
%{FW}%f_add_class(ORG_BASE,ClassX,OrganismBase,ADDS)
%{ADDS}%f_set_documentation(Base class for Organisms)
%{ADDS}%f_add_operation(CTOR_DEF,ctor,make_deafult():OrganismBase,TMP)
%{TMP}%f_set_documentation(Default parameterless constructor - has to be defined by a specific instant)
%{TMP}%f_set_abstraction_type(abstract)
%{ADDS}%f_add_operation(EX1,,all_my_parts():BodyPartsBase,TMP)
%{TMP}%f_set_documentation(Example operation)
// Framework for Life Forms
module XFramework {
// Base class for Body Parts
class BodyPartsBase {
};
// Base class for Organisms
class OrganismBase {
// Default parameterless constructor - has to be defined by a specific instant
operation makeDeafult (): OrganismBase {
}
// Example operation
operation allMyParts (): BodyPartsBase {
}
};
}
It is all fine except there is no function’s body and, generally speaking, abstract constructor should have no body. Let us finish the template so that abstract methods have no body and end with "= 0;" and so the body appears for the rest methods including implementation:
: ClassX::Class
%f _element_definition
%f_set_var(CONTEXT,S)\
class %SN[ --\> <{, }%GN>] {
...
: ClassX::Class::Operation
%f _element_definition
operation %f_to_javaex(%SN) (<{, }%CN:%C%TN>): %TN\
[{%{CONTEXT}U=%PU&%Sa=abstract}{%S%f_body()} = 0;]
%f _body
{
%U[
]
}
In the class template we first specify the current context, i.e. a specific instant of the class whose content is currently generated. Next we check the method template for the context we work in and, if this is an abstract method and the context is our direct parent (i.e. the class that defines the abstract method), then we output " = 0;", otherwise we call the method with the body template defined below.
%U outputs the so called User Sections – text blocks framed with the special comments (their format is adjusted for each generator separately) that are parsed by generator on the second generation, their content is identified and added to the newly generated code near the same identifiers. It allows to update the generated artifacts with any user updates that are saved during the regeneration, moved to the other place and, if needed, deleted in case the related element is deleted.
Let’s launch generation:
// Framework for Life Forms
module XFramework {
// Base class for Body Parts
class BodyPartsBase {
};
// Base class for Organisms
class OrganismBase {
// Default parameterless constructor - has to be defined by a specific instant
operation makeDeafult (): OrganismBase = 0;
// Example operation
operation allMyParts (): BodyPartsBase {
//#UC START# *D5AD8599E380*
//#UC END# *D5AD8599E380*
}
};
}
//#UC START# *D5AD8599E380* begins the user section with ID D5AD8599E380 which is the element’s ID of the operation all_my_parts that we get automatically when summoning and which is saved in the file of the model. In its turn, //#UC END# ends the user section.
The contents would be saved when regenerated, so we can write the implementation of this method and stay calms.
Let’s say the framework is finished and we may start to display the subject domain. The first problem we face is elements’ names. In our case they are in Russian and we need English characters to display in langX. To solve it we add a property field to all elements of the subject domain which can be used by developer to specify the English equivalent of the Russian name. In order to avoid adding the property to each element of the subject domain we implement one base stereotype:
: LF Element::Class
p eng_name:s ? English name
%f _x_name
[{}{%SN}%S{eng_name}]
: Body Part::Class
= LF Element::Class
%f _x_name
%S%[inherited]Of%P%f_x_name()
%f _x_name
%S%[inherited]Of%P%f_x_name()
...
: Organism::Class
= LF Element::Class
...
p <property name>:<type>[ ? <description>] specifies the user property for stereotype. There are binary (yes/no) and string (free strings) properties, values lists or files. In this case, this is a usual string.
Function _x_name was introduced for ease in case English name is not specified so we can use the main element’s name. It also uses parent’s name for Body Part in order to get a unique name of the class.
Let’s add English names to all elements:
Function _x_name was introduced for ease in case English name is not specified so we can use the main element’s name. It also uses parent’s name for Body Part in order to get a unique name of the class.
Let’s add English names to all elements:
: Life Forms::Category
+ spell
%S%f_summon_framework()
%S%f_add_category(X_IMPL,ModuleX,%S{eng name},TMP)
%{TMP}%f_add_dependency(%{FW}U)
<%CX>
<%C#f_spell_usage()>
After our root package creates the framework we add the display module. As a module name we use the value of the user property {eng name}. We’ll specify that the display depends on the framework (in order to control the access between the elements)m and then we’ll call summoning of the children. Since circular dependencies are allowed in the meta-model of the subject domain (John loves Snoopy and Snoopy trusts in John) we'll split the display in two phases. The first phase is summoning of the needed classes and the second phase is joining it with each other. Otherwise double-meaning is possible when we have to use the unsummoned element. So, we add the _spell_usage method and define it on the Organism only (method call form through # means that the method is called only if it is specified on the instant, any attempt to call some method through % fails).
: Organism::Class
+ spell
%P%{X_IMPL}%f_add_class(X_%SU,ClassX,%S%f_x_name(),ADDS)
%{ADDS}%f_add_inheritable(OrganismBase)
%{ADDS}%f_set_documentation(Implementation class for %SN[ %SD])
%S%f_set_var(X_IMPL,{ADDS})
<%CX>
%f _spell_usage
%f_set_var(MY_IMPL,S%{X_IMPL})
%f_clear_list(CTOR_ARGS)
<
[{%CC=Dependency}{
%{MY_IMPL}%f_add_attribute(X_%CU,property,%C{eng_name}:%C%{X_IMPL}N,TMP)
%{TMP}%f_set_documentation(%CN[ %CD])
}
%f_add_to_list(CTOR_ARGS,C)
]
>
[{%{CTOR_ARGS}<{}{}{%CC}>!=0}
%{MY_IMPL}%f_add_operation(X_CTOR,ctor,make_with(%{CTOR_ARGS}<{,}%C{eng_name}:%C%T%{X_IMPL}N>),TMP)
%{TMP}%f_set_documentation(Constructor. Parameters: %{CTOR_ARGS}<{; }%C{eng_name}:%C%TN[ %C%TD]>)
]
The Organism adds its display class to the module created on the parent and specifies the base class to inherit from, defines the documentation and saves the reference to it in the variable X_IMPL. In the second phase of display (method _spell_usage) we go through our children, add appropriate properties and fill the list of relations which will then be used as a basis for constructor forming.
: Body Part::Class
+ spell
%P%P%{X_IMPL}%f_add_class(X_%SU,ClassX,%S%f_x_name(),ADD)
%{ADD}%f_add_inheritable(BodyPartsBase)
%{ADD}%f_set_documentation(Implementation class for %SN[ %SD] of %PN[ %PD])
%S%f_set_var(X_IMPL,{ADD})
Finally, the Body Part simply adds its display class with the ancestor specified, and that’s that.
It seems we are ready to launch generation. As a result, we get one new file with display description:
module Example {
// Implementation class for Head with teeth of Snoopy the Dog
class HeadOfSharickTheDog --> BodyPartsBase {
};
// Implementation class for Foot of Snoopy the Dog
class PawOfSharickTheDog --> BodyPartsBase {
};
// Implementation class for Trundle Tail of Snoopy the Dog
class TailOfSharickTheDog --> BodyPartsBase {
};
// Implementation class for Spotty Body of Snoopy the Dog
class BodyOfSharickTheDog --> BodyPartsBase {
};
// Implementation class for Fair Hair of John
class HeadOfVasija --> BodyPartsBase {
};
// Implementation class for Left Hand of John
class LeftHandOfVasija --> BodyPartsBase {
};
// Implementation class for Right Hand of John
class RightHandOfVasija --> BodyPartsBase {
};
// Implementation class for Left Foot of John
class LeftFootOfVasija --> BodyPartsBase {
};
// Implementation class for Right Foot is a bit shorter than the left one of John
class RightFootOfVasija --> BodyPartsBase {
};
// Implementation class for Second Head is better than the first one of Buddha
class SecondHeadOfBuddah --> BodyPartsBase {
};
// Implementation class for Fair Hair of Buddha
class HeadOfBuddah --> BodyPartsBase {
};
// Implementation class for Left Hand of Buddha
class LeftHandOfBuddah --> BodyPartsBase {
};
// Implementation class for Right Hand of Buddha
class RightHandOfBuddah --> BodyPartsBase {
};
// Implementation class for Left Foot of Buddha
class LeftFootOfBuddah --> BodyPartsBase {
};
// Implementation class for Right Foot is a bit shorter than left one of Buddha
class RightFootOfBuddah --> BodyPartsBase {
};
// Implementation class for Buddha
class Buddah --> OrganismBase {
// Second Head is better than the first one
property secondHead: SecondHeadOfBuddah;
// Fair Hair
property head: HeadOfBuddah;
// Left Hand
property leftHand: LeftHandOfBuddah;
// Right Hand
property rightHand: RightHandOfBuddah;
// Left Foot
property leftFoot: LeftFootOfBuddah;
// Right Feet is a bit shorter than left one
property rightFoot: RightFootOfBuddah;
// Default parameterless constructor - has to be defined by a specific instant
operation makeDeafult (): OrganismBase {
//#UC START# *F66E89A23C1C_BASE*
//#UC END# *F66E89A23C1C_BASE*
}
};
// Implementation class for Fair Hair of Mary
class HeadOfMary --> BodyPartsBase {
};
// Implementation class for Left Hand of Mary
class LeftHandOfMary --> BodyPartsBase {
};
// Implementation class for Right Hand of Mary
class RightHandOfMary --> BodyPartsBase {
};
// Implementation class for Left Foot of Mary
class LeftFootOfMary --> BodyPartsBase {
};
// Implementation class for Right Foot is a bit shorter than the left one of Mary
class RightFootOfMary --> BodyPartsBase {
};
// Implementation class for Mary
class Mary --> OrganismBase {
// Fair Hair
property head: HeadOfMary;
// Left Hand
property leftHand: LeftHandOfMary;
// Right Hand
property rightHand: RightHandOfMary;
// Left Foot
property leftFoot: LeftFootOfMary;
// Right Feet is a bit shorter than left one
property rightFoot: RightFootOfMary;
// Default parameterless constructor - has to be defined by a specific instant
operation makeDeafult (): OrganismBase {
//#UC START# *F66E89A23C1C*
//#UC END# *F66E89A23C1C*
}
};
// Implementation class for Snoopy the Dog
class SharickTheDog --> OrganismBase {
// Head with teeth
property head: HeadOfSharickTheDog;
// Paw
property paw: PawOfSharickTheDog;
// Trundle Tail
property tail: TailOfSharickTheDog;
// Spotty Body
property body: BodyOfSharickTheDog;
// Constructor. Parameters: beleveTo:John
operation makeWith (beleveTo:Vasija): SharickTheDog {
//#UC START# *3565C7C4C308*
//#UC END# *3565C7C4C308*
}
// Default parameterless constructor - has to be defined by a specific instant
operation makeDeafult (): OrganismBase {
//#UC START# *F66E89A23C1C*
//#UC END# *F66E89A23C1C*
}
};
// Implementation class for John
class Vasija --> OrganismBase {
// Fair Hair
property head: HeadOfVasija;
// Left Hand
property leftHand: LeftHandOfVasija;
// Right Hand
property rightHand: RightHandOfVasija;
// Left Foot
property leftFoot: LeftFootOfVasija;
// Right Feet is a bit shorter than left one
property rightFoot: RightFootOfVasija;
// Constructor. Parameters: loveTo:Snoopy the Dog; loveTo:Mary; beleveTo:Buddha
operation makeWith (loveTo:SharickTheDog, loveTo:Mary, beleveTo:Buddah): Vasija {
//#UC START# *5558B8CAB716*
//#UC END# *5558B8CAB716*
}
// Default parameterless constructor - has to be defined by a specific instant
operation makeDeafult (): OrganismBase {
//#UC START# *F66E89A23C1C*
//#UC END# *F66E89A23C1C*
}
};
}
A careful look at the display code shows some drawbacks. First, we need to load the module with framework. Secondly, we want the special constructor makeWith to call the default constructor. The third drawback is that user sections IDs for the base abstract constructor have been generated identically that would obviously cause failure of the second-generation since sections’ correspondence is not clear.
We add the following code to the module template to load the framework:
: ModuleX::Category
%f _element_definition
[import <{,} %DN>;
]module %SN {
<{\n\n}%CX>
}
This template outputs other modules that the current module is depended from separated by comma.
To call the base constructor we add the following template:
: ClassX::Class::ctor::Operation
+ spell
%S%f_set_target(%PU)
%S%f_set_pre_uc_content(langx,,%S%f_ctor_pre_content())
%f _ctor_pre_content
self = %P%GN.makeDefault;
_set_pre_uc_content is a system function that adds the template specified by the argument BEFORE the user section is output.
Finally, in order to remove the double IDs of user sections we use the existing generation context:
: ClassX::Class::Operation
%f _body
{
[{%{CONTEXT}U=%PU}{\
%U[{_BASE_FOR_%{CONTEXT}U}
]
}\
%U[
]
]\
}
Here we check the base of method generation and, in case of direct parent, we extend user sections to make it unique.
The problems will be eliminated after regeneration:
import XFramework;
module Example {
....
// Constructor. Parameters: beleveTo:John
operation makeWith (beleveTo:Vasija): SharickTheDog {
self = OrganismBase.makeDefault;
//#UC START# *3565C7C4C308*
//#UC END# *3565C7C4C308*
}
// Default parameterless constructor - has to be defined by a specific instant
operation makeDeafult (): OrganismBase {
//#UC START# *F66E89A23C1C_BASE_FOR_5ECD455013CA*
//#UC END# *F66E89A23C1C_BASE_FOR_5ECD455013CA*
}
....
}
Conclusion
Overall, that is where I stop. I hope my example helped to learn the main principles. In conclusion, let me add that even within this imaginary example we can demonstrate the flexibility of the tool we use. For example, we can add the settings mentioned previously, make entities persistent, add display support on other languages or show how to extend the existing languages by adding useful features and so on.
In response to your demand I give the meta-model:
In response to your demand I give the meta-model:
The key PART of the meta-model. Again, it is COMMON for ALL:
The most interesting “block” is MDAClass since it is self-determined and self-generated.
Комментариев нет:
Отправить комментарий