Content-type: text/html Man page of esh

esh

Section: User Commands (1)
Index Return to Main Contents
 

BEZEICHNUNG

esh - EFEU Befehlsinterpreter

 

ÜBERSICHT

esh--help[=type] ] [ --version ] [ --info[=entry] ] [ --debug[=mode] ] [ --verbose ] [ -I dir ] [ -D name=val ] [ -c string ] [ -E ] [ file ] [ arg(s) ]

 

BESCHREIBUNG

Das Programm esh interpretiert Skriptfiles in der Syntax des EFEU-Befehlsinterpreters. Die Syntax der Sprache ist ähnlich zu C/C++. Wer damit vertraut ist, sollte keine Schwierigkeiten haben, diese Sprache zu lernen.

Das Kommando esh akzepiert Kommentare im Stil von C/C++ und verwendet einen Präprozessor ähnlich zu C/C++. Dieser wird weiter unten beschrieben. Eine Zeile, die mit #! startet, wird nicht von esh interpretiert. Sie dient primär dazu, den Interpreter für die Ausführung des Skripts zu definieren. Die Interpreterkennung hat die Form

#!vollständiger Pfadname von esh
Damit ein Skript unabhängig vom Installationsort von esh ist, kann auch folgende Interpreterdefinition verwendet werden:
#!/usr/bin/env esh
Ausdrücke im EFEU-Befehlsinterpreter werden entweder durch einen Strichpunkt oder einen Zeilenvorschub beendet. Tabulatoren und Leerzeichen werden überlesen. Ein Ausdruck wird ebenfalls beendet, wenn einem Term kein rechter Operator mehr nachfolgt. Falls in diesem Fall ein Punktierungszeichen dem Term folgt, wird dieses als Abschlußzeichen genommen, ansonsten ein Leerzeichen. An manchen Stellen, z.B. innerhalb einer Argumentliste einer Funktion, wird ein Zeilenvorschub nicht als Abschlußzeichen, sondern nur wie ein gewöhnliches Leerzeichen behandelt.

Im äußersten Modus (außerhalb eines Blocks oder eines Funktionsrumpfes) wird jeder Ausdruck sofort nach dem Lesen ausgewertet. Falls der Ausdruck nicht durch einen Strichpunkt abgeschlossen wurde und ein Ergebnis liefert, wird dieses, gefolgt vom Abschlußzeichen, ausgegeben.

Zum Beispiel wird die Zeile

3 * 5 4 + 7 $ 2 - 1; 4 + 1
in die 4 Ausdrücke
3 * 5 mit einem Leerzeichen als Abschluß,
4 + 7 mit einem $ als Abschlußzeichen,
2 - 1 mit einem ; als Abschlußzeichen und
4 + 1 mit einem Zeilenvorschub als Abschluß
aufgespalten und die Ausgabe ist
15 11$5
im äußersten Modus.

Falls esh ohne Skriptnamen aufgerufen wird, oder falls anstelle des Skriptnamens ein einzelnes Minus steht, werden die Befehlszeilen von der Standardeingabe gelesen. Falls die Standardeingabe und die Standardausgabe mit einem Terminal verbunden sind, läuft esh interaktiv und readline wird für die Eingabe vom Terminal verwendet. Die Steuerzeichen von readline sind aktiv und ein Rufzeichen ! zu Beginn einer Zeile erlaubt die Eingabe von History-Befehlen und Systemaufrufen.

Die Verwendung von readline im interaktiven Modus und die automatische Ausgabe von Resultaten machen esh zu einem komfortablen Tischrechner.

Der EFEU-Befehlsinterpreter ist mit C-Bibliotheksfunktionen implementiert, esh ist ein einfaches Programm, das diese Funktionen nutzt. Der Interpreter kann Datenpointer gemeinsam mit C-Funktionen nutzen und mit eigenen Funktionen und Datentypen erweitert werden. Er eignet sich zur Verarbeitung von Konfigurationsdateien und zum Testen von Funktionen.

Falls EFEU für gemeinsam genutzte Bibliotheken kompiliert wurde, kann esh zur Laufzeit erweitert werden. Falls ein dynamisches Linken von Funktionen nicht möglich ist, können die Erweiterungen in einer Kopie von esh.c eingebaut werden.

 

Optionen

Optionen und Argumente nach dem Skriptnamen werden vom Skript selbst interpretiert. Der Interpreter esh selbst akzeptiert die folgenden Optionen und Argumente:

--help[=type]
generiert eine Beschreibung des Kommandos. Der zusätzliche Parameter type bestimmt die Formatierung und die Ausgabe der Beschreibung.
term
Terminalausgabe (default)
raw
Rohformat für efeudoc
man
nroff/troff Sourcen für man
lp
Ausgabe zum Drucker

--version
gibt die Versionsnummer des Kommandos aus.
--info[=entry]
listet verfügbare Informationseinträge des Kommandos auf.
--debug[=mode]
setzt den Protokollmodus für das Kommando. Vergleiche dazu .
--verbose
setzt den Debug-Level auf .info.
-I dir
erweitert den Suchpfad für Skriptfiles um dir. Die Vorgabe für den Suchpfad ist:
"/home/efeu/www/efeu-3.3-1/lib/esh/%S:/home/efeu/www/efeu-3.3-1/lib/esh:/usr/local/lib/esh/%S:/usr/local/lib/esh:/usr/lib/esh/%S:/usr/lib/esh:/lib/esh/%S:/lib/esh".
-D name=val
definiert den Makro name mit Wert value
-c string
liest Befehle vom String string.
file
ist der Name des Skriptfiles.
arg(s)
sind die Skriptparameter.
-E
es wird nur der Präprozessor verwendet, das Skriptfile wird nicht verarbeitet.
 

