The files that are contained in the ADT library are located in a subdirectory to the directory referred to by the environment variable sdtrelease. The subdirectory is identified by $sdtrelease/include/ADT.
------------------------------------------------------------------- Note: Using the SDT2.3 versions of the ADTs will result in C compilation errors! -------------------------------------------------------------------
This data type, which you may include in any SDL system, makes it possible to access, at the SDL level, a subset of the file and I/O operations provided by C.
The implementation of the operators are harmonized with the I/O in the monitor, including the Simulator Graphical User interface. All terminal I/O, for example, will be logged on the interaction log file if the monitor command Log-On is given.
The data type defines a "file" type and contains three groups of operations:
--------------------------------------------------------------- Note: This data type is not intended to be used in the SDT Validator! ---------------------------------------------------------------
To make the data type available you include the file containing the definition with an analyzer include in an appropriate text symbol:
Example 106 : Including an ADT File /*#include 'file.pr' */You should also specify the path to the file. You will find the file in a subdirectory to the directory referred to by the environment variable sdtrelease. The subdirectory is named include/ADT.
LITERALS NULL, stdin, stdout; OPERATORS GetAndOpenR : Charstring -> TextFile; GetAndOpenW : Charstring -> TextFile; OpenR : CharString -> TextFile; OpenW : Charstring -> TextFile; Close : TextFile -> TextFile; IsOpened : TextFile -> Boolean; AtEOF : TextFile -> Boolean; PutReal : TextFile, Real -> TextFile; PutTime : TextFile, Time -> TextFile; PutDuration : TextFile, Duration -> TextFile; PutPId : TextFile, PId -> TextFile; PutInteger : TextFile, Integer -> TextFile; PutBoolean : TextFile, Boolean -> TextFile; PutCharacter : TextFile, Character -> TextFile; PutCharstring : TextFile, Charstring -> TextFile; PutString : TextFile, Charstring -> TextFile; PutLine : TextFile, Charstring -> TextFile; PutNewLine : TextFile -> TextFile; "//" : TextFile, Real -> TextFile; "//" : TextFile, Time -> TextFile; "//" : TextFile, Duration -> TextFile; "//" : TextFile, Integer -> TextFile; "//" : TextFile, Charstring -> TextFile; "//" : TextFile, Boolean -> TextFile; "//" : TextFile, PId -> TextFile; "+" : TextFile, Character -> TextFile; GetReal : TextFile, Charstring -> Real; GetTime : TextFile, Charstring -> Time; GetDuration : TextFile, Charstring -> Duration; GetPId : TextFile, Charstring -> PId; GetInteger : TextFile, Charstring -> Integer; GetBoolean : TextFile, Charstring -> Boolean; GetCharacter : TextFile, Charstring -> Character; GetCharstring : TextFile, Charstring -> Charstring; GetString : TextFile, Charstring -> Charstring; GetLine : TextFile, Charstring -> Charstring; GetSeed : TextFile, Charstring -> Integer; GetReal : TextFile -> Real; GetTime : TextFile -> Time; GetDuration : TextFile -> Duration; GetPId : TextFile -> PId; GetInteger : TextFile -> Integer; GetBoolean : TextFile -> Boolean; GetCharacter : TextFile -> Character; GetCharstring : TextFile -> Charstring; GetString : TextFile -> Charstring; GetLine : TextFile -> Charstring; GetSeed : TextFile -> Integer;The operators may be divided into three groups with different purpose:
------------------------------------------------------------------- Caution! The operators GetAndOpenR, and GetAndOpenW do not work with the Application library. The operators GetPId and PutPId (and the // operator to write PIds) can be used with the Application li brary, but they will use a different output format. -------------------------------------------------------------------
typedef FILE * TextFile;The literal NULL represents a null value for files. This literal is translated to TextFileNull() in the generated C code by an appropriate #NAME directive and is then implemented using the macro:
#define TextFileNull() (TextFile)0All variables of the type TextFile will have this value as default value.
The literals stdin and stdout represent the standard files stdin and stdout in C, which are the files used in C for I/O to the terminal. The file stdin is used for reading information from the keyboard, while stdout is used for writing information on the screen.
The standard operators assignment and test for equality is implemented in such a way that A:=B means that now A refers to the same file as B, while A=B tests if A and B refer to the same file.
If you type <Return>, - or the file name stdin at the prompt in GetAndOpenR a reference to stdin is returned by the operator. GetAndOpenW will, in the same way, return a reference to stdout if the prompt is answered by <Return>, - or the file name stdout.
---------------------------------------------------------------- Note: To work properly in the Simulator Graphical User Interface, the prompt string should be terminated with: ": ", i.e. colon space. ----------------------------------------------------------------
Example 107 : ADT for File I/O, Print to ScreenIf the information is to be printed on the screen, you may use the following structure:
DCL F TextFile; TASK F := stdout; TASK F := F // 'example';Declare a variable of type TextFile and assign it the value stdout. You may then use it in the write operators discussed under "Write Operators" on page 2063.
Example 108 : ADT for File I/O, Print to FileIf the information is to be printed on a file with a given name, you may use the following structure:
DCL F TextFile; TASK F := OpenW('filename'); TASK F := F // 'example';The difference from the above is that the operator OpenW is used to open a file with the specified name. This outline may be complemented with a test if the OpenW operation was successful or not.
Example 109 : ADT for File I/O, Accessing Text FileIf you want to be able to determine at run-time where the information should be printed, you should define a TextFile as in the examples above, and then use the following structure
Figure 553 : Accessing a TextFile ----- (fig) -----If you answer the question by hitting <Return> or by typing stdout, the information will be printed on screen (stdout). If you type the name of a file, the information will be printed on that file.
If you want to open the file for read instead of write, you may use almost identical structures.
The operator PutString takes a TextFile and a Charstring parameter and prints the string on the TextFile. PutString prints the string as a C string, not using the format for SDL Charstring. This means that no ' is printed. PutString returns the TextFile given as parameter as result.
The infix write operator "//" takes as parameters a TextFile and a value of type Boolean, Charstring, Integer, PId, Real, Time, or Duration. TextF // Val prints the value `Val' to the Textfile referenced by `TextF', and returns value `TextF'. Character strings are printed without enclosing `''. All // operators except the one for Charstring append a space to the file, after the value is written.
The infix write operator "+" takes as parameters a TextFile and a Character. "+" behaves just as "//", but it has its special name in order to avoid type conflicts with Charstring.
The operator PutNewLine takes a TextFile as parameter, prints a carriage return (actually a "\n") on this file, and returns the TextFile as operator result.
The different Put operators are equivalent to the // operators, and they are mainly present for backward compatibility reasons.
There is a function named xPutValue in the implementation of the data type TextFile. This function may print a value of any type that may be handled by the monitor system, but may only be accessed from in-line C code and not from SDL. A detailed description of the xPutValue function may be found under "Accessing the Operators from C" on page 2066.
Example 110 : ADT for File I/O, Print to FileTo print a line according to the following example, where 137 is the value of the variable NoOfJobs:
Number of jobs: 137 Current time: 137.0000You could use the following statements, assuming that the TextFile F is already opened:
TASK TASK F := F // `Number of jobs: ` // 137; TASK F := F // `current time: ` // Now; TASK F := PutNewLine(F);----------------------------------------------------------------- Note: Do not attempt to use too many values within one expression (typi cally not more than 4), as the time to analyze the system will in crease dramatically! -----------------------------------------------------------------
The operator GetSeed is used to read appropriate values to initialize random number generators (odd integers in the range 1 to 32767).
There are two versions of each Get operator: one that only takes as parameters a TextFile, and the other that takes as parameters a TextFile and a Charstring which is used as prompt. All Get operators behave differently depending on if the value should be read from the terminal (stdin) or from a file.
If the value should be read from the terminal, the Get operators with the prompt parameter may be used. This prompt is printed on the screen, and then an attempt to read a value of the current type is made. If a Get operator with only the TextFile parameter is used, a default prompt is used, that depends on the type that is to be input. If the operation is successful, the read value is returned as a result, otherwise the message "Illegal value" is printed and the user is given a new chance to type a value.
---------------------------------------------------------------- Note: To work properly in the Simulator Graphical User Interface, the prompt string should be terminated with: ": ", i.e. colon space. ----------------------------------------------------------------If the value should be read from a file, it is recommended to use the Get operators without the prompt parameter, as it is not used. It is assumed that a value of the correct type will be found.
There are several Get operators for reading character strings:
GetString reads a sequence of characters until the first white space character, and is equivalent to fscanf (f, "%s").
GetLine reads a sequence of characters until the end of line is reached. It is equivalent to fgets, but the end-of-line character will not be part of the string.
GetCharstring reads a sequence of characters on a single line that is enclosed by single quotes (`). This operator is mainly present for backward compatibility reasons.
There is a function named xGetValue in the implementation of the data type TextFile, which may read a value of any type that may be handled by the monitor system. This function can only be accessed from in-line C code and not from SDL. A detailed description of the xGetValue function may be found under "Accessing the Operators from C" on page 2066.
Example 111 : ADT for File I/O, Read from File TASK Mean := GetReal (F), A(1) := GetReal (F), A(2) := GetReal (F), A(3) := GetReal (F);
'Open file F' /*#CODE #(F) = GetAndOpenW("LFile : "); while ( ! IsOpened(#(F)) ) #(F) = GetAndOpenW("LIllegal name. File : "); */which is more compact and gives a better overview at the SDL level.
#(F) is an SDL directive telling the C Code Generator to translate the SDL variable F to the name it will receive in the generated C code.
To simplify the use of in-line C code, #NAME directives are introduced on all identifiers defined in this data type. The same names are used in C as in SDL.
------------------------------------------------------------------- Note: Upper and lower case letters are significant in C (but not in SDL). Note also the additional L in the Charstring literals, for example "LIllegal name. File : ". This first character is used in the implementation of the SDL sort Charstring and should always be L in a charstring literal. -------------------------------------------------------------------From in-line C, two functions xGetValue and xPutValue are also available to read and write values of any type. These functions have the following prototypes:
extern void xGetValue( TextFile F, SDL_Charstring Prompt, xSortIdNode SortId, void * Result, char * FunctionName ); extern void xPutValue( TextFile F, xSortIdNode SortId, void * Value, char * FunctionName );To handle, for example, I/O of an SDL struct, the ideas presented below may be used.---------------------------------------------------------------- Parameter Interpretation ---------------------------------------------------------------- TextFile F The file to read from or to print to. In xGetValue:
Is used as prompt in exactly the same SDL_Charstring Prompt way as for the ordinary Get operators. xSortIdNode SortId A reference to the xIdNode that repre sents the SDL sort to be read or printed For the predefined SDL sorts you may use variables named xSrtN_SDL_Real, xSrtN_SDL_Integer, and so on, as parameter. For user-defined sorts, you may use similar variables named ySrtN_#(SortName), where Sort Name should be replaced by the sort name. void * Result
The address to the variable where the void * Value result should be stored, or the address to the variable that should be printed. char * FunctionName A string specifying the name of an appropriate function. This name will be given if an error is detected during reading or printing. ------------------------------------------------------------------------------------------------------------------------------- Note: xGetValue and xPutValue will not work together with the Applica tion library. ---------------------------------------------------------------
Example 112 : ADT for File I/O of an SDL Struct NEWTYPE SName STRUCT a, b Integer; ENDNEWTYPE; DCL FIn, FOut TextFile, SVar SName; TASK 'Put SVar on FOut' /*#CODE xPutValue( #(FOut), ySrtN_#(SName), &(#(SVar)), "PutSName" ); */; TASK 'Get SVar from FIn' /*#CODE xGetValue( #(FIn), "Value : ", ySrtN_#(SName),&(#(SVar)), "LGetSName"); */;
In this section an SDL abstract data type according to the previous discussion is presented. This data type may be included in any SDL system.
The basic mechanism behind pseudo-random number generation is as follows. A sequence of bit-patterns is defined using a formula of type:
The function f should be such that the sequence of elements seen as numbers should be "random", and the number of element in the sequence, before it starts to repeat itself, should be as large as possible.
To obtain a new random number is thus a two step process:
The result returned by the operator Random, which is the basic random number generator, is this bit-pattern seen as a number between 0 and 1, and expressed as a float (SDL_Real).
The data type RandomControl may be included in any SDL system using an analyzer include statement, where the file containing the definition of the data type is included. Example:
/*#include 'random.pr' */The path to the file should also be given. In the installation the file is placed in a subdirectory named include/ADT, to the directory referred to by the environment variable sdtrelease.
As the C standard functions log and exp are used in the random.pr file, it is necessary to link the application together with the library for math functions, i.e. to have -lm in the link operation in the makefile. See "Compile and Link Options" on page 1115 in chapter 22, The SDT Organizer. To use the entry -lm in the link list seems to be a fairly standard way to find the library for math functions. If this does not work, or you want more details, please see the documentation for your C compiler.
typedef unsigned long * RandomControl;The reason for passing the address to the bit-pattern (that is to the unsigned long), is that this bit-pattern has to be updated by the random functions.
Below the operators provided in this data type are listed. There are, for many of the operators, several versions with different sets of parameters and/or result types to support different usage of the operator.
Random : RandomControl -> Real; Random : RandomControl -> Duration; Random : RandomControl -> Time; Erlang : Real, Integer, RandomControl -> Real; Erlang : Real, Integer, RandomControl -> Duration; Erlang : Real, Integer, RandomControl -> Time; Erlang : Duration, Integer, RandomControl -> Real; Erlang : Duration, Integer, RandomControl -> Duration; Erlang : Duration, Integer, RandomControl -> Time; Erlang : Time, Integer, RandomControl -> Real; Erlang : Time, Integer, RandomControl -> Duration; Erlang : Time, Integer, RandomControl -> Time; HyperExp2 : Real, Real, Real, RandomControl -> Real; HyperExp2 : Real, Real, Real, RandomControl -> Duration; HyperExp2 : Real, Real, Real, RandomControl -> Time; HyperExp2 : Duration, Duration, Real, RandomControl -> Real; HyperExp2 : Duration, Duration, Real, RandomControl -> Duration; HyperExp2 : Duration, Duration, Real, RandomControl -> Time; HyperExp2 : Time, Time, Real, RandomControl -> Real; HyperExp2 : Time, Time, Real, RandomControl -> Duration; HyperExp2 : Time, Time, Real, RandomControl -> Time; NegExp : Real, RandomControl -> Real; NegExp : Real, RandomControl -> Duration; NegExp : Real, RandomControl -> Time; NegExp : Duration, RandomControl -> Real; NegExp : Duration, Rando mControl -> Duration; NegExp : Duration, RandomControl -> Time; NegExp : Time, RandomControl -> Real; NegExp : Time, RandomControl -> Duration; NegExp : Time, RandomControl -> Time; Uniform : Real, Real, RandomControl -> Real; Uniform : Real, Real, RandomControl -> Duration; Uniform : Real, Real, RandomControl -> Time; Uniform : Duration, Duration, RandomControl -> Real; Uniform : Duration, Duration, RandomControl -> Duration; Uniform : Duration, Duration, RandomControl -> Time; Uniform : Time, Time, RandomControl -> Real; Uniform : Time, Time, RandomControl -> Duration; Uniform : Time, Time, RandomControl -> Time; Draw : Real, RandomControl -> Boolean; Geometric : Real, RandomControl -> Integer; Geometric : Real, RandomControl -> Duration; Geometric : Real, RandomControl -> Time; Geometric : Duration, RandomControl -> Integer; Geometric : Duration, RandomControl -> Duration; Geometric : Duration, RandomControl -> Time; Geometric : Time, RandomControl -> Integer; Geometric : Time, RandomControl -> Duration; Geometric : Time, RandomControl -> Time; Poisson : Real, RandomControl -> Integer; Poisson : Real, RandomControl -> Duration; Poisson : Real, RandomControl -> Time; Poisson : Duration, RandomControl -> Integer; Poisson : Duration, RandomControl -> Duration; Poisson : Duration, RandomControl -> Time; Poisson : Time, RandomControl -> Integer; Poisson : Time, RandomControl -> Duration; Poisson : Time, RandomControl -> Time; RandInt : Integer, Integer, RandomControl -> Integer; RandInt : Integer, Integer, RandomControl -> Duration; RandInt : Integer, Integer, RandomControl -> Time; DefineSeed : Integer -> RandomControl; GetSeed : Charstring -> Integer; Seed : RandomControl -> Integer;
to compute the next value stored in the parameter of type RandomControl. The result from Random is a real random number in the interval 0.0 < Value < 1.0.
---------------------------------------------------------------------- Caution! Since the range of feasible samples from the distribution is infinite and the result type is integer, integer overflow may occur. ----------------------------------------------------------------------
---------------------------------------------------------------------- Caution! Since the range of feasible samples from the distribution is infinite and the result type is integer, integer overflow may occur. ----------------------------------------------------------------------
--------------------------------------------------------------------- Note: SDL variables can only be declared in processes and will be local to the process instances. ---------------------------------------------------------------------
Example 113 : Using RandomControl, DefineSeed SYNONYM Seed1 RandomControl = DefineSeed(GetSeed(stdin, 'Seed1 : ')); TASK Delay := NegExp(Mean1, Seed1);This is correct according to SDL as operators only have IN parameters and therefore expressions are allowed as actual parameters. In C it is also an IN parameter and cannot be changed. But as a RandomControl value is an address it is possible to change the contents in that address.
The C Code Generator will, for synonyms that cannot be computed at generation time, allocate a variable and initialize it according to the synonym definition at start-up time. Note that this will be performed before any transitions have been executed.
A typical application of RandomControl synonyms are together with the Seed operator. The Seed operator is used to generate values suitable to initialize RandomControl variables with.
Example 114 : Using RandomControl, Seed SYNONYM BasicSeed RandomControl = DefineSeed(GetSeed(stdin, 'Seed : ')); DCL S1 RandomControl := DefineSeed(Seed(BasicSeed)); DCL S2 RandomControl := DefineSeed(Seed(BasicSeed));The variety of operators with the same name makes it possible to directly use operators in many more situations. This is called overloading of operators. If, for example, there were only the NegExp version:
NegExp : Real, RandomControl -> Real;then explicit conversion operators would have been necessary to draw, for example, a Duration value from the negative exponential distribution. The code to draw a Duration value would then be something like:
RealToDuration(NegExp(Mean, Seq))We have instead introduced several operators with the same name and purpose, but with different combinations of parameter types and result type. So for the NegExp operator discussed above, there is also a version:
NegExp : Real, RandomControl -> Duration;which exactly what we wanted.
There is, however, a price to be paid for having overloaded operators. It must be possible for the SDT Analyzer to tell which operator that is used in a particular situation. It then uses all available information about the parameters and what the result is used for. Consider Example 115 below.
Example 115 : Overloaded Operator TIMER T; DCL Mean, Rand Real, D Duration, Seq RandomControl := DefineSeed(GetSeed('Seed : ')); TASK Rand := NegExp(Mean, Seq); TASK D := NegExp(Mean, Seq); TASK D := NegExp(TYPE Real 1.5, Seq); DECISION NegExp(Mean, Seq) > TYPE Duration 10.0; (true) : .... ELSE : ENDDECISION; SET (Now + NegExp(Mean, Seq), T);
"+" : Time, Duration -> Time;defined in the sort Time. So, both the type for the parameter and the result are possible to determine for the NegExp operator in this example.
Most of these problems can be avoided by using SYNONYMS or variables instead of literal values. This is in most cases a better solution than to introduce qualifiers.
Example 116 : Using SYNONYMSIf, for example, the synonyms:
SYNONYM MeanValue Real = 1.5; SYNONYM Limit Duration = 10.0;were defined, the third and fourth NegExp call would cause no problem:
TASK D := NegExp(MeanValue, Seq); DECISION NegExp(Mean, Seq) > Limit; (true) : .... ELSE : ENDDECISION;
----------------------------------------------------------------- Note: Each operator returning a random number will call the basic opera tor Random at least once. -----------------------------------------------------------------
--------------------------------------------------------------------- Note: This data type is not implemented in a way that makes it possible to be used in the SDT Validator. ---------------------------------------------------------------------
The entities which may be members of a queue are called object instances. An object instance is a passive entity containing user defined information. This information is described in the object description.
In SDL these definitions are implemented using sorts called Queue, ObjectInstance, and ObjectDescr, where ObjectDescr should be defined by the user. ObjectDescr should have the structure given in the example below (Example 117).
The data types for list processing may be included in any SDL system using Analyzer #include statements, where the files containing the definitions of the data types are included. The definitions should be placed in the order given in the example below:
Example 117 : Including ADT for List Processing /*#include 'list1.pr'*/ NEWTYPE ObjectDescr /*#NAME 'ObjectDescr'*/ STRUCT SysVar SysTypeObject; /* other user defined components */ ENDNEWTYPE; /*#include 'list2.pr'*/The file list1.pr contains the definition of the sort Queue (and the help sorts ObjectType and SysTypeObject), while the file list2.pr contains the definition of the type ObjectInstance. The directory path to the files list1.pr and list2.pr should also be specified. In the installation the files are placed in a directory designated by:------------------------------------------------------------------- Note: In the SDT2.3 version of the list "package", an ADT directive was necessary in the sort ObjectDescr. This ADT directive is no longer necessary but still allowed. -------------------------------------------------------------------
Variables of the sorts Queue and ObjectInstance are references (pointers) to the representation of the queue or the object instance. In both sorts there is a null value, the literal NULL, which indicates that a variable refers to no queue or no object instance. The default value for Queue and ObjectInstance variables is NULL.
A variable of sort ObjectInstance can refer to a data area containing the components defined in the struct ObjectDescr. The example below shows how to manipulate these components.
Example 118 : ADT for List Processing, Struct ObjectDescr /*#include 'list1.pr'*/ NEWTYPE ObjectDescr /*#NAME 'ObjectDescr'*/ STRUCT SysVar SysTypeObject; Component1 Integer; Component2 Boolean; ENDNEWTYPE; /*#include 'list2.pr'*/ DCL O1 ObjectInstance; TASK O1 := NewObject; /* see next section */ TASK O1!ref!Component1 := 23, O1!ref!Component2 := false; TASK IntVar := O1!ref!Component1, BoolVar := O1!ref!Component2;A component is thus referenced by the syntax:
ObjectInstanceVariable ! ref ! ComponentNameAssignments and test for equality may be performed for queues and for object instances. The assignments:-------------------------------------------------------------------- Caution! You should never directly manipulate the component SysVar in the struct ObjectDescr. It contains information about if and how the object instance is inserted into a queue and should only be used by the queue handling operators. --------------------------------------------------------------------
Q1 := Q2; O1 := O2;mean that Q1 now refers to the same queue as Q2 and that O1 now refers to the same object instance as O2. Assignment is thus implemented as copying of the reference to the queue (and not as copying of the contents of the queue). The same is true for object instances.
The test for equality is in the same way implemented as a test if the left and right hand expression reference the same queue or the same object instance (and not if two queue or object instances have the same contents).
Due to the order in which the sorts are defined, a component of sort Queue can be a part of the ObjectDescr struct, while components of type ObjectInstance cannot be part of ObjectDescr.
If you want several different types of objects in a queue, with different contents, the #UNION directive (see "Union" on page 1982 in chapter 34, The C Code Generator) may be used according to the following example:
Example 119 : Unions and Queues NEWTYPE Ob1 STRUCT Comp1Ob1 integer; Comp2Ob1 boolean; ENDNEWTYPE; NEWTYPE Ob2 STRUCT Comp1Ob2 character; Comp2Ob2 charstring; ENDNEWTYPE; NEWTYPE Ob STRUCT /*#UNION*/ Tag integer; C1 Ob1; C2 Ob2; ENDNEWTYPE; NEWTYPE ObjectDescr /*#NAME 'ObjectDescr'*/ STRUCT SysVar SysTypeObject; U Ob; /*#ADT (X)*/ ENDNEWTYPE;The components may now be reached using:
O1 ! ref ! U ! Tag O1 ! ref ! U ! C1 ! Comp1Ob1 O1 ! ref ! U ! C2 ! Comp2Ob1
null NewQueue Cardinal : Queue -> Integer; DisposeQueue : Queue -> Queue; Empty : Queue -> Boolean; FirstInQueue : Queue -> ObjectInstance; Follow : Queue, ObjectInstance, ObjectInstance -> Queue; IntoAsFirst : Queue, ObjectInstance -> Queue; IntoAsLast : Queue, ObjectInstance -> Queue; LastInQueue : Queue -> ObjectInstance; Member : Queue, ObjectInstance -> Boolean; Precede : Queue, ObjectInstance, ObjectInstance -> Queue; Predecessor : ObjectInstance -> ObjectInstance; Remove : ObjectInstance -> ObjectInstance; Successor : ObjectInstance -> ObjectInstance;
null NewObject DisposeObject: ObjectInstance -> ObjectInstance;The operators defined in the sorts Queue and ObjectInstance have the behavior described below. All operators will check the consistency of the parameters. Each queue and object instance parameter should, for example, be /= null. If an error is detected the operator will cause an SDL dynamic error that will be treated as any other dynamic error found in an SDL system.
----------------------------------------------------------------------- Note: Any references to an object instance or to a queue that is returned to the avail stack is now invalid and any use of such a reference is er roneous and has an unpredictable result. -----------------------------------------------------------------------
--------------------------------------------------------------------- Note: The operator Member is used to check that the second object instance is member of the queue. ---------------------------------------------------------------------
--------------------------------------------------------------------- Note: The operator Member is used to check that the second object instance is member of the queue. ---------------------------------------------------------------------
----------------------------------------------------------------------- Note: Any references to an object instance that is returned to the available stack are now invalid and any use of such a reference is erroneous and has an unpredictable result. -----------------------------------------------------------------------
/*#include 'list1.pr' */ NEWTYPE ObjectDescr /*#NAME 'ObjectDescr'*/ STRUCT SysVar SysTypeObject; Number Integer; Name Charstring; ENDNEWTYPE; /*#include 'list2.pr' */ Example 120 : Creating a QueueTo create a new queue and insert two objects in the queue, so that the first object has Number = 23 and Name = 'xyz' and the second object has Number = 139 and Name = 'Telelogic', you could use the following code (assuming appropriate variable declarations):
TASK Q := NewQueue, O1 := NewObject, O1!ref!Number := 23, O1!ref!Name := 'xyz', Q := IntoAsFirst(Q, O1), O1 := NewObject, O1!ref!Number := 139, O1!ref!Name := 'Telelogic', Q := IntoAsLast(Q, O1); Example 121 : Removing from QueueTo remove the last object instance from a queue, assuming the queue is not empty, you could use the following code:
TASK O1 := Remove(LastInQueue(Q)); Example 122 : Looking in QueueYou may look at the component Name in the first object instance in the queue in the following way:
TASK O1 := FirstInQueue(Q), StringVar := O1!ref!Name;or if the reference to O1 is not going to be used any further
TASK StringVar := FirstInQueue(Q)!ref!Name; Example 123 : Searching in QueueThe result of the following algorithm is that O1 will be a reference to the first object instance that has the value IntVar in the component Number. If no such object is found O1 is assigned the value null.
TASK O1 := FirstInQueue(Q); NextObject: DECISION O1 /= null; (true) : DECISION O1!ref!Number /= IntVar; (true): TASK O1 := Successor(O1); JOIN NextObject; (false): ENDDECISION; (false): ENDDECISION; Example 124 : Removing Duplicates from QueueThe algorithm below removes all duplicates from a queue (and returns them to the avail stack). A duplicate is here defined as an object instance with the same Number as a previous object in the queue.
TASK O1 := FirstInQueue(Q); NextObject: DECISION O1 /= null; (true) : TASK O2 := Successor(O1); NextTry: DECISION O2 /= null; (true): DECISION O1!ref!Number = O2!ref!Number; (true): TASK Temp := O2, O2 := Successor(O2), Temp := DisposeObject ( Remove(Temp)); (false): TASK O2 := Successor(O2); ENDDECISION; JOIN NextTry; (false): TASK O1 := Successor(O1); JOIN NextObject; ENDDECISION; (false) : ENDDECISION;
You may use the monitor command Examine-Variable to examine the values stored in a variable of type ObjectInstance. By typing an additional index number after the variable Queue the value of the ObjectInstance at that position of the queue is printed.
In C you access a component in an ObjectInstance using the -> operator:
OI_Var -> ComponentAs an example of an algorithm in C, consider the algorithm in Example 123 on page 2088. A reference to the first object instance that has the value IntVar in the component Number is computed:
#(O1) = FirstInQueue(#(Q)); while ( #(O1) != ObjectInstanceNull () ) { if ( #(O1)->Number == #(IntVar) ) break; #(O1) = Successor(#(O1)); }
The data type becomes available by including the file containing the definition with an analyzer included in an appropriate text symbol.
Example 125 : /*#include 'byte.pr' */This file may be found in a subdirectory, called include/ADT, to the directory referenced by the environment variable sdtrelease.
Implementation:
(byte)( (b << i) & 0xFF )
Implementation: ( b >> i)
unsigned.pr unsigned_long.pr longint.prwhere three SDL sorts implemented in C as unsigned, unsigned long, and long int may be found. All these types are in SDL implemented as syntypes of integer. For more information please see the definitions of the data types.
As the data types are implemented as syntypes you have to use the idea described in the example below to define syntypes of them. The reason is that defining a syntype of a syntype is in SDL specified as being identical of defining the syntype of the originating newtype.
SYNTYPE Syntype_Name = Unsigned /*#ADT(D(H) W(H) R(H)) #HEADING #define yDef_#(Syntype_Name)(yVar) yDef_#(Unsigned)(yVar) #ifdef XREADANDWRITEF #define yWri_#(Syntype_Name) yWri_#(Unsigned) #define yRead_#(Syntype_Name) yRead_#(Unsigned) #endif */ ENDSYNTYPE;Using these types, together with the Byte type described in the previous section; and the predefined SDL types Integer, Character, Charstring and Real, the following mapping table between SDL sorts and C standard types is obtained:
--------------------------- SDL sort C type --------------------------- Character char Byte unsigned char Integer int LongInt long int Unsigned unsigned UnsignedLong unsigned long Real double Charstring char * ---------------------------Other mapping of interest are
---------------------------------------------------- SDL C ---------------------------------------------------- #UNION union (with tag) ARRAY array (if simple type as index) ARRAY link list (if complex type as index) STRING link list POWERSET bit array (if simple type as component) POWERSET linked list (if complex type as component) POINTER pointer type ----------------------------------------------------
During system start-up there is no way to obtain the PId value for a static process instance at the output that starts a communication session. The receiver of the first signal must therefore be implicit, by using an output without TO.
To be able to handle outputs without TO, in SDL-92 types and in separate generated units, complete knowledge about the structure of channels and signal routes must be known at run-time. The same knowledge is also necessary if we want to check that there is a path from the sender to the receiver in an output with TO. As the information needed about channels and signal routes requires substantial amounts of memory, it would be nice, in applications with severe memory requirements, to be able to optimize this.
To remove all information about channels and signal routes from a generated application means two things:
The first limitation, that output without TO cannot be used, is however more difficult. In an SDL system not using the OO concepts (block type, process type, and so on) and not using separate generation there are no problems, but otherwise such outputs are necessary at the system start-up phase to establish communication between processes in different blocks. The purpose of this abstract data type is to provide a way to establish PId literals and thereby to be able to avoid outputs without TO.
------------------------------------------------------------------- Note: PId literals cannot be created for processes within block types or system types. -------------------------------------------------------------------
---------------------------------------------------------------------- Caution! The PIdLit data type should only be used in the way described here to introduce synonyms referring to static process instances. Other usage may not work! If you are using this data type in a system that is to be validated us ing the SDT Validator there are two additional requirements: · Only process types with the number of instances equal to (N,N) for N>0, may be referenced in PId_Lit operators. · No process type with the number of instances equal to (N,N) for N>0, may contain a Stop symbol, independently if a PId_Lit op erator is used for the process type or not. ----------------------------------------------------------------------The data type PIdLit contains the following operators:
PId_Lit : xPrsIdNode -> PId; PId_Lit : xPrsIdNode -> PIdList; PId_Lit : xPrsIdNode, Integer -> PId;In the file containing the data type (pidlist.pr) there is also a synonym that you may use to access the environment:
SYNONYM EnvPId PId = ...;The type xPrsIdNode corresponds to the C type xPrsIdNode, which is used to refer to the process nodes in the symbol table tree built up by a generated application.
Use the first version of PId_Lit to obtain a synonym referring to the process instance of a process instance set with one initial instance.
Use the second version of PId_Lit to obtain a synonym of array type referring to the process instances of a process instance set with several initial instances.
Use the third version of PId_Lit to obtain a synonym referring to one of the process instances of a process instance set with several initial instances.
To introduce PId literals implemented as SDL synonyms, follow the steps below:
Example 126 : PIdList Data Type, Including Type /*#include 'pidlist.pr' */The path to the file should also be given. In the installation the file is placed in a subdirectory called include/ADT, to the directory referred to by the environment variable sdtrelease.
/*#CODE #HEADING extern XCONST struct xPrsIdStruct yPrsR_ProcessName1; extern XCONST struct xPrsIdStruct yPrsR_ProcessName2; extern XCONST struct xPrsIdStruct yPrsR_ProcessName3; */There should be an external definition for each process instance set identified in step 2. ProcessNameX should be replaced by the name introduced in the #NAME directives for the processes.
SYNONYM Name1 PId = PId_Lit(#CODE('&yPrsR_ProcessName1'));If the process type has several initial instances:
SYNONYM Name2 PIdList = PId_Lit(#CODE('&yPrsR_ProcessName2'));If the process type has several initial instances, but only one of them should be possible to refer to by a synonym:
SYNONYM Name3 PId = PId_Lit(#CODE('&yPrsR_ProcessName3'), No);where No should be the instance number, that is, if No is 2, then the synonym Name3 should refer to the second instance of the process type.
Example 127 : PIdList Data Type OUTPUT Sig1 TO Name1;where InstNo is an integer variable or synonym and PId_Variable is a variable of type PId.
OUTPUT Sig2 TO Name2(2);
OUTPUT Sig3 TO Name2(InstNo);
OUTPUT Sig4 TO EnvPId;
DECISION (Name3 = Sender);
TASK PId_Variable := Name2(1);
------------------------------------------------------------------- Note: Note that no index check will be performed when indexing a PIdList synonym. -------------------------------------------------------------------
The operators may be grouped into two groups:
--------------------------------------------------------------------- Caution! Be very careful using these operators, as you will then not be design ing true SDL systems. If the SDL description is a goal in itself you should not use the op erators. If the SDL system is just a means to obtain something else, an application for example, the operators may be very useful. ---------------------------------------------------------------------
/*#include 'idnode.pr'*/The path to the file should also be given. In the installation the file is placed in a subdirectory called include/ADT, to the directory referred to by the environment variable sdtrelease.
This abstract data type file introduces three SDL sorts called PrsIdNode, PrdIdNode, and SignalIdNode in SDL. These sort correspond to the types xPrsIdNode, xPrdIdNode, and xSignalIdNode in C, which are used to represent the symbol table in the generated application. The symbol table, which is a tree, will contain the static information about the SDL system during the execution of the generated program.
Is is possible to refer to processes, procedures, and signals (among others) using the following the names:
yPrsN_ProcessNamey or &PrsR_ProcessName yPrdN_ProcedureNamey or &PrdR_ProcedureName ySigN_SignalNamey or &SigR_SignalNamewhere ProcessName, ProcedureName, and SignalName should be replaced by the name of the process, procedure, or signal with prefix, or by the name given to the unit in a #NAME directive. To obtain a name of a unit with prefix the directive #SDL may be used:
yPrsN_#(ProcessName) or &yPrsR_#(ProcessName)To avoid problems when separate generation is to be used, the &yPrsR_... syntax is recommended.
The #SDL directive is not always possible to use. It will look for an entity with the specified name in the current scope unit (where the #SDL directive is used) and outwards in the scope hierarchy. So, for example, if the reference for a process is to be used in a process defined in another block, a #SDL directive cannot be used for the referenced process. The name of the referenced process ought then to be given in a #NAME directive.
If separate generation is used there may be more problems to access these references. The variables will be defined in the compilation unit where the entity they represent is defined.
Problems occur when we want to use a reference in a place where it is not visible, for example using an xPrsIdNode for a process defined in a separate block, in a process in another block. All references are, however, extern, which makes it possible for a user to introduce an appropriate extern definition (in a #CODE directive) himself in the compilation units where it is needed.
Example 128 /*#CODE #HEADING extern XCONST struct xPrsIdStruct yPrsR_ProcessName; */To know the name of the referenced process, a #NAME directive ought to be used.
-------------------------------------------------------------------- Note: Such extern definitions introduce dependencies between otherwise independent compilation units. It is your responsibility completely to maintain these dependencies. --------------------------------------------------------------------
---------------------------------------------------------------------- Note: During the enumeration of the process instances, no action that stops any instance of the enumerated process type may be executed. This means, for example, that the complete enumeration should take place in one transition and that Kill operations should not be used in the enumeration. ----------------------------------------------------------------------
-------------------------------------------------------------------- Note: When you use this operator you hide signal sending in an expression in, for example, a task. This will decrease the readability of your SDL description, and should be well documented, at least with a comment. --------------------------------------------------------------------
------------------------------------------------------------------- Note: These operator has no meaning in the SDT Validator. It can be used but will in the Validator be a null action. -------------------------------------------------------------------The operator takes a reference to an IdNode (of one of the three type above) that represents a process, a procedure, or a signal and returns the memory in the avail list for the specified IdNode to the free memory by calling the sctOS function xFree. The function xFree uses the C standard function 'free' to release the memory. The FreeAvailList operator requires thus that free really releases the memory in such a way that it can be reused in subsequent memory allocations. Otherwise the operator is meaningless.
FreeAvailList is intended to be applied for reusing memory allocated for processes, procedures, and signals used only during a start-up phase. If the system, for example, contains a process type used only during start-up, that is, all instances of this type perform stop actions early during the execution and no more processes will be created later, then the memory for these instances can be reused.
-------------------------------------------------------------------- Caution! This operator should only be used as one of the last resorts in the process of minimizing the memory requirements of an application. --------------------------------------------------------------------
--------------------------------------------------------------- Note: The generator POINTER is not intended to be used in the SDT Val idator! ---------------------------------------------------------------The POINTER generator may be used to create pointer types in SDL according to the following example:
Example 129 : Pointer Type, Creating NEWTYPE ptr POINTER(Charstring) ENDNEWTYPE;The sort ptr is now a pointer to Charstring.
A pointer type created by the generator POINTER contains a literal value null, which is the default value of all variables of pointer types. There will also be two operators:
OPERATORS Addr : S -> POINTER; Value : POINTER -> S;where POINTER is the pointer type and S in the type pointer to. These operators may be used to convert between pointer and the value pointed to according to the following example.
Example 130 : Pointer Type, Conversion NEWTYPE ptr POINTER(Charstring) ENDNEWTYPE; DCL p ptr, c Charstring; TASK p := Addr(c), c := Value(p);The operator Addr is implemented as the C operator &, while the operator Addr is implemented as the C operator *.
We will below present two application areas for pointer types: optimizing parameters passing to, for example, operators, and building linked lists.
It is of course also possible to pass pointer values as parameters in signals, in create operations, and in procedure calls in the same way as described for operators above.
This is not especially useful in procedure calls, as IN/OUT parameters to procedures serve exactly this purpose.
------------------------------------------------------------------------ Caution! In create and output operations, passing an address is potentially very risky and should be used with extreme care. A signal is, for example, not necessarily received and treated at the same time it is sent. If the contents of the address is updated between the signal is sent and received, these updates will be seen by the re ceiving process. ------------------------------------------------------------------------
Example 131 : Passing Address as Operator Parameter SYNONYM MaxIndex Integer = 999; SYNTYPE IndexSort = Integer CONSTANTS 0:MaxIndex ENDSYNTYPE; NEWTYPE Arr ARRAY(IndexSort, Integer) ADDING OPERATORS EqualEntries : Arr, Arr -> Integer; /*#ADT(HP) #HEADING extern SDL_Integer #(EqualEntries) XPP((#(Arr) P1, #(Arr) P2)); #BODY #ifndef XNOPROTO extern SDL_Integer #(EqualEntries) (#(Arr) P1, #(Arr) P2) #else extern SDL_Integer #(EqualEntries) (P1, P2) #(Arr) P1; #(Arr) P2; #endif { SDL_Integer No = 0; SDL_Integer I; for (I = 0; I <= #(MaxIndex); I++) if (P1.A[I] == P2.A[I]) No++; return No; } */ ENDNEWTYPE;XPP and XNOPROTO are used to handle compilers that accept or do not accept function prototypes.
The array type Arr is defined as an array with 1000 elements and the operator EqualEntries take two such arrays and return the number of entries that are equal in the two arrays; that is, for how many indexes P1(Index)is equal to P2(Index). An example of an operator application is:
Example 132 : Passing Address as Operator Parameter DCL I Integer, A1, A2 Arr; TASK I := EqualEntries(A1, A2);When EqualEntries is called the two array parameters are copied, as the parameter types are the array type. For this operator it is unnecessary to copy the parameters, as parameters are never changed. Passing the addresses of the parameters instead of copying the arrays would increase the speed substantially.
Consider the following definition of the array type:
Example 133 : Passing Address as Operator Parameter SYNONYM MaxIndex Integer = 999; SYNTYPE IndexSort = Integer CONSTANTS 0:MaxIndex ENDSYNTYPE; NEWTYPE ArrAddr POINTER(Arr) ENDNEWTYPE; NEWTYPE Arr ARRAY(IndexSort, Integer) ADDING OPERATORS EqualEntries : ArrAddr, ArrAddr -> Integer; /*#ADT(HP) #HEADING extern SDL_Integer #(EqualEntries) XPP((#(ArrAddr) P1, #(ArrAddr) P2)); #BODY #ifndef XNOPROTO extern SDL_Integer #(EqualEntries) (#(ArrAddr) P1, #(ArrAddr) P2) #else extern SDL_Integer #(EqualEntries) (P1, P2) #(ArrAddr) P1; #(ArrAddr) P2; #endif { SDL_Integer No = 0; SDL_Integer I; for (I = 0; I <= #(MaxIndex); I++) if ((*P1).A[I] == (*P2).A[I]) No++; return No; } */ ENDNEWTYPE;The major difference from the previous version is the introduction of the type ArrAddr, which is a pointer to Arr.
If we now look at the Arr type, the EqualEntries operator now takes two ArrAddr parameters. In the implementation of EqualEntries we now have addresses to Arr:s as parameters and the code is modified to handle this.
This abstract data type may now be used as follows:
DCL I Integer, A1, A2 Arr; TASK I := EqualEntries(Addr(A1), Addr(A2));Now it is only the addresses to the arrays that are passed to EqualEntries.
What is it that we lose by working with pointers? We lose the ability of the operator to take other actual parameters than variables. In the first version of Arr the call:
I := EqualEntries( A1, (. 1 .) );is valid and will count the number of components in A1 that have the value 1. If we try the same thing in the second version of Arr; that is, if we write:
I := EqualEntries( Addr(A1), Addr( (. 1 .) ) );then there will be a C compilation error, as it is not possible to take the address of a function result. The application of the make operator, (. 1 .), will be implemented as a yMake function and we try to take the address of the result of such a function call, which is illegal in C.
This is the reason why the C Code Generator cannot always work with addresses instead of values; the restrictions for the allowed parameters to operators would be too severe.
Assume that we want to create a linked list containing integer values, then we define the following sorts:
Example 134 : Linked Structure NEWTYPE Item STRUCT Element Integer; Next ItemPointer; ENDNEWTYPE; NEWTYPE ItemPointer POINTER(Item); /*#ADT(() #TYPE #define yAddr_#(ItemPointer)(VAR, ref) (*(VAR)) #define yExtr_#(ItemPointer)(VAR, ref) (*(VAR)) */ ENDNEWTYPE;The #ADT section in the pointer type is introduced to simplify accessing components of an Item struct directly from an ItemPointer, see the examples below.
Example 135Assume the sorts in the previous example.
DCL sum Integer; a,b,c Item, p ItemPointer; TASK /* Create a linked list of a, b, c */ a!Element := 1, b!Element := 2, c!Element := 3, a!Next := Addr(b), b!Next := Addr(c), c!Next := NULL; TASK /* add Item value in list starting with a */ sum := 0, p := Addr(a); again: DECISION (p /= NULL); (true) : TASK sum := sum + p!ref!Element p := p!ref!Next; JOIN again; ELSE: ENDDECISION;-------------------------------------------------------------- Note: The way to refer to a struct component directly for a pointer: p!ref!Element p!ref!Next --------------------------------------------------------------
-------------------------------------------------------- Sim. Real- Perf. Sim. Appl. Valid. Time Sim. with Sim. env. -------------------------------------------------------- list1, list2 3 3 3 3 3 ? file 3 3 3 3 * * random 3 3 3 3 3 * pidlist 3 3 3 3 3 * idnode 3 3 3 3 3 3 byte 3 3 3 3 3 3 longint 3 3 3 3 3 3 unsigned 3 3 3 3 3 3 unsigned_long 3 3 3 3 3 3 pointer 3 3 3 3 3 ? --------------------------------------------------------Table Legend:
3Compatible
?Incompatible
*Meaningless combination or restrictions. See respective section for more information.
This page intentionally left blank
Table of Contents Next Chapter