next up previous contents index
Next: Die deutsche Library Up: Der Baum der Erkenntnis: Previous: Der Baum der Erkenntnis:

Subsections


Inform


Elementare Syntax

,,Inform sieht seinen Quellcode als eine Auflistung von Dingen, die es sich ansehen soll und die durch Strichpunkte ; getrennt sind.'' (DM$^{4}$, p. 7)

Einfache Datentypen und Literale

Byte
ist die kleinste adressierbare Speichereinheit; es hat 8 Bits und kann ein Zeichen (Charakter) oder eine Zahl zwischen 0 und 255 darstellen. Meistens arbeitet man mit der Zusammenfassung zweier Bytes zu einem Word.
Word
hat 16 Bits (im Z-Code, unter Glulx sind es 4 Bytes - 32 Bits) und wird für die Darstellung von Adressen, Zeigern (Wörterbucheinträgen) oder Zahlen benutzt.
Zahlen
(ein Word) können dezimal (-32768 bis +32767), hexadezimal ($0 bis $FFFF) oder binär ($$0 bis $$1111111111111111) angegeben werden.
Charakter
ist ein einzelner Buchstabe in einfachem Hochkomma: 'a'
String
ist eine Zeichenkette aus bis zu 4000 Zeichen in Anführungszeichen. Eine Tilde ~ wird beim Ausgeben in ein Anführungszeichen, ein Caret ^ in eine Zeilenschaltung umgesetzt. Die Tilde selbst erhält man mit @@126, das Caret (zu deutsch ,,Dächle'') mit @@94, den Backslash \ mit @@92, den Klammeraffen selbst mit @@64. Latin-1-Zeichen sind zulässig, es ist nicht mehr notwendig, deutsche Sonderzeichen besonders zu behandeln. Ausnahme: Im name-Property müssen Umlaute und ß als ae oe ue ss transkribiert werden.
Wörterbucheinträge (dictionary words)
haben neun38 signifikante Zeichen (Achtung: Umlaute und ß zählen als zwei!) und werden in einfache Hochkommas eingeschlossen: 'Haus'. Ein Wörterbucheintrag mit Länge 1 muss von einem Character unterschieden werden durch eine der Schreibweisen "a" (nur im name-property zulässig, veraltet, bitte nicht mehr benutzen) oder besser 'a//'. Intern werden Wörterbucheinträge als Zeiger vom Typ word verarbeitet; der Zeiger verweist auf den Eintrag der Zeichenkette im Wörterbuch. Wörterbucheinträge können auch Leerstellen und Satzzeichen enthalten; solche Einträge werden aber vom Parser bei der Eingabe nicht erkannt, weil er Leerstellen etc. als Trennzeichen verwendet.
Aktionen
erkennt Inform (und der Leser) an zwei vorangestellten Gattern: ##Take oder - in eine Anweisung eingeschlossen - an den umschließenden spitzen Klammern: <take babelfisch>
Bezeichner
müssen mit einem Buchstaben beginnen, können Ziffern und den Unterstrich enthalten und bis zu 32 Zeichen lang sein.
Groß- und Kleinschreibung
sind irrelevant39 mit zwei Ausnahmen:
  1. Bestimmte Schlüsselwörter (die Anweisungen) müssen immer klein geschrieben sein: box break continue do until font (on off) for give if else jump move...to new_line objectloop print print_ret remove return rfalse rtrue spaces string style (roman bold underline reverse fixed) switch default while
  2. Die Namen von Dateien, die mit include eingebunden werden sollen, müssen korrekte Groß-/Kleinschreibung aufweisen, wenn das verwendete Betriebssystem (wie alle vernünftigen Betriebssysteme) Groß-/Kleinschreibung unterscheidet.

Konstante, Variablen und Arrays

Konstante und Variablen sind nicht typisiert, d.h. sie können jeden Datentyp (insbesondere auch den Typ Object) enthalten.


Konstante

werden mit dem Schlüsselwort Constant definiert; bei der Definition kann ein Wert zugewiesen werden:

Constant NAME_DER_KONSTANTE; 
Constant NAME_DER_KONSTANTE = AUSDRUCK;     
Inform kennt bereits die Konstanten false (=0) und true (=1); logisch wahr ist aber auch jeder andere von 0 verschiedene Wert. Außerdem noch nothing (=0) und null (=-1). Null steht für ein undefinierte Aktion oder Property, nothing für ,,kein Objekt''.


Variable

Globale Variable werden wie Konstante definiert, jedoch mit dem Schlüsselwort Global. Lokale Variable können innerhalb von Objekten (als properties, s.u.) und im Kopf von Routinen definiert werden.


Arrays

Mit dem Schlüsselwort array können Felder bestimmter Datentypen eingerichtet werden. Je nach der Elementgröße (Byte oder Word) und nach der verwendeten Adressierung verwendet Inform vier Arraytypen. Bei der Adressierung ist zu unterscheiden zwischen ,,einfachen'' Arrays, die bei Länge N von 0 bis N-1 referenziert werden und Arrays, die zum Speichern von Text gedacht sind und bei Länge N von 1 bis N adressiert werden, wobei im ,,nullten'' Element Längeninformationen gespeichert sind zu Beginn vorbelegt mit der Länge N des Arrays). Allen Arrays ist gemeinsam, dass sie eine bei der Definition festgelegte und später nicht mehr änderbare Länge haben.

