Manual
Declarations
 previous   up   next 

3. DECLARATIONS

A 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. All declarations are introduced with a specific keyword and follow a common pattern. The keyword is followed by a type (in example code red is used for types). In the table below aType is a placeholder for any type.

Declaration Comment
var aType: name is ... A variable declaration
const aType: name is ... A constant declaration
val aType: name Declaration of a value-parameter
ref aType: name Declaration of a reference-parameter
in aType: name Declaration of an in-parameter
in var aType: name Declaration of an in-var-parameter
inout aType: name Declaration of an inout-parameter
in func aType: name Declaration of a call-by-name parameter
const type: name is ... A type declaration
const proc: name is ... A procedure declaration
const func aType: name is ... A function declaration
const func type: name is ... Declaration of an abstract data type
const proc: name (in type: aType) is ... Declaration of a template
syntax expr: pattern is ... A syntax declaration

3.1 Variable declarations

A variable is a named place for a value. The value of a variable can be changed with an assignment. Variables must be declared before they can be used. E.g.:

var integer: number is 0;

This declares the integer variable 'number'. The variable is initialized with the value 0. Later the value of the variable can be changed with e.g.:

number := 1;

Variables can only hold values of their type. An attempt to assign a value with the wrong type results in a compile error:

*** tst545.sd7(7):52: Match for {number := "hello" } failed
    number := "hello";

3.2 Constant declarations

A constant is a named value that cannot change during run-time. Constants must be declared before they can be used. E.g.:

const integer: ONE is 1;

This declares the integer constant 'ONE' with the value 1. An attempt to change a constant results in an compile error:

*** tst544.sd7(7):53: Variable expected in {ONE := 2 } found constant integer: ONE
    ONE := 2;

3.2.1 Type declarations

Type declarations define a name for a type. Type declarations are done as constant declarations where the type of the declared constant is type. E.g.:

const type: myChar is char;

Afterwards the new type can be used in other declarations. E.g.:

var myChar: aChar is 'x';

3.2.2 Procedure declarations

The code of a procedure does not change at run-time. Thus procedures are declared with a constant declaration:

const proc: helloWorld is func
  begin
    writeln("hello world");
  end func;

Local declarations can be added to a procedure with the keyword local. E.g.:

const proc: helloWorld is func
  local
    const string: greeting is "hello world";
  begin
    writeln(greeting);
  end func;

A procedure with a parameter (greeting) is defined with:

const proc: hello (in string: greeting) is func
  begin
    writeln(greeting);
  end func;

Procedure and function parameters are explained in chapter 6 (PARAMETERS).

3.2.3 Function declarations

Like with procedures the code of a function does not change at run-time. Thus functions are also declared with a constant declaration:

const func boolean: flipCoin is
  return rand(FALSE, TRUE);

Note that return is not a statement. Instead return is a shortcut for a function declaration with a result variable:

const func boolean: flipCoin is func
  result
    var boolean: coinState is FALSE;
  begin
    coinState := rand(FALSE, TRUE);
  end func;

The keyword local allows the introduction of local declarations:

const func string: alphabet is func
  result
    var string: abc is "";
  local
    var char: ch is ' ';
  begin
    for ch range 'a' to 'z' do
      abc &:= ch;
    end for;
  end func;

A function with a parameter (number) is defined with:

const func float: inverse (in float: number) is
  return 1 / number;
Procedure and function parameters are explained in chapter 6 (PARAMETERS).

3.2.4 Abstract data type

An abstract data type is a type that requires additional information. E.g.: The abstract data type array requires the type of the array elements as additional information. Predefined abstract data types are array, subtype, struct, subrange, hash, set, interface and enum. The definition of the predefined abstract data type array in the library array.s7i starts with:

const func type: array (in type: baseType) is ...

The parameter baseType specifies the type of the array elements. The abstract data type array can be used in declarations:

var array integer: numbers is [] (1);

Other abstract data types are declared similar to the declaration of array.

User defined abstract data types are also possible.

3.2.5 Template

Templates do declarations for a given type. Templates are executed at compile-time. The function FOR_ENUM_DECLS from the library forloop.s7i defines a for-loop that loops over all values of a given enumeration type:

const proc: FOR_ENUM_DECLS (in type: aType) is func
  begin

    const proc: for (inout aType: variable) range (attr aType) do
        (in proc: statements) end for is func
      begin
        for variable range aType.first to aType.last do
          statements;
        end for;
      end func;

  end func;

The template FOR_ENUM_DECLS is invoked in the library enumeration.s7i with:

FOR_ENUM_DECLS(enumType);

