• Keine Ergebnisse gefunden

Implementation of the Static Semantics

Im Dokument Formal Semantics for SDL (Seite 164-167)

Part 5: RSDL Reference Implementation

5.4 Implementation of the Static Semantics

The static semantics is given by several parts. First, there are transformation rules that transform the RSDL shorthand notations (given in AS0) into their derived counterparts (also in AS1). When doing this, there is also a static analysis included, which means to check whether the identifiers used are really declared, if the types match and the like. This is expressed by well formedness conditions given by Boolean functions that have to be true for the corresponding nodes of the AS0 tree. The transformations are done in several steps. For each step, there are some initial static conditions to check.

The static conditions and the transformation rules are again analysed using the general methodology. They all use a common part of the ASM abstract grammar, namely expressions and patterns as shown below.

expr: Variable(casestring) /* a variable name */

| Literal(casestring) /* a literal name */

| Program(casestring) /* a program name */

| Domain(casestring) /* a domain name */

| IfExpr(expr expr expr) /* a conditional expression */

| CaseExpr(expr cases) /* a case expression */

| FunCall(casestring argumentList) /* a function call */

| Index(expr expr) /* an index expression */

| MKCall(casestring argumentList) /* a constructor call */

| Select(casestring casestring expr) /* a selection function call */

| BinOp(casestring expr expr) /* a binary operator */

| UnOp(casestring expr) /* an unary operator */

| Quant(qkind nameList expr expr) /* a quantified expression */

| SetComp(expr casestring expr expr) /* a set comprehension */

| SeqComp(expr casestring expr expr) /* a sequence comprehension */

| EmptySet() /* an empty set */

| Undef() /* an undefined value */

;

cases: list caseentry; /* list of case entries */

caseentry: Case(pattern expr defList); /* one case of a case expression */

qkind: Exi() | Gen(); /* quantificator: existential or general */

argumentList: list expr; /* list of arguments */

nameList: list casestring; /* list of names */

pattern: NamedPattern(casestring pattern) /* pattern with a name */

| ConstructorPattern(casestring parameters) /* a constructor pattern */

| MatchAll() /* unspecified pattern (“*”) */

| KeywordP(casestring) /* keyword in a pattern */

| TwoKeywordP(casestring casestring) /* two keywords in a pattern */

| UndefP() /* an undefined value in a pattern */

;

parameters: list pattern; /* list of patterns */

These are more than necessary for the transformations, but this way they can also be used for the ASM part. Now we have to insert the rules for the transformations as shown below.

definition: Transformation(letStatements pattern expr casestring expr depTrans);

depTrans: list depTransform;

depTransform:

DependentTransformation(letStatements expr expr) | DependentForall(casestring expr letStatements expr) ;

The same is true for the conditions, that are now represented by the following additional rule.

definition: Condition(casestring expr);

These abstract syntax descriptions are again supplemented by corresponding syntax and lexis descriptions that are omitted here for reasons of brevity. They are really straightforward and do not pose special problems.

The transformation rules are analysed and then transformed into kimwitu rewrite rules. In fact, the generated rewrite rules are grouped into different rewrite views in order to reflect the different steps of the static semantics.

The conditions are transformed into kimwitu functions that are checked using kimwitu unparsing rules before and after the individual steps of the static analysis.

5.4.1 Generation of Rewrite Rules from the Transformations

The format of simple transformations does nicely match the kimwitu rewrite rules format. It is only necessary to introduce rewrite rule names for the single steps. For simplicity they are named trans_1, trans_2, etc.

The following special problems have to be handled for the full expressiveness of the transformations:

• auxiliary functions (see Section 5.4.2),

• provided-clauses, and

• multiple rules.

Generation of Provided-clauses

The original kimwitu does not allow to formulate conditional rewriting. However, the new version of kimwitu++ provides support for conditional rewriting as necessary for the RSDL semantics transformations.

Multiple rules

Multiple transformation rules as used within the RSDL semantics description are far beyond the kimwitu built-in capabilities. However, they can be implemented usbuilt-ing the possibility to state arbitrary C-code withbuilt-in the rewrite rules. This way the tree can be reorganised without kimwitu noting it. Utmost care is necessary here in order not to destroy the abstract tree accidentally.

5.4.2 Auxiliary functions

There are three kinds of auxiliary functions, namely derived functions, controlled functions and external or predefined functions. Auxiliary functions are used within the formulation of the transformations and within the static conditions and also within the mapping and in the compilation. Their implementation is given by a corresponding kimwitu function.

Derived functions

Derived functions are implemented almost one-to-one in kimwitu. The elementary functions used are