Die Definition eines Array erfolgt nach folgendem Muster:

Array NAME_DES_ARRAY PFEIL DATEN;
im Einzelnen:

Array
das Schlüsselwort Array
NAME_DES_ARRAY
ein Bezeichner für den Namen
PFEIL
für jeden der vier Typen unterschiedlich:

->
für ein von 0 bis n-1 adressiertes Array aus Bytes (byte array)
->
für ein von 0 bis n-1 adressiertes Array aus Words (2 Bytes lang, s.o.) (word array)
string
für ein von 1 bis n adressiertes Array aus Bytes (string array)
table
für ein von 1 bis n adressiertes Array aus Words (table array)
DATEN
entweder eine einzelne Zahl, die dann die Länge des zu definierenden Array festlegt;
oder eine Liste von Werten (Ausdrücken), mit denen die einzelnen Elemente des Array vorbelegt werden (die Länge ist dann gleich der Länge dieser Liste)
oder schließlich ein String, der zeichenweise auf die einzelnen Elemente des Array verteilt wird (die Länge ist dann gleich der Länge dieses Strings).
Der Zugriff auf ein Array erfolgt mit NAME_DES_ARRAY->AUSDRUCK für die Typen Byte und String bzw. mit NAME_DES_ARRAY->AUSDRUCK für die Typen Word und Table. Die Auswertung des Ausdrucks muss eine Zahl innerhalb der Arraygrenzen ergeben.

Die name-property eines Objekts (3.2.7) besteht tatsächlich aus einem word array, in dem Referenzen auf Wörterbucheinträge abgelegt sind. Zur Adressierung der name-property als Array muss ein & vorangestellt werden, z.B:

objekt.&name->4='Haus';


Operatoren

Zuweisungsoperator ist das einfache Gleichheitszeichen =.

Inform kennt binäre Operatoren für die Grundrechenarten + - * / und die modulo-Funktion %; außerdem bitweises and & und or |.40 Unäre Operatoren sind das einfache Minuszeichen, das bitweise not~ und die Pre- und Postfixoperatoren ++ und -.41

Inform kennt die normalen binären Vergleichsoperatoren gleich ==, ungleich ~=, größer >, größer oder gleich >=, kleiner <, kleiner oder gleich <=, logisches and && und logisches or ||; außerdem ein unäres logisches not ~~.

Spezielle binäre Vergleichsoperatoren sind:

ofclass
testet das links stehende Objekt auf Zugehörigkeit zur rechts angegebenen Klasse
in
und
notin
testen das links stehende Objekt darauf, ob es child des rechts stehenden Objekts ist oder nicht
provides
testet, ob das links stehende Objekt die rechts angegebene property besitzt;
has
und
hasnt
testen, ob das links angegebene Objekt das rechts angegebene attribute besitzt oder nicht.
Eine weitere Besonderheit ist das Schlüsselwort or, mit dem auf der rechten Seite eines Vergleichsoperators ganze Listen gebildet werden können:

if (messer in tisch or schrank or schublade) ...
Logische Ausdrücke werden immer von links nach rechts abgearbeitet und abgebrochen, sobald das Ergebnis feststeht.

Ausgewertet werden Operatoren in der Reihenfolge ihrer Priorität, ansonsten von links nach rechts:

Der Superclass-Operator ::
Der Punkt . als Auswahloperator in Objekt.property
Die Klammern () im Aufruf einer Routine
.& und .# als Auswahloperatoren eines property array
++ und -, die Prä- und Postfixoperatoren
Das unäre Minuszeichen -
Die Pfeile -> und -> zur Referenzierung eines Arrayelements
Die mathematischen Punktrechnungsoperatoren *, / und %, sowie die bitweisen Operatoren &, | und ~
Die mathematischen Strichrechnungsoperatoren + und -
or für die Auflistung logischer Alternativen
Alle oben beschriebenen logischen Operatoren und Vergleichsoperatoren mit Ausnahme von
&&, || und ~~
Der Zuweisungsoperator =
Das Komma , als Reihungsoperator für verschiedene aufeinanderfolgende Zuweisungen.


Objekte und Klassen

Alles, was der Spieler zu sehen bekommt ist ein Objekt: Räume, Gegenstände, Lebewesen - auch die Spielerfigur selbst.

Klassen sind vordefinierte Muster für mehrere Objekte, die dann die vordefinierten Eigenschaften ,,ihrer'' Klasse erben.

Alle Objekte, die sich in einem Raum befinden, sind zusammen mit dem Raum selbst Knoten in einem Baum, dem object tree. Die Anfangsposition der Objekte in diesem Baum wird normalerweise bei der Definition der Objekte festgelegt; sie kann sich im Spiel dynamisch verändern. Ein Objekt ohne Elternobjekt (parent) ist entweder ein Raum oder gerade nicht im Spiel.

Definition von Objekten und Klassen

Ein Objektdefinition enthält bzw. kann enthalten:

Die mit class, with, private und has eingeleiteten Abschnitte können in beliebiger Folge stehen, sie werden dann durch Kommata getrennt.

