Seed7

From Bauman National Library
This page was last modified on 8 June 2016, at 12:30.
Seed7
fraimed
Paradigm multi-paradigm, extensible, object-oriented, imperative, structured, generic, reflective
Designed by Thomas Mertes
Stable release 2015-09-14 / September 14, 2015; 7 years ago (2015-09-14)
Typing discipline static, strong, safe, nominative, manifest
License GPL, LGPL (for the runtime library)
Website seed7.sourceforge.net
Major implementations
open source reference implementation.

Seed7 is a general-purpose programming language. It is a higher level language compared to Ada, C++ and Java.

Introduction

What is Seed7?

In Seed7 new statements and operators can be defined 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.

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 which search for some data in a database can become more understandable by using a for statement to loop over the tables. A usage of such a for statement could be:

 for person1, person2
  where person1.age = person2.age and
      person1.mother = person2.mother and
      person1 <> person2 do
    writeln("Twins: " <&amp; person1.name <&amp; " and " <&amp; person2.name);
 end for;
Such extensions make understanding, changing and debugging of a program easier.

Features of Seed7

Seed7 has the following features:

  • The Seed7 Structured Syntax Description
The Seed7 Structured Syntax Description is abreviated with S7SSD. The S7SSD can describe most but not all of the syntax of a programming language. The syntax of identifiers, literals and comments is not described with S7SSD. S7SSD views a program as a big typeless expression. The syntax of this expression is described with prefix-, infix- and postfix-operators. The operators have a priority and an associativity. Operators can have one or more operator symbols. The operator symbols of an operator can be adjacent or they can have parameters in between. The S7SSD of an infix + is:
$ syntax expr: .(). + .()   is -> 7;
This defines the + as left associative infix operator with priority 7. The + operator is an infix o:perator because the operator pattern is:
() + ()
The place of the parameters is specified with (). Any expression can be used as parameter. The type of the parameters and the type of the result of + is not specified by the S7SSD. Checks for the correct type are not done at the syntactic level. This way S7SSD allows syntax that would not be allowed in a corresponding EBNF description. S7SSD considers just operator symbols and their priority and associativity.
  • Types are first class objects and therefore templates and generics can be defined easily without special syntax.
  • Predefined constructs like arrays or for-loops are defined in the language itself.
  • Object orientation is based on interfaces, supports multiple dispatch and allows to connect methods to objects.
  • Static type checking and no automatic casts.
With static type checking all type checks are performed during compile time. Type errors, such as an attempt to divide an integer by a string, can be caught earlier (unless this unusual operation has been defined). The key point is that type errors are found without the need to execute the program. Some type errors can be hidden in rarely executed code paths. Static type checking can find such errors easily. With dynamic type checking extensive tests are necessary to find all type errors. Even tests with 100% code coverage are not enough since the combination of all places where values are created and all places where these values are used must be taken into account. That means that testing cannot guarantee to find all type errors that a static type checker can find. Additionally it would be necessary to repeat all tests every time the program is changed. Naturally there are doubts that enough tests are done and that the tests are adjusted and repeated for every change in the program. Therefore it can be said that compile time type checks increase the reliability of the program.
Seed7 makes sure that the object values always have the type of the object. This goal is reached with mechanisms like mandatory initialization, runtime checks and the impossibility to change arbitrary places in memory. When the generation of garbage values is avoided, it can be guaranteed that only legal values of the correct type are used as object values. This way runtime type checks are unnecessary and the program execution can be more efficient.
  • The overloading rules are much simpler.
An expression can be understood without its calling context.
Errors caused by unplanned automatic type conversions cannot happen.
Since you have to do type conversions explicit you are more aware of the run time overhead.
  • Interface types can be used when an object can have several types at runtime.
In this case the interface type of the object can be determined at compile time and the type of the object value (implementation type) can vary at runtime. The static type checking can still check the interface type and the presence of interface functions. Additionally the compiler can also check that all functions granted by the interface type are defined for the implementation type.
  • Various predefined types like resizable arrays, hashes, bitsets, structs, etc.
