Kurzbeschreibung der Aufgabenmodellierungssprache Tombola

Werbung
Kurzbeschreibung der
Aufgabenmodellierungssprache Tombola
Holger Uhr
26. September 2003
Inhaltsverzeichnis
1 Einleitung
1
2 Tokens
1
3 Produktionsregeln
3.1 Die Startdeklaration .
3.2 Klassendeklarationen .
3.3 Aufgabendeklarationen
3.4 Unteraufgaben . . . .
3.5 Ausdrücke . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4 Die Bedienung des Simulators
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
3
5
6
8
1 Einleitung
Die Sprache Tombola dient der Spezifikation von Aufgabenmodellen, die in einem Simulator ausgeführt werden können. Die Aufgabenmodelle werden durch eine Vielzahl
temporaler Relationen hierarchisch verfeinert. Tombola besitzt weiterhin ein reichhaltiges Objektmodell, mit dessen Hilfe die Ausführung von Aufgaben an Bedingungen über
Objekte geknüpft werden kann sowie durch Nachwirkungen von Aufgaben Objekte verändert werden können.
Der Aufbau von Tombola wird im folgenden durch eine nähere Beschreibung der
Produktionsregeln ihrer Grammatik erläutert. (Wenn diese Regeln manchmal unnötig
komplex erscheinen, liegt das daran, dass sie direkt aus der Eingabe für den ParserGenerator von Tombola übernommen und nicht vereinfacht wurden.) Zum Schluss wird
die Bedienung des Simulator-Prototyps kurz erläutert.
1
2 Tokens
Die Produktionsregeln basisieren auf einer Reihe von Tokens, die entweder Terminalsymbole sind und in der Produktionsregel wörtlich angegeben werden, oder als komplexe Tokens durch die folgenden regulären Ausdrücke spezifiziert werden. Die in den
Produktionsregeln auftauchenden komplexen Tokens haben dabei folgende Bedeutung:
hINTEGER_LITERALi, hBOOLEAN_LITERALi und hSTRING_LITERALi stehen
jeweils für Literale der angegebenen Datentypen und sind aufgebaut wie ihre Java-Äquivalente:
hINTEGER_LITERALi ::= 0
| [1-9] ([0-9])*
hBOOLEAN_LITERALi ::= false
| true
hSTRING_LITERALi ::= " ( ( [",\,\n,\r] ) | ( \ ( [n,t,b,r,f,\,’,"] | [0-7] ( [0-7] )? |
[0-3] [0-7] [0-7] ) ) )* "
Das Token hIDi steht für einen Bezeichner in Tombola und hat folgenden Aufbau:
hIDi ::= hLETTERi (h#LETTERi|h#DIGIT i)*
h#LETTERi ::= [ a-z, ä, ö, ü, ß, A-Z, Ä, Ö, Ü, _ ]
h#DIGIT i ::= [ 0-9 ]
Tombola erlaubt zwischen allen Tokens beliebig viele »Whitespaces« (Leerzeichen,
Tabulatoren und Zeilenumbrüche) sowie beide aus Java bekannten Arten von Kommentaren.
3 Produktionsregeln
Eine Tombola-Eingabedatei besteht aus einer Startdeklaration und einer Folge von
Klassendeklarationen und Aufgabendeklarationen.
hInputi ::= hStartDeclarationi ; (hClassDeclarationi | hTaskDeclarationi)*
3.1 Die Startdeklaration
Die Startdeklaration legt die oberste Aufgabe der auszuführenden Hierarchie fest. Dazu wird der Name der Hauptaufgabenklasse angegeben und eine (optional leere) Liste
von Ausdrücken als Parameter, die der Hauptaufgabe beim Start übergeben werden. Als
Parameter bei der Startdeklaration sind nur Ausdrücke, in denen keine Variable und
keine new-Operatoren auftauchen, zulässig. Zu den zulässigen Ausdrücken mehr in Abschnitt 3.5
2
hStartDeclarationi ::= start hIDi hExternalTaskInvocationi
hExternalTaskInvocationi ::= ( hActualParameterListi )
hActualParameterListi ::= [ hExpressioni ( , hExpressioni )* ]
3.2 Klassendeklarationen
Neben den »primitiven« Datentypen int, boolean und String kann man in Tombola eigene Klassen definieren, zu denen Objekte als Instanzen gebildet werden können.
Eine Klassendefinition besteht aus dem Klassennamen (der eindeutig sein muss), einer
eventuellen Oberklasse, die wie in Java mit dem Schlüselwort extends angegeben wird,
und einer Liste von Instanzvariablen. Eine Instanzvariable wird durch ihren Typ, ihren
Namen und einen optionalen Defaultwert beschrieben. Als Typen sind wiederum die primitiven Datentypen und selbst definierte Klassen sowie Arrays über Klassentypen (durch
[] gekennzeichnet) zulässig. Wird kein Defaultwert angegeben, sind die Felder bei neu
erzeugten Objekten jeweils mit dem typabhängigen Defaultwert initialisiert (0 bei int,
false bei boolean sowie null bei String und den Klassentypen).
Tombola hat, wie Java, nur einfache Vererbung. Mit dem instanceof-Operator (siehe
Abschnitt 3.5) kann man testen, ob ein Objekt einer bestimmten Klasse angehört.
hClassDeclarationi ::= class hIDi [ extends hIDi ] { ObjVariableDeclList }
hObjVariableDeclListi ::= ( hTypei hIDi [ = hExpressioni ] ; )*
hTypei ::= hPrimitiveTypei
| hIDi [ [ ] ]
hPrimitiveTypei ::= int
| boolean
| String
3.3 Aufgabendeklarationen
Eine Aufgabendeklaration besteht aus dem Namen der Aufgabenklasse, gefolgt von der
(optional leeren) Liste der formalen Parameter (die wiederum aus dem Parametertyp und
dem Namen bestehen) und der Aufgabenspezifizierung.
hTaskDeclarationi ::= hIDi ( [ hFormalParameterListi ] )
hTaskSpecificationi
hFormalParameterListi ::= hFormalParameter i ( , hFormalParameter i )*
hFormalParameter i ::= hTypei hIDi
3
hTaskSpecificationi ::=-
(
-
-
=> {
autostart hOrExpressioni
SEQ
SER
ALT
OPT
PAR
SIM
=> {
prompt
:
(
hOrExpressioni
) -
} autostop hTaskInitializerListi
)
ATOM
( hSubtaskListi )
foreach hIDi hIDi ( hIDi ) ( hSubtaskInvoci
LOOP ( hOrExpressioni ) ( hSubtaskInvoci )
hEffectListi } -
)
-
Abbildung 1: Eisenbahndiagramm der Produktionsregel für Aufgabenspezifierung
Die komplexeste aller Produktionsregeln beschreibt die Aufgabenspezifizierung. Um
den Überblick zu erleichtern, wird diese Regel in Abbildung 1 als Eisenbahndiagramm
angegeben.
Am Anfang einer Aufgabenspezifizierung kann, mit dem Schlüsselwort prompt markiert, optional ein Prompt-Ausdruck angegeben werden, dessen ausgewertete Form (die
vom Typ String sein muss) dann bei den Dialogen im Simulator an Stelle des Aufgabennamens verwendet wird. Danach folgt eine optionale Vorbedingung für die Aufgabenausführung. Nur wenn diese erfüllt ist, kann die Aufgabe begonnen werden. In der dann
folgenden optionalen Aufgabeninitialisierungsliste können aufgabeninterne Variablen definiert und mit Defaultwerten versehen werden. Der Gültigkeitsbereich von aufgabeninternen Variablen reicht bis zum Ende der Effektliste der Aufgabe.
Falls eine Aufgabe so bald wie möglich selber starten soll, kann dies mit dem Schlüsselwort autostart angegeben werden. (Falls gleichzeitig mehrere Aufgaben automatisch
gestartet werden können, ist nicht definiert, welche als erste startet.) Umgekehrt spezifiziert das Schlüsselwort autostop, dass eine Aufgabe sich so bald wie möglich selbst
beendet.
Dann hat man die Wahl, entweder eine der Standard-Temporalrelationen zu wählen
oder eine freie Schleifenaufgabe (mit LOOP gekennzeichnet). Die Standard-Temporalrelationen haben folgende Bedeutung:
ATOM Aufgaben dieses Typs haben keine Unteraufgaben.
SEQ Die Unteraufgaben werden in der angegebenen Reihenfolge nacheinander ausgeführt.
SER Die Unteraufgaben werden in einer beliebigen Reihenfolge nacheinander ausgeführt.
ALT Genau eine der Unteraufgaben wird ausgeführt.
4
OPT Eine oder keine der Unteraufgaben wird ausgeführt.
PAR Alle Unteraufgaben werden, beliebig miteinander verschränkt, ausgeführt.
SIM Alle Unteraufgaben werden simultan ausgeführt, d. h., zuerst werden alle Aufgaben
in beliebiger Reihenfolge gestartet und dann wiederum in beliebiger Reihenfolge
beendet.
Nach Angabe einer nicht-atomaren Temporalrelation hat man zwei Möglichkeiten, die
Unteraufgaben festzulegen: Entweder gibt man eine Liste von Unteraufgaben an, oder
man spezifiziert mit Hilfe des Schlüsselwortes foreach eine Schleife über alle Elemente
eines Arrays. Dazu müssen der Elementtyp des Arrays, ein Bezeichner für die Schleifenvariable und das Array, über das iteriert wird, angegeben werden, gefolgt von einem
Unteraufgabenaufruf. Dieser Aufruf wird dann für alle Arrayelemente durchgeführt.
Die Schleifenvariable ist bei jeder Iteration eine Referenz auf das entsprechende Element des Arrays, d. h., man kann sie sowohl zum Lesen des Elementwertes benutzen, als
auch zum Setzen des Wertes. Dies ist insbesondere unverzichtbar, wenn man ein neu
erzeugtes Array in einer Schleife mit Objektreferenzen füllen will. Ob und in welcher Reihenfolge die einzelnen Unteraufgabenaufrufe geschehen, richtet sich natürlich nach der
gewählten temporalen Relation.
Bei der freien Schleife wird eine Bedingung angegeben, die vor jedem Schleifendurchlauf
ausgewertet wird. Trifft die Bedingung zu, wird die angegebene Unteraufgabe aufgerufen.
Am Ende der Aufgabenspezifikation kann noch eine Effektliste stehen. Die Anweisungen in dieser Liste werden direkt nach der Beendigung der Aufgabe ausgeführt.
hTaskSpecificationi ::= : [ hSTRING_LITERALi ] [ ( hOrExpressioni ) => ]
[ { hTaskInitializerListi } ] [ autostart ] [ autostop ]
( ATOM | ((SEQ | SER | ALT | OPT | PAR | SIM ) ( hSubtaskListi )
| foreach hIDi hIDi ( hIDi ) ( hSubtaskInvoci )
| LOOP ( hOrExpressioni ) ( hSubtaskInvoci ) ))
[ => { hEffectListi } ]
hTaskInitializerListi ::= ( Type hIDi [ = Expression ] ; )+
hSubtaskListi ::= hSubtaskInvoci ( , hSubtaskInvoci )*
hEffectListi ::= ( hAssignmenti ; )+
3.4 Unteraufgaben
Es gibt zwei Möglichkeiten, eine Unteraufgabe anzugeben:
Externe Unteraufgaben (bereits im Abschnitt 3.1 erwähnt) werden getrennt von der
Oberaufgabe definiert. Der beim Aufruf angegebene Bezeichner bestimmt die aufgerufene Aufgabenklasse. Man kann einer externen Unteraufgabe über eine Parameterliste Werte übergeben. (Eine Rückgabe von Werten an die Oberaufgabe gibt es
5
in dem Sinne nicht; jedoch kann die externe Unteraufgabe in ihrer Effektliste Felder
von übergebenen Objekten verändern.) Eine externe Unteraufgabe hat einen eigenen Gültigkeitsbereich für Variablen (Variablen aus der Oberaufgabe sind also hier
nicht zugänglich) und kann auch von verschiedenen Oberaufgaben aus aufgerufen
werden.
Interne Unteraufgaben werden im Körper der Oberaufgabendefinition spezifiziert. Ihr
Gültigkeitsbereich ist eine Erweiterung des Oberaufgabengültigkeitsbereichs, so
dass alle Variablen aus der Oberaufgabe weiterhin zugänglich sind. Sie eignen
sich für Unteraufgaben, die ihrer Natur nach nur als Teile einer ganz bestimmten Oberaufgabe Sinn machen. Der beim Aufruf angegebene Bezeichner hat hier
nur kommentierenden Character. Der vollständige Name einer internen Unteraufgabenklasse ergibt sich, wenn man dem Namen der internen Unteraufgabenklasse
den vollständigen Namen der Oberaufgabe, gefolgt von einem Punkt, voranstellt.
Unteraufgabenaufrufe können beliebig tief geschachtelt werden.
hSubtaskInvoci ::= hIDi
[ hExternalTaskInvocationi | hTaskSpecificationi ]
3.5 Ausdrücke
Die in Tombola möglichen algebraischen und logischen Ausdrücke und die Zuweisung
orientieren sich wiederum an ihren Java-Äquivalenten. Die Addition ist ebenfalls überladen, so dass Strings konkateniert werden können. Ist nur einer der beiden Operanden ein
String, wird er mit einer String-Repräsentation des anderen Operanden verknüpft. Die
logischen Operatoren || und && ermöglichen die aus Java bekannte verkürzte Auswertung.
hExpressioni ::= hAssignmenti
| hOrExpressioni
hAssignmenti ::= hNamei = hExpressioni
hOrExpressioni ::= hAndExpressioni ( || hAndExpressioni )*
hAndExpressioni ::= hExclusiveOrExpressioni ( && hExclusiveOrExpressioni )*
hExclusiveOrExpressioni ::= hEqualityExpressioni ( ^ hEqualityExpressioni )*
hEqualityExpressioni ::= hInstanceOfExpressioni
( == hInstanceOfExpressioni> | != hInstanceOfExpressioni» )*
Mit Hilfe des instanceof-Operators kann, wie in Java, festgestellt werden, ob das
Objekt, auf das die Referenz links vom Operator verweist, zu der rechts stehenden Klasse
oder einer ihrer Oberklassen gehört.
6
hInstanceOfExpressioni ::= RelationalExpression [ instanceof hIDi ]
hRelationalExpressioni ::= hAdditiveExpressioni
( < hAdditiveExpressioni | > hAdditiveExpressioni | <= hAdditiveExpressioni | >=
hAdditiveExpressioni )*
hAdditiveExpressioni ::= hMultiplicativeExpressioni ( + hMultiplicativeExpressioni | hMultiplicativeExpressioni )*
hMultiplicativeExpressioni ::= hUnaryExpressioni
( * hUnaryExpressioni | / hUnaryExpressioni | % hUnaryExpressioni )*
hUnaryExpressioni ::= [ + ] hPrimaryExpressioni
| - hPrimaryExpressioni
| ! hPrimaryExpressioni
hPrimaryExpressioni ::= hLiteral i
| hNamei
| hCreationExpressioni
| hInteractiveInputExpressioni
| ( hExpressioni )
hLiteral i ::= hIntegerLiteral i
| hBooleanLiteral i
| hStringLiteral i
| hObjectLiteral i
hIntegerLiteral i ::= hINTEGER_LITERALi
hBooleanLiteral i ::= hBOOLEAN_LITERALi
hStringLiteral i ::= hSTRING_LITERALi
hObjectLiteral i ::= null
Ein Feld eines Objektes, das in einer Variablen abgelegt ist, wird wie in Java angesprochen, indem der Feldname, durch einen . getrennt, hinter den Variablennamen gehängt
wird.
hNamei ::= hIDi ( . hIDi )*
Neue Objekte einer Klasse werden wie in Java durch den new-Operator, gefolgt vom
Klassennamen erzeugt. Bei der Erzeugung von Arrays folgt noch ein Ausdruck, der die
Größe des Arrays angibt. Arrays können (zumindest momentan) nur über Klassentypen
erzeugt werden. Die Elemente von neu erzeugten Arrays sind mit dem Wert null vorbelegt.
7
hCreationExpressioni ::= new hIDi [ [ hAdditiveExpressioni ] ]
Um interaktive Eingaben in das Aufgabenmodell einfließen zu lassen, kann man Eingabefenster erzeugen, mit denen eine ganze Zahl, ein Wahrheitswert oder ein String
eingelesen werden kann. Als Parameter kann dabei ein Ausdruck übergeben werden, der
dem Benutzer als Prompt bei der Eingabe angezeigt wird.
hInteractiveInputExpressioni ::= hInteractiveIntegerInputExpressioni
| hInteractiveBooleanInputExpressioni
| hInteractiveStringInputExpressioni
hInteractiveIntegerInputExpressioni ::= INT_INPUT ( [ hExpressioni ] )
hInteractiveBooleanInputExpressioni ::= BOOLEAN_INPUT ( [ hExpressioni ] )
hInteractiveStringInputExpressioni ::= STRING_INPUT ( [ hExpressioni ] )
4 Die Bedienung des Simulators
Der Simulator ist für Release-Zwecke in einer JAR-Datei abgelegt. Man startet ihn mit
dem Aufruf
java -jar Tombola.jar hTombola-Dateii.
Voraussetzung dafür ist ein Java-Interpreter der Version 1.3 (oder höher). Der Simulator
liest dann die angegebene Datei ein und beginnt, falls er keine Fehler feststellt, mit der
Simulation.
Die Simulation besteht aus einer Abfolge einzelner Schritte. Jeder Schritt besteht entweder aus der Auswahl einer Aufgabe, die fortschreiten (also beginnen oder enden) kann,
oder dem Ausfüllen eines Eingabeelements. Alle Aufgaben, die momentan fortschreiten
können, werden in einem Auswahlfeld angezeigt, zusammen mit der Art des möglichen
Fortschreitens. Der Benutzer kann dann die gewünschte Aufgabe auswählen, so dass
diese im nächsten Schritt fortschreitet. Steht nur eine Aufgabe zum Fortschreiten zur
Verfügung, wird lediglich ein Dialogfenster angezeigt, in dem das Fortschreiten bestätigt
werden kann. Kann keine Aufgabe fortschreiten (weil noch eine oder mehrere Aufgaben
ihre Effektliste abarbeiten), so wird gar kein Aufgabenfenster angezeigt.
Werden mehrere Fenster angezeigt (Eingabefenster für die Effektlisten sich beendender
Aufgaben und evtl. das Aufgabenauswahlfenster), so werden diese anfangs alle in der
Bildschirmmitte positioniert, so dass sie sich gegenseitig verdecken.
Es kann sein, dass das Aufgabenauswahlfenster manchmal kurz aufzuflackern scheint.
Dies geschieht immer dann, wenn eine Aufgabe, deren Effektliste abgearbeitet wurde, beendet wird. Da sich die Liste der Aufgaben, die fortschreiten können, dadurch verändern
kann, wird das Aufgabenfenster zu diesem Zeitpunkt immer vom Bildschirm entfernt und
neu erzeugt.
8
Herunterladen