• Keine Ergebnisse gefunden

Eine neue dynamisch-getypte und imperative Programmiersprache mit verz¨ogerter Auswertung

N/A
N/A
Protected

Academic year: 2022

Aktie "Eine neue dynamisch-getypte und imperative Programmiersprache mit verz¨ogerter Auswertung"

Copied!
54
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

imperative Programmiersprache mit verz¨ ogerter Auswertung

Bachelorarbeit Jan van den Brand

16. Januar 2015

eingereicht bei PD Dr. David Sabel

(2)

2011 § 25 Abs. 11

Hiermit best¨ atige ich, dass ich die vorliegende Arbeit selbstst¨ andig verfasst habe und keine anderen Quellen oder Hilfsmittel als die in dieser Arbeit angegebenen verwendet habe.

Frankfurt, den 16.01.2015

Jan van den Brand

(3)

1 Einleitung 5

1.1 Ziele vonlazy Imp . . . 5

1.1.1 Funktionen . . . 5

1.1.2 Bl¨ocke, yield und return . . . 7

1.1.3 Zuweisungen . . . 8

1.1.4 Namensr¨aume und Aufl¨osung . . . 8

1.1.5 Objekte . . . 10

1.1.6 Kontrollstrukturen . . . 11

1.2 Uberblick . . . .¨ 12

2 Kernsprache 13 2.1 Syntax . . . 13

2.2 Genaue Syntax . . . 13

2.2.1 Eindeutigkeit der Grammatik . . . 15

2.3 Semantik . . . 20

2.3.1 Operationale Semantik . . . 21

2.3.2 Variablen . . . 22

2.3.3 Funktionen . . . 24

2.3.4 Beispiele . . . 25

2.3.5 Eindeutigkeit der Semantik . . . 27

3 Objekte und Kontrollstrukturen 36 3.1 Syntax . . . 36

3.2 Operationale Semantik . . . 37

3.2.1 Variablen . . . 37

3.2.2 Funktionen . . . 38

3.2.3 Schleifen . . . 39

4 Komplexe Objektstrukturen und Funktionen 40 4.1 Syntax . . . 40

4.2 Operationale Semantik . . . 41

5 Implementierung 43 5.1 Grundlagen zum Interpreter und Compilerbau . . . 43

5.2 Codeausf¨uhrung . . . 44

5.2.1 Zwischencode . . . 44

5.2.2 Abweichungen von FunNode zur operationalen Semantik . . . 47

5.3 Parser . . . 47

5.3.1 Lexikalische Analyse . . . 47

5.3.2 Syntax¨uberpr¨ufung und Zwischencodeerzeugung . . . 48

5.4 Codebeispiele . . . 49

5.5 Laufzeitvergleich . . . 52

6 Fazit 53

(4)

Im Rahmen dieser Bachelorarbeit wird eine neue Programmiersprache entworfen. Diese wird zun¨achst durch Beispiele beschrieben, sodass die Ziele und Schwerpunkte der Sprache bekannt sind. Die Sprache wird danach mit mathematischen Mitteln dargestellt und analysiert.

Dazu werden Grammatik und operationale Semantik der Kernsprache angegeben. Diese Kernsprache wird dann erweitert und eine allge- meine Methode beschrieben, wie die Sprache um weitere Funktionen und Datentypen erg¨anzt werden kann.

Zudem wird ein Interpreter f¨ur die neue Sprache entwickelt.1 Dieser Interpreter ist nicht auf Laufzeit optimiert und ¨ubernimmt auch kein Speichermanagement (es ist kein Garbage Collector vorhanden). Er dient nur als Proof-of-Concept, um die Funktionalit¨at des theoretischen Konzepts zu beweisen.

(5)

Das Ziel dieser Arbeit ist der Entwurf einer neuen objekt-orientierten imperativen high-order Sprache, die optional auch eine verz¨ogerte Auswertung mit Referenzoptimierung (lazy-evaluation) unterst¨utzt. Im Folgenden bezeichnen wir diese neue Sprache mit lazy Imp (en. fauler Kobold), in Anlehnung an die imperative und verz¨ogerte Auswertung.

In diesem Kapitel beschreiben wirlazy Impzun¨achst informell mit Hilfe von Codebeispielen, damit die Funktionsweise bekannt ist und somit die sp¨atere theoretische Analyse verst¨andlicher wird.

1.1 Ziele von lazy Imp

1.1.1 Funktionen

Lazy Imp ist eine high-order Programmiersprache, das heißt Funktionen sind selbst Objekte/

Werte. Hat man zum Beispiel eine Funktion f, so bezeichnet f auch immer die Funktion, und nicht etwa das Ergebnisf().

def f u n ( )

return 3 end

a = f u n b = f u n ( )

p r i n t ( b == a ) p r i n t ( b == a ( ) )

Wertet man obigen Code aus, so enth¨alt a nun die gleiche Funktion wie fun. Die Variable b enth¨alt hingegen das Ergebnis der Funktion, also die Zahl 3. Der Code gibt demnach zuerstfalse und danntrue aus.

Nat¨urlich ist es auch m¨oglich Funktionen zu definieren, ohne sie in einer Variable zu speichern, um beispielsweise eine Funktion durchreturnzur¨uckzugeben. Die Funktionsweise ist ¨ahnlich zu Lambda-Ausdr¨ucken.

f u n = do x -> y i e l d 2 ∗ x end

Dasyieldist zun¨achst wie einreturnzu verstehen, der genaue Unterschied wird sp¨ater erkl¨art.

Dieser Code erzeugt eine Funktion, die ein Argument x erh¨alt und 2 * x zur¨uckgibt. Diese Funktion wird dann der Variablefunzugeordnet. Der Code ist also ¨aquivalent zu

def f u n ( x )

y i e l d 2 ∗ x end

Weiterhin ist f¨ur Funktionen in lazy Imp besonders, dass man sie wie in Haskell die partielle Anwendung unters¨utzen, das heißt dass sie mit zu wenigen Argumenten aufgerufen werden k¨onnen.

i n c a l l b y o n e = map do x -> y i e l d x + 1 end

Seimapeine eine Funktion, die zwei Argumente erwartet - eine Funktion und eine Liste - und die Funktion auf jedes Listenelement anwendet. Die Funktionmaphat bereits die anonyme Funktion

(6)

Funktion, die eine Liste erwartet und jedes Listenelement um 1 erh¨oht. Diese neue Funktion wird ininc all by onegespeichert. Zu beachten ist hierbei aber der folgende Unterschied zuHaskell:

l i s t e = map do x -> y i e l d x + 1 end [ 1 , 2 , 3 ]

In Haskell w¨urde der ¨aquivalente Code liste = map (\x -> x+1) [1,2,3] die Liste [2,3,4]

zur¨uckgeben. In lazy Imp hingegen wird eine Funktion zur¨uckgegeben, die kein Argument er- wartet und bei ihrer Ausf¨uhrung [2,3,4] berechnet. Der Funktionsaufruf ohne Klammern erzeugt also immer eine Funktion, analog zu dem Unterschied zwischen a = fun und b = fun(). Auf diese Weise wird auch dielazy-order Auswertung realisiert.

Der Ausdruckmap(do x -> yield x + 1 end, [1,2,3])ruft die Funktionmapauf und berech- net sofort das Ergebnis,map do x -> yield x + 1 end [1,2,3]hingegen verz¨ogert die Berech- nung, bis die resultierende argumentlose Funktion aufgerufen wird.

Eine weitere M¨oglichkeit Funktionen aufzurufen ist per Attributzugriff. Betrachte dazu folgen- den Code:

a = f i b o n a c c i ( 1 0 0 ) p r i n t ( a . even ) und

a = f i b o n a c c i 100 p r i n t ( a . even )

Die erste Variante berechnet direkt die 100. Fibonaccizahl und speichert sie in der Variable a.

Danach wird auf dessen Attributevenzugegriffen.

In der zweiten Variante hingegen wird der Variable a eine argumentlose Funktion zugeordnet.

Diese neue Funktion enth¨alt aber nicht das Attribut even, sodass klar ist, dass eigentlich das Attribut des Ergebnisses vonfibonacci(100)gemeint ist. Entsprechend wird also in der zweiten Zeile die Zahl berechnet, damit auf even zugegriffen werden kann. Die Funktion wird durch das Ergebnis ersetzt, sodass sp¨atere Zugriffe auf a keine erneute Berechnung erfordern. Es handelt sich also um eine lazy-order Auswertung, denn fibonacci(100) wird erst berechnet, wenn das Ergebnis tats¨achlich ben¨otigt wird.

Funktionen werden des Weiteren direkt aufgerufen, wenn sie der einzige Ausdruck in der Zeile sind.

def a u s g a b e ( x ) p r i n t x end

Offensichtlich steht das print xnicht f¨ur einenlazy-order Ausdruck, das Ergebnis von print x wird schließlich nirgends verwendet. Stattdessen soll einfachprint(x)ausgef¨uhrt werden.

Die Motivation hierf¨ur erfolgt in den n¨achsten Abschnitten 1.2 und 1.5.

(7)

1.1.2 Bl¨ ocke, yield und return

Eine Besonderheit vonlazy Imp ist, dass es keinen Unterschied zwischen Bl¨ocken und Funktionen gibt:

i f ( x < 0 ) do . . . end

Die Bl¨ocke in den if Konstrukten sind einfach nur anonymedo ... endFunktionen ohne Ar- gument. Entsprechend gibt es zwei verschiedene Abbruchoperatorenyield undreturn.

def f u n ( a )

i f ( a < 0 ) do return end

. . . end

returnsoll nicht nur die anonyme Funktion abbrechen, sondern auch bis aus dem Rumpf vonfun herausspringen. Dies wird realisiert, indemyieldnur die aktuelle Funktion abbricht undreturn solange zur¨uckspringt, bis eincatchgefunden wird. Obiger Code ist also ¨aquivalent zu:

f u n = do c a t c h a ->

i f ( a < 0 ) do return end

. . . end

Die Zusammenfassung von Funktionen und Bl¨ocken ist auch konsistent mit der direkten Ausf¨uhrung von Funktionen, sollten sie der einzige Ausdruck in einer Zeile sein.

