Manual for the Seed7 programming language
Seed7 is a general-purpose programming language. It is a higher level language compared to Ada, C++ and Java. In Seed7 new statements and operators can be declared easily. Functions with type results and type parameters are more elegant than the usual template or generics concept. Object orientation is used when it brings advantages and not in places when other solutions are more obvious. Although Seed7 contains several concepts of other programming languages it is generally not considered as a direct descendant of any other programming language.
The programmer should concentrate on problem solving instead of administration or the fulfillment of some paradigm. Therefore Seed7 allows programming in the "problem space" instead of bending everything into a small syntactic or semantic concept. The predefined constructs of Seed7 are defined in a way to be easy readable and understandable. This practical approach can be summarized as:
"Programming should be fun"
Seed7 programs can be interpreted or compiled. Therefore Seed7 can be used for scripting and for "real" programs.
1.2 Why a new programming language?Conventional programming languages have a firmly given syntactic structure. The form of the statements, operators, declarations, procedures and functions is fixed in the language definition and cannot be changed by the user. It is only possible to declare new procedures, functions and in some languages also new operators. However the syntax of procedure-, function and operator calls cannot be changed. Although this rigid pattern is favorable for the portability of programs, the improvement of a programming language is almost impossible. Extensions are however desirable, in order to repair existing weaknesses, to introduce new more obvious constructs and to adapt the programming language to different application areas. E.g.: In the area of mathematics the readability of a program can be substantially increased by the introduction of matrix and vector operators. After declaring an inner product and an outer (or cross) product for vectors it is possible to write e.g.
v1: = v2 cross v3; write(v1 * v2);
Programs searching some data can become more understandable by using a search statement instead of a search procedure. A call of a new declared search statement could be:
search person1.age = person2.age and person1.mother = person2.mother and person1 <> person2 when found write("Twins: " <& person1.name <& " and " <& person2.name); else write("No twins found."); end search;
Such extensions make understanding, changing and debugging of a program easier.
1.3 Features of Seed7Seed7 has the following features
But a new programming language differs not only from existing ones by new features. The real advantage comes from omitting features which are outdated.
Several concepts in use by other languages are not present
There are several concepts which are also used by other languages:
There are several concepts which are new
Several restrictions of other languages are released
You can have several views of the Seed7 programming language. Dependent on the view you can concentrate on specific chapters.
For example Seed7 can be used as conventional programming language. In this case you are interested in how the statements look like, which types are available, which operators are predefined, how to declare variables and procedures and other things like these. The statements and the predefined types are described in chapter 4 (Predefined statements) and chapter 5 (Predefined types) and the declaration mechanism is described in chapter 3 (Declarations).
But Seed7 is also an object oriented programming language. In this case you are interested in how to define new classes, how instances are generated, the method calling mechanism, the predefined class hierarchy and other things like these. The object orientation of Seed7 is described in chapter 7 (Object orientation). A good example for classes and instances is the file system which is described in chapter 8 (The file system).
And Seed7 is also an extensible programming language. In this case you are interested in how to declare new statements, how to define new operators, assigning a priority and an associativity to operators and other things like these. An overview about syntax declarations can be found in Chapter 3.2 (Syntax declarations). A detailed description of the Seed7 syntax definitions can be found in chapter 9 (Structured syntax definition). Chapter 4 (Predefined statements) contains various examples of syntax and semantic declarations. The basic parts of the syntax are described in chapter 10 (Tokens) and chapter 11 (Expressions).
2. TUTORIALWe begin with a tutorial introduction to Seed7. In this chapter we want to show the principal ideas that make Seed7 work. At this point, we are not trying to be complete or precise. We just want to give a clear view to the primary philosophic ideas of Seed7. When the primary ideas are understood a complete and precise reference can be learned easier.
2.1 Hello worldA Seed7 program consists of a sequence of declarations. With each declaration a type and a name is attached to the new object. In addition every new declared object gets an initial value.
Here is an example of an object declaration:
const proc: main is func begin writeln("hello world"); end func;
The object 'main' is declared as constant and 'proc' is the type of 'main'. Declaring 'main' with the type 'proc' makes a procedure out of it. The object 'main' gets a
func ... end func
construct as value. The 'func' construct is similar to begin ... end in PASCAL and { ... } in C. Inside the 'func' is a 'writeln' statement with the "hello world" string. The 'writeln' statement is used to write a string followed by a newline character. To use this declaration as the standard hello world example program, we have to add a few things:
$ include "seed7_05.s7i"; const proc: main is func begin writeln("hello world"); end func;
The first line includes all definitions of the standard library. In contrast to other standard libraries the seed7_05.s7i library contains not only function declarations but also declarations of statements and operators. Additionally the seed7_05.s7i library defines the 'main' function as entry point for a Seed7 program.
If you write this program in a file called hello.sd7 and execute the command
hi hello
The Seed7 interpreter writes something like
HI INTERPRETER Version 4.5.79 Copyright (c) 1990-2005 Thomas Mertes 245 syntax.s7i 2635 seed7_05.s7i 33 hello.sd7 2913 lines total 29130 lines per second 1184171 bytes hello world
You get information about the Seed7 interpreter, a list of libraries included and how many lines they contain, the number of bytes used by the hello.sd7 program and finally the output of the hello.sd7 program itself:
hello world2.2 Local declarations and expressions
To write a Fahrenheit to Celsius conversion table we use the following program:
(* Print a Fahrenheit-Celsius table for Fahrenheit values between 0 and 300 *) $ include "seed7_05.s7i"; const proc: main is func local const integer: lower is 0; const integer: upper is 300; const integer: increment is 20; var integer: fahr is 0; var integer: celsius is 0; begin fahr := lower; while fahr <= upper do celsius := 5 * (fahr - 32) div 9; write(fahr); write(" "); writeln(celsius); fahr := fahr + increment; end while; end func;
Everything between (* and *) is a comment which is ignored. This program contains local constants and variables of the type 'integer'. The constants and variables must be initialized when they are declared. This program contains also an assignment, a while loop and the expression to compute the 'celsius' value. Note that the statements inside the 'while' loop are between 'do' and 'end while'. The expression to compute the 'celsius' value uses an integer division ('div'). The 'write' statement can be used to write strings and integers without a newline character. The output produced by this program is
0 -17 20 -6 40 4 60 15 80 26 100 37 120 48 140 60 160 71 180 82 200 93 220 104 240 115 260 126 280 137 300 1482.3 For loop and float expressions
An improved version of the program to write the Fahrenheit to Celsius conversion table is:
$ include "seed7_05.s7i"; include "float.s7i"; const proc: main is func local const integer: lower is 0; const integer: upper is 300; const integer: increment is 20; var integer: fahr is 0; var float: celsius is 0.0; begin for fahr range lower to upper step increment do celsius := flt(5 * (fahr - 32)) / 9.0; writeln(fahr lpad 3 <& " " <& celsius digits 2 lpad 6); end for; end func;
To use the type 'float' it is necessary to include "float.s7i". The 'float' variable 'celsius' must be initialized with 0.0 (instead of 0). The 'for' loop is written as:
for ... range ... to ... step ... do ... end for
To specify a lower and an upper limit together with a step value. For a step value of 1 the for loop it is written as:
for ... range ... to ... do ... end for
And for a step value of -1 it can be written as:
for ... range ... downto ... do ... end for
Since Seed7 is strong typed 'integer' and 'float' values cannot be mixed in expressions. Therefore the 'integer' expression '5 * (fahr - 32)' is converted to 'float' with the 'flt' function. For the same reason a '/' division and the value '9.0' must be used. The '<&' operator is used to concatenate elements before writing. If the right operand of the '<&' operator has not the type 'string' it is converted to a 'string' using the 'str' function. The 'lpad' operator converts the value of 'fahr' to a string and pads spaces to the left until the string has length 3. The 'digits' operator converts the value of 'celsius' to a string with 2 decimal digits. The resulting string is padded left up to a length of 6.
2.4 ParametersMost parameters are not changed inside a function. To express this explicit Seed7 uses the 'in' parameter as in the following examples:
const func integer: negate (in integer: num1) is return -num1; const func integer: fib (in integer: num1) is func result var integer: result is 1; begin if num1 <> 1 and num1 <> 2 then result := fib(pred(num1)) + fib(num1 - 2); end if; end func;
In both cases the formal parameter 'num1' is used in the function but no assignment is done to 'num1'. Inside the functions the parameter 'num1' behaves like a constant.
When a function should change the value of the actual parameter we can use the 'inout' parameter as in the following example:
const proc: reset (inout integer: num2) is func begin num2 := 0; end func;
If you call this function with
reset(number)
the variable 'number' has the value 0 afterwards. By the way, writing 'in' instead of 'inout' would have been illegal in the example above.
Sometimes an 'in' parameter is needed, but you need to change the formal parameter in the function without affecting the actual parameter. In this case we use the 'in var' parameter:
const func string: oct_str (in var integer: number) is func result var string: result is ""; begin while number >= 0 do result := str(number rem 8) & result; number := number div 8; end while; end func;
As you can see this works like a combination of an 'in' parameter with a local 'var'.
Conventionally there are two kinds of parameters: 'call by value' and 'call by reference'. When taking the access right (constant or variable) into account we get the following table:
+-----------+-----------+--------------+ | parameter | call by | access right | +-----------+-----------+--------------+ | val | value | const | | ref | reference | const | | in | val / ref | const | | in var | value | var | | inout | reference | var | +-----------+-----------+--------------+
Additionally to the parameters we already know this table describes also 'val' and 'ref' parameters which use 'call by value' and 'call by reference' and have a constant formal parameter. The 'in' parameter is called by 'val / ref' in this table which is easily explained:
An 'in' parameter is either a 'val' or a 'ref' parameter depending on the type of the parameter.
The parameter
in integer: number
is a 'val' parameter which could also be declared as
val integer: number
while the parameter
in string: stri
is a 'ref' parameter which could also be declared as
ref string: stri
The meaning of the 'in' parameter is predefined for most types. Usually types with small amounts of data use 'val' as 'in' parameter while types with bigger data amounts use 'ref'. Most of the time it is not necessary to care if an 'in' parameter is really a 'val' or 'ref' parameter.
In rare cases a 'ref' parameter would have undesired side effects with global variables or other 'ref' parameters. In these cases an explicit 'val' parameter instead of an 'in' parameter makes sense.
In all normal cases an 'in' parameter should be preferred over an explicit 'val' and 'ref' parameter.
2.5 Declare a statementThis example program writes its arguments
$ include "seed7_05.s7i"; # Standard Seed7 library const proc: main is func local var string: stri is ""; begin for stri range argv(PROGRAM) do write(stri <& " "); end for; writeln; end func;
The 'for' statement iterates over 'argv(PROGRAM)'. The 'argv(PROGRAM)' function returns an 'array string' (=array of string elements). The 'for' statement is overloaded for various collection types. In the standard Seed7 library "seed7_05.s7i" the 'for' statement for arrays is declared as follows:
const proc: for (inout baseType: variable) range (in arrayType: arr_obj) do (in proc: statements) end for is func local var integer: number is 0; begin for number range 1 to length(arr_obj) do variable := arr_obj[number]; statements; end for; end func;
The syntax of this 'for' statement is declared as:
$ syntax expr: .for.().range.().to.().do.().end.for is -> 25;
Additionally everybody can overload the 'for' statement also. Because of these powerful features Seed7 does not need Iterators.
2.6 Template declaring a statementTemplates are just normal functions with types as parameters. The following template function declares 'for' statements:
const proc: FOR_DECLS (in type: aType) is func begin const proc: for (inout aType: variable) range (in aType: low) to (in aType: high) do (in proc: statements) end for is func begin variable := low; if variable <= high then statements; while variable < high do incr(variable); statements; end while; end if; end func; end func; FOR_DECLS(char); FOR_DECLS(boolean);
The body of the 'FOR_DECLS' function contains a declaration of the 'for' statement for the type aType. Calling 'FOR_DECLS' with char and boolean as parameter creates corresponding declarations of 'for' statements. The example above is a simplified part of the standard Seed7 library "seed7_05.s7i".
3. DECLARATIONSA declaration specifies the identifier, type, and other aspects of language elements such as variables, constants and functions. In Seed7 everything must be declared before it is used. Seed7 uses three kinds of declarations:
which are described in detail in the following subchapters.
3.1 Normal declarationsNormal declarations are the most commonly used form of declarations. To contrast them to the syntax declarations normal declarations are sometimes called semantic declarations. Seed7 uses uniform looking declaration constructs to declare variables, constants, types, functions and parameters. For example:
const integer: ONE is 1;
declares the 'integer' constant 'ONE' which is initialized with the value 1. Variable declarations are also possible. For example:
var integer: number is 0;
declares the 'integer' variable 'number' which is initialized with the value 0. Type declarations are done as constant declarations where the type of the declared constant is 'type':
const type: myChar is char;
Function declarations are also a form of constant declaration:
const func boolean: flipCoin is return rand(FALSE, TRUE);
Each object declared with a 'const' or 'var' declaration obtains an initial value. It is not possible to use 'const' or 'var' declarations without initial value. Declarations with initialisation expressions are also possible. For example
var string: fileName is NAME & ".txt";
The expression is evaluated and the result is assigned to the new object. This is done in the analyze phase of the interpreter or compiler, before the execution of the program starts. The initialisation expressions may contain any function (or operator) call. That way user defined functions can also be used to initialize a constant or variable:
const boolean: maybe is flipCoin;
Constant and variable declarations can be global or local. The mechanism to define a parameter like 'x' is similar to the 'const' or 'var' declarations:
const func float: inverse (in float: x) is return 1/x;
Function parameters, such as the parameter 'statement' in the example below, act as closures:
const proc: possiblyDo (in proc: statement) is func begin if flipCoin then statement; end if; end func;
Abstract data types such as 'subtype', 'struct', 'subrange', 'array', 'hash', 'set', 'interface' and 'enum' are realized as functions which return a type. E.g.: The type 'array' is defined in the "seed7_05.s7i" library with the following header:
const func type: array (in type: baseType) is func
User defined abstract data types are also possible.
The initialisation uses the creation operation ( ::= ). Explicit calls of the create operation are not needed.
The lifetime of an object goes like this:
The first three steps are usually hidden in the declaration statement. The expression
ONE . ::= . 1
is executed to assign 1 to the object ONE. There are two reasons to use ::= instead of := to assign the initialisation value.
For all predefined types the creation operator ( ::= ) is already defined. To allow the declaration of objects of a new user defined type the constructor operation for this type must be defined.
3.2 Syntax declarationsSyntax declarations are used to specify the syntax, priority and associativity of operators, statements, declarations and other constructs. A syntax declaration which defines the '+' operator is:
$ syntax expr: .(). + .() is -> 7;
Most syntax definitions can be found in the file "syntax.s7i". A detailed description of the syntax declarations can be found in chapter 9 (Structured syntax definition) There is also a hard coded syntax for function calls with a parenthesis enclosed parameter list where the parameters are separated by commas. The hard coded syntax is described in chapter 11 (Expressions). Here we use a more complex syntax description:
3.3 System declarationsWith system declarations the analyzer and the interpreter are informed about which objects should be used for various system internal purposes. An example of a system declaration is
$ system "integer" is integer;
This defines that the type of all integer literals is 'integer'. Additionally 'integer' is used as type for all integers generated by primitive actions. There are different objects which are defined by a system declaration
TRUE, FALSE and empty
NUMERIC_ERROR and MEMORY_ERROR
:= ::= 'destroy' 'write' and 'flush'
The following system declarations exist
$ system "type" is type; $ system "expr" is expr; $ system "integer" is integer; $ system "char" is char; $ system "string" is string; $ system "proc" is proc; $ system "float" is float; $ system "true" is TRUE; $ system "false" is FALSE; $ system "empty" is empty; $ system "memory_error" is MEMORY_ERROR; $ system "numeric_error" is NUMERIC_ERROR; $ system "range_error" is RANGE_ERROR; $ system "io_error" is IO_ERROR; $ system "illegal_action" is ILLEGAL_ACTION; $ system "assign" is := ; $ system "create" is ::= ; $ system "destroy" is destroy; $ system "ord" is ord; $ system "in" is in; $ system "prot_outfile" is PROT_OUTFILE; $ system "flush" is flush; $ system "write" is write; $ system "writeln" is writeln; $ system "main" is main;4. PREDEFINED STATEMENTS
The library contains several predefined statements: assignment, while-statement, repeat-statement, for-statement, if-statement, case-statement and procedure call.
Syntax:
statement ::= single_statement [ ';' [ statement ] ] . single_statement ::= assignment_statement | while_statement | repeat_statement | for_statement | if_statement | case_statement | procedure_call | empty_statement . empty_statement ::= 'noop' .
Everywhere where one statement can be written a sequence of statements can also be used. The semicolon-operator concatenates two statements giving a new statement. The semicolon operator can also be used behind the last statement of a statement sequence. In this case the semicolon is just ignored.
Declaration:
$ syntax expr: (). ; .() is <- 50; $ syntax expr: (). ; is <- 50 [1]; const proc: (ref void param) ; (ref void param) is noop;4.1 Assignment
For example:
minimum := maximum div 2;
Syntax:
assignment_statement ::= designator ':=' expression .
The assignment statement is defined for every standard type.
If an assignment for a new user defined type is needed it must be defined additionally.
Declaration:
$ syntax expr: (). := .() is <-> 20; const proc: (inout type param) := (ref type param) is action "TYP_CPY"; const proc: (inout proc param) := (ref proc param) is action "PRC_CPY"; const proc: (inout func aType param) := (ref func aType param) is action "PRC_CPY"; const proc: (inout varfunc aType param) := (ref varfunc aType param) is action "PRC_CPY"; const proc: (inout ACTION param) := (in ACTION param) is action "ACT_CPY"; const proc: (inout boolean param) := (in boolean param) is action "BLN_CPY"; const proc: (inout integer param) := (in integer param) is action "INT_CPY"; const proc: (inout char param) := (ref char param) is action "CHR_CPY"; const proc: (inout string param) := (ref string param) is action "STR_CPY"; const proc: (inout reference param) := (ref reference param) is action "REF_CPY"; const proc: (inout ref_list param) := (in ref_list param) is action "RFL_CPY"; const proc: (inout ptrType param) := (in ptrType param) is action "REF_CPY"; const proc: (inout varptrType param) := (in varptrType param) is action "REF_CPY"; const proc: (inout arrayType param) := (in arrayType param) is action "ARR_CPY"; const proc: (inout bitset param) := (in bitset param) is action "SET_CPY"; const proc: (inout structType param) := (in structType param) is action "SCT_CPY"; const proc: (inout enumType param) := (in enumType param) is action "ENU_CPY"; const proc: (inout PRIMITIVE_FILE param) := (ref PRIMITIVE_FILE param) is action "FIL_CPY"; const proc: (inout file param) := (ref file param) is action "CLS_CPY"; const proc: (inout file param) := (ref null_file param) is action "CLS_CPY2"; const proc: (inout file param) := (ref external_file param) is action "CLS_CPY2";4.2 while-statement
For example:
while maximum > minimum do minimum := 2 * minimum + stepValue; decr(stepValue); end while;
Syntax:
while_statement ::= 'while' expression 'do' statement 'end' 'while' .
The expression must be of type 'boolean'.
Declaration:
$ syntax expr: while.().do.().end.while is -> 25; const proc: while (ref func boolean param) do (ref proc param) end while is action "PRC_WHILE"; const proc: while (ref boolean param) do (ref proc param) end while is action "PRC_WHILE";
Alternate declaration:
const proc: while (ref func boolean: condition) do (ref proc: statement) end while is func begin if condition then statement; while condition do statement; end while; end if; end func;4.3 repeat-statement
For example:
repeat incr(minimum); maximum := maximum - stepValue; until 2 * minimum > maximum;
Syntax:
repeat_statement ::= 'repeat' statement 'until' expression .
The expression must be of type 'boolean'.
Declaration:
$ syntax expr: repeat.().until.() is -> 25; const proc: repeat (ref proc param) until (ref func boolean param) is action "PRC_REPEAT"; const proc: repeat (ref proc param) until (ref boolean param) is action "PRC_REPEAT";
Alternate declaration:
const proc: repeat (ref proc: statement) until (ref func boolean: condition) is func begin statement; if not condition then repeat statement; until condition; end if; end func;4.4 for-statement
For example:
for index range min_index to max_index do sumValue +:= field[index]; end for;
Syntax:
for_statement ::= 'for' identifier 'range' expression [ 'to' | 'downto' ] expression 'do' statement 'end' 'for' .
Declaration:
$ syntax expr: for.().range.().to.().do.().end.for is -> 25; $ syntax expr: for.().range.().downto.().do.().end.for is -> 25; const proc: FOR_DECLS (in type: aType) is func begin const proc: for (inout aType: variable) range (in aType: lower_limit) to (in aType: upper_limit) do (in proc: statements) end for is func begin variable := lower_limit; if variable <= upper_limit then statements; while variable < upper_limit do incr(variable); statements; end while; end if; end func; const proc: for (inout aType: variable) range (in aType: upper_limit) downto (in aType: lower_limit) do (in proc: statements) end for is func begin variable := upper_limit; if variable >= lower_limit then statements; while variable > lower_limit do decr(variable); statements; end while; end if; end func; end func; FOR_DECLS(integer); FOR_DECLS(char); FOR_DECLS(boolean);4.5 for-iterator-statement
For example:
for currObject range element_list do result &:= " " & str(currObject); end for;
Syntax:
for_statement ::= 'for' identifier 'range' expression 'do' statement 'end' 'for' .
Declaration:
$ syntax expr: .for.().range.().do.().end.for is -> 25; const proc: for (ref reference param) range (ref ref_list param) do (ref proc param) end for is action "RFL_FOR"; const proc: for (inout baseType: variable) range (in arrayType: arr_obj) do (in proc: statements) end for is func local var integer: number is 0; begin for number range 1 to length(arr_obj) do variable := arr_obj[number]; statements; end for; end func; const proc: for (inout baseType: variable) range (in setType: a_set) do (in proc: statements) end for is func begin for variable range min(a_set) to max(a_set) do if variable in a_set then statements; end if; end for; end func;4.6 if-statement
For example:
if sumValue < minimum then factor := sumValue; sumValue := minimum; elsif sumValue > maximum then factor := -sumValue; sumValue := maximum; else factor := 0; end if;
Syntax:
if_statement ::= 'if' expression 'then' statement { 'elsif' expression 'then' statement } [ 'else' statement ] 'end' 'if' .
The expression must be of type 'boolean'.
Declaration:
$ syntax expr: .if.().then.().end.if is -> 25; $ syntax expr: .if.().then.().().end.if is -> 25; $ syntax expr: .elsif.().then.() is <- 60; $ syntax expr: .elsif.().then.().() is <- 60; $ syntax expr: .else.() is <- 60; const type: ELSIF_RESULT is newtype; const proc: (ref ELSIF_RESULT param) ::= enumlit is action "ENU_GENLIT"; const ELSIF_RESULT: ELSIF_EMPTY is enumlit; const type: ELSIF_PROC is (func ELSIF_RESULT); const proc: (ref ELSIF_PROC param) ::= (ref ELSIF_RESULT param) is action "ENU_CREATE"; const proc: if (in boolean param) then (in proc param) end if is action "PRC_IF"; const proc: if (in boolean param) then (in proc param) (in ELSIF_PROC param) end if is action "PRC_IF_ELSIF"; const ELSIF_PROC: elsif (in boolean param) then (in proc param) is action "PRC_IF"; const ELSIF_PROC: elsif (in boolean param) then (in proc param) (in ELSIF_PROC param) is action "PRC_IF_ELSIF"; const ELSIF_PROC: else (in void param) is ELSIF_EMPTY; const proc: if TRUE then (in void param) end if is noop; const proc: if TRUE then (in void param) (in ELSIF_PROC param) end if is noop; const proc: if FALSE then (in proc param) end if is noop; const proc: if FALSE then (in proc param) (in ELSIF_RESULT param) end if is noop; const ELSIF_PROC: elsif TRUE then (in void param) is ELSIF_EMPTY; const ELSIF_PROC: elsif TRUE then (in void param) (in ELSIF_PROC param) is ELSIF_EMPTY; const ELSIF_PROC: elsif FALSE then (in proc param) is ELSIF_EMPTY; const ELSIF_PROC: elsif FALSE then (in proc param) (in ELSIF_RESULT param) is ELSIF_EMPTY;4.7 case-statement
For example:
case currChar of when {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}: characterClass := LETTER; when {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}: characterClass := DIGIT; when {'!', '$', '%', '&', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '\', '^', '`', '|', '~'}: characterClass := SPECIAL; when {'(', ')', '[', ']', '{', '}'}: characterClass := PAREN; when {'"'}: # Also possible '\"' characterClass := APPOSTROPHE; when {'''}: # Also possible '\'' characterClass := QUOTE; otherwise: characterClass := ILLEGAL; end case;
Syntax:
case_statement ::= 'case' expression 'of' { 'when' set_expression ':' statement } [ 'otherwise' ':' statement ] 'end' 'case' .
Declaration:
$ syntax expr: .case.().of.().end.case is -> 25; $ syntax expr: .case.().of.().otherwise. : .().end.case is -> 25; $ syntax expr: .case.().of.end.case is -> 25; $ syntax expr: .when.(). : .().() is <- 60; $ syntax expr: .when.(). : .() is <- 60; const proc: CASE_DECLS (in type: aType) is func local var type: WHEN_RESULT is void; var type: WHEN_PROC is void; var type: SELECTOR_TYPE is void; begin WHEN_RESULT := newtype; WHEN_PROC := (func WHEN_RESULT); SELECTOR_TYPE := set of aType; const proc: case (ref aType param) of end case is noop; const proc: case (ref aType param) of (ref WHEN_PROC param) end case is action "PRC_CASE"; const proc: case (ref aType param) of (ref WHEN_PROC param) otherwise : (ref proc param) end case is action "PRC_CASE_DEF"; const proc: (ref WHEN_RESULT param) ::= enumlit is action "ENU_GENLIT"; const WHEN_RESULT: WHEN_EMPTY (attr aType) is enumlit; const proc: (ref WHEN_PROC param) ::= (ref WHEN_RESULT param) is action "ENU_CREATE"; const WHEN_PROC: when (ref SELECTOR_TYPE param) : (ref proc param) is WHEN_EMPTY(aType); const WHEN_PROC: when (ref SELECTOR_TYPE param) : (ref proc param) (ref WHEN_PROC param) is WHEN_EMPTY(aType); end func; CASE_DECLS(integer); CASE_DECLS(char);5. PREDEFINED TYPES
In the following subchapters the predefined types of the standard library are introduced. The operators have, when not stated otherwise, the type described in the subchapter as parameter type and result type. The relations have also the type described in the subchapter as parameter type and a result of type 'boolean'. In the descriptions => is used to show an equivalent expression.
5.1 booleanThe type 'boolean' consists of the two truth values TRUE and FALSE.
Prefix operators: not Negation ( not TRUE => FALSE, not FALSE => TRUE ) Infix operators: and Logical and ( TRUE and TRUE => TRUE, A and B => FALSE else ) or Inclusive logical or ( FALSE or FALSE => FALSE, A or B => TRUE else ) boolean conv A Conversion of integer to boolean ( Type of argument A: integer, boolean conv 0 => FALSE, boolean conv 1 => TRUE ) boolean parse A Conversion of string to boolean ( Type of argument A: string, boolean parse "FALSE" => FALSE, boolean parse "TRUE" => TRUE, boolean parse "TRUE " => EXCEPTION RANGE_ERROR, boolean parse "ASDF" => EXCEPTION RANGE_ERROR ) Relations: =, <>, >, >=, <, <= ( A relation B => ord(A) relation ord(B) ) Functions: ord(A) Ordinal number ( Type of result: integer, ord(FALSE) => 0, ord(TRUE) => 1 ) succ(A) Successor ( succ(FALSE) => TRUE, succ(TRUE) => EXCEPTION RANGE_ERROR ) pred(A) Predecessor ( pred(FALSE) => EXCEPTION RANGE_ERROR ) pred(TRUE) => FALSE ) str(A) Conversion to string ( Type of result: string, str(FALSE) => "FALSE", str(TRUE) => "TRUE" ) rand(A, B) Random value in the range [A, B] The random values are uniform distributed. ( rand(A, B) returns a random value such that A <= rand(A, B) and rand(A, B) <= B holds. rand(A, A) => A, rand(TRUE, FALSE) => EXCEPTION RANGE_ERROR ) compare(A, B) Compare function ( Type of result: integer, compare(FALSE, TRUE) => -1, compare(TRUE, TRUE) => 0, compare(TRUE, FALSE) => 1 ) hashCode(A) Hash function ( Type of result: integer ) Statements: incr(A) Increment ( incr(A) => A:=succ(A) ) decr(A) Decrement ( decr(A) => A:=pred(A) )
The logical operators 'and' and 'or' work strictly left to right. First they evaluate the left operand and then the right operand. When the result of the operation can be determined after evaluation of the left operand the right operand is not evaluated. This can be used to check for a boundary in a boolean expression. Naturally side effects of the right operand of the 'and' and 'or' operator only take place when the operand is executed.
Table for the behaviour of different boolean expressions:
Result when the Result when the Expression first operand first operand evaluates to FALSE evaluates to TRUE not A TRUE FALSE A and B respectively not((not A)or(not B)) FALSE B A or B respectively not((not A)and(not B)) B TRUE A > B respectively A and(not B) FALSE not B A >= B respectively A or(not B) not B TRUE A < B respectively (not A)and B B FALSE A <= B respectively (not A)or B TRUE B not (A and B) respectively (not A)or(not B) TRUE not B not (A or B) respectively (not A)and(not B) not B FALSE
Optimizing boolean expressions:
When the result of a boolean expression can be determined at compile time, the expression can be replaced by a constant. Additionally the following equations can be used:
(A or B) and (A or C) = A or (B and C) (A and B) or (A and C) = A and (B or C)5.2 integer
The type 'integer' consists of signed integer numbers which are at least 32 bits wide. An 'integer' literal is a sequence of digits which is taken to be decimal. The sequence of digits may be followed by the letter E or e an optional + sign and a decimal exponent. Based numbers can be specified when the sequence of digits is followed by the # character and a sequence of extended digits. The decimal number in front of the # character specifies the base of the number which follows the # character. As base a number between 2 and 36 is allowed. As extended digits the letters A or a can be used for 10, B or b can be used for 11 and so on to Z or z which can be used as 35. Examples of 'integer' literals are:
0 2147483647 1E6 2e+9 16#c0 16#FFFF 8#177777 2#1010101010
The result of an 'integer' operation is undefined when it overflows.
Prefix operators: + Identity - Change sign ! Factorial Infix operators: + Addition - Subtraction * Multiplication div Integer division truncated towards zero ( A div B => trunc(flt(A) / flt(B)), A div 0 => EXCEPTION NUMERIC_ERROR ) rem Reminder of integer division div ( A rem B => A - (A div B) * B, A rem 0 => EXCEPTION NUMERIC_ERROR ) mdiv Integer division truncated towards negative infinity ( A mdiv B => round(floor(flt(A) / flt(B))), A mdiv 0 => EXCEPTION NUMERIC_ERROR ) mod Reminder of integer division mdiv ( A mod B => A - (A mdiv B) * B, A mod 0 => EXCEPTION NUMERIC_ERROR ) ** Power ( A ** B is okay for B >= 0, A ** 0 => 1, 1 ** B => 1 for B >= 0, A ** B => -(-A) ** B for A <= 0 and B >= 0 and odd(B), A ** B => (-A) ** B for A <= 0 and B >= 0 and not odd(B), A ** -1 => EXCEPTION NUMERIC_ERROR ) A << B Shift left ( A << B is okay for B >= 0 and B <= 31, A << B => A * 2_ ** B, A << 0 => A ) A >> B Arithmetic shift right ( A >> B is okay for B >= 0 and B <= 31, A >> B => A mdiv 2_ ** B for B <= 30, A >> B => C for A >= 0 holds: C >= 0 A >> B => C for A < 0 holds: C < 0 A >> B => 0 for A >= 0 and B > ord(log2(A)), A >> B => -1 for A < 0 and B > ord(log2(-A)), A >> 0 => A ) ! Binomial coefficient ( A ! B => !A div (!B * !(A - B)) ) lpad0 Left padding with zeros ( 123 lpad0 8 => "00000123", 123 lpad0 4 => "0123", 123 lpad0 3 => "123", 123 lpad0 2 => "123", 123 lpad0 -8 => "123", -12 lpad0 4 => "-012", -12 lpad0 3 => "-12", -12 lpad0 2 => "-12" ) integer conv A Identity ( integer conv A => A ) integer parse A Conversion of string to integer ( Type of argument A: string, integer parse "123" => 123, integer parse "-123" => -123, integer parse "+5" => 5, integer parse " 1" => EXCEPTION RANGE_ERROR, integer parse "10 " => EXCEPTION RANGE_ERROR, integer parse "ASDF" => EXCEPTION RANGE_ERROR ) Relations: =, <>, <, <=, >, >= Functions: ord(A) Identity succ(A) Successor ( succ(A) => A+1 ) pred(A) Predecessor ( pred(A) => A-1 ) abs(A) Absolute value odd(A) Odd value ( Type of result: boolean ) str(A) Conversion to string ( Type of result: string ) literal(A) Conversion to a literal ( Type of result: string, literal(A) => str(A) ) sqrt(A) Integer square root ( sqrt(A) is okay for A >= 0 sqrt(A) => trunc(sqrt(flt(A))), sqrt(-1) => EXCEPTION NUMERIC_ERROR ) log2(A) Truncated base 2 logarithm ( log2(A) returns the position of the highest bit set. It is defined for A >= 0 log2(2 ** A) = A for A >= 0, log2(0) => -1, log2(1) => 0, log2(2) => 1, log2(-1) => EXCEPTION NUMERIC_ERROR ) bitLength(A) Number of bits in the minimal two's-complement representation, excluding the sign bit. ( bitLength(A) => succ(log2(A)) for A >= 0, bitLength(A) => succ(log2(pred(-A))) for A < 0 ) lowestSetBit(A) Index of the lowest-order one bit For A <> 0 this is equal to to number of lowest-order zero bits. ( A >> B << B = A for A <> 0 and B = lowestSetBit(A), lowestSetBit(0) => -1, lowestSetBit(1) => 0, lowestSetBit(2) => 1 ) rand(A, B) Random number in the range [A, B] The random values are uniform distributed. ( rand(A, B) returns a random number such that A <= rand(A, B) and rand(A, B) <= B holds. rand(A, A) => A, rand(1, 0) => EXCEPTION RANGE_ERROR ) compare(A, B) Compare function ( compare(1, 2) => -1, compare(5, 5) => 0, compare(8, 7) => 1 ) hashCode(A) Hash function Statements: A +:= B Increment A by B ( A +:= B => A := A + B ) A -:= B Decrement A by B ( A -:= B => A := A - B ) A *:= B Multiplying copy ( A *:= B => A := A * B ) A <<:= B Shift left copy ( A <<:= B => A := A << B ) A >>:= B Shift right copy ( A >>:= B => A := A >> B ) incr(A) Increment with 1 ( incr(A) => A +:= 1 ) decr(A) Decrement with 1 ( decr(A) => A -:= 1 )
For the operations 'div' and 'rem' holds for all A:
(A div B) * B + A rem B = A when B <> 0 -A div B = -(A div B) when B <> 0 -A rem B = -(A rem B) when B <> 0 A rem B >= 0 and A rem B < abs(B) when B <> 0 and A >= 0 A rem B <= 0 and A rem B > -abs(B) when B <> 0 and A <= 0
For the operations 'mdiv' and 'mod' holds for all A:
(A mdiv B) * B + A mod B = A when B <> 0 -A mdiv B = A mdiv -B when B <> 0 -A mod -B = -(A mod B) when B <> 0 A mod B >= 0 and A mod B < B when B > 0 A mod B <= 0 and A mod B > B when B < 0
For the operation 'mdiv' holds:
A mdiv B = A div B - 1 when A and B have different signs and A rem B <> 0 holds. A mdiv B = A div B when A and B have the same sign or A rem B = 0 holds. A mdiv B = (A - 1) div B - 1 when A > 0 and B < 0 holds. A mdiv B = (A + 1) div B - 1 when A < 0 and B > 0 holds.
For the operation 'mod' holds:
A mod B = A rem B + B when A and B have different signs and A rem B <> 0 holds. A mod B = A rem B when A and B have the same sign or A rem B = 0 holds.
Table for the behaviour of 'div', 'rem', 'mdiv' and 'mod':
A B A div B A rem B A mdiv B A mod B 5 3 1 2 1 2 4 3 1 1 1 1 3 3 1 0 1 0 2 3 0 2 0 2 1 3 0 1 0 1 0 3 0 0 0 0 -1 3 0 -1 -1 2 -2 3 0 -2 -1 1 -3 3 -1 0 -1 0 -4 3 -1 -1 -2 2 -5 3 -1 -2 -2 1 A B A div B A rem B A mdiv B A mod B 5 -3 -1 2 -2 -1 4 -3 -1 1 -2 -2 3 -3 -1 0 -1 0 2 -3 0 2 -1 -1 1 -3 0 1 -1 -2 0 -3 0 0 0 0 -1 -3 0 -1 0 -1 -2 -3 0 -2 0 -2 -3 -3 1 0 1 0 -4 -3 1 -1 1 -1 -5 -3 1 -2 1 -2
For the 'sqrt' function holds (when A >= 0):
sqrt(A) * sqrt(A) <= A and (sqrt(A) + 1) * (sqrt(A) + 1) > A5.3 bigInteger
The type 'bigInteger' describes signed integer numbers of unlimited size. The literals of the type 'bigInteger' are sequences of digits followed by an underscore character (for example 1_ ). Although 'bigInteger' operations cannot overflow, it can happen that there is not enough memory to represent a 'bigInteger' value. In this case the exception 'MEMORY_ERROR' is raised.
Prefix operators: + Identity - Change sign Infix operators: + Addition - Subtraction * Multiplication div Integer division truncated towards zero ( A div B => trunc(A / B), A div 0_ => EXCEPTION NUMERIC_ERROR ) rem Reminder of integer division div ( A rem B => A - (A div B) * B, A rem 0_ => EXCEPTION NUMERIC_ERROR ) mdiv Integer division truncated towards negative infinity ( A mdiv B => floor(A / B), A mdiv 0_ => EXCEPTION NUMERIC_ERROR ) mod Reminder of integer division mdiv ( A mod B => A - (A mdiv B) * B, A mod 0_ => EXCEPTION NUMERIC_ERROR ) A ** B Power ( Type of argument B: integer, A ** B is okay for B >= 0, A ** 0 => 1_, 1_ ** B => 1_ for B >= 0, A ** B => -(-A) ** B for A <= 0 and B >= 0 and odd(B), A ** B => (-A) ** B for A <= 0 and B >= 0 and not odd(B), A ** -1 => EXCEPTION NUMERIC_ERROR ) A << B Shift left ( Type of argument B: integer, A << B is okay for B >= 0, A << B => A * 2_ ** B, A << 0 => A, A << -1 => EXCEPTION NUMERIC_ERROR ) A >> B Arithmetic shift right ( Type of argument B: integer, A >> B is okay for B >= 0, A >> B => A mdiv 2_ ** B, A >> B => C for A >= 0_ holds: C >= 0_ A >> B => C for A < 0_ holds: C < 0_ A >> B => 0_ for A >= 0_ and B > ord(log2(A)), A >> B => -1_ for A < 0_ and B > ord(log2(-A)), A >> 0 => A, A >> -1 => EXCEPTION NUMERIC_ERROR ) bigInteger conv A Conversion of integer to bigInteger ( Type of argument A: integer, bigInteger conv 1 => 1_ ) bigInteger parse A Conversion of string to integer ( Type of argument A: string, bigInteger parse "123" => 123_, bigInteger parse "-123" => -123_, bigInteger parse " 1" => EXCEPTION RANGE_ERROR, bigInteger parse "+5" => EXCEPTION RANGE_ERROR, bigInteger parse "10 " => EXCEPTION RANGE_ERROR, bigInteger parse "ASDF" => EXCEPTION RANGE_ERROR ) Relations: =, <>, <, <=, >, >= Functions: ord(A) Ordinal number ( Type of result: integer ) ord(99999999999999999999_) => EXCEPTION RANGE_ERROR ) succ(A) Successor ( succ(A) => A+1_ ) pred(A) Predecessor ( pred(A) => A-1_ ) abs(A) Absolute value odd(A) Odd value ( Type of result: boolean ) str(A) Conversion to string ( Type of result: string ) sqrt(A) Integer square root ( sqrt(A) is okay for A >= 0_ sqrt(A) => trunc(sqrt(flt(A))), sqrt(-1_) => EXCEPTION NUMERIC_ERROR ) log2(A) Truncated base 2 logarithm ( log2(A) returns the position of the highest bit set. It is defined for A >= 0 log2(2_ ** A) = A for A >= 0, log2(0_) => -1_, log2(1_) => 0_, log2(2_) => 1_, log2(-1) => EXCEPTION NUMERIC_ERROR ) gcd(A, B) Greatest common divisor of A and B. ( gcd(A, B) = gcd(B, A), gcd(A, B) = gcd(-A, B), gcd(A, 0) = abs(A) ) bitLength(A) Number of bits in the minimal two's-complement representation, excluding the sign bit. ( Type of result: integer, bitLength(A) => ord(succ(log2(A))) for A >= 0_, bitLength(A) => ord(succ(log2(pred(-A)))) for A < 0_ ) lowestSetBit(A) Index of the lowest-order one bit For A <> 0_ this is equal to to number of lowest-order zero bits. ( Type of result: integer, A >> B << B = A for A <> 0_ and B = lowestSetBit(A), lowestSetBit(0_) => -1, lowestSetBit(1_) => 0, lowestSetBit(2_) => 1 ) rand(A, B) Random number in the range [A, B] The random values are uniform distributed. ( rand(A, B) returns a random number such that A <= rand(A, B) and rand(A, B) <= B holds. rand(A, A) => A, rand(1_, 0_) => EXCEPTION RANGE_ERROR ) compare(A, B) Compare function ( Type of result: integer, compare(1_, 2_) => -1, compare(5_, 5_) => 0, compare(8_, 7_) => 1 ) hashCode(A) Hash function ( Type of result: integer ) Statements: A +:= B Increment A by B ( A +:= B => A := A + B ) A -:= B Decrement A by B ( A -:= B => A := A - B ) A *:= B Multiplying copy ( A *:= B => A := A * B ) A <<:= B Shift left copy ( A <<:= B => A := A << B ) A >>:= B Shift right copy ( A >>:= B => A := A >> B ) incr(A) Increment with 1 ( incr(A) => A +:= 1_ ) decr(A) Decrement with 1 ( decr(A) => A -:= 1_ )
For the operations 'div' and 'rem' holds for all A:
(A div B) * B + A rem B = A when B <> 0_ -A div B = -(A div B) when B <> 0_ -A rem B = -(A rem B) when B <> 0_ A rem B >= 0_ and A rem B < abs(B) when B <> 0_ and A >= 0_ A rem B <= 0_ and A rem B > -abs(B) when B <> 0_ and A <= 0_
For the operations 'mdiv' and 'mod' holds for all A:
(A mdiv B) * B + A mod B = A when B <> 0_ -A mdiv B = A mdiv -B when B <> 0_ -A mod -B = -(A mod B) when B <> 0_ A mod B >= 0_ and A mod B < B when B > 0_ A mod B <= 0_ and A mod B > B when B < 0_
For the operation 'mdiv' holds:
A mdiv B = A div B - 1_ when A and B have different signs and A rem B <> 0_ holds. A mdiv B = A div B when A and B have the same sign or A rem B = 0_ holds. A mdiv B = (A - 1_) div B - 1_ when A > 0_ and B < 0_ holds. A mdiv B = (A + 1_) div B - 1_ when A < 0_ and B > 0_ holds.
For the operation 'mod' holds:
A mod B = A rem B + B when A and B have different signs and A rem B <> 0_ holds. A mod B = A rem B when A and B have the same sign or A rem B = 0_ holds.
Table for the behaviour of 'div', 'rem', 'mdiv' and 'mod':
A B A div B A rem B A mdiv B A mod B 5_ 3_ 1_ 2_ 1_ 2_ 4_ 3_ 1_ 1_ 1_ 1_ 3_ 3_ 1_ 0_ 1_ 0_ 2_ 3_ 0_ 2_ 0_ 2_ 1_ 3_ 0_ 1_ 0_ 1_ 0_ 3_ 0_ 0_ 0_ 0_ -1_ 3_ 0_ -1_ -1_ 2_ -2_ 3_ 0_ -2_ -1_ 1_ -3_ 3_ -1_ 0_ -1_ 0_ -4_ 3_ -1_ -1_ -2_ 2_ -5_ 3_ -1_ -2_ -2_ 1_ A B A div B A rem B A mdiv B A mod B 5_ -3_ -1_ 2_ -2_ -1_ 4_ -3_ -1_ 1_ -2_ -2_ 3_ -3_ -1_ 0_ -1_ 0_ 2_ -3_ 0_ 2_ -1_ -1_ 1_ -3_ 0_ 1_ -1_ -2_ 0_ -3_ 0_ 0_ 0_ 0_ -1_ -3_ 0_ -1_ 0_ -1_ -2_ -3_ 0_ -2_ 0_ -2_ -3_ -3_ 1_ 0_ 1_ 0_ -4_ -3_ 1_ -1_ 1_ -1_ -5_ -3_ 1_ -2_ 1_ -2_
For the 'sqrt' function holds (when A >= 0_):
sqrt(A) * sqrt(A) <= A and (sqrt(A) + 1_) * (sqrt(A) + 1_) > A5.4 rational
The type 'rational' consists of rational numbers represented with an 'integer' numerator and an 'integer' denominator. The values of the type 'rational' are finite and periodical decimal numbers. Rational literals do not exist. The result of a 'rational' operation is undefined when it overflows.
Elements: var integer: numerator is 0; var integer: denominator is 1; Prefix operators: + Identity - Change sign Infix operators: + Addition - Subtraction * Multiplication / Division / Create rational from numerator and denominator ( Type of left operand: integer, Type of right operand: integer ) ** Power ( rational ** integer ) rational conv A Conversion of integer to rational ( Type of argument A: integer, rational conv 1 => 1 / 1 ) rational parse A Conversion of string to rational ( Type of argument A: string ) Relations: =, <>, <, <=, >, >= Functions: abs(A) Absolute value rat(A) Conversion of integer to rational ( Type of argument A: integer, rat(1) => 1 / 1 ) floor(A) Truncation towards negative infinity ( Type of result: integer, floor( 1.8)=> 1, floor( 1.0)=> 1, floor(-1.0)=>-1, floor(-1.8)=>-2 ) ceil(A) Rounding up towards positive infinity ( Type of result: integer, ceil( 1.2)=> 2, ceil( 1.0)=> 1, ceil(-1.0)=>-1, ceil(-1.2)=>-1 ) trunc(A) Truncation towards zero ( Type of result: integer, trunc( 1.8)=> 1, trunc( 1.0)=> 1, trunc(-1.0)=>-1, trunc(-1.8)=>-1 ) round(A) Round towards zero ( Type of result: integer, round(0.5)=>1, round(-0.5)=>-1, round(0.4)=>0, round(-0.4)=>0 ) str(A) Conversion to string ( Type of result: string ) compare(A, B) Compare function ( Type of result: integer, compare(1.9, 2.0) => -1, compare(5.2, 5.2) => 0, compare(8.0, 7.9) => 1 ) hashCode(A) Hash function ( Type of result: integer ) Statements: A +:= B Increment A by B ( A +:= B => A := A + B ) A -:= B Decrement A by B ( A -:= B => A := A - B ) A *:= B Multiplying copy ( A *:= B => A := A * B )
All calculations with 'rational' numbers are done exact. (Without any rounding)
5.5 bigRationalThe type 'bigRational' consists of rational numbers represented with an 'bigInteger' numerator and an 'bigInteger' denominator. The values of the type 'bigRational' are finite and periodical decimal numbers. BigRational literals do not exist. Although 'bigRational' operations cannot overflow, it can happen that there is not enough memory to represent a 'bigRational' value. In this case the exception 'MEMORY_ERROR' is raised.
Elements: var bigInteger: numerator is 0_; var bigInteger: denominator is 1_; Prefix operators: + Identity - Change sign Infix operators: + Addition - Subtraction * Multiplication / Division / Create bigRational from numerator and denominator ( Type of left argument: bigInteger, Type of right argument: bigInteger ) ** Power ( bigRational ** integer ) bigRational conv A Conversion of integer to bigRational ( Type of argument A: integer, bigRational conv 1 => 1_ / 1_ ) bigRational conv A Conversion of bigInteger to bigRational ( Type of argument A: bigInteger, bigRational conv 1_ => 1_ / 1_ ) bigRational parse A Conversion of string to bigRational ( Type of argument A: string ) Relations: =, <>, <, <=, >, >= Functions: abs(A) Absolute value rat(A) Conversion of bigInteger to bigRational ( Type of argument A: bigInteger, rat(1_) => 1_ / 1_ ) floor(A) Truncation towards negative infinity ( Type of result: bigInteger, floor( 1.8)=> 1, floor( 1.0)=> 1, floor(-1.0)=>-1, floor(-1.8)=>-2 ) ceil(A) Rounding up towards positive infinity ( Type of result: bigInteger, ceil( 1.2)=> 2, ceil( 1.0)=> 1, ceil(-1.0)=>-1, ceil(-1.2)=>-1 ) trunc(A) Truncation towards zero ( Type of result: bigInteger, trunc( 1.8)=> 1, trunc( 1.0)=> 1, trunc(-1.0)=>-1, trunc(-1.8)=>-1 ) round(A) Round towards zero ( Type of result: bigInteger, round(0.5)=>1, round(-0.5)=>-1, round(0.4)=>0, round(-0.4)=>0 ) str(A) Conversion to string ( Type of result: string ) compare(A, B) Compare function ( Type of result: integer, compare(1.9, 2.0) => -1, compare(5.2, 5.2) => 0, compare(8.0, 7.9) => 1 ) hashCode(A) Hash function ( Type of result: integer ) Statements: A +:= B Increment A by B ( A +:= B => A := A + B ) A -:= B Decrement A by B ( A -:= B => A := A - B ) A *:= B Multiplying copy ( A *:= B => A := A * B )
All calculations with 'bigRational' numbers are done exact. (Without any rounding)
5.6 floatThe type 'float' consists of single precision floating point numbers.
Constants: Infinity Positive infinity NaN Not-a-Number Prefix operators: + Identity - Change sign Infix operators: + Addition - Subtraction * Multiplication / Division ( A / 0.0 => Infinity for A > 0.0, A / 0.0 => -Infinity for A < 0.0, 0.0 / 0.0 => NaN ) ** Power ( A ** B is okay for A > 0.0, A ** B is okay for A < 0.0 and B is integer, A ** B => NaN for A < 0.0 and B is not integer, A ** 0.0 => 1.0, 0.0 ** B => 0.0 for B > 0.0, 0.0 ** 0.0 => 1.0, 0.0 ** B => Infinity for B < 0.0 ) ** Power ( Type of right operand: integer A ** B is okay for A > 0.0, A ** B is okay for A < 0.0, A ** 0 => 1.0, 0.0 ** B => 0.0 for B > 0, 0.0 ** 0 => 1.0, 0.0 ** B => Infinity for B < 0 ) float conv A Conversion of integer to float ( Type of argument A: integer, float conv 1 => 1.0 ) digits Conversion to string with specified precision ( Type of right operand: integer, Type of result: string, 3.1415 digits 2 => "3.14", Infinity digits A => "Infinity", -Infinity digits A => "-Infinity", NaN digits A => "NaN" ) float parse A Conversion of string to float ( Type of argument A: string ) Relations: =, <>, <, <=, >, >= Functions: abs(A) Absolute value flt(A) Conversion of integer to float ( Type of argument A: integer, flt(1) => 1.0 ) floor(A) Truncation towards negative infinity ( floor( 1.8)=> 1.0, floor( 1.0)=> 1.0, floor(-1.0)=>-1.0, floor(-1.2)=>-2.0, floor( 0.9)=> 0.0, floor(-0.1)=>-1.0 ) ceil(A) Rounding up towards positive infinity ( ceil( 1.2)=> 2.0, ceil( 1.0)=> 1.0, ceil(-1.8)=>-1.0, ceil(-1.0)=>-1.0, ceil( 0.1)=> 1.0, ceil(-0.9)=> 0.0 ) trunc(A) Truncation towards zero ( Type of result: integer, trunc( 1.8)=> 1, trunc( 1.0)=> 1, trunc(-1.8)=>-1, trunc(-1.0)=>-1, trunc( 0.9)=> 0, trunc(-0.9)=> 0 ) round(A) Round towards zero ( Type of result: integer, round(1.5)=>2, round(-1.5)=>-2, round(0.5)=>1, round(-0.5)=>-1, round(0.4)=>0, round(-0.4)=>0 ) str(A) Conversion to string ( Type of result: string, str(Infinity) => "Infinity", str(-Infinity) => "-Infinity", str(NaN) => "NaN" ) isnan(A) Check if A is Not-a-Number sin(A) Sine cos(A) Cosine tan(A) Tangent exp(A) Exponential function log(A) Natural logarithm ( log(A) is okay for A > 0.0, log(0.0) => -Infinity, log(-1.0) => NaN ) log10(A) Base 10 logarithm ( log10(A) is okay for A > 0.0, log10(0.0) => -Infinity, log10(-1.0) => NaN ) sqrt(A) Square root ( sqrt(A) is okay for A >= 0.0, sqrt(-1.0) => NaN ) asin(A) Inverse sine ( asin(A) is okay for A >= -1.0 and A <= 1.0, asin(2.0) => NaN ) acos(A) Inverse cosine ( acos(A) is okay for A >= -1.0 and A <= 1.0, acos(2.0) => NaN ) atan(A) Inverse tangent atan2(A, B) Inverse tangent of A / B sinh(A) Hyperbolic sine cosh(A) Hyperbolic cosine tanh(A) Hyperbolic tangent rand(A, B) Random number in the range [A, B] The random values are uniform distributed. ( rand(A, B) returns a random number such that A <= rand(A, B) and rand(A, B) <= B holds. rand(A, A) => A, rand(1.0, 0.0) => EXCEPTION RANGE_ERROR ) compare(A, B) Compare function ( Type of result: integer, compare(1.9, 2.1) => -1, compare(5.3, 5.3) => 0, compare(7.8, 7.7) => 1 ) hashCode(A) Hash function ( Type of result: integer ) Statements: A +:= B Increment A by B ( A +:= B => A := A + B ) A -:= B Decrement A by B ( A -:= B => A := A - B ) A *:= B Multiplying copy ( A *:= B => A := A * B ) A /:= B Dividing copy ( A /:= B => A := A / B )5.7 complex
The type 'complex' consists of complex numbers represented with an 'float' real part and an 'float' imaginary part. Complex literals do not exist.
Elements: var float: re is 0.0; var float: im is 0.0; Prefix operators: + Identity - Change sign conj Complex conjugate Infix operators: + Addition - Subtraction * Multiplication / Division ( A / complex(0.0) => complex(NaN, NaN) ) ** Power ( Type of right operand: integer A ** B is okay for A > complex(0.0), A ** B is okay for A < complex(0.0), A ** 0 => complex(1.0), complex(0.0) ** B => complex(0.0) for B > 0, complex(0.0) ** 0 => complex(1.0), complex(0.0) ** B => complex(Infinity, NaN) for B < 0 ) complex conv A Conversion of integer to complex ( Type of argument A: integer, complex conv A => complex(flt(A)) ) complex conv A Conversion of float to complex ( Type of argument A: float, complex conv A => complex(A) ) digits Conversion to string with specified precision ( Type of right operand: integer, Type of result: string, complex(3.1415) digits 2 => "3.14+0.00i" ) complex parse A Conversion of string to complex ( Type of argument A: string ) Relations: =, <> Functions: abs(A) Absolute value ( Type of result: float ) sqrAbs(A) Square of absolute value ( Type of result: float ) arg(A) Argument (=angle of the polar form of A) ( Type of result: float ) complex(A, B) Return a complex number from its real and imaginary part ( Type of argument A: float, Type of argument B: float ) complex(A) Return a complex number from its real part ( Type of argument A: float ) polar(A, B) Return a complex number from polar coordinates ( Type of argument A: float, Type of argument B: float ) str(A) Conversion to string ( Type of result: string, str(complex(1.125)) => "1.125+0.0i" ) Statements: A +:= B Increment A by B ( A +:= B => A := A + B ) A -:= B Decrement A by B ( A -:= B => A := A - B ) A *:= B Multiplying copy ( A *:= B => A := A * B ) A /:= B Dividing copy ( A /:= B => A := A / B )5.8 char
The type 'char' describes UNICODE characters encoded with UTF-32. In the source file a character literal is written as UTF-8 encoded UNICODE character enclosed in single quotes. In order to represent nonprintable characters and certain printable characters the following escape sequences may be used.
audible alert BEL \a backslash (\) \\ backspace BS \b apostrophe (') \' escape ESC \e double quote (") \" formfeed FF \f newline NL (LF) \n control-A \A carriage return CR \r ... horizontal tab HT \t control-Z \Z vertical tab VT \v
Additionally the following escape sequence can be used:
Examples of character literals are:
'a' ' ' '\n' '!' '\\' '2' '"' '\"' '\'' '\8\'
To use characters beyond ASCII (which is a subset of UNICODE) in the source file make sure that the editor uses UTF-8 encoded characters.
Infix operators: char conv A Conversion of integer to char ( Type of argument A: integer, char conv 65 => 'A' ) char parse A Conversion of string to char ( Type of argument A: string ) Relations: =, <>, <, <=, >, >= Functions: ord(A) Ordinal number ( Type of result: integer ) chr(A) Conversion of integer to char ( Type of argument: integer ) succ(A) Successor ( succ(A)=>chr(succ(ord(A))) ) pred(A) Predecessor ( pred(A)=>chr(pred(ord(A))) ) str(A) Conversion to string ( Type of result: string, str('A') => "A" ) literal(A) Conversion to a literal ( Type of result: string, literal('A') => "'A'" ) upper(A) Conversion to upper case character ( upper('A') => 'A' ) ( upper('z') => 'Z' ) lower(A) Conversion to lower case character ( lower('A') => 'a' ) ( lower('z') => 'z' ) rand(A, B) Random character in the range [A, B] The random values are uniform distributed. ( rand(A, B) returns a random character such that A <= rand(A, B) and rand(A, B) <= B holds. rand(A, A) => A, rand('B', 'A') => EXCEPTION RANGE_ERROR ) compare(A, B) Compare function ( Type of result: integer, compare('A', 'B') => -1, compare('A', 'A') => 0, compare('B', 'A') => 1 ) hashCode(A) Hash function ( Type of result: integer ) Statements: incr(A) Increment ( incr(A) => A := succ(A) ) decr(A) Decrement ( decr(A) => A := pred(A) )5.9 string
The type 'string' describes sequences of UNICODE characters (including the empty string). The characters in the 'string' use the UTF-32 encoding. Strings are not '\0\' terminated and therefore can also contain binary data. Although 'string's are allowed to grow very big, it can happen that there is not enough memory to represent a 'string' value. In this case the exception 'MEMORY_ERROR' is raised. In the source file a string literal is a sequence of UTF-8 encoded UNICODE characters surrounded by double quotes.
To represent control characters and certain other characters in strings the same escape sequences as for character literals may be used. E.g.: Quotation characters (") inside strings can be represented by preceding them with a backslash ( \" ). Additionally there is the following possibility:
Examples of string literals are:
"" " " "\"" "'" "String" "CAN\"T !"
To use characters beyond ASCII (which is a subset of UNICODE) in the source file make sure that the editor uses UTF-8 encoded characters.
Infix operators: & String concatenation ( "All " & "OK" => "All OK" ) <& String concatenation with weak priority Overloaded for various types with 'enable_io' ( write("i=" <& i digits 2 len 6 <& " $"); ) mult String multiplication ( Type of right operand: integer, "LA" mult 3 => "LALALA", "WORD" mult 0 => "", "ANY" mult -1 => EXCEPTION RANGE_ERROR ) lpad Left padding with spaces ( Type of right operand: integer, "HELLO" lpad 8 => " HELLO", "HELLO" lpad 6 => " HELLO", "HELLO" lpad 5 => "HELLO", "HELLO" lpad 4 => "HELLO", "HELLO" lpad 0 => "HELLO", "HELLO" lpad -8 => "HELLO" ) lpad0 Left padding with spaces ( Type of right operand: integer, "12" lpad0 5 => "00012", "12" lpad0 3 => "012", "12" lpad0 2 => "12", "12" lpad0 1 => "12", "12" lpad0 0 => "12", "12" lpad0 -5 => "12" ) rpad Right padding with spaces ( Type of right operand: integer, "HELLO" rpad 8 => "HELLO ", "HELLO" rpad 6 => "HELLO ", "HELLO" rpad 5 => "HELLO", "HELLO" rpad 4 => "HELLO", "HELLO" rpad 0 => "HELLO", "HELLO" rpad -8 => "HELLO" ) string parse A Identity Indices: [ A ] Access one character ( Type of argument A: integer, Type of result: char, A[1] => First character, A[length(A)] => Last character, A[0] => EXCEPTION RANGE_ERROR, A[succ(length(A))] => EXCEPTION RANGE_ERROR ) [ A .. B ] Access a substring from position A to B ( Type of arguments A and B: integer ) [ A len B ] Access a substring from position A with length B ( Type of arguments A and B: integer ) [ A .. ] Access a substring beginning at position A ( Type of argument A: integer ) [ .. A ] Access a substring ending at position A ( Type of argument A: integer ) Relations: =, <>, <, <=, >, >= Functions: length(A) Length of string ( Type of result: integer, length("") => 0 ) pos(A,B) First position of char B in string A ( Type of argument B: char, Type of result: integer, pos("ABCABC",'B')=>2, pos("XYZ",'A')=>0 ) pos(A,B) First position of string B in string A ( Type of result: integer, pos("ABCDE ABCDE","BC")=>2, pos("XYZXYZ","ZYX")=>0, pos("123456789","")=>0 ) pos(A,B,C) First position of char B in string A The search starts at position C of string A ( Type of argument B: char, Type of argument C: integer, Type of result: integer, pos("ABCABC",'B', 3)=>5, pos("XYZYX",'Z', 4)=>0, pos("12345",'3', 7)=>0 ) pos(A,B,C) First position of string B in string A The search starts at position C of string A ( Type of argument C: integer, Type of result: integer, pos("ABCDE ABCDE","BC", 3)=>8, pos("XYZXYZ","ZXY", 4)=>0, pos("12345","34", 7)=>0 ) pos("123456789","", 2)=>0 ) rpos(A,B) Last position of char B in string A ( Type of argument B: char, Type of result: integer, rpos("ABCABC",'B')=>5, rpos("XYZ",'A')=>0 ) rpos(A,B) Last position of string B in string A ( Type of result: integer, rpos("ABCDE ABCDE","BC")=>8, rpos("XYZXYZ","ZYX")=>0, rpos("123456789","")=>0 ) replace(A,B,C) Replace all occurrences of string B in string A by string C ( replace("old gold", "old", "one")=> "one gone" ) split(A,B) Split A into strings delimited by B ( Type of argument B: char, Type of result: array string, split("", ':') => [](""), split(":", ':') => []("", ""), split("15:30", ':') => []("15", "30") ) split(A,B) Split A into strings delimited by B ( Type of result: array string, split("", "") => [](""), split("ABC", "") => []("ABC"), split("", "; ") => [](""), split("writeln; readln;", "; ") => []("writeln", "readln;") ) join(A,B) Join the elements of A together with B's between them ( Type of argument A: array string, Type of argument B: char, join([]("This", "is", "a", "test"), ' ') => "This is a test" ) join(A,B) Join the elements of A together with B's between them ( Type of argument A: array string, Type of argument B: string, join([]("pro", "gram"), "") => "program" ) trim(A) Removes leading and trailing spaces and control chars ( trim(" /n xyz /r") = "xyz" ) str(A) Conversion to string ( Type of result: string, str(A) => A ) literal(A) Conversion to a literal ( Type of result: string, literal("ABC") => "\"ABC\"", literal("O' \"X\"") => "\"O\' \\\"X\\\"\"" ) upper(A) Conversion to upper case characters ( upper("Upper")=>"UPPER" ) lower(A) Conversion to lower case characters ( lower("Lower")=>"lower" ) compare(A, B) Compare function ( Type of result: integer, compare("ABC", "ABCD") => -1, compare("ABC", "ABC") => 0, compare("ABCD", "ABCC") => 1 ) hashCode(A) Hash function ( Type of result: integer ) Statements: A &:= B Append B to A ( A &:= B => A := A & B ) A @:= [B] C Assign C to element B of string A ( Type of argument B: integer, Type of argument C: char, A @:= [B] C => A := A[..pred(B)] & str(C) & A[succ(B)..], A @:= [0] 'x' => EXCEPTION RANGE_ERROR, A @:= [succ(length(A))] 'x' => EXCEPTION RANGE_ERROR )5.10 array
The type 'array baseType' describes sequences of identical elements of a 'baseType'. (including the empty sequence). For example: 'array integer' describes arrays of integer elements. The minimal and maximal indices of an array are part of the value and can be obtained with the functions 'minIdx' and 'maxIdx'. There are functions which generate arrays with the default minimal index of 1 and other functions which generate arrays with the minimal index taken from a parameter.
Literal: [] (elem1, elem2) Create an array with the given elements The starting index of the array is 1. [0] (elem1, elem2) Create an array with the given elements The starting index of the array is 0. Infix operators: & Array concatenation times Array generation ( Left operand: integer, Right operand: baseType, A times B Generates an 'array baseType' with A elements of B, (1 times B)[1] => B -1 times B => EXCEPTION RANGE_ERROR ) [ A .. B ] times C Array generation ( Type of arguments A and B: integer ) Type of argument C: baseType, [ A .. B ] times C Generates an 'array baseType' with pred(B - A) elements of C, [ -1 .. -2 ] times B => empty array, [ -1 .. -3 ] times B => EXCEPTION RANGE_ERROR ) Indices: [ A ] Access one array element ( Type of argument A: integer, Type of result: baseType, A[minIdx(A)] => First element, A[maxIdx(A)] => Last element, A[pred(minIdx(A))] => EXCEPTION RANGE_ERROR, A[succ(maxIdx(A))] => EXCEPTION RANGE_ERROR ) [ A .. B ] Access a sub array ( Type of arguments A and B: integer ) [ A .. ] Access a sub array beginning at position A ( Type of argument A: integer ) [ .. A ] Access a sub array ending at position A ( Type of argument A: integer ) Relations: =, <> Functions: length(A) Length of array ( Type of result: integer, length(A) = succ(maxIdx(A) - minIdx(A)), length(0 times TRUE) => 0, length(5 times TRUE) => 5 ) minIdx(A) Minimal index of array ( Type of result: integer, minIdx(3 times TRUE) => 1, minIdx([-1 .. 4] times TRUE) => -1 ) maxIdx(A) Maximal index of array ( Type of result: integer, maxIdx(3 times TRUE) => 3 ) rand(A) Random element from an array The random elements are uniform distributed. ( Type of result: baseType ) remove(A,B) Remove element with index B from array A and return the removed element ( Type of argument B: integer, Type of result: baseType, remove(0 times TRUE, 1) => EXCEPTION RANGE_ERROR ) sort(A) Sort array using the compare(baseType, baseType) function Statements: A &:= B Append B to A ( A &:= B => A := A & B ) for A range B do C end for Loop over all elements of an array ( Type of argument A: baseType, Type of argument C: proc ) insert(A, B, C) Insert C to the array A at position B ( Type of argument B: integer, Type of argument C: baseType ) insert(A, B) Insert B into the sorted array A ( Type of argument C: baseType )5.11 hash
The type 'hash [keyType] baseType' describes hash tables with elements of 'baseType' which can be accessed using an index of 'keyType'. For example: 'hash [string] integer' describes hash table of integer elements with a 'string' key.
Constants: hashType.EMPTY_HASH Empty hashtable Infix operators: in Element ( Left argument: baseType, Type of result: boolean ) not in Is not Element ( Left argument: baseType, Type of result: boolean ) Indices: [ A ] Access one hashtable element ( Type of argument A: keyType, Type of result: baseType ) Functions: length(A) Number of elements in hashtable A ( Type of result: integer, length(hashType.EMPTY_HASH) => 0 ) keys(A) Unsorted array of keys of the hashtable A ( Type of result: array keyType ) values(A) Unsorted array of valuess of the hashtable A ( Type of result: array baseType ) flip(A) Deliver a hash with keys and values flipped ( Type of result: hash [baseType] array keyType ) Statements: incl(A,B,C) Include element B to hashtable A ( Type of argument B: keyType, Type of argument C: baseType ) excl(A,B) Exclude element B from hashtable A ( Type of argument B: keyType ) A @:= [B] C Assign C to element B of hashtable A ( Type of argument B: keyType, Type of argument C: baseType ) for A range B do C end for Unsorted loop over all values of a hash ( Type of argument A: baseType, Type of argument C: proc ) for key A range B do C end for Unsorted loop over all keys of a hash ( Type of argument A: keyType, Type of argument C: proc ) for A key B range C do D end for Unsorted loop over all values and keys of a hash ( Type of argument A: baseType, Type of argument B: keyType, Type of argument D: proc )5.12 set
The type 'set of baseType' describes a set of elements of a 'baseType'. (including the empty set).
Constants: setType.EMPTY_SET Empty set Infix operators: | Union & Intersection - Difference >< Symmetric Difference in Element ( Left argument: baseType, Type of result: boolean ) not in Is not Element ( Left argument: baseType, Type of result: boolean ) Relations: =, <>, <, <=, >, >= Functions: card Cardinality of a set ( Type of result: integer, card(setType.EMPTY_SET) = 0 ) min Minimal element ( Type of result: baseType, Delivers the element from the set for which the following condition holds: Element <= X for all X which are in the set. min(setType.EMPTY_SET) => EXCEPTION RANGE_ERROR ) max Maximum element ( Type of result: baseType, Delivers the element from the set for which the following condition holds: Element >= X for all X which are in the set. min(setType.EMPTY_SET) => EXCEPTION RANGE_ERROR ) compare(A, B) Compare function ( Type of result: integer ) hashCode(A) Hash function ( Type of result: integer ) Statements: incl(A,B) Include element B to set A ( Type of argument B: baseType ) excl(A,B) Exclude element B from set A ( Type of argument B: baseType ) for A range B do C end for Loop over all elements of a set ( Type of argument A: baseType, Type of argument C: proc )5.13 struct
The type 'struct' describes all structured types.
Type generators: new struct var aType: name is value; ... end struct Create new structure type new metaType struct var aType: name is value; ... end struct Create new structure type as subtype of 'metaType', which is not a structure sub metaType struct var aType: name is value; ... end struct Create new structure type as subtype of 'metaType', which is a structure type. The new stucture type inherits all elements of the structure type 'metaType'. var aType: name is value Declare structure element 'name' with 'value' Infixoperators: . Access Element of STRUCT ( example.element ) -> Access Element of ptr STRUCT ( example->element ) Relations: =, <> Functions: incl(A, B) Include element in MODULE ( Type of argument B: reference ) excl(A, B) Exclude element from MODULE ( Type of argument B: reference )5.14 category
The type 'category' describes the category of a 'reference'.
Constants: SYMBOLOBJECT, DECLAREDOBJECT, FORWARDOBJECT, FWDREFOBJECT, BLOCKOBJECT, CALLOBJECT,MATCHOBJECT, TYPEOBJECT, FORMPARAMOBJECT, INTOBJECT, BIGINTOBJECT, CHAROBJECT, STRIOBJECT, BSTRIOBJECT, ARRAYOBJECT, HASHOBJECT, STRUCTOBJECT, CLASSOBJECT, INTERFACEOBJECT, SETOBJECT, FILEOBJECT, SOCKETOBJECT, LISTOBJECT, FLOATOBJECT, WINOBJECT, ENUMLITERALOBJECT, CONSTENUMOBJECT, VARENUMOBJECT, REFOBJECT, REFLISTOBJECT, EXPROBJECT, ACTOBJECT, VALUEPARAMOBJECT, REFPARAMOBJECT, RESULTOBJECT, LOCALVOBJECT, PROGOBJECT Infix operators: category conv A Conversion of integer to category ( Type of argument A: integer, category conv ord(INTOBJECT) => INTOBJECT ) category parse A Conversion of string to category ( Type of argument A: string, category parse "FLOATOBJECT" => FLOATOBJECT ) Relations: =, <> Functions: ord(A) Ordinal number ( Type of result: integer ) str(A) Conversion to string ( Type of result: string, str(CHAROBJECT) => "CHAROBJECT" )5.15 reference
The type 'reference' describes a reference to any object.
Constants: NIL Reference to no element. Relations: =, <> Functions: category(A) Get the category of the referenced object ( Type of result: category, category(NIL) => EXCEPTION RANGE_ERROR ) str(A) Conversion to string ( Type of result: string ) getType(A) Get the type of the referenced object ( Type of result: type, getType(NIL) => EXCEPTION RANGE_ERROR ) obj_number(A) Delivers an unique number for each object ( Type of result: integer, obj_number(NIL) => 0 ) isVar(A) Reference to a variable object ( Type of result: boolean, isVar(NIL) => EXCEPTION RANGE_ERROR ) params(A) Gets the formal params of a function ( Type of result: ref_list ) local_vars(A) Gets the local variables of a function ( Type of result: ref_list, local_vars(NIL) => EXCEPTION RANGE_ERROR, local_vars(A) => EXCEPTION RANGE_ERROR for category(A) <> BLOCKOBJECT ) local_consts(A) Gets the local constants of a function ( Type of result: ref_list, local_consts(NIL) => EXCEPTION RANGE_ERROR, local_consts(A) => EXCEPTION RANGE_ERROR for category(A) <> BLOCKOBJECT ) body(A) Gets the body of a function ( body(NIL) => EXCEPTION RANGE_ERROR, body(A) => EXCEPTION RANGE_ERROR for category(A) <> BLOCKOBJECT ) func_result(A) Gets the result object of a function func_res_init(A) Gets the init value of the result object of a function array_to_list(A) Return the array elements as list ( Type of result: ref_list, array_to_list(NIL) => EXCEPTION RANGE_ERROR, array_to_list(A) => EXCEPTION RANGE_ERROR for category(A) <> ARRAYOBJECT ) array_min_index(A) Return the minimal index of an array ( Type of result: integer, array_min_index(NIL) => EXCEPTION RANGE_ERROR, array_min_index(A) => EXCEPTION RANGE_ERROR for category(A) <> ARRAYOBJECT ) array_max_index(A) Return the maximal index of an array ( Type of result: integer, array_max_index(NIL) => EXCEPTION RANGE_ERROR, array_max_index(A) => EXCEPTION RANGE_ERROR for category(A) <> ARRAYOBJECT ) struct_to_list(A) Return the struct elements as list ( Type of result: ref_list, struct_to_list(NIL) => EXCEPTION RANGE_ERROR, struct_to_list(A) => EXCEPTION RANGE_ERROR for category(A) <> STRUCTOBJECT ) interface_to_struct(A) Return the struct to which the interface object points. ( interface_to_struct(NIL) => EXCEPTION RANGE_ERROR, interface_to_struct(A) => EXCEPTION RANGE_ERROR for category(A) <> INTERFACEOBJECT ) file(A) File name of the referenced object ( Type of result: string ) line(A) Line number of the referenced object ( Type of result: integer ) alloc(A) Create a copy of the object referenced by A The object value of the copy is set to NULL getValue(A, reference) Dereference as reference ( Type of result: reference, getValue(NIL, reference) => EXCEPTION RANGE_ERROR, getValue(A, reference) => EXCEPTION RANGE_ERROR for category(A) not in {FWDREFOBJECT, REFOBJECT, REFPARAMOBJECT, RESULTOBJECT, LOCALVOBJECT, ENUMLITERALOBJECT, CONSTENUMOBJECT, VARENUMOBJECT} ) getValue(A, ref_list) Dereference as ref_list ( Type of result: ref_list, getValue(NIL, ref_list) => EXCEPTION RANGE_ERROR, getValue(A, ref_list) => EXCEPTION RANGE_ERROR for category(A) not in {MATCHOBJECT, CALLOBJECT, REFLISTOBJECT} ) getValue(A, integer) Dereference as integer ( Type of result: integer, getValue(NIL, integer) => EXCEPTION RANGE_ERROR, getValue(A, integer) => EXCEPTION RANGE_ERROR for category(A) <> INTOBJECT ) getValue(A, bigInteger) Dereference as bigInteger ( Type of result: bigInteger, getValue(NIL, bigInteger) => EXCEPTION RANGE_ERROR, getValue(A, bigInteger) => EXCEPTION RANGE_ERROR for category(A) <> BIGINTOBJECT ) getValue(A, float) Dereference as float ( Type of result: float, getValue(NIL, float) => EXCEPTION RANGE_ERROR, getValue(A, float) => EXCEPTION RANGE_ERROR for category(A) <> FLOATOBJECT ) getValue(A, char) Dereference as char ( Type of result: char, getValue(NIL, char) => EXCEPTION RANGE_ERROR, getValue(A, char) => EXCEPTION RANGE_ERROR for category(A) <> CHAROBJECT ) getValue(A, string) Dereference as string ( Type of result: string, getValue(NIL, string) => EXCEPTION RANGE_ERROR, getValue(A, string) => EXCEPTION RANGE_ERROR for category(A) <> STRIOBJECT ) getValue(A, bitset) Dereference as bitset ( Type of result: bitset, getValue(NIL, bitset) => EXCEPTION RANGE_ERROR, getValue(A, bitset) => EXCEPTION RANGE_ERROR for category(A) <> SETOBJECT ) getValue(A, PRIMITIVE_FILE) Dereference as PRIMITIVE_FILE ( Type of result: PRIMITIVE_FILE, getValue(NIL, PRIMITIVE_FILE) => EXCEPTION RANGE_ERROR, getValue(A, PRIMITIVE_FILE) => EXCEPTION RANGE_ERROR for category(A) <> FILEOBJECT ) getValue(A, program) Dereference as program ( Type of result: program, getValue(NIL, program) => EXCEPTION RANGE_ERROR, getValue(A, program) => EXCEPTION RANGE_ERROR for category(A) <> PROGOBJECT ) getValue(A, ACTION) Dereference as ACTION ( Type of result: ACTION, getValue(NIL, ACTION) => EXCEPTION RANGE_ERROR, getValue(A, ACTION) => EXCEPTION RANGE_ERROR for category(A) <> ACTOBJECT ) getValue(A, type) Dereference as type ( Type of result: type, getValue(NIL, type) => EXCEPTION RANGE_ERROR, getValue(A, type) => EXCEPTION RANGE_ERROR for category(A) <> TYPEOBJECT ) compare(A, B) Compare function ( Type of result: integer ) hashCode(A) Hash function ( Type of result: integer ) Statements: setVar(A, B) Set var flag of referenced object A to B ( Type of argument B: boolean, setVar(NIL, B) => EXCEPTION RANGE_ERROR ) setCategory(A, B) Set the category of the referenced object A to B ( Type of argument B: category, setCategory(NIL, B) => EXCEPTION RANGE_ERROR ) setType(A, B) Set the type of the referenced object A to B ( Type of argument B: type, setType(NIL, B) => EXCEPTION RANGE_ERROR ) setValue(A, B) Set the value of the referenced object A to B ( Type of argument B: ref_list ) setParams(A, B) Set the formal params of a function ( Type of argument B: ref_list )5.16 ref_list
The type 'ref_list' describes a list of 'reference' objects.
Constants: ref_list.EMPTY Empty reference list. Infix operators: & Ref_list list concatenation A in B Is element in ref_list ( Type of argument A: reference, Type of result: boolean ) A not in B Is element not in ref_list ( Type of argument A: reference, Type of result: boolean ) Indices: [ A ] Access one ref_list element ( Type of argument A: integer, Type of result: reference, A[1]=>First element, A[length(A)]=>Last element, A[0] => EXCEPTION RANGE_ERROR, A[succ(length(A))] => EXCEPTION RANGE_ERROR ) [ A .. B ] Access a sub list ( Type of arguments A and B: integer ) [ A .. ] Access a sub list beginning at position A ( Type of argument A: integer ) [ .. A ] Access a sub list ending at position A ( Type of argument A: integer ) Relations: =, <> Functions: length(A) Length of ref_list ( Type of result: integer, length(ref_list.EMPTY) => 0 ) make_list(A) Create ref_list with element A ( Type of argument A: reference ) pos(A,B) First position of reference B in ref_list A ( Type of argument B: reference, Type of result: integer ) pos(A,B,C) First position of reference B in ref_list A The search starts at position C of ref_list A ( Type of argument B: reference, Type of argument C: integer, Type of result: integer ) incl(A, B) Include element in list ( Type of argument B: reference ) excl(A, B) Exclude element from list ( Type of argument B: reference ) Statements: A &:= B Append B to A ( A &:= B => A := A & B ) A @:= [B] C Assign C to element B of ref_list A ( Type of argument B: integer, Type of argument C: reference, A @:= [B] C => A := A[..pred(B)] & make_list(C) & A[succ(B)..], A @:= [0] C => EXCEPTION RANGE_ERROR, A @:= [succ(length(A))] C => EXCEPTION RANGE_ERROR ) for A range B do C end for Loop over all elements of a ref_list ( Type of argument A: reference, Type of argument C: proc )5.17 program
The type 'program' describes a Seed7 program.
Constants: program.EMPTY Empty program. Relations: =, <> Functions: parseFile(A) Parse the file with the name A ( Type of argument A: string ) parseStri(A) Parse the string A ( Type of argument A: string ) evaluate(A, B) Evaluate the expression B which is part of program A ( Type of result: reference, Type of argument B: reference ) sys_var(A, B) Return a reference of the system var B of program A ( Type of result: reference, Type of argument B: string ) error_count(A) Number of errors in the program A ( Type of result: integer ) declared_objects(A) List of objects declared in the program A ( Type of result: ref_list ) syobject(A, B) Return object with name B in program A ( Type of result: reference, Type of argument B: string ) match(A, B) Return object from program A which matches B ( Type of result: reference, Type of argument B: ref_list ) Statements: execute(A) Execute the program referred by A5.18 ptr
The type 'ptr baseType' describes a pointer to an object of a 'baseType'. With
const type: ptrType is ptr baseType;
a new pointer type 'ptrType' is declared.
Constants: ptrType.NIL Reference to no element Prefix operators: & Address of ( Type of operand: baseType ) Postfix operators: ^ Dereference ( Type of result: baseType ) Infix operators: ptrType conv A Conversion from reference A to ptrType reference conv A Conversion from ptrType A to reference Relations: =, <> Functions: base_type(ptrType) Gets the baseType of a ptrType ( Type of argument ptrType: type )5.19 ENUMERATION
With
const type: enumType is new enum enum_literal1, enum_literal2 end enum;
a new enumeration type is declared. The values of this type are:
enum_literal1 and enum_literal2
For a enumeration type only few operations are predefined. Additional operations must be defined separately. So it is necessary to define the 'str' and 'parse' functions in order to do I/O for a new enumeration type.
Infix operators: enumType conv A Conversion from integer A to enumType ( Type of argument A: integer, enumType conv 0 => enum_literal1 ) integer conv A Conversion from enumType A to integer ( Type of result: integer, integer conv enum_literal1 => 0 ) Relations: =, <>, <, <=, >, >= Functions: ord(A) Ordinal number ( Type of result: integer ) succ(A) Successor ( succ(A)=>enumType conv(succ(ord(A))) ) pred(A) Predecessor ( pred(A)=>enumType conv(pred(ord(A))) ) Statements: incr(A) Increment ( incr(A) => A:=succ(A) ) decr(A) Decrement ( decr(A) => A:=pred(A) )5.20 color
The type 'color' describes colors.
Elements: var integer: red_part is 0; var integer: green_part is 0; var integer: blue_part is 0; Constants: black is color(0, 0, 0); dark_red is color(32768, 0, 0); dark_green is color(0, 32768, 0); brown is color(32768, 16384, 0); dark_blue is color(0, 0, 32768); dark_magenta is color(32768, 0, 32768); dark_cyan is color(0, 65535, 65535); light_gray is color(49152, 49152, 49152); dark_gray is color(16384, 16384, 16384); light_red is color(65535, 0, 0); light_green is color(0, 65535, 0); yellow is color(65535, 65535, 0); light_blue is color(0, 0, 65535); light_magenta is color(65535, 0, 65535); light_cyan is color(32768, 65535, 65535); white is color(65535, 65535, 65535); orange is color(65535, 32768, 0); amber is color(49152, 32768, 16384); pink is color(65535, 32768, 32768); Infix operators: + Add two colors in an additive color system Relations: =, <> Functions: color(R,G,B) Creates a color from Red, Green and Blue ( Type of argument R: integer, Type of argument G: integer, Type of argument B: integer )5.21 time
The type 'time' describes times and dates.
Elements: var integer: year is 0; var integer: month is 1; var integer: day is 1; var integer: hour is 0; var integer: minute is 0; var integer: second is 0; Infix operators: + Add a duration to a time ( Type of right operand: duration ) - Subtract a duration from a time ( Type of right operand: duration ) - Subtract two times ( Type of result: duration ) time parse A Conversion of string to time ( Type of argument A: string, time parse "2005-02-28 12:00:01" => 2005-02-28 12:00:01, time parse "2005-02-29 12:00:01" => EXCEPTION RANGE_ERROR ) Relations: =, <>, <, <=, >, >= Functions: time(NOW) Gets the current time str(A) Conversion to string ( Type of result: string ) strDate(A) Conversion of the date to string ( Type of result: string ) strTime(A) Conversion of the daytime to string ( Type of result: string ) strTimeZone(A) Conversion of the time zone to string ( Type of result: string ) truncToSecond(A) Trunc a time to a second truncToMinute(A) Trunc a time to a minute truncToHour(A) Trunc a time to a hour truncToDay(A) Trunc a time to a day truncToMonth(A) Trunc a time to a month truncToYear(A) Trunc a time to a year isLeapYear(A) Determine if a given year is a leap year ( Type of argument A: integer ) ( Type of result: boolean ) daysInMonth(Y, M) Calculate the number of days in a month ( Type of argument Y: integer, Type of argument M: integer, Type of result: integer ) dayOfWeek(A) Day of the week with Monday as 1 ( Type of result: integer ) dayOfYear(A) Day of the year with 1 January as 1 ( Type of result: integer ) weekOfYear(A) Compute the week number of a year (0 to 53). According to ISO 8601: Week number 1 of every year contains the 4. of january. ( Type of result: integer ) weekDateYear(A) Compute the year of the ISO 8601 week date ( Type of result: integer ) weekDateWeek(A) Compute the week of the ISO 8601 week date ( Type of result: integer ) toUTC(A) Conversion to Coordinated Universal Time (UTC) julianDayNumber(A) Number of days that have elapsed since January 1, 4713 BC in the proleptic Julian calendar ( Type of result: integer ) julianDayNumToTime(A) Convert julian day number to time ( Type of argument A: integer ) timestamp1970(A) Time expressed in seconds since the Unix Epoch (1970-01-01 00:00:00 UTC) ( Type of result: integer ) compare(A, B) Compare function ( Type of result: integer ) hashCode(A) Hash function ( Type of result: integer ) Statements: A +:= B Increment A by B ( Type of argument B: duration, A +:= B => A := A + B ) A -:= B Decrement A by B ( Type of argument B: duration, A -:= B => A := A - B ) await(A) Wait until the given time5.22 duration
The type 'duration' describes time and date durations.
Prefix operators: + Identity - Change sign Infix operators: + Add two durations - Subtract two durations * Multiply a duration by an integer ( Type of left operand: integer ) * Multiply a duration by an integer ( Type of right operand: integer ) duration parse A Conversion of string to duration ( Type of argument A: string, duration parse "0-02-28 12:00:01" => 0-02-28 12:00:01, duration parse "0-13-29 12:00:01" => EXCEPTION RANGE_ERROR ) Relations: =, <>, <, <=, >, >= Functions: years(A) Years of the duration ( Type of result: integer ) months(A) Months of the duration ( Type of result: integer ) days(A) Days of the duration ( Type of result: integer ) hours(A) Hours of the duration ( Type of result: integer ) minutes(A) Minutes of the duration ( Type of result: integer ) seconds(A) Seconds of the duration ( Type of result: integer ) mycro_seconds(A) Mycro seconds of the duration ( Type of result: integer ) str(A) Conversion to string ( Type of result: string ) compare(A, B) Compare function ( Type of result: integer ) hashCode(A) Hash function ( Type of result: integer ) Statements: wait(A) Wait for given duration5.23 file
The type 'file' describes sequential files.
Constants: STD_NULL Standard null file STD_IN Standard input of the operating system STD_OUT Standard output of the operating system STD_ERR Standard error output of the operating system Variables: IN Standard input file used for file input operations when no file is specified ( IN is initialized with STD_IN ) OUT Standard output file used for file output operations when no file is specified ( OUT is initialized with STD_OUT ) Relations: =, <> Functions: open(A, B) Open external file ( Type of argument A: string, Type of argument B: string, Type of result: file, Returns STD_NULL if open was not possible ) open_utf8(A, B) Open external UTF-8 file ( Type of argument A: string, Type of argument B: string, Type of result: file, Returns STD_NULL if open was not possible ) popen(A, B) Open a pipe to a process ( Type of argument A: string, Type of argument B: string, Type of result: file, Returns STD_NULL if popen was not possible ) openInetSocket(port) Open local internet client socket ( Type of argument port: integer, Type of result: file, Returns STD_NULL if open was not possible ) openInetSocket(addr, port) Open internet client socket ( Type of argument addr: string, Type of argument port: integer, Type of result: file, Returns STD_NULL if open was not possible ) length(A) Length of file A ( Type of result: integer ) tell(A) Return the actual file position ( Type of argument: file, The first position in the file is 1 ) getc(A) Get one character from file A ( Type of result: char ) gets(A, B) Get string with maximum length B from file A ( Type of argument A: integer, Type of argument B: file, Type of result: string, gets(A, -1) => EXCEPTION RANGE_ERROR ) getwd(A) Get one word from file A ( Type of result: string ) getln(A) Get one line from file A ( Type of result: string ) eoln(A) End of line ( Type of result: boolean ) hasNext(A) A call of getc does not return the EOF character ( Type of result: boolean ) eof(A) End of file ( Type of result: boolean ) Statements: write(A, B) Write string B to file A ( Type of argument B: string ) writeln(A) Write a new line to file A writeln(A, B) Write string B and new line to file A ( Type of argument B: string ) read(A, B) Read a word from file A into string B ( Type of right operand: string ) readln(A) Read a line from file A readln(A, B) Read a line from file A into the string B ( Type of right operand: string ) backSpace(A) Write backspace to file A close(A) Close file A flush(A) Flush file A seek(A, B) Set actual file position of file A to B ( Type of argument B: integer, seek(A, 1) => Set to file begin, seek(A, length(A)) => Set to last position, seek(A, length(A) + 1) => Set to end of file, seek(A, -1) => EXCEPTION RANGE_ERROR )5.24 text
The type 'text' describes two dimensional files.
Relations: =, <> Functions: open_window(F, A, B, C, D) Open a text ( Type of argument A: integer, Type of argument B: integer, Type of argument C: integer, Type of argument D: integer ) height(A) Height of the text ( Type of result: integer ) width(A) Width of the text ( Type of result: integer ) line(A) Current line of the text ( Type of result: integer ) column(A) Current column of the text ( Type of result: integer ) Statements: write(A, B) Write string B to text A ( Type of argument B: string ) writeln(A) Write a new line to text A writeln(A, B) Write string B and new line to text A ( Type of argument B: string ) read(A, B) Read a word from text A into string B ( Type of right operand: string ) readln(A) Read a line from text A readln(A, B) Read a line from text A into the string B ( Type of right operand: string ) backSpace(A) Write backspace to text A close(A) Close text A flush(A) Flush text A clear(A) Clear the window v_scroll(A) Scroll the window vertical h_scroll(A) Scroll the window horizontal color(A, B) Set foreground color of the text A ( Type of argument B: color ) color(A, B, C) Set foreground and background color of the text A ( Type of argument B: color, Type of argument C: color ) setPos(A, B, C) Set the current position of the text A ( Type of argument B: integer Type of argument C: integer ) setLine(A, B) Set the current line of the text A ( Type of argument B: integer ) setColumn(A, B) Set the current column of the text A ( Type of argument B: integer ) box(A) Write a box around the window clear_box(A) Clear the box around the window cursor_on(A) Make the cursor visible cursor_off(A) Make the cursor invisible5.25 func
The type 'func baseType' describes functions which return a 'baseType'. For example: 'func integer' describes an 'integer' function.
Values: ord, str, abs, sqrt, rand, A + B, A * B, A ** B, trunc, round, sin, cos, compare, hashCode, pos, replace, trim, length, keys, color, dayOfWeek, ... Every function declared with const func ... is a value Prefix operators: func result var baseType: result is baseType.value; begin statements end func Create a baseType function ( Type of 'statements': proc, Type of result: func baseType ) func result var baseType: result is baseType.value; local declarations begin statements end func Create a baseType function with local variables ( Type of 'declarations': proc, Type of 'statements': proc, Type of result: func baseType ) return value Create a function with the result type of value ( Type of value: anyType - which means: any type, Type of result: func anyType )
Functions are declared as constants with a 'func' type and are initialized with a 'func result ...' or 'return ...' operator. For example:
const func integer: tak (in integer: x, in integer: y, in integer: z) is func result var integer: result is 0; begin if y >= x then result := z; else result := tak(tak(pred(x), y, z), tak(pred(y), z, x), tak(pred(z), x, y)); end if; end func
Another example using the 'return' function:
const func float: convertRadianToDegree (in float: x) is return x * 57.295779513082320876798154814114;
This 'return' function should not be confused with a 'return' statement. It is important to note that no 'return' statement exists. The declaration for the 'return' function is as follows:
const func func aType: return (ref func aType param) is action "PRC_RETURN"; const func func aType: return (ref aType param) is action "PRC_RETURN";
The 'func' types can also be used for parameters. Functions which use a 'func' parameter do not evaluate this parameter before the function call. Instead this parameter can be evaluated zero or more times inside the function. For example:
const func boolean: (in boolean: first) and (in func boolean: second) is func result var boolean: result is FALSE; begin if first then result := second; end if; end func;
Here the second parameter is only evaluated when the first parameter is 'TRUE'.
5.26 varfuncThe type 'varfunc baseType' describes functions which return a 'baseType' variable. For example: A function which returns an 'integer' variable is described with 'varfunc integer'. A call of a 'varfunc' can be used at the left side of an assignment. Generally a 'varfunc' can be used at places where an 'inout' parameter requests a variable.
Prefix operators: return var value; Create a varfunc which returns the variable 'value' ( Type of value: anyType - which means: any type, Accessright of value: var = A variable, an 'inout' parameter or a 'varfunc' Type of result: varfunc anyType )
Varfunctions are used to express 'array', 'hash' and 'struct' accesses which can be used at the left and right side of an assignment. The access function for a 'hash' is defined as:
const func baseType: (in hashType: aHash) [ (in keyType: aKey) ] is return INDEX(aHash, aKey, hashCode(aKey), hashType.keyCompare); const varfunc baseType: (inout hashType: aHash) [ (in keyType: aKey) ] is return var INDEX(aHash, aKey, hashCode(aKey), hashType.keyCompare);
The example above shows that functions with 'in' and 'inout' parameters can be overloaded. At the right side of an assignment the 'func' is called, while at the left side the 'varfunc' is called. That way the access functions of arrays, hashs and structs can be used in the usual way.
5.27 voidThe type 'void' describes the empty type.
Value: empty This is the only value of the type 'void'.5.28 proc
The type 'proc' describes procedures. The type 'proc' is defined as 'func void'.
Values: noop; while ... do ... end while; repeat ... until ... ; writeln( ... ); A := B; incr(A); ... Every procedure declared with const proc: ... is a value The procedure 'noop' does nothing and is used as empty procedure. Prefix operators: func begin statements end func Create a procedure ( Type of 'statements': proc, Type of result: proc ) func local declarations begin statements end func Create a procedure with local variables ( Type of 'declarations': proc, Type of 'statements': proc, Type of result: proc )5.29 type
The type 'type' describes all types.
Values: void, boolean, integer, rational, float, char, string, reference, ref_list, color, time, duration file, proc, type, ... Every type declared with const type: ... is a value The type 'void' is used as empty type. Prefix operators: func Function type ( func char => Function which returns a char ) varfunc Varfunc type ( varfunc char => Function which returns a char variable ) ptr Pointer type ( ptr bitset => Pointer to bitset ) array Array type ( array string => Array of strings ) set of Set type ( set of integer => Set of integer ) subtype Create subtype of existing type ( subtype char => Subtype of char ) Relations: =, <> Functions: str(A) Conversion to string ( Type of result: string ) newtype Create a new type gentype Generate a type gensub(A) Generate a subtype typeof(A) Get the type of an expression ( Type of argument A: Defined for all types, typeof(1) => integer, typeof("asdf") => string ) is_func(A) Is this type a 'func' type ( Type of result: boolean, is_func(func char) => TRUE, is_func(varfunc char) => FALSE ) is_func(char) => FALSE ) is_varfunc(A) Is this type a 'varfunc' type ( Type of result: boolean, is_varfunc(func char) => FALSE, is_varfunc(varfunc char) => TRUE, is_varfunc(char) => FALSE ) result_type(A) Get the result type of a 'func' or 'varfunc' type ( result_type(func char) => char, result_type(char) => EXCEPTION RANGE_ERROR ) base_type(A) Get the base type of an array, pointer or set type ( base_type(array char) => char, base_type(ptr string) => string, base_type(set of integer) => integer ) type_number(A) Get an unique number for a type ( Type of result: integer ) match_obj(A) Get the match object of a type ( Type of result: reference ) compare(A, B) Compare function ( Type of result: integer ) hashCode(A) Hash function ( Type of result: integer ) Statements: const aType: name is value Declare constant 'name' with 'value' var aType: name is value Declare variable 'name' with 'value'5.30 object
The type 'object' is used as meta type for various types. This allows defining common operations for all this types. The type 'object' is not used as element type for container classes since this can be done much better and type save with abstract data types like 'array', 'set', 'hash' and others.
Functions: TRACE_OBJ(A) Write internal information5.31 expr
The type 'expr' is used to describe unmatched expressions. These are expressions where the recognizing of the functions and the type check is not done yet. This is used for example in the definition of function bodies.
Functions: WRITE_EXPR(A) Write expr A to FILE OUT6. PARAMETERS
The following subchapters introduce the parameter types of Seed7.
6.1 'val' parameterThis function appends a comma and a string to the globalStri variable:
const proc: appendStri (val string: stri) is func begin globalStri &:= ","; globalStri &:= stri; end func;
After doing
globalStri &:= "a"; appendStri(globalStri);
the globalStri variable contains the value "a,a". If the function header would be
const proc: appendStri (in string: stri) is func
the globalStri variable would contain the value "a,a,". This difference is because of the following reasons:
For arrays 'in' parameters are equal to 'ref' parameters. When appendStri called with globalStri as parameter an unwanted side effect takes place: Every change of globalStri changes also the 'ref' parameter stri. Changes to the 'ref' parameter would also change the global variable. Such unwanted side effects can also take place between parameters (when at least one parameter is an 'inout' parameter).
In most cases such unwanted side effects are impossible or can be avoided easily. When possible 'in' parameters should be preferred over 'val' parameters.
Syntax:
val_parameter ::= 'val' type_expression ':' identifier_declaration | 'val' type_expression 'param' .
Declaration:
$ syntax expr: .val.().param is -> 40; $ syntax expr: .val.(). : .(expr) is -> 40; const func f_param: val (ref type param) param is action "DCL_VAL1"; const func f_param: val (ref type param) : (ref expr param) is action "DCL_VAL2";6.2 'ref' parameter
The following function defines the primitive action for the semicolon operator:
const proc: (ref void param) ; (ref void param) is noop;
In this definition and other definitions of primitive actions 'ref' parameters are used. For normal functions usually 'in' parameters are used instead of 'ref' parameters:
const func integer: total_length (in array string: arr) is func result var integer: result is 0; local var integer: index is 0; begin for index range 1 to length(arr) do result +:= length(arr[index]); end for; end func;
Above function could also be defined with the following function head:
const func integer: total_length (ref array string: arr) is func
Since for array types (and also for struct types) 'in' parameters are defined to act as 'ref' parameters both definitions are equal. When possible 'in' parameters should be preferred over 'ref' parameters.
Syntax:
ref_parameter ::= 'ref' type_expression ':' identifier_declaration | 'ref' type_expression 'param' .
Declaration:
$ syntax expr: .ref.().param is -> 40; $ syntax expr: .ref.(). : .(expr) is -> 40; const func f_param: ref (ref type param) param is action "DCL_REF1"; const func f_param: ref (ref type param) : (ref expr param) is action "DCL_REF2";6.3 'in' parameter
This function checks if a given number is a prime number:
const func boolean: is_prime (in integer: number) is func result var boolean: result is FALSE; local var integer: count is 2; begin if number = 2 then result := TRUE; elsif number >= 3 then while number rem count <> 0 and count * count <= number do incr(count); end while; result := number rem count <> 0; end if; end func;
The following function defines the ex (outer) product:
const func array array integer: (in array integer: a) ex (in array integer: b) is func return var array array integer: result is 0 times 0 times 0; local var integer: index1 is 1; begin result := length(a) times length(b) times 0; for index1 range 1 to length(a) do for index2 range 1 to length(b) do result[index1][index2] := a[index1] * b[index2]; end for; end for; end func;
Although both examples use 'in' parameters the parameter in the first example is actually a 'val' parameter while the parameters in the second example are actually 'ref' parameters. When a new type is created with the 'newtype' function it is necessary to specify the meaning of the 'in' parameter. This is done with a call of the IN_PARAM_IS_VALUE or the IN_PARAM_IS_REFERENCE function with the new generated type as parameter. If a new type is created with the 'subtype' function this specification is optional since the base type has already a specification of the 'in' parameter.
Syntax:
in_parameter ::= 'in' type_expression ':' identifier_declaration .
Declaration:
$ syntax expr: .in.().param is -> 40; $ syntax expr: .in.(). : .(expr) is -> 40; const func f_param: in (ref type param) param is action "DCL_REF1"; const proc: IN_PARAM_IS_VALUE (ref type: aType) is func begin const func f_param: in (attr aType) : (ref expr param) is action "DCL_VAL2"; end func; const proc: IN_PARAM_IS_REFERENCE (ref type: aType) is func begin const func f_param: in (attr aType) : (ref expr param) is action "DCL_REF2"; end func;6.4 'in var' parameter
This function computes the greatest common divisor:
const func integer: gcd (in var integer: a, in var integer: b) is func result var integer: result is 0; local var integer: help is 0; begin while a <> 0 do help := b rem a; b := a; a := help; end while; result := b; end func;
Syntax:
in_var_parameter ::= 'in var' type-expression ':' identifier_declaration .
Declaration:
$ syntax expr: .in.var.().param is -> 40; $ syntax expr: .in.var.(). : .(expr) is -> 40; const func f_param: in var (ref type param) param is action "DCL_IN1VAR"; const func f_param: in var (ref type param) : (ref expr param) is action "DCL_IN2VAR";6.5 'inout' parameter
This procedure doubles the given parameter 'number':
const proc: double (inout integer: number) is func begin number := 2 * number; end func;
Syntax:
inout_parameter ::= 'inout' type_expression ':' identifier_declaration .
Declaration:
$ syntax expr: .inout.().param is -> 40; $ syntax expr: .inout.(). : .(expr) is -> 40; const func f_param: inout (ref type param) param is action "DCL_INOUT1"; const func f_param: inout (ref type param) : (ref expr param) is action "DCL_INOUT2";6.6 Symbol parameter
Some functions need symbols at fixed places in the parameter list. The following IF-statement requests the keywords 'THEN', 'END' and 'IF' at specific places:
IF condition THEN statement END IF;
After defining the syntax of this IF-statement with
$ syntax expr: .IF.().THEN.().END.IF is -> 25;
the semantic can be defined with:
const proc: IF (in boolean: condition) THEN (in proc: statement) END IF is func begin case condition of when {TRUE}: statement; end case; end func;
The symbol parameters are just written outside the parentheses. A call of this statement could be:
IF value < maximum THEN write(value) END IF;
Syntax:
symbol_parameter ::= name_identifier | special_identifier .6.7 'attr' parameter
This declaration associates a name to the type 'char':
const string: name (attr char) is "char";
This 'name' can be used as follows:
writeln(name(char));
It is possible to overload such declarations:
const string: name (attr boolean) is "boolean"; const string: name (attr float) is "float";
An 'attr' parameter can be used in a function also:
const func char: (attr char) parse (in string: stri) is func result var char: result is ' '; begin if length(stri) >= 1 then result := stri[1]; else raise RANGE_ERROR; end if; end func;
Syntax:
attr_parameter ::= 'attr' type_expression .7. OBJECT ORIENTATION
Many people will be familiar with object-orientation from languages like C++, Smalltalk, and Java. Seed7 follows the route of declaring "interfaces". An interface is a common set of operations supported by an object. For instance cars, motorcycles, lorries and vans can all accelerate or brake, if they are legal to drive on the road they can all indicate right and left.
This view isn't new. C provides a primitive form of interfacing. When you write to a 'file' in C you use the same interface ('fprintf') for hard disk files, console output and printer output. The implementation does totally different things for these files. UNIX has used the "everything is a file" philosophy for ages (even network communication uses the file' interface (see sockets)).
For short: An interface defines which methods are supported while the implementation describes how this is done. Several types with different method implementations can share the same interface.
7.1 Interface and implementationSeed7 uses interface types and implementation types. Objects declared with an interface type refer to a value which has an implementation type. This situation is described with the following picture:
+----------------+ declared | interface |<--- interface type object: | object | (known at compile-time) +----------------+ | | refer to value V +----------------+ value: | implementation |<--- implementation type | object | (unknown at compile-time) +----------------+
The interface type of an object can always be determined at compile-time. Several implementation types can belong to one interface type (they implement the interface type). E.g.: The types 'null_file', 'external_file' and 'socket' implement the 'file' interface. On the other hand: An implementation type can also implement several interface types. An interface object can only refer to a value with an implementation type that implements the interface. E.g.: A 'shape' variable cannot refer to a 'socket'.
A new interface type is declared with:
const type: shape is new interface;
Interface (DYNAMIC) functions describe what can be done with objects of an interface type. An interface function for a 'shape' could be:
const proc: draw (in shape param, inout window param) is DYNAMIC;
Now we know that it is possible to 'draw' a 'shape' to a 'window'. How this drawing is done is described in the implementation type. An implementation type for 'shape' is:
const type: circle is new struct var integer: radius is 0; end struct;
The fact that the type 'circle' is an implementation type of 'shape' is described with:
type_implements_interface(circle, shape);
The function which implements 'draw' for 'circle's is:
const proc: draw (in circle: aCircle, inout window: aWindow) is func begin circle(aWindow.win, aWindow.currX, aWindow.currY, aCircle.radius, aWindow.foreground); end func;
In the classic OOP philosophy a message is sent to an object. To express this situation classic OO languages use the following method call syntax:
param1.method(param2, param3)
In the method the receiving object is referred with 'self' or 'this'. The other parameters use the same mechanisms as in procedural programming languages (value or reference parameter). Seed7 uses a different approach: Instead of an implicit defined 'self' or 'this' parameter, all formal parameters get a user defined name. To reflect this symmetric approach a Seed7 method call looks like a normal function call:
method(param1, param2, param3)
The definition of the 'draw' function above uses the formal parameter 'aCircle' in the role of a 'self' or 'this' parameter. Formal parameters which have an implementation type are automatically in the role of a 'self' or 'this' parameter.
A function to create new circle objects can also be helpful:
const func circle: circle (in integer: radius) is func result var circle: result is circle.value; begin result.radius := radius; end func;
Now we can draw a 'circle' object with:
draw(circle(50), aWindow);
Although the statement above does exactly what it should do and the separation between interface and implementation is obvious, most OO enthusiasts would not be thrilled. All decisions which implementation function should be called can be made at compile time. To please the OO fans such decisions must be made at runtime. This decision process is called dynamic dispatch.
7.2 Dynamic dispatchWhen the implementation types have different implementations of the same function (method) a dynamic dispatch is necessary. The type of the value, referred by an interface object, is not known at compile-time. In this case the program must decide at runtime which implementation of the function should be invoked. This decision is based on the implementation type of the value (referred by the interface object). A dynamic dispatch only takes place when a DYNAMIC (or interface) function is called. When the program is analyzed (in the interpreter or compiler) the interface functions take precedence over normal functions when both are to be considered.
To demonstrate the dynamic dispatch we define the type 'line' which also implements a 'shape':
const type: line is new struct var integer: xLen is 0.0; var integer: yLen is 0.0; end func; type_implements_interface(line, shape); const proc: draw (in line: aLine, in window: aWindow) is func begin line(aWindow.win, aWindow.currX, aWindow.currY, aLine.xLen, aLine.yLen, aWindow.foreground); end func; const func line: line (in integer: xLen, in integer: yLen) is func result var line: result is line.value; begin result.xLen := xLen; result.yLen := yLen; end func;In addition we define a normal (not DYNAMIC) function which draws 'shape's to the 'currWindow':
const proc: draw (in shape: aShape) is func begin draw(aShape, currWindow); end func;
In the example above the call of the (DYNAMIC) interface function is 'draw(aShape, currWindow)'. The interface function declared with
const proc: draw (in shape param, inout window param) is DYNAMIC;
decides which implementation function has to be called. The dynamic dispatch works as follows:
This process describes the principal logic of the dynamic dispatch. In practice it is not necessary to execute the analyze part of the compiler during the runtime. It is possible to simplify this process with tables and function pointers.
7.3 InheritanceWhen a new 'struct' type is defined it is possible to inherit from an existing 'struct' type. E.g.:
const type: external_file is sub null_file struct var PRIMITIVE_FILE: ext_file is PRIMITIVE_NULL_FILE; var string: name is ""; end struct;
That way the type 'external_file' inherits the fields and methods of 'null_file', which is declared as:
const type: null_file is new struct var char: bufferChar is '\n'; var boolean: io_empty is FALSE; var boolean: io_ok is TRUE; end struct;
In most situations it makes sense when the implementation types inherit from a basic implementation type such as 'null_file'. That way it is possible to define functions which are inherited by all derived implementation types. In the standard library the function 'getln' is such a function:
const func string: getln (inout null_file: aFile) is func result var string: stri is ""; local var string: buffer is ""; begin buffer := gets(aFile, 1); while buffer <> "\n" and buffer <> "" do stri &:= buffer; buffer := gets(aFile, 1); end while; aFile.bufferChar := buffer[1]; end func;
All inherited types of 'null_file' inherit the function 'getln', but they are also free to redefine it. In the 'getln' function above the function call 'gets(aFile, 1)' uses the (DYNAMIC) interface function:
const func string: gets (inout file param, in integer param) is DYNAMIC;
In other OO languages the distinction between interface type and basic implementation type is not done. Such languages either use a dynamic dispatch for every method call (as Java does) or need a keyword to request a dynamic dispatch (as C++ does with the 'virtual' keyword).
When assignments take place between inherited implementation types it is important to note that structure assignments are done with (deep) copies. Naturally such assignments can only copy the elements that are present in both structures. In the following example just the 'null_file' elements are copied from 'anExternalFile' to 'aNullFile':
const proc: example is func local var null_file: aNullFile is null_file.value; var external_file: anExternalFile is external_file.value; begin aNullFile := anExternalFile; write(aNullFile, "hello"); end func;
Although the variable 'anExternalFile' is assigned to 'aNullFile', the statement 'write(aNullFile, "hello")' calls the 'write' function (method) of the type 'null_file'.
A new interface type can also inherit from an existing interface type:
const type: shape is sub object interface;
Although inheritance is a very powerful feature it should be used with care. In many situations it makes more sense that a new type has an element of another type (so called has-a relation) instead of inheriting from that type (so called is-a relation).
7.4 Class methodsMany object-oriented programming languages support methods that are associated with a class instead of an instanciated object. Such methods are called class methods or static methods. Seed7 supports class methods via attribute ('attr') parameters which allow that a function is attached to a type:
const func circle: create (attr circle, in integer: radius) is return circle(radius);
This 'create' function is attached to the type 'circle' and can be called with
create(circle, 10)
Many languages require that the class name must precede the method name when a class method is called (E.g. 'circle::create(10)' in C++). In contrast to that 'attr' parameters are not restricted to a specific parameter position. They can be used in any parameter position as in the following example:
const func circle: create (in integer: radius, attr circle) is return circle(radius);
This function can be called with
create(10, circle)
Attribute parameters can be used for any type not just for interface and implementation types. Objects which do not have a function type such as a character constant can also be attached to a type:
const char: (attr char) . value is ' ';
This way attributes can be used to specify propertys of a type such as its default 'value'. Programming languages such as Seed7 which support function definitions outside a class can also use normal functions instead of class methods. It is a matter of tast if a function should be grouped to a type or if it should exist stand alone and is called with:
circle(10)
7.5 Multiple dispatch
The Seed7 object system allows multiple dispatch (not to be confused with multiple inheritance). The methods are not assigned to one type (class). The decision which function (method) is called at runtime is done based upon the types of several arguments. The classic object orientation is a special case where a method is connected to one class and the dispatch decision is done based on the type of the 'self' or 'this' parameter. The classic object orientation is a single dispatch system.
In the following example the type 'Number' is introduced which is capable to unify numerical types. The type 'Number' is an interface type which defines the interface function for the '+' operation:
const type: Number is sub object interface; const func Number: (in Number param) + (in Number param) is DYNAMIC;
The interface type 'Number' can represent an 'Integer' or a 'Float':
const type: Integer is new struct var integer: val is 0; end struct; type_implements_interface(Integer, Number); const type: Float is new struct var float: val is 0.0; end struct; type_implements_interface(Float, Number);
The declarations of the converting '+' operators are:
const func Float: (in Integer: a) + (in Float: b) is func result var Float: result is Float.value; begin result.val := flt(a.val) + b.val; end func; const func Float: (in Float: a) + (in Integer: b) is func result var Float: result is Float.value; begin result.val := a.val + flt(b.val); end func;
The declarations of the normal '+' operators (which do not convert) are:
const func Integer: (in Integer: a) + (in Integer: b) is func result var Integer: result is Integer.value; begin result.val := a.val + b.val; end func; const func Float: (in Float: a) + (in Float: b) is func result var Float: result is Float.value; begin result.val := a.val + b.val; end func;
The type 'Number' can be extended to support other operators and there can be also implementations using 'complex', 'bigInteger', 'bigRational', etc. . That way 'Number' can be used as universal type for math calculation. Further extending can lead to an universal type. Such an universal type is loved by proponents of dynamic typed languages, but there are also good reasons to have distinct types for different purposes.
7.6 Replacing pointers with interface typesMany languages have the concept of a pointer. It is possible to implement data structures, such as lists and trees, with pointers. Although Seed7 supports the concept of a pointer, they are not well suited to describe such data structures. Instead of pointers interface types can be used. This way list, trees and other advanced data structures can be defined.
The following example shows how to do this: The interface type 'element' will be used as "pointer":
const type: element is new interface;
An implementation type for the empty 'element' (emptyElement) can be used as basic implementation type from which other implementation types can inherit:
const type: emptyElement is new struct end struct;
That the implementation type 'emptyElement' implements the interface type 'element' is described with:
type_implements_interface(emptyElement, element);
Since every Seed7 expression has exactly one type, it is necessary to define a special 'NIL' value (used with 'element.NIL') for the type 'element':
const element: (attr element) . NIL is emptyElement.value;
Now the struct with two "pointers" and an 'integer' can be declared:
const type: treeElement is sub emptyElement struct var element: left is element.NIL; var element: right is element.NIL; var integer: item is 0; end struct;
Finally the type 'treeElement' is defined as implementation of the type 'element':
type_implements_interface(treeElement, element);
To allow the direct access to the structure elements 'left', 'right' and 'item' for objects of type 'element' the following declarations are necessary:
const func element: (ref element param).left is DYNAMIC; const varfunc element: (inout element param).left is DYNAMIC; const func element: (ref element param).right is DYNAMIC; const varfunc element: (inout element param).right is DYNAMIC; const func integer: (ref element param).item is DYNAMIC; const varfunc integer: (inout element param).item is DYNAMIC;
When all this was declared the following code is possible:
const proc: addItem (inout element: anElem, in integer: item) is func begin if anElem = element.NIL then anElem := xalloc(treeElement.value); anElem.item := item; elsif item < anElem.item then addItem(anElem.left, item); elsif item > anElem.item then addItem(anElem.right, item); end if; end func; const proc: listItems (in element: anElem) is func begin if anElem <> element.NIL then listItems(anElem.left); write(" " <& anElem.item); listItems(anElem.right); end if; end func; const func integer: sum (in element: anElem) is func result var integer: result is 0; begin if anElem <> element.NIL then result := anElem.item + sum(anElem.left) + sum(anElem.right); end if; end func;
New elements can be created with the function 'xalloc'. This way interface and implementation types help to provide the pointer functionality.
Pointers and interface types are not always the best solution. Abstract data types like dynamic arrays, hash tables, struct types and set types can also be used to declare data structures.
8. THE FILE SYSTEMThe file system is used for communication in various ways. For example: To write strings on the screen we use the following statements:
write("hello world"); writeln;
'writeln' means write newline. We can also write data of various types with 'write':
write("result = "); write(number div 5); write(" "); writeln(not error);
The 'writeln' above writes data and then terminates the line. This is equal to a 'write' followed by a 'writeln'. Instead of multiple write statements the '<&' operator can be used to concatenate the elements to be written:
writeln("result = " <& number div 5 <& " " <& not error);
The '<&' operator needs a 'string' as left operand and is overloaded for various types as right operand. To allow things like
write(next_time <& " \r");
the '<&' operator is also overloaded for various types as left operand and a 'string' as right operand. This allows you to concatenate several objects with '<&' when at least the first or the second object is a 'string'. We can also read data from the keyboard:
write("Amount? "); read(amount);
The user is allowed to use backspace and sends the input to the program with the RETURN-key. To let the user respond with the RETURN-key we can write:
writeln("Type RETURN"); readln;
To read a line of data we can use 'readln':
write("Your comment? "); readln(user_comment_string);
In the previous examples all 'read' statements read from the file IN and all 'write' statements write to the file OUT. The files IN and OUT are initialized with STD_IN and STD_OUT which are the stdin and stdout files of the operating system. (Usually the keyboard and the screen). When we want to write to other files we use write statements with the file as first parameter. To write a line of text to the file "info.fil" we use the following statements:
info_file := open("info.fil", "w"); writeln(info_file, "This is the first line of the info file."); close(info_file);
First the external file is opened for writing and then it is used. To read the file back in the string 'stri' we write:
info_file := open("info.fil", "r"); readln(info_file, stri); close(info_file);
It is also possible to write values of other types to 'info_file':
writeln(info_file, number);
Here the 'number' is converted to a string which is written to the file. A 'number' is read back with:
readln(info_file, number);
For doing I/O to a window on the screen we write:
window1 := open_window(SCREEN, 10, 10, 5, 60); box(window1); setPos(window1, 3, 1); write(window1, "hello there");
This opens the window 'window1' on the SCREEN at the position 10, 10. This window has 5 lines and 60 columns. A box (of characters: - | + ) is written to surround the 'window1' and finally the string "hello there" is written in the window 'window1' at Position 3, 1. If we want to clear the 'window1' we write:
clear(window1);
Files can be used for much more things. Here is a list of goals for a file system:
In the following subchapters we discuss each of these goals.
8.1 Conversion to strings and backWe archive the goal of doing I/O for arbitrary types with two conversion functions. In order to do I/O with a type the 'str' and 'parse' functions must be defined for that type. As an example we show the conversion functions for the type 'boolean':
const func string: str (in boolean: aBool) is func result var string: result is ""; begin if aBool then result := "TRUE"; else result := "FALSE"; end if; end func; const func boolean: (attr boolean) parse (in string: stri) is func result var boolean: result is FALSE; begin if stri = "TRUE" then result := TRUE; elsif stri = "FALSE" then result := FALSE; else raise RANGE_ERROR; end if; end func;
The 'str' function must deliver a corresponding string for every value of the type. The 'parse' function parses a string and delivers the converted value as result. If the conversion is not successful the exception RANGE_ERROR is raised. The attribute used with 'parse' allows that it is overloaded for different types.
After defining the 'str' and 'parse' functions for a type the 'enable_io' function can be called for this type as in:
enable_io(boolean);
The 'enable_io' template declares various io functions like 'read', 'write' and others for the provided type (in this example 'boolean'). If only output (or only input) is needed for a type it is possible to define just 'str' (or 'parse') and activate just enable_output (or enable_input).
There is also a formatting operator called 'lpad' which is based on the 'str' function. The statements
write(12 lpad 6); write(3 lpad 6); writeln(45 lpad 6); write(678 lpad 6); write(98765 lpad 6); writeln(4321 lpad 6);
produce the following output:
12 3 45 678 98765 4321
As we see the 'lpad' operator can be used to produce right justified output. There is also a 'rpad' operator to produce left justified output. The basic definitions of the 'lpad' and 'rpad' operators work on strings and are as follows:
const func string: (ref string: stri) lpad (in integer: leng) is func result var string: result is ""; begin if leng > length(stri) then result := " " mult leng - length(stri) & stri; else result := stri; end if; end func; const func string: (ref string: stri) rpad (in integer: leng) is func result var string: result is ""; begin if leng > length(stri) then result := stri & " " mult leng - length(stri); else result := stri; end if; end func;
The 'enable_io' template contains definitions of 'lpad' and 'rpad' to work on the type specified with enable_io:
const func string: (in aType: aValue) lpad (in integer: leng) is func result var string: stri is ""; begin stri := str(aValue) lpad leng; end func; const func string: (in aType: aValue) rpad (in integer: leng) is func result var string: stri is ""; begin stri := str(aValue) rpad leng; end func;
For 'float' values exists an additional way to convert them to strings. The 'digits' operator allows the specification of a precision. For example the statements
writeln(3.1415 digits 2); writeln(4.0 digits 2);
produce the following output:
3.14 4.00
A combination with the 'lpad' operator as in
writeln(3.1415 digits 2 lpad 6); writeln(99.9 digits 2 lpad 6);
is also possible and produces the following output:
3.14 99.908.2 Basic input and output operations
To allow arbitrary user defined file-types beside the operating system files we chose a model in which the I/O methods are assigned to the type of the file-value and not to the type of the file-variable. This allows a file variable to point to any file-value. The file-variables have the type 'file' which has only the assignment method defined. For the operating system files and for each user defined file a file-type must be declared which has the I/O methods defined. These file-types are derived (direct or indirect) from the type 'null_file' for which all I/O methods are defined upon a base of basic string I/O methods. So for a new user defined file-type only the basic string I/O methods must be defined.
The two basic I/O methods defined for the 'null_file' are
const proc: write (ref null_file param, in string param) is noop; const string: gets (ref null_file param, ref integer param) is "";
This means that writing any string to the 'null_file' has no effect and reading any number of characters from the 'null_file' delivers the empty string. When a user defined file type is declared these are the two methods that must be redefined for the new file-type. Based upon these two methods three more methods are defined for the 'null_file' named 'getc', 'getwd' and 'getln'. These methods get a character, a word and a line respectively. A word is terminated by a space, a tab or a linefeed. A line is terminated by a linefeed. This methods need not to be redefined for a user defined file type but for performance reasons they can also be redefined. The definitions for 'getc', 'getwd' and 'getln' for the 'null_file' are
const func char: getc (ref null_file: aFile) is func result var char: ch is ' '; begin ch := gets(aFile, 1)[1]; end func; const func string: getwd (inout null_file: aFile) is func result var string: stri is ""; local var string: buffer is ""; begin repeat buffer := gets(file conv aFile, 1); until buffer <> " " and buffer <> "\t"; while buffer <> " " and buffer <> "\t" and buffer <> "\n" and buffer <> "" do stri &:= buffer; buffer := gets(file conv aFile, 1); end while; if buffer = "" then aFile.bufferChar := EOF; else aFile.bufferChar := buffer[1]; end if; end func; const func string: getln (inout null_file: aFile) is func result var string: stri is ""; local var string: buffer is ""; begin buffer := gets(file conv aFile, 1); while buffer <> "\n" and buffer <> "" do stri &:= buffer; buffer := gets(file conv aFile, 1); end while; if buffer = "" then aFile.bufferChar := EOF; else aFile.bufferChar := buffer[1]; end if; end func;
Note that 'getwd' skips leading spaces and tabs while 'getc' and 'getln' do not. When 'getc', 'getwd' or 'getln' is not defined for a new user defined file type the declarations from the 'null_file' are used instead. These declarations are based on the method 'gets' which must be defined for every new user defined file-type.
Note that there is an assignment to the variable 'bufferChar'. This variable is a component of 'null_file' and therefore also a component of all derived file types. This allows an 'eoln' function to test if the last 'getwd' or 'getln' reach the end of a line. Here is a definition of the 'eoln' function:
const func boolean: eoln (ref null_file: aFile) is func result var boolean: result is TRUE; begin result := aFile.bufferChar = '\n'; end func;
Besides assigning a value to 'bufferChar' in 'getwd' and 'getln' and using it in 'eoln' the standard 'file' functions do nothing with 'bufferChar'. The functions of the "scanfile.s7i" library use the 'bufferChar' variable as current character in the scan process. As such all functions of the "scanfile.s7i" library assume that the first character to be processed is always in 'bufferChar'. Since the standard 'file' functions do not have this behaviour, care has to be taken when mixing scanner and file functions.
The next declarations allows various I/O operations for strings:
const proc: writeln (in file: aFile, in string: stri) is func begin write(aFile, stri); writeln(aFile); end func; const proc: read (inout file: aFile, inout string: stri) is func begin stri := getwd(aFile); aFile.io_empty := stri = ""; aFile.io_ok := TRUE; end func; const proc: readln (inout file: aFile, inout string: stri) is func begin stri := getln(aFile); aFile.io_empty := stri = ""; aFile.io_ok := TRUE; end func;8.3 Input and output with conversion
Normally we need a combination of an I/O operation with a conversion operation. There are several functions which are based on the 'str' and 'parse' conversions and on the basic I/O-functions. The following declarations allow the 'write' function to be used for all types which define 'enable_io':
const proc: write (in file: aFile, in aType: aValue) is func begin write(aFile, str(aValue)); end func;
To allow the use of the 'read' and 'readln' functions the following declarations are made with 'enable_io':
const proc: read (inout file: aFile, inout aType: aValue) is func local var string: stri is ""; begin stri := getwd(aFile); aFile.io_empty := stri = ""; block aValue := aType parse stri; aFile.io_ok := TRUE; exception catch RANGE_ERROR: aFile.io_ok := FALSE; end block; end func; const proc: readln (inout file: aFile, inout aType: aValue) is func local var string: stri is ""; begin stri := getln(aFile); aFile.io_empty := stri = ""; block aValue := aType parse stri; aFile.io_ok := TRUE; exception catch RANGE_ERROR: aFile.io_ok := FALSE; end block; end func;
The next two declarations define 'writeln' and 'backSpace':
const proc: writeln (ref external_file: aFile) is func begin write(aFile, "\n"); end func; const proc: backSpace (ref external_file: aFile) is func begin write(aFile, "\b \b"); end func;8.4 Simple read and write statements
The simple input/output for the standard I/O-files are 'read' and 'write' which are defined with 'enable_io'. Simple I/O may look like:
write("Amount? "); read(amount);
'read' and 'write' use the files IN and OUT which are described in the next chapter. Here is the definition of the 'read' and 'write' procedures done with 'enable_io':
const proc: read (inout aType: aValue) is func begin read(IN, aValue); end func; const proc: readln (inout aType: aValue) is func begin readln(IN, aValue); end func; const proc: write (in aType: aValue) is func begin write(OUT, aValue); end func; const proc: writeln (in aType: aValue) is func begin write(OUT, aValue); writeln(OUT); end func;
Additional procedures defined outside of 'enable_io' are:
const proc: readln is func local var string: stri is ""; begin stri := getln(IN); IN.io_empty := stri = ""; IN.io_ok := TRUE; end func; const proc: read (NL) is func begin readln; end func; const proc: writeln is func begin writeln(OUT); end func; const proc: write (NL) is func begin writeln(OUT); end func;
As an example when you call
readln(number);
the readln(integer) procedure calls
readln(IN, number);
if the file IN has not redefined readln(IN, integer) this procedure calls
stri := getln(IN);
and 'getln' may call gets(IN, 1) in a loop or may be defined for the file IN. Finally the 'parse' function converts the string read into an 'integer' and assigns it to 'number'
number := integer parse stri;8.5 Standard input and output files
The standard I/O files are OUT for output and IN for input. This TWO are file-variables which are declared as follows:
var file: IN is STD_IN; var file: OUT is STD_OUT;
STD_IN and STD_OUT are the standard input and output files of the operating system (Usually the keyboard and the screen). Because IN and OUT are variables redirection of standard input or standard output can be done easily by assigning a new value to them:
IN := OTHER_FILE;
After that all 'read' statements refer to OTHER_FILE. Most operating systems have also a stderr file which can be accessed via the name STD_ERR. If you want to write error messages to the screen even when stdout is redirected elsewhere you can write:
writeln(STD_ERR, "ERROR MESSAGE");
To redirect the standard output to STD_ERR you can write:
OUT := STD_ERR;
There is also a file STD_NULL defined. Anything written to it is ignored. Reading from it does deliver empty strings. This file can be used to initialize file variables as in:
var file: MY_FILE is STD_NULL;
It is also used to represent an illegal file value when for example an 'open' procedure fails.
8.6 Access to operating system filesThe access to operating system files is done via files of the types 'external_file', KEYBOARD_FILE and SCREEN_FILE. The type 'external_file' is defined as:
const type: external_file is sub null_file struct var PRIMITIVE_FILE: ext_file is PRIMITIVE_null_file; var string: name is ""; end struct;
This means that every data item of the type 'external_file' has the components from 'null_file' and additionally the components ext_file and name. Note the type 'PRIMITIVE_FILE' which points directly to an operating system file. Objects of type 'PRIMITIVE_FILE' can only have operating system files as values while objects of type 'file' can also have other files as values. To allow the implementation of the type 'external_file' several operations for the type 'PRIMITIVE_FILE' are defined. But outside 'external_file' the type 'PRIMITIVE_FILE' and its operations should not be used.
There are three predefined external files STD_IN, STD_OUT and STD_ERR which have the following declarations:
const func external_file: INIT_STD_FILE (ref PRIMITIVE_FILE: primitive_file, in string: file_name) is func result var external_file: result is external_file.value; begin result.ext_file := primitive_file; result.name := file_name; end func; var external_file: STD_IN is INIT_STD_FILE(PRIMITIVE_INPUT, "STD_IN"); var external_file: STD_OUT is INIT_STD_FILE(PRIMITIVE_OUTPUT, "STD_OUT"); var external_file: STD_ERR is INIT_STD_FILE(PRIMITIVE_ERROR, "STD_ERR");
It is possible to do I/O directly with them, but it is more wisely to use them only to initialize user defined file variables as in:
var file: ERR is STD_ERR;
In the rest of the program references to such a variable can be used:
writeln(ERR, "Some error occurred");
In this case redirection of the ERR file can be done very easy. The second way to access external files is to use the 'open' function. Usually a file variable is declared
var file: MY_OUT is STD_NULL;
and the result of the 'open' function is assigned to this file variable
MY_OUT := open("my_file", "w");
If the 'open' has failed it returns STD_NULL so we must check the file variable to be on the save side
if MY_OUT <> STD_NULL then
After that output to MY_OUT is possible with
writeln(MY_OUT, "hi there");
As stated earlier STD_IN provides an interface to the keyboard which is line buffered and echoed on STD_OUT. This means that you can see everything you typed in and correct it with BACKSPACE until you press RETURN. But sometimes an unbuffered and unechoed input is needed. This is provided with the file KEYBOARD. This are the declaration of the type KEYBOARD_FILE and the file KEYBOARD itself:
const type: KEYBOARD_FILE is subtype file; var KEYBOARD_FILE: KEYBOARD is SCREEN_KEYBOARD;
Reading from KEYBOARD may deliver simple ASCII characters or special codes for function keys. These special codes can also be copied to a character variables because the type 'char' is not limited to 8 bits. For each function key exists a predefined constant that can be used to test which key is pressed.
Additionally to the operations possible with other files there are two functions that are applicable only for the file KEYBOARD. The busy_getc(KEYBOARD) function delivers the next character from the keyboard or the character KEY_NONE if no key has been pressed. The busy_gets(KEYBOARD, integer) function delivers a string consisting of the characters in the keyboard buffer but with the maximum length which is specified in the second parameter.
To allow random access output to a text screen (or text window) the type SCREEN_FILE is defined. The function
open(SCREEN_FILE)
returns a SCREEN_FILE.
8.7 User defined file typesIn addition to the predefined file types it is often necessary to define a new type of file. Such a new file has several possibilities:
With the following declaration we define a new file type:
const type: NEW_FILE is sub null_file struct ... (* Local data *) ... end struct;
It is not necessary to derive the NEW_FILE type directly from 'null_file'. The NEW_FILE type may also be an indirect descendant of 'null_file'. So it is possible to create file type hierarchies. The interface implemented by the new file needs also to be specified:
type_implements_interface(NEW_FILE, file);
The type 'file' is not the only interface type which can be used. There is also the type 'text' which is derived from 'file'. The type 'text' describes a line oriented file which allows 'setPos' (which moves the current position to the line and column specified) and other functions. It is also possible to define new interface types which derive from 'file' or 'text'.
As next an open function is needed to generate a new NEW_FILE:
const func file: open_new_file ( (* Parameters *) ) is func result var file: result is STD_NULL; begin ... (* Initialisation of the local data *) result := malloc( ... ); ... end func;
Note the usage of the 'malloc' function to generate a new data item. This data item is not freed automatically but if you do not open files to often this does not hurt. Now only the two basic I/O operations must be defined:
const proc: write (inout NEW_FILE: new_fil, in string: stri) is func begin ... (* Statements that do the output *) ... end func; const proc: gets (inout NEW_FILE: new_fil, in integer: leng) is func result var string: stri is ""; begin ... (* Statements that do the input *) ... end func;
The I/O concept introduced in the previous chapters separates the input of data from its conversion. The 'read', 'readln', 'getwd' and 'getln' functions are designed to read whitespace separated data elements. When the data elements are not separated by whitespace characters this I/O concept is not possible. Instead the functions which read from the file need some knowledge about the type which they intend to read. Fortunately this is a well researched area. The lexical scanners used by compilers solve exactly this problem.
Lexical scanners read symbols from a file and use the concept of a current character. A symbol can be a name, a number, a string, an operator, a parenthesis or something else. The current character is the first character to be processed when scanning a symbol. After a scanner has read a symbol the current character contains the character just after the symbol. This character could be the first character of the next symbol or some whitespace character. If the set of symbols is chosen wisely all decisions about the type of the symbol and when to stop reading characters for a symbol can be done based on the current character.
Every 'file' contains a 'bufferChar' variable which is used as current character by the scanner functions defined in the "scanfile.s7i" library. The "scanfile.s7i" library contains skip... and get... functions. The skip... procedures return void and are used to skip input while the get... functions return the string of characters they have read. The following basic scanner functions are defined in the "scanfile.s7i" library:
Contrary to 'read' and 'getwd' basic scanner functions do not skip leading whitespace characters. To skip whitespace characters one of the following functions can be used:
The advanced scanner functions do skip whitespace characters before reading a symbol:
All scanner functions assume that the first character to be processed is in 'bufferChar' and after they are finished the next character which should be processed is also in 'bufferChar'. To use scanner functions for a new opened file it is necessary to assign the first character to the 'bufferChar' with:
myFile.bufferChar := getc(myFile);
In most cases whole files are either processed with normal I/O functions or with scanner functions. When normal I/O functions need to be combined with scanner functions care has to be taken:
Scanner functions are helpful when it is necessary to read numeric input without failing when no digits are present:
skipWhiteSpace(IN); if eoln(IN) then writeln("empty input"); elsif IN.bufferChar in {'0' .. '9'} then number := integer parse getDigits(IN); skipLine(IN); writeln("number " <& number); else stri := getLine(IN); writeln("command " <& literal(stri)); end if;
The function 'getSymbol' is designed to read Seed7 symbols. When the end of the file is reached it returns "". With 'getSymbol' name-value pairs can be read:
name := getSymbol(inFile); while name <> "" do if name <> "#" and getSymbol(inFile) = nt color=maroon>"="/font> then aValue = getSymbol(inFile); if aValue <> "" then if aValue[1] = '"' then keyValueHash @:= [name] aValue[2 ..]; elsif aValue[1] in {'0' .. '9'} then keyValueHash @:= [name] aValue; end if; end if; end if; end while;
The following loop can be used to process the symbols of a Seed7 program:
inFile.bufferChar := getc(inFile); currSymbol := getSymbol(inFile); while currSymbol <> "" do ... process currSymbol ... currSymbol := getSymbol(inFile); end while;
Whitespace and comments are automatically skipped with the function 'getSymbol'. When comments should also be returned the function 'getSymbolOrComment' can be used. Together with the function 'getWhiteSpace' it is even possible to get the whitespace between the symbols:
const func string: processFile (in string: fileName) is func result var string: result is ""; local var file: inFile is STD_NULL; var string: currSymbol is ""; begin inFile := open(fileName, "r"); if inFile <> STD_NULL then inFile.bufferChar := getc(inFile); result := getWhiteSpace(inFile); currSymbol := getSymbolOrComment(inFile); while currSymbol <> "" do result &:= currSymbol; result &:= getWhiteSpace(inFile); currSymbol := getSymbolOrComment(inFile); end while; end if; end func;
In the example above the function 'processFile' gathers all symbols, whitespace and comments in the string it returns. The string returned by 'processFile' is equivalent to the one returned by the function 'getf'. That way it is easy to test the scanner functionality.
The logic with 'getWhiteSpace' and 'getSymbolOrComment' can be used to add HTML tags to comments and literals. The following function colors comments with green, string and char literals with maroon and numeric literals with purple:
const proc: sourceToHtml (inout file: inFile, inout file: outFile) is func local var string: currSymbol is ""; begin inFile.bufferChar := getc(inFile); write(outFile, "<pre>\n"); write(outFile, getWhiteSpace(inFile)); currSymbol := getSymbolOrComment(inFile); while currSymbol <> "" do currSymbol := replace(currSymbol, "&", "&"); currSymbol := replace(currSymbol, "<", "<"); if currSymbol[1] in {'"', '''} then write(outFile, "<font color=\"maroon\">"); write(outFile, currSymbol); write(outFile, "</font>"); elsif currSymbol[1] = '#' or startsWith(currSymbol, "(*") then write(outFile, "<font color=\"green\">"); write(outFile, currSymbol); write(outFile, "</font>"); elsif currSymbol[1] in digit_char then write(outFile, "<font color=\"purple\">"); write(outFile, currSymbol); write(outFile, "</font>"); else write(outFile, currSymbol); end if; write(outFile, getWhiteSpace(inFile)); currSymbol := getSymbolOrComment(inFile); end while; write(outFile, "</pre>\n"); end func;
The functions 'skipSpace' and 'skipWhiteSpace' are defined in the "scanfile.s7i" library as follows:
const proc: skipSpace (inout file: inFile) is func local var char: ch is ' '; begin ch := inFile.bufferChar; while ch = ' ' do ch := getc(inFile); end while; inFile.bufferChar := ch; end func; const proc: skipWhiteSpace (inout file: inFile) is func begin while inFile.bufferChar in white_space_char do inFile.bufferChar := getc(inFile); end while; end func;
The functions 'skipComment' and 'skipLineComment', which can be used to skip Seed7 comments, are defined as follows:
const proc: skipComment (inout file: inFile) is func local var char: character is ' '; begin character := getc(inFile); repeat repeat while character not in special_comment_char do character := getc(inFile); end while; if character = '(' then character := getc(inFile); if character = '*' then skipComment(inFile); character := getc(inFile); end if; end if; until character = '*' or character = EOF; if character <> EOF then character := getc(inFile); end if; until character = ')' or character = EOF; if character = EOF then inFile.bufferChar := EOF; else inFile.bufferChar := getc(inFile); end if; end func; # skipComment const proc: skipLineComment (inout file: inFile) is func local var char: character is ' '; begin repeat character := getc(inFile); until character = '\n' or character = EOF; inFile.bufferChar := character; end func; # skipLineComment
9. STRUCTURED SYNTAX DEFINITION
Most programming languages have only predefined constructs like statements and operators. Seed7, on the other hand, additionally allows user defined constructs. This chapter introduces the Seed7 Structured Syntax Description (S7SSD) which is used to define the syntax of new constructs. The syntax of predefined constructs is also defined with S7SSD.
The syntax descriptions used in manuals of conventional programming languages have no relationship to the approach used by the syntax analysis of the corresponding interpreters/compilers. S7SSD is a simple syntax description that can be used by humans and compilers/interpreters. Although compiler-compilers follow the path of machine readable syntax descriptions, they use much more complicated syntax and semantic descriptions and do not allow users of the language to define new constructs.
There are different existing notations to specify the syntax of programming languages. Backus-Naur Form (BNF) and its variants like Extended Backus-Naur Form (EBNF) are examples of such syntax specifications. Since it is easier to understand new concepts when they are compared to well known concepts, EBNF will be used as a base to explain S7SSD.
9.1 The Extended Backus-Naur FormAs the name says the Extended Backus-Naur Form is an extension of BNF. The extension allows the definition of repetitions and optional parts without the use of recursion. EBNF has the following elements:
syntax_description ::= { statement } . statement ::= identifier '::=' expression '.' . expression ::= term { '|' term } . term ::= factor { factor } . factor ::= identifier | string | '(' expression ')' | '[' expression ']' | '{' expression '}' .9.2 The syntax of a statement
To explain the Seed7 Structured Syntax Description we design a new statement, the 'loop' statement. The 'loop' statement should be similar to 'while' and 'repeat' loops but instead of having the conditional exit at the beginning or at the end, it should have a conditional exit in the middle of the loop. This middle conditional exit should be part of the 'loop' statement. Note that the 'break' statement, which exists in some programming languages, is a statement on its own and is not part of the loop which it leaves. Therefore the middle conditional exit should not be confused with a 'break' statement. An example of the new 'loop' statement is:
loop ch := getc(inFile); until ch = '\n' do stri &:= str(ch); end loop;
The 'loop' example above reads characters from a file and concatenates them to a string until the character '\n' is read. The '\n' ends the loop. Hence it is not added to the string. An equivalent solution without the usage of the 'loop' statement would be:
repeat ch := getc(inFile); if ch <> '\n' then stri &:= str(ch); end if; until ch = '\n';
The S7SSD of the 'loop' statement is:
$ syntax expr: .loop.().until.().do.().end.loop is -> 25;
The details of the S7SSD 'syntax' definition will be explained later. For now we concentrate at the heart of the S7SSD, the expression:
.loop.().until.().do.().end.loop
For the purpose of the syntax description we can just remove the dots, which gives:
loop () until () do () end loop
This are the keywords used in a 'loop' statement. The symbol () acts as placeholder for an expression. With EBNF the 'loop' statement can be described as:
loop_statement ::= 'loop' statement 'until' expression 'do' statement 'end' 'loop' .
An EBNF description may use many nonterminal symbols such as 'statement' or 'expression'. S7SSD does not distinguish between different nonterminal symbols. Instead S7SSD only knows one nonterminal symbol: ()
Therefore S7SSD cannot distinguish between 'statement', 'expression' or something else. At the syntax level any kind of expression can by substituted for a S7SSD nonterminal symbol (). With EBNF it is possible to describe constraints such as the type of an expression. S7SSD relies on semantic checks to verify such constraints. Given the S7SSD of the 'loop' statement an expression like
loop "X" until 1+2 do integer end loop
would be legal as it contains the required keywords
loop until do end loop
and the expressions
"X" 1+2 integer
at the places of the () symbols. This is exactly what the syntax definition specifies, but it would be not be considered correct given the description of the 'loop' statement at the beginning of the chapter. To determine which types of expressions are allowed at the places of the () symbol, a semantic definition of the 'loop' statement is necessary. A semantic definition is just a function definition which uses the keywords and parameters from the syntax definition. The definition of the 'loop' function (semantic definition of the 'loop' statement) is:
const proc: loop (in proc: statements1) until (ref func boolean: condition) do (in proc: statements2) end loop is func local var boolean: exitLoop is FALSE; begin repeat statements1; if not condition then statements2; else exitLoop := TRUE; end if; until exitLoop; end func;
This definition determines the types of the expressions accepted between the keywords. Besides that the semantic definition of the 'loop' statement is just a normal function definition. Note that the sequence of keywords and parameters in the header of this function definition is determined by the corresponding syntax definition.
The parameters 'statements1', 'condition' and 'statements2' are closures. A closure is a function without parameters. Function types such as 'proc' or 'func boolean' are used as type of formal closure parameters. An expression with the correct type is allowed as actual closure parameter. This actual parameter expression is not evaluated when the function is called. Instead the closure expression is evaluated every time the formal closure parameter is used. This way 'statements1', 'condition' and 'statements2' are not executed when the 'loop' function is called. Inside the body of the 'loop' function the closures are executed at some places.
The 'loop' function uses a 'repeat' and an 'if' statement to implement the desired behaviour. When necessary the closures are executed several times.
For the 'loop' example with the semantic errors (see above) we would get an error message like:
*** chkloop.sd7(35):51: Match for {loop "X" until {1 + 2 } do integer end loop } failed9.3 Priority and associativity
When a syntax construct has parameters before the first symbol or after the last symbol the priority and the associativity of the construct are significant. Constructs with stronger priority bind their parameters earlier than constructs with weaker priority. The priority is described by a natural number (inclusive 0). The strongest priority is 0. Weaker priorities are described by larger numbers. What bind means is can be explained with an example:
= A + B = C * D / \ / \ * priority 6 + * + priority 7 / \ / \ = priority 12 A B C D
First the * takes its parameters, then the + and at last the = follows.
The associativity describes, in which order constructs with equal priority bind their parameters. For example
A - B - C
can be interpreted in two ways:
(A - B) - C or A - (B - C)
The first interpretation is usually preferred by mathematicians and is described with the associativity -> . Generally four associativities are possible:
Symbol Binding from left to right -> Binding from right to left <- Neither the left nor the right parameter are allowed to have the same priority <-> At the left side there is a binding from left to right and at the right side there is a binding from right to left -><-
The last two possibilities give no legal interpretation in the subtraction example. The third kind of associativity ( <-> ) is used by the equal operator ( = ) of Pascal because there a expression like
A = B = C
is not legal.
There is a second way to describe the associativity. The associativity describes if an operand must have a stronger priority than the priority of the operator. For example:
- 7 A - B - C / \ / \ / \ <=7 / \ <7 - priority 7 -> / \ / \ - C 7 0 / \ / \ / \ <=7 / \ <7 / \ / \ A B 0 0
The numbers in the nodes of the right tree show the priority of each sub expression (sub tree). With < and <= the required condition for the priority of an operand is described. An interpretation is legal if all this conditions are met. If there are more than one legal interpretations or no legal interpretation the expression is illegal.
Table for the possibilities of associativity:
+---------------+------------------------------+ | associativity | The priority of the | +---------------+--------------+---------------+ | | left operand | right operand | | | must be | must be | +---------------+--------------+---------------+ | -> | <= | < | | <- | < | <= | | <-> | < | < | | -><- | <= | <= | +---------------+--------------+---------------+ | | than that of the operator | +---------------+------------------------------+
The parameter before the operator symbol is called left operand. The parameter after the last symbol of a construct is called right operand. In case of normal operators the last symbol of a construct and the operator symbol are identical. If this is not the case there is a third kind of operand. Between the operator symbol and the last symbol of a construct are the middle operands. Middle operands can have any priority.
9.4 The syntax of operatorsA syntax definition specifies the way a usage of a statement or operator must be written. For example a call of the 'not' operator looks like:
not okay
To describe the syntax of the 'not' operator we write:
$ syntax expr: .not.() is <- 13;
This means that a 'not' expression is constructed with the symbol 'not' followed by a parameter. The place of the parameter is marked with the () sign. The syntax description contains no information about the types of the parameters. At the syntax level a parameter may be anything. With '<-' the associativity of the 'not' operator is specified as right associative. This means that the right operand is allowed to have the same priority as the operator symbol. So the expression
not not okay
is legal and means
not (not okay)
When the associativity of the 'not' operator is specified with '->' instead of '<-' the 'not not' expression above is not legal. With 13 the priority of the whole 'not' operator is determined. As convention priorities from 1 to 20 are used by operators and priority 25 is used by statements. Arithmetic operators have priorities from 1 to 11 and comparisons have priority 12.
To define the 'not' operator completely there must be also a semantic definition which is as follows:
const func boolean: not (in boolean: aBool) is func result var boolean: result is FALSE; begin if aBool then result := FALSE; else result := TRUE; end if; end func;
In the declaration the 'not' operator is written exactly in the same way it is written when it is called. The syntax definition is used at both places: declaration and call. The syntax and semantic declarations define precisely how the 'not' operator works.
As next example we try an infix operator like the 'and' operator. A call of the 'and' operator may look like:
okay and not error
To describe the syntax of the 'and' operator we write:
$ syntax expr: ().and.() is -> 14;
This means that an 'and' expression is constructed with the symbol 'and' surrounded by parameters. The '->' defines the 'and' operator as left associative. This means that an expression like
A and B and C
is interpreted as
(A and B) and C
With 14 the priority of the whole 'and' operator is determined. Since priority 14 is weaker than the priority of the 'not' operator which is 13 the example expression is evaluated as:
okay and (not error)
Note that the expression
okay and not error
makes no sense when the 'and' operator has priority 12 instead of 14.
S7SSD treats everything as operator description. Operators have priority and associativity. The priority and associativity determine in which succession S7SSD syntax rules get applied. To explain priority and associativity we use the basic arithmetic operations (+,-,*,/). To describe them with EBNF we can write:
factor := number | name . expression_5 ::= factor | ( '+' expression_5 ) | ( '-' expression_5 ) . expression_6 ::= expression_5 | ( expression_6 '*' expression_7 ) | ( expression_6 '/' expression_7 ) . expression_7 ::= expression_6 | ( expression_7 '+' expression_6 ) | ( expression_7 '-' expression_6 ) .
This describes the following things:
$ syntax expr: . + .() is <- 5; $ syntax expr: . - .() is <- 5; $ syntax expr: .(). * .() is -> 6; $ syntax expr: .(). / .() is -> 6; $ syntax expr: .(). + .() is -> 7; $ syntax expr: .(). - .() is -> 7;
As we can see S7SSD is shorter as the description with EBNF. A syntax statement is explained as follows:
Predefined statements can also be defined with S7SSD. E.g.: The 'while' statement. A use of the 'while' statement is:
while element_index > 0 and okay do processElement; write("."); end while;
To describe the syntax of the 'while' statement we write:
$ syntax expr: while.().do.().end.while is -> 25;
This means that the 'while' statement is an expression with the symbols 'while', 'do', 'end' and 'while'. With '->' the associativity of the 'while' statement is specified as left associative. The associativity has no meaning for the 'while' statement since there is no parameter before the first symbol or after the last symbol. The priority of the whole 'while' statement is 25.
The semantic definition of the 'while' statement is as follows:
const proc: while (ref func boolean: condition) do (ref proc: statement) end while is func begin if condition then statement; while condition do statement; end while; end if; end func;
The syntax definition is used for the declaration and for the call. This declaration defines precisely how the 'while' statement works. It is based on the 'if' statement and uses recursion to emulate the repetition of the loop body. Another example for a syntax description is the 'repeat' statement
repeat processElement; write("."); until element_index = 0 or not okay;
which has the following syntax description:
$ syntax expr: repeat.().until.() is -> 25;
This means that the 'repeat' statement is an expression with the symbols 'repeat' and 'until' and a parameter between 'repeat' and 'until' and after 'until'. With 25 the priority of the whole 'repeat' statement is determined. With '->' the associativity of the 'repeat' statement is specified as left associative. This allows priorities from 0 to 24 for the parameter after 'until'. Since statements have priority 25 it is not possible to write a statement direct behind 'until'.
A simple 'if' statement, without 'elsif' part, is the next example. A usage of this 'if' statement might be:
if okay then writeln("okay"); else writeln("not okay"); end if;
As syntax description we use
$ syntax expr: .if.().then.().end.if is -> 25; $ syntax expr: .if.().then.().else.().end.if is -> 25;
Note that this description allows 'if' statements with and without 'else' parts. As semantic description we use
const proc: if (in boolean: condition) then (in proc: statement) end if is func begin case condition of when {TRUE}: statement; end case; end func; const proc: if (in boolean: condition) then (in proc: statement1) else (in proc: statement2) end if is func begin case condition of when {TRUE}: statement1; when {FALSE}: statement2; end case; end func;
The two forms of the 'if' statement are based on the 'case' statement. A more complex 'if' statement with 'elsif' parts can be:
if number < 0 then write("less"); elsif number = 0 then write("equal"); else write("greater"); end if;
How to define the syntax and the semantic for this statement is described in the next chapter.
9.6 Advanced syntax definitionsWhen we want to use some special syntax which should be only allowed at some place we do the following:
The EBNF of the 'if' statement with 'elsif' parts is:
if_statement ::= 'if' expression 'then' statement { 'elsif' expression 'then' statement } [ 'else' statement ] 'end' 'if' .
The S7SSD of this 'if' statement is:
$ syntax expr : .if.().then.().end.if is -> 25; $ syntax expr : .if.().then.().().end.if is -> 25; $ syntax expr : .elsif.().then.() is <- 60; $ syntax expr : .elsif.().then.().() is <- 60; $ syntax expr : .else.() is <- 60;
Instead of one rule (as EBNF does) the rule is broken into several S7SSD rules. This is necessary because S7SSD does not support the [ ] and { } notations. They are not supported for good reasons: They complicate the parameter lists and they are also not so easy to implement. On the other hand, the BNF like rules of S7SSD lead to semantic constructs which are easy to parse and easy to compile. The broken down S7SSD rules of the 'if' statement corresponds to the following EBNF description:
if_statement ::= 'if' expression 'then' statement 'end' 'if' . if_statement ::= 'if' expression 'then' statement elseif_or_else_part 'end' 'if' . elseif_or_else_part ::= 'elsif' expression 'then' statement . elseif_or_else_part ::= 'elsif' expression 'then' statement elseif_or_else_part . elseif_or_else_part ::= 'else' statement .
Since S7SSD uses only one nonterminal symbol '()' it is the job of the semantic level to make sure that only the right nonterminal symbol can be used. This is done by introducing the type 'ELSIF_PROC' (which corresponds to the nonterminal symbol 'elseif_or_else_part' of the EBNF) and the type 'ELSIF_RESULT' (which is the result of the 'ELSIF_PROC').
Normally a syntax declaration can be used in many semantic declarations. E.g.: The syntax of the '+' operator is defined once and the semantic of the '+' operator is defined for the types 'integer', 'bigInteger', 'float', 'complex', ... This possibility is not needed for the 'if' statement. For each of the five S7SSD syntax rules of the 'if' statement just one corresponding semantic declaration is done:
# Semantic for the syntax: .if.().then.().end.if const proc: if (in boolean: condition) then (in proc: statements) end if is func begin case condition of when {TRUE}: statements; end case; end func; # Semantic for the syntax: .if.().then.().().end.if const proc: if (in boolean: condition) then (in proc: statements) (in ELSIF_PROC: elsifPart) end if is func begin case condition of when {TRUE}: statements; when {FALSE}: elsifPart; end case; end func; # Semantic for the syntax: .elsif.().then.() const ELSIF_PROC: elsif (in boolean: condition) then (in proc: statements) is func begin case condition of when {TRUE}: statements; end case; end func; # Semantic for the syntax: .elsif.().then.().() const ELSIF_PROC: elsif (in boolean: condition) then (in proc: statements) (in ELSIF_PROC: elsifPart) is func begin case condition of when {TRUE}: statements; when {FALSE}: elsifPart; end case; end func; # Semantic for the syntax: .else.() const ELSIF_PROC: else (ref void: voidValue) is ELSIF_EMPTY;
Since no other functions of type 'ELSIF_PROC' are defined only legal 'if' statements can be written.
9.7 Comparison of EBNF and S7SSDIn the S7SSD of the 'loop' statement
$ syntax expr: .loop.().until.().do.().end.loop is -> 25;
are no nonterminal expressions '()' before the first keyword or after the last keyword. Therefore the associativity does not play any role. The nonterminal expressions '()' of the 'loop' statement are all surrounded by keywords and therefore they can have any priority. As priority of the 'loop' 25 is chosen just because most other statements have also priority 25. The assignments (:= +:= *:= ...) have priority 20 and all operators used in arithmetic, boolean and string expressions have priorities less than 20. BTW: The semicolon operator (;) is defined with the priority 50. Operators with a priority of 0 get their parameters before operators with priority 1 and so on.
The corresponding EBNF description of the 'loop' statement would be:
expression_25 ::= 'loop' expression_127 'until' expression_127 'do' expression_127 'end' 'loop' .
We must keep in mind that alternative rules for expression_25 are also possible and that for every priority level a rule like
expression_127 ::= expression_126 .
is defined. Additionally the following rules are defined:
expression_0 ::= token | parentheses_expression | call_expression | dot_expression . token ::= identifier | literal . parentheses_expression ::= '(' expression_127 ')' . call_expression ::= expression_127 [ '(' [ expression_127 { ',' expression_127 } ] ')' ] . dot_expression ::= [ '.' ] call_expression { '.' call_expression } .
There are some things which are out of the scope of S7SSD. The syntax of tokens (whitespace, comments, identifiers and literals) and expressions (parentheses, function calls and dot expressions) is hard coded. The hard coded constructs are described in chapter 10 (Tokens) and chapter 11 (Expressions).
For the reasons mentioned above it is not possible to transform every EBNF syntax description into S7SSD. Transforming S7SSD descriptions to EBNF is always possible.
The advantage of S7SSD lies in its simplicity and that a fast automated syntax recognition algorithm can be easily implemented. It is exactly the combination of hard coded syntax recognition and flexible syntax rules that make it successful.
10. TOKENSA program consists of a sequence of tokens which may be delimited by white space. There are two types of tokens:
identifiers literals
Syntax:
program ::= { white_space | token } . token ::= identifier | literal .10.1 White space
There are three types of white space
spaces comments line comments
White space always terminates a preceding token. Some white space is required to separate otherwise adjacent tokens.
Syntax:
white_space ::= ( space | comment | line_comment ) { space | comment | line_comment } .10.1.1 Spaces
There are several types of space characters which are ignored except as they separate tokens:
blanks, horizontal tabs, carriage returns and new lines.
Syntax:
space ::= ' ' | TAB | CR | NL .10.1.2 Comments
Comments are introduced with the characters (* and are terminated with the characters *) . For example:
(* This is a comment *)
Comment nesting is allowed so it is possible to comment out larger sections of the program which can also include comments. Comments cannot occur within string or character literals.
Syntax:
comment ::= '(*' { any_character } '*)' .10.1.3 Line comments
Line comments are introduced with the character # and are
terminated with the end of the line.
For example:
# This is a comment
Comments cannot occur within string, character or numerical literals.
Syntax:
line_comment ::= '#' { any_character } NL .10.2 Identifiers
There are three types of identifiers
name identifiers special identifiers parenthesis
Identifiers can be written adjacent except that between two name identifiers and between two special identifiers white space must be used to separate them.
Syntax:
identifier ::= name_identifier | special_identifier | parenthesis .10.2.1 Name identifiers
A name identifier is a sequence of letters, digits and underscores ( _ ). The first character must be a letter or an underscore. Examples of name identifiers are:
NUMBER integer const if UPPER_LIMIT LowerLimit x5 _end
Upper and lower case letters are different. Name identifiers may have any length and all characters are significant. The name identifier is terminated with a character which is neither a letter (or _ ) nor a digit. The terminating character is not part of the name identifier.
Syntax:
name_identifier ::= ( letter | underscore ) { letter | digit | underscore } . letter ::= upper_case_letter | lower_case_letter . upper_case_letter ::= 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' . lower_case_letter ::= 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' . digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' . underscore ::= '_' .10.2.2 Special identifiers
A special identifier is a sequence of special characters. Examples of special identifiers are:
+ := <= * -> , &
Here is a list of all special characters:
! $ % & * + , - . / : ; < = > ? @ \ ^ ` | ~
Special identifiers may have any length and all characters are significant. The special identifier is terminated with a character which is not a special character. The terminating character is not part of the special identifier.
Syntax:
special_identifier ::= special_character { special_character } . special_character ::= '!' | '$' | '%' | '&' | '*' | '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | '\' | '^' | '`' | '|' | '~' .10.2.3 Parentheses
A parenthesis is one of the following characters:
( ) [ ] { }
Note that a parenthesis consists of only one character. Except for the character sequence (* (which introduces a comment) a parenthesis is terminated with the next character.
Syntax:
parenthesis ::= '(' | ')' | '[' | ']' | '{' | '}' .10.3 Literals
There are three types of literals
integer literals character literals string literals
Syntax:
literal ::= integer_literal | character_literal | string_literal .10.3.1 Integer literals
An integer literal is a sequence of digits which is taken to be decimal. The sequence of digits may be followed by the letter E or e an optional + sign and a decimal exponent. Based numbers can be specified when the sequence of digits is followed by the # character and a sequence of extended digits. The decimal number in front of the # character specifies the base of the number which follows the # character. As base a number between 2 and 36 is allowed. As extended digits the letters A or a can be used for 10, B or b can be used for 11 and so on to Z or z which can be used as 35.
Syntax:
integer_literal ::= decimal_integer [ exponent | based_integer ] . decimal_integer ::= digit { digit } . exponent ::= ( 'E' | 'e' ) [ '+' ] decimal_integer . based_integer ::= '#' extended_digit { extended_digit } . extended_digit ::= letter | digit .10.3.2 String literals
A string literal is a sequence of characters surrounded by double quotes. For example:
"" " " "\"" "'" "\'" "String" "ch=\" " "\n\n"
In order to represent nonprintable characters and certain printable characters the following escape sequences may be used.
audible alert BEL \a backslash (\) \\ backspace BS \b apostrophe (') \' escape ESC \e double quote (") \" formfeed FF \f newline NL (LF) \n control-A \A carriage return CR \r ... horizontal tab HT \t control-Z \Z vertical tab VT \v
Additionally there are the following possibilities:
Syntax:
string_literal ::= '"' { string_character } '"' . string_character ::= printable_character | escape_sequence . escape_sequence ::= '\a' | '\b' | '\e' | '\f' | '\n' | '\r' | '\t' | '\v' | '\\' | '\''' | '\"' | '\' upper_case_letter | '\' { space } '\' | '\' integer_literal '\' .10.3.3 Character literals
A character literal is a character enclosed in single quotes. For example:
'a' ' ' '\n' '!' '\\' '2' '"' '\"' '\''
To represent control characters and certain other characters in character literals the same escape sequences as for string literals may be used.
Syntax:
character_literal ::= ''' ( printable_character | escape_sequence ) ''' . escape_sequence ::= '\a' | '\b' | '\e' | '\f' | '\n' | '\r' | '\t' | '\v' | '\\' | '\''' | '\"' | '\' upper_case_letter | '\' { space } '\' | '\' integer_literal '\' .11. EXPRESSIONS
There are two types of expressions. On one side there so called simple expressions which are constructed using fixed predefined syntax rules. On the other side there are expressions which are constructed according to syntax rules defined with syntax declarations. Here we describe only simple expressions. How syntax declarations work is described in Chapter 3.2 (Syntax declarations) and chapter 9 (Structured syntax definition). There are only few fixed predefined syntax rules:
11.1 ParenthesesParentheses can be used to override any precedence rules of predefined and user defined syntax constructs. For example
2 * (3 + 4)
specifies that the + operator gets his parameters first.
Syntax:
parentheses_expression ::= '(' expression ')' .11.2 Call expressions
Call expressions can also be used to form a list. For example
writeln("hello world")
forms a list expression with the elements
"hello world" writeln
The meta object of this list is specified with the system declaration "system expr" which is defined in the include file "syntax.s7i" included from "seed7_05.s7i" as
$ system "expr" is expr;
A call expression with two parameters as
pos("Scotty! Beam me up.", "am")
forms a list expression with the elements
"Scotty! Beam me up." "am" pos
Syntax:
call_expression ::= primary_expression [ '(' comma_expression ')' ] . primary_expression ::= parentheses_expression | atom .11.3 Dot expressions
Dot expressions start with a dot and have dots as separator between the elements of the list. For example
.not.TRUE
and
.OKAY.and.GO_ON
form list expressions with the elements
not TRUE
and
OKAY and GO_ON
The meta object of this list is specified with the system declaration "system expr" which is defined in the include file "syntax.s7i" included from "seed7_05.s7i" as
$ system "expr" is expr;
Dot expressions override the priority of the elements. Dot expressions are used in 'syntax' declarations.
Syntax:
dot_expression ::= [ '.' ] call_expression { '.' call_expression } .12. OPERATING SYSTEM ACCESS
Seed7 provides a portable access to the services provided by an operating system. This interface is oriented towards POSIX and UNIX.
12.1 Directory accessA portable access to the contents of directories in the file system is provided. For example: After the declaration
var array string: dir_array is 0 times "";
the following statement
dir_array := read_dir(".");
reads the current working directory and stores it into the string-array 'dir_array'. The components of the directory can now be accessed via indexing:
for index range 1 to length(dir_array) do writeln(dir_array[index]); end for;
Note that the strings contain only the name of the file. Additional information must be obtained by other calls. Other directories can be read by using their name in the 'read_dir' call. Basing on this mechanism another mechanism is constructed to read the contents of a directory as file. This is shown in the following example
... include "dir.s7i"; var file: dir_file is STD_NULL; var string: file_name is ""; ... dir_file := open_dir("."); file_name := getln(dir_file); while file_name <> "" do writeln(file_name); file_name := getln(dir_file); end while;
This is useful in programs that accept a list of filenames as input. Using the 'open_dir' mechanism it is possible to read the filenames directly from a directory without large changes in the program.
12.2 Other directory operationsIn most operating systems each process has a current working directory. With the following statement
my_dir := getcwd();
the full path of the current working directory is assigned to the string variable 'my_dir'. To change the current working directory the next statement can be used
chdir("/usr/bin");
A new directory can be created with
mkdir("my_dir");12.3 fileType
The type of a file can determined with 'fileType' or 'fileTypeSL':
const func integer: fileType (in string: filePath) is ... const func integer: fileTypeSL (in string: filePath) is ...
The function 'fileType' does follow symbolic links. Therefore 'fileType' never returns 'FILE_SYMLINK'. The function 'fileTypeSL' does not follow symbolic links. Therefore 'fileTypeSL' can also return 'FILE_SYMLINK'. Most functions which use a file path except 'fileTypeSL' and 'readlink' follow symbolic links.
Returns:
Possible exceptions:
The size of a file can be determined with 'fileSize' and 'bigFileSize':
const func integer: fileSize (in string: filePath) is ... const func bigInteger: bigFileSize (in string: filePath) is ...
Returns:
For directorys a size of 0 is returned. For other file types the operating system functions 'stat()' and 'seek()' are used to determine the size of a file. The functions 'fileSize' and 'bigFileSize' succeed when at least one strategy to determine the file size succeeds.
Possible exceptions:
The access time of a file is returned by the function 'getATime':
const func time: getATime (in string: filePath) is ...
Possible exceptions:
The change time of a file is returned by the function 'getCTime':
const func time: getCTime (in string: filePath) is ...
Possible exceptions:
The modification time of a file is returned by the function 'getMTime':
const func time: getMTime (in string: filePath) is ...
Possible exceptions:
The function 'setATime' sets the access time of a file:
const proc: setATime (in string: filePath, in time: aTime) is ...
Possible exceptions:
The function 'setMTime' sets the modification time of a file:
const proc: setMTime (in string: filePath, in time: aTime) is ...
Possible exceptions:
The function 'readlink' reads the destination of a symbolic link:
const func string: readlink (in string: filePath) is ...
Returns:
The symbolic link refered by 'filePath'.
Possible exceptions:
The function 'symlink' creates a symbolic link called 'dest' that contains the string referred by 'source':
const proc: symlink (in string: source, in string: dest) is ...
Parameters:
Possible exceptions:
The function 'removeAnyFile' removes a file independend of its file type:
const proc: removeAnyFile (in string: filePath) is ...
Possible exceptions:
Other functions are:
remove("file"); rename("old_name", "new_name"); copy("from", "to");13. PRIMITIVE ACTIONS
Not all functions can be described by calling other functions of the same language. For this reason and for performance reasons several functions are defined using a mechanism called action. For example: It is easy to define the 'while' statement by using recursion. But this would hurt performance and it would also use a huge amount of memory for the runtime stack. In practise an implementation of the 'while' statement can use a conditional jump instead of a subroutine call. Since Seed7 has no 'goto' statement, this is not an option. Instead the primitive action PRC_WHILE can be used. The 'while' statement is defined in the basic Seed7 library 'seed7_05.s7i' with:
const proc: while (in func boolean param) do (in proc param) end while is action "PRC_WHILE";
This declaration shows the types and the position of the parameters of a 'while' statement. Such an action declaration contains enough information to use the defined construct. The semantic of all primitive actions is hard coded in the interpreter and in the compiler. The parameters of the hard coded actions and the corresponding definitions in Seed7 must match. If you are interested in the Seed7 definitions of primitive actions just look into the file 'seed7_05.s7i'.
Currently there are several hundred primitive actions predefined in the interpreter. They all have names in upper case characters which have the form:
TYPE_ACTION
Which means that for example all 'integer' actions start with INT_ and all assignment actions end with _CPY . The following list shows actions which are used with more than one type:
_ABS Absolute value _ADD Addition _CAT Concatenation _CMP Compare _CPY Copy (Assignment) _CREATE Initialize (Construct) _DESTR Destroy (Destruct) _DECR Decrement _DIV Division _EQ Equal _GE Greater equal _GETC Get one character from a file _GETS Get string with maximum length from a file _GT Greater than _HASHCODE Compute a hashCode _HEAD Head of ref_list, array, string _ICONV Conversion of integer to another type _IDX Index (Element) of ref_list, array, string _INCR Increment _LE Less equal _LNG Length _LOG2 Base 2 logarithm _LOWER Convert to lower case _LSHIFT Shift left _LT Less than _MDIV Modulo division (Integer division truncated towards negative infinity) _MINUS Change sign _MOD Modulo (Reminder of _MDIV integer division) _MULT Multiply _NE Not equal _ODD Odd number _ORD Ordinal number _PARSE Conversion of string to another type _PLUS Positive sign (noop) _POW Power _PRED Predecessor _RAND Random value _RANGE Range of ref_list, array, string _REM Remainder (Reminder of _DIV integer division) _RSHIFT Arithmetic shift right _SBTR Subtract _SCAN Convert from string to another type _SEEK Set actual file position of a file _SQRT Square root _STR Convert to string _SUCC Successor _TAIL Tail of ref_list, array, string _TELL Return the actual file position _UPPER Convert to upper case _VALUE Dereference a reference _WRITE Write string to file
Primitive actions are defined for many types. The functions which implement the primitive actions are grouped together in *lib.c files. The following list contains the action prefix, the file containing the functions and a description:
ACT_ actlib.c ACTION operations ARR_ arrlib.c array operations BIG_ biglib.c bigInteger operations BLN_ blnlib.c boolean operations BST_ bstlib.c Operations for byte strings CHR_ chrlib.c char operations CMD_ cmdlib.c Various directory, file and other commands DCL_ dcllib.c Declaration operations DRW_ drwlib.c Drawing operations ENU_ enulib.c Enumeration operations FIL_ fillib.c PRIMITIVE_FILE operations FLT_ fltlib.c float operations HSH_ hshlib.c hash operations INT_ intlib.c integer operations ITF_ itflib.c Operations for interface types KBD_ kbdlib.c Keyboard operations LST_ lstlib.c List operations PRC_ prclib.c proc operations and statements PRG_ prglib.c Program operations REF_ reflib.c reference operations RFL_ rfllib.c ref_list operations SCR_ scrlib.c Screen operations SCT_ sctlib.c struct operations SET_ setlib.c set operations SOC_ soclib.c PRIMITIVE_SOCKET operations STR_ strlib.c string operations TIM_ timlib.c time and duration operations TYP_ typlib.c type operations UT8_ ut8lib.c utf8_file operations
The C functions which implement primitive actions have lowercase names. E.g.: The action 'PRC_WHILE' is implemented with the C function 'prc_while()' in the file 'prclib.c'. The parameter list for all C action functions is identical. Every *lib.c file has a corresponding *lib.h file which contains the prototypes for the action functions.
The primitive action which describes the addition of two integers is 'INT_ADD'. The Seed7 interface to the action 'INT_ADD' is defined in the file 'seed7_05.s7i' with:
const func integer: (in integer param) + (in integer param) is action "INT_ADD";
To execute an action a corresponding C function must be present in the hi interpreter. The function for the action 'INT_ADD' is 'int_add()'. The function 'int_add()' is defined in the file 'intlib.c' with:
#ifdef ANSI_C objecttype int_add (listtype arguments) #else objecttype int_add (arguments) listtype arguments; #endif { /* int_add */ isit_int(arg_1(arguments)); isit_int(arg_3(arguments)); return(bld_int_temp( take_int(arg_1(arguments)) + take_int(arg_3(arguments)))); } /* int_add */
The action functions use ANSI C prototypes and K&R function headers. The function 'int_add()' adds the first and the third argument (the second argument contains the + symbol. The file 'memory.h' contains several macros and functions which help to handle the arguments (parameter list) of a C primitive action function.
The file 'intlib.h' contains the prototype for the 'int_add()' function:
objecttype int_add (listtype);
and also a definition for the K&R C language:
objecttype int_add ();
Additionally every primitive action is registered in the file 'primitive.c'. The line which incorporates 'INT_ADD' is:
{ "INT_ADD", int_add, },
The entries of the primitive action in the file 'primitive.c' are sorted alphabetically. With this definitions the hi interpreter understands a primitive action.
To allow a primitive function in a compiled Seed7 program the Seed7 compiler (comp.sd7) needs to know the action also. The compiler function which creates code for the 'INT_ADD' action is:
const proc: process_int_add (in ref_list: params, inout expr_type: c_expr) is func begin c_expr.expr &:= "("; process_expr(params[1], c_expr); c_expr.expr &:= ") + ("; process_expr(params[3], c_expr); c_expr.expr &:= ")"; end func;
This function is called from the function 'process_action' with the line:
elsif action_name = "INT_ADD" then process_int_add(params, c_expr);
Some primitive actions are more complicated and inline code would not be the best solution for it. In this case an additional helper function is used. The action 'INT_STR' is such an action. The definition of the function 'int_str()' in the file 'intlib.c' is:
#ifdef ANSI_C objecttype int_str (listtype arguments) #else objecttype int_str (arguments) listtype arguments; #endif { /* int_str */ isit_int(arg_1(arguments)); return(bld_stri_temp(intStr( take_int(arg_1(arguments))))); } /* int_str */
The main work for the primitive action 'INT_STR' is done in the helper function 'intStr()'. The helper function 'intStr()' can be found in the file 'int_rtl.c':
#ifdef ANSI_C stritype intStr (inttype number) #else stritype intStr (number) inttype number; #endif { uinttype unsigned_number; booltype sign; strelemtype buffer_1[50]; strelemtype *buffer; memsizetype len; stritype result; /* intStr */ if ((sign = (number < 0))) { unsigned_number = -number; } else { unsigned_number = number; } /* if */ buffer = &buffer_1[50]; do { *(--buffer) = (strelemtype) (unsigned_number % 10 + '0'); } while ((unsigned_number /= 10) != 0); if (sign) { *(--buffer) = (strelemtype) '-'; } /* if */ len = &buffer_1[50] - buffer; if (!ALLOC_STRI(result, len)) { raise_error(MEMORY_ERROR); return(NULL); } else { result->size = len; memcpy(result->mem, buffer, (SIZE_TYPE) (len * sizeof(strelemtype))); return(result); } /* if */ } /* intStr */
The file 'int_rtl.h' contains a prototype definition for the 'intStr()' helper function:
stritype intStr (inttype number);
and also a definition for the K&R C language:
stritype intStr ();
The helper functions are also used in the code generated by the Seed7 compiler:
const proc: process_int_str (in ref_list: params, inout expr_type: c_expr) is func begin prepare_stri_result(c_expr); c_expr.result_expr &:= "intStr("; getStdParamToResultExpr(params[1], c_expr); c_expr.result_expr &:= ")"; end func;
All the *lib.c files containing primitive actions and various other files with their functions are grouped together in the 's7_comp.a' library (Licensed under GPL). Furthermore the C primitive action functions (E.g.: int_parse) of the *lib.c files may use corresponding functions (E.g.: intParse) which can be found in *_rtl.c files (E.g.: 'int_rtl.c'). The *_rtl.c files are grouped together in the 'seed7_05.a' library (Licensed under LGPL). When a Seed7 program is compiled with the Seed7 compiler (comp.sd7) inline code is generated for many primitive actions. To implement the remaining primitive actions the functions of the 'seed7_05.a' library are used.
13.1 Actions for the type ACTIONAction name | actlib.c function | act_comp.c function |
ACT_ILLEGAL | act_illegal | |
ACT_CPY | act_cpy | = |
ACT_CREATE | act_create | |
ACT_GEN | act_gen | |
ACT_STR | act_str | actStr |
ACT_VALUE | act_value | actValue |
Action name | arrlib.c function | arr_rtl.c function |
ARR_APPEND | arr_append | arrAppend |
ARR_ARRLIT | arr_arrlit | arrArrlit |
ARR_ARRLIT2 | arr_arrlit2 | arrArrlit2 |
ARR_BASELIT | arr_baselit | arrBaselit |
ARR_BASELIT2 | arr_baselit2 | arrBaselit2 |
ARR_CAT | arr_cat | arrCat |
ARR_CONV | arr_conv | (noop) |
ARR_CPY | arr_cpy | cpy_ ... |
ARR_CREATE | arr_create | create_ ... |
ARR_DESTR | arr_destr | destr_ ... |
ARR_EMPTY | arr_empty | |
ARR_EXTEND | arr_extend | arrExtend |
ARR_GEN | arr_gen | arrGen |
ARR_HEAD | arr_head | arrHead |
ARR_IDX | arr_idx | a->arr[b-a->min_position] |
ARR_LNG | arr_lng | a->max_position-a->min_position + 1 |
ARR_MAXIDX | arr_maxidx | a->max_position |
ARR_MINIDX | arr_minidx | a->min_position |
ARR_RANGE | arr_range | arrRange |
ARR_REMOVE | arr_remove | arrRemove |
ARR_SORT | arr_sort | arrSort |
ARR_TAIL | arr_tail | arrTail |
ARR_TIMES | arr_times | times_ ... |
Action name | biglib.c function | big_rtl.c function |
BIG_ABS | big_abs | bigAbs |
BIG_ADD | big_add | bigAdd, bigAddTemp |
BIG_BIT_LENGTH | big_bit_length | bigBitLength |
BIG_CLIT | big_clit | bigCLit |
BIG_CMP | big_cmp | bigCmp |
BIG_CPY | big_cpy | bigCpy |
BIG_CREATE | big_create | bigCreate |
BIG_DECR | big_decr | bigDecr |
BIG_DESTR | big_destr | bigDestr |
BIG_DIV | big_div | bigDiv |
BIG_EQ | big_eq | bigEq |
BIG_GCD | big_gcd | bigGcd |
BIG_GE | big_ge | bigCmp >= 0 |
BIG_GROW | big_grow | bigGrow |
BIG_GT | big_gt | bigCmp > 0 |
BIG_HASHCODE | big_hashcode | bigHashCode |
BIG_ICONV | big_iconv | bigIConv |
BIG_INCR | big_incr | bigIncr |
BIG_IPOW | big_ipow | bigIPow |
BIG_LE | big_le | bigCmp <= 0 |
BIG_LOG2 | big_log2 | bigLog2 |
BIG_LOWEST_SET_BIT | big_lowest_set_bit | bigLowestSetBit |
BIG_LSHIFT | big_lshift | bigLShift |
BIG_LSHIFT_ASSIGN | big_lshift_assign | bigLShiftAssign |
BIG_LT | big_lt | bigCmp < 0 |
BIG_MDIV | big_mdiv | bigMDiv |
BIG_MINUS | big_minus | bigMinus |
BIG_MOD | big_mod | bigMod |
BIG_MULT | big_mult | bigMult |
BIG_MULT_ASSIGN | big_mult_assign | bigMultAssign |
BIG_NE | big_ne | bigNe |
BIG_ODD | big_odd | bigOdd |
BIG_ORD | big_ord | bigOrd |
BIG_PARSE | big_parse | bigParse |
BIG_PLUS | big_plus | (noop) |
BIG_PRED | big_pred | bigPred |
BIG_RAND | big_rand | bigRand |
BIG_REM | big_rem | bigRem |
BIG_RSHIFT | big_rshift | bigRShift |
BIG_RSHIFT_ASSIGN | big_rshift_assign | bigRShiftAssign |
BIG_SBTR | big_sbtr | bigSbtr, bigSbtrTemp |
BIG_SHRINK | big_shrink | bigShrink |
BIG_STR | big_str | bigStr |
BIG_SUCC | big_succ | bigSucc |
BIG_VALUE | big_value | bigValue |
Action name | blnlib.c function | bln_rtl.c function |
BLN_AND | bln_and | && |
BLN_CPY | bln_cpy | blnCpy |
BLN_CREATE | bln_create | blnCreate |
BLN_GE | bln_ge | >= |
BLN_GT | bln_gt | > |
BLN_ICONV | bln_iconv | & 1 |
BLN_LE | bln_le | <= |
BLN_LT | bln_lt | < |
BLN_NOT | bln_not | ! |
BLN_OR | bln_or | || |
BLN_ORD | bln_ord | (inttype) |
Action name | bstlib.c function | bst_rtl.c function |
BST_APPEND | bst_append | bstAppend |
BST_CAT | bst_cat | bstCat |
BST_CPY | bst_cpy | bstCpy |
BST_CREATE | bst_create | bstCreate |
BST_DESTR | bst_destr | bstDestr |
BST_EMPTY | bst_empty |
Action name | chrlib.c function | chr_rtl.c function |
CHR_CHR | chr_chr | (chartype) |
CHR_CMP | chr_cmp | chrCmp |
CHR_CONV | chr_conv | (noop) |
CHR_CPY | chr_cpy | chrCpy |
CHR_CREATE | chr_create | chrCreate |
CHR_DECR | chr_decr | -- |
CHR_EQ | chr_eq | == |
CHR_GE | chr_ge | >= |
CHR_GT | chr_gt | > |
CHR_HASHCODE | chr_hashcode | (inttype) |
CHR_ICONV | chr_iconv | (chartype) |
CHR_INCR | chr_incr | ++ |
CHR_LE | chr_le | <= |
CHR_LOW | chr_low | chrLow |
CHR_LT | chr_lt | < |
CHR_NE | chr_ne | != |
CHR_ORD | chr_ord | (inttype) |
CHR_PRED | chr_pred | -1 |
CHR_STR | chr_str | chrStr |
CHR_SUCC | chr_succ | +1 |
CHR_UP | chr_up | chrUp |
CHR_VALUE | chr_value | chrValue |
Action name | cmdlib.c function | cmd_rtl.c function |
CMD_CHDIR | cmd_chdir | cmdChdir |
CMD_CONFIG_VALUE | cmd_config_value | cmdConfigValue |
CMD_COPY | cmd_copy | cmdCopy |
CMD_FILETYPE | cmd_filetype | cmdFileType |
CMD_GETCWD | cmd_getcwd | cmdGetcwd |
CMD_LNG | cmd_lng | cmdLng |
CMD_LS | cmd_ls | cmdLs |
CMD_MKDIR | cmd_mkdir | cmdMkdir |
CMD_MOVE | cmd_move | cmdMove |
CMD_REMOVE | cmd_remove | cmdRemove |
CMD_SH | cmd_sh | cmdSh |
CMD_SYMLINK | cmd_symlink | cmdSymlink |
Action name | dcllib.c function | |
DCL_ATTR | dcl_attr | |
DCL_CONST | dcl_const | |
DCL_ELEMENTS | dcl_elements | |
DCL_FWD | dcl_fwd | |
DCL_GETFUNC | dcl_getfunc | |
DCL_GETOBJ | dcl_getobj | |
DCL_GLOBAL | dcl_global | |
DCL_IN1VAR | dcl_in1var | |
DCL_IN2VAR | dcl_in2var | |
DCL_INOUT1 | dcl_inout1 | |
DCL_INOUT2 | dcl_inout2 | |
DCL_REF1 | dcl_ref1 | |
DCL_REF2 | dcl_ref2 | |
DCL_SYMB | dcl_symb | |
DCL_VAL1 | dcl_val1 | |
DCL_VAL2 | dcl_val2 | |
DCL_VAR | dcl_var |
Action name | drwlib.c function | drw_rtl.c/drw_x11.c/drw_win.c function |
DRW_ARC | drw_arc | drwArc |
DRW_ARC2 | drw_arc2 | drwArc2 |
DRW_BACKGROUND | drw_background | drwBackground |
DRW_CIRCLE | drw_circle | drwCircle |
DRW_CLEAR | drw_clear | drwClear |
DRW_COLOR | drw_color | drwColor |
DRW_COPYAREA | drw_copyarea | drwCopyArea |
DRW_CPY | drw_cpy | drwCpy |
DRW_CREATE | drw_create | drwCreate |
DRW_DESTR | drw_destr | drwDestr |
DRW_EMPTY | drw_empty | |
DRW_EQ | drw_eq | == |
DRW_FARCCHORD | drw_farcchord | drwFArcChord |
DRW_FARCPIESLICE | drw_farcpieslice | drwFArcPieSlice |
DRW_FCIRCLE | drw_fcircle | drwFCircle |
DRW_FELLIPSE | drw_fellipse | drwFEllipse |
DRW_FLUSH | drw_flush | drwFlush |
DRW_FPOLYLINE | drw_fpolyLine | drwFPolyLine |
DRW_GENPOINTLIST | drw_genPointList | drwGenPointList |
DRW_GET | drw_get | drwGet |
DRW_HEIGHT | drw_height | drwHeight |
DRW_IMAGE | drw_image | drwImage |
DRW_LINE | drw_line | drwLine |
DRW_NE | drw_ne | != |
DRW_NEW_PIXMAP | drw_new_pixmap | drwNewPixmap |
DRW_OPEN | drw_open | drwOpen |
DRW_PARC | drw_parc | drwPArc |
DRW_PCIRCLE | drw_pcircle | drwPCircle |
DRW_PFARCCHORD | drw_pfarcchord | drwPFArcChord |
DRW_PFARCPIESLICE | drw_pfarcpieslice | drwFArcPieSlice |
DRW_PFCIRCLE | drw_pfcircle | drwPFCircle |
DRW_PFELLIPSE | drw_pfellipse | drwPFEllipse |
DRW_PLINE | drw_pline | drwPLine |
DRW_POINT | drw_point | drwPoint |
DRW_POLYLINE | drw_polyLine | drwPolyLine |
DRW_PPOINT | drw_ppoint | drwPPoint |
DRW_PRECT | drw_prect | drwPRect |
DRW_PUT | drw_put | drwPut |
DRW_RECT | drw_rect | drwRect |
DRW_RGBCOL | drw_rgbcol | drwRgbColor |
DRW_ROT | drw_rot | |
DRW_SCALE | drw_scale | |
DRW_SETTRANSPARENTCOLOR | drw_setTransparentColor | drwSetTransparentColor |
DRW_TEXT | drw_text | drwText |
DRW_WIDTH | drw_width | drwWidth |
Action name | enulib.c function | |
ENU_CONV | enu_conv | (noop) |
ENU_CPY | enu_cpy | = |
ENU_CREATE | enu_create | |
ENU_EQ | enu_eq | == |
ENU_GENLIT | enu_genlit | |
ENU_ICONV2 | enu_iconv2 | (noop) |
ENU_NE | enu_ne | != |
ENU_ORD2 | enu_ord2 | (noop) |
ENU_SIZE | enu_size | |
ENU_VALUE | enu_value | enuValue |
Action name | fillib.c function | fil_rtl.c function |
FIL_BIG_LNG | fil_big_lng | filBigLng |
FIL_BIG_SEEK | fil_big_seek | filBigSeek |
FIL_BIG_TELL | fil_big_tell | filBigTell |
FIL_CLOSE | fil_close | fclose |
FIL_CPY | fil_cpy | fltCpy |
FIL_CREATE | fil_create | fltCreate |
FIL_EMPTY | fil_empty | |
FIL_EOF | fil_eof | feof |
FIL_EQ | fil_eq | == |
FIL_ERR | fil_err | stderr |
FIL_FLUSH | fil_flush | fflush |
FIL_GETC | fil_getc | fgetc |
FIL_GETS | fil_gets | filGets |
FIL_HAS_NEXT | fil_has_next | filHasNext |
FIL_IN | fil_in | stdin |
FIL_LINE_READ | fil_line_read | filLineRead |
FIL_LIT | fil_lit | filLit |
FIL_LNG | fil_lng | filLng |
FIL_NE | fil_ne | != |
FIL_OPEN | fil_open | filOpen |
FIL_OUT | fil_out | stdout |
FIL_POPEN | fil_popen | filPopen |
FIL_PRINT | fil_print | filPrint |
FIL_SEEK | fil_seek | filSeek |
FIL_TELL | fil_tell | filTell |
FIL_VALUE | fil_value | filValue |
FIL_WORD_READ | fil_word_read | filWordRead |
FIL_WRITE | fil_write | filWrite |
Action name | fltlib.c function | flt_rtl.c function |
FLT_A2TAN | flt_a2tan | atan2 |
FLT_ABS | flt_abs | fabs |
FLT_ACOS | flt_acos | acos |
FLT_ADD | flt_add | + |
FLT_ASIN | flt_asin | asin |
FLT_ATAN | flt_atan | atan |
FLT_CAST | flt_cast | (x.floatvalue=a, x.intvalue) |
FLT_CEIL | flt_ceil | ceil |
FLT_CMP | flt_cmp | fltCmp |
FLT_COS | flt_cos | cos |
FLT_COSH | flt_cosh | cosh |
FLT_CPY | flt_cpy | fltCpy |
FLT_CREATE | flt_create | fltCreate |
FLT_DGTS | flt_dgts | fltDgts |
FLT_DIV | flt_div | / |
FLT_DIV_ASSIGN | flt_div_assign | /= |
FLT_EQ | flt_eq | == |
FLT_EXP | flt_exp | exp |
FLT_FLOOR | flt_floor | floor |
FLT_GE | flt_ge | >= |
FLT_GROW | flt_grow | += |
FLT_GT | flt_gt | > |
FLT_HASHCODE | flt_hashcode | (x.floatvalue=a, x.intvalue) |
FLT_ICAST | flt_icast | (x.intvalue=a, x.floatvalue) |
FLT_ICONV | flt_iconv | (float) |
FLT_IFLT | flt_iflt | (float) |
FLT_IPOW | flt_ipow | fltIPow |
FLT_ISNAN | flt_isnan | isnan |
FLT_LE | flt_le | <= |
FLT_LOG | flt_log | log |
FLT_LOG10 | flt_log10 | log10 |
FLT_LT | flt_lt | < |
FLT_MINUS | flt_minus | - |
FLT_MULT | flt_mult | * |
FLT_MULT_ASSIGN | flt_mult_assign | *= |
FLT_NE | flt_ne | != |
FLT_PARSE | flt_parse | fltParse |
FLT_PLUS | flt_plus | (noop) |
FLT_POW | flt_pow | pow |
FLT_RAND | flt_rand | fltRand |
FLT_ROUND | flt_round | a<0.0?-((inttype)(0.5-a)):(inttype)(0.5+a) |
FLT_SBTR | flt_sbtr | - |
FLT_SHRINK | flt_shrink | -= |
FLT_SIN | flt_sin | sin |
FLT_SINH | flt_sinh | sinh |
FLT_SQRT | flt_sqrt | sqrt |
FLT_STR | flt_str | fltStr |
FLT_TAN | flt_tan | tan |
FLT_TANH | flt_tanh | tanh |
FLT_TRUNC | flt_trunc | (inttype) |
FLT_VALUE | flt_value | fltValue |
Action name | drwlib.c function | kbd_rtl.c/drw_x11.c/drw_win.c function |
GKB_BUSY_GETC | gkb_busy_getc | gkbKeyPressed() ? gkbGetc() : 512 |
GKB_GETC | gkb_getc | gkbGetc |
GKB_GETS | gkb_gets | gkbGets |
GKB_KEYPRESSED | gkb_keypressed | gkbKeyPressed |
GKB_LINE_READ | gkb_line_read | gkbLineRead |
GKB_RAW_GETC | gkb_raw_getc | gkbRawGetc |
GKB_WINDOW | gkb_window | gkbWindow |
GKB_WORD_READ | gkb_word_read | gkbWordRead |
GKB_XPOS | gkb_xpos | gkbXpos |
GKB_YPOS | gkb_ypos | gkbYpos |
Action name | hshlib.c function | hsh_rtl.c function |
HSH_CONTAINS | hsh_contains | hshContains |
HSH_CPY | hsh_cpy | hshCpy |
HSH_CREATE | hsh_create | hshCreate |
HSH_DESTR | hsh_destr | hshDestr |
HSH_EMPTY | hsh_empty | hshEmpty |
HSH_EXCL | hsh_excl | hshExcl |
HSH_FOR | hsh_for | for |
HSH_FOR_DATA_KEY | hsh_for_data_key | for |
HSH_FOR_KEY | hsh_for_key | for |
HSH_IDX | hsh_idx | hshIdx, hshIdxAddr |
HSH_IDX2 | hsh_idx2 | |
HSH_INCL | hsh_incl | hshIncl |
HSH_KEYS | hsh_keys | hshKeys |
HSH_LNG | hsh_lng | a->size |
HSH_REFIDX | hsh_refidx | |
HSH_VALUES | hsh_values | hshValues |
Action name | intlib.c function | int_rtl.c function |
INT_ABS | int_abs | labs |
INT_ADD | int_add | + |
INT_BINOM | int_binom | intBinom |
INT_BIT_LENGTH | int_bit_length | intBitLength |
INT_CMP | int_cmp | intCmp |
INT_CONV | int_conv | (noop) |
INT_CPY | int_cpy | intCpy |
INT_CREATE | int_create | intCreate |
INT_DECR | int_decr | -- |
INT_DIV | int_div | / |
INT_EQ | int_eq | == |
INT_FACT | int_fact | fact[a] |
INT_GE | int_ge | >= |
INT_GROW | int_grow | += |
INT_GT | int_gt | > |
INT_HASHCODE | int_hashcode | (noop) |
INT_INCR | int_incr | ++ |
INT_LE | int_le | <= |
INT_LOG2 | int_log2 | intLog2 |
INT_LOWEST_SET_BIT | int_lowest_set_bit | intLowestSetBit |
INT_LPAD0 | int_lpad0 | intLpad0 |
INT_LSHIFT | int_lshift | << |
INT_LSHIFT_ASSIGN | int_lshift_assign | <<= |
INT_LT | int_lt | < |
INT_MDIV | int_mdiv | a>0&&b<0?(a-1)/b-1:a<0&&b>0?(a+1)/b-1:a/b |
INT_MINUS | int_minus | - |
INT_MOD | int_mod | c=a%b,((a>0&&b<0)||(a<0&&b>0))&&c!=0?c+b:c |
INT_MULT | int_mult | * |
INT_MULT_ASSIGN | int_mult_assign | *= |
INT_NE | int_ne | != |
INT_ODD | int_odd | &1 |
INT_ORD | int_ord | (noop) |
INT_PARSE | int_parse | intParse |
INT_PLUS | int_plus | (noop) |
INT_POW | int_pow | intPow |
INT_PRED | int_pred | -- |
INT_RAND | int_rand | intRand |
INT_REM | int_rem | % |
INT_RSHIFT | int_rshift | a>>b a<0?~(~a>>b):a>>b |
INT_RSHIFT_ASSIGN | int_rshift_assign | a>>=b if (a<0) a= ~(~a>>b); else a>>=b; |
INT_SBTR | int_sbtr | - |
INT_SHRINK | int_shrink | -= |
INT_SQRT | int_sqrt | intSqrt |
INT_STR | int_str | intStr |
INT_STR_BASED | int_str_based | intStrBased |
INT_SUCC | int_succ | +1 |
INT_VALUE | int_value | intValue |
Action name | itflib.c function | |
ITF_CONV2 | itf_conv2 | (noop) |
ITF_CPY | itf_cpy | = |
ITF_CPY2 | itf_cpy2 | = |
ITF_CREATE | itf_create | |
ITF_CREATE2 | itf_create2 | |
ITF_EQ | itf_eq | == |
ITF_NE | itf_ne | != |
ITF_SELECT | itf_select |
Action name | kbdlib.c function | kbd_rtl.c/kbd_inf.c function |
KBD_BUSY_GETC | kbd_busy_getc | kbdKeyPressed() ? kbdGetc() : 512 |
KBD_GETC | kbd_getc | kbdGetc |
KBD_GETS | kbd_gets | kbdGets |
KBD_KEYPRESSED | kbd_keypressed | kbdKeyPressed |
KBD_LINE_READ | kbd_line_read | kbdLineRead |
KBD_RAW_GETC | kbd_raw_getc | kbdRawGetc |
KBD_WORD_READ | kbd_word_read | kbdWordRead |
Action name | lstlib.c function | |
LST_CAT | lst_cat | |
LST_CPY | lst_cpy | |
LST_CREATE | lst_create | |
LST_DESTR | lst_destr | |
LST_ELEM | lst_elem | |
LST_EMPTY | lst_empty | |
LST_EXCL | lst_excl | |
LST_HEAD | lst_head | |
LST_IDX | lst_idx | |
LST_INCL | lst_incl | |
LST_LNG | lst_lng | |
LST_RANGE | lst_range | |
LST_TAIL | lst_tail |
Action name | prclib.c function | |
PRC_ARGS | prc_args | |
PRC_BEGIN | prc_begin | |
PRC_BLOCK | prc_block | |
PRC_BLOCK_DEF | prc_block_def | |
PRC_CASE | prc_case | switch |
PRC_CASE_DEF | prc_case_def | switch |
PRC_CPY | prc_cpy | |
PRC_CREATE | prc_create | |
PRC_DECLS | prc_decls | |
PRC_DYNAMIC | prc_dynamic | |
PRC_EXIT | prc_exit | exit |
PRC_FOR_DOWNTO | prc_for_downto | for |
PRC_FOR_TO | prc_for_to | for |
PRC_HEAPSTAT | prc_heapstat | |
PRC_HSIZE | prc_hsize | heapsize |
PRC_IF | prc_if | if |
PRC_IF_ELSIF | prc_if_elsif | if |
PRC_INCLUDE | prc_include | |
PRC_LOCAL | prc_local | |
PRC_NOOP | prc_noop | prcNoop |
PRC_RAISE | prc_raise | raise_error |
PRC_REPEAT | prc_repeat | do |
PRC_RES_BEGIN | prc_res_begin | |
PRC_RES_LOCAL | prc_res_local | |
PRC_RETURN | prc_return | |
PRC_RETURN2 | prc_return2 | |
PRC_SETTRACE | prc_settrace | |
PRC_TRACE | prc_trace | |
PRC_VARFUNC | prc_varfunc | |
PRC_VARFUNC2 | prc_varfunc2 | |
PRC_WHILE | prc_while | while |
Action name | prglib.c function | prg_comp.c function |
PRG_CPY | prg_cpy | prgCpy |
PRG_CREATE | prg_create | |
PRG_DECL_OBJECTS | prg_decl_objects | prgDeclObjects |
PRG_DESTR | prg_destr | |
PRG_EMPTY | prg_empty | |
PRG_EQ | prg_eq | == |
PRG_ERROR_COUNT | prg_error_count | prgErrorCount |
PRG_EVAL | prg_eval | prgEval |
PRG_EXEC | prg_exec | prgExec |
PRG_FIL_PARSE | prg_fil_parse | prgFilParse |
PRG_FIND | prg_find | |
PRG_MATCH | prg_match | prgMatch |
PRG_NAME | prg_name | arg_0 |
PRG_NE | prg_ne | != |
PRG_PROG | prg_prog | |
PRG_STR_PARSE | prg_str_parse | prgStrParse |
PRG_SYOBJECT | prg_syobject | prgSyobject |
PRG_SYSVAR | prg_sysvar | prgSysvar |
PRG_VALUE | prg_value | prgValue |
Action name | reflib.c function | ref_data.c function |
REF_ADDR | ref_addr | & |
REF_ALLOC | ref_alloc | refAlloc |
REF_ARRMAXIDX | ref_arrmaxidx | refArrmaxidx |
REF_ARRMINIDX | ref_arrminidx | refArrminidx |
REF_ARRTOLIST | ref_arrtolist | refArrtolist |
REF_BODY | ref_body | refBody |
REF_BUILD | ref_build | |
REF_CATEGORY | ref_category | refCategory |
REF_CAT_PARSE | ref_cat_parse | refCatParse |
REF_CAT_STR | ref_cat_str | refCatStr |
REF_CMP | ref_cmp | refCmp |
REF_CONTENT | ref_content | |
REF_CONV | ref_conv | (noop) |
REF_CPY | ref_cpy | refCpy |
REF_CREATE | ref_create | refCreate |
REF_DEREF | ref_deref | |
REF_EQ | ref_eq | == |
REF_FILE | ref_file | refFile |
REF_FIND | ref_find | |
REF_HASHCODE | ref_hashcode | (inttype)(((uinttype)a)>>6) |
REF_ISSYMB | ref_issymb | |
REF_ISVAR | ref_isvar | refIsvar |
REF_ITFTOSCT | ref_itftosct | refItftosct |
REF_LINE | ref_line | refLine |
REF_LOCAL_CONSTS | ref_local_consts | refLocalConsts |
REF_LOCAL_VARS | ref_local_vars | refLocalVars |
REF_MKREF | ref_mkref | |
REF_NAME | ref_name | |
REF_NE | ref_ne | != |
REF_NIL | ref_nil | |
REF_NUM | ref_num | refNum |
REF_PARAMS | ref_params | refParams |
REF_PROG | ref_prog | |
REF_RESINI | ref_resini | refResini |
REF_RESULT | ref_result | refResult |
REF_SCAN | ref_scan | |
REF_SCTTOLIST | ref_scttolist | refScttolist |
REF_SELECT | ref_select | a->stru[b] |
REF_SETCATEGORY | ref_setcategory | refSetCategory |
REF_SETPARAMS | ref_setparams | refSetParams |
REF_SETTYPE | ref_settype | refSetType |
REF_STR | ref_str | refStr |
REF_SYMB | ref_symb | |
REF_TRACE | ref_trace | printf |
REF_TYPE | ref_type | refType |
REF_VALUE | ref_value | refValue |
Action name | rfllib.c function | rfl_data.c function |
RFL_APPEND | rfl_append | rflAppend |
RFL_CAT | rfl_cat | rflCat |
RFL_CPY | rfl_cpy | rflCpy |
RFL_CREATE | rfl_create | rflCreate |
RFL_DESTR | rfl_destr | rflDestr |
RFL_ELEM | rfl_elem | rflElem |
RFL_ELEMCPY | rfl_elemcpy | rflElemcpy |
RFL_EMPTY | rfl_empty | |
RFL_EQ | rfl_eq | rflEq |
RFL_EXCL | rfl_excl | |
RFL_EXPR | rfl_expr | |
RFL_FOR | rfl_for | for |
RFL_HEAD | rfl_head | rflHead |
RFL_IDX | rfl_idx | rflIdx |
RFL_INCL | rfl_incl | rflIncl |
RFL_IPOS | rfl_ipos | rflIpos |
RFL_LNG | rfl_lng | rflLng |
RFL_MKLIST | rfl_mklist | rflMklist |
RFL_NE | rfl_ne | rflNe |
RFL_NOT_ELEM | rfl_not_elem | !rflElem |
RFL_POS | rfl_pos | rflPos |
RFL_RANGE | rfl_range | rflRange |
RFL_SETVALUE | rfl_setvalue | rflSetvalue |
RFL_TAIL | rfl_tail | rflTail |
RFL_TRACE | rfl_trace | |
RFL_VALUE | rfl_value | rflValue |
Action name | scrlib.c function | scr_inf.c/scr_rtl.c/scr_win.c function |
SCR_CLEAR | scr_clear | scrClear |
SCR_CURSOR | scr_cursor | scrCursor |
SCR_FLUSH | scr_flush | scrFlush |
SCR_HEIGHT | scr_height | scrHeight |
SCR_H_SCL | scr_h_scl | scrHScroll |
SCR_OPEN | scr_open | scrOpen |
SCR_SETPOS | scr_setpos | scrSetpos |
SCR_V_SCL | scr_v_scl | scrVScroll |
SCR_WIDTH | scr_width | scrWidth |
SCR_WRITE | scr_write | scrWrite |
Action name | sctlib.c function | |
SCT_ALLOC | sct_alloc | |
SCT_CAT | sct_cat | |
SCT_CONV | sct_conv | |
SCT_CPY | sct_cpy | cpy_ ... |
SCT_CREATE | sct_create | create_ ... |
SCT_DESTR | sct_destr | destr_ ... |
SCT_ELEM | sct_elem | |
SCT_EMPTY | sct_empty | |
SCT_INCL | sct_incl | |
SCT_LNG | sct_lng | |
SCT_REFIDX | sct_refidx | |
SCT_SELECT | sct_select | a->stru[b] |
Action name | setlib.c function | set_rtl.c function |
SET_ARRLIT | set_arrlit | setArrlit |
SET_BASELIT | set_baselit | setBaselit |
SET_CARD | set_card | setCard |
SET_CMP | set_cmp | setCmp |
SET_CONV | set_conv | (noop) |
SET_CPY | set_cpy | setCpy |
SET_CREATE | set_create | setCreate |
SET_DESTR | set_destr | setDestr |
SET_DIFF | set_diff | setDiff |
SET_ELEM | set_elem | setElem |
SET_EMPTY | set_empty | |
SET_EQ | set_eq | setEq |
SET_EXCL | set_excl | setExcl |
SET_GE | set_ge | setGe |
SET_GT | set_gt | setGt |
SET_HASHCODE | set_hashcode | setHashCode |
SET_ICONV | set_iconv | setIConv |
SET_INCL | set_incl | setIncl |
SET_INTERSECT | set_intersect | setIntersect |
SET_LE | set_le | setLe |
SET_LT | set_lt | setLt |
SET_MAX | set_max | setMax |
SET_MIN | set_min | setMin |
SET_NE | set_ne | setNe |
SET_NOT_ELEM | set_not_elem | setNotElem |
SET_RAND | set_rand | setRand |
SET_SCONV | set_sconv | setSConv |
SET_SYMDIFF | set_symdiff | setSymdiff |
SET_UNION | set_union | setUnion |
SET_VALUE | set_value | setValue |
Action name | strlib.c function | str_rtl.c function |
SOC_ACCEPT | soc_accept | socAccept |
SOC_BIND | soc_bind | socBind |
SOC_CLOSE | soc_close | socClose |
SOC_CONNECT | soc_connect | socConnect |
SOC_CPY | soc_cpy | = |
SOC_CREATE | soc_create | |
SOC_EMPTY | soc_empty | |
SOC_EQ | soc_eq | == |
SOC_GETC | soc_getc | socGetc |
SOC_GETS | soc_gets | socGets |
SOC_INET_ADDR | soc_inet_addr | socInetAddr |
SOC_INET_LOCAL_ADDR | soc_inet_local_addr | socInetLocalAddr |
SOC_INET_SERV_ADDR | soc_inet_serv_addr | socInetServAddr |
SOC_LINE_READ | soc_line_read | socLineRead |
SOC_LISTEN | soc_listen | socListen |
SOC_NE | soc_ne | != |
SOC_RECV | soc_recv | socRecv |
SOC_RECVFROM | soc_recvfrom | socRecvfrom |
SOC_SEND | soc_send | socSend |
SOC_SENDTO | soc_sendto | socSendto |
SOC_SOCKET | soc_socket | socSocket |
SOC_WORD_READ | soc_word_read | socWordRead |
SOC_WRITE | soc_write | socWrite |
Action name | strlib.c function | str_rtl.c function |
STR_APPEND | str_append | strAppend |
STR_CAT | str_cat | strConcat, strConcatTemp |
STR_CHIPOS | str_chipos | strChIpos |
STR_CHPOS | str_chpos | strChPos |
STR_CHSPLIT | str_chsplit | strChSplit |
STR_CLIT | str_clit | strCLit |
STR_CMP | str_cmp | strCompare |
STR_CNT | act_illegal | |
STR_CPY | str_cpy | strCopy |
STR_CREATE | str_create | strCreate |
STR_DESTR | str_destr | strDestr |
STR_ELEMCPY | str_elemcpy | a->mem[b-1]=c |
STR_EQ | str_eq | a->size==b->size&&memcmp(a,b,a->size*sizeof(strelemtype))==0 |
STR_GE | str_ge | strGe |
STR_GETENV | str_getenv | strGetenv |
STR_GT | str_gt | strGt |
STR_HASHCODE | str_hashcode | strHashCode |
STR_HEAD | str_head | strHead |
STR_IDX | str_idx | a->mem[b-1] |
STR_IPOS | str_ipos | strIpos |
STR_LE | str_le | strLe |
STR_LIT | str_lit | strLit |
STR_LNG | str_lng | a->size |
STR_LOW | str_low | strLow, strLowTemp |
STR_LPAD | str_lpad | strLpad |
STR_LPAD0 | str_lpad0 | strLpad0, strLpad0Temp |
STR_LT | str_lt | strLt |
STR_MULT | str_mult | strMult |
STR_NE | str_ne | a->size==b->size&&memcmp(a,b,a->size*sizeof(strelemtype))==0 |
STR_POS | str_pos | strPos |
STR_RANGE | str_range | strRange |
STR_RCHPOS | str_rchpos | strRChPos |
STR_REPL | str_repl | strRepl |
STR_RPAD | str_rpad | strRpad |
STR_RPOS | str_rpos | strRpos |
STR_SPLIT | str_split | strSplit |
STR_STR | str_str | (noop) |
STR_SUBSTR | str_substr | strSubstr |
STR_TAIL | str_tail | strTail |
STR_TOUTF8 | str_toutf8 | strToUtf8 |
STR_TRIM | str_trim | strTrim |
STR_UP | str_up | strUp, strUpTemp |
STR_UTF8TOSTRI | str_utf8tostri | strUtf8ToStri |
STR_VALUE | str_value | strValue |
Action name | timlib.c function | tim_unx.c/tim_win.c function |
TIM_AWAIT | tim_await | timAwait |
TIM_NOW | tim_now | timNow |
Action name | typlib.c function | typ_data.c function |
TYP_ADDINTERFACE | typ_addinterface | |
TYP_CMP | typ_cmp | typCmp |
TYP_CPY | typ_cpy | typCpy |
TYP_CREATE | typ_create | typCreate |
TYP_DESTR | typ_destr | typDestr |
TYP_EQ | typ_eq | == |
TYP_FUNC | typ_func | typFunc |
TYP_GENSUB | typ_gensub | |
TYP_GENTYPE | typ_gentype | |
TYP_HASHCODE | typ_hashcode | (inttype)(((uinttype)a)>>6) |
TYP_ISDECLARED | typ_isdeclared | |
TYP_ISDERIVED | typ_isderived | typIsDerived |
TYP_ISFORWARD | typ_isforward | |
TYP_ISFUNC | typ_isfunc | typIsFunc |
TYP_ISVARFUNC | typ_isvarfunc | typIsVarfunc |
TYP_MATCHOBJ | typ_matchobj | typMatchobj |
TYP_META | typ_meta | typMeta |
TYP_NE | typ_ne | != |
TYP_NUM | typ_num | typNum |
TYP_RESULT | typ_result | typResult |
TYP_STR | typ_str | typStr |
TYP_VALUE | typ_value | typValue |
TYP_VARCONV | typ_varconv | |
TYP_VARFUNC | typ_varfunc | typVarfunc |
Action name | ut8lib.c function | ut8_rtl.c function |
UT8_GETC | ut8_getc | ut8Getc |
UT8_GETS | ut8_gets | ut8Gets |
UT8_LINE_READ | ut8_line_read | ut8LineRead |
UT8_SEEK | ut8_seek | ut8Seek |
UT8_WORD_READ | ut8_word_read | ut8WordRead |
UT8_WRITE | ut8_write | ut8Write |
The compile time errors are not fatal (the program can execute) except for the error 1 (Out of heap space) which terminates the compilation process and no execution occurs.
1: Fatal Error: Out of heap space 2: File "%s" not found 3: Include file "%s" not found 4: "END OF FILE" encountered 5: Illegal character in text "%s" 6: Unclosed comment 7: Illegal pragma "%s" 8: Illegal action "%s" 9: Illegal system declaration "%s" 10: Integer "%s" too big 11: Negative exponent in integer literal 12: Digit expected found "%s" 13: Integer "%dE%s" too big 14: Integer base "%ld" not between 2 and 36 15: Extended digit expected found "%s" 16: Illegal digit "%c" in based integer "%d#%s" 17: Based integer "%d#%s" too big 18: "'" expected found "%s" 19: Character literal exceeds source line 20: Use \" instead of "" to represent " in a string 21: Use / instead of \\ as path delimiter 22: Illegal string escape "\%s" 23: Numerical escape sequences should end with "\" not "%s"); 24: String continuations should end with "\" not "%s"); 25: String literal exceeds source line 26: Name expected found "%s" 27: Integer literal expected found "%s" 28: String literal expected found "%s" 29: Identifier expected found "%s" 30: Expression expected found "%s" 31: Expression expected after "begin" 32: Declaration expected found "%s" 33: Initialisation of "%s" failed 34: "%s" declared twice 35: "%s" not declared 36: Associativity expected found "%s" 37: Statement priority "%s" too big 38: Syntax with two parameters before operator is illegal 39: Empty syntax declaration 40: "%s" redeclared with infix priority %d not %d 41: "%s" redeclared with prefix priority %d not %d 42: Priority %d required for parameter after "%s" not %d 43: Priority <= %d expected found "%s" with priority %d 44: Priority <= %d expected found "%s" with priority %d 45: "%s" must have priority %d not %d for dot expression 46: "%s" expected found "%s" 47: "%s" expected found "%s" 48: Undefined type for literal "%s" 49: "newtype", "subtype", "func", "enumlit" or "action" expected found "%s" 50: "func" or "type" expected found "%s" 51: Match for %s failed 52: Variable expected in %s found %s 53: Type expected found %s 54: Procedure expected found %s expression 55: Parameter specifier expected found "%s" 56: Evaluate type expression %s failed Undefined error14.2 Exceptions
There are various exceptions which can be raised during program execution:
To catch an EXCEPTION the following handler construct can be used:
block number := 1 div 0; exception catch NUMERIC_ERROR: number := 1; end block;14.4 Stack trace
When an EXCEPTION is not catched at any level the program is terminated and the hi interpreter generates a stack trace. For example:
*** Uncaught EXCEPTION NUMERIC_ERROR raised with {integer <80b2e64>:14.5 Other errors and warnings0 div integer <80b2e64>: 0 } {(in integer <80b2e64> param) div (in integer <80b2e64> param) } at lander.sd7(1028) drawLogo {} at lander.sd7(1080) advanced_lander {} at lander.sd7(873) setup {} at lander.sd7(1441) main {} no POSINFO