PRÄPROZESSOR

Der verwendete Präprozessor hat eine ähnliche Syntax wie der C-Präprozessor. Es gibt jedoch einen wesentlichen Unterschied: Der Präprozessor wird nicht zur Vorverarbeitung der gesamten Datei verwendet, sondern ist zeilenweise implementiert und arbeitet direkt mit dem Befehlsinterpreter zusammen. Insbesonders kann durch Verändern von Variablen im äußeren Modus auch die Verarbeitung nachfolgender Präprozessorzeilen beeinflußt werden.

Eine Direktive (Präprozessoranweisung) wird mit einem Gittersymbol # eingeleitet. Danach folgt der Name der Direktive und die zugehörigen Argumente.

Eine Präprozessorzeile, bei der nach dem Startzeichen (#) ein Sonderzeichen folgt, wird ignoriert. Insbesonders gilt das für die Interpreterkennung #!.

 

Einbinden von Dateien

Dateien werden mit der #include Direktive eingebunden. Zunächst werden im Argument der Direktive alle Makros expandiert. Anschließend bleiben drei mögliche Fälle:

#include <file>
Die Suche nach der Datei file erfolgt in den durch die Variable IncPath (Kann mit der Umgebungsvariablen ESHPATH und der Option -I erweitert werden) definierten Verzeichnissen.
#include expr
Zunächst wird expr ausgewertet und in einen String konvertiert. Falls der resultierende String nicht mit < beginnt, wird die Datei zunächst im aktuellen Verzeichnis gesucht und erst anschließend in den durch IncPath definierten Verzeichnis.
#include "file"
Das ist nur ein Spezialfall der zweiten Variante. Die Datei name wird zuerst im aktuellen Verzeichnis gesucht.

In esh ist die folgende Konstruktion erlaubt:

str header = paste("/", "SubDir", "MyHeader");
#include "<" + header + ">"

 

Bedingte Verarbeitung

Eine bedingte Verarbeitung beginnt mit einer der Direktiven #if, #ifdef oder #ifndef und endet mit #endif. Innerhalb der bedingten Verarbeitung können beliebig viele #elif Anweisungen und eine #else Anweisung enthalten sein. Bereiche mit bedingter Verarbeitung können beliebig geschachtelt werden.

Die Direktiven

#ifdef name
#ifndef name
dienen zum Testen, ob der Makro mit Namen name definiert (bzw. nicht definiert) ist. Auf diese Direktiven wird keine Makrosubstitution angewendet.

Die einfachste Form eines bedingten Ausdrucks hat die Form:

#if expr
Zeilen werden verarbeitet, falls expr wahr ist.
#endif

Ein etwas komplexerer Ausdruck könnte etwa so aussehen:

#if expr1
Zeilen werden verarbeitet, falls expr1 wahr ist.
#elif expr2
Zeilen werden verarbeitet, falls expr2 wahr und expr1 falsch ist.
#else
Zeilen werden verarbeitet, falls weder expr1 noch expr2 wahr ist.
#endif

Wie bereits im Abschnitt "Einbinden von Dateien" erwähnt, können in expr beliebige Variablen oder Funktionen, die vor der bedingten Verarbeitung erklärt wurden, verwendet werden.

 

Makros

Makros werden mit der #define Direktive definiert. Es gibt zwei Formen:

#define name repl
definiert einen Makro name der durch repl ersetzt wird.
#define name(arglist) repl
definiert einen Makro name mit Argumenten. Die öffnende Klammer muß unmittelbar nach dem Namen folgen.

Der Name eines Makros muß mit einem Buchstaben oder einem Unterstreichungszeichen beginnen und darf nur Buchstaben, Ziffern oder Unterstreichungszeichen enthalten.

In esh werden Makros nur selten verwendet. In fast allen Fällen sind Variablen und Funktionen die bessere Lösung. Normalerweise werden Makros nur zum Absichern von Headerdateien gegen mehrfache Einbindung verwendet.

Ein Makrodefinition kann mit der #undef Direktive gelöscht werden.

 

VARIABLEN UND KONSTANTEN

Im Gegensatz zu anderen Interpretersprachen müssen in esh Variablen wie in C/C++ vor ihrer Verwendung deklariert werden. Die Deklaration kann an beliebiger Stelle (mit Berücksichtigung der aktuellen Hierarchie der Variablentabellen) im Skript erfolgen. Eine Deklaration liefert als Rückgabewert den Initialisierungswert der deklarierten Variablen.

Zum Beispiel deklariert

int x;
double a, b;
x = (int y = 5);

zunächst die Ganzzahlvariable x und die Gleitkommavariablen a und b. Anschließend wird die Gaanzzahlvariable y mit dem Initialisierungswert 5 eingerichtet und der Rückgabewert (der Wert 5) der Variablen x zugewiesen.

Variablen werden in einer Hierarchie von Variablentabellen gespeichert. An oberster Stelle steht die globale Variablentabelle, an unterster Stelle die lokale oder aktuelle Variablentabelle. Neue Variablen werden in der lokalen Variablentabelle generiert. Die Suche von Variablen erfolgt von unten nach oben. Im äußersten Verarbeitungsmodus stimmt die lokale Variablentabelle mit der globalen Variablentabelle überein.

Zu jedem vordefinierten Datentyp im EFEU-Interpreter gibt es einen zugehörigen Datentyp in C. Der Interpreter kennt keine Pointer, aber manche Datentypen werden durch Pointer auf C-Datentypen repräsentiert.

Der Interpreter unterscheidet zwischen L-Werten und Konstanten. Ein L-Wert ist alles, was auf der linken Seite einer Zuweisung stehen darf. Typische L-Werte sind Variablen. Das Resultat eines Terms oder eines Funktionsaufrufes kann ein L-Wert sein.

 

Ganzzahltypen

Wie in C/C++ gibt es eine Reihe von Ganzzahldatentypen. Die folgende Tabelle listet die Ganzzahltypen und ihre Äquivalente in C auf.


esh TypeC Type

boolint

int8_tint8_t
int16_tint16_t
intint
int32_tint32_t
int64_tint64_t
varintint64_t

uint8_tuint8_t
uint16_tuint16_t
unsignedunsigned int
uint32_tuint32_t
uint64_tuint64_t
varsizeuint64_t

Das Schlüsselwort unsigned ist ein Datentypnamen und keine Typqualifikation wie in C. Die Datentypen varint und varsize unterscheiden sich von int64_t und uint64_t durch eine kompakte, wertabhängige Darstellung in Binärdateien.

Die Syntax von Ganzzahlkonstanten ist wie in C/C++. Die Schlüsselworter true (Ganzzahlwert 0) und false (Ganzzahlwert 1) sind vom Typ bool.

 

Gleitkommatypen

Der EFEU-Befehlsinterpreter verwendet die Gleitkommatypen float und double wie in C/C++. Alle Gleitkommakonstanten sind vom Type double. Die gesamte Arithmetik wird mit double durchgeführt, float wurde nur aus Gründen der Vollständigkeit und für große Datenfelder, wo die Genauigkeit der Werte nicht so wichtig ist, eingerichtet.

 

Zeichen und Zeichenketten

Ein Zeichen in esh ist vom Type char und sein Kodewert wird als vorzeichenlos interpretiert (Wertebereich 0 ... 255). Weiters steht zur Speicherung von Unicodezeichen der Datentyp wchar_t zur verfügung. Der zugehörige C-Datentyp ist int32_t und nicht wchar_t.

Zeichenketten (Strings) werden in esh völlig anders als in C implementiert. Sie werden nicht als Felder vom Type char eingerichtet, sondern haben den Datentyp str, der über einen char Pointer eingerichtet wird. Falls einem String ein Wert zugeordnet wird, wird die gesamte Zeichenkette und nicht die Adresse kopiert. Stringkopien erfolgen immer mit dynamischer Speicherzuweisung und es gibt eine Speicherbereinigung (Garbage-Collection) für Strings und generell für alle Objekte mit dynamisch zugewiesenem Speicherbereich.

Zeichenkonstanten werden von einfachen, Stringkonstanten von doppelten Hochkommas begrenzt. Der Gegenschrägstrich wird wie in C als Fluchtsymbol verwendet.

Für lange Stringkonstanten gibt es das Schlüsselwort string, welches in der folgenden Form verwendet wird:

string !
Inhalt des Strings
!

Unmittelbar nach ! muß ein Zeilenvorschub stehen und ! muss das erste Zeichen der letzten Zeile sein. Eine so definierte Zeichenkonstante enthält immer einen Zeilenvorschub am Ende. Der Gegenschrägstrich wird nur mehr zum Schutz eines ! am Anfang einer Zeile verwendet. Diese Konstruktion kann an beliebiger Stelle innerhalb eines Ausdrucks stehen.

Innerhalb solcher Stringdefinitionen werden Kommentare im C/C++-Style überlesen und Präprozessor-Direktiven interpretiert.

So kann zum Beispiel mit

str s = string !
#include "file"
!;

der Inhalt der Datei file in den String s geladen werden. Aber Achtung: Die Datei file wird vom Präprozessor überarbeitet.

Anmerkung: In EFEU (und damit auch in esh) können Nullstrings (Zeichenpointer auf NULL) wie gewöhnliche Strings verwendet werden. Nullstrings werden jedoch anders als Leerstrings (Zeichenketten, die nur aus der Abschlußnull bestehen) behandelt. Die EFEU-Bibliotheken beinhalten eine Reihe von Hilfsfunktionen zur Handhabung von Zeichenketten mit dynamisch zugewiesenem Speicher. Auch können sie mit Stringkonstanten gemischt werden. Die Speicherverwaltungsfunktionen von EFEU wissen, ob der Speicher eines Strings freigegeben werden kann.

 

Pointertypen

Datentypen, die über Pointer implementiert werden, kopieren bei einer Zuweisung nur die Adresse und nicht die Daten (Zeichenketten sind eine Ausnahme). Aber sie benutzen einen Referenzzähler für die Speicherbereinigung, der beim Kopieren erhöht wird. Alle diese Datentypen sind von der Basisklasse _Ref_ abgeleitet.

Der Datentyp _Ref_ und alle anderen Datentypen mit Pointerrepräsentation (wie str) sind wiederum vom Datentyp _Ptr_ abgeleitet, dieser ist auch der Typ der Konstanten NULL.

Datentypen mit einem Unterstreichungszeichen am Anfang und Ende des Namens sind für die interne Verwendung reserviert. Normalerweise werden keine Variablen dieser Typen deklariert. Aber sie können in Argumentlisten von virtuellen Funktionen auftreten, etwa um zwischen der Konstanten NULL (vom Type _Ptr_) und einer Zeichenkette, die mit NULL initialisiert wurde, zu unterscheiden.

 

Listen

Listen sind geordnete Mengen von Objekten beliebiger Datentypen. Sie haben den Type List_t. Es gibt drei Methoden, um Listen zu erzeugen:
*
Mit dem Gruppierungsoperator: { 3, 5 }. Er kann nicht zu Beginn eines Ausdrucks verwendet werden, da { ... } auch für Blöcke verwendet wird.
*
Mit dem Listenoperator: 3, 5. Beachte die niedrige Priorität des Operators. In Termen muß er geklammert werden. Es können nur Listen mit mindestens zwei Elementen erzeugt werden.
*
Mit dem Bereichsoperator: 3 : 5 : 2. Alle Mitglieder haben den gleichen Datentyp.

Jedes Objekt vom Type List_t hat die folgenden Komponenten:

obj
liefert den ersten Eintrag der Liste oder NULL bei leeren Listen.
next
liefert die Teilliste, die beim zweiten Element startet oder eine leere Liste, falls keine Elemente mehr existieren.

 

Datenfelder

Datenfelder können auf zwei Arten deklariert werden:

type name[dim]
deklariert name als Datenfeld vom Type type.
type[dim] name
deklariert name als Skalar vom Type type[dim].

Im ersten Fall, und falls das Datenfeld mit einer Liste von Werten initialisiert wird, kann dim entfallen und die Größe des Datenfeldes wird durch die Zahl der Elemente der Liste bestimmt. Im zweiten Fall wird implizit ein neuer Datentyp eingerichtet. Falls hier keine Größe oder 0 angegeben wird, handelt es sich um einen Vektor variabler Länge. Die Größe wird dynamisch angepaßt.

Falls mehr als eine Dimension benötigt wird, wird eine Deklaration der Form:

type name[n0][n1]...[nk];
in
type[nk]...[n1] name[n0];
konvertiert, da Datenfelder nur eindimensional sein können, aber es keine Beschränkungen bei der Konstruktion von Vektordatentypen gibt. Es ist klar, dass nur n0 weggelassen werden kann.

Datenfelder werden bei der Verwendung immer in ein Objekt vom Typ EfiVec gepackt. Ein Datenfeld kann in eine Liste konvertiert werden und die Werte eines Datenfeldes können durch Zuweisung einer Liste verändert werden. Falls eine Liste weniger Elemente enthält, als das Datenfeld auf der linken Seite, werden nur die zugehörigen Elemente verändert.

Neben den gewöhnlichen Datenfeldern stehen in EFEU noch die folgenden Datentypen für ein mächtigeres Hantieren mit Daten zur Verfügung:

EDB
ist eine Datenschnittstelle zur Manipulation großer Datenmengen.
TimeSeries
sind dynamische Datenfelder vom Type double mit einem Zeitindex.
mdmat
verwaltet einen Datenwürfel beliebigen Typs und beliebiger Zahl an Dimensionen. Seine Größe wird nur durch den verfügbaren Speicher begrenzt. EFEU enthält eine Reihe von Hilfswerkzeugen zur Manipulation solcher Datenwürfel.
 

Abgeleitete Datentypen

Der einfachste Weg um einen neuen Datentyp einzurichten, ist typedef, z.B:

typedef int myint;
Der neue Datentyp myint wird von int als Basisklasse abgeleitet (Vererbung). Damit kann myint wie int verwendet werden, ist aber nicht nur ein anderer Namen für int wie in C.

Strukturen werden mit der struct Anweisung erzeugt. Die Syntax ist

struct type [: base [ name ]] { Deklarationen }
analog zu C++. Fals base definiert ist, wird type von base abgeleitet. Nur ein Basistype kann angegeben werden. Der EFEU-Interpreter kennt keine Mehrfachvererbung.

Die folgenden zwei Datentypen

struct T1 {
        int a;
        int b;
}

struct T2 : int a {
        int b;
}

haben gleiche Komponenten, aber nur T2 kann stellvertretend für ein int verwendet werden.

Jeder zuvor definierte Datentyp kann bei der Definition einer Datenstruktur verwendet werden. Es gibt aber keine Vorwärtsdeklaration.

Jedes Objekt mit einem Strukturdatentyp kann in eine Liste umgewandelt werden und jede Liste läßt sich in ein Strukturobjekt konvertieren.

Beispiel für eine etwas komplexere Struktur:

struct MyDataType {
        int i;
        double d;
        str s;
        int v[10];
};

Datentypen müssen nicht benannt werden. Eine Variable kann z.B. auch mit einer namenlosen Struktur eingerichtet werden:

struct {
        int i;
        double d;
} data;

Falls eine gleichwertige, benannte Struktur existiert, wird diese Stellvertretend eingesetzt. Sollten mehr als zwei gleichwertige Strukturen existieren, ist undefiniert, welche zum Zug kommt. Zwei Strukturen sind gleichwertig, wenn alle Komponenten gleichen Namen und Datentyp haben.

Der EFEU Interpreter unterstützt Aufzählungstypen. Die Syntax ist:

enum type [: base [ name ]] { Kennungsliste }
mit einer durch Komma getrennten Liste von Kennungen mit optionalen Zuweisungswert. Die Kennungen werden an den Aufzählungstype gebunden. Sie können unmittelbar nach ihrer Deklaration (also bereits für die nächste Kennung innerhalb der Aufzählungsdeklaration) verwendet werden.

Die folgende Anweisung:

enum Color { Red, Green = 5, Blue };
erzeugt einen Aufzählungstype mit Namen Color und den Kennungen Color::Red, Color::Green und Color::Blue. Die zugehörigen Ganzzahlwerte sind 0, 5 und 6. Für jeden Aufzählungstype werden Konverter von/nach int/str eingerichtet. Daher sind die folgenden Zuweisungen alle gültig:

Color c1 = "Red";
Color c2 = 0;
str s = Color::Red;
int n = Color::Red;

Die Funktion enumlist(Typ) liefert eine Liste aller Kennungen des Aufzählungstypes Typ oder eine leere Liste, falls Typ keine Kennungen hat oder kein Aufzählungstype ist. Vergleiche dazu auch den Abschnitt KLASSIFIKATIONEN weiter unten.

 

Konstruktion von Datentypen mit Ausdrücken

Datentypen können auch über eine Konstruktionsfunktion eingerichtet werden. Das Grundprinzip ist einfach: Eine Datenstruktur wird über eine Liste von Ausdrücken definiert. Der Datentyp (und teilweise auch der Variablenname) kann implizit aus dem Ausdruck abgeleitet werden. Ein so abgeleiteter Datentyp ist unbenannt (außer es existiert eine benannte, gleichwertige Datenstruktur).

Die Möglichkeiten dieser Datentypkonstruktionen werden am besten an einem Beispiel demonstriert:

Der folgende Code

construct DATA : int a, double b {
        str date = today();
        a;
        data = {
                double x = 3 * a;
                y = 2 * b;
        };
}

generiert die Datenstruktur

struct DATA {
        str date;
        int a;
        struct {
                double x;
                double y;
        } data;
}

und die Konstruktionsfunktionen

virtual DATA DATA (List_t)
virtual DATA DATA (int a, double b)

Die Variante mit der Liste als Argument wird für jede Struktur eingerichtet. Die spezifisch mit construct eingerichtete Version hat die Parameter int a, double b, wie sie in der ersten Datenzeile angegeben wurde.

Der Aufruf DATA(2, 3.5) generiert ein Objekt vom Typ DATA mit den folgenden Werten:

{"2011-12-04", 2, {6.00000, 7.00000}}

Die allgemeine Syntax ist:

construct Typ [ : varlist ] {
vardef;

}
wobei vardef entweder eine einfache Variable in der Form
[[type] name ] [ = expr ];
oder eine Teilstruktur mit Syntax
name = {
[[type] name ] [ = expr ];
}
ist. Der Datentyp type ist optional und wird aus dem Wert von expr bestimmt. Falls expr eine Strukturkomponente ist, kann auch der Name name entfallen. Umgekehrt kann bei bekannten Datentyp und Variablennamen der Ausdruck expr entfallen.

Die Variablenliste varlist kann auch leer sein. In diesem Fall können bei expr nur globale Variablen genutzt werden.

Diese Form der Datentypkonstruktion wird implizit bei der Aufbereitung und Unmformung von EDB-Datenfiles genutzt.

 

AUSDRÜCKE

Konstanten und Variablen können mit Operatoren zu einfachen Ausdrücken verknüpft werden.

Die nachfolgenden Tabellen zeigen die verfügbaren Operatoren von esh, sie sind absteigend nach ihrer Priorität aufgelistet. Operatoren, die nicht durch eine horizontale Linie voneinander getrennt sind, haben die gleiche Priorität.


Linke Operatoren


::global::name
++pre increment++lvalue
-pre decrement-lvalue
~complement~expr
!not!expr
-unary minus-expr
+unary plus+expr
{list grouping{ expr [, expr ] }
(grouping( expr )
[expression[ expr ]
()cast (type conversion)(type) expr
()lvalue cast(type &) expr

Der "list grouping" Operator generiert eine Liste von Werten. Im Genensatz zu C/C++ ist dieser Operator nicht auf die Initialisierung von Variablen in Deklarationen beschränkt.

Der "expression" Operator liefert einen Ausdruck ohne ihn auszuwerten. Er kann einer Variablen zugewiesen oder einer Funktion für eine spätere Auswertung übergeben werden.

Ein Datentyp, der von runden Klammern eingeschlossen ist, bildet einen Cast-Operator, der eine Umwandlung des nachfolgenden Terms auf den gewünschten Datentyp erzwingt.

Der EFEU-Interpreter erlaubt auch casts von L-Werten. Diese sind für alle Datentypen zulässig, für die es wechselseitige Konvertierungen gibt.

Beispiel: Durch die Anweisungen

str s = "5";
(int &) s++;

wird die Zeichenkette s auf "6" gesetzt.


Rechte Operatoren


++post incrementlvalue++
-post decrementlvalue-
::scope resolutiontype::name
::variable selectionvartab::name
.member selectionexpr.name
[]subscriptingexpr[expr]
()function callexpr(list)

In esh kann auf die Variablentabellen direkt zugegriffen werden und es können eigene Variablentabellen generiert werden. Mithilfe des "scope resolution" Operators kann auf ein Element der Tabelle zugegriffen werden. Dieser Operator kann auf jeden Datentyp angewendet werden, der sich in eine Variablentabelle (Type VarTab) konvertieren läßt.


Arithmetische Operatoren


*multiplyexpr * expr
/divisionexpr / expr
%modulo (remainder)expr % expr

+add (plus)expr + expr
-subtractexpr - expr

<<shift leftexpr << expr
>>shift rightexpr >> expr


Vergleichsoperatoren


<less thanexpr < expr
<=less than or equalexpr <= expr
>greater thanexpr > expr
>=greater than or equalexpr >= expr

==equalexpr == expr
!=not equalexpr != expr


Bitoperatoren


&bitwise ANDexpr & expr

^bitwise exclusive ORexpr ^ expr

|bitwise inclusive ORexpr | expr


Logische Operatoren


&&logical ANDexpr && expr

||logical ORexpr || expr

Der rechte Operand eines logischen Operators wird nur ausgewertet, wenn das Resultat nicht bereits vom linken Operanden determiniert wird. So wird in

false && expr
true || expr
der rechte Operand expr niemals ausgewertet.


Bedingter Ausdruck und Bereichsoperator


? :conditional operatorcond ? expr1 : expr2

: :range operatorstart : end [ : step ]

Der Bereichsoperator liefert eine Liste von Werten, beginnend bei start bis einschließlich end. Je nachdem, ob start kleiner oder größer als end ist, werden die einzelnen Elemente um step erhöht bzw. verkleinert. Für step dürfen nur positive Werte angegeben werden, die Vorgabe ist 1. Der Operator kann überladen werden. Eine falsche Verwendung kann in einer Endlosschleife enden. Es gibt keinen Bereichsoperator in C/C++.


Zuweisungsoperatoren


=simple assignmentlvalue = expr
*=multiply and assignlvalue *= expr
/=divide and assignlvalue /= expr
%=modulo and assignlvalue %= expr
+=add and assignlvalue += expr
-=subtract and assignlvalue -= expr
<<=shift left and assignlvalue <<= expr
>>=shift right and assignlvalue >>= expr
&=AND and assignlvalue &= expr
^=exclusive OR and assignlvalue ^= expr
|=inclusive OR and assignlvalue |= expr

Zuweisungsoperatoren sind rechtsassoziativ.


Listentrenner


,list separatorexpr , expr

Das Komma , wird von esh so wie von python als Listentrenner und nicht als Komma-Operator wie in C/C++ verwendet. Der Ausdruck a, b, ..., n liefert eine Liste mit den Elementen a, b, ..., n.

Falls der Rückgabewert nicht verwendet wird (die übliche Anwendung), gibt es keinen Unterschied im Verhalten vom Komma-Operator und Listentrenner.

 

Schleifen

while (cond) cmd
Definiert eine Schleife. Der Ausdruck cmd wird solange ausgeführt, wie cond logisch wahr ist. Bei cmd handelt es sich entweder um eine einfache Befehlszeile oder einen Block.
do cmd while (cond)
Bei der zweiten Form wird cmd zumindest einmal ausgeführt.
for (a; cond; b) cmd
Zu Beginn der Verarbeitung wird a ausgeführt. Der Ausdruck cmd wird solange ausgeführt, wie cond wahr ist. Nach jedem Schleifendurchlauf wird b ausgeführt.
for (name in list) cmd
Für jedes Element der Liste list wird cmd ausgeführt. Name ist der Name einer temporäre Variablen, die das aktuelle Element der Liste enthält. Anstelle von list kann auch ein einzelnes Objekt stehen, das in eine Liste konvertierbar ist (z.B: ein Datenfeld).

Bei Schleifen kann ein Block vorzeitig mit der break Anweisung verlassen werden. Die continue Anweisung startet einen neuen Zyklus.

 

Bedingungen

if (cond) cmd1
Falls die Bedingung cond wahr ist, wird cmd1 ausgeführt.
if (cond) cmd1 else cmd2
Falls die Bedingung cond wahr ist, wird cmd1 ausgeführt, ansonsten wird cmd2 ausgeführt.
 

Switch-Anweisung

Die Syntax der Switch-Anweisung lautet:

switch (expr)
{
label:
        
cmdlist
label:
        
cmdlist

}
wobei label wahlweise case val oder default sein kann. Der Ausdruck val wird dabei bereits beim Einlesen der Anweisung ausgewertet. Der Wert von expr wird mit den einzelnen Werten der Labels in der Reihenfolge des Auftretens verglichen. Falls der Vergleich wahr liefert, werden alle nachfolgenden Anweisungen bis zum Erreichen eines der Anweisungen break, continue, return oder dem Ende des Switch-Blocks ausgeführt. Falls keiner der Labels mit expr übereinstimmt, werden die Anweisungen nach default (falls vorhanden) ausgeführt.

Im Gegensatz zu C kann jeder Datentyp, für den der Vergleichsoperator == definiert ist, für Switch-Anweisungen verwendet werden. Insbesonders können Zeichenketten und reguläre Ausdrücke in Switch-Anweisungen verwendet werden.

 

Blockbildung

Mithilfe von geschwungenen Klammern lassen sich einzelne Ausdrücke zu einem Block zusammenfassen. Bei der Ausführung eines Blocks werden zwei zusätzliche Variablentabellen aktiviert. Die weniger sichtbare wird bereits beim Lesen der Befehlszeilen, die zweite bei der Ausführung (und bei einer Schleife jedesmal neu) eingerichtet. Jeder Ausdruck nach dem Schlüsselwort static wird bereits beim Lesen der Befehlszeilen ausgewertet. Damit können Variablen in der inneren (oder statischen) Variablentabelle eingerichtet werden. Die Verwendung von static ist aber nicht auf Variablendeklarationen beschränkt.

 

FUNKTIONEN

Eine Funktionsdefinition in esh hat die allgemeine Form
type name ( arglist )
        expr
Normalerweise ist expr ein Block von Ausdrücken, in esh kann aber auch ein einzelner (nicht leerer) Ausdruck stehen. Falls die Funktion keinen Wert zurückliefern soll, ist als Datentyp void zu verwenden.

Die folgenden Funktionsdefinitionen sind gleichwertig:

int f (int x) x + 1;
int f (int x) return x + 1;
inline int f (int x) { return x + 1; }

Im EFEU-Befehlsinterpreter hat das Schlüsselwort inline primär etwas mit Sichtbarkeit zu tun. Eine inline Funktion sieht alle Variablentabellen, die auch in der Zeile mit dem Funktionsaufruf sichtbar waren. Alle Funktionen, die nur aus einem einzelnen Ausdruck bestehen, gelten als inline Funktionen.

Das folgende Beispiel zeigt einen Anwendungsfall für das Schlüsselwort inline:

inline str f (str fmt)
{
        return psub(fmt);
}

{
        str x = "foo";
        f("x = $(x)");
}

Die Funktion psub substituiert Parameter entsprechend einem Formatstring. Falls f nicht inline ist, ist die Variable x für psub unsichtbar und die Substitution $(x) schlägt fehl.

Der Overhead für den Aufruf einer inline-Funktion ist geringer als der einer normale Funktion, allerdings kann es zu Seiteneffekten bezüglich der Sichtbarkeit von Variablen kommen.

Funktionen haben den Datentyp Func und jede Funktion kann auch als Variable (Funktionsname ohne Argumentliste) angesprochen werden. Die Standarddarstellung einer Funktion (z.B: Eingabe des Funktionsnamens im äußersten Modus) ist der Prototype der Funktion.

Wie in C++ können Funktionsargumente Vorgabewerte besitzen. Diese müssen dann beim Aufruf nicht angegeben werden. Die allgemeine Form eines Funktionsarguments ist:

type [ & ] name [ = value ]
Das & zeigt an, dass das Argument ein gültiger L-Wert sein muss. Eine Tilde ... anstelle des Funktionsarguments steht für eine variable Argumentliste. Auf sie kann innerhalb der Funktion unter dem Namen va_list zugegriffen werden.

 

Virtuelle Funktionen

Wie in C++ können Funktionen mit verschiedenen Argumentlisten überladen werden. Überladene Funktionen werden mit dem Schlüsselwort virtual deklariert. Der Datentyp solcher Funktionen ist VirFunc. Jede Funktion kann in eine virtuelle Funktion konvertiert werden.

Eine virtuelle Funktion kann auch in eine gewöhnliche Funktion umgewandelt werden. Dies erfolgt mit einem Prototyp-Cast wie im folgendem Beispiel:

Func f = operator+ (int a, int b);;

Nun kann f zur Addition von zwei Ganzzahlwerten verwendet werden. Beachte die zwei Strichpunkte am Ende der Zuweisung: Der erste gehört zum Prototyp (und unterscheidet ihn von einer Funktionsdefinition), der zweite schließt den Ausdruck ab.

 

Typgebundene Funktionen

Funktionen können auch an einen Datentyp gebunden werden. Sie haben die allgemeine Form:

type btype::name [ & ] ( arglist )
        expr

Falls nach dem Funktionsnamen ein & steht, kann die Funktion nur für L-Werte verwendet werden. Eine gebundene Funktion wird folgend aufgerufen:

obj.name(args)

Dabei ist obj ein Objekt vom Type btype. Der Datentyp einer typgebundenen Funktion ist ObjFunc. Dabei kann es sich sowohl um eine virtuelle, als auch um eine gewöhnliche Funktion handeln.

In gebundenen Funktionen kann mit dem Schlüsselwort this auf das zugehörige Datenobjekt zugegriffen werden.

 

Operatoren

Operatoren werden intern wie Funktionen behandelt. Mit dem Schlüsselwort operator kann ein Operatorname direkt angesprochen werden. Folgende Schreibweisen sind zulässig:

operator op
operator "op"

Bei der ersten Schreibweise muß nach op ein Leerzeichen folgen, vor op kann ein Leerzeichen stehen.

Damit linke Operatoren von rechten unterscheidbar sind, werden sie intern mit dem Zusatz () versehen (z.B: -() für die Negation. Dies ist bei der Definition von Funktionen zu beachten.

Folgende Terme sind gleichwertig:

a + b
operator+ (a, b)

Operatoren sind in der Regel virtuelle Funktionen. Alle Zuweisungsoperatoren sind gebundene, virtuelle Funktionen.

 

Spezielle Funktionen

Funktionen, die den gleichen Namen wie ein zuvor definierter Datentyp haben, definieren Konstruktoren und Konverter. Konverter werden meist indirekt bei Zuweisungen, der Wertübergabe bei Funktionsaufrufen oder durch explizite Typumwandlungen (casts) aufgerufen.

Konstruktoren haben die Form

virtual type type ( arglist )
Die spezielle Form
type type ()
wird Copy-Konstruktor genannt. Ist er definiert, wird er jedesmal beim Kopieren eines Datenelementes aufgerufen.

Im Gegensatz dazu ist

type type (void)
ein gewöhnlicher Konstruktor ohne Argumente.

Konverter haben die Form

tg_type src_type ()
mit einer leeren Argumentliste.

Die Ausgangsdaten werden unter dem Namen this referiert. Falls der Zieldatentype void ist, definiert die Funktion den Destruktor für den Datentyp, der jedesmal aufgerufen wird, wenn ein Objekt diesen Types gelöscht wird.

Copy-Konstruktor und Destruktor können als Spezialfall eines Konverters gesehen werden. Wegen der internen Speicherbereinigung werden sie kaum benötigt. Bei ihrer Definition ist besondere Vorsicht notwendig: Sobald hier ein Objekt dieses Types kopiert wird (z.B. bei der Weitergabe an eine andere Funktion), führt der Aufruf des Konstruktors/Destruktors zu einer endlosen Rekursion.

 

KLASSIFIKATIONEN

Der EFEU-Interpreter bietet vielfältige Möglichkeiten, um Daten zu Klassifizieren. Implizit wird dabei immer auch ein unbenannter Aufzählungstyp eingerichtet.

Es gibt zwei Möglichkeiten, eine Klassifikation einzurichten:

bool Type_t::classification (str name, str def)

generiert zu einem Datentyp eine Komponente name, die bei Abfrage die Klassifikation des Wertes liefert.
virtual Expr_t classification (. &, str def)
virtual Expr_t classification (Expr_t, str def)
liefert einen Ausdruck, der bei Auswertung die Klassifikation liefert. Der Klassifikationausdruck kann wahlweise für einen L-Wert oder einen Ausdruck eingerichtet werden.
Die Klassifikationsdefinition def hat immer den gleichen Aufbau. Zu Beginn steht die Art der Klassifikation. Die verfüggbaren Klassifikationsarten sind Datentypabhängig.

Folgende Klassifikationsarten stehen für alle Datentypen zur Verfügung:

flag
Entscheidung über einen Ausdruck.
switch
Einteilung über eine switch-Anweisung.
test
Einteilung über einen Testausdruck
generic
Kombination der obengenannten Klassifikationsarten.

Die Klassifikationsarten switch, test und flag sind Sonderfälle der generische Klassifikationsart generic. Diese hat den folgenden Aufbau:

generic
generic-group
generic-group:
switch-group
test-group
@eval expr
@other label desc
Der Eintrag @eval dient zur Berechnung von Zwischenergebnissen. @other steht für alle noch nicht abgedeckten Fälle.

test-group:

@test expr
key label desc
generic-group
@end
In expr wird $1 gegen den aktuellen Schlüssel key ersetzt.

switch-group:

@case
key label desc
generic-group
@end

 

BEMERKUNGEN

Dieser Handbucheintrag kann nur eine verkürzte Darstellung des Interpreters liefern. An einem entsprechend umfangreichen Buch wird gearbeitet. Einzelne Kapitel dieses Buches sind bereits in den Dokumentationen zu EFEU enthalten.

Zusätzliche Informationen können mit esh selbst abgerufen werden. Die Option --info liefert eine Schnittstelle zu eingebauten Informationen. Im Interpreter kann auf diese Informationen mit der Funktion Info() zugegriffen werden.

Die Nutzung der eingebauten Informationen hat den Vorteil, dass sie immer vollständig und aktuell sind. Allerdings sind die zugehörigen Erläuterungen (wenn es welche gibt) sehr knapp gehalten.

Falls die Verwendung einer Funktion nicht klar ist: Durch Eingabe des Funktionsnamens wird der Prototype (bei virtuellen Funktionen auch alle Überladungen) angezeigt.

Informationen zu einem Datentyp foo können interaktiv mithilfe von foo.info(["mode"]) abgerufen werden. Diese Anweisung entspricht dem Aufruf von esh mit der Option --info=[mode:]/Type/foo. Die Angabe von mode ist optional.

Einzelne Parameter eines Datentyps können auch über Komponenten abgerufen werden. Eine Liste aller verfügbaren Komponenten kann mit Type_t.var abgerufen werden.

Weitere nützliche Variablen und Funktionen:

global
ist die Tabelle der globalen Variablen (sehr umfangreich).
local
ist die Tabelle der lokalen Variablen. Im äußeren Modus stimmt local mit global überein. str whatis (.)

liefert Informationen zu dem Argument. liefert den Datentyp des Arguments.

void vtabstack (int = 0, IO = iostd)

zeigt die aktuelle Hierarchie der Variablentabellen.
List_t typelist ()
liefert eine Liste aller definierten Datentypen.
void Type_t::info (str mode = NULL)
liefert Informationen zu einem bestimmten Datentyp.

 

UMGEBUNGSVARIABLEN

APPLPATH
definiert zusätzliche Verzeichnisse für Konfigurationsdateien.
LANG
bestimmt die Sprache für Meldungen und Hilfetexte.
ESHPATH
erweitert den Suchpfad für Skriptfiles.
 

COPYRIGHT

Copyright (C) 1994, 2001 Erich Frühstück


 

Index

BEZEICHNUNG
ÜBERSICHT
BESCHREIBUNG
Optionen
PRÄPROZESSOR
Einbinden von Dateien
Bedingte Verarbeitung
Makros
VARIABLEN UND KONSTANTEN
Ganzzahltypen
Gleitkommatypen
Zeichen und Zeichenketten
Pointertypen
Listen
Datenfelder
Abgeleitete Datentypen
Konstruktion von Datentypen mit Ausdrücken
AUSDRÜCKE
Schleifen
Bedingungen
Switch-Anweisung
Blockbildung
FUNKTIONEN
Virtuelle Funktionen
Typgebundene Funktionen
Operatoren
Spezielle Funktionen
KLASSIFIKATIONEN
BEMERKUNGEN
UMGEBUNGSVARIABLEN
COPYRIGHT

This document was created by man2html, using the manual pages.
Time: 09:44:47 GMT, December 18, 2011