a = true do

l o c a l a = f a l s e p r i n t ( a )

end p r i n t ( a )

Dieser Code gibtfalse,trueaus. Derdo ... endAusdruck definiert eine argumentlose Funk- tion, die direkt ausgef¨uhrt wird, da sie alleine steht. Im Inneren der Funktion wird eine lokale Variable adeklariert und mit false initialisiert. Der erste print Befehl gibt deshalb false aus.

Der zweite print Befehl ist aber außerhalb der Funktion, also in einem anderem Namensraum und gibt trueaus. Die argumentlose Funktion verh¨alt sich also genau wie ein Block in anderen Programmiersprachen.

Mehr zu Namensr¨aumen gibt es in 1.4.

(8)

1.1.3 Zuweisungen

Lazy Imp besitzt nur Objekte (Tabellen) und Funktionen, eine implizite Unterscheidung zwischen Referenz und Kopie, wie in J ava, P ython oder ¨Ahnlichen ist also nicht m¨oglich, da es keine Unterscheidung zwischen Objekten und primitiven Datentypen gibt.

Die Unterscheidung erfolgt stattdessen explizit durch einen Doppelpunkt:

a = true b := a c = a b = f a l s e

Nach diesem Code sind a und b beide dasselbe false Objekt und c hingegen ein anderes true Objekt.

Der Operator=ist eine Kopiezuweisung. Das Objekt inawird in Zeile 1 zu einer Kopie vontrue.

Der Operator:=ist hingegen eine direkte Referenzzuweisung, in Anlehnung an die mathematische Definition. Nach Zeile 2 zeigt die Variable b auf dasselbe Objekt wie die Variable a. Deshalb werden beide in Zeile 4 zu f alse, die Variable chingegen bleibt true, da sie nur eine Kopie des Objektes ausbbzw. aenth¨alt.

Ahnlich funktioniert die Zuweisung bei den Funktionsargumenten:¨ def f u n ( a : , b )

a = f a l s e b = f a l s e end

c = true d = true f u n ( c , d )

Nach der Ausf¨uhrung dieses Codes, w¨are c false und b weiterhin true. Man kann sich hier vorstellen, dass zu Beginn der Funktion argumentVariable = argumentaufgerufen wird, durch den Doppelpunkt hinterawird diese Zuweisung also zua: = cbzw. syntaktisch korrekta := c.

1.1.4 Namensr¨ aume und Aufl¨ osung

Die Deklaration von Variablen erfolgt implizit, das heißta = b deklariert automatisch die Vari- ablena undb, sollten sie nicht aufgel¨ost werden k¨onnen und initialisiert sie mit dem leerennil Objekt.

Eine solche automatische Deklaration erzeugt immer eine lokale Variable. Will man explizit eine lokale Variable deklarieren, erfolgt dies per local a oder local a = wert. Auf diese Weise

¨

uberschreibt man nicht ungewollt die Variableaim Namensraum dar¨uber.

Die Variablenaufl¨osung erfolgt per Namensr¨aumen auf einem Stack. Zuerst wird im obersten Namensraum auf dem Stack gesucht, danach wird die Suche auf den n¨achsten Namensraum aus- geweitet, falls keine solche Variable gefunden wurde. Zu beachten ist, das die Aufl¨osung nicht zum Zeitpunkt der Definition einer Funktion erfolgt, sondern zum Zeitpunkt ihrer Ausf¨uhrung:

i = true def f u n ( )

p r i n t ( i ) end

def funB ( )

l o c a l i = f a l s e f u n ( )

end f u n ( ) funB ( )

(9)

Dieser Code gibt zuerst true, dann false aus, da die Variable i bei der Ausf¨uhrung von fun verschieden aufgel¨ost wurde.

Will man hingegen eine Variable unbedingt im Namensraum zum Zeitpunkt der Definition aufl¨osen, so geschieht dies per^. Motivation f¨ur die Notation ist, dass man auf die im Quelltext weiter oben stehende Variable verweist.

i = true def f u n ( )

p r i n t ( ˆ i ) end

def funB ( )

l o c a l i = f a l s e f u n ( )

end f u n ( ) funB ( )

Dieser Code gibt zwei maltrueaus, da die Aufl¨osung beide Male die oberste Variableiverwendet und diese im Laufe des Programms ver¨andert wurde. Dasiinfunwurde also nicht mehr zu dem lokaleniausfunBaufgel¨ost.

Aber Achtung:

i = true def f u n ( )

p r i n t ( ˆ i ) end

i := f a l s e f u n ( )

Dieser Code ergibt zwei Mal true, obwohl iein false zugeordnet wurde. Der Ausdruck ^iwird n¨amlich zu dem Objekt inizum Zeitpunkt der Definition aufgel¨ost. Sp¨ater handelt es sich um eine Referenzzuweisung zur Variablei, das Objekt, welches zuvor iniwar, wurde also nicht ver¨andert.

Diese Art der Namensaufl¨osung mag f¨ur viele unintuitiv wirken, da in den meisten impera- tiven Sprachen die Variablen immer im definierenden Namensraum aufgel¨ost werden. Diese neue Aufl¨osungsvariante hat aber durchaus seine Motivation.

Es soll m¨oglich sein, durch zus¨atzlichen Code, den bereits bestehenden Code zu manipulieren und zu ver¨andern. Manche Programme sollen dem Anwender eine Anpassung an eigene Bed¨urfnisse erm¨oglichen. Der Quellcode kann aber nachtr¨aglich nicht ver¨andert werden. Bei diesen Pro- grammen wird h¨aufig eine Skriptsprache wie LUA verwendet, die es erlaubt, Methoden bereits bestehender Objekte zu ver¨andern, und so dem Anwender erm¨oglicht, das Programm anzupassen.

In LU A hatte ich aber schon mehrfach das Problem, dass ich beim Modifizieren eines fremden Programms, nicht auf die lokalen Variablen zugreifen konnte. Man deklariert inLU A Variablen nicht als lokal, um eine Manipulation von außen zu verhindern (LU Awird gew¨ahlt, um eine Ma- nipulation des Codes zu erlauben). Die Deklaration ist nur lokal, weil die Deklaration von allen Variablen als global den Namensraum zu sehr f¨ullen w¨urde und man dann bei der Benennung der eigenen Variablen darauf achten m¨usste, nicht aus Versehen etwas zu ¨uberschreiben.

Die Variablenaufl¨osung vonlazy Imp erm¨oglicht weiterhin die Verwendung von lokalen Variablen, als auch den Zugriff auf diese Variablen von außerhalb.

(10)

1.1.5 Objekte

Objekte in lazy Imp sind Tabellen, bestehen also aus Schl¨ussel-Wert-Paaren. Der Schl¨ussel ist dabei ein Variablenname und der Wert eine weitere Tabelle. Objekte sind insofern einfach nur eine Sammlung von Variablen. Streng genommen aber kein neuer Namensraum, da auf die Variablen nur perobjekt.attributzugegriffen werden kann.

Damit eine Methode also auf das Objekt zugreifen kann, muss es als Argument ¨ubergeben werden:

o b j . add = do s e l f , o t h e r -> y i e l d s e l f. v a l u e + o t h e r . v a l u e end

Damit man nicht immerobj.add(obj, obj2)schreiben muss, gibt es daf¨ur die verk¨urzte Schreib- weiseobj:add(obj2). Diese wiederum kann noch weiter verk¨urzt werden, indem man Punkte und Klammern wegl¨asst: obj add obj2

Dies hat einerseits den Vorteil, dass die Programmiersprache nat¨urlicher wirkt. Man nehme zum Beispiel den Code ”ich programmiere Haskell”. Dieser Code besitzt das Objekt ich, welches die Methodeprogrammiere besitzt und diese mit dem ArgumentHaskellausf¨uhrt. Es handelt sich aber auch um einen v¨ollig korrekten Satz, mit einer sehr ¨ahnlichen Interpretation der W¨orter.

”ich” ist das Subjekt, f¨uhrt also wie im Programm die Methode/Aktion aus, ”programmiere” ist das Pr¨adikat, beschreibt ebenso wie die Methode, was getan wird und ”Haskell” ist das Objekt, auf den die Aktion bzw. die Methode angewendet wird.

Der zweite viel wesentlichere Vorteil ist, dass man auf diese Weise beliebige Operatoren implemen- tieren kann. Man erkennt dies bereits am simplen a + b Beispiel, welches verk¨urzt f¨ur a:+(b) steht. Es wird einfach nur die Methode+des Objektes inaausgef¨uhrt.

Man kann also alle m¨oglichen Operatoren definieren, indem man einfach eine Methode mit diesem Namen definiert. Man ist also nicht wie beispielsweise inC++ an eine feste Auswahl an Opera- toren gebunden.

Man beachte, dass auch Funktionen Objekte/Tabellen sind. Damit ist nicht nur die high-order Eigenschaft gemeint, sondern auch Funktionen besitzen Schl¨ussel-Wert-Paare.

Es gibt also leichte Konflikte mit der punktlosen Notation f¨ur Methoden und der Notation f¨ur die verz¨ogerte Auswertung. Daf¨ur gibt es die folgende Priorit¨atsliste:

Betrachte den Codefun varA varB

1. funbesitzt das AttributvarA, dann wirdfun:varA(varB)ausgef¨uhrt.

2. funbesitzt das Attribut nicht und ben¨otigt kein Argument.

Dann istfunein verz¨ogerter Wert, berechne also den Wert und betrachte das AttributvarA des Ergebnisses.

3. funbesitzt das Attribut nicht und ben¨otigt Argumente.

In diesem Fall ist die verz¨ogerte Auswertung vonfun(varA, varB)gemeint.

Die verz¨ogerte Auswertung ist in imperativen Sprache wegen der Unvorhersehbarkeit sehr unge- wohnt und ist deshalb in lazy Imp nicht standardm¨aßig der Fall. Man nehme beispielsweise die Funktion stack.pop(), welche jedes Mal ein anderes Ergebnis liefern k¨onnte. In lazy Imp hat deshalb die imperative Auswertung immer Vorrang und die verz¨ogerte Auswertung wird nur ver- wendet, wenn der Programmierer dies explizit w¨unscht.

