[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4. The STELLA Language


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.1 Language Overview

STELLA is a strongly typed, object-oriented, Lisp-like language. STELLA programs are first translated into either Common Lisp, C++, or Java, and then compiled with any conventional compiler for the chosen target language to generate executable code. Over 95% of the STELLA system is written in STELLA itself, the rest is written in target-language-specific native code.

The design of STELLA borrows from a variety of programming languages, most prominently from Common Lisp, and to a lesser degree from other object-oriented languages such as Eiffel, Sather, and Dylan. Since STELLA has to be translatable into C++ and Java, various restrictions of these languages also influenced its design.

In the following, we assume that the reader is familiar with basic Common Lisp concepts, and has at least some familiarity with C++ or Java. Let us start with a cursory overview of STELLA’s main features:

Syntax: STELLA uses a parenthesized, uniform expression syntax similar to Lisp. Most definitional constructs and control structures are similar to their Common Lisp analogues with variations to support types.

Type system: STELLA is strongly typed and supports efficient static compilation similar to C++. Types are required for the arguments and return values of functions and methods, for global variables, and for slot definitions. Local, lexically scoped variables can be typed implicitly by relying on type inference.

Object system: Types are organized into a single inheritance class hierarchy. Restricted multiple inheritance is allowed via mixin classes. Dynamic method dispatch is based on the runtime type of the first argument (similar to C++ and Java). Slots can be static (native) or dynamic. Dynamic slots can be defined at runtime and do not occupy any space until they are filled. Slots can have both initial and default values, and demons can be triggered by slot accesses. A meta-object protocol allows the control of object creation, initialization, termination, and destruction.

Control structure: Functions and methods are distinguished. They can have multiple (zero or more) return values and a variable number of arguments. Lisp-style macros are supported to facilitate syntax extensions. Expressions and statements are distinguished. Local variables are lexically scoped, but dynamically scoped variables (specials) are also supported. STELLA has an elegant, uniform, and efficient iteration mechanism plus a built-in protocol for iterators. An exception mechanism can be used for error handling and non-local exits.

Symbolic programming: Symbols are first-class objects, and extensive support for dynamic datatypes such as cons-trees, lists, sets, association lists, hash tables, extensible vectors, etc., is available. A backquote mechanism facilitates macro writing and code generation. Interpreted function call, method call, slot access, and object creation is supported, and a restricted evaluator is also available.

Name spaces: Functions, methods, variables, and classes occupy separate name spaces (i.e., the same name can be used for a function and a class). A hierarchical module system compartmentalizes symbol tables and supports large-scale programming.

Memory management: STELLA relies on automatic memory management via a garbage collector. For Lisp and Java the native garbage collector is used. For the C++ version of STELLA we use the Boehm- Weiser conservative garbage collector with good results. Various built-in support for explicit memory management is also available.

The Common Lisp features most prominently absent from STELLA are anonymous functions via lambda abstraction, lexical closures, multi-methods, full-fledged eval (a restricted evaluator is available), optional and keyword arguments, and a modifiable readtable. STELLA does also not allow dynamic re/definition of functions and classes, even though the Lisp-based development environment provides this facility (similar to Dylan). The main influences of C++ and Java onto STELLA are the strong typing, limited multiple inheritance, first-argument polymorphism, and the distinction between statements and expressions.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2 Basic Data Types (tbw)

To be written.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3 Control Structure (tbc)

To be completed.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3.1 Conditionals

STELLA conditionals are very similar to those found in Common-Lisp. The main difference is that most STELLA conditionals are statements and therefore do not return a value. For this reason, a C++-style choose directive has been added to the language to allow value conditionalization based on a boolean expression.

Statement: if condition then-statement else-statement

Evaluate the boolean expression condition. If the result is true execute then-statement, otherwise, execute else-statement. Note that unlike the Common-Lisp version of if the else-statement is not optional in STELLA. Example:

 
(if (> x y)
    (print "x is greater than y" EOL)
  (print "x is less than or equal to y" EOL))
Statement: when condition statement…

Evaluate the boolean expression condition. Only if the result is true execute the statement’s in the body. Example:

 
(when (symbol? x)
  (print "x is a symbol, ")
  (print "its name is " (symbol-name (cast x SYMBOL)) EOL))
Statement: unless condition statement…

Evaluate the boolean expression condition. Only if the result is false execute the statement’s in the body. Therefore, (unless test …) is equivalent to (when (not test) …). Example:

 
(unless (symbol? x)
  (print "x is not a symbol, ")
  (print "hence, its name is unknown" EOL))
Statement: cond clause…

cond is a conditional with an arbitrary number of conditions each represented by a clause. Each cond clause has to be of the following form:

 
(condition statement…)

The first clause whose condition evaluates to true will be selected and its statement’s will be executed. Each clause can have 0 or more statements. The special condition otherwise always evaluates to true and can be used for the catch-all case. Example:

 
(cond ((symbol? x)
       (print "x is a symbol" EOL))
      ((cons? x)
       (print "x is a cons" EOL))
      (otherwise
       (print "x is an object" EOL)))
Expression: choose condition true-expression false-expression

Evaluate the boolean expression condition. If the result is true return the value of true-expression, otherwise, return the value of false-expression. STELLA computes the most specific common supertype of true-expression and false-expression and uses that as the type returned by the choose expression. If no such type exists, a translation error will be signaled. Example:

 
(setq face (choose happy? :smile :frown))
Statement: case expression clause…

Each case clause has to be of one of the following forms:

 
(key statement…)
((key…) statement…)

case selects the first clause whose key (or one of the listed key’s) matches the result of expression and executes the clause’s statement’s. Each case key has to be a constant such as a number, character, string, symbol, keyword or surrogate. Keys are compared with eql? (or string-eql? for strings). All keys in a case statement have to be of the same type. The special key otherwise can be used to catch everything. It is a run-time error if no clause with a matching key exists. Therefore, a STELLA case without an otherwise clause corresponds to a Common Lisp ecase. An empty otherwise clause can always be specified via (otherwise NULL). Example:

 
(case car-make
  ("Yugo" 
   (setq price :cheap))
  ("VW"   
   (setq price :medium))
  (("Ferrari" "Rolls Royce")
   (setq price :expensive))
  (otherwise 
   (setq price :unknown)))
Statement: typecase expression clause…

Each typecase clause has to be of one of the following forms:

 
(type statement…)
((type…) statement…)

typecase selects the first clause whose type (or one of the listed type’s) equals or is a supertype of the run-time type of the result of expression and then executes the clause’s statement’s. Therefore, typecase can be used to implement a type dispatch for cases where the run-time type of an expression can be different from the static type known at translation time. Currently, the static type of expression is required to be a subtype of OBJECT.

Each type expression has to be a symbol describing a simple type (i.e., parametric or anchored types are not allowed). Similar to case, the special key otherwise can be used to catch everything. It is a run-time error if no clause with a matching type exists. Therefore, a STELLA typecase without an otherwise clause corresponds to a Common Lisp etypecase. An empty otherwise clause can always be specified via (otherwise NULL). typecase does allow the value of expression to be undefined, in which case the otherwise clause is selected. Example:

 
(typecase (first list)
  (CONS
   (print "it is a cons"))
  ((SYMBOL KEYWORD)
   (print "it is a symbol"))
  (STANDARD-OBJECT
   (print "it is a regular object"))
  (otherwise NULL))

Note that in the example above it is important to list STANDARD-OBJECT after SYMBOL and CONS, since it subsumes the preceding types. Otherwise, it would always shadow the clauses with the more specific types.

The semantics of typecase is slightly extended for the case where expression is a local variable. In that case each reference to the variable within a typecase clause is automatically casted to the appropriate narrower type. For example, in the code snippet below method calls such as first or slot accesses such as symbol-name are translated correctly without needing to explicitly downcast x which is assumed to be of type OBJECT:

 
(typecase x
  (CONS
   (print "it is a cons with value " (first x)))
  ((SYMBOL KEYWORD)
   (print "it is a symbol with name " (symbol-name x)))
  (STANDARD-OBJECT
   (print "it is a regular object"))
  (otherwise NULL))

Since the typecase expression has to be a subtype of OBJECT, a typecase cannot be used to test against literal types such as STRING or INTEGER. If such type names are encountered as keys in a typecase, they are automatically converted to their wrapped version, e.g., STRING-WRAPPER, INTEGER-WRAPPER, etc.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4 Functions (tbw)

To be written.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.5 Classes (tbw)

To be written.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.6 Methods (tbw)

To be written.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.7 Macros (tbw)

To be written.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.8 Modules (tbw)

To be written.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Hans Chalupsky on January 5, 2023 using texi2html 1.82.