• No results found

Similarities to Programming Language Design

The design of modeling languages and the construction of compilers for text-based pro- gramming languages (e.g., C, C++, Java) present many similar aspects among themselves.

Of particular relevance for this dissertation are the similarities regarding the definition of the language’s abstract syntax, semantics, and concrete syntax.

Text-based programming language compilers – or computer-based textual language processors of any kind – are typically constructed by defining [Sco 09]: (1) a scanner; (2) a parser; and (3) a parse tree processor. Figure 2.17 illustrates the various stages in the traditional compilation of a computer program, namely the components that are used; note that what we designated in this list as a parse tree processor corresponds in Figure 2.17 to the last four stages of the compilation process.

2.5. SIMILARITIES TO PROGRAMMING LANGUAGE DESIGN

A scanner, also called a lexical analyzer or lexer, is a tool to support the process of (1) taking a sequence of characters as input and (2) outputting a corresponding sequence of tokens (also called a token stream). This kind of tool is defined by using support tools like lex [LMB 92] (or using a similar syntax), depending on the supporting technologies or frameworks. A scanner is responsible for handling the language’s textual syntax, and is typically specified as a set of pairs <recognized input characters, token to output>. At runtime, it will try to match input characters with each of the specified pairs (in order, from first to last); if a match is found, the corresponding token will be added to the output token stream. Nevertheless, it is possible for each pair to provide additional processing code, which can help determine whether the pair should be considered a match. Listing 2.1 provides a small example of a lex-based scanner specification, which captures numbers and words (returning the tokens NUMBERand WORD, respectively), and ignores whitespace.

Listing 2.1: Example of a scanner specification (adapted from [LMB 92]).