Der Codefun varA varBwird deshalb wenn m¨oglich alsfun:varA(varB)interpretiert und nicht als die ver¨ogerte Auswertung vonfun(varA,varB).

Will man die MethodevarAvonfunverz¨ogert ausf¨uhren, schreibt man stattdessenfun:varA varB

(11)

Ein Beispiele zum besseren Verst¨andnis,funsei hier eine zweistellige Funktion:

r e s u l t = f u n a r g 1 a r g 2 i f r e s u l t == o b j do

. . . end

Es gibt f¨ur die Funktion fun keine Methode namens arg1. Die Variable result ist nach der ersten Zeile also die verz¨ogerte Auswertung vonfun(arg1, arg2), demnach selbst eine Funktion.

Diese Funktion besitzt aber nicht das Attribut==, sodass in der zweiten Zeile der verz¨ogerte Wert berechnet wird und das ==des Ergebnisses verwendet wird. Wie in lazy-order wurde der Wert vonresultalso erst berechnet, wenn er ben¨otigt wurde.

Ein leeres Objekt, besitzt bei Initialisierung bereits die Variable :==(Identit¨atsvergleich, in An- lehnung an:=). Objekte, die keine Funktionen sind, besitzen zus¨atzlich die Variable==(f¨uhrt den Identit¨atsvergleich auf alle Variablen aus).

Die Methode == ist bei Funktionen nicht vorhanden, um beim Vergleich die Ausf¨uhrung von lazy-order Ausdr¨ucken zu erzwingen. Vor allem aber ist die Frage, ob zwei Algorithmen sich f¨ur alle Eingaben gleich verhalten als Folge des Halteproblems unentscheidbar. Man kann also nicht erkennen ob zwei Funktionen sich gleich verhalten.

1.1.6 Kontrollstrukturen

if und else i f ( i > 0 ) do

. . . e l s e

. . . end

In Abschnitt 1.1.2 wurde erkl¨art, dass der Block hinter demifeine Funktion ist. Der Ausdruck if beschreibt also eigentlich eine Funktion, die zwei Argumente erh¨alt. Das erste Argument ist die Bedingung und das zweite eine Funktion. Die do-end und do-else-end Ausdr¨ucke sind Funktionen, die kein Argument erwarten, aber trotzdem mit einem Argument aufgerufen werden k¨onnen. Ist dieses Argumentfalseoder das leere Objektnil, so wird derelseBlock ausgef¨uhrt, bzw gar nichts, ist kein else Block vorhanden. Ansonsten wird der erste Block vor dem else ausgef¨uhrt. Obiger Code ist also ¨aquivalent zu:

do

. . . e l s e

. . . end( i > 0 ) Schleifen

Schleifen lassen sich vor allem durch Rekursion und ¨Ubergabe des Schleifenblocks implementieren.

Es gibt aber dennoch eine spezielle Schleife inlazy Imp , diewhileSchleife:

while ( i > 0 ) do . . . end

whileverh¨alt sich hier effektiv wie dasif-Konstrukt. Ist also eine Funktion, die zwei Argumente erh¨alt. Der einzige Unterschied ist hier, dass die Bedingung nicht vor dem Einsetzen ausgef¨uhrt

(12)

1.2 ¨ Uberblick

In den folgenden Kapiteln wirdlazy Imp genauer beschrieben und untersucht.

• Kernsprache

In Kapitel 2 wird die Kernsprache definiert und analysiert. Dazu wird zun¨achst die Syn- tax der Kernsprache durch eine Grammatik beschrieben und deren Eindeutigkeit bewiesen.

Danach wird auf die Semantik eingegangen und diese durch eine operationale Semantik definiert. Der Rest des Kapitels befasst sich mit der Eindeutigkeit der Semantik.

• Objekte und Kontrollstrukturen

In Kapitel 3 werden der Kernsprache Objekte, Verzweigungen und Schleifen hinzugef¨ugt.

Hier wird wie im vorherigen Kapitel zun¨achst die Syntax beschrieben, indem die Gram- matik der Kernsprache erweitert wird. Daraufhin wird die Semantik f¨ur die neuen Syn- taxkonstrukte durch weitere Regeln erg¨anzt.

• Komplexe Objektstrukturen

In Kapitel 4 wird eine allgemeine Methode beschrieben, wie sich lazy Imp durch beliebige Objekte und Funktionen erweitern l¨asst. Diese Methode wird dabei f¨ur das Hinzuf¨ugen von Integern, Strings und derprint-Funktion verwendet.

• Implementierung

Kapitel 5 befasst sich zun¨achst kurz mit dem Unterschied zwischen Interpretern und Com- pilern. Danach wird die Implementierung des lazy Imp Interpreters erl¨autert und anhand einiger Beispiele erkl¨art. Der Interpreter ist unter

http://www.ki.informatik.uni-frankfurt.de/bachelor/programme/lazyimp/ zu finden.

Zum Schluss wird derlazy ImpInterpreter mit Interpretern anderer Sprachen im Bezug auf Laufzeit verglichen.

• Fazit

Kapitel 6 fasst die Ergebnisse dieser Arbeit noch einmal zusammen. Es wird auf Probleme von lazy Imp eingegangen, die w¨ahrend der Arbeit aufgetaucht sind und eine Aussicht auf Verbesserungsm¨oglichkeiten gegeben.

(13)

Eine Kernsprache beschreibt die fundamentalsten Eigenschaften einer Sprache und bezieht sich bei lazy Imp nur auf die Aufl¨osung von Variablen, Zuweisungen und Funktionen. Andere Objekte wie Listen oder Zahlen sowie Kontrollstrukturen wie Schleifen oder if-Abfragen sind nicht vorhanden.

In diesem Kapitel wird die Syntax und Semantik der Kernsprache formal beschrieben und deren Eindeutigkeit bewiesen. Die Eindeutigkeit ist wichtig, damit es f¨ur ein Programm nicht etwa zwei verschiedene M¨oglichkeiten der Auswertung gibt.

2.1 Syntax

Zum einfacheren Parsen verwendetlazy Imp ein Semikolon, um Zeilen zu beenden. Im vorherigen Entwurf reichte auch ein Zeilenumbruch aus. Diese vom Entwurf abweichende Konvention bleibt in der gesamten restlichen Arbeit und auch in der Implementierung des Interpreters erhalten.

Die genaue Syntax der Kernsprache vonlazy Imp ist auf den ersten Blick sehr umfangreich und nicht sofort verst¨andlich. Deshalb wird zuerst eine vereinfachte Form der Syntax angegeben.

P → L;...L;

L → R |R=R |V :=R |returnR|yieldR

R → V |doV,...,V ->P end|(R... R)|R(R,...,R)|^V

L sind also einzelne Zeilen eines Programms, R sind Ausdr¨ucke, die einen Wert zur¨uckgeben und mitV sind beliebige Variablenausdr¨ucke gemeint und hier nicht explizit angegeben.

Dalazy Imp mit Referenzen arbeitet, geben dieRAusdr¨ucke nicht einfach nur einen Wert zur¨uck, sondern eine Referenz auf einen Wert - daher die BenennungR.

Die Angabe dieser Grammatik ist formal nicht korrekt und soll nur die grobe Struktur von zul¨assigem Programmcode darstellen. So soll zum Beispiel der Ausdruck ”do V,...,V ->P end”

allgemein f¨ur Funktionen stehen - also auch f¨ur Funktionen ohne Argument ”doP end”, obwohl sich das Wort nicht daraus ableiten l¨asst.

2.2 Genaue Syntax

Die Syntax der Kernsprache l¨asst sich durch folgende Grammatik beschreiben:

Hierbei werden regul¨are Ausdr¨ucke in den Produktionsregeln verwendet. Klammern, die zu den regul¨aren Ausdr¨ucken geh¨oren, also keine Terminale sind, werden gr¨oßer dargestellt.

Erkl¨arungen zur verwendeten Notation:

1. + steht f¨ur beliebige Anzahl an Wiederholungen, das Wort muss aber mindestens ein mal produziert werden.

2. steht f¨ur beliebige Anzahl an Wiederholungen, das Wort darf aber auch ¨ubersprungen werden.

3. [·] produziert das Wort ein oder kein Mal.

(14)

N = {LIN ES,LIN E,F U N DEF,RET U RN,ASSIGN, LOCAL,VAR,GLOBAL,VARPART,RVALUE,F U N, ARGDEKL,F U N CALL,ARGS,SP ACE }

T = {$, , (, ), :, ;, =, +,−, *, /} ∪ {a, ... , z, A, ... , Z, 0, ... , 9}

S = LIN ES

P = {

LIN ES →

(

LIN E;

)

+

LIN E → RVALUE |ASSIGN |COP Y |RET U RN RET U RN → return RVALUE |yield RVALUE

COP Y → RVALUE = RVALUE

ASSIGN → VAR := RVALUE

VAR → GLOBAL|LOCAL|VARPART

LOCAL → local VARPART

GLOBAL → $VARPART

VARPART →

((

a|...|Z

)(

a|...|Z|0|...|9

)*

|

[

:

](

+|−|*|/|=

)

+

)

\ {=, :=, do, end, local, catch, return, yield} RVALUE → VAR|F U N |F U N CALL |SP ACE| ^VARPART

F U N → do

[

catch

] [

ARGDEKL ->

]

LIN ES end ARGDEKL → VARPART

[

:

] (

,VARPART

[

:

])*

F U N CALL → RVALUE(

[

ARGS

]

) ARGS → RVALUE

(

,RVALUE

)*

SP ACE → (RVALUE

(

RVALUE

)

+)

}

G = (N,T,S,P)

(15)

2.2.1 Eindeutigkeit der Grammatik

Ein Syntaxbaum ist eine M¨oglichkeit, wie man die Herleitung eines Wortes aus einer Grammatik darstellen kann. Dabei besteht jeder Knoten des Baumes aus einem (Nicht-)Terminal und besitzt als Kinder alle (Nicht-)Terminale, die daraus abgeleitet wurden.