Eine Klassendefinition wird begonnen mit dem Schlüsselwort Class gefolgt vom Namen der Klasse und optional einer Zahl in runden Klammern. Diese Zahl gibt an, wieviele Instanzen der Klasse später dynamisch erzeugt werden können. Danach können - wiederum in beliebiger Reihenfolge - mit class, with, private und has eingeleitete Abschnitte folgen.

Die Funktion metaclass() liefert die Klasse eines Objekts zurück.

Erbschaften und Erbfolge

Die mit with und private definierten Eigenschaften sind Variablen, die samt ihren in der Klassendefinition definierten Werten vererbt werden. Auf sie kann jederzeit zugegriffen werden, indem die Bezeichner des Objekts und des Properties mit einem Punkt verknüpft werden: OBJEKTNAME.PROPERTY . Statt des Objektnamens kann innerhalb der Objektdefinition selbst auch self verwendet werden. Für Code in Klassendefinitionen ist das unumgänglich, weil die Klasse ja nicht weiß, wie ihre Instanz später heißen wird.

Im Normalfall handelt es sich bei diesen Eigenschaften um Routinen. Sie können auch mit Parametern aufgerufen werden: x = Topf.Kochen(Apfel); bedeutet, dass man an das Objekt Topf eine in dessen Property Kochen definierte Nachricht mit dem Inhalt Apfel schickt und den Rückgabewert in der Variablen x ablegt (unterstellt, wir hätten eine sinnvolle Property Kochen für den Topf definiert, könnte das dann etwa ein Objekt Kompott sein).

Werden vordefinierte Eigenschaften der Klasse in der Objektdefinition nochmals mit Werten belegt, so werden die in der Klasse vordefinierten Eigenschaften überschrieben, wenn nicht vor ihrer ersten Verwendung die Eigenschaft als additiv deklariert wurde:

Property additive NAME_DER_NEUEN_PROPERTY
Neue Common properties lassen sich auf dieselbe Weise definieren:

Property NAME_DER_NEUEN_PROPERTY;
Bei additiven Properties - in der Regel sind es Routinen - wird zuerst der zuletzt definierte Teil ausgeführt. Gibt dieser false zurück, wird in der ,,Ahnenreihe'' eine Klasse weiter zurückgegangen, solange bis true zurückgegeben wird oder die oberste Klasse erreicht ist. Additiv sind die vordefinierten common Properties after, before, describe, each_turn, life, name, time_out.

Attribute werden immer additiv vererbt. Dies führt bei male, female, neuter zu einem kleinen Problem; siehe 3.2.6.

An den Code der überschriebenen Properties der Superklasse kommt man mit dem Superclass-Operator :: heran: Sei z.B. Dampftopf von der Klasse Topf hergeleitet, habe aber die Property Kochen überschrieben, so kann der Dampftopf mit

Dampftopf.Topf::Kochen(Apfel)
den Apfel genauso kochen wie jeder andere Topf auch. Zumeist wird der Superclass-Operator aber verwendet werden, um innerhalb der Property, die man gerade überschreibt, den ursprünglichen Code der Klasse aufrufen zu können.


Traversieren des object tree

Zur Ermittlung der Umgebung eines Objekts dienen die Funktionen

parent()
liefert das im Baum übergeordnete Objekt (oder nothing, wenn es keines gibt)
children()
liefert die Zahl der nachgeordneten Elemente (kann auch 0 sein)
child()
liefert das erste nachgeordnete Objekt (oder nothing),
sibling()
liefert das nächste gleichgeordnete Objekt (oder nothing),
IndirectlyContains(WALD,BLATT)
prüft, ob WALD im object tree irgendwo oberhalb von BLATT steht; der Rückgabewert ist kein Objekt, sondern vom Typ boolean.

Manipulieren des object tree

Verändert wird der Baum durch die Befehle

move
MEIN_OBJEKT to NEUER_PARENT; setzt ein Objekt samt allen seinen Kindern an eine neue Stelle.
remove
MEIN_OBJEKT; nimmt das Objekt samt allen seinen Kindern aus dem Spiel; löscht es aber nicht, es kann jederzeit mit move ... to wieder eingesetzt werden.

Nachrichten an verschiedene Klassen und dynamische Erzeugung von Objekten

OBJEKT.PROPERTY(AUSDRUCK1,AUSDRUCK2,...);
ist der Aufruf einer in einer property eines Objekts enthaltenen Routine,
ROUTINE.call(AUSDRUCK1,AUSDRUCK2,...);
ist in gleicher Weise der Aufruf einer selbständigen Routine.
STRING.print();
gibt den String aus und
STRING.print_to_array(NAME_DES_ARRAYS);
kopiert ihn in einen Array.
KLASSE.remaining()
gibt an, wieviele Instanzen einer Klasse noch mit
KLASSE.create()
dynamisch ins Leben gerufen werden können. Damit das überhaupt geht, muss bei der Klassendefinition eine Zahl größer 0 angegeben worden sein.
KLASSE.destroy(OBJEKT)
löscht das Objekt und gibt den Platz wieder frei; nur mit create erzeugte Objekte können gelöscht werden!
KLASSE.recreate(OBJEKT)
setzt ein Objekt auf seine Anfangswerte zurück.
KLASSE.copy(OBJEKT1,OBJEKT2)
kopiert alle Eigenschaften der Klasse von Objekt 1 nach Objekt 2.