• selection functions that are generated together with the abstract syntax structure,

• construction functions that are used as generated by kimwitu,

• structural equality which are the kimwitu generated equal functions

In order to have a better performance for the functions, all auxiliary functions are implemented as memo functions. This means, they are evaluated only once and the result is then stored (memorised) at the corresponding syntax node. A consecutive call of the function with the same arguments is then simply retrieved and not computed once more.

Controlled functions

There is a difference in the handling of derived functions and controlled functions. Derived functions are implemented as memo functions that could access a local node variable for their return value. Controlled functions, on the other hand, are implemented as attributes of nodes with the initial value of undefined.

Predefined/External Functions

Predefined ASM functions like concatenation, etc. are implemented as predefined functions over the abstract syntax tree. The functions might include several predefined functions that are available for ASM functions.

These predefined functions are declared separately within a file functions.k. Especially, there are set and sequence handling functions in this file.

There are also two external function that are heavily used within the transformations and within the compilation, namely the functions newName and uniqueLabel. The semantics of newName is, that it yields for every step a unique new name. However, it is only called once per transformation such that an implementation as C function returning always a new unique name is perfectly valid.12 The semantics of uniqueLabel is to return a new, unique label for each abstract syntax tree node. This is easily implemented using appropriate node attributes.

5.4.3 Generation of a Mapping Function

The mapping from the AS0 to the AS1 is given as a function within the formal semantics description. It will also be implemented as a function in kimwitu. The mapping as well as the compilation functions are just very large function definitions. So they are represented as function definitions in the ASM abstract grammar.

12 Please note the slight difference between the two functions: the correct implementation would have to return the same result whenever the function is called twice in the same transformation step.

The mapping function is generated using the function construction means of kimwitu. However, the implementation is particularly easy in this case, as the mapping function is given using patterns that are then transformed to parts that in turn are generated using other patterns. This kind of function definition is very much supported in kimwitu such that the implementation generation is very simple. Please find below a sample part of the mapping as defined within the semantics and the counterpart in the implementation.

AS1_rule

Mapping(AS0_rule $a) {

AS0_TOKEN(v_x):

{ return AS1_Name(AS1_TOKEN(v_x)); } v_i=AS0_identifier(v_q, v_name):

{ return refersto0(v_i)->eq(AS1_TOKEN(mkcasestring("predefined"))) ?

AS1_Literal(Mapping(v_name)) : AS1_Identifier(Mapping(v_q), Mapping(v_name)); } AS0_qualifier(v_q):

{ return Mapping(v_q); }

AS0_path_item(AS0_TOKEN("block"), v_n):

{ return AS1_Agent_qualifier(v_n); } AS0_path_item(AS0_TOKEN("block type"), v_n):

{ return AS1_Agent_type_qualifier(v_n); } AS0_rsdl_specification(

AS0_textual_system_specification_gen_agent_type_definition(v_t, v_s), *):

{ return AS1_RSDL_specification(Mapping(v_t), Mapping(v_s)); } AS0_block_type_definition(AS0_block_type_heading(v_name),

AS0_agent_type_structure(v_entities, v_body), *):

{ return AS1_Agent_type_definition(Mapping(v_name),

AS0_TOKEN("sender"):

{ return AS1_Sender_expression(); } AS0_timer_active_expression(v_id):

{ return AS1_Timer_active_expression(Mapping(v_id)); } AS0_UNDEF():

{ return AS1_TOKEN(mkcasestring("--error-no match--")); } }

5.4.4 Generation of a Compilation Function

The compilation function is also given in the same way as the mapping function. However, the destination domain, i.e. the domain BEHAVIOUR and all the domains below it are not defined within kimwitu. Moreover, the abstract representation of the behaviour is not used within kimwitu but instead has to be transformed to the ASM format. So a simple behaviour domain is defined in kimwitu to be used as the result domain of the compilation. The generated behaviour structure is then output using a simple unparse view as shown below.

%uview outputCode, reallyOutputCode;

genCode: list genInstr;

genInstr: GenInstr(casestring genCode);

c=ConsgenCode(*,*) /* wrapper for the predefined part */

-> [outputCode: "transition InitCompilation ==\t\n"

c:reallyOutputCode "\b" ];

GenInstr(n,NilgenCode()) -> [reallyOutputCode: n ];

GenInstr(n,args)

-> [reallyOutputCode: "mk_" n "(\t" args "\b)" ];

ConsgenCode(h,NilgenCode()) -> [reallyOutputCode: h ];

ConsgenCode(h,t)

-> [reallyOutputCode: t ",\n" h ];

Im Dokument Formal Semantics for SDL (Seite 164-167)