Der Herleitungsbaum f¨ur das Worta := (b c); sieht wie folgt aus:

LINES

LINE

ASSIGN

VAR

VARPART

a

: = RVALUE

SPACE

( RVALUE

VAR

VARPART

b

RVALUE

VAR

VARPART

c

)

Die Ausf¨uhrung vonlazy Impsteht in einem engen Bezug zu obiger Grammatik, da nach und nach der Syntaxbaum reduziert wird. Im obigen Syntaxbaum, wird beispielsweise der rechte Teilbaum vonASSIGN ausgehend vonRVALUE abgearbeitet und dann dem Variablenausdruck im linken Teilbaum zugeordnet.

F¨ur eine eindeutige Ausf¨uhrung von lazy Imp ist also wichtig, dass f¨ur ein Programm (also ein Wort der Sprache) nur ein einziger Syntaxbaum existiert.

Definition 1: Eindeutigkeit einer Grammatik.

Eine Grammatik heißt eindeutig, wenn f¨ur jedes Wort der Sprache, die von der Grammatik erzeugt wird, nur eine m¨ogliche Herleitung existiert.

Satz 1: Die Grammatik der Kernsprache ist eindeutig.

Beweis: Es wird gezeigt, dass die Herleitung eines Wortes, welches aus einem bestimmten Nicht- terminal hergeleitet werden kann, bis auf dieses erste Nichtterminal eindeutig ist.

(16)

Das Wortprint(a)kann beispielsweise sowohl ausLIN E,RVALUE oderF U N CALLhergeleitet werden, aber die Herleitungen beginnend mitLIN E, die Herleitung beginnend mitRVALUE und die Herleitung beginnend mitF U N CALLsind jeweils eindeutig.

Der Beweis erfolgt per Induktion ¨uber die Anzahlnder Terminale eines Wortes.

Induktionsanfang: n= 1

Seiω ein Wort mitn= 1 Terminalen, welches aus einem beliebigen Nichtterminal der Grammatik hergeleitet wurde. Die Herleitung kann nur mit den NichtterminalenVAR,VARPART,RVALUE, ARGDEKL oder ARGS beginnen, denn die anderen Nichtterminale erzeugen nur Worte mit mehr als einem Terminal.

Beginne die Herleitung zuωmit einem dieser Nichtterminale als Startnichtterminal:

1. VARPART

Die Herleitung ist eindeutig, denn die einzige M¨oglichkeit ist die direkte Aufl¨osung von VARPART→ω.

2. VAR

Die Herleitung ist eindeutig, dennVARkann nur zuVARPART aufgel¨ost werden. GLOBAL und LOCAL w¨urden wegen $ und local zu mehr als einem Nichtterminal f¨uhren. Die restliche Herleitung von ω ausVARPART ist eindeutig nach 1).

3. ARGDEKL

Die Herleitung von ARGDEKL ist eindeutig, da ARGDEKL nur zu einem einzelnen VARPART aufgel¨ost werden kann. Weitere VARPART oder ein : w¨urden zu mehr als einem Terminal f¨uhren. Die Herleitung ausVARPART ist eindeutig nach 1).

4. RVALUE

F U NFUN, F U N CALL, SP ACE, ^VARPART w¨urden alle zu mehr als einem Terminal f¨uhren. RVALUE muss also zuVARaufgel¨ost werden. Die weitere Herleitung ist eindeutig nach 2).

5. ARGS

Die Herleitung von ARGS ausgehend ist eindeutig, da ARGS nur zu einem einzelnen RVALUE aufgel¨ost werden kann ohne mehr als ein Terminal zu produzieren. RVALUE ist wiederum eindeutig nach 4).

Induktionsschritt:

Induktionsvorraussetzung: Sei n ∈ N derart, dass alle Worte, bestehend aus maximal n Ter- minalen, eine bis auf das Startnichtterminal eindeutige Herleitung haben. F¨ur ein gegebenes Startterminal ist die Herleitung also eindeutig.

Induktionsbehauptung: Dann haben auch alle Worte mit maximaln+ 1 Terminalen eine bis auf das Startterminal eindeutige Herleitung.

Induktionsbeweis: Seiω ein Wort bestehend ausn+ 1 Terminalen.

Man beachte, dass die Terminalfolgendo und end,def und endsowie(und)immer paarweise auftreten (siehe Regeln von F U N DEF, F U N, F U N CALL und SP ACE). Liegt ein Terminal zwischen einem solchen Paar, muss das Terminal (evtl. indirekt) durch eine der eben aufgez¨ahlten Nichtterminale erzeugt worden sein.

Es wird nun gezeigt, dass f¨ur ein gegebenes Startterminal, der Rest der Herleitung eindeutig ist.

Beginne die Herleitung vonω mit einem der folgenden Nichtterminale:

(17)

1. VARPART

Die Herleitung ist einfach nur die direkte Reduktion VARPART→ω und somit eindeutig.

2. GLOBAL

Das Nichtterminal GLOBAL kann nur zum Terminal $ und Nichtterminal VARPART aufgel¨ost werden. Der Rest des Wortesω (ohne das f¨uhrende$) besitztnTerminale und ist nach I.V. eindeutig ausVARPART herleitbar. GLOBAList also eindeutig.

3. LOCAL

LOCAL kann nur zu den Terminalenlocal und dem NichtterminalVARPART aufgel¨ost werden. Der Rest des Wortes (ohnelocal ) besitzt weniger alsn+ 1 Terminale und ist nach I.V. eindeutig aus VARPART herleitbar. LOCAList also eindeutig.

4. VAR

Beginntω mit einem$, so kann das Wort nur durchGLOBALerzeugt werden, beginnt es mit einem local , so muss das Wort durch LOCAL erzeugt werden. Genau dann, wenn ω mit keinem der beiden Teilworte beginnt, kann das Wort nur durch VARPART erzeugt werden. Zu welchem Nichtterminal VAR aufgel¨ost werden muss, ist f¨ur gegebenes ω also eindeutig bestimmt.

VARist also eindeutig, da die Herleitung aus VARPART,GLOBALundLOCALnach 1), 2) und 3) eindeutig ist.

5. ARGDEKL

, k¨onnen nicht inVARPART erzeugt werden, entsprechend werden diese Terminale direkt von ARGDEKL hergeleitet. Die Teilw¨orter zwischen (sowie vor und nach) diesen Termi- nalen werden von VARPART[:] erzeugt. Betrachte ein solches Teilwort.

Das Terminal : kann inVARPART nicht am Ende eines Wortes erzeugt werden, also muss das : Terminal am Ende eines Teilwortes von ARGDEKLerzeugt worden sein. Der Rest des Teilwortes (bzw. das gesamte Teilwort, sollte es nicht auf : enden), wird hingegen von VARPART hergeleitet. Diese Herleitung ist eindeutig nach I.V. beziehungsweise nach 1), falls keine : oder,im Wort vorkommen.

6. F U N

ω beginnt und endet offensichtlich auf einemdo-end-Paar.

Folgt in ω demdo direkt eincatch , so kann das catch nur von dem Startnichtterminal F U N erzeugt werden. Existiert in ω vor dem ersten -> Teilwort nur ein einziges do , muss es ebenfalls von dem Startnichtterminal F U N erzeugt worden sein und somit wird alles zwischen do und -> (bzwcatch und -> ) vonARGDEKLhergeleitet.

Die Herleitung durch ARGDEKL ist eindeutig nach I.V. da weniger als n+ 1 Terminale von ARGDEKLerzeugt werden.

Alles zwischen -> (oder zwischen dem ersten do , sollte kein -> vorhanden sein oder es nach einem anderendo kommen) und endmuss also vonLIN ES erzeugt werden. Dieses vonLIN ES erzeugte Unterwort hat weniger alsn+ 1 Terminale (do und endfehlen), die Herleitung ist also eindeutig nach I.V..

7. F U N CALL

ω muss auf einem )-Terminal enden. Das )-Terminal hat ein eindeutiges zugeh¨origes (- Terminal. Alles vor diesem(-Terminal muss vonRVALUEerzeugt werden und alles zwischen dem (-)-Paar muss vonARGS erzeugt werden (falls es nicht das leere Wort ist).

Da bei den Herleitungen ausgehen von RVALUE undARGS W¨orter mit weniger als n+ 1 Terminalen entstehen m¨ussen, ist deren Herleitung eindeutig nach I.V.. Somit ist auch F U N CALL eindeutig.

(18)

8. SP ACE

ω beginnt auf einem (-Terminal und endet auf einem )-Terminal. Die Leerzeichen in ω werden genau dann von derSP ACEWurzel des Herleitungsbaumes erzeugt, wenn sie nicht zwischen einem anderen(-)-Paar oder einemdo-end-Paar stehen.

Alles zwischen den Leerzeichen, die von dem Startnichtterminal erzeugt werden, muss von RVALUE hergeleitet werden. Die Herleitung dieser Teilw¨orter ist eindeutig nach I.V., da die Teilworte weniger als n+ 1 Terminale besitzen. Die Herleitung von ω ausgehend von SP ACE ist also eindeutig.

9. RVALUE

Beginntω mit einem(-Terminal, mussRVALUE zu einemSP ACEaufgel¨ost werden.

Beginnt es nicht mit einem (-Terminal aber endet auf ein )-Terminal, muss RVALUE zu einem F U N CALLaufgel¨ost werden.

Treten diese beiden F¨alle nicht ein und ω beginnt mit einem do-Terminal, muss RVALUE zu einemF U N reduziert werden.

Beginnt es hingegen mit einem^Terminal, muss die Herleitung mitRVALUE→^VARPART beginnen.

Genau dann, wenn keiner dieser F¨alle eintritt, mussRVALUE zu einemVAR werden.

Alle diese Herleitungen sind eindeutig nach 1),4),6),7) und 8) und somit ist auch RVALUE eindeutig.

10. ARGS