Schleifen und andere Konstrukte


if und else

if (BEDINGUNG) 
     ANWEISUNGSBLOCK1 
else 
     ANWEISUNGSBLOCK2
Ist die Bedingung true, wird ANWEISUNGSBLOCK1 ausgeführt, sonst ANWEISUNGSBLOCK2. Der else-Zweig ist optional, er bezieht sich immer auf das letzte if. (Abweichende Konstruktionen sind durch 'Blocken' natürlich möglich.)

Ein ANWEISUNGSBLOCK besteht entweder aus einer einzelnen, mit Semikolon terminierten Anweisung oder aus mehreren { in geschweifte Klammern eingeschlossenen } Anweisungen. Die Bedingung muss immer in runden Klammern stehen!


switch

switch (AUSDRUCK) 
 
    KONSTANTE1: ANWEISUNGEN ... ; 
    KONSTANTE2, KONSTANTE3: ANWEISUNGEN ... ; 
    KONSTANTE4 TO KONSTANTE5: ANWEISUNGEN ...; 
    ... 
    default: ANWEISUNGEN ... ; 
}
Der Ausdruck wird ausgewertet, entspricht sein Wert einer der Konstanten, wird der zugehörige Anweisungszweig ausgeführt; sonst der default-Zweig. Der default-Zweig ist optional. Der auszuwertende Ausdruck muss in runden Klammern stehen. Die Anweisungen der einzelnen Zweige müssen nicht 'geblockt' werden.


Schleifen

Eine Schleife bewirkt die wiederholte Ausführung eines Befehls oder einer Befehlsfolge. Die Schleifenkonstrukte folgen wie if und switch ebenfalls der C-Syntax (mit Ausnahme des Inform-spezifischen objectloop). Jede Schleife kann mit dem Befehl break; unmittelbar verlassen werden. Der Befehl continue; springt unmittelbar an den Schleifenanfang und beginnt mit dem nächsten Durchlauf. Die Schleifenbedingung steht immer in runden Klammern, der Anweisungsblock ist auch hier entweder eine einzelne, mit Semikolon abgeschlossene Anweisung oder eine in geschweifte Klammern eingeschlossene Folge solcher Anweisungen.


while

definiert eine Schleife mit Anfangsbedingung, die ausgeführt wird, solange die Auswertung der Bedingung true ergibt:

while(AUSDRUCK) ANWEISUNGSBLOCK


do ... until

definiert eine Schleife mit Endbedingung, die ausgeführt wird, bis die Auswertung der Bedingung true ergibt:

do ANWEISUNGSBLOCK until (AUSDRUCK);


for

definiert eine Zählschleife (d.h. eine Schleife, bei der die Anzahl der Wiederholungen durch eine Zählvariable gesteuert wird) nach dem von C bekannten Muster (Vorsicht, Doppelpunkt statt Strichpunkt!):

for (INITIALISIERUNG : BEDINGUNG : VERÄNDERUNG) ANWEISUNGSBLOCK
INITIALISIERUNG
enthält üblicherweise einen bei Beginn einmalig auszuführenden Zuweisungsbefehl an eine Variable (Zählvariable), die zur Steuerung der Schleife dient, z.B. i=0
BEDINGUNG
Die for-Schleife wird ausgeführt, solange die Auswertung der Bedingung true ergibt. Beispiel: i<= 100
VERÄNDERUNG
Eine Anweisung, mit der die Variable für jeden Durchlauf verändert wird (wobei man darauf achten sollte, dass auf diese Weise irgendwann die Abbruchbedingung erfüllt wird ;-). Beispiel: i++


objectloop

definiert eine Schleife über alle im Spiel definierten Objekte oder über eine Auswahl dieser Objekte:

objectloop (SELEKTIONSAUSDRUCK) ANWEISUNGSBLOCK
Besteht der SELEKTIONSAUSDRUCK einfach nur aus dem Namen einer Variable, so wird der Anweisungsblock für jedes Objekt im Spiel einmal ausgeführt, wobei im Anweisungsblock mit eben dieser Variable auf das aktuelle Objekt zugegriffen werden kann.

Der Selektionsausdruck kann auch bestehen aus einer Bedingung mit einem Variablennamen auf der linken Seite. Dann wird der Anweisungsblock für jedes Objekt, das diese Bedingung erfüllt, einmal ausgeführt; wiederum referenziert der Variablenname im Anweisungsblock das gerade aktuelle Objekt.

Beachte: Eine Konstruktion, die auf einer Bedingung mit in aufbaut und die so gefundenen Objekte bewegt, sägt den Ast ab, auf dem sie sitzt. Statt:

objectloop (x in player) remove x; ! SO NICHT!
schreibt man also:

while (child(player)) remove child(player);

Anweisungen, Routinen und Funktionen


Anweisungen


Zuweisung mit =

Inform verwendet das einfache Gleichheitszeichen für Zuweisungen, das doppelte == ist ein Vergleichsoperator. Der Compiler warnt bei möglichen Verwechslungen, aber man sollte sich nicht darauf verlassen! Mehrfache Zuweisungen sind möglich:

VARIABLE1 = VARIABLE2 = AUSDRUCK;


Anweisungsfolgen

Jede Anweisung muss mit einem Semikolon ; abgeschlossen werden. Ausnahme: Die Definition einer Property eines Objekts wird mit einem Komma abgeschlossen; das letzte Semikolon vor diesem Komma entfällt. Umgekehrt wird die gesamte Objektdefinition mit einem Semikolon beendet, vor diesem abschließenden Semikolon braucht es kein Komma.

Das Komma , ist eine Möglichkeit, eine Folge aus mehreren Anweisungen zu schreiben: ANWEISUNG1, ANWEISUNG2; gilt syntaktisch als nur eine Anweisung.


Anweisungsblock

Ein Anweisungsblock besteht entweder aus einer einzelnen Anweisung oder aus mehreren aufeinanderfolgenden Anweisungen, die in geschweifte Klammern {} eingeschlossen sind.


Rückgabewert

Eine Anweisung hat einen Rückgabewert; bei Zuweisungen ist das das Ergebnis.

Der Rückgabewert von Routinen kann mit der Anweisung return AUSDRUCK; explizit angegeben werden. Beachte: Bei Ausführung von return erfolgt auch der Rücksprung aus der Routine.

return; ohne Zusatz bedeutet immer return true; und sollte der besseren Lesbarkeit wegen so oder abgekürzt als rtrue; geschrieben werden. Das Gegenstück dazu ist der Befehl return false; abgekürzt rfalse;

Wird der Rückgabewert nicht explizit gesetzt, haben selbständig definierte Routinen immer true; unselbständige (als property eines Objekts definierte) Routinen immer false als Rückgabewert.


Routinen (Unterprogramme)

sind Anweisungsfolgen mit bis zu 15 (oder ohne) lokalen Variablen und einem Namen. Sie werden in eckige Klammern [ ] eingeschlossen. Im einzelnen besteht eine Routinendefinition aus folgenden Elementen:

[ NAME VARIABLENLISTE; ANWEISUNGEN ]
[
einer eckigen Klammer zu Beginn
NAME
Nur bei selbständig, d.h. außerhalb eines Objekts definierten Routinen ist der erste nach der Klammer folgende Bezeichner der Name der Routine (unter dem sie auch wieder aufgerufen werden kann, s.u.) . Besondere Regeln gelten für Aktionsroutinen: Für jede (vom Benutzer neu definierte) Aktion, z.B. ##MachIrgendwas muss eine (selbständig definierte) Routine mit dem Namen der Aktion und einem angehängten Sub (hier also: MachIrgendwasSub ) definiert sein.

Ist eine Routine unselbständig, d.h. als property eines Objektes definiert, ist kein Name anzugeben, die Routine erhält automatisch den Namen der property und ist mit der Nachricht OBJEKT.PROPERTY aufrufbar.

VARIABLENLISTE
optional, Bezeichner für bis zu 15 lokale Variable, die entweder mit 0 vorbelegt sind oder beim Aufruf als Parameter übergeben werden.
;
ein Semikolon schließt den Kopf der Routine ab (auch wenn weder ein Name noch lokale Variable angegeben sind, das Semikolon muss geschrieben werden!) und es folgen die
ANWEISUNGEN
in beliebiger Anzahl und Länge, gefolgt von einer abschließenden
]
eckigen Klammer am Ende. (Semikolon oder Komma danach nicht vergessen.)
Wird der Rückgabewert nicht explizit gesetzt, haben selbständig definierte Routinen immer true; unselbständige (als property eines Objekts definierte) Routinen immer false als Rückgabewert. (Man kann das gar nicht oft genug sagen!)


Aufruf von Routinen und Funktionen

Routinen werden aufgerufen mit ihrem Namen gefolgt von einer Parameterliste in runden Klammern. Die Parameterliste kann leer sein oder bis zu sieben durch Kommata zu trennende Argumente enthalten. Die Argumente werden der Reihenfolge nach auf die lokalen Variablen der Routinendefinition abgebildet. Rekursion ist zulässig. Der Aufruf kann als Funktion innerhalb einer Zuweisung erfolgen, muss dies aber nicht. Beispiele:

x = ackermann (y, z); ! (Vom Gebrauch wird abgeraten!)42

streik.posten();    


Dispatcherroutinen

Ein besonderes Format gilt für die Routinen in den properties after, before, life, orders, react_after und react_before. Diese Routinen legen fest, wie ein Objekt sich zu verschiedenen Handlungen (Actions) des Spielers verhält, die mit ihm oder in seiner Nähe stattfinden.

Sie werden gewissermaßen so geschrieben, als wäre ein switch(Action) vorangestellt:

[OPTIONALE LISTE LOKALER VARIABLER; 
  
    AKTION1: ANWEISUNGEN ... ; 
    AKTION2, AKTION3: ANWEISUNGEN ...; 
    ... 
    default: ANWEISUNGEN ... ; 
],
Auch hier müssen die Anweisungen in den einzelnen Zweigen nicht 'geblockt' werden. Die Aktionen werden ohne einleitendes ## geschrieben.