1 %{ 2 # i n c l u d e ” y . t a b . h ” 3 %} 4 %% 5 [0 −9]+ { r e t u r n NUMBER; } 6 [ a−zA−Z]+ { r e t u r n WORD; } 7 \n /∗ i g n o r e end o f l i n e ∗/ ; 8 [ \ t ]+ /∗ i g n o r e w h i t e s p a c e ∗/ ; 9 %%

A parser, also called a syntactic analyzer, is another tool to support the compilation process. In particular, it is used to (1) receive a token stream as input and (2) generate and return the corresponding parse tree. A parser is typically based on tools like yacc [LMB 92], and it is defined as a formal grammar (i.e., a set of rules or constraints that must be followed, otherwise the input token stream will be considered invalid and the parsing fails). In turn, this grammar is specified using BNF (Backus-Naur Format) or a similar formalism. Furthermore, it is possible for each grammar rule to provide additional processing source code, which has the added advantage of enabling further semantic constraints checks. Listing 2.2 also provides a small example of a yacc-based parser specification.

Thus, a parser is responsible for mapping the program’s string (written in the language’s textual concrete syntax) into a parse tree, a data structure representing the captured ele- ments of the language’s concrete syntax (e.g., Java’s classes and interfaces). Although optional, it is possible to further translate this parse tree into another data structure that more accurately reflects the language’s abstract syntax, instead of its concrete syntax.

Finally, the parse tree processor is a program that receives a parse tree and performs a set of actions (e.g., verifications, transformations to another language). An example is again

Listing 2.2: Example of a parser specification (extracted from [LMB 92]). 1 %{ 2 #i n c l u d e < s t d i o . h> 3 %} 4 % t o k e n NAME NUMBER 5 %% 6 s t a t e m e n t : NAME ’= ’ e x p r e s s i o n 7 | e x p r e s s i o n { p r i n t f (”= %d\n ” , $ 1 ) ; } 8 ; 9 10 e x p r e s s i o n : e x p r e s s i o n ’+ ’ NUMBER { $$ = $1 + $ 3 ; } 11 | e x p r e s s i o n ’ − ’ NUMBER { $$ = $1 − $ 3 ; } 12 | NUMBER { $$ = $ 1 ; } 13 ;

depicted in Figure 2.17, in which the parse tree processor consists of a sequence of steps that translate the input parse tree into the corresponding machine-specific assembly code. Nevertheless, it is possible to perform other operations over the parse tree: an example can be found in WebC-Docs [SS 09 b, SS 11 a], which defines a processor that receives a parse tree (obtained from a user’s search string) and converts it into a semantically equivalent object structure, which is then provided to the underlying indexing technology.

As previously mentioned, the metamodeling aspects already presented in this chapter can be considered as similar to these programming language definition components. More concretely, the scanner definition process can be considered as the equivalent to defining a program that can read the modeling language’s concrete syntax. Furthermore, the activity of defining a parser (and the facilities to convert the parse tree into a more abstract tree) can be considered as equivalent to defining a program that can receive a stream of the modeling language’s concrete syntax elements and mapping them to the corresponding abstract syntax model. Finally, the parse tree processor is equivalent to whichever facilities are created to deal with models, such as ensuring that semantic constraints are not violated or applying a model transformation to obtain source code. Even code generation can be regarded as being equivalent to model-to-model transformations (which are analyzed in Chapter 3), if we consider a destination modeling language with a textual concrete syntax.

Summary

In this chapter, we have presented the MDE paradigm and some important modeling language creation approaches which are relevant to the work presented in this dissertation, namely the MDA software development approach (which is based on the usage of model transformations and of standards like the UML or MOF modeling languages) and the DSM approach (which is supported by the definition and usage of DSLs).

2.5. SIMILARITIES TO PROGRAMMING LANGUAGE DESIGN

Furthermore, we have explained and discussed some metamodeling and modeling language design issues, namely (1) a modeling language’s abstract syntax, semantics, and concrete syntax, (2) the usage of Strict or Loose metamodeling, (3) the potential ambiguity of the is-a relationship between two modeling elements, (4) the Level Compaction technique and its effect on the possible number of modeling levels allowed, and (5) the accidental vs. essential complexity in modeling languages. Figure 2.18 shows a simple mindmap overview of the analyzed approaches and the issues that were presented and discussed.

Figure 2.18: Overview of approaches and metamodeling aspects.

In the next chapter, we elaborate on the subject of model transformations in model- -driven engineering and in traditional software development activities. These transfor- mations assume particular relevance in this dissertation, because some of the presented transformation mechanisms provide the underlying concepts and ideas for the solution presented in Chapters 6 and 9. The solution presented in those chapters requires the definition of a model synchronization mechanism, which is based on these transformations.

Chapter 3

Model Transformations

I object to doing things that computers can do.

Olin Shivers In Chapter 2 we present some approaches for creating modeling languages, as well as some aspects that modeling language designers should take into consideration. However, it would be unreasonable to assume that metamodels – and models – do not change over time. Some of those changes will be performed by users (e.g., editing the model, adding a new element to the metamodel, changing a name), but other model changes – namely those resulting from repetitive tasks, such as updating class name prefixes (because of changes to the metamodel) or adding trace elements – should be performed automatically.

The issue of how to change a metamodel assumes particular relevance because models, consistent in the context of a certain metamodel, can become inconsistent with even a small number of changes to that metamodel (e.g., if an element is removed from the metamodel) [MV 11]. Obviously, this introduces a potential element of disruption that should be avoided.

One possible way of ensuring the validity of existing models, when changing their corresponding metamodel(s), is through the definition of a migration process based on model-to-model transformations: for any change to a metamodel, a corresponding transfor- mation must be defined, receiving a (previously consistent) model and producing a new model that is now consistent with the updated metamodel. If each model transformation is correct (i.e., it correctly specifies how to transform an input model to another output model) and the input model is also correct (i.e., it is well-formed according to its metamodel and it correctly expresses the reality that the model aims to represent), then the model that results from composing those transformations will also be correct. In other words, if we assume that:

• Ma represents a metamodel in a version a (i.e., the metamodel M is in a certain

stage a of its lifetime),

• mMa represents a model that expresses a certain meaning m using the concepts

provided by metamodel Ma,

• ∀a, b : δMa

b (Ma) ⇒ Ma+1

(Applying any change δb to a metamodel Ma originates a new metamodel Ma+1,

which consists of metamodel M but now in the next stage, a + 1, of its lifetime), • ∀a, b : δMa

b → T Ma

b

(Any change δMa

b that is applicable to a metamodel Ma, can also be mapped to a

corresponding transformation TMa

b that receives an Ma model as input), and

• δMa

c (Ma) = Ma+1 → TcMa(mMa) = mMa+1

(A model transformation Tcthat results from a metamodel change δcMa, when applied

to a model mMa defined using metamodel M

a, originates a new model mMa+1 that

has the same meaning but is defined with metamodel Ma+1),

then we can infer that composing the transformations that result from the various changes to a metamodel will result in a transformation that effectively upgrades corresponding models to another version of the metamodel:

∀x, i = 1, . . . , n : δMx+1 i+1 ◦ δ Mx i (Mx) = Mx+2 → T Mx+1 i+1 ◦ T Mx i (m Mx) = mMx+2

Of course, metamodel changes are not the sole motivation for the definition of model- -to-model transformations. Another worthwhile (and more common) scenario is providing

a model MA and obtaining a corresponding model MB in another language.

In the context of our research work, we have studied and analyzed a small set of MDE-oriented model transformation languages, namely: (1) the OMG’s Query/View/- Transformation (QVT) language [OMG 11 a]; (2) the OMG’s MOF Model To Text Trans- formation Language (MOFM2T) [OMG 08]; and (3) AtlanMod’s ATL Transformation Language (ATL). We have also analyzed some traditional software development topics and mechanisms that can be considered as related to the topic of model transformations, namely (1) the usage of SQL scripts, (2) the usage of Object-Relational Mapping (ORM) frameworks and the definition of Migrations, (3) source code management tools, and (4) the compilation of computer programs to executable form. Finally, we also provide an analysis of XSLT (eXtensible Stylesheet Language Transformations), as it can be considered a model transformation mechanism for XML-based models.

These languages and mechanisms (some of which are frequently used in software development processes) were chosen either because of their relevance for our research work, or because they share some underlying concepts with existing model transformation