Die ,-Terminale in ω stehen genau dann nicht in einem do-end-Paar oder einem(-)-Paar, wenn sie direkt von dem ARG Startnichtterminal erzeugt werden. Welche ,-Terminale also von der Wurzel erzeugt werden, ist eindeutig. Alle Teilw¨orter von ω zwischen diesen ,-Terminalen m¨ussen jeweils vonRVALUE erzeugt werden. Die Herleitung ausRVALUE ist nach I.V. gegeben (die,-Terminale fehlen, Anzahl Terminale ist also kleinern+ 1). Werden keine ,-Terminale direkt von derARGS Wurzel erzeugt, erfolgt die Eindeutigkeit per 9).

11. ASSIGN

Ein := Teilwort, kann nur durchASSIGNerzeugt werden (:=ist kein g¨ultigerVARPART).

Es gibt genau ein := Teilwort inω, das nicht im Inneren einesdo-end-Paares oderdef-end- Paares enthalten ist, denn weitereASSIGNNichtterminale k¨onnen im Herleitungsbaum nur (indirekt) ¨uberF U N DEF undF U N auftreten.

Alles vor diesem, von demASSIGN Startnichtterminal erzeugten, := Teilwort muss von VAR erzeugt werden und alles nach dem := wird vonRVALUE erzeugt. Die Herleitung ausgehend vonVARundRVALUE sind nach I.V. eindeutig.

Die Herleitung beginnend mitASSIGN ist also eindeutig.

12. COP Y

Ein = Teilwort, kann nur durchCOP Y erzeugt werden (=ist kein g¨ultigerVARPART). Es gibt genau ein = Teilwort inω, das nicht im Inneren einesdo-end-Paar oderdef-end-Paar enthalten ist, denn weitere COP Y Nichtterminale k¨onnen im Herleitungsbaum nur (indi- rekt) ¨uberF U N DEF undF U N auftreten.

Alles vor und nach diesem, von der COP Y Wurzel erzeugten, = Teilwort muss von RVALUE erzeugt werden. Die Herleitungen ausgehend von den beidenRVALUE sind nach I.V. eindeutig.

Die Herleitung beginnend mitCOP Y ist also eindeutig.

13. RET U RN

ω beginnt mityield oder mitreturn . Alles dahinter muss vonRVALUE erzeugt werden und enth¨alt weniger alsn+ 1 weitere Terminale. Die Herleitung ist nach I.V. also eindeutig.

(19)

14. LIN E

Beginntω mitreturn oderyield , mussLIN E zuRET U RN reduziert werden.

Enth¨altωein = , das nicht zwischen einemdo-end-Paar steht, mussLIN Ezu einemCOP Y aufgel¨ost werden. Bei einem := außerhalb einesdo-end-Paares, wirdLIN EzuASSIGN.

Genau dann, wenn keiner dieser F¨alle eintritt, kann ω ausgehend von LIN E nur ¨uber RVALUE erzeugt werden.

Alle diese Herleitungen sind eindeutig nach den vorherigen F¨allen.

15. LIN ES

Genau die ;-Terminale, die nicht in einem do-end-Paar stehen, werden von dem LIN ES Startnichtterminal erzeugt.

Alle Teilworte zwischen diesen eindeutig gegebenen;-Terminalen werden vonLIN Ehergeleitet.

Nach I.V. sind diese Herleitungen eindeutig, denn die Anzahl Terminale ist durch fehlende

;kleiner alsn+ 1.

Alle Worte der Kernsprache werden mit LIN ES als Startnichtterminal gebildet. Mit dieser Festlegung des ersten Nichtterminals ist somit die Herleitung aller Worte der Kernsprache ein- deutig.

Die Grammatik ist eindeutig.

In Anwendung ist es nat¨urlich m¨oglich, weitere Leerzeichen oder Klammern zu verwenden oder auch Klammern wegfallen zu lassen. Man beachte aber, dass nach einem Funktionsaufruf kein Leerzeichen gesetzt werden darf. Ansonsten kann a (b c)sowohl als normaler Funktionsaufruf wiea(b c), aber auch alslazy-order Aufruf von(a (b c))verstanden werden.

F¨ur fehlende Klammern bei einem SP ACE Ausdruck gilt, dass so geparst wird, als seien die Klammern so weit wie m¨oglich voneinander entfernt.

a b c d() wird also zu (a b c d()) und nicht etwa zu (a b c d)()

Des Weiteren ist die Grammatik auch eindeutig, wenn bei der Herleitung aus LIN ES auf das letzte LIN E kein ; folgt. F¨ur die operationale Semantik ist der Beweis der Eindeutigkeit aber einfacher, wenn immer ein ; erwartet wird. So ist n¨amlich offensichtlicher, ob ein Wort ausLIN ES oderLIN Ehergeleitet wurde.

(20)

2.3 Semantik

F¨ur die formale Beschreibung von Semantiken gibt es verschiedene Ans¨atze. Die folgenden drei sind am meisten verbreitet (siehe [6]):

1. Dieaxiomatische Semantik verwendet logische Aussagen um ein Programm zu beschreiben.

Ein Programm besitzt Axiome und der Programmablauf wird durch Schlussfolgerungen beschrieben.

2. Diedenotationale Semantikverwendet mathematische R¨aume um ein Programm zu beschrei- ben. Der Ablauf des Programms sind dann Funktionen, die R¨aume in andere R¨aume ¨uberge- hen lassen.

3. Dieoperationale Semantikdefiniert m¨ogliche Auswertungsschritte des Programms. Der Pro- grammablauf ist dann die Ausf¨uhrung solcher Schritte. Es wird des Weiteren zwischen big-step undsmall-step unterschieden. Small-step definiert kleine Auswertungsschritte, die nacheinander ausgef¨uhrt werden. Big-step hingegen wertet das gesamte Programm durch wenige (oder gar einen einzigen) großen Schritt ab.

F¨ur die Beschreibung der Semantik vonlazy Imp wird in dieser Arbeit dieoperationale Semantik verwendet. Dabei wird folgende Darstellung verwendet:

Bedingung

Aktueller Zustand =⇒ Folgezustand

Der Zustand gibt dabei die Variablenzuordnung und den aktuell zu betrachtenden Code an. Zum besseren Verst¨andnis hier ein einfaches Beispiel:

1 + 1 =⇒ 2

({}, a= 1 + 1) =⇒ ({a7→2})

Dieser Ausdruck beschreibt ”Wenn 1+1 zu 2 ausgewertet wird, wirda= 1 + 1 zu dem Zustand ausgewertet, bei dem die Variableazu 2 aufgel¨ost wird”. Meistens l¨asst sich diese Notation nicht nur als eine Implikation lesen, sondern auch als eine Folge von Ausf¨uhrungen. Der vorherige Aus- druck l¨asst sich auch wie folgt interpretieren: ”Uma= 1 + 1 auszuwerten, werte zuerst 1 + 1 aus.

Ordne das Ergebnis dann Variableazu.”

Der Unterschied zwischen small-step und big-step Semantik sollte durch folgendes Beispiel klar werden. Beide Regeln beschreiben die Abarbeitung mehrerer Zeilen:

(Z1, zeile1; ) =⇒ (Z2) (Z1, zeile1;rest; ) =⇒ (Z2, rest; ) (Z1, zeile1; ) =⇒ (Z2),(Z2, rest; ) =⇒ (Z3)

(Z1, zeile1;rest; ) =⇒ (Z3)

Die erste Variante ist einesmall-step Semantik. Sie formt den Code in einen neuen Code um, der dann durch die Anwendung weiterer Regeln weiter ausgef¨uhrt werden kann. Die zweite Variante hingegen wertet gleich den gesamten Ausdruck aus, indem die Auswertung vonrestals eine Art rekursiver Aufruf in der Bedingung geschieht.

Dieoperationale Semantik vonlazy Imp ist einebig-step Semantik.

(21)

2.3.1 Operationale Semantik

Der Zustand des Programms wird durch einen Stackν = (ν0, ..., νn) und die partiellen Funktionen νi und ρdargestellt. νi ist dabei die ite Funktion auf dem Stack, νn liegt ganz oben. F¨ur die Funktion gilt:

νi:V →R , dabei ist V die Menge der Variablen undR eine Referenzmenge.

R kann alsNbetrachtet werden und f¨ur die Speicheradresse von Werten stehen. Rdarf aber auch eine beliebige andere abz¨ahlbare Menge sein.

ρ:R→W , dabei ist W die Menge der Werte, also Funktionen und Objekte.

Anschaulich l¨ost ν die Variablen also zu einer Speicheradresse auf und ρ liest an einer gegebe- nen Speicheradresse den gespeicherten Wert.

Zur besseren Darstellung der operationalen Semantik werden noch einige Ausdr¨ucke und Funktio- nen definiert:

ν(v) :=νi(v), wobeii das gr¨oßtei, f¨ur dasνi(v) definiert ist.

νi

hv r i

:=νi0 mitνi0(w) :=

r fallsw=v νi(w) sonst νhv

r i:=

0, ..., νn−1, νn[vr]) fallsν(v) nicht definiert.

0, ..., νi−1, νi[vr], νi+1, ..., νn) mit gr¨oßtemi, f¨ur dasνi(v) definiert ist.

ρhr w i

:=ρ0 mitρ0(s) :=

w fallss=r ρ(s) sonst Des Weiteren gibt es einige interne Variablen:

jr , genau danntrue, wenn einreturnSprung ausgef¨uhrt wird.

jy , genau danntrue, wenn einyieldSprung ausgef¨uhrt wird.

jv , ist eine Globale Variable inν und enth¨alt den R¨uckgabewert vonreturnoderyield.

Der Zustand ist das Tupel σ= (ν, ρ, jy, jr). Falls jy oder jr w¨ahrend der Berechnung eines Teilausdruckstruewerden, bricht die Regel ab und gibt den zuletzt berechneten Zustand zusam- men mit dessenν(jv) als Ergebnis aus. Zur besseren Lesbarkeit wird diese Eigenart nicht in den unteren Regeln mit aufgef¨uhrt, entsprechend werden auch jyund jrim Zustand nicht angegeben, sofern eine Regel nicht in besonderer Weise auf diese Werte eingeht. H¨aufig werden auchν undρ nicht explizit angegeben und stattdessenσverwendet. In diesem Fall istσ(v) := (ρ◦ν)(v).