But a new programming language differs not only from existing ones by new features. The real advantage comes from omitting features which are outdated.

Concepts in use by other languages are not present

  • There is no goto statement. Hidden gotos like break- and continue-statements are also omitted.
  • There is no return statement. Instead a result variable can be defined to which the result of a function can be assigned.
  • There are no automatic type conversions. When a subprogram should be used for different types it must be overloaded.
  • There are no variable length parameter lists. Instead it is possible to use arrays as parameters.
  • There are no default parameters. But it is easy to define two subprograms: One with and one without an additional parameter.
  • There is no special "parameter" called "self" or "this". In a procedure the receiving object is defined as formal parameter with a user-defined name.
  • There is no macro feature since this mechanism is too similar to the subprogram feature. Instead subprograms can be used in a more flexible way than in other languages.
  • There are no reserved words.
  • There is no conceptual distinction between functions, operators, procedures and statements.
  • The procedure calling mechanism is not based on a concept with an object-message pair (An object receives a message). Instead a match is done over a list of objects. This more general (and powerful) mechanism is called multiple dispatch and it includes the simple object-message mechanism as special case.

Concepts which are also used by other languages:

  • Block comments start with (* and end with *) and may be nested.
  • Line comments start with # and are terminated with the end of the line.

Concepts which are new

  • Variables and constants must be initialized when they are defined.
  • Every expression has exactly one type. That means that overloaded functions are resolved with their actual parameters and not with the context of their call. (This is different to the overloading mechanism used by ADA)
  • With a syntax declaration new operators and statements can be defined.
  • Not only predefined operator symbols can be overloaded. Additionally it is possible to invent completely new operator symbols.

Restrictions of other languages are released

  • There is no limitation in the length of an identifier and all characters of an identifier are significant.
  • Statements and parentheses can be nested without limitation in depth.
  • The number of parameters and local variables is not limited.
  • Strings can contain any characters (also the NUL character) This allows holding binary information in strings.
  • Although strings are not NUL terminated they have no size limitation. (Except there is no more memory)
  • String literals can have any length.
  • There is no limitation in the length of a source line.
  • There is no level limitation for nesting includes.

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. Seed7 uses three kinds of declarations:

  • Normal declarations
  • Syntax declarations
  • System declarations

which are described in detail in the following sub-chapters.

Normal declarations

Normal 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 initialization expressions are also possible. For example

var string: fileName is NAME &amp; ".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 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;

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 call-by-name parameters:

 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 initialization uses the creation operation ( ::= ). Explicit calls of the create operation are not needed.

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 statements copies the right expression to the left expression taking into account that the left expression is undefined.
  4. If the object is variable other values can be assigned using the assign statement ( := ). The assignment can assume that the left expression contains a legal value. This allows that for strings (and some other types which are just references to a memory area) the memory containing the old string value (and not the memory of the object itself) can be freed when necessary.
  5. 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 memory containing the string value (and not the memory of the object itself) is freed.
  6. The memory of the object is freed.
  7. 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 initialization value.

The assignment ( := ) can only be used to assign a value to a variable and initialization is also needed for constants. Sometimes some initializations are needed for the new object in addition to the pure assignment. 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.

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:

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 types of literals and simple expressions for example: string for strings and integer for integers

Which objects should be used as result values for primitive actions for example
  • TRUE, FALSE and empty
  • The EXCEPTIONS which are to be raised by primitive actions for example:
  • NUMERIC_ERROR and MEMORY_ERROR

The following system declarations exist

 $ system "type" is type;
 $ 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 "range_error" is RANGE_ERROR;
 $ system "file_error" is FILE_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;

Hello world

A 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

s7 hello

The Seed7 interpreter writes something like

SEED7 INTERPRETER Version 5.0.4  Copyright (c) 1990-2013 Thomas Mertes
hello world

LINKS