![]() |
![]() |
![]() |
![]() |
![]() |
Abstract Data Types
This section is a reference to the abstract data types. The following topics will be discussed:
- We will have a look at the implementation of the predefined data types in SDL, see SDL Predefined Types. We then discuss how user-defined abstract data types are translated, see Translation of Sorts.
- Next implementation of operators and the possibility to include hand-coded C functions as implementation of the operators is presented, see Implementation of User Defined Operators.
- Last, in More about Abstract Data Types, we discuss more details about operators and the possibilities to include a hand-coded type definition in C to represent the SDL sort.
Removing un-used SDL Operators
When implementing an SDL system, you do not always use all available SDL operators. The Cbasic/Cadvanced SDL to C Compiler removes the declarations of unused operators, thus minimizing the code size of the generated application. Unused operators that are removed are:
- operators in predefined data types, for example substring, concatenate, length in the newtype Charstring, etc.
- operators defined in the predefined generators String, Array, Powerset, Bag
- special operators (and help functions) like assign, equal, default, make, extract, modify, free
The Cbasic/Cadvanced SDL to C Compiler performs the following steps to optimize the code:
Example 360 The #ifndef definition
#ifndef XNOUSE_AND_BIT_STRING/* function implementing the operator */#endif
- During the code generation, the usage of the operators in the translated SDL transitions is recorded.
- The interdependencies between different operators are updated. For instance, an equal operator for a struct type may depend on equal operators for all its component types.
- For each operator that is found to be unused, a #define definition is generated that removes the code for that operator. All the defines are placed in a file called sdl_cfg.h.
Example 361 The #define command
#define XNOUSE_AND_BIT_STRING
Even though the code size of the generated application is reduced, the code size of the generated C code is increased.
Manual override
In order to handle cases where operators are used invisibly from the Cbasic/Cadvanced SDL to C Compiler, for example in inline C code, you can manually override the automatic configuration of the unused operators.
In the code generation process, the Targeting Expert always generates a manual configuration file called sct_mcf.h. In this file you can list the unused operators that you have decided to include in the application. This is done by un-defining the previous definitions made in the sdl_cfg.h file.
The sct_mcf.h can be edited directly from Targeting Expert. Select Edit Configuration Header File from the Edit menu to open the file.
Example 362 The #undef command in the sct_mcf.h file
#ifdef XNOUSE_AND_BIT_STRING#undef XNOUSE_AND_BIT_STRING#endifOne section of the sct_mcf.h file, is dedicated for the manual edits. This section is marked with the text:
/* BEGIN User Code *//* END User Code */The manual edits must be inserted between these to lines otherwise they will be deleted, as the sct_mcf.h file is re-generated each time you generate code. The information about the unused operators available in the sdl_cfg.h file is imported to the sct_mcf.h file. This allows you to quickly see which operators that are unused.
SDL Predefined Types
Mapping Table
Below is a table which summarizes the mapping rules between SDL and C, concerning the predefined types in SDL and their operators. Note that many of the operators are in C defined as macros, and expanded by the C preprocessor to simple operators in C.
C Definitions
We will here discuss the types and macros supplied by the runtime library in the Cadvanced/Cbasic SDL to C Compiler for the predefined types in SDL. These macros and extern definitions for functions can be found in the file sctpred.h, except for the Pid sort which is handled in the file scttypes.h.
For more information about the Charstring sort, see the section Handling of the Charstring Sort.
Translation of Sorts
The following data types are handled by the Cadvanced/Cbasic SDL to C Compiler:
- Predefined Types
- Enumeration Type
- Struct
- Choice
- Array
- String
- Powerset
- Bag
- Ref, Own, Oref
- Syntypes
- Inheritance
Predefined Types
All the predefined data types (Integer, Natural, Boolean, Character, Charstring, Real, Time, Duration, Pid, Bit, Bit_string, Octet, Octet_string, Object_identifier, IA5String, NumericString, PrintableString, and VisibleString) are completely handled. The name of these types in the generated C code will be SDL_Integer, SDL_Natural, SDL_Boolean, and so on. The translation rules for these types and their operators are discussed in more detail in the SDL Predefined Types.
Enumeration Type
A sort which is not a struct and does not contain any inheritance or generator instantiation, but which contains a literal list, is seen as an enumeration type. See the example below. Such a type is translated to int, together with a list of defines where the literals are defined as 0, 1, 2, and so on. As in all examples in this sub-section, the prefixes, which are added to names when they are translated to C, are not shown. The prefixes are added to make sure that no name conflicts occur in the generated program. For more information about prefixes see Names and Prefixes in Generated Code.
Example 363 : Enumeration Type
NEWTYPE EnumTypeLITERALS Lit1, Lit2, Lit3;ENDNEWTYPE;typedef XENUM_TYPE EnumType;#define Lit1 0#define Lit2 1#define Lit3 2Where the macro XENUM_TYPE is defined in sctpred.c as:
#ifndef XENUM_TYPE#define XENUM_TYPE int#endifThis means that all enum types will be int types, except if the macro XENUM_TYPE is redefined by the user (to unsigned char for example). An enum type with 256 or more values will always be of type int and will not be affected by the macro XENUM_TYPE.
Struct
An SDL struct is translated to a struct in C, as can be seen in the example below.
NEWTYPE Str STRUCTa Integer;b Boolean;c Real;ENDNEWTYPE;typedef struct {SDL_Integer a;SDL_Boolean b;SDL_Real c;} Str;All the properties of a struct in SDL are preserved in the C code.
The predefined operators extract! and modify! are implemented as component selections in the struct in the same way as in SDL, that is, if S is a variable of type Str, then S!a in SDL is translated to S.a in C.
The predefined operator make!, which is a constructor of a struct value, is implemented by generating a Make function in C. This means that the expression "(. 12, true, 0.22 .)" in SDL is in principle translated to the C function call Make(12, true, 0.22).
The components of a struct may be of any sort that the code generator can handle. A component may, however, not directly or indirectly refer to the struct sort itself. As an example the sort Str above may not have a component of sort Str. In such a case the translation to a C struct would not any longer be valid.
There are some extensions to SDL that are handled by the code generator. It is possible to define bit fields, i.e, to define the size of components (as in C) and to have optional components and components with initial values (as in ASN.1). Examples are shown below.
Example 365 : Struct with bit fields
NEWTYPE str STRUCTa Integer : 4;b Integer : 4;: 0;c UnsignedInt : 2;d Integer;ENDNEWTYPE;typedef struct str_s {SDL_Integer a : 4;SDL_Integer b : 4;int : 0;UnsignedInt c : 2;SDL_Integer d;} str;Note that only Integer and UnsignedInt should be used in bit field components.
NEWTYPE str STRUCTa, b integer;c Boolean OPTIONAL;d str2 OPTIONAL;e Charstring := 'telelogic';f arr3 := (. 11 .);ENDNEWTYPE;typedef struct str_s {SDL_Integer a;SDL_Integer b;SDL_Boolean c;SDL_Boolean cPresent;str2 d;SDL_Boolean dPresent;SDL_Charstring e;SDL_Boolean ePresent;arr3 f;SDL_Boolean fPresent;} str;Both optional components and components with initial values have a Present flag. This is according to ASN.1 and the translation of ASN.1 to SDL defined in Z.105. The present flag for a component with initial value is true if the component contains its default value otherwise false (the Present flag is used to determine code for some ASN.1 encoding scheme). The present flag for an optional component is false until the component is assigned a value. In SDL the present flags can only be accessed through operators and cannot be changed.
Union
Please see also the CHOICE concept presented below, as it usually provides a better and more secure solution to the same kind of problems.
Using the directive #UNION (see example below) it is possible to tell the Cadvanced/Cbasic SDL to C Compiler to generate a union according to the following example:
NEWTYPE Str /*#UNION*/ STRUCTtag integer;a integer;b Boolean;c real;ENDNEWTYPE;typedef struct {SDL_Integer tag;union {SDL_Integer a;SDL_Boolean b;SDL_Real c;} U;} Str;The first component in the struct is assumed to be a tag value indicating which of the union components that are active. The tag should either be integer or an enumeration type. Tag value 0 or first enumeration literal is used to indicate that the first of the remaining components are active, and so on. On the SDL level a #UNION struct should be handled just like any other struct. It is up to the code generator to generate the correct code for operations on the struct, like assignment, test for equality, component selection, and so on.
UnionC
By using the directive #UNIONC according to the example below, it is possible to tell the Cadvanced/Cbasic SDL to C Compiler to generate a true C union.
NEWTYPE Str /*#UNIONC*/ STRUCTa integer;b Boolean;c real;ENDNEWTYPE;typedef union {SDL_Integer a;SDL_Boolean b;SDL_Real c;} Str;.
Note also that pointer types, including Charstrings are not allowed in #UNIONC structs, as it is not possible to know when to allocate and de-allocate memory for such components.
Choice
Choice, which is an SDL extension originating from the needs when translating ASN.1 to SDL and which is included in SDL-2000, can be used to express a union with implicit tag.
NEWTYPE Str CHOICEa integer;b Boolean;c real;ENDNEWTYPE;typedef enum {a, b, c} StrPresent;typedef struct {StrPresent Present;union {SDL_Integer a;SDL_Boolean b;SDL_Real c;} U;} Str;The component Present, which is the tag field, and its type (StrPresent in the example above) are both available in SDL. The Present component can in SDL be accessed, but not changed, through:
- component selection, i.e. by Variable!Present, i.e. it is possible to for example test: V!Present = a
- the operators aPresent, bPresent, or cPresent, which returns true or false depending on if the component is active or not.
The Present component is automatically set by the code generator when a component in the choice is given a value.
Note that during simulations and validations, it is automatically tested that a component "is present" when an attempt is made to access the component. A run-time error is issued if this is not the case.
Array
Instantiations of the predefined generator array can be handled by the code generator with the following restriction: The component and index sort may be any sorts that the code generator can handle, but may not directly or indirectly refer to the array type itself (see also the previous paragraph on struct).
If the index sort is a discrete sort, with one closed interval of values, that is of the following sorts:
- Character
- Boolean
- Octet
- Bit
- A sort that is considered as an enumeration type
- Syntypes of integer, character, Boolean, Octet, Bit, and enumeration types. The subtypes may only have one range condition that specifies a closed interval of values,
then the SDL array is translated to a struct containing an element which is an array in C.
If the index sort is not one of the sort in the enumeration above, the SDL array is translated to a linked list. The list head contains the default value for all possible indexes, while the list elements contain value pairs, (index_value, component_value), for each index having a component value not equal to the default value.
SYNTYPE Syn = integerCONSTANTS 0:10ENDSYNTYPE;NEWTYPE Arr ARRAY(Syn, real)ENDNEWTYPE;typedef SDL_Integer Syn;typedef struct {SDL_Real A[11];} Arr;All the properties of an array in SDL are preserved in the C code.
The predefined operators extract! and modify! are implemented as component selection of the array in C in the same way as in SDL, so if AVar is a variable of type Arr, and Index is a valid index expression, then AVar(Index) in SDL is translated to AVar.A[Index] in C. In the case of a link list implementation of the array, component selection is made through function calls.
The predefined operator make!, which is a constructor of an array value, is implemented by generic Make function in C.
String
Instantiations of the predefined generator string can be handled by the code generator with the following restriction: The component sort may be any sorts that the code generator can handle, but may not directly or indirectly refer to the string type itself.
There are two translation schemes for Strings. The directive #STRING decide whether the string should be translated to linked list or to an array. For the #STRING directive please see Alternative Implementations of the String Generator - Directive #STRING.
Strings are translated to linked list containing one element for each element in the string value. Operations and component selection in string sorts are fully supported.
Powerset
Instantiations of the predefined generator powerset can be handled by the code generator with the following restriction: The component sort may be any sorts that the code generator can handle, but may not directly or indirectly refer to the powerset type itself.
There are two translation schemes for powersets. If the component sort fulfills the conditions for index sorts mentioned in the subsection about arrays above (Array), an array of 32-bit integers are used. Each bit will be used to represent a certain element whether it is a member of the powerset or not. If this is not the case, a linked list of all elements that are member of the set, is used to represent the powerset. All the available operations defined for Powersets in SDL are supported.
Bag
The Bag generator, which is introduced in SDL in Z.105, i.e. in the mapping from ASN.1 to SDL, is similar to powerset. However, it is possible to have several elements with the same value in a bag. A bag is always translated into a linked list, with one element for each value that is a member of the bag. Each element contains the value and the number of occurrences of this value.
Ref, Own, Oref
These generators represent pointers with different properties. They are all translated to pointers in C.
Syntypes
Syntypes may be defined for any sort that the code generator can handle, giving a new name for the sort and possibly a new default value for variables of the sort. Range conditions that restrict the allowed range of values are also allowed.
A syntype is translated to a type equal to the parent type using typedef. The check that a variable of a syntype is only assigned legal values is implemented in a test function that is generated together with the type definition. An attempt to assign an illegal value to such a variable will be reported as an SDL dynamic error. If the syntype is can be used as index sort in an array and the generated type in C would become an array, there will also be a test function that can be used to check that an index value is within its range in an array component selection.
SYNTYPE Syn = integerCONSTANTS 0:10ENDSYNTYPE;SYNTYPE Syn2 = integerCONSTANTS <0, =2, >=10ENDSYNTYPE;SYNTYPE Arr1 = ArrDEFAULT (. 2.0 .);ENDSYNTYPE;/* Arr defined above */typedef SDL_Integer Syn;typedef SDL_Integer Syn2;typedef Arr Arr1;Inheritance
A type that inherits another type is translated to a type equal to the parent type using a typedef.
Default Values
Default values are fully supported for all sorts that the code generator can handle, both if a default value is given in a sort definition and if an initial value is given in a variable definition (DCL).
Default values will also be assigned to all variables and components which do not have a default value specified in SDL. The reason for this is to avoid handling undefined variables in C, which might give serious problems and unexpected behavior of an executing program. The values selected by the code generator in such a case can be found below.
This is a deviation from SDL-92. It means that the generated program does not handle the value undefined for any type.
If no default value is given in the sort and no start value is given in the data definition (DCL) for a variable, the variable will be set to 0 by using a memset to 0.
Operators
In SDL-92, it is possible to define the behavior of operators in ADTs directly in SDL, using operator diagrams or operator implementations in SDL textual form. Such operators are translated to C by the Cadvanced/Cbasic SDL to C Compiler, and none of what is said below is valid for such an operator. It is also possible to specify that an operator is external. In this case the code generator assumes that a C function with the name used in SDL exists and translates calls to the external operator directly to calls to the C function.
A user defined operator in an SDL sort definition, which is not defined by an operator diagram, is translated to a C function which asks the user for the result of the operation. At a call of an operator, the user is supplied with information describing what the operator and the sort are called, and given information about the parameter values. You are then requested to answer with the result value. If you press <Return> at the prompt for the result, the default value of the actual result type is returned. If the operator does not have a result type, no question is asked.
Operator Op in sort S is called.Parameter 1: trueParameter 2: 10Enter value (integer) : 12assuming that newtype S contains an operator
Op: Boolean, Integer -> Integer;More about operator implementation, both parameter passing and how to include implementations written in C can be found in the next two sections.
Literals
In sorts that are translated to enumeration types in C, literals are obviously handled by the code generator. In sorts that are not enumeration types, literals are treated as operators without parameters and are handled in exactly the same way as user defined operators.
The Cadvanced/Cbasic SDL to C Compiler does not permit naming of literals using name class literals or character strings.
Axioms and Literal Mappings
Axioms and literal mapping are allowed by the code generator in sorts, but are completely ignored.
Parameter Passing to Operators
For performance reasons the data types in SDL have been divide in two groups, simple, small types that are passed as values and structured, larger types that are passed as references (addresses).
Table 1 : Types Passed as Addresses Table 2 : Types Passed as Values
For types represented as pointers (Charstring, including its syntypes, Ref, Own, ORef), the pointers, not the addresses of the pointers, are passed as parameters.
The parameter passing for operators implemented in C works as follows (for Cmicro the mechanism described below is also used for operator diagrams and procedures):
- Passed as a value in C if the type is in the list "Passed as value". This means that the parameter type in C is the same type as in SDL.
- Passed as an address in C if the type is in the list "Passed as address". This means that the C parameter is (SDL_type *) if the type in SDL is SDL_type.
Parameters are always passed as addresses, i.e the C parameter is (SDL_type *) if the type in SDL is SDL_type.
- If the result type is in the list "Passed as value", the C function result type will be the same as in SDL.
- If the result type is in the list "Passed as address", two things are changed. Firstly, the C result type will be (SDL_type *), i.e the result will be an address. Secondly, an extra parameter is inserted last in the C function. This parameter is also of type (SDL_type *) and is used as a location to store the result of the function. At an operator call, a "dummy" variable should be passed as the actual parameter. The C function can then use this to store the result of the operator and should return the variable again as result.
Assume that struct1 is a newtype struct in SDL.
operatorsX : integer, in/out integer -> integer;Y : struct1, in/out struct1 -> struct1;The C prototypes for these operators are:
SDL_Integer X (SDL_Integer, SDL_Integer *);struct1 * Y (struct1 *, struct1 *, struct1 *);The example implementations are:
SDL_Integer X(SDL_Integer Param1, SDL_Integer *Param2){*Param2 = *Param2+Param1;return *Param2;}struct1 * Y (struct1 *Param1,struct1 *Param2,struct1 *Result){/* implementation assuming struct1 to containtwo integers */(*Param2).comp1 = (*Param2).comp1+(*Param1).comp1;(*Param2).comp2 = (*Param2).comp2+(*Param1).comp2;*Result = *Param2;return Result;/* always return the last, extra, parameter */}
Implementation of User Defined Operators
Including Implementations of Operators
In a previous subsection, the default behavior of the Cadvanced/Cbasic SDL to C Compiler concerning operators (not defined in operator diagrams) and literals were described. If you do not specify otherwise interactive functions are generated, which, in each case, will ask you for the operator result or literal value. This is a fast way of getting started, but you will probably find it tedious in the long run, especially if you are using abstract data types extensively. To cope with this problem and to make it possible to generate applications, the code generator offers a possibility to include implementations written in C of the operator and literal functions. This possibility can be used as an alternative to operator diagrams or operators defined using SDL textual form, where the operator is defined directly in SDL.
When the choice between an implementation in SDL or in C is to be made there are a few things to consider:
- There is always problems when mixing languages, for example how are C names for SDL entities constructed.
- Checking of SDL is performed by the SDL Analyzer, which will find problems much earlier than the C compiler checking C code. Also pointing to the error will be more accurate in SDL.
- SDL implementations will be more portable and might benefit from future improvements in the SDL Compiler.
- The risk for backward compatibility problems in future releases of the SDL Suite will be less for an SDL implementation.
- However, a C implementation might be more efficient or you might already have a corresponding C function.
So it is not obvious if SDL or C implementations should be used. However, we recommend SDL if there are no specific reasons for using C. Note also that the SDL extension described in Grammar for the Algorithmic Extensions could be very useful when writing implementations in SDL.
It is possible to choose between two alternatives to implement operators and literal functions:
- Q (question)
This is the default value and specifies that the code generator should generate the interactive routines describe above.- B (body)
This specifies that the code generator should generate the heading of the operator and literal functions, while the user must supply the bodies of the functions.
The C functions are divided into a function heading (extern or static declaration) and a function body.
An example of a function heading (extern declaration) is:
Example 374 : Implementing an Operator
extern SDL_Integer Max(SDL_Integer Para1,SDL_Integer Para2);while the corresponding function body is:
SDL_Integer Max(SDL_Integer Para1,SDL_Integer Para2){if (Para1 > Para2)return Para1;return Para2;}The main reason for this division of functions into heading and body is the separate compilation scheme used in C. If, for example, an abstract data type is defined in a system and used in a process in the system, and the process is generated on a separate file, then there has to be a module interface file (a .h file) for the system containing the external interface (types, extern declarations of functions and so on). The interface file should then be included in the file generated for the process.
Even if separate compilation is not used, the division of functions into heading and body is useful. By having static declarations of the functions, the order in which functions must be defined is relaxed. If static declarations were not used, a function could only call the functions that are defined textually before the actual function.
To select the way the Cadvanced/Cbasic SDL to C Compiler should generate code for operators and literals, code generator directives are used. A code generator directive is an SDL comment with the first characters equal to `#', followed by a sequence of letters identifying the directive. In this case the letters are ADT (for Abstract Data Type) and OP (for operator). An ADT directive and a OP directive should thus look like:
/*#ADT */ /*#OP */The text is not case sensitive.
OP directives are recognized at two different positions in an abstract data type:
- Directly after the name of a literal
- Directly after the semicolon ending the definition of an operator.
ADT directives are recognized immediately before the reserved word ENDNEWTYPE (or ENDSYNTYPE).
Example 375 : Implementing an Operator (#ADT)
NEWTYPE Str STRUCTa integer;b Boolean;c real;ADDINGLITERALSLit1 /*#OP */,Lit2 /*#OP */;OPERATORSOp1 :Str,integer -> Str; /*#OP */Op2 :Str,Boolean -> Str; /*#OP *//*#ADT */ENDNEWTYPE;At each of the positions after a literal name or operator definition, there is a possibility to specify how this literal or operator should be implemented. In the directive immediately before ENDNEWTYPE the default implementation technique can be given. When the code generator determines how to generate code for a literal or an operator, it first looks for an OP directive after the literal name or operator definition. If no such directive is found it looks for a directive immediately before ENDNEWTYPE. If no ADT directive is found here, the generation technique Q (question) is assumed. For Cmicro, Q is not used, so the default is B.
An OP or ADT directive specifying a generation technique should have the following structure:
/*#OP (B) */ /*#ADT (B) */The letter between the parentheses should be either Q (question) or B (body). The interpretation of Q and B was explained earlier. If B has been specified for any operators or literals, then the C code for these functions must be supplied by the user. This code should be placed in the #BODY section in the ADT directive, according to the following example:
Example 376 : Implementing an Operator (#ADT)
/*#ADT (B)#BODYC code, representing bodies of functions*/The section name, i.e #BODY, must be given on a line of its own and must have the # character in the first position of the line. Upper case and lower case letters are as usual considered to be equal. If the section is empty, the section name can also be removed.
Unfortunately it is not possible to have C comments within the code that is included in a #ADT directive, as SDL and C use the same symbols for start and end of comments. If a C comment is included, the SDL Analyzer will consider the end of the C comment as the end of the SDL comment. Instead a C macro called COMMENT can be used according to the examples below. Note that there might be some compiler dependent restriction of the character set allowed within the COMMENT macro. For example, the character `;' might not be allowed.
COMMENT(This is a comment)COMMENT(These comments may not contain commas \and should have a backslash at each \line break)COMMENT((By having double parenthesis, any textcan be entered into the comments. Somecompilers might not allow everything.))The function headings representing literals and operators are determined by their corresponding definition in SDL. The number of parameters, their types, the result type of the function and function name are all defined in SDL. In the example above, where the struct Str is defined, there are two literals (Lit1 and Lit2) and two operators
(Op1: Str, integer -> Str; and Op2: Str, Boolean -> Str;). The type Str will be passed as an address, so the parameter passing rules described previously have to be applied. The function heading of the corresponding C functions should be:Example 378 : Implementing an Operator
extern Str* Lit1 (Str*);extern Str* Lit2 (Str*);extern Str* Op1 (Str*, SDL_Integer, Str*);extern Str* Op2 (Str*, SDL_Boolean, Str*);The function bodies, which should be supplied by the user if B is specified in the OP or ADT directive, are ordinary C functions.
Example 379 : Implementing an Operator
Str* Lit1 (Str* Result){Result->a = 2;Result->b = false;Result->c = 10.0;return Result;}Str* Op1 (Str* P1, SDL_Integer P2, Str* Result),{*Result = *P1;Result->a = P1->a + P2;return Result;}Before it is possible to give a complete example of an abstract data type with implementation of its operators supplied as C functions, it is necessary to look at the problem of names. When a name of some object in SDL is translated to C, a suitable sequence of characters, a prefix, is added to the SDL name, to make the name unique in the C program, see also Names and Prefixes in Generated Code. This strategy is selected in the Cadvanced/Cbasic SDL to C Compiler to avoid name conflicts in the generated code, but it makes it also impossible to predict the full name of, for example, a type or a function, in the generated program. To handle this problem the user can tell the code generator to translate a name in the C code in the same way as SDL names are otherwise translated. This is specified by enclosing the SDL name between `#(' and `)' in the C code. The two functions in the previous example and their headings would then become:
Example 380 : Including SDL name in C Code
extern #(Str)* #(Lit1) (#(Str)*);extern #(Str)* #(Op1) (#(Str)*, SDL_Integer,#(Str)*);#(Str)* #(Lit1) (#(Str)* Result){Result->a = 2;Result->b = false;Result->c = 10.0;return Result;}#(Str)* Op1 (#(Str)* P1, SDL_Integer P2,#(Str)* Result),{*Result = *P1;Result->a = P1->a + P2;return Result;}This facility to access an SDL name in C code is described in more detail in the section Accessing SDL Names in C Code - Directive #SDL. A few observations concerning the example above might be appropriate:
- The predefined sorts in SDL, that is for example integer, natural, Boolean have the names SDL_Integer, SDL_Natural, SDL_Boolean, and so on in the generated code. These types should not be enclosed between `#(' and `)'.
- The component names of a struct are unchanged in the struct implementation in C, which means that struct components should not be enclosed between `#(' and `)' either.
Two Examples of ADTs
We now give two complete examples of abstract data types.
NEWTYPE Str STRUCTa Integer;b Boolean;c Real;ADDING LITERALSLit1;OPERATORSOp1 : Str, Integer -> Str;Op2 : Str, Boolean -> Str;/*#ADT (B)#BODY#(Str)* #(Lit1) (#(Str)* Result){Result->a = 2;Result->b = SDL_False;Result->c = 10.0;return Result;}#(Str)* #(Op1) (#(Str)* P1, SDL_Integer P2,#(Str)* Result){*Result = *P1;Result->a = P1->a + P2;return Result;}#(Str)* #(Op2) (#(Str)* P1, SDL_Boolean P2,#(Str)* Result){if (P2)*Result = *P1;else(void)#(Lit1)(Result);return Result;}*/ENDNEWTYPE;The example above should be compared with the same example written in SDL. Note that the literal in the previous example is replaced with an operator without parameters. The algorithmic extensions described in Grammar for the Algorithmic Extensions is also used as they provide a powerful way to write textual algorithms.
Example 382 : ADT Example in pure SDL
NEWTYPE Str STRUCTa Integer;b Boolean;c Real;OPERATORSLit1 : -> Str;Op1 : Str, Integer -> Str;Op2 : Str, Boolean -> Str;OPERATOR Lit1 RETURNS Str{RETURN (. 2, false, 10.0 .);}OPERATOR Op1 FPAR P1 Str, P2 Integer RETURNS Str{DCL Result Str;Result := P1;Result!a := P1!a + P2;RETURN Result;}OPERATOR Op2 FPAR P1 Str, P2 Boolean RETURNS Str{IF (P2)RETURN P1;RETURN Lit1;}ENDNEWTYPE;SYNTYPE Index = Integer CONSTANTS 1:10ENDSYNTYPE,NEWTYPE A Array(Index, Integer)ADDING LITERALSZero /*#OP (B) */;OPERATORSAdd : A, A -> A; /*#OP (B) */Sum : A -> Integer;/*#ADT()#BODY#(A)* #(Zero) (#(A)* Result){SDL_Integer i = 0;GenericMakeArray(Result,(tSDLTypeInfo *)&ySDL_#(A), &i);return Result;}#(A)* #(Add) (#(A)* P1, #(A)* P2, #(A)* Result){int I;for (I = 1; I<=10; I++)Result->A[I] = P1->A[I] + P2->A[I];return Result;}*/ENDNEWTYPE;Note that no body is supplied for the operator Sum as the default implementation strategy for operators, which should be used for Sum, is Q (question). The GenericMakeArray function used to implement the literal is a generic function that constructs array values. The details for this function will be described later in this section.
For more information about the functions and types (supplied by the runtime library in the Cadvanced/Cbasic SDL to C Compiler and contained in generated code) that can be useful when implementing operators in C, see SDL Predefined Types, and last in More about Abstract Data Types.
Error Situations in Operators
In the C function used to implement operators (and literals) it is possible to define error situations and handle them as ordinary SDL run-time errors. The C library function xSDLOpError, with the following prototype:
extern void xSDLOpError(char *OpName,char *ErrText )Example 384 : Error Handler in Operator
if ( strlen(C) <= 1 ) {#ifdef XECSOPxSDLOpError("First in sort Charstring","Charstring length is zero." );#endifreturn SDL_NUL;} elsereturn C[1];This is a simplified version of the test in the function for the operator First in the sort Charstring. Here the error situation is when we try to access the first character in a charstring of length 0. In this case the xSDLOpError is called and a default value is returned (NUL). By including the xSDLOpError call between #ifdef XECSOP - #endif the function is only called to report the error if error checks are turned on. The first parameter to xSDLOpError should identify the operator and the sort, while the second parameter should describe the error.
Handling of the Charstring Sort
The SDL sort Charstring is implemented as char * in C.
This means that the value NUL (ASCII character 0) cannot be part of a Charstring, as this value is used as string terminator in C (this is checked by the library functions for Charstring).
The code generator and the library functions for the Charstring operators use the first character (index 0) in the C string to indicate the status of the string. If the first character is:
- 'V'
the string is assigned to an SDL variable and may not be changed in any way.- 'L'
the string is a C char * literal, and may of course not be changed.- 'T'
the string is a temporary result from a function returning a Charstring. This memory should either be assigned to an SDL variable or returned to the pool of free memory.All the library functions for Charstrings handle memory in an appropriate way. A user only has to take the extra character in to account, when Charstrings are handled in C. Any Charstring function parameters having a `T' as first character must be handled according to the discussion above. A function that returns a Charstring and that creates new temporary memory to store the result, should assign the value `T' to the first character in the Charstring.
As pointers and dynamic memory are used to implement Charstrings, it is necessary to be careful when Charstrings are handled in C code, which we show in two examples.
Example 385 : Equal Test on Charstring Sort
If the C operator == is used to check if two charstrings are equal, then the actual test that is performed is to see if the two pointer values to the data areas representing the characters in the string are equal.
To check if the characters in the charstrings are equal the equal function should be used:
yEqF_SDL_CharstringExample 386 : Assignment on Charstring Sort
If the C assignment operator, =, is used to assign the value of one charstring variable (C1) to another charstring variable (C2), then two things will go wrong:
- The memory used to represent the old value of C1 is lost and can never be reused.
- C1 and C2 now refer to the same memory area, which means that if one of the variables is changed the other will also be changed. This leads to unpredictable behavior of the program.
The correct way to handle assignment of charstrings is to use the routine:
yAssF_SDL_CharstringThe problems mentioned above can of course also occur if a struct or array containing charstring components (or subcomponents) is handled carelessly. It is, for example, necessary to use the generic equal and assign functions to perform equal test and assignment.
To avoid problems one should be aware that Charstring is implemented as char * in C and take the consequences thereof. There are a number of help functions (that implement the operators for the Charstring sort) supplied in the runtime library that might be helpful when handling Charstrings. See SDL Predefined Types).
Other Types Containing Pointers
The principal discussion about Charstrings in the previous section is also relevant for all other types containing pointers. Such types are:
All these types contain a boolean component, IsAssigned, that gives the status of the data area. IsAssigned serves the same purpose as the first extra character in a Charstring and has to be treated in a similar way.
- IsAssigned equal to false means that this data area is a temporary result from a function returning the data type. This memory should either be assigned to an SDL variable or returned to the pool of free memory.
- IsAssigned equal to true means that this value is assigned to a variable and may not be changed in any way. It can also mean that the value is part of (i.e. is assigned to) a larger data structure.
External Properties
As an alternative to the #ADT directive, which is a comment, the external properties clause in a newtype can be used as container for this information. See the following example:
Example 387 : External Properties in a Newtype
NEWTYPE Str STRUCTa integer;b Boolean;c real;ADDING LITERALSLit;OPERATORSOp1 : Str, integer -> Str;Op2 : Str, Boolean -> Str;ALTERNATIVE C;#ADT (B)#BODYsome appropriate C codeENDALTERNATIVE;ENDNEWTYPE;The #ADT directive, without the /* */ can be placed between ALTERNATIVE C; and ENDALTERNATIVE.
According to the syntax of SDL, if you have an external properties clause (i.e. alternative - endalternative), you cannot, in the same newtype, have operator diagrams, axioms, or literal mappings.
More about Operators
For an operator in an abstract data type, not only B (body) or Q (question) may be specified. The following choices are available:
- Q (question)
This is the default value and specifies that the code generator should generate the interactive routines describe above.- B (body)
This specifies that the code generator should generate the heading of the operator or literal function, while the user should supply the body of the function.- H (heading)
This specifies that the code generator should neither generate the heading nor the body of the operator or literal function. The user is assumed to supply the necessary code.- S (standard)
This is used to indicate that a standard function or operator is available in the target language, which should be used as implementation of the SDL operator (literal). No function heading or function body is generated. In expressions where such an operator is used, no prefix is added to the SDL name during the translation, but the SDL name is used as it is (if no #NAME directive is present).- P (prefix) or
I (infix)
where P is the default value. These letters are used to indicate if the operator should be used as a function or an operator:
As C does not include the possibility to have user defined operators, I (infix) is only adequate together with S (standard).
For each operator one of the letters B, Q, H, S and one of the letters P, I should be supplied, either in a #OP directive, or in a #ADT directive, or as the defaults Q and P; for literals P and I have no meaning.
The purpose of S is straight forward and easy to understand, but H might require some explanation. H means that the code generator will not generate any code for the operator, which leaves the user with a number of possibilities:
- By not including any code for an operator, the user may skip the code for an unused operator.
- There might already exist external declarations for a number of operators in a .h file that should be used instead of the generated headings.
Example 388 : Using S (Standard Function or Operator)
Example of usage of S (standard)
"+" : integer, real -> real; /*#OP (SI) */sin : real -> real; /*#OP (SP) */An SDL expression using these operators:
sin(a + 7.0) will be translated to: sin(zh723_a + 7.0)
These examples show how standard functions in the target language can be directly utilized in abstract data types. In C, it is often easiest to use #OP(HP) for such special cases, and implement the operator in the #HEADING section as a C macro transforming the call to the appropriate syntax.
Generic Functions
Type Info Nodes
A generic function can perform a certain task for several different types. To be able to write generic functions, type-specific information for the types must be made available. This type of information could be, for instance, size of the type, component types for structured types and component offsets. This information is provided by the type info nodes.
A type info node is a struct that contains information that defines the type. Each type has a corresponding type info node. Each type info node contains two sections. The first section contains a sequence of general components that is identical for all type info nodes. The second section is an individual type-specific sequence of components that defines each unique type.
Every newtype or syntype introduced in SDL will be described by a type info node in the generated C code. For the predefined data types the following type info nodes can be found in sctpred.h and sctpred.c:
extern tSDLTypeInfo ySDL_SDL_Integer;extern tSDLTypeInfo ySDL_SDL_Real;extern tSDLTypeInfo ySDL_SDL_Natural;extern tSDLTypeInfo ySDL_SDL_Boolean;extern tSDLTypeInfo ySDL_SDL_Character;extern tSDLTypeInfo ySDL_SDL_Time;extern tSDLTypeInfo ySDL_SDL_Duration;extern tSDLTypeInfo ySDL_SDL_PId;extern tSDLTypeInfo ySDL_SDL_Charstring;extern tSDLTypeInfo ySDL_SDL_Bit;extern tSDLTypeInfo ySDL_SDL_Bit_String;extern tSDLTypeInfo ySDL_SDL_Octet;extern tSDLTypeInfo ySDL_SDL_Octet_String;extern tSDLTypeInfo ySDL_SDL_IA5String;extern tSDLTypeInfo ySDL_SDL_NumericString;extern tSDLTypeInfo ySDL_SDL_PrintableString;extern tSDLTypeInfo ySDL_SDL_VisibleString;extern tSDLTypeInfo ySDL_SDL_Null;extern tSDLGenListInfo ySDL_SDL_Object_Identifier;For a user-defined type the type info node will have the name
ySDL_#(TypeName)Generic Assignment Functions
Each type in SDL has access to an assignment macro yAssF_typename. Examples for type Boolean and for a user-defined type A:
#define yAssF_SDL_Boolean(V,E,A) (V = E)#define yAssF_A(V,E,A) yAss_A(&(V),E,A)#define yAss_A(Addr,Expr,AssName) \ (void)GenericAssignSort(Addr,Expr,AssName,
(tSDLTypeInfo *)&ySDL_A)This macro is used in the generated code (and in the kernel) at each location where an assignment should take place. The three macro parameters are:
- V: the variable on the left hand side
- E: the expression on the right hand side
- A: an integer giving the properties of the assignment
This macro will either become an assignment statement in C or a call of an assignment function. An assignment statement will be used if assignment is allowed according to C for the current type and if it has the correct semantics comparing with assignment in SDL.
If assignment is not possible to use, the assign macro will become a call to an assignment function. The basic generic assignment function can be found in sctpred.c and sctpred.h:
extern void * GenericAssignSort(void *, void *,int, tSDLTypeInfo *);
- The first parameter is the address of the variable on the left hand side.
- The second parameter is the address of the expression on the right hand side.
- The third parameter is the properties of the assignment
- The fourth parameter is the type info node for the actual type.
GenericAssignSort returns the address passed as the first parameter.
The GenericAssignSort function performs three tasks:
- The old value on the left hand side variable is released, if that is specified in properties of the assignment and if the value contains any pointers.
- The value is copied from the expression to the variable. If possible this is performed by the function memcpy, otherwise special code depending on the kind of type is executed.
- The IsAssigned flags are set up for the variable according to the properties of the assignment.
Special treatment of Charstring and instantiations of the Own generator has made it necessary to introduce specific wrapper functions that in their turn call GenericAssignSort for these types:
extern void xAss_SDL_Charstring (SDL_Charstring *,SDL_Charstring, int);extern void * GenOwn_Assign (void *, void *, int,tSDLTypeInfo *);An GenericAssignSort function must consider the following questions in order to handle the objects correctly.
How should one copy the object?
This is very important because performing the wrong action will lead to memory leaks or access errors. Three different possibilities exist:
- AC: always copy the referenced object.
- AR: always copy the pointer, i.e reusing the referenced object.
- MR: copy pointer if the object is temporary or copy object if not temporary.
What should be the status of the new object?
This is a preparation for the next operation on this object so the correct decision can be made according to the first question. Two different possibilities exists:
- ASS: an object should become assigned if it is assigned to a variable and needs to be copied in future assignments, i.e corresponds to the values `V' and `L' for the first character in a C- string representing the Charstring sort. A typical case is a normal assignment statement in SDL.
- TMP: an object should become temporary if it is not assigned to any persistent variable and therefore should not be copied in subsequent assignments, i.e corresponds to the value `T' for the first character in a C-string representing the Charstring sort. A typical case is a result value from an operator.
What should be done with the old value referenced by the left hand side variable?
Normally free should be performed on the value, as otherwise there would be a memory leak. However, when initializing a variable, no free ought to be performed, as free might be called on a random address. Two different possibilities exists:
The third assignment property parameter in the GenericAssignSort function should be given a value according to the ideas given above, preferably using the macros indicated.
#define XASS_AC_ASS_FR (int)25#define XASS_MR_ASS_FR (int)26#define XASS_AR_ASS_FR (int)28#define XASS_AC_TMP_FR (int)17#define XASS_MR_TMP_FR (int)18#define XASS_AR_TMP_FR (int)20#define XASS_AC_ASS_NF (int)9#define XASS_MR_ASS_NF (int)10#define XASS_AR_ASS_NF (int)12#define XASS_AC_TMP_NF (int)1#define XASS_MR_TMP_NF (int)2#define XASS_AR_TMP_NF (int)4The macro names above are all of the form XASS_1_2_3, where the abbreviations placed at 1, 2, and 3 should be read:
- 1 = AC: always copy
- 1 = MR: may reuse (take pointer if temporary object)
- 1 = AR: always reuse (take pointer)
- 2 = ASS: new object assigned to "variable"
- 2 = TMP: new object temporary
- 3 = FR: call free for old value referred to by variable
- 3 = NF: do not call free for old value
The distinction between all these assignment possibility is only of interest when handling types using or containing pointers.
Generic Equal Functions
Each type in SDL has access to an equal macro yEqF_typename and an not equal macro yNEqF_typename. Examples for type Boolean and for a user-defined type A:
#define yEqF_SDL_Boolean(E1,E2) ((E1) == (E2))#define yNEqF_SDL_Boolean(E1,E2) ((E1) != (E2))#define yEqF_z3_A(Expr1,Expr2) yEq_z3_A(Expr1,Expr2)#define yNEqF_z3_A(Expr1,Expr2) ( ! yEq_z3_A(Expr1,Expr2) )#define yEq_z3_A(Expr1,Expr2) \GenericEqualSort((void *)Expr1,(void *)Expr2, \(tSDLTypeInfo *)&ySDL_z3_A)These macros are used in the generated code (and in the kernel) at each location where equality tests are needed. The parameters to the equal and not equal macro are the two expressions that should be tested.
If C equal or not equal are not possible to use, the equal macros will become calls to an equal function. The basic generic equal function can be found in sctpred.h and sctpred.h:
extern SDL_Boolean GenericEqualSort(void *, void *,tSDLTypeInfo *);
- the first two parameters are the addresses to the two expressions to be tested
- the third parameter is the type info node for the actual type.
Special treatment of Charstring and instantiations of the Own generator has made it necessary to introduce specific wrapper functions that in turn calls GenericEqualSort for these types:
extern SDL_Boolean xEq_SDL_Charstring(SDL_Charstring, SDL_Charstring);extern SDL_Boolean GenOwn_Equal (void *, void *,tSDLTypeInfo *);Generic Free Functions
Each type in SDL that is implemented as a pointer, or that contains a pointer that references to memory that is automatically handled (in principle all pointers except Ref pointers), has access to a corresponding yFree_typename function or macro. In the generic function model, this is always a macro.
#define yFree_SDL_Charstring(P) xFree_SDL_Charstring(P)#define xFree_SDL_Charstring(P) \GenericFreeSort(P,(tSDLTypeInfo *)&ySDL_SDL_Charstring)#define yFree_A(P) \GenericFreeSort(P,(tSDLTypeInfo *)&ySDL_A)The yFree macro will always be translated to a call to the function GenericFreeSort.
extern void GenericFreeSort (void **, tSDLTypeInfo *);This function takes the address of a variable and a type info node and releases the dynamic memory used by this value contained in the variable.
Generic Make Functions
There are four generic functions constructing values of structured types:
extern void * GenericMakeStruct (void *, tSDLTypeInfo *, ...);extern void * GenericMakeChoice (void *, tSDLTypeInfo *,int, void *);extern void * GenericMakeOwnRef (tSDLTypeInfo *, void *);extern void * GenericMakeArray (void *, tSDLTypeInfo *,void *);GenericMakeStruct: According to SDL, the Make operator is only available for the struct type. However, in the SDL Suite the Make operator, and thus the GenericMakeStruct function, is also available for the Object_identifier type and the instantiations of the generators string, powerset, and bag.
- The void * parameter is the address of a variable where the result should be placed. This value is also returned.
- The tSDLTypeInfo * parameter is the address to the type info node for the type to be created.
- "..." denotes a list of addresses to the values for the components in the struct. All parameters must be passed as addresses (void *) regardless if the component type should be passed as an address or as a value. The only exceptions are the types represented as pointers themselves (Charstring, Ref, Own, ORef, and syntypes of these types), where the pointers are passed, not the addresses of the pointers. In case of an optional field or a field with an initial value, a `0' or `1' is passed to indicate if a value for the component is present or not. If `1' is passed the value follows as next parameter. If `0' is passed no value is present in the actual parameter list.
GenericMakeChoice: This function is used for choice types.
- The first void * parameter is the address of a variable where the result should be placed. This value is also returned.
- The tSDLTypeInfo * parameter is the address to the type info node for the type to be created.
- The int parameter decides which choice component that is present.
- The last void * parameter is the address of the value.
GenericMakeOwnRef: This function is used for instantiations of generators Own and Ref.
- The tSDLTypeInfo * parameter is the address to the type info node for the type to be created.
- The void * parameter is the address to the value that should be assigned to the memory allocated by this function.
GenericMakeArray: This function is used for instantiations of the generators Array, Carray, and GArray.
- The first void * parameter is the address of a variable where the result should be placed. This value is also returned.
- The tSDLTypeInfo * parameter is the address to the type info node for the type to be created.
- The last void * parameter is the address to the value that should be assigned to all components of the array.
Generic Function for Operators in Pre-defined Generators
The generic function for the operators in the pre-defined generators follow the general rules for operators with a few exceptions:
- a type info node is needed as a parameter, as the C function can handle all instantiations of a certain generator.
- parameters of generator parameter types (component and index types for example) must in many cases be passed as addresses, as the properties of these types are not known.
General array
extern void * GenGArray_Extract (xGArray_Type *, void *,tSDLGArrayInfo *);extern void * GenGArray_Modify (xGArray_Type *, void *,tSDLGArrayInfo *);
- Parameter 1: The array
- Parameter 2: The index value passed as an address
- Parameter 3: The type info node
- Result: The address of the component
Powerset
Generic functions available for powersets with a simple component type. The powerset is represented a sequences of bits (unsigned char[Appropriate_Length]).
#define GenPow_Empty(SDLInfo,Result) \memset((void *)Result,0,(SDLInfo)->SortSize)extern SDL_Boolean GenPow_In (int, xPowerset_Type *,tSDLPowersetInfo *);extern void * GenPow_Incl (int, xPowerset_Type *,tSDLPowersetInfo *, xPowerset_Type *);extern void * GenPow_Del (int, xPowerset_Type *,tSDLPowersetInfo *, xPowerset_Type *);extern void GenPow_Incl2 (int, xPowerset_Type *,tSDLPowersetInfo *);extern void GenPow_Del2 (int, xPowerset_Type *,tSDLPowersetInfo *);extern SDL_Boolean GenPow_LT (xPowerset_Type *,xPowerset_Type *, tSDLPowersetInfo *);extern SDL_Boolean GenPow_LE (xPowerset_Type *,xPowerset_Type *, tSDLPowersetInfo *);extern void * GenPow_And (xPowerset_Type *, xPowerset_Type *,tSDLPowersetInfo *, xPowerset_Type *);extern void * GenPow_Or (xPowerset_Type *, xPowerset_Type *,tSDLPowersetInfo *, xPowerset_Type *);extern SDL_Integer GenPow_Length (xPowerset_Type *,tSDLPowersetInfo *);extern int GenPow_Take (xPowerset_Type *, tSDLPowersetInfo *);extern int GenPow_Take2 (xPowerset_Type *, SDL_Integer,tSDLPowersetInfo *);
- Parameter of type int in GenPow_In, GenPow_Incl, GenPow_Del, GenPow_Incl2, GenPow_Del2: A component value.
- Result of type int in GenPow_Take, GenPow_Take2: A component value.
- Parameters of type tSDLPowersetInfo *: The type info node.
- Parameters of type xPowerset_Type * after the type info node: The address where the result should be stored. This address is returned by the function.
- Other xPowerset_Type * parameters: Powerset in parameters.
Bag and General Powerset
The following generic functions are available for bags and powersets with complex component type. These types are represented as linked lists in C.
#define GenBag_Empty(SDLInfo,Result) \memset((void *)Result,0,(SDLInfo)->SortSize)extern void * GenBag_Makebag (void *, tSDLGenListInfo *,xBag_Type *);extern SDL_Boolean GenBag_In (void *, xBag_Type *,tSDLGenListInfo *);extern void * GenBag_Incl (void *, xBag_Type *,tSDLGenListInfo *, xBag_Type *);extern void * GenBag_Del (void *, xBag_Type *,tSDLGenListInfo *, xBag_Type *);extern void GenBag_Incl2 (void *, xBag_Type *,tSDLGenListInfo *);extern void GenBag_Del2 (void *, xBag_Type *,tSDLGenListInfo *);extern SDL_Boolean GenBag_LT (xBag_Type *, xBag_Type *,tSDLGenListInfo *);extern SDL_Boolean GenBag_LE (xBag_Type *, xBag_Type *,tSDLGenListInfo *);extern void * GenBag_And (xBag_Type *, xBag_Type *,tSDLGenListInfo *, xBag_Type *);extern void * GenBag_Or(xBag_Type *, xBag_Type *,tSDLGenListInfo *, xBag_Type *);extern SDL_Integer GenBag_Length (xBag_Type *,tSDLGenListInfo *);extern void * GenBag_Take (xBag_Type *, tSDLGenListInfo *,void *);extern void * GenBag_Take2 (xBag_Type *, SDL_Integer,tSDLGenListInfo *, void *);
- Parameter of type int in GenBag_Makebag, GenBag_In, GenBag_Incl, GenBag_Del, GenBag_Incl2, GenBag_Del2: The address of the component value.
- Result of type int in GenBag_Take, GenBag_Take2: The address of the component value.
- Parameters of type tSDLGenListInfo *: The type info node.
- Parameters of type xBag_Type * after the type info node: The address where the result should be stored. This address is returned by the function.
- Parameters of type void * after the type info node: The address where the result should be stored. This address is returned by the function.
- Other xBag_Type * parameters: Bag/Powerset in parameters.
String
The following Generic functions are available for String instantiations. A String is implemented as a linked list.
#define GenString_Emptystring(SDLInfo,Result) \memset((void *)Result,0,(SDLInfo)->SortSize)extern void * GenString_MkString (void *, tSDLGenListInfo *,xString_Type *);extern SDL_Integer GenString_Length (xString_Type *,tSDLGenListInfo *);extern void * GenString_First (xString_Type *,tSDLGenListInfo *, void *);extern void * GenString_Last (xString_Type *,tSDLGenListInfo *, void *);extern void * GenString_Concat (xString_Type *,xString_Type *, tSDLGenListInfo *, xString_Type *);extern void * GenString_SubString (xString_Type *,SDL_Integer, SDL_Integer, tSDLGenListInfo *,xString_Type *);extern void GenString_Append (xString_Type *, void *,tSDLGenListInfo *);extern void * GenString_Extract (xString_Type *, SDL_Integer,tSDLGenListInfo *);
- Parameter of type void * in GenString_MkString, GenString_Append: Address of component value.
- Parameter of type void * or xString_Type * after type info node: The address where the result should be stored. This address is returned by the function.
- Parameters of type tSDLGenListInfo *: The type info node.
- Other parameters: According to SDL definition of parameters.
Limited String
Generic functions available for limited strings, i.e. strings with #STRING directive giving a max size of the string. These strings are implemented as an array in C.
#define GenLString_Emptystring(SDLInfo,Result) \memset((void *)Result,0,(SDLInfo)->SortSize)extern void * GenLString_MkString (void *, tSDLLStringInfo *,xLString_Type *);#define GenLString_Length(ST,SDLInfo) (ST)->Lengthextern void * GenLString_First (xLString_Type *,tSDLLStringInfo *, void *);extern void * GenLString_Last (xLString_Type *,tSDLLStringInfo *, void *);extern void * GenLString_Concat (xLString_Type *,xLString_Type *, tSDLLStringInfo *, xLString_Type *);extern void * GenLString_SubString (xLString_Type *,SDL_Integer, SDL_Integer, tSDLLStringInfo *,xLString_Type *);extern void GenLString_Append (xLString_Type *, void *,tSDLLStringInfo *);extern void * GenLString_Extract (xLString_Type *,SDL_Integer, tSDLLStringInfo *);
- Parameter of type void * in GenLString_MkString, GenString_Append: Address of component value.
- Parameter of type void * or xLString_Type * after type info node: The address where the result should be stored. This address is returned by the function.
- Parameters of type tSDLLStringInfo *: The type info node.
- Other parameters: According to SDL definition of parameters.
More about Abstract Data Types
Including Type Definitions
In this subsection, the inclusion of a type definition in the target language for an abstract data type will be described. When this facility is used, it is necessary to specify how to perform assignment, test for equal, assign default values, and so on, as it is not possible to generate when the type definition is not known (not generated). All this information is given in the #ADT directive, which has the following structure:
/*#ADT(T(x) A(x) E(x) F(x) K(x) X(x) M(x) W(x) R(x)xy 'file name')#TYPEC code#HEADINGC code#BODYC code*/where each x on the first line should be replaced by one of the characters B, H, Q, S, or G. Replace y by P or I. The interpretation of these characters is similar to the their interpretation for operators.
The reason why G (generate) is not allowed for operators or literals is of course that it would mean to generate the implementation of the operators from the axioms, which is, at least in the general case, an impossible task. For an operator defined in an operator diagram, G is assumed independently of what the user specifies.
The specifications, given in ADT directives, of how to generate code for type definition, assignment, test for equal, default values, and free function should be interpreted according to the table below.
Type Definition
First the actual type definition. The entry - should be interpreted as if no specification is given for T.
Do not generate type definition. Assume the type should be "passed as value" to operators
Do not generate type definition. Assume the type should be "passed as address" to operators
Assignment
It is possible to select how assignments should be performed for values of the type. Note that all generated assignments will be of the form:
yAssF_#(SortName)(....);The yAssF_#(SortName) is a macro either implemented as assignment or as a call to the yAss_#(SortName) function (if such function is to be used), i.e as:
#define yAssF_#(S)(V,E,A) V = E#define yAssF_#(S)(V,E,A) yAss_#(S)(&(V),E,A)If you define your own assign function, it must be implemented as a function, as the address of the function will be stored (in the type info node for the data type) so GenericAssignSort can call it to handle subcomponents of this type. An assign function has the following heading:
void yAss_#(SortName)(#(SortName) *yVar,#(SortName) *yExpr,int AssType)It should assign the value passed as second parameter to the variable passed as first parameter. If the type that is to be assigned contains any pointers the assign function is a bit complicated to write in order to avoid access errors and memory leaks. See the discussion about the AssType parameter to GenericAssignSort in Generic Assignment Functions.
There is one special case when two assign functions are needed. When the user has decided to write his own assign function and at the same time the type should be passed as value, a second assign function should be added:
void yAss2_#(Sortname)(#(Sortname) *yVar,#(Sortname) yExpr,int AssType)The difference is that the yAss2 function takes a value as second parameter, not an address. In generated code the yAss2 function will be used for direct assignment of this type (assignment statements, parameter assignments in input, output, set and so on). The yAss function will be called from GenericAssignSort to handle assignments of this type, when it is a component of a larger structured type (for example component in struct, array or string).
Equal Test
It is possible to select how test for equality should be performed for values of the type. Note that all generated equal tests will be of the form:
yEqF_#(SortName)(....);The yEqF_#(SortName) is a macro either implemented as C equal or as a call to the yEq_#(SortName) function (if such function is to be used), i.e as:
#define yEqF_#(S)(E1,E2) E1 == E2#define yEqF_#(S)(E1,E2) yEq_#(S)(E1,E2)The /= operator is represented by the macro
#define yNEqF_#(S)(E1,E2) (! yEqF_#(S)(E1,E2) ).
Use and generate an equal function that asks for the result of the test (same as Q for operators).
If you define your own equal function, it must be implemented as a function, as the address of the function will be stored (in the type info node for the data type) so GenericEqualSort can call it to handle subcomponents of this type. An equal function has the following heading:
SDL_Boolean yEq_#(SortName)(#(SortName) *yExpr1,#(SortName) *yExpr2);It should return true or false depending on if the two values passed as parameters are equal or not. If the parameters contain pointers it might be necessary to free these values, please see the discussion on general parameters to operators inOther Types Containing Pointers.
Just as for assignment there is a special case when two equal functions are needed. If the user has decided to write his own equal function and at the same time the type should be passed as value, a second equal function should be added:
SDL_Boolean yEq2_#(Sortname)(#(Sortname) yExpr1,#(Sortname) yExpr2)The difference is that the parameters are passed as values instead of as addresses. The yEq function will be called from the GenericEqualSort function to handle when this type is a component in a structured type, while the yEq2 function is used for direct equal test between values of this type.
Free of Dynamic Memory
This section describes how dynamic memory (if used for the type) will be released for reuse when it is no longer needed.
Generate heading, but no body of the free function yFree_#(SortName).
If you define your own free function, it must be implemented as a function, as the address of the function will be stored (in the type info node for the data type) so GenericFreeSort can call it to handle subcomponents of this type. A free function should have the following prototype
void yFree_#(SortName) (void **yVar)The function should take the address to a pointer, return the allocated memory to the pool of available memory and assign 0 to the pointer.
Extract! and Modify!
This entry specifies how component selection (struct components, array components for example) should be performed. In SDL a component can be selected in two ways:
Variable ! ComponentVariable (Index)An Extract operation can be generated in four ways:
Variable.Component used for struct and #UNIONCVariable.U.Component used for #UNION and choiceVariable.A(Index) used for arrayyExtr_SortName(Variable,Expr)The last version, the Extract function, is used for all other cases.
A Modify operation can in the same way be generated in four ways:
Variable.Component used for struct and #UNIONCVariable.U.Component used for #UNION and choiceVariable.A(Index) used for array(* yAddr_SortName((&Variable), Expr))The last version, the Addr function, is used for all other cases.
Read and Write Function
The write function is used by the monitor system to write values of the type.
The read function is used by the monitor system to read values of the type.
In order to examine variable values for variables that are of a sort that is implemented in C, i.e. has #ADT(T) in its ADT directive, it is necessary to implement a write function. Otherwise the value can only be presented as a HEX string. Note that the run-time kernel can automatically handle all SDL sorts for which the code generator generates the C type definition. A write function should look like:
extern char * yWri_SortName (void * Value)Given the address of a value of the type SortName, this function should return a char *, i.e. a character string, containing the value represented in a printable form. This character string is the string that will be printed by the monitor, when it needs to print a value of this type. To implement the write function it is not uncommon that a static char array is needed.
The following two considerations when it comes to write and read functions:
- The read and write functions and any help variables and help functions are surrounded by
#ifdef XREADANDWRITEF#endif- The string format used to represent value of a type should be the same in the read and the write function. Otherwise communicating simulations will not work if this type is passed as parameter between the systems.
The function xWriteSort, which is part of the run-time kernel can be useful when implementing Write functions.
extern char * xWriteSort (void *In_Addr,xSortIdNode SortNode);The xWriteSort function takes the address of a value to be printed, and a pointer to a xSortIdNode and returns the given value as a string. This function is typically useful if the sort we are implementing a write function for contains one or several components of sorts defined in SDL.
Read Function
In order to assign new values to variables that are of a sort that is implemented in C, i.e. has #ADT(T) in its ADT directive, it is necessary to implement a read function. Otherwise the value can only be read as a HEX string. Note that the run-time kernel can automatically handle all SDL sorts for which the code generator generates the C type definition. A read function should look like:
extern int yRead_SortName (void * Result)A read function is given an address to store the value that is read. It should return 1 if the read operation was successful. Otherwise, 0 should be returned and Result should be unchanged.
There are some suitable functions in the run-time kernel which can help you when you are implementing a read function. Basically the function xScanToken described below is a tokenizer that transforms sequences of characters to tokens. This function returns tokens according to the following enum type:
typedef enum {xxId, /* identifiers, numbers */xxString, /* SDL Charstring literal */xxSlash, /* / */xxColon, /* : */xxMinus, /* - */xxPlus, /* + */xxStar, /* * */xxComma, /* , */xxSemicolon, /* ; */xxArrowHead, /* ^ */xxLPar, /* ( */xxRPar, /* ) */xxLParDot, /* (. */xxRParDot, /* .) */xxLParColon, /* (: */xxRParColon, /* :) */xxDot, /* . */xxLBracket, /* [ */xxRBracket, /* ] */xxLCurlyBracket, /* { */xxRCurlyBracket, /* } */xxAt, /* @ */xxQuaStart, /* << */xxQuaEnd, /* >> */xxLT, /* < */xxLE, /* <= */xxGT, /* > */xxGE, /* >= */xxEQ, /* = */xxNE, /* /= */xxQuestionMark, /* ? */xx2QuestionMark, /* ?? */xxExclMark, /* ! */xxSystem, /* system */xxPackage, /* package */xxBlock, /* block */xxProcess, /* process */xxProcedure, /* procedure */xxOperator, /* operator */xxSubstructure, /* substructure */xxChannel, /* channel */xxSignalroute, /* signalroute */xxType, /* type */xxNull, /* null */xxEnv, /* env */xxSelf, /* self */xxParent, /* parent */xxOffspring, /* offspring */xxSender, /* sender */xxEoln, /* end of line */xxEOF, /* end of file */xxErrorToken /* used to indicate error */} xxToken;Function xScanToken
extern xxToken xScanToken ( char * strVar);reads the next token from input (stdin or Simulator UI) and returns the type of the next token as function result. If the token is xxId or xxString the strVar parameter will contain the identifier, number, or string. The size of the char[] parameter passed as actual parameter should be large enough to store the possible values. If some other token was found, no information is stored in strVar.
xUngetToken
Sometimes it is necessary to look-ahead to determine how to handle the current token. Using the function xUngetToken below it is possible return one token to the input. Note that both parameters must have the values obtained from xScanToken.
extern void xUngetToken (xxToken Token,char * strVar);The functions below can also be useful while implementing Read function. xPromptQuestionMark is suitable to obtain prompt in a similar way as for SDL defined sorts, while xReadOneParameter can be used to read element for element in a list, separated by commas and terminated either by ".)" or "]". The function xReadSort is similar to xWriteSort and can be used to read a component in the treated sort.
xPromptQuestionMark
extern xxToken xPromptQuestionMark (char * Prompt,char * QuestionPrompt,char * strVar);The function result and the parameter strVar behave in the same way as for the function xScanToken (see above). The parameter Prompt is the prompt that should be used. This string has to start with a ` ', i.e. a space. To conform with other built-in read function, the Prompt parameter should be: "(SortName) : " (note the ending space colon space). The QuestionPrompt parameter should either be identical to the Prompt parameter, or be null, i.e. (char *)0. If QuestionPrompt is null, the xPromptQuestionMark function will return xxEoln if a end-of-line is found. If QuestionPrompt is not null, the xPromptQuestionMark function will print the QuestionPrompt, and continue to read. Normally QuestionPrompt should be equal to Prompt in a simple data type, while it should be null in a structured data type.
Example 389 : ADT Example, Byte Type
This example is taken from the ADT byte (see Abstract Data Type for Byte). The byte type should be read and printed using HEX format.
#ifdef XREADANDWRITEFstatic char yCTmp[20];extern int yRead_byte( void *Result ){unsigned temp;xxToken Token;Token = xPromptQuestionMark(" (byte) : "," (byte) : ", yCTmp);if (Token==xxId && sscanf(yCTmp, "%X", &temp)>=1) {*(byte *)Result = (byte)temp;return 1;}xPrintString("Illegal byte value\n");return 0;}extern char *yWri_byte( void * C){sprintf(yCTmp, "%0.2X", *(byte *)C);return yCTmp;}#endifExample 390 : ADT Example, Struct Write Functions
This is an example of how the read and write functions for a struct with two components can look. The monitor system can handle reading of writing of struct values automatically, so please see this just as an example.
newtype struct1 /*#NAME `struct1' */ structa,b Integer;/*#ADT(W)#BODY#ifdef XREADANDWRITEFstatic char CTemp[500];char * yWri_struct1 (void *In_Addr){strcpy(CTemp, "(. ");strcat(CTemp, xWriteSort((void *)(&((struct1 *)In_Addr)->a), xSrtN_SDL_Integer) );strcat(CTemp, ", ");strcat(CTemp, xWriteSort((void *)(&((struct1 *)In_Addr)->b), xSrtN_SDL_Integer) );strcat(CTemp, " .)");return CTemp;}#endif*/endnewtype;More about #ADT
When generate is specified for a function, the code generator might decide not to generate the heading of the function, as in some cases it is not needed.
All code that is not generated is assumed to be included by the user in the #TYPE, #HEADING and #BODY sections in the #ADT directive.
Another name for an assign function, equal function and so on may be used, by including the desired name within quotes together with the generation options in the #ADT directive.
If, for example, the name of a certain assign function should be AssX, this can be obtained by specifying: A(B `AssX') for the assign function. This name will then be used throughout the generated code, both in generated declaration and at the places where the function is called. The name should be last in the specification for the actual function.
An include statement may be generated together with or replacing the type definition by giving a file name within quotes last in the specification part of the #ADT directive, immediately before the first section with code.
Example 391 : Including a File in ADT
/*#ADT (T(B) A(S) E(S) 'file name') */is used, the following include statement will be generated:
#include "file name"
Turning off the generation of the objects contained in the include file must be performed by the user.
Directive #REF
The directive #REF can be used to specify that the address, not the value, of a variable should be passed as parameter to an ADT operator, as it is defined in SDL. This feature cannot be used for operators defined in operator diagrams (the directive will be ignored for such operators).
The #REF directive is used as shown in the example below.
Example 392 : Including a File in ADT
operatorseq1 : Integer, Integer -> Integer;eq2 : Integer/*#REF*/, Integer/*#REF*/ -> Integer;The headings for these two operator will become in ANSI-C syntax (ignoring prefixes)
extern SDL_Integer eq1 (SDL_Integer P1,SDL_Integer P2);extern SDL_Integer eq2 (SDL_Integer *P1,SDL_Integer *P2);This feature can be used to optimize parameter passing to operators. The directive, however, also imposes the restriction that the actual parameters must be a variable or a formal parameter (see Example 393 below). This is checked by the code generator. A #REF directive does not in any way effect the way a operator call should be implemented in SDL. It is the responsibility of the code generator to generate the proper actual parameters in C.
Example 393 : Including a File in ADT
With the ADT in the previous example the following operator call is valid:
eq1(sVar, (. 1, 2, 3, 4 .) )The same call of eq2 would not be valid as the second parameter is not a variable or a formal parameter.
Generators
The Cadvanced/Cbasic SDL to C Compiler handles all the predefined generators in SDL, i.e. Array, String, Powerset, and Bag. It is also possible for a user to write his own generators and instantiate them in newtypes. However, the behavior of a user defined generator has to be specified completely by the user. This is performed in a somewhat extended #ADT directive placed just before the endgenerator keyword. These extensions are described below.
There are three additional sections in the directive, apart from #TYPE, #HEADING, and #BODY. These are #INSTTYPE, #INSTHEADING, and #INSTBODY. The inline C code in #TYPE, #HEADING, and #BODY is placed at the point of the generator, i.e. it is generated once. The contents of #INSTTYPE, #INSTHEADING, and #INSTBODY is inserted at each instantiation of the generator, i.e. in each newtype defined using the generator.
In the #INSTTYPE, #INSTHEADING, and #INSTBODY it is possible to use # followed by a number to access the information given in the generator instantiation:
- #0 means the name of the newtype in the instantiation
- #1 and ##1 is the first actual generator parameter
- #2 and ##2 is the second actual generator parameter
- and so on.
#1 and ##1 are equal, except when the corresponding actual generator parameter is a struct (or union). In that case, assuming the SDL struct:
newtype aaa structa, b integer;endnewtype;typedef struct aaa_s {SDL_Integer a;SDL_Integer b;} aaa;#1 will become struct aaa_s (or union aaa_s if a union), while ##1 will become aaa.
Example 394 : Example of User Defined Generator
GENERATOR Ref (TYPE Itemsort)LITERALSNull; /*#OP(S)*/OPERATORSRef2VStar : Ref -> VoidStar; /*#OP(HP)*/DEFAULT Null;/*#ADT()#INSTTYPEtypedef #1 *#0;#INSTHEADING#define #(Null)() 0#define #(Ref2VStar)(P) ((#(VoidStar))P)*/ENDGENERATOR Ref;Note the usage of #INSTTYPE and #INSTHEADING in the example above. The code in these section will be inserted in each newtype defined with this generator. For example, in a newtype:
NEWTYPE p Ref(Integer)ENDNEWTYPE;The #INSTTYPE section will become:
typedef SDL_Integer *p;
http://www.ibm.com/rational |
![]() |
![]() |
![]() |
![]() |