Um bei den Regeln nicht immer den Typ mit anzugeben, stehen folgende Metavariablen immer f¨ur bestimmte Typen. Mit Typ ist hierbei gemeint, ob es sich um eine Variable handelt, einen Wert oder ein Element ausR.

r steht immer f¨ur ein Element ausR.

v steht f¨ur einen Variablennamen, aber ohne daslocal oder $ Prefix.

w steht f¨ur einen Wert, also ein Element aus W. Im Falle der Kernsprache also eine Funktion.

e steht f¨ur ein beliebigen Ausdruck, genauer gesagt ein Programm - dazu gleich mehr.

Der Hauptgedanke der Auswertung ist, alles zu einem Referenzwert auszuwerten, damit dieser dann ggf. f¨ur eine Referenzzuweisung verwendet werden kann. Die Semantik vonlazy Imperlaubt deshalb auch Referenzwerte in ihren Programmen. Genauer definiert heißt das:

Das WortP nennen wir Programm, wenn es sich durch eines der Nichtterminale der Grammatik vonlazy Imp herleiten l¨asst, wobei diese um folgende Produktionsregeln erweitert wurde:

RVALUE → REF EREN CE REF EREN CE → ein Element ausR

Die Grammatik ist mit diesen ¨Anderungen offensichtlich weiterhin eindeutig, dennREF EREN CE kann ausschließlich zu Referenzwerten aufgel¨ost werden und Referenzwerte k¨onnen wiederrum nur

(22)

Um die ^v Ausdr¨ucke verarbeiten zu k¨onnen, wird die Funktion ζ eingef¨uhrt. Diese ersetzt in einem Ausdruck alle ^v, die im obersten Namensraum auftauchen, durchν(v). Formal bedeutet dies:

ζ(ν, e1=e2) =ζ(ν, e1)=ζ(ν, e2) ζ(ν, e1:=e2) =ζ(ν, e1):=ζ(ν, e2)

ζ(ν, e(e1, ..., en)) =ζ(ν, e)(ζ(ν, e1), ..., ζ(ν, en)) ζ(ν,(e1e2...en)) = (ζ(ν, e1)ζ(ν, e2)...ζ(ν, en))

ζ(ν, e1;...;en; ) =ζ(ν, e1);...;ζ(ν, en);

ζ(ν,^v) =ν(v) ζ(ν, v) =v ζ(ν,local v) =local v ζ(ν,do ... end) =do ... end

Manche Regeln der lazy Imp Semantik erfordern die Auswertung von Unterausdr¨ucken, deshalb ist folgende Regel vorhanden:

Referenz

(ν, ρ, r) =⇒ (ν, ρ, r)

Ein Referenzwert muss nicht ausgewertet werden, denn er ist bereits ausgewertet. Diese Regel ist nur vorhanden, um Unterausdr¨ucke, die nur ein Referenzwert sind, auswerten zu k¨onnen.

2.3.2 Variablen

Kopiezuweisung

KZuweisung (ν, ρ, e2) =⇒ (ν0, ρ0, r2),(ν0, ρ0, e1) =⇒ (ν00, ρ00, r1), ρ00(r2) =w (ν, ρ, e1=e2) =⇒ (ν00, ρ00[rw1], r1)

Diese Regel beschreibt, dass zuerst die rechte Seite der Zuweisung ausgef¨uhrt wird. Danach wird die linke Seite ausgef¨uhrt undρso ver¨andert, dass die linke Speicheradresse auf dem gleichen Wert wie die rechten Speicheradresse zeigt.

Referenzzuweisung

RZuweisung (ν, ρ, e) =⇒ (ν0, ρ0, r) (ν, ρ, v:=e) =⇒ (ν0[vr], ρ0, r)

RZLokal

(ν, ρ, e) =⇒ (ν0, ρ0, r) νn0(v) nicht definiert, ν00= (ν00, ..., νn−10 , νn0[vr]) (ν, ρ,local v:=e) =⇒ (ν00, ρ0, r)

RZGlobal

(ν, ρ, e) =⇒ (ν0, ρ0, r) ν00= (ν00[$vr], ν01, ..., νn−10 ,) (ν, ρ,$v:=e) =⇒ (ν00, ρ0, r)

Hier wird ebenfalls die rechte Seite zuerst ausgef¨uhrt und der resultierende Referenzwert der Variable auf der linken Seite zugeordnet.

(23)

return, yield

Return (ν, ρ, e) =⇒ (ν0, ρ0, r)

(ν, ρ,return e) =⇒ (ν0[ jvr ], ρ0, true, true, r)

Yield (ν, ρ, e) =⇒ (ν0, ρ0, r)

(ν, ρ,return e) =⇒ (ν0[ jvr ], ρ0, true, f alse, r)

Return und Yield werten den darauf folgenden Ausdruck aus und speichern ihn unter jv. Des Weiteren wird jrbeziehungsweise jy gesetzt, damit der restliche Code ¨ubersprungen wird.

Auslesen

VarDekl

ν(v) nicht definiert, ρ(r) nicht definiert (ν, ρ, v) =⇒ (ν[vr], ρ[nilr ], r)

LokalVarDekl

νn(v) nicht definiert, ρ(r) nicht definiert, ν0= (ν0, ..., νn−1, νn[vr]) (ν, ρ,local v) =⇒ (ν0, ρ[nilr ], r)

GlobalDekl

ν0($v) nicht definiert, ρ(r) nicht definiert, ν0 = (ν0[$vr], ν1, ..., νn) (ν, ρ,$v) =⇒ (ν0, ρ0[nilr ], r)

Sollte die Variable noch nicht deklariert sein, wird sie angelegt und mit einer Referenz auf eine neuenil-Kopie initialisiert. Da es in der Kernsprache bis auf Funktionen noch keine Objekte gibt, ist mitnilerst einmal die Funktiondo yield nil; endgemeint. Sp¨ater istnilein eigenst¨andiges Objekt.

Var ν(v) =r (σ, v) =⇒ (σ, r)

GlobalVar ν0($v) =r (σ,$v) =⇒ (σ, r)

Sind die Variablen hingegen deklariert, werden sie einfach ¨uber dieν Funktion ausgelesen.

(24)

2.3.3 Funktionen

Definition

FErzeugung ρ(r) nicht definiert, ζ(ν, e2) =e0 (ν, ρ,do [catch] e1 -> e2 end) =⇒ (ν, ρ[ r

do [catch]e1 -> e0 end], r) e1 kann hierbei mehrere oder auch keine Argumente repr¨asentieren.

Im Fallee1=ist mit ”doe1->e2 end” ein ”doe2 end” gemeint.

Diese Regel legt ein neues Funktionsobjekt an und gibt den neuen Referenzwert auf die Funktion zur¨uck.

FVerz¨ogert

(ν, ρ, e0) =⇒ (ν0, ρ0, r)

ρ0(r) =do [catch]v1[:], ..., vn[:] -> e end

0, ρ0, e1) =⇒ (ν1, ρ1, r1)...(νk−1, ρk−1, ek−1) =⇒ (νk, ρk, rk)

k, ρk,do vk+1:, ..., vn:-> yield r(r1, ..., rk, vk+1, ..., vn) end) =⇒ (ν00, ρ00, r00)) (ν, ρ, (e0 e1e2...ek)) =⇒ (ν00, ρ00, r00)

Dieser Schritt mag etwas unintuitiv wirken, denn dieei werden ausgewertet, obwohl diese Schreibweise f¨urlazy-order steht. Die Auswertung hat aber einen guten Grund:

fun fun2(a,b)soll fun2 direkt ausf¨uhren undfunverz¨ogert auswerten. Soll der gesamte Ausdruck verz¨ogert ausgewertet werden, verwendet man stattdessen:

fun (fun2 a b) Aufruf

FAusf¨uhrung

(ν, ρ, e0) =⇒ (ν0, ρ0, r)

ρ0(r) =do [catch]v1[:], ..., vn[:] -> e end,

0, ρ0, e1) =⇒ (ν1, ρ1, r1), ...,(νn−1, ρn−1, en) =⇒ (νn, ρn, rn) νpush= ((νn)0, ...,(νn)m,{}),

push, ρn,local v1[:] =r1) =⇒ (ν01, ρ01), ...,

0n−1, ρ0n−1,local vn[:] =rn) =⇒ (ν0n, ρ0n) (ν0n, ρ0n, e) =⇒ (ν00, ρ00, jy, jr, r00), νpop= (ν000, ..., νm00)

(ν, ρ, e0(e1, ...en)) =⇒ (νpop, ρ00, f alse, jr, ν0000(jv))

Dieser Regel beschreibt, dass zuerst die Argumentausdr¨ucke nacheinander ausgewertet werden.

Danach wird ein neuer Namensraum auf den Stack gelegt und die Argumentsvariablen belegt.

Zuletzt wird dann der Funktionsrumpf ausgef¨uhrt und der neue Namensraum wieder vom Stack entfernt.

Sollte die Funktion einencatch modifier haben, so wird auch jrim Ergebnis auf f alsegesetzt.

Wird jr bei der Auswertung eines local vi = ri zu true, so wird hier nicht wie bei anderen semantischen Regeln direkt abgebrochen! Stattdessen wird trotzdem νpop = (ν0, ..., νm) f¨ur den zuletzt erhaltenen (ν, ρ, jy, true) Status angewendet und stattdessen (νpop, ρ, jy, true, vpop(jv)) zur¨uckgegeben.

Diese Regel l¨asst sich wie FErzeugung f¨ur n = 0 auch auf Funktionen, die kein Argument ben¨otigen, anwenden.

Zeilen

FAufImplizitA

e1=do [catch] e0 end,(σ, e1) =⇒ (σ0, r1), (σ0, r1()) =⇒ (σ00, r2)

00, e2;...;en) =⇒ (σ000, r3) (σ, e1;e2;...;en; ) =⇒ (σ000[rjv

3], r3)

e1 kann hierbei mehrere oder auch keine Argumente repr¨asentieren.