Der richtige Ausdruck


print und print_ret

Der grundlegende Ausgabebefehl ist print gefolgt von einer durch Kommata getrennten Liste aus Zeichenketten und/oder Objekten oder Ausdrücken mit vorangestellter Regel. Die Regel - in runden Klammern vor das Objekt oder den Ausdruck gestellt - legt fest, ob z.B. die Adresse eines ihr folgenden Objekts oder aber sein Name im Nominativ Singular mit vorangestelltem Artikel ausgegeben werden soll:

print (GDer) Objektname, " hat als Objekt intern die Nummer ", Objektname; 
Für die häufig benötigte Folge print ... ; new_line; return true; gibt es die abgekürzte Schreibweise print_ret ... ; oder - noch kürzer und wenn die zu druckenden Daten mit einem String anfangen - "..."; d.h. einfach der zu Beginn stehende String, optional wie print gefolgt von der Liste der weiter zu druckenden Daten. Die meisten auf den ersten Blick unmotiviert herumstehenden Zeichenketten eines Inform-Programms sind bei näherem Hinsehen solcherart abgekürzte print_ret-Befehle.

Im auszugebenden String haben zwei Zeichen besondere Bedeutung: das Caret ^ wird als Zeilenumbruch und die Tilde ~ als Anführungszeichen ausgegeben.

(allgemeine) Regeln für den Ausdruck