3.3 Initialization

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. The type of the initial value must fit to the type of the variable or constant. A compile error is triggered if this is not the case:

*** tst546.sd7(3):52: Match for {number ::= ' ' } failed
var integer: number is ' ';
---------------------------^
*** tst546.sd7(3):32: Declaration of "number" failed
var integer: number is ' ';

Expressions can be used as initial value. E.g.:

var string: fileName is NAME & ".txt";

The expression is evaluated and the result is assigned to the new object. This is done at compile-time by the interpreter or compiler. The initialization 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;

3.3.1 How the initialization works

The initialization of variables and constants is done at compile-time. The initialization uses the create statement ( ::= ). Note that ::= is used internally by the interpreter and compiler. Explicit calls of ::= in user programs are not needed. A create statement is similar to an assignment ( := ), but with two important differences:

  1. Assignments can assume that the destination has a legal value. Create statements assume that the destination has an undefined value. A create statement needs to do all the initializations necessary.
  2. Assignments can only be used to assign a value to a variable. Create statements are not restricted to variables. Interpreter and compiler use ::= to initialize constants.

The lifetime of an object goes like this:

  1. Memory is reserved for the new object (stack or heap memory make no difference here).
  2. The content of the new memory is undefined (It may contain garbage), therefore a create statement is necessary instead of an assignment.
  3. The create statement ( ::= ) copies the right expression to the left expression taking into account that the left expression is undefined.
  4. If the object is a variable other values can be assigned using the assignment statement ( := ). The assignment can assume that the destination contains a legal value.
  5. Strings (and some other types) are just references to a memory area where the data resides. These references are the sole owner of the memory area. This allows that an assignment can reallocate the memory area.
  6. At the end of the lifetime of an object the destroy statement is executed. For strings (and some other types which are just references to a memory area) the referenced memory is freed.
  7. The memory of the object is freed.

The first three steps are usually hidden in the declaration statement. The declaration statement executes

ONE ::= 1

to assign 1 to the object ONE. The destroy statement is the opposite of the create statement. It is automatically executed at the end of the lifetime of an object. The run-time library executes

destroy(fileName)

to free the memory referred by the string variable fileName. For simple types such as integer the destroy statement does nothing.

The mechanism with create and destroy is also used for call-by-value parameters (val, in var and in if it uses call-by-value). When a function is called the call-by-value parameters are initialized with a create statement:

formalParameter ::= actualParameter

At the end of the function the destroy statement is executed:

destroy(formalParameter)

For all predefined types the create statement ( ::= ) and the destroy statement are already defined. To allow the declaration of objects of a new user defined type the create and destroy statements for this type must be defined.

3.4 Syntax declarations

Syntax 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.5 System declarations

With 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

The following system declarations exist

$ system "expr" is expr;
$ system "integer" is integer;
$ system "bigInteger" is bigInteger;
$ 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 "overflow_error" is OVERFLOW_ERROR;
$ system "range_error" is RANGE_ERROR;
$ system "index_error" is INDEX_ERROR;
$ system "file_error" is FILE_ERROR;
$ system "database_error" is DATABASE_ERROR;
$ system "graphic_error" is GRAPHIC_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;

3.6 Pragmas

Pragmas specify how a program is processed. Like system declarations pragmas are introduced with a dollar sign ($) followed by the name of the pragma. The following pragmas exist:

pragma parameter comment
$ library string Specify additional directory for *.s7i files.
$ message string Writes a message during parsing.
$ info on or off Switch compilation info on or off.
$ trace string Sets compile-time tracing flags.
$ decls - Traces the declarations.
$ names unicode or ascii Allows Unicode (or Ascii) identifiers.

An unknown pragma results in a parsing error:

*** pragma.sd7(1):7: Illegal pragma "unknownPragma"
$ unknownPragma
---------------^

The pragma message can be used to write a message during parsing. To write "hello world" during parsing use:

$ message "hello world";

The pragma info can be used to change the verbosity level of the parsing phase. This overrules the -vn option of the interpreter. With

$ info on;

the parser writes information about library names and the number of the line currently processed. With

$ info off;

no such information is written.

The pragma trace can be used to turn interpreter tracing on or off during the parsing of the program. This overrules the -dx option of the interpreter. The string parameter of the trace pragma allows a sequence of the characters +, -, a, c, d, e, h, m, u, s and *. These characters have the following meaning:

The pragma names can be used to allow Unicode in name identifiers:

$ names unicode;

This allows variables with e.g. German umlauts or Cyrillic letters. This way beginners can use variable and function names from their native language.


 previous   up   next