Im Fallee1=ist mit ”doe1->e2 end” ein ”doe2 end” gemeint.

Zum Schluss der Auswertung wird das zuletzt erhaltene Ergebnis der jv Variable zugewiesen, sodass Funktionen ohneyieldoderreturn, den zuletzt berechneten Wert zur¨uckgeben.

(25)

FAufImplizitB

e1= (e01 e02 ... e0n), (σ, e1) =⇒ (σ0, r1), (σ0, r1()) =⇒ (σ00, r2) (σ00, e2;...;en) =⇒ (σ000, r3) (σ, e1;e2;...;en; ) =⇒ (σ000[rjv

3], r3)

FAufImplizitC

e1=v odere1= $v, (σ, e1) =⇒ (σ0, r1), (σ0, r1()) =⇒ (σ00, r2) (σ00, e2;...;en) =⇒ (σ000, r3) (σ, e1;e2;...;en; ) =⇒ (σ000[rjv

3], r3)

FAufImplizitD

ρ(r) =do [catch] e end, (σ0, r1()) =⇒ (σ00) (σ00, e2;...;en) =⇒ (σ000, r3) (σ, r1;e2;...;en; ) =⇒ (σ000[rjv

3], r3)

Diese Regeln besagen, dass zuerst die erste Zeile des Programms ausgef¨uhrt wird. Sollte diese Zeile eine Funktion ohne Argumente zur¨uckliefern, so kann diese direkt ausgef¨uhrt werden. Be- dingung daf¨ur ist, dass die ausgef¨uhrte Zeile einF U N,SP ACE,VARoder^VARPART Ausdruck war.

F¨ur alle anderen Worte ist folgende Regel wichtig:

MultiLine

FAufImplizitA-FAufImplizitDlassen sich nicht anwenden, (σ, e1) =⇒ (σ0, r1)

0, e2;...;en; ) =⇒ (σ00, r2) (σ, e1;...;en; ) =⇒ (σ00[rjv

2], r2)

Diese Regel f¨uhrt einfach nur die erste Zeile und danach das Restprogramm aus.

2.3.4 Beispiele

Das erste Beispiel soll die Funktionsweise von jrund jy verdeutlichen:

Multiline Yield

Var ν(a) = 1

(ν = ({a7→1)}), ρ,a) =⇒ (ν= ({a7→1)}), ρ,1)

(ν= ({a7→1)}), ρ,yield a) =⇒ (ν = ({a7→1, jv7→1}), ρ, true, f alse,1) (ν= ({a7→1)}), ρ,yield a; b;) =⇒ (ν= ({a7→1, jv7→1}), ρ, true, f alse,1) Wie inMultiLinebeschrieben, wird zuerst die erste Zeile ausgef¨uhrt. Danach sollten eigentlich die restlichen Zeilen ausgef¨uhrt werden, aber das Zwischenergebnis hat f¨ur jy den Wert true.

Entsprechend wird die Regel abgebrochen und dieses Zwischenergebnis zur¨uckgegeben.

Das n¨achste Beispiel geht etwas n¨aher auf die Kopiezuweisung und Deklaration von Variablen ein.

(26)

2 Kernsprache

ManbeachtedasKommaamEndederoberstenReduktion!DiebeidenoberenAuswertungensindbeideTeilderBedingungf¨urdiedritteReduktion. KZuweisung

FErzeugungρ(1)nichtdefiniert,ζ(ν,yieldy;)=yieldy; (ν=({}),ρ={},dox:,y:->yieldy;end)=⇒(ν=({}),ρ={17→dox:,y:->yieldy;end},1) VarDeklρ(2)nichtdefiniert (ν=({}),ρ={17→dox:,y:->yieldy;end},a)=⇒(ν={a7→2},ρ={17→dox:,y:->yieldy;end, 27→dox:->yieldnil;end} w=ν(2)=dox:,y:->yieldy;end (ν=({}),ρ={},a:=dox:,y:->yieldy;end)=⇒(ν=({a7→2}),ρ={17→dox:,y:->yieldy;end, 27→dox:,y:->yieldy;end},2) (ν=({}),ρ={},a=dox:,y:->yieldy;end;)=⇒(ν=({a7→2, jv7→2}),ρ={17→dox:,y:->yieldy;end, 27→dox:,y:->yieldy;end}) WahlderverwendetensemantischenRegelnl¨asstsichauchineinemBaumdarstellen.DabeiistjedeverwendeteRegeleinKnotenunddie dieinderBedingungverwendetwurden,sinddieKinder. Semantikbaumf¨urdasvorherigeBeispielsiehtzumBeispielwiefolgtaus: MultiLine KZuweisung FErzeugungVarDekl ArtderDarstellungfunktioniertnichtbeismall-stepSemantiken.BeidiesenSemantikenk¨onnennacheinandermehrereRegelnangewendet erden,umProgrammeSchrittf¨urSchrittumzuformenundsoeinenAusdruckauszuwerten.DieKindereinesKnotenm¨usstenalsoeineFolgevon sein,wobeijedeRegelwiederFolgenvonRegelnalsKinderbesitzt. derSemantikvonlazyImpfunktioniertdieseDarstellung,daimmernureineRegelf¨urdieAuswertungeinesAusdrucksangewendetwird.Alle eiterenRegeln,diezurAuswertungverwendetwerden,werdenindenBedingungenausgef¨uhrt-siesindalsowiederumKinderdervorherigenRegel keineFolgenglieder. dieseArtderDarstellungf¨urdieSemantikvonlazyImpm¨oglichist,istf¨ursp¨atereInduktionsbeweisenachderTiefediesesBaumesn¨otig.

(27)

2.3.5 Eindeutigkeit der Semantik

Definition und Vor¨uberlegung

Definition 2: Eindeutigkeit einer Semantik.

Eine Semantik heißt eindeutig, wenn f¨ur jeden ZustandZ und jedes ProgrammP gilt:

(Z, P) =⇒ (ZA, PA) und (Z, P) =⇒ (ZB, PB) dann gilt (ZA, PA) = (ZB, PB)

Anders ausgedr¨uckt bedeutet dies, dass f¨ur jedes (Z, P) maximal ein m¨ogliches Ergebnis der Auswertung existiert.

Dies ist f¨ur die Semantik von lazy Imp nicht gegeben, denn manche Regeln sind nicht deter- ministisch. Die Bedingung ”ρ(r) ist nicht definiert” in den RegelnVarDekl, LokalVarDekl, GlobalVarDekl und FErzeugung schreibt n¨amlich nicht vor, wie das r ∈ R zu w¨ahlen ist.

Entsprechend k¨onnen zwei Auswertungen des Programms verschiedenerw¨ahlen, sodass die Funk- tionenν undρnicht eindeutig festgelegt sind.

Dies ist nicht weiter schlimm, bedenkt man, dass R effektiv die Menge der Speicheradressen repr¨asentiert. Es ist nicht wichtig, wo im Speicher ein Wert gespeichert wird, es ist nur wichtig, welche Variablen vonν zur gleichen Adresse aufgel¨ost werden und dassν◦τ sich unabh¨angig von der Wahl der Speicheradresse, gleich verh¨alt.

Da der Speicherort eines Wertes nicht wichtig ist, kann man den Begriff der Eindeutigkeit f¨ur lazy Imp ein wenig verallgemeinern. Wir wollen die Auswertung eindeutig nennen, wenn sich Zust¨ande und resultierendes Programm nur durch die Referenzwerte unterscheiden. Des Weiteren soll man diesen Unterschied durch eine Neuordnung der Referenzwerte (Neuordnung des Speichers) aufgehoben werden. Dazu definieren wir zun¨achst folgende Notation:

Definition 3: Seiβ:R→Reine Funktion undP ein Programm. Dann istβ[P] das Programm, bei dem alle ReferenzwerterinP durchβ(r) ersetzt wurden. Formal istβ[·] also definiert als:

β[e1=e2] =β[e1]=β[e2] β[e1:=e2] =β[e1]:=β[e2] β[e(e1, ..., e2)] =β[e](β[e1], ..., β[e2]) β[(e1 e2...en)] = (β[e1] β[e2]...β[en])

β[e1;...;en; ] =β[e1];...;β[en];

β[^v] =^v β[v] =v β[r] =β(r) β[local v] =local v

β[do e1 -> e2 end] =do e1 -> β[e2] end Seif eine Funktion, dann istβ[f](x) :=β[f(x)], das heißtβ[f] =β[·]◦f. Darauf basierend l¨asst sich folgende ¨Aquivalentsrelation definieren:

Definition 4: semantisch ¨aquivalent,≡⊂ {(ν, ρ, P)}2A, ρA, PA)≡(νB, ρB, PB) :⇔

Es existiert eine Bijektionβ :R→Rmit

1. β◦νiAiB f¨ur allei(also f¨ur alle Namensr¨aume), 2. β[ρA] =ρB◦β und

(28)

Satz 2: ≡ist eine ¨Aquivalenzrelation.

1. Reflexiv:

F¨urβ =idgilt

β◦νii f¨ur alle partielle Funktionenνi:V →R, β[P] =P f¨ur alleP und somit auch

β[ρ] =ρ=ρ◦β f¨ur alleρ.

⇒F¨ur alle (ν, ρ, P) gilt also (ν, ρ, P)≡(ν, ρ, P).

2. Symmetrisch:

Sei (νA, ρA, PA)≡(νB, ρB, PB) undβ die entsprechende Bijektion, dann gilt f¨ur α=β−1: νiA=α◦β◦νiA=α◦νiB,

α[β[P]] =P f¨ur alleP, dennα[β[r]] =α(β(r)) =rf¨ur aller∈R. Somit gilt auch α[ρB](r) =α[β[ρA]](r) =α[β[ρA(r)]] =ρA(r)

⇒Es gilt (νB, ρB, PB)≡(νA, ρA, PA) 3. Transitiv:

Sei (νA, ρA, PA)≡(νB, ρB, PB) mitβ und (νB, ρB, PB)≡(νC, ρC, PC) mit γ, dann gilt f¨urα=β◦γ:

α◦νiA =β◦γ◦νiA=β◦νiBiC, α[ρA] =β[γ[ρA]] =β[ρB] =ρC, α[PA] =β[γ[PA]] =β[PB] =PC

⇒(νA, ρA, PA)≡(νC, ρC, PC)

Definition 5: zul¨assiger auszuwertender Ausdruck

Ein Tupel (ν, ρ, ω) nennen wir einen zul¨assigen auszuwertenden Ausdruck, wenn gilt:

1. Bild(ν)⊂Def(ρ)

2. Alle Referenzwerter, die in Worten aus Bild(ρ) auftauchen, liegen inDef(ρ).

3. Alle Referenzwerter, die inω auftauchen, liegen inDef(ρ).

Mit anderen Worten: (ν, ρ, ω) enth¨alt keine Referenzwerte, die nicht aufgel¨ost werden k¨onnen.

Definition 6: erweiterte Eindeutigkeit

Die Semantik vonlazy Imp heißt erweitert eindeutig, wenn gilt:

F¨ur alle zul¨assigen auszuwertenden Ausdrucke (ν, ρ, ω) und zwei endliche Auswertungen (ν, ρ, P) =⇒ (νA, ρA, PA) und

(ν, ρ, P) =⇒ (νB, ρB, PB)

=⇒(νA, ρA, PA)≡(νB, ρB, PB)

Die erweiterte Eindeutigkeit ist also die klassische Eindeutigkeit bez¨uglich der semantischen ¨Aqui- valenz (≡) f¨ur endliche Auswertungen.

Die Einschr¨ankung auf endliche Auswertungen ist wichtig, f¨ur unendliche Auswertungen ist lazy Imp tats¨achlich nicht eindeutig. Man betrachte z.B. ein Programm, dass unendliche lange l¨auft und einfach nur Speicher allokiert (man betrachte R = N). Einerseits kann der Speicher

”nach” der Ausf¨uhrung als voll betrachtet werden, wenn bei der i-ten Allokation ρ(i) definiert wird. Es gilt also f¨ur alle r∈Rdassr∈Def(ρ). Andererseits k¨onnte bei der Ausf¨uhrung auch f¨ur diei-te Allokationρ(2i) definiert werden, sodass nochr∈R existieren mitr /∈Def(ρ).

(29)

Eindeutigkeit von lazy Imp

Um die Eindeutigkeit der Semantik zu beweisen, wird zuerst folgende ¨Uberlegung gemacht:

Wir erlauben Referenzwerte als Teil der Syntax, das heißt man f¨ugt folgende Produktionsregeln der Grammatik hinzu:

RVALUE → REF EREN CE REF EREN CE → ein Element ausR

Dann lassen sich die Auswertungsregeln der Semantik immer nur auf bestimmte Worte der Gram- matik anwenden.

Auswertungsregeln Nichtterminal

Referenz REF EREN CE

KZuweisung COP Y

RZuweisung,RZLokal,RZGlobal ASSIGN

Return, Yield RET U RN

VarDekl, LokalVarDekl, VAR Var,LokalVar,

GlobalVar,GlobalVarDekl,

FErzeugung F U N

FVerz¨ogert SP ACE

FAusf¨uhrung CALL

FAufImplizitA,FAufImplizitB, LIN ES FAufImplizitC,FAufImplizitD,

MultiLine

Umgekehrt lassen sich die Worte, die von obigen Nichtterminalen herleiten lassen, nur von den entsprechenden Regeln auswerten.

Es gibt aber auch kein Wort, das durch mehr als eines dieser Nichtterminale hergeleitet werden kann - ansonsten w¨are die Grammatik n¨amlich nicht eindeutig.

Seiω ein Wort, dass sich von mindestens zwei der obigen Nichtterminale (ausgenommenLIN ES) herleiten l¨asst, dann g¨abe es f¨urω mehr als eine m¨ogliche Herleitung ausgehend von LIN E.

Mindestens zwei der folgenden Herleitungsanf¨ange m¨ussten dannω erzeugen k¨onnen:

LIN E→COP Y; LIN E→RVALUE;→F U N;

LIN E→RET U RN; LIN E→RVALUE;→VAR;

LIN E→ASSIGN; LIN E→RVALUE;→SP ACE;

LIN E→RVALUE;→CALL;

LIN E→RVALUE;→REF EREN CE;

Das ist ein Widerspruch zum Beweis der Eindeutigkeit der Grammatik. Dort wurde gezeigt, dass f¨ur ein beliebiges festes Startnichtterminal die Herleitung eindeutig ist.

Worteω, die sich ausLIN ESherleiten lassen, k¨onnen nicht aus anderen Nichtterminalen hergeleitet werden, da nurLIN ESdie ; Terminale erzeugt.

(30)

Offensichtlich gilt weiterhin, dass aus den Gruppen von Regeln, die f¨ur ein Nichtterminal anwend- bar sind, f¨ur jedes Wort immer maximal eine dieser Regeln anwendbar ist.

Insgesamt bedeutet dies, das f¨ur jedes auszuwertende Wort, die erste anzuwendende Regel ein- deutig vorgegeben ist.

Es liegt nahe, daraus direkt die (erweiterte) Eindeutigkeit der Semantik zu folgern, schließlich ist die zu verwendende Regel fest vorgegeben. Es ist aber nicht gezeigt, dass das auszuwertende Wort eindeutig ist. In den Bedingungen werden z.B. Teilworte ausgewertet, die Wahl dieser Teilworte muss auch eindeutig bestimmt sein.

Es bleibt also noch mehr zu beweisen.

Satz 3: Verwendet man bei VarDekl, LokalDekl, GlobalVarDekl und FErzeugung statt ”ρ(r) nicht definiert” die Bedingung ”kleinstesrmitρ(r) nicht definiert”, so ist die Seman- tik eindeutig bez¨uglich einer Wohlordnung<aufR.

R ist eine abz¨ahlbare Menge und somit wohl geordnet. Es existiert oBdA eine totale Ordnung, bei der jede nicht-leere Menge ein kleinstes Element existiert. Als eine abz¨ahlbare Menge existiert eine Bijektionf von Rauf eine Teilmenge von Nund somit l¨asst sich eine solche Ordnung durch r1< r2:⇐⇒f(r1)< f(r2) induzieren.

Beweis zu Satz 3: Der Beweis erfolgt per Induktion nach der Tiefe des Semantikbaumes. Es wird f¨ur Auswertungen, deren Semantikbaum Tiefenbesitzt, gezeigt, dass sie die einzige m¨ogliche Auswertung f¨ur ihren Startzustand und ihr Startwort sind.

Induktionsanfang: n= 0

Sei (ν, ρ, ω) =⇒ (ν0, ρ0, ω0) eine Auswertung dessen Semantikbaum die Tiefe 0 besitzt. Dann war die Auswertung eine der folgenden Regeln:

1. Reference

Da nichts ge¨andert wird, ist das Auswertungsergebnis offensichtlich eindeutig.

2. Var

Diese Regel l¨asst sich nicht gleichzeitig mit VarDekl anwenden, denn f¨urVar mussν(r) definiert sein, f¨urVarDekl hingegen nicht.

Da ν und v = ω vorgegeben sind, ist r = ν(v) eindeutig bestimmt. Somit ist (ν, ρ, r) eindeutig bestimmt.

3. VarDekl

Als wohlgeordnete Menge, besitzt jede nichtleere Teilmenge vonRein eindeutig bestimmtes kleinstes Element. Somit existiert ein eindeutig bestimmtes r unter allen r ∈ R mit ρ(r) nicht definiert. (ν, ρ, v) ist gegeben und somit (ν[vr], ρ[nilr ], r) eindeutig bestimmt.

4. LokalVar

Diese Regel l¨asst sich nicht gleichzeitig mitLokalVarDeklanwenden, denn f¨urLokalVar mussν(r) definiert sein, f¨urLokalVarDeklhingegen nicht.

ν und somitνn sind gegeben. v ist ebenfalls gegeben, da es sich einfach nur um das Suffix des auszuwertenden Wortes handelt. Somit muss auch r=νn(v) eindeutig bestimmt sein, weshalb es nur ein m¨ogliches Ergebnis (ν, ρ, r) der Auswertung gibt.

5. LokalVarDekl

r ist wie in VarDekl eindeutig festgelegt. Der Stack ν und somit die Funktionen νi sind gegeben, sodass ν0 eindeutig ist. Entsprechend ist (ν0, ρ[nilr ], r) eindeutig bestimmt.

6. GlobalVar

Analog zuLokalVarnur mit Index 0 anstelle vonn.

Referenzen

ÄHNLICHE DOKUMENTE

Dennoch steht für O RIGENES das JohEv dem &#34;ewigen Evangelium&#34; näher als die Synoptiker, schon dadurch deutlich, daß er hier gerade Johannes zitiert (wenngleich

gebers, der von seinen Brüdern seines Erbes bcranbt wird, sich erhallen habe.&#34; Dabei verhehlt sicb aber Lassen die Schwierigkeit nichl, welche darin Hegt , dass dieser Sohn

Ich bin für einige Tage in Bagdad , um meine Reise nach dem Süden Chaldäa's, nach Niffer und Warkn , vorzubereiten. In Bagdad bat man wenigstens nocb einige

Falls ein λ-Term M eine β-Normalform hat, dann terminiert jede mit M beginnende L-Reduk- tionsfolge (und damit auch jede

§ Anfrage eines Kunden nach einem Kredit bestimmter Höhe. § Ziel: Entscheidung, ob Kredit gewährt

In dieser Aufgabe soll gezeigt werden, dass minimale NEAs nicht eindeutig sind (bis auf Isomorphie, d.h. bis auf Umbenennung ihrer Zust¨ande). Sei L = {a} · {b} + .?. a) Geben Sie

Die Wissenserwerbsprozesse beim Lernen aus Programmen, wie sie in der &#34;zweiten Stunde&#34; beim Lernen von LISP auftreten, können nun für Anfänger und Fortge- schrittene wie

Um dies zu gewährleisten wurden für dieses Modul Arbeitsblätter entwickelt, die die Schülerinnen und Schüler durch die einzelnen Phasen führen sollen.. Der