(number) AUSDRUCK
Das Ergebnis des Ausdrucks als ausgeschriebene Zahl
(char) AUSDRUCK
Das Ergebnis des Ausdrucks als Zeichen
(string) ADRESSE
Der String an dieser Adresse
(address) ADRESSE
Der Wörterbucheintrag mit dieser Adresse
(name) OBJEKT
Der short_name des Objekts
(ROUTINEAUSDRUCK
Das Ergebnis der mit Ausdruck als Parameter aufgerufenen Routine. Nicht verwechseln mit ROUTINE(AUSDRUCK), das zwar den selben Text, aber obendrein noch den Rückgabewert der Routine (meist 1, entsprechend true) ausgibt.
Die deutsche Library enthält eine ganze Reihe solcher Regeln für die grammatisch wohlgeformte Ausgabe der Namen von Objekten. Dazu später 3.2.5.

sonstige Ausgabebefehle

new_line;
schaltet eine neue Zeile
spaces AUSDRUCK;
gibt soviele Leerzeichen aus, wie der Ausdruck angibt
box
gefolgt von einem oder mehreren Strings erzeugt eine gesonderte Ausgabe dieser Strings (zentriert in einem invertierten Fenster über dem normalen Text )
font off;
schaltet vom normalen Ausgabefont auf einen nicht proportionalen Font um
font on;
schaltet die (normale) Proportionalschrift wieder ein (falls vom Interpreter unterstützt!)
style bold;
schaltet auf Fettschrift
style underline;
schaltet Unterstreichung ein
style reverse;
schaltet invertiertes Video ein
style roman;
ist normale Schrift ohne Auszeichnung.

Beachte: Alle style-Befehle werden von Glulx nicht unterstützt.

Miscellen

string N ''EIN_HÄUFIG_GEBRAUCHTER_STRING''
Mit Werten von 0 bis 31 für N kann so eines von 32 Stringmakros belegt werden. (Glulx: 64)
@00 ... @31
gibt eine solches Makro innerhalb eines Strings wieder aus.
inversion;
gibt die Versionsnummer des Compilers aus.
Release AUSDRUCK;
definiert die 'laufende Nummer' des Spiels (üblicherweise 1 für die erste veröffentlichte Fassung etc.)
Serial "DATUM";
definiert die Seriennummer; Datum ist eine sechsstellige Zahl, default der Tag der letzten Änderung der Quelldatei.
Statusline score;
(Voreinstellung) und
Statusline time;
ändern das Aussehen der Statuszeile.
quit;
Beendet das Programm. Sofort.
.MARKE;
Ein Bezeichner mit vorangestelltem Punkt markiert eine Stelle des Programms als Ziel für (geflüstert) duweißtschonwas. Die Marke ist lokal zu der Routine, in der sie steht.
jump MARKE;
(noch leiser geflüstert:) duweißtschonwas ...43
save MARKE;
Speichert den kompletten Programmstatus und springt dann zur Marke, wenn
restore MARKE;
erfolgreich ausgeführt wird. Im Normalfall braucht man das nicht, weil das Abspeichern und Laden von Spielständen von der Library erledigt wird, der Spieler muss nur die Aktionen ##Save und ##Restore auslösen.
read TEXTPUFFER N;
und
read TEXTPUFFER PARSERPUFFER;
lesen die Eingabe zeichenweise in den TEXTPUFFER, einen byte array der Länge N (TEXTPUFFER->0 enthält N; TEXTPUFFER->1 die Zahl der eingelesenen Zeichen, die ab TEXTPUFFER->2 enthalten sind); die zweite Variante trennt den eingelesenen Text bereits in Wörterbucheinträge; PARSERPUFFER->1 enthält die Zahl der gelesenen Worte, deren Wörterbucheinträge (oder 0, wenn das Wort nicht bekannt war) stehen in PARSERPUFFER->3, PARSERPUFFER->7, ... , PARSERPUFFER->2*i-1 ...


Compileranweisungen

message ''TEXT_DER_NACHRICHT''
gibt während des Übersetzens eine Nachricht aus
System_file;
gibt für diese Datei keine Compilerwarnungen aus und ermöglicht das ,,Ausblenden'' einzelner Unterprogrammdefinitionen einer Bibliotheksdatei mit dem Befehl:
Replace NAME_EINER_BIBLIOTHEKSROUTINE;
Diese Direktive muss vor dem Einbinden der Bibliothek angegeben werden und bewirkt, dass eben diese Routine der Bibliothek nicht mit eingebunden wird, so dass sie nachher noch mit eigenem Code definiert werden kann.
Ifdef BEZEICHNER; Ifndef BEZEICHNER; Iftrue AUSDRUCK; Iffalse AUSDRUCK;
beginnen einen Abschnitt, der nur dann kompiliert werden soll, wenn die Bedingung zutrifft;
Ifnot;
(optional) beendet diesen Abschnitt und beginnt einen alternativen Abschnitt, der nur dann kompiliert wird, wenn die vorherige Bedingung nicht zutrifft;
Endif;
beendet die bedingte Kompilierung.
Default BEZEICHNER AUSDRUCK;
definiert eine Konstante mit dem Namen Bezeichner (und optional mit dem Wert des Ausdrucks), aber nur dann, wenn es sie nicht schon gibt.
Stub BEZEICHNER ANZAHL;
definiert in ähnlicher Weise einen Unterprogrammkopf mit der in Anzahl angegebenen Zahl lokaler Variabler (maximal 3), falls es eine Routine mit diesem Namen noch nicht gibt.
Include "QUELLDATEI";
bindet eine Datei aus dem Bibliotheksverzeichnis ein; die Endungen .h und .inf können weggelassen werden.
Include ">QUELLDATEI";
bindet eine Datei aus dem aktuellen Verzeichnis ein.
Link "MODULDATEI";
bindet ein vorkompiliertes Modul ein (das hat schon lange keiner mehr gemacht; es diente ursprünglich zur Zeitersparnis bei der Kompilierung langer Spiele, aber heute sind die Computer so schnell, dass es den Aufwand nicht lohnt).
Abbreviate "STRING1" "STRING2" ...;
gibt bis zu 64 Strings an, die durch Abkürzungen ersetzt werden sollen. Benötigt Compilerschalter -e. Die besten Abkürzungen lassen sich mit -u ermitteln.


ICL-Befehle

Der Compiler wird normalerweise nach folgendem Muster aufgerufen:

inform ICL_BEFEHLE EINGABEDATEI AUSGABEDATEI
Die Angabe einer Ausgabedatei ist optional. ICL steht für Inform Command Language und umfasst folgende Möglichkeiten:

-SWITCHES
Eine mit einem einfachen Strich eingeleitete Folge von Compilerschaltern (siehe nächsten Abschnitt)
(DATEINAME)
In runden Klammern der Name einer Datei, die ihrerseits ICL-Kommandos (eines pro Zeile) enthält
compile EINGABEDATEI AUSGABEDATEI
Die Anweisung, eine weitere Datei zu kompilieren (Ausgabedatei optional).
+include_path=VERZEICHNIS,VERZEICHNIS...
Die Anweisung, Bibliotheksdateien aus einem bestimmten Verzeichnis zu laden
+module_path=VERZEICHNIS,VERZEICHNIS...
Die Anweisung, vorkompilierte Module aus einem bestimmten Verzeichnis zu laden.
In gleicher Weise können der source_path für Quelldateien, der code_path (für das Kompilat) und auch der icl_path für weitere Kommandodateien angegeben werden.

+language_name=german
damit die deutschen Varianten der Bibliotheksdateien geladen werden
In gleicher Weise können der transcript_name für die mit -t erzeugte Textdatei und der debugging_name für die mit -k zu schreibende Debuggerinformation verändert werden.

$large, $huge und $small sind drei verschiedene Speichermodelle je nach der Größe des zu erstellenden Spiels; $large ist voreingestellt. Angeblich soll mit $listmehr über die Details des jeweiligen Speichermodells zu erfahren sein, aber meine Version des Compilers reagiert nicht darauf. So kann ich auch nicht mit $NAME=WERT einzelne dieser Einstellungen ändern.


Compilerschalter

Es gibt zwei Arten von Compilerschaltern: solche, die eingeschaltet (-a) oder mit der Tilde ausgeschaltet (-~a) werden (markiert mit ein/aus) und solche, an die eine Zahl angehängt werden muss (markiert mit *). Die Schalter werden ohne Punkt und Komma einfach hintereinandergesetzt, z.B. bedeutet -a~bcd2, dass a und c eingeschaltet, b ausgeschaltet und d auf 2 gesetzt wird. Innerhalb der zu übersetzenden Datei können ebenfalls Compilerschalter mit dem Befehl switches LISTE_VERSCHIEDENER_SCHALTER; gesetzt werden.

-d*
(Für englische Schreibmaschinisten) automatisches Eliminieren doppelter Leerzeichen: 0 - niemals, 1 - nach Punkten, 2 - nach Punkten, Frage- und Ausrufungszeichen
-e ein/aus
Deklarierte Abkürzungen (abbreviations) benutzen
-g*
Trace für Unterprogrammaufrufe: 0 - keine, 1 - außerhalb von System files, 2 - alle
-i ein/aus
In der Datei gesetzte Switches ignorieren
-k ein/aus
Ausgabe der Informationen für das Debuggen mit Infix; schaltet auch -D automatisch ein.
-r ein/aus
Der gesamte Text des Spiels wird in eine Textdatei geschrieben
-v*
3 bis 8 um Spieldateien im Format .z3 bis .z8 zu erzeugen (default 5)
-C*
0 bis 9 für den Zeichensatz der Quelldatei; 0 bedeutet ASCII, 1 bis 9 bedeuten ISO 8859-1 bis -9 (default 1)
-D ein/aus
die Debugging-Verben werden mit übersetzt
-F*
0 or 1 temporäre Dateien werden angelegt, um Speicherplatz zu sparen
-G ein/aus
(nur beim Biplattform-Compiler): übersetzt nicht ins Z-Code-Format, sondern ins Glulx-Format. Das wird hier nicht weiter beschrieben.
-M ein/aus
als Modul kompilierten
-S ein/aus
Strenge Fehlerprüfung zur Laufzeit (schließt automatisch -D mit ein)
-U ein/aus
vorkompilierte Bibliotheksmodule einbinden
-X ein/aus
den Infix Debugger einbinden
-a ein/aus
Trace der Assemblerbefehle (ohne Hexdump)
-c ein/aus
ausführlichere Fehlermeldungen
-f ein/aus
Ermittelt die tatsächliche Einsparung bei der Verwendung von Abkürzungen
-h* ein/1/2
Hilfe zur Compilerbenutzung
-j ein/aus
Konstruierte Objekte auflisten
-l ein/aus
Alle übersetzten Anweisungen auflisten
-m ein/aus
Speicherbenutzung angeben
-n ein/aus
Nummern der properties, attributes und actions angeben
-o ein/aus
Offsetadressen ausgeben
-p ein/aus
Compilerfortschritt prozentual anzeigen
-q ein/aus
Obsolete Befehle nicht monieren
-s ein/aus
Statistik ausgeben
-t ein/aus
Trace der Assemblerbefehle (mit Hexdump)
-u ein/aus
mögliche Abkürzungen ermitteln (kann länger dauern)
-w ein/aus
Warnungen aus
-x ein/aus
Je ein Gatter # für 100 übersetzte Zeilen ausgeben
-y ein/aus
Trace der Linkerinformationen
-z ein/aus
Speicheraufteilung der Z-Maschine angeben
-E*
Fehlermeldungen im Stil von Acorn (0), Microsoft (1) oder Mac (2)



Footnotes

... neun38
Auch in Glulx sind es - aus Kompatibilitätsgründen mit der ,,alten'' Inform-Library - nicht mehr als neun.
... irrelevant39
Ich verwende an vielen Stellen CamelCase, weil es die Lesbarkeit verbessert.
... COLOR="#0000ff">|.40
Bitweise heißt, dass jedes einzelne Bit zweier (bei and und or) bzw. eines (bei not) Wertes mit dem entsprechenden Operator verknüpft wird: Sei x=$$0010110; dann ergibt ~x den Wert $$1101001. Anders das logische not: ~~x ergibt den Wert false (jeder von 0 verschiedene Wert ist true).
... COLOR="#0000ff">-.41
Die Pre- und Postfixoperatoren addieren (++) bzw. subtrahieren (-) jeweils 1 zum/vom Wert einer Variablen und weisen der Variablen das Ergebnis wieder zu. (Kürzer: Sie inkrementieren bzw. dekrementieren die Variable um den Wert 1.) Dies geschieht entweder vor der Auswertung des gesamten Ausdrucks (wenn der Operator als Prefix vor der Variablen steht) oder danach (wenn er als Postfix nach der Variablen steht).

x=i++; weist erst x den Wert von i zu und erhöht danach i um 1. y=-i; vermindert zuerst i um 1 und weist danach y den neuen Wert zu.

...(Vom Gebrauch wird abgeraten!)42
Die Ackermann-Funktion (in Inform nicht vordefiniert!) ist ein Beispiel für eine zweifach rekursive Funktion. Ihr bekanntester ,,Seiteneffekt'' ist der enorme Verbrauch an Systemressourcen für vergleichsweise kleine Werte von x und y.
...43
Hier geht es um die Dunkle Seite der Programmierkunst: Die Marke ist ein Sprungziel, zu dem mit dem Befehl jump gesprungen werden kann. Die Verwendung von Sprungbefehlen führt in etwa 1% der Fälle zu eleganten Lösungen und in den anderen 99% zu Komplikationen in der Form von schwer lesbarem Code bis hin zu völliger Verwirrung. Wenn du also nicht wußtest, was das Flüstern sollte, bist du jetzt gewarnt.

next up previous contents index
Next: Die deutsche Library Up: Der Baum der Erkenntnis: Previous: Der Baum der Erkenntnis:
Frank Borger
2003-05-02