IBM
Contents Index Previous Next



Abstract Data Types


This section is a reference to the abstract data types. The following topics will be discussed:

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:

The Cbasic/Cadvanced SDL to C Compiler performs the following steps to optimize the code:

  1. Every C function that implements an operator is surrounded by an #ifndef definition.

Example 360 The #ifndef definition

#ifndef XNOUSE_AND_BIT_STRING
  /* function implementing the operator */
#endif

  1. During the code generation, the usage of the operators in the translated SDL transitions is recorded.
  2. 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.
  3. 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

Note:

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
#endif

One 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.

SDL name/operator C name/expression/operator
Boolean

SDL_Boolean

False, True

SDL_False, SDL_True

not

xNot_SDL_Boolean

and

xAnd_SDL_Boolean 

or

xOr_SDL_Boolean

xor

xXor_SDL_Boolean

=>

xImpl_SDL_Boolean

=, /=

yEqF_SDL_Boolean, 
yNEqF_SDL_Boolean

Character

SDL_Character

NUL

SOH

...

SDL_NUL 

SDL_SOH 

... (for all unprintable characters)

'a'

'b'

...

'a' 

'b'

... (for all printable characters except ' and \)

'''', '\'

'\'',  '\\'

chr

xChr_SDL_Character

num

xNum_SDL_Character

<, <=, >, >=

xLT_SDL_Character, 
xLE_SDL_Character, 
xGT_SDL_Character, 
xGE_SDL_Character

=, /=

xEqF_SDL_Character, 
xNEqF_SDL_Character

Charstring

SDL_Charstring

'aa'

SDL_CHARSTRING_LIT("Laa","aa")

mkstring

xMkString_SDL_Charstring

length

xLength_SDL_Charstring

first

xFirst_SDL_Charstring

last

xLast_SDL_Charstring

//

xConcat_SDL_Charstring

substring

xSubString_SDL_Charstring

=, /=

yEqF_SDL_Charstring, 
yNEqF_SDL_Charstring

Integer

SDL_Integer

0, 1 etc.

SDL_INTEGER_LIT(0),

SDL_INTEGER_LIT(1) etc.

+

xPlus_SDL_Integer

- (monodic, dyadic)

xMonMinus_SDL_Integer, 
xMinus_SDL_Integer

*

xMult_SDL_Integer

/

xDiv_SDL_Integer

mod

xMod_SDL_Integer

rem

xRem_SDL_Integer

float

xFloat_SDL_Integer

fix

xFix_SDL_Integer

<, <=, >, >=

xLT_SDL_Integer, 
xLE_SDL_Integer, 
xGT_SDL_Integer, xGE_SDL_Integer

=, /=

yEqF_SDL_Integer, 
yNEqF_SDL_Integer

Natural

SDL_Natural

Real

SDL_Real

12.45, ...

SDL_REAL_LIT(12.45, 12, 
450000000)

- (monodic, dyadic)

xMonMinus_SDL_Real, 
xMinus_SDL_Real

+

xPlus_SDL_Real

*

xMult_SDL_Real

/

xDiv_SDL_Real

<, <=, >, >=

xLT_SDL_Real, xLE_SDL_Real, 
xGT_SDL_Real, xGE_SDL_Real

=, /=

yEqF_SDL_Real, yNEqF_SDL_Real

Pid

SDL_PId

Null

SDL_NULL

=, /=

yEqF_SDL_PId, yNEqF_SDL_PId

Duration

SDL_Duration

23.45

SDL_DURATION_LIT(23.45, 23, 
450000000)

+

xPlus_SDL_Duration

- (monodic)

xMonMinus_SDL_Duration

- (dyadic)

xMinus_SDL_Duration

* (Duration * Real)

* (Real * Duration)

xMult_SDL_Duration, 
xMultRD_SDL_Duration

/

xDiv_SDL_Duration

<, <=, >, >=

xLT_SDL_Duration, 
xLE_SDL_Duration, 
xGT_SDL_Duration, 
xGE_SDL_Duration

=, /=

yEqF_SDL_Duration, 

yNEqF_SDL_Duration

Time

SDL_Time

23.45

SDL_TIME_LIT(23.45, 23, 
450000000)

+ (Time + Duration)

+ (Duration + Time)

xPlus_SDL_Time,

xPlusDT_SDL_Time

- (result: Time)

xMinusT_SDL_Time

- (result: Duration)

xMinusD_SDL_Time

<, <=, >, >=

xLT_SDL_Time, xLE_SDL_Time,

xGT_SDL_Time, xGE_SDL_Time

=, /=

yEqF_SDL_Time, 

yNEqF_SDL_Time

IA5String

SDL_IA5String

NumericString

SDL_NumericString

VisibleString

SDL_VisibleString

PrintableString

SDL_PrintableString

Bit

SDL_Bit

not

xNot_SDL_Bit

and

xAnd_SDL_Bit

or

xOr_SDL_Bit

xor

xXor_SDL_Bit

=>

xImpl_SDL_Bit

=, /=

yEq_SDL_Bit, yNEq_SDL_Bit

Bit_string

SDL_Bit_String

not

xNot_SDL_Bit_String

and

xAnd_SDL_Bit_String

or

xOr_SDL_Bit_String

xor

xXor_SDL_Bit_String

=>

xImpl_SDL_Bit_String

mkstring

xMkString_SDL_Bit_String

length

xLength_SDL_Bit_String

first

xFirst_SDL_Bit_String

last

xLast_SDL_Bit_String

//

xConcat_SDL_Bit_String

substring

xSubString_SDL_Bit_String

bitstr

xBitStr_SDL_Bit_String

hexstr

xHexStr_SDL_Bit_String

=, /=

yEq_SDL_Bit_String, 
yNEq_SDL_Bit_String

Octet

SDL_Octet

not

xNot_SDL_Octet

and

xAnd_SDL_Octet

or

xOr_SDL_Octet

xor

xXor_SDL_Octet

=>

xImpl_SDL_Octet

shiftl

xShiftL_SDL_Octet

shiftr

xShiftR_SDL_Octet

+

xPlus_SDL_Octet

-

xMinus_SDL_Octet

*

xMult_SDL_Octet

i2o

xI2O_SDL_Octet

o2i

xO2I_SDL_Octet

/

xDiv_SDL_Octet

mod

xMod_SDL_Octet

rem

xRem_SDL_Octet

bitstr

xBitStr_SDL_Octet

hexstr

xHexStr_SDL_Octet

<, <=, >, >=

yLT_SDL_Octet, yLE_SDL_Octet, 
yGT_SDL_Octet, yGE_SDL_Octet

=, /=

yEq_SDL_Octet, yNEq_SDL_Octet

Octet_string

SDL_Octet_String

mkstring

xMkString_SDL_Octet_String

length

xLength_SDL_Octet_String

first

xFirst_SDL_Octet_String

last

xLast_SDL_Octet_String

//

xConcat_SDL_Octet_String

substring

xSubString_SDL_Octet_String

bitstr

xBitStr_SDL_Octet_String

hexstr

xHexStr_SDL_Octet_String

bit_string

xBit_String_SDL_Octet_String

hex_string

xHex_String_SDL_Octet_String

=, /=

yEq_SDL_Octet_String, 
yNEq_SDL_Octet_String

Object_identifier

SDL_Object_Identifier

mkstring

xMkString_SDL_Object_Identifier

length

xLength_SDL_Object_Identifier

first

xFirst_SDL_Object_Identifier

last

xLast_SDL_Object_Identifier

//

xConcat_SDL_Object_Identifier

substring

xSubString_SDL_Object_Identifier

=, /=

yEq_SDL_Object_Identifier, 
yNEq_SDL_Object_Identifier

NULL (sort)

SDL_Null

NULL (literal)

SDL_NullValue

=, /=

yEq_SDL_Null, yNEq_SDL_Null

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.

Note:

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

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 EnumType	 
  LITERALS Lit1, Lit2, Lit3;
ENDNEWTYPE;

is translated to:

typedef XENUM_TYPE EnumType;
#define Lit1 0
#define Lit2 1
#define Lit3 2

Where the macro XENUM_TYPE is defined in sctpred.c as:

#ifndef XENUM_TYPE
#define XENUM_TYPE int
#endif

This 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.

Example 364 : Struct

NEWTYPE Str STRUCT
  a Integer;
  b Boolean;
  c Real;
ENDNEWTYPE;

is translated to:

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 STRUCT
  a Integer : 4;
  b Integer : 4;
            : 0;
  c UnsignedInt : 2;
  d Integer;
ENDNEWTYPE;

is translated to:

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.

Example 366 : Struct

NEWTYPE str STRUCT
  a, b integer;
  c    Boolean OPTIONAL;
  d    str2 OPTIONAL;
  e    Charstring := 'telelogic';
  f    arr3 := (. 11 .);
ENDNEWTYPE;

is translated to:

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:

Example 367 : Union

NEWTYPE Str /*#UNION*/ STRUCT
  tag integer;
  a integer;
  b Boolean;
  c real;
ENDNEWTYPE;

is translated to:

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.

Note:

It is completely up to the user to make certain that only valid components in a #UNION struct are accessed. During simulation, however, tests are inserted to ensure that only valid components are accessed.

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.

Example 368 : UnionC

NEWTYPE Str /*#UNIONC*/ STRUCT
  a integer;
  b Boolean;
  c real;
ENDNEWTYPE;

is translated to:

typedef union {
  SDL_Integer a;
  SDL_Boolean b;
  SDL_Real c;
} Str;

.

Note:

The #UNIONC directive is not recommended for use as the Cadvanced/Cbasic SDL to C Compiler cannot give any support for checking the validity of component selection. Both the #UNION directive and the CHOICE concept discussed below are much better.

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.

Example 369 : Choice

NEWTYPE Str CHOICE
  a integer;
  b Boolean;
  c real;
ENDNEWTYPE;

is translated to:

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:

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:

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.

Example 370 : Array

SYNTYPE Syn = integer
  CONSTANTS 0:10
ENDSYNTYPE;

NEWTYPE Arr ARRAY(Syn, real)
ENDNEWTYPE;

is translated to:

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.

Example 371 : Syntypes

SYNTYPE Syn = integer
  CONSTANTS 0:10
ENDSYNTYPE;

SYNTYPE Syn2 = integer
  CONSTANTS <0, =2, >=10
ENDSYNTYPE;

SYNTYPE Arr1 = Arr
  DEFAULT (. 2.0 .);
ENDSYNTYPE;
/* Arr defined above */

is translated to:

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.

Note:

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.

Example 372 : Operator

Operator Op in sort S is called.
Parameter 1: true
Parameter 2: 10
Enter value (integer) : 12

assuming 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.

Note:

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).

Types passed as addresses (structured types)

Bit_string

Octet_string

Object_identifier

Struct types (including #UNION, #UNIONC)

Choice types

Instantiations of generator Powerset

Instantiations of generator Bag

Instantiations of generator Array

Instantiations of generator String

Instantiations of generator Carray

Syntypes of a type in this list

Types that inherit a type in this list

Table 1 : Types Passed as Addresses

Types passed as values (simple types)

Integer

Real

Natural

Boolean

Character

Time

Duration

PId

Charstring

Bit

Octet

IA5String

NumericString

PrintableString

VisibleString

NULL

Enumeration types

Instantiations of generator Ref, Own, ORef

Syntypes of a type in this list

Types that inherit a type in this list

Table 2 : Types Passed as Values

Note:

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):

In parameters:

In/Out parameters:

Parameters are always passed as addresses, i.e the C parameter is (SDL_type *) if the type in SDL is SDL_type.

Operator result:

Example 373 :

Assume that struct1 is a newtype struct in SDL.

operators
 X : 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 contain
     two integers */
  (*Param2).comp1 = (*Param2).comp1+(*Param1).comp1;
  (*Param2).comp2 = (*Param2).comp2+(*Param1).comp2;
  *Result = *Param2;
  return Result;
    /* always return the last, extra, parameter */
}


Note: VERY IMPORTANT

As IN parameters are passed as addresses for structured types, changing such a parameter inside the operator might have undesired effects. A variable passed as actual parameter is then also changed. If you want to change the formal parameter copy it first to a operator local variable.

For Cadvanced/Cbasic this rule applies to operators implemented in C. For Cmicro this rule also applies to operators and procedures defined in SDL.

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:

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:

Note:

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:

ADT directives are recognized immediately before the reserved word ENDNEWTYPE (or ENDSYNTYPE).

Example 375 : Implementing an Operator (#ADT)

NEWTYPE Str STRUCT 
  a integer; 
  b Boolean;
  c real;
ADDING 
LITERALS 
  Lit1 /*#OP */,
  Lit2 /*#OP */;
OPERATORS
  Op1 :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)
#BODY
C 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.

Note:

The Cadvanced/Cbasic SDL to C Compiler will not check the consistency between the specification of implementation techniques and the actual code included in the body section. This check is, together with checking the C code for syntactic and semantic errors, left to the C compiler.

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.

Example 377 : Comment in ADT

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 text
         can be entered into the comments. Some
         compilers 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:

  1. 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 `)'.
  2. 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.

Example 381 : ADT Example

NEWTYPE Str STRUCT
  a Integer;
  b Boolean;
  c Real;
  ADDING LITERALS
    Lit1;
  OPERATORS
    Op1 : 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 STRUCT
  a Integer;
  b Boolean;
  c Real;
  OPERATORS
    Lit1 : -> 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;

Example 383 : ADT Example

SYNTYPE Index = Integer CONSTANTS 1:10
ENDSYNTYPE,

NEWTYPE A Array(Index, Integer)
  ADDING LITERALS
    Zero /*#OP (B) */;
  OPERATORS
    Add : 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 )

can be used for this purpose.

Example 384 : Error Handler in Operator

Example of use:

  if ( strlen(C) <= 1 ) {
#ifdef XECSOP
    xSDLOpError("First in sort Charstring",
                "Charstring length is zero." );
#endif
    return SDL_NUL;
  } else
    return 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.

Note:

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:

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_Charstring

Example 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:

  1. The memory used to represent the old value of C1 is lost and can never be reused.
  2. 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_Charstring

The 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.

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 STRUCT
  a integer;
  b Boolean;
  c real;
  ADDING LITERALS
    Lit;
  OPERATORS
    Op1 : Str, integer -> Str;
    Op2 : Str, Boolean -> Str;

ALTERNATIVE C;
#ADT (B)
#BODY
 some appropriate C code
ENDALTERNATIVE;

ENDNEWTYPE;

The #ADT directive, without the /* */ can be placed between ALTERNATIVE C; and ENDALTERNATIVE.

Note:

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:

Note:

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:

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:

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 *);

where:

GenericAssignSort returns the address passed as the first parameter.

The GenericAssignSort function performs three tasks:

  1. 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.
  2. 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.
  3. 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:

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:

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)4

The macro names above are all of the form XASS_1_2_3, where the abbreviations placed at 1, 2, and 3 should be read:

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 *);

where:

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.

GenericMakeChoice: This function is used for choice types.

GenericMakeOwnRef: This function is used for instantiations of generators Own and Ref.

GenericMakeArray: This function is used for instantiations of the generators Array, Carray, and GArray.

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:

General array


extern void * GenGArray_Extract (xGArray_Type *, void *,
   tSDLGArrayInfo *);
extern void * GenGArray_Modify  (xGArray_Type *, void *,
   tSDLGArrayInfo *);

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 *);

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 *);

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 *);

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)->Length
extern 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 *);

More about Abstract Data Types

Including Type Definitions

Note:

Use the features presented in this section with care. The features were developed early in the SDL Suite history. Now in principle every data type in C can be expressed in SDL as well. Therefore, the recommended method is to write the types in SDL or to translate C types to SDL using the cpp2sdl tool.

History has also shown that it has been difficult to keep full backward compatibility for these features and at the same time improve the performance of the generated code. This of course comes from that these features is highly dependent on the way code is generated.

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')
#TYPE
C code
#HEADING
C code
#BODY
C 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.

B

Body

H

Heading

Q

Question

S

Standard

G

Generate

P

Prefix

I

Infix

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.

Type Interpretation

T(G)

Generate type definition from SDL sort

T(B)

Do not generate type definition. Assume the type should be "passed as value" to operators

T(BV)

Do not generate type definition. Assume the type should be "passed as address" to operators

T

Same as T(B)

-

Same as T(G)

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)

Type Interpretation

A(B)

Use and generate heading, but not body, of yAss_#(S)

A(H)

Use, but generate no code for yAss_#(S)

A(G)

  • If the type definition is generated:
    • Use = if possible.
    • Otherwise use the GenericAssignSort function
  • If type definition is not generated (T, T(B)):
    • Use =

A(S)

Use =

A

Same as A(B)

-

Same as A(G)

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) ).

Type Method

E(B)

Use and generate heading, but not body, of yEq_#(S)

E(H)

Use but generate no code for yEq_#(S)

E(G)

  • If the type definition is generated:
    • Use == if possible
    • Otherwise use the GenericEqualSort function.
  • If the type definition is not generated
    • Use ==

E(S)

Use ==

E(Q)

Use and generate an equal function that asks for the result of the test (same as Q for operators).

E

Same as E(B)

-

Same as E(G)

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.



Type Interpretation

F(B)

Generate heading, but no body of the free function yFree_#(SortName).

F(H)

Generate neither heading nor body of the free function.

F(S)

Use the function GenericFreeSort

F

Same as F(B)

-

Do not use free function.

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 ! Component
Variable (Index)

An Extract operation can be generated in four ways:

Variable.Component       used for struct and #UNIONC
Variable.U.Component     used for #UNION and choice
Variable.A(Index)        used for array
yExtr_SortName(Variable,Expr)

The last version, the Extract function, is used for all other cases.

Type Interpretation

X(B)

Use Extract function

X(G)

Use component selection according to table above.

X

Same as X(B)

-

Same as X(G)

A Modify operation can in the same way be generated in four ways:

Variable.Component       used for struct and #UNIONC
Variable.U.Component     used for #UNION and choice
Variable.A(Index)        used for array
(* yAddr_SortName((&Variable), Expr))

The last version, the Addr function, is used for all other cases.

Type Interpretation

M(B)

Use Addr function

M(G)

Use component selection according to table above.

M

Same as M(B)

-

Same as M(G)

Read and Write Function

The write function is used by the monitor system to write values of the type.

Type Interpretation

W(B)

Generate heading but not the body of a write function.

W(H)

Generate neither heading nor body of a write function, but assume that the user has provided such a function.

W(S)

Values of this type are to be printed as a HEX string. No write function is assumed to be present.

W

Same as W(B)

-

Same as W(S)

The read function is used by the monitor system to read values of the type.

Type Interpretation

R(B)

Generate heading but not the body of a read function.

R(H)

Generate neither heading nor body of a read function, but assume that the user has provided such a function.

R(S)

Values of this type are to be read as a HEX string. No read function is assumed to be present.

R

Same as R(B)

-

Same as R(S)

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.

Note:

The following two considerations when it comes to write and read functions:

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

The 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 XREADANDWRITEF
static 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;
}
#endif

Example 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' */ struct
  a,b Integer;

/*#ADT(W)
#BODY
#ifdef XREADANDWRITEF
static 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

If the directive

/*#ADT (T(B) A(S) E(S) 'file name') */ 

is used, the following include statement will be generated:

#include "file name"

Note:

Turning off the generation of the objects contained in the include file must be performed by the user.

Directive #REF

Note:

This directive is provided only for backward compatibility. The SDL Suite now supports in/out parameters for operators, which serves exactly the same purpose. In/out parameter is an SDL-2000 extension and is supported also in operator diagrams.

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

operators
  eq1 : 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:

#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 struct
  a, b integer;
endnewtype;

which will be generated as

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)
  LITERALS
    Null;                           /*#OP(S)*/
  OPERATORS
    Ref2VStar : Ref -> VoidStar;    /*#OP(HP)*/
  DEFAULT Null;
/*#ADT()
#INSTTYPE
typedef #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
Contents Index Previous Next