Programmanalyse

Werbung
Vorlesungsskript
Programmanalyse
Prof. Dr. phil. Dr. rer. nat. habil. Michael
Schenke
Version vom: 15.05.2016
1
Inhaltsverzeichnis
Inhaltsverzeichnis ................................................................................... 1
Vorwort ................................................................................................. 2
1
2
3
4
5
Einführung....................................................................................... 4
1.1
Allgemeines ............................................................................... 4
1.2
Syntax und Semantik .................................................................. 5
Semantik von Programmen ................................................................ 6
2.1
Einführung................................................................................. 6
2.2
Operationelle Semantik ............................................................... 7
2.3
Substitutionslemma .................................................................... 9
Kalküle.......................................................................................... 10
3.1
Einführung................................................................................ 10
3.2
Hoaresche Kalküle (HK).............................................................. 13
3.3
Vollständigkeit von PD und TD .................................................... 14
3.4
Beweisskizzen zur Korrektheit von PD .......................................... 16
Compilerbautechniken ..................................................................... 21
4.1
Parsingtechniken ....................................................................... 21
4.2
Vermeiden von Backtracking ....................................................... 26
4.3
Attributierte Grammatiken .......................................................... 27
4.4
Rekursiver Abstieg ..................................................................... 30
Praktikum ...................................................................................... 32
2
Vorwort
Im Rahmen der Lehrveranstaltung Programmanalyse erlernen Sie Standardalgorithmen für Hoaresche Logik und erhalten eine knappe Einführung in die
Welt des Compilerbaus.
Die Korrektheitsbeweise stehen im theoretischen Teil im Vordergrund. Dabei
vertiefen und erweitern Sie ihr Wissen in Themen wie Syntax und Semantik
und erlernen völlig Neues, wie z. B. die Hoaresche Logik. Themen aus der
Theoretischen Informatik finden ihre Anwendung, somit wird der Kreis zum
Grundlagenstudium
geschlossen.
Im
Grundlagenstudium
erlerntes
theoretisches Wissen wird hier praxisnah angewendet.
Ein wesentlicher Bestandteil dieser Veranstaltung ist praktisch. Dafür wird ein
elementares System für Korrektheitsbeweise implementiert. Es werden
Theorie und Praxis verschmolzen.
!
ACHTUNG
Das Skript ist kein Ersatz zur Vorlesung. Es soll Sie lediglich
unterstützen und es Ihnen ermöglichen, die Vorlesung
aufmerksam zu verfolgen.
3
4
1
Einführung
Dieses Kapitel gibt Ihnen eine Einleitung in die Programmanalyse. Sie
wiederholen Ihr Wissen über Syntax und Semantik.
1.1
Allgemeines
Die Programmanalyse beschäftigt sich mit der (möglichst automatisierten)
Analyse des Verhaltens von Computerprogrammen. Eine besondere Rolle
spielt dabei die Korrektheit eines Programmes.
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..1:
Ein Programm wird als korrektes Programm bezeichnet, wenn es
seine Spezifikation (Anforderungen) erfüllt.
Diese Definition ist zunächst noch wenig aussagekräftig: Es fehlen noch
formale Definitionen einer Spezifikation und des Erfüllungsbegriffs. Um diese
Begriffe konkret zu fassen und eine solche Korrektheit nachzuweisen,
verwenden wir später die Hoaresche Logik und ihre Kalküle.
Ein Programm steht also immer im Zusammenhang mit einer Spezifikation.
Aus Sicht des Programmes ist die Spezifikation ein formales Modell, nach dem
es sich auszurichten gilt.
Im allgemeinen wird dazu der Weg über die Semantik gegangen. Der
Spezifikationssprache sei eine Semantik durch eine Funktion ⟦ . ⟧ gegeben, der
Spezifikation S also der semantische Wert ⟦𝑆⟧; entsprechend gehöre zur
Programmiersprache die Semantikfunktion 𝑀⟦ . ⟧, zum Programm P also der
semantische Wert 𝑀⟦𝑃⟧. Der Definitionsbereich der semantischen Funktion ist
jeweils die Menge der syntaktisch korrekt gebildeten
Spezifikationen/Programme. Die Wertebereiche können von Sprache zu
Sprache verschieden sein; normalerweise sind es Mengen oder Formeln. In
Abhängigkeit von dieser Wahl muss es dann eine Implementierungsrelation
geben, die aussagt, was ⟦ 𝑆⟧ mit 𝑀⟦𝑃⟧ zu tun haben muß, damit gesagt
werden kann, P implementiere S. Beispielsweise könnte im Falle logischer
Formeln die Implikation gefordert werden:
𝑀 ⟦ 𝑃⟧ → ⟦𝑆⟧.
Programm
Spezifikation
[[S]]
M[[P]]
5
Abbildung Error! Use the Home tab to apply Überschrift 1 to the text that you want to
appear here..1: Zusammenhang Spezifikation und Programm.
6
1.2
Syntax und Semantik
Um ein besseres Verständnis zu entwickeln, betrachten wir zunächst folgende
Grammatik:
Prog →
Zuweisung →
Prog;Prog | SKIP | Zuweisung | IF bed THEN Prog
{ ELSE Prog } FI | WHILE bed DO Prog OD
var = exp
bed →
element | bed && bed | bed || bed | !bed
exp →
var | zahl | exp op exp | ( exp )
element →
exp vglop exp
op →
+ | - | * | /
vlgop →
== | != | > | >= | < | <=
Beispielgrammatik. Terminale sind blau eingefärbt.
Unsere Grammatik bildet bereits eine kleine Sprache ab. Mithilfe dieser
Sprache ist es uns möglich, Programme wie z. B. ein Fakultätsprogramm zu
schreiben.
Im wesentlichen hat jede Sprache zwei elementare Bestandteile. Einmal ist
der Aufbau eines Satzes klar definiert. Es existieren Strukturregeln, die
beschreiben, wie die Wörter zu Sätzen aneinandergereiht werden können. Des
Weiteren steht hinter jedem Wort und jedem Satz eine Bedeutung.
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..2:
Die Regeln für die formale Struktur (Satzbau), die durch die
Grammatik der entsprechenden Sprache festgelegt sind, werden als
Syntax einer Sprache bezeichnet.
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..3:
Die Semantik steht für die Bedeutung eines Wortes bzw. Satzes.
7
2
Semantik von Programmen
Hier erweitern und vertiefen Sie Ihr Wissen über die Semantik.
2.1
Einführung
Wir bezeichnen 𝑀⟦𝑃⟧ als Semantik des Programms 𝑃.
𝑀⟦𝑃⟧(𝜎) = 𝜏
𝜎 – Variablenbelegung am Anfang
𝜏 – Variablenbelegung am Ende
Σ - Menge aller Belegungen
𝑀⟦𝑃⟧: Σ ↪ Σ
Die genaue Definition dieser Funktion stellt ein Problem dar. Es existieren
hierzu zwei Hauptmethoden:


denotationelle Semantik
operationelle Semantik
S
P
denot.
[[S]]
dichter an der Spezifikation
dichter an der Maschinensprache (MS)
MS
operat.
[[P]]
[[MS]]
Die Semantik der Sprachen könnte im Prinzip durch Induktive Konstruktion
definiert werden. Eine Ausnahme bilden allerdings Programmschleifen.
Ein Programm P ist syntaktisch aus kleinen Teilen aufgebaut, deren Semantik
ist durch Induktion bekannt. Es wird versucht, die Semantik von P aus der
Semantik der Programmteile zu berechnen. Dies ist (unter anderem) die Idee
der denotationellen Semantik.
Die mathematische Finesse der denotationellen Semantik ist beträchtlich. Aus
diesem Grund beschäftigen wir uns nachfolgend intensiver mit der
operationellen Semantik und lassen die denotationelle außen vor.
8
2.2
Operationelle Semantik
Wir führen eine neue Dimension ein, die Abarbeitungsreihenfolge der
einzelnen Schritte. Im Grunde handelt es sich dabei um die Einführung einer
rudimentären Form der Zeit, die allerdings nicht weiter quantifiziert wird. Zu
jedem Zeitpunkt wird der Prozess beschrieben durch eine Konfiguration. Das
ist ein Paar (𝑃, 𝜎) mit:


𝑃 ist das noch abzuarbeitende Restprogramm.
𝜎 ist die zum gegebenen Zeitpunkt gültige Belegung der Variablen.
Damit gilt:

Am Anfang ist 𝑃 das gesamte Programm;

Zustand.
Am Ende ist das Programm abgearbeitet; die Endkonfiguration ist
𝜎 ist der am Anfang gültige
(𝐸, 𝜏).
Aus formalen Gründen wird hier die Einführung von E nötig. Dieses bezeichnet
aus Sprachsicht das leere Wort, hier also das leere Programm.
Für einen Beispielablauf (𝑃, 𝜎) → (𝑃1 , 𝜎1 ) → (𝑃2 , 𝜎2 ) → ⋯ → (𝑃𝑛 , 𝜎𝑛 )
Ergebnis 𝜎𝑛 , falls 𝑃𝑛 = E gilt. Das heißt, dann gilt 𝑀⟦𝑃⟧(𝜎) = 𝜎𝑛 .
!
ist
das
Beispiel Error! Use the Home tab to apply Überschrift 1 to the
text that you want to appear here..1 – Fakultätsprogramm
𝑃≡
𝑓𝑎𝑘 = 1; 𝑃1
Sei 𝜎 ∈ Σ
(𝑃, 𝜎) → (𝑃1 , 𝜏) mit 𝜏 = 𝜎{𝑓𝑎𝑘⁄1}
{𝑓𝑎𝑘⁄1} bezeichne hier logisch eine Substitution: Die Variable 𝑓𝑎𝑘 wird zu 1
gesetzt. 𝑃1 ist dann das Restprogramm.
Das Problem ist dabei die Definition von →.
→ ist eine Relation auf Konfigurationen. Eine Berechnung des Programms ist
ein Element von →∗ , also der reflexiven, transitiven Hülle von →.
Die Konstruktion von → erfolgt induktiv! Dieser Ansatz, bei dem nicht das
Programm sondern sein Ablauf mit einer induktiven Semantik versehen
werden, heißt strukturelle operative Semantik.
Es ist (𝑃, 𝜎) gegeben, dafür ist (𝑃, 𝜎) → (𝑃1 , 𝜏) zu bestimmen. Das wird durch
Induktion über den Aufbau von P bewerkstelligt.
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..1:
→ ist folgendermaßen definiert:
(SKIP, σ) → (Ε, σ)
(var = exp, σ) → (E, σ{var⁄σ(exp)})
9
(WHILE bed DO P OD, σ) → (P; WHILE bed DO P OD, σ),
falls σ(bed) = true
(WHILE bed DO P OD, σ) → (E, σ),
falls σ(bed) = false
(IF bed THEN P1 ELSE P2 FI, σ) → (P1 , σ), falls σ(bed) = true
(IF bed THEN P1 ELSE P2 FI, σ) → (P2 , σ), falls σ(bed) = false
wenn (P1 , σ) → (P1′ , σ′),
dann (P1 ; P2 , σ) → (P1′ ; P2 , σ′)
(E; P, σ) → (P, σ)
Damit ist → definiert und folglich kann die Semantik insgesamt definiert
werden.
Das folgende Beispiel zeigt den Anfang bei der Abarbeitung des
Fakultätsprogrammes.
!
Beispiel Error! Use the Home tab to apply Überschrift 1 to the text
that you want to appear here..2
(fak = 1; P1 , σ)
Ausgangskonfiguration
(fak = 1, σ) → (E, σ{fak⁄1})
Der erste Schritt
Da E das leere Programm ist gilt auch
(fak = 1; P1 , σ) → (𝐸; 𝑃1 , 𝜎{𝑓𝑎𝑘 ⁄1}) → (𝑃1 , 𝜎{𝑓𝑎𝑘⁄1}) → ⋯
Hilfssatz:
1. Jede Konfiguration hat höchstens eine Nachfolgekonfiguration in
→. Die Semantik ist also deterministisch.
2. Die einzigen Konfigurationen, die keine Nachfolge in → haben,
sind von der Form (E, σ). Programme blockieren nicht.
3. Die maximalen Berechnungsfolgen sind entweder
a. unendlich (Das Programm divergiert.)
oder
b. enden in (E, σ). (Das Programm terminiert im Zustand σ.)
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..2:
Semantik der partiellen Korrektheit:
𝑀(⟦𝑃⟧)(𝜎) = 𝜏 gdw. (P, σ) →∗ (𝐸, 𝜏).
Für jedes Programm P gilt also 𝑀⟦𝑃⟧: Σ ↪ Σ
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..3:
10
Semantik der totalen Korrektheit:
𝑀𝑡𝑜𝑡 (⟦𝑃⟧)(𝜎) = {
𝜏
gdw. (𝑃, 𝜎) →∗ (𝐸, 𝜏)
⊥ 𝑃 divergiert von 𝜎 aus
Hinweis:
𝑀𝑡𝑜𝑡 (⟦𝑃⟧) ist eine totale Funktion:
Σ → Σ ∪ {⊥}
𝑀𝑡𝑜𝑡 (⟦𝑃⟧)(⊥) = ⊥
Wenn gilt 𝑀𝑡𝑜𝑡 (⟦𝑃⟧)(𝜎) =⊥, dann gilt auch 𝑀𝑡𝑜𝑡 (⟦𝑃; 𝑅⟧)(𝜎) =⊥.
Divergente Programme wirken wie Nullelemente bezüglich ;.
2.3
Substitutionslemma
Eine Formel G gilt in einem Zustand
gültig ist:
𝜎, wenn die mit 𝜎 substituierte Formel
𝜎 ⊨ 𝐺 gdw. ⊨ 𝐺{𝜎}
⊨ 𝐹 heißt: „F gilt unter einer leeren Menge von Voraussetzungen.“. Das heißt,
⊨ semantisches
F ist allgemeingültig.
Zeichen
⊢syntaktisches
Zeichen
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..4:
Die Erfüllungsmenge einer Formel F lautet:
𝛿(𝐹) = {𝜎| 𝜎 ⊨ 𝐹}
Also ist
⊨ 𝐹 gdw. 𝐸(𝐹) = Σ.
Oft ist das Ziel die Bestimmung der Allgemeingültigkeit einer Formel F, also
von
⊨ 𝐹.
Als Beispiel kann die Gültigkeit der Formel 𝑝 → (𝑞 → 𝑝) genommen werden.
Diese kann bekanntlich z. B. durch eine Wahrheitstafel nachgewiesen werden.
Die Komplexität einer Wahrheitstafel wächst jedoch exponentiell.
Folglich versucht man einen anderen Weg: Statt mit der semantischen
Methode „Wahrheitstafel“ wird schneller auf der syntaktischen Ebene
gearbeitet. Zum Beispiel:
𝑝 → (𝑞 → 𝑝) ⇔ ¬𝑝 ∨ (𝑞 → 𝑝) ⇔ ¬𝑝 ∨ (𝑝 ∨ ¬𝑞) ⇔ (𝑝 ∨ ¬𝑝) ∨ ¬𝑞 ⇔
𝑡𝑟𝑢𝑒 ∨ ¬𝑞 ⇔ 𝑡𝑟𝑢𝑒
Bemerkung:
1. Es wird rein formal syntaktisch geschlossen.
2. Es muß eine Menge von allgemeinen Gesetzen gefunden werden,
die beschreiben, welche syntaktischen Manipulationen erlaubt sind
(und welche nicht).
11
3. Bei allgemeinen Logiken ist der Weg zum „true“ schwierig zu
finden. Bei praktischen Anwendungen muß natürlich dafür ein
effizienter Weg gefunden werden.
12
3
Kalküle
In diesem Kapitel erhalten Sie eine Einführung in die Welt der
Kalküle. Sie beschäftigen sich im speziellen mit den Hoareschen
Kalkülen.
3.1
Einführung
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..1:
1. Ein Kalkül ist eine Menge von Regeln
2. Eine Regel ist ein Tripel
𝑃𝑟ä
𝐶𝑜𝑛𝑑
𝐶𝑜𝑛𝑐
Prä (Prämisse): ist eine Menge von Formeln.
Conc (Conclusion): ist eine Formel.
Cond (Condition): Anwendungsbedingungen (Entscheidbar)
Beispiele:
1. De Morgan
¬(𝐹 ∪ 𝐺)
{}
¬𝐹 ∩ ¬𝐺
2. Ordnungsrelationen
𝑥 < 𝑦, 𝑦 < 𝑧
{}
𝑥<𝑧
3. SLD (aus Prolog)
𝐴1 , … , 𝐴𝑛 , 𝐵 ← 𝐵1 ∧ … ∧ 𝐵𝑚
{𝐴1 Θ = 𝐵Θ … }
(𝐵1 , … , 𝐵𝑚 , 𝐴2 , … , 𝐴𝑛 )Θ
4. Jedes Axiomensystem ist ein Kalkül.
Es stellt sich die Frage, wann ein gegebener Kalkül richtig ist. Es könnte auf
zwei weisen falsch sein:

Es könnte falsche Sätze ableiten:
𝐹→𝐺
𝐺→𝐹
wäre eine falsche Regel.
Ein Kalkül, der eine solche Regel enthält, ist wertlos.

Es könnte wahre Sätzen geben, die im Kalkül nicht abgeleitet werden
können.
13
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..2:
Seien 𝐹1 , … , 𝐹𝑛 , 𝐺 Formeln; 𝑘 ein Kalkül.
1. 𝐺 ist ableitbar aus 𝐹1 , … , 𝐹𝑛 in 𝑘, wenn es in 𝑘 eine Regel gibt
mit
𝐹1 , … , 𝐹𝑛
𝐺
und erfüllter NB, in Zeichen 𝐹1 , … , 𝐹𝑛 ⊢𝑘 𝐺
2. 𝐺 ist ableitbar aus einer Menge 𝑀 von Formeln, wenn es eine
endliche Menge 𝑀′ ∈ 𝑀 gibt mit 𝑀′ ⊢𝑘 𝐺
3. Eine Menge 𝑁 von Formel ist aus einer Menge 𝑀 ableitbar,
wenn für alle 𝐹 ∈ 𝑁 gilt: 𝑀 ⊢𝑘 𝐺
Damit ist ⊢𝑘 eine Relation auf der Menge aller Formelmengen.
4. Die Relation ⊢𝑘 wird erweitert, indem man zu ihrer reflexiven,
transitiven Hülle übergeht.
Die Arbeit mit einem Kalkül soll die Arbeit mit der Semantik ersetzen. Folglich
stellt sich die Frage, was ⊨ und ⊢𝑘 miteinander zu tun haben.
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..3:
Ein Kalkül 𝑘 heißt korrekt wenn gilt:
𝑀 ⊢𝑘 𝑁 ⇒ 𝑀 ⊨ 𝑁
Ein Kalkül 𝑘 heißt vollständig wenn gilt:
𝑀 ⊨ 𝑁 ⇒ 𝑀 ⊢𝑘 𝑁
Hilfssatz:
Ein Kalkül ist korrekt gdw. jeder seiner Regeln korrekt ist.
Beispiele für vollständige Kalküle:
1. Für Aussagelogik:
a. Hilbert-Kalkül
b. „Freistil-Kalküle“
2. Für Prädikatenlogik: Prädikaten-Kalkül von Gödel
3. SLD-Kalkül für die Logik-Programmierung
4. Die Kalküle der Hoareschen Logik sind „relativ vollständig“ (s.u.).
14
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..4:
Sei 𝐻 eine Menge von Zuständen. Dann ist:
𝑀⟦𝑃⟧(𝐻) = {𝑀⟦𝑃⟧(ℎ)|ℎ ∈ 𝐻}
Sei 𝐹 eine Formel, dann sei:
𝑀⟦𝑃⟧(𝐹) = 𝑀⟦𝑃⟧(𝛿(𝐹))
Hilfssatz:
Sei 𝑃 ein Programm und 𝐹 eine Formel. Dann existiert eine Formel 𝐺
mit:
𝑀⟦𝑃⟧(𝐹) = 𝛿(𝐺)
Dies alles gilt sinngemäß auch für 𝑀𝑡𝑜𝑡 .
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..5:
Ein Hoaresches Tripel ist ein Tripel
{𝐹} 𝑃 {𝐺}
mit:


𝐹, 𝐺 sind Prädikatenlogische Formeln
𝑃 ist ein WHILE-Programm
Definition Error! Use the Home tab to apply Überschrift 1 to the text that you
want to appear here..6:
Ein Hoaresches Tripel heißt korrekt (gültig) wenn gilt:
𝑀⟦𝑃⟧(𝐹) ⊆ 𝛿(𝐺)
Das Programm P erfüllt eine Spezifikation aus den Formeln 𝐹 und 𝐺 gdw. das
Hoaresche Tripel {𝐹} 𝑃 {𝐺} gültig ist.
Wir haben festgelegt was es bedeutet, dass {𝐹} 𝑃 {𝐺} gültig ist. Die Menge
dieser Tripel bildet die Syntax der Hoareschen Logik. Das heißt: Wir kennen
die Bedeutung von ⊨ {𝐹} 𝑃 {𝐺}.
Jetzt gilt es, einen Kalkül 𝑘 (Hoarescher Kalkül) zu finden mit ⊢𝑘 {𝐹} 𝑃 {𝐺}.
Dieser Kalkül muss korrekt und soll möglichst vollständig sein.
15
3.2
Hoaresche Kalküle (HK)
Zunächst definieren wir die sechs grundlegenden Regeln der Hoareschen
Logik. Dabei handelt es sich um einen Kalkül für die partielle Korrektheit
deterministischer Programme (PD).

SKIP
{𝐹} 𝑆𝐾𝐼𝑃 {𝐹}

Zuweisung
{𝐹 {𝑥 ⁄𝑡}} 𝑥 = 𝑡 {𝐹}

Konsequenzregel
𝐹1 → 𝐹, {𝐹} 𝑃 {𝐺}, 𝐺 → 𝐺1
{𝐹1 } 𝑃 {𝐺1 }

Sequenzregel
{𝐹} 𝑃1 {𝐺}, {𝐺} 𝑃2 {𝐻}
{𝐹} 𝑃1 ; 𝑃2 {𝐻}

Verzweigung
{𝐹 ∧ 𝑏𝑒𝑑} 𝑃1 {𝐺}
{𝐹 ∧ ¬𝑏𝑒𝑑} 𝑃2 {𝐺}
{𝐹}𝐼𝐹 𝑏𝑒𝑑 𝑇𝐻𝐸𝑁 𝑃1 𝐸𝐿𝑆𝐸 𝑃2 {𝐺}

Schleife
{𝐼𝑛𝑣 ∧ 𝑏𝑒𝑑} 𝑃 {𝐼𝑛𝑣}
{𝐼𝑛𝑣} 𝑊𝐻𝐼𝐿𝐸 𝑏𝑒𝑑 𝐷𝑂 𝑃 𝑂𝐷 {𝐼𝑛𝑣 ∧ ¬𝑏𝑒𝑑}
Die Korrektheit von PD ist bis auf die Zuweisungsregel anschaulich klar.
Bemerkungen zu einigen Regeln:
1. Die Zuweisungsregel sieht auf den ersten Blick überraschend aus. Die
Regel {𝐹} 𝑥 = 𝑡 {𝐹 {𝑥 ⁄𝑡}} scheint auf der Hand zu liegen. Sie ist aber
falsch, wie man leicht durch eine Anwendung sieht. Dann würde
nämlich gelten: {𝑥 = 3} 𝑥 = 𝑥 + 1 {𝑥 = 2}.
Die Tatsache, daß wir bei der Zuweisungsregel von hinten nach vorne
schließen hat erhebliche Konsequenzen für die Implementierung des
Verifikationsverfahrens. Alle Algorithmen müssen von hinten durch das
Programm laufen.
2. Die Konsequenzregel ist insofern ein Ärgernis, als sie uns zwingt, die
gesamte Prädikatenlogik in die Hoaresche Logik hereinzuholen.
16
3. Für den Nachweis eines Tripels {𝐹} 𝑃1 ; 𝑃2 {𝐻} mittels der Sequenzregel,
muß eine Zwischenbehauptung G gefunden werden. Das ist oft
möglich, indem man auf die weiter unten beschriebene „schwächste
Vorbedingung“ zurückgreift.
4. Auch die Schleifenregel hat ihre Tücken: Damit sie angewandt werden
kann, muß eine Formel Inv, eine Invariante, gefunden werden. Leider
gibt es beweisbar kein allgemeines Verfahren zum Finden einer
Invariante. Es gibt Sonderfälle, wo dies geht; ansonsten ist eine
Intervention durch den Nutzer unumgänglich.
Der Kalkül TD: totale Korrektheit deterministischer Programme
Betrachten wir als nächstes die totale Korrektheit deterministischer
Programme (TD). Zunächst gelten hier ebenfalls die Regeln, der partiellen
Korrektheit (Siehe Seite 13). Nur die Schleifenregel wird ersetzt durch

2. Schleifenregel
{𝐼𝑛𝑣 ∧ 𝑏𝑒𝑑} 𝑃 {𝐼𝑛𝑣}
{𝑧0 = 𝑡} 𝑃 {𝑧0 > 𝑡}
𝐼𝑛𝑣 → 𝑡 ∈ ℕ0
{𝐼𝑛𝑣} 𝑊𝐻𝐼𝐿𝐸 𝑏𝑒𝑑 𝐷𝑂 𝑃 𝑂𝐷 {𝐼𝑛𝑣 ∧ ¬𝑏𝑒𝑑}
wobei 𝑧0 eine Variable ist, die in 𝑃 nicht vorkommt. Bei 𝑡 handelt es sich um
einen Term über den Programmvariablen.
3.3
Vollständigkeit von PD und TD
Zur Vollständigkeit der beiden Kalküle kann folgendes Gesagt werden:
!
Merksatz Error! Use the Home tab to apply Überschrift 1 to the
text that you want to appear here..1
Die Kalküle der partiellen und totalen Korrektheit deterministischer
Programme gilt die Vollständigkeit NICHT.
PD ist „relativ vollständig bezüglich der Prädikatenlogik (PL)“. Das heißt, wenn
wir ein Entscheidungsverfahren für die PL hätten, wäre PD vollständig.
Da PL nicht entscheidbar ist, muss man sich bei der Implementierung anders
helfen.
1. Möglichkeit: Abfrage an den Nutzer
2. Möglichkeit: Vertrauen auf Mathematica 
17
TD ist noch nicht einmal relativ vollständig. Es gibt terminierende Programme,
bei denen man die Terminierung mit TD selbst dann nicht nachweisen kann,
wenn ein Entscheidungsverfahren für die PL vorausgesetzt wird.
Was man zur Erzwingung der Terminierung braucht ist eine Ordnungsrelation
mit der Artinschen Kettenbedingung:
Unter jedem Element ist jede Kette endlich.
Beispiel: Anordnung nach Gold-, Silber-, Bronzemedaillen
(𝑔1 , 𝑠1 , 𝑏1 ) < (𝑔2 , 𝑠2 , 𝑏2 ) gdw.
𝑔1 < 𝑔2 oder 𝑔1 = 𝑔2 ∧ 𝑠1 < 𝑠2 oder 𝑔1 = 𝑔2 ∧ 𝑠1 = 𝑠2 ∧ 𝑏1 < 𝑏2
Das ist eine lineare Ordnung mit der Artinschen Kettenbedingung.
18
3.4
Beweisskizzen zur Korrektheit von PD
Zuweisungsregel
Zu zeigen:
𝑀⟦𝑥 = 𝑡⟧ (𝛿(𝐹{𝑥⁄𝑡})) ⊆ 𝛿(𝐹)
Zum Beweis benutzen wir das aus der Logik bekannte Substitutionslemma:
𝑀(𝐹)(𝜎{𝑥⁄𝑡}) = 𝑀 (𝐹(𝑥⁄𝑡)) (𝜎)
mit der Substitution {𝑥⁄𝑡} und der dadurch modifizierten Belegung
𝜎{𝑥⁄𝑡}(𝑦) = {
𝜎(𝑦)
𝜎(𝑡)
𝑥≠𝑦
𝑥=𝑦
Im Substitutionslemma werden eine syntaktische Veränderung (𝐹 → 𝐹{𝑥⁄𝑡})
und eine semantische Veränderung (𝜎 → 𝜎{𝑥⁄𝑡}) verknüpft.
Sei 𝜎 ∈ 𝛿(𝐹{𝑥⁄𝑡}).
Zu zeigen: (𝑥 = 𝑡, 𝜎) → (𝐸, 𝜏) für ein 𝜏 ∈ 𝛿(𝐹).
Nach der Definition der →-Relation gilt:
(𝑥 = 𝑡, 𝜎) → (𝐸, 𝜎 (𝑥⁄
)) = (𝐸, 𝜎(𝑥⁄𝑡))
𝜎(𝑡)
Wegen 𝜎 ∈ 𝛿(𝐹{𝑥⁄𝑡}) und wegen des Substitutionslemmas gilt:
1 = 𝑀(𝐹{𝑥⁄𝑡})(𝜎) = 𝑀(𝐹)(𝜎{𝑥⁄𝑡})
das heißt
𝜎{𝑥⁄𝑡} ∈ 𝛿(𝐹)
∎
Konsequenzregel
Man sieht leicht die folgende Bemerkung ein („Monotonie der semantischen
Abbildung“): Aus
𝑁1 ⊆ 𝑁2
folgt
𝑀⟦𝑃⟧(𝑁1 ) ⊆ 𝑀⟦𝑃⟧(𝑁2 )
Wenn jetzt die Vorbedingungen der Konsequenzregel gelten,
𝐹1 → 𝐹, {𝐹} 𝑃 {𝐺}, 𝐺 → 𝐺1
dann bedeutet das:
𝛿(𝐹1 ) ⊆ 𝛿(𝐹),
𝑀⟦𝑃⟧(𝛿(𝐹)) ⊆ 𝛿(𝐺),
19
𝛿(𝐺) ⊆ 𝛿(𝐺1 )
Also
𝑀⟦𝑃⟧(𝛿(𝐹1 )) ⊆ 𝑀⟦𝑃⟧(𝛿(𝐹)) ⊆ 𝛿(𝐺) ⊆ 𝛿(𝐺1 )
Folglich
{𝐹1 } 𝑃 {𝐺1 }
Sequenzregel
Aus dem folgenden Hilfssatz folgt die Sequenzregel unmittelbar:
𝑀⟦𝑃1 ; 𝑃2 ⟧(𝜎) = 𝑀⟦𝑃2 ⟧(𝑀⟦𝑃1 ⟧(𝜎))
Beweis des Hilfssatzes:
Aus der Regel der Semantik
(𝑅1 , 𝜎) → (𝑅2 , 𝜏)
(𝑅1 ; 𝑃, 𝜎) → (𝑅2 ; 𝑃, 𝜏)
folgt durch Induktion über die Länge der Ableitung die stärkere Fassung,
(𝑅1 , 𝜎) →∗ (𝑅2 , )
(𝑅1 ; 𝑃, 𝜎) →∗ (𝑅2 ; 𝑃, )
Die Voraussetzung besagt für den Fall 𝑅2 = E gerade, daß 𝑀⟦𝑅1 ⟧(𝜎) =  gilt.
Jetzt seien 𝑅1 = 𝑃1 und 𝑃 = 𝑃2 mit
𝑀⟦𝑃2 ⟧() = 𝜚
also
(𝑃2 , ) →∗ (𝐸, 𝜚)
dann gilt
𝑀⟦𝑃2 ⟧(𝑀⟦𝑃1 ⟧(𝜎)) = 𝜚
Aber es gilt auch
(𝑃1 ; 𝑃2 , 𝜎) →∗ (𝐸; 𝑃2 , ) → (𝑃2 , ) →∗ (𝐸, 𝜚)
und damit
𝑀⟦𝑃1 ; 𝑃2 ⟧(𝜎) = 𝜚
was den Hilfssatz beweist.
Verzweigung
Nach Definition ist
𝑀⟦𝐼𝐹 𝑏𝑒𝑑 𝑇𝐻𝐸𝑁 𝑃1 𝐸𝐿𝑆𝐸 𝑃2 𝐹𝐼⟧(𝜎) = {
𝑀⟦𝑃1 ⟧(𝜎) 𝜎 ⊨ 𝑏𝑒𝑑
𝑀⟦𝑃2 ⟧(𝜎) 𝜎 ⊭ 𝑏𝑒𝑑
Durch leicht Fallunterscheidung folgt für jede Menge 𝑁 von Belegungen:
𝑀⟦𝐼𝐹 𝑏𝑒𝑑 𝑇𝐻𝐸𝑁 𝑃1 𝐸𝐿𝑆𝐸 𝑃2 𝐹𝐼⟧(𝑁) = 𝑀⟦𝑃1 ⟧(𝑁 ∩ 𝛿(𝑏𝑒𝑑)) ∪ 𝑀⟦𝑃2 ⟧(𝑁 ∩ 𝛿(¬𝑏𝑒𝑑))
20
Wir kommen jetzt zum Beweis der Verzweigungsregel: Die Voraussetzungen
{𝐹 ∧ 𝑏𝑒𝑑} 𝑃1 {𝐺}
{𝐹 ∧ ¬𝑏𝑒𝑑} 𝑃2 {𝐺}
bedeuten
𝑀⟦𝑃1 ⟧(𝛿(𝐹 ∧ 𝑏𝑒𝑑)) ⊆ 𝛿(𝐺)
𝑀⟦𝑃2 ⟧(𝛿(𝐹 ∧ ¬𝑏𝑒𝑑)) ⊆ 𝛿(𝐺)
Damit ist
𝑀⟦𝐼𝐹 𝑏𝑒𝑑 𝑇𝐻𝐸𝑁 𝑃1 𝐸𝐿𝑆𝐸 𝑃2 𝐹𝐼⟧(𝛿(𝐹))
= 𝑀⟦𝑃1 ⟧(𝛿(𝐹) ∩ 𝛿(𝑏𝑒𝑑)) ∪ 𝑀⟦𝑃2 ⟧(𝛿(𝐹) ∩ 𝛿(¬𝑏𝑒𝑑))
= 𝑀⟦𝑃1 ⟧(𝛿(𝐹 ∧ 𝑏𝑒𝑑)) ∪ 𝑀⟦𝑃2 ⟧(𝛿(𝐹 ∧ ¬𝑏𝑒𝑑)) ⊆ 𝛿(𝐺)
Schleife (Beweisidee)
Hilfssatz:
Sei Ω ein Programm das total undefiniert ist, z. B.
𝑊𝐻𝐼𝐿𝐸 𝑡𝑟𝑢𝑒 𝐷𝑂 𝑆𝐾𝐼𝑃 𝑂𝐷 ≡ Ω
Dann gilt:
Für alle 𝜎: 𝑀⟦Ω⟧(𝜎) = ∅
Sei jetzt eine Abkürzung vereinbart: 𝑊𝐻𝐼𝐿𝐸 𝑏𝑒𝑑 𝐷𝑂 𝑃 𝑂𝐷 ≡ W
Dann sei 𝑊0 = Ω
𝑊𝑖+1 = 𝐼𝐹 𝑏𝑒𝑑 𝑇𝐻𝐸𝑁 𝑃; 𝑊𝑖 𝐸𝐿𝑆𝐸 𝑆𝐾𝐼𝑃 𝐹𝐼
𝑊𝑖 ist das Progamm, das uns genau alle Ergebnisse von 𝑊 bis zum iten Schleifendurchlauf liefert und sonst nichts.
Dann gilt:
∞
𝑀⟦𝑊⟧(𝜎) = ⋃ 𝑀⟦𝑊𝑖 ⟧(𝜎)
𝑖=0
Beweis: ∎
Gemäß der Voraussetzung für die Schleifenregel gelte jetzt:
𝑀⟦𝑃⟧(𝜎(𝐼𝑛𝑣 ∧ 𝑏𝑒𝑑) ) ⊆ 𝛿(𝐼𝑛𝑣 )
Für die Konklusion der Schleifenregel ist zu zeigen:
∞
𝑀⟦𝑊⟧(𝜎) = ⋃ 𝑀⟦𝑊𝑖 ⟧(𝛿(𝐼𝑛𝑣)) ⊆ 𝛿(𝐼𝑛𝑣 ∧ ¬𝑏𝑒𝑑)
𝑖=0
21
Zum Beweis der Teilmengenbeziehung reicht es, zu zeigen:
∀ 𝑖: 𝑀⟦𝑊𝑖 ⟧(𝛿(𝐼𝑛𝑣)) ⊆ 𝛿(𝐼𝑛𝑣 ∧ ¬𝑏𝑒𝑑)
Das geschieht durch Induktion:
𝑖 = 0:
𝑖 → 𝑖 + 1:
𝑀⟦𝑊0 ⟧(𝛿(𝐼𝑛𝑣)) = ∅ ⊆ 𝛿(𝐼𝑛𝑣 ∧ ¬𝑏𝑒𝑑)
𝑀⟦𝑊𝑖+1 ⟧(𝛿(𝐼𝑛𝑣)) = 𝑀⟦𝐼𝐹 𝑏𝑒𝑑 𝑇𝐻𝐸𝑁 𝑃; 𝑊𝑖 𝐸𝐿𝑆𝐸 𝑆𝐾𝐼𝑃 𝐹𝐼⟧(𝛿(𝐼𝑛𝑣))
= 𝑀⟦𝑃; 𝑊𝑖 ⟧(𝛿(𝐼𝑛𝑣) ∩ 𝛿(𝑏𝑒𝑑)) ∪ 𝑀⟦𝑆𝐾𝐼𝑃⟧(𝛿(𝐼𝑛𝑣) ∩ 𝛿(¬𝑏𝑒𝑑))
Der zweite Teil der Vereinigung ist in 𝛿(𝐼𝑛𝑣 ∧ ¬𝑏𝑒𝑑) enthalten, also das, was
wir haben wollen. Für den ersten gilt:
𝑀⟦𝑃; 𝑊𝑖 ⟧(𝛿(𝐼𝑛𝑣) ∩ 𝛿(𝑏𝑒𝑑)) = 𝑀⟦𝑊𝑖 ⟧(𝑀⟦𝑃⟧(𝛿(𝐼𝑛𝑣 ∧ 𝑏𝑒𝑑))) ⊆ 𝑀⟦𝑊𝑖 ⟧(𝛿(𝐼𝑛𝑣))
⊆ 𝛿(𝐼𝑛𝑣 ∧ ¬𝑏𝑒𝑑)
Begründungen für die drei Schritte der letzten Ungleichung:
-
Hilfssatz zur Sequenzregel,
Voraussetzung für die Schleifenregel und Monotonie der semantischen
Abbildung
Induktionsvoraussetzung
22
Zum Beweis der Vollständigkeit der Beweissysteme PD und TD (soweit diese
gegeben ist) müssen wir den Begriff der „schwächsten Vorbedingung“
klären. Zunächst müssen wir den Begriff der „schwächsten Vorbedingung“
klären. Es handelt sich um die Vorbedingung, die mindestens gelten muß,
damit ein Programm 𝑃 bei Start mit dieser Vorbedingung in einen Zustand
gelangen kann, in dem eine gegebene Formel 𝐹 gilt.Geschrieben:
𝑤𝑙𝑝(𝑃, 𝐹)
Es soll also gelten { 𝑤𝑙𝑝(𝑃, 𝐹)} P {F} und es darf kein 𝜎 geben mit
𝜎 𝛿(𝑤𝑙𝑝(𝑃, 𝐹)). Der folgende Satz ist schwer zu beweisen. Er wird hier nur
zitiert.
Satz:
Für all P und F existiert eine Prädikatenlogische Formel G mit
𝐺 ⇔ 𝑤𝑙𝑝(𝑃, 𝐹)
Satz:
{𝐹} 𝑃 {𝐺} ist genau dann korrekt, wenn gilt:
𝐹 → 𝑤𝑙𝑝(𝑃, 𝐺)
Beweis: Konsequenzregel
Wir wollen jetzt noch kurz sehen, wie die Zwischenbehauptung der
Sequenzregel konstruiert werden kann. Es soll also das Tripel
{𝐹} 𝑃1 ; 𝑃2 {𝐻} nachgewiesen werden. Damit die Sequenzregel angewandt
werden kann muß man eine Zwischenformel G finden mit {𝐹} 𝑃1 {𝐺} und
{𝐺} 𝑃2 {𝐻}. Man wähle dazu G = wlp(P2,H). Nach dem Satz von eben gilt
{𝐺} 𝑃2 {𝐻} dann von selbst. Es bleibt also, wlp(P2,H) zu bestimmen und
{𝐹} 𝑃1 {wlp(P2, H)} nachzuweisen.
Beispiel: Zu zeigen sei
{𝑥 = 𝑦} 𝑥 = 𝑥 + 1; 𝑦 = 𝑦 + 1 {𝑥 = 𝑦}
Es für Korrektheitsbeweise immer eine gute Taktik, die Ein- und die Ausgabe
in irgendeiner Form in Variablen zu speichern, damit man sie auch während
des Korrektheitsbeweises noch in der Ausgangsform zur Verfügung hat.
Wegen des Durchlaufes von hinten nach vorne (s. Bemerkung zur
Zuweisungsregel) gilt das insbesondere für die Variablen in der
Nachbedingung. Hier wird eine neue Variable z eingeführt und die
Nachbedingung verändert sich zu 𝑥 = z  𝑦 = z. Das ist wegen der
Konsequenzregel erlaubt. Mit der Zuweisungsregel berechnet sich die
Vorbedingung zu
wlp(𝑦 = 𝑦 + 1, 𝑥 = z  𝑦 = z) = (𝑥 = z  𝑦 + 1 = z)
Ferner gilt
wlp(𝑥 = 𝑥 + 1, 𝑥 = z  𝑦 + 1 = z) = (𝑥 + 1 = z  𝑦 + 1 = z)
Damit ergibt sich folgende Beweisskizze
{𝑥 = 𝑦} {𝑥 + 1 = z  𝑦 + 1 = z} 𝑥 = 𝑥 + 1 {𝑥 = z  𝑦 + 1 = z}
𝑦 = 𝑦 + 1 {𝑥 = z  𝑦 = 𝑧} {𝑥 = 𝑦}
23
4
Compilerbautechniken
Ziel dieses Kapitels ist es, eine Einführung in grundlegende
Vorgehensweisen und Techniken des Compilerbaus zu geben.
4.1
Parsingtechniken
Ein Parser ist ein Programm, das für die Zerlegung und Umwandlung einer
Angabe anhand einer gegebenen Grammatik verantwortlich ist. Für unsere
Zwecke gilt, daß eine kontextfreie Grammatik vorhanden sein muß.
Prog →
WHILE bed DO Prog OD → …
Prog →
IF…
…
1. Beobachtung
Beim Aufbau eines
Wahlmöglichkeiten.
Ableitungsbaums
hat
man
an
manchen
Stellen
Wie geht man damit um?
1) „zufällige“ Wahl, Backtracking,
2) Ziel: Kosten minimieren (entstehen besonders beim Backtracking)
3) Techniken entwickeln, die die Wahl der Produktion regeln
→ Backtracking wird vermieden
Beispiele für Parsingverfahren, die besser sind als simples Backtracking
1. mit Backtracking
2. ohne Backtracking
zu 1.: Chart-Parsing
Um Mehrfachanalysen eines Satzteiles zu vermeiden, hat man das ChartParsing entwickelt. Das Chart-Parsingverfahren zeichnet sich dadurch aus,
daß es sich bereits analysierte Teile eines Satzes merkt.
Definition:
1. Ein Chart ist eine endliche Folge von Items.
2. Ein Item ist eine Struktur 𝒏𝟏 𝒏𝟐 𝒘𝟏 → 𝒘𝟐 ∙ 𝒘𝟑 .
Dabei sind 𝒏𝟏 𝒏𝟐 ∈ 𝑵𝟎 , 𝒏𝟏 ≤ 𝒏𝟐 , 𝒘𝟏 → 𝒘𝟐 𝒘𝟑 ∈ 𝑷.
Die intuitive Bedeutung soll an einem Beispiel erläutert werden:
Bei einer Analyse von „0 der
1
mann
2
sieht
3
die
4
frau 5“ entsteht an
Beispiel
einer Stelle des Algorithmus das Item 𝑛1 𝑛2 𝑤1 → 𝑤2 ∙ 𝑤3 mit 𝑛1 = 0, 𝑛2 = 2. Der
Rest des Items habe die Form 𝑆 → 𝑁𝑃 ∙ 𝑉𝑃. Die Zahlen zeigen an, welche Teile
24
schon analysiert und bestimmt sind. Der Punkt trennt den schon analysierten
und bestimmten Teil von der Vorhersagekomponente, dem noch spekulativen
Rest. Hier würde das bedeuten, dass die von 0 bis 2 reichende Zeichenkette „ 0
der
1
mann
“ bereits bestimmt ist. Und da sich links des Punktes das
2
Nichtterminale NP befindet, wissen wir auch, als was der Teil von 0 bis 2
analysiert worden ist: als Nominalphrase (NP). Zusätzlich wissen wir: Können
wir den Rest als VP bestimmen (soweit die Spekulation), dann ist das ganze
Konstrukt ein S, ein Satz.
Der am weitesten verbreitete Chart-Parsing- Algorithmus ist der 1970 von Jay
Earley vorgestellte Earley-Algorithmus. Er zeichnet sich besonders dadurch
aus, dass er kein Backtracking nutzt. Es werden zeitgleich alle Alternativen
verfolgt. Am Ende des Parsingvorgangs sind alle alternativen Syntaxanalysen
in der Chart.
Eingabe: Eine kontextfreie Grammatik G, ein Wort W der Länge n
Ausgabe:
ja, wenn W  ℒ(G)
nein sonst
Es ist auch leicht, den Algorithmus so zu modifizieren, dass für den Fall W 
ℒ(G) ein Ableitungsbaum für W ausgegeben wird (oder sogar alle).
Initialisierung:
Zu Anfang stehen Items eines einzigen Typs in der Chart. Dies soll jetzt für
die Zeichenkette „0 der 1 mann 2 sieht 3 die 4 frau 5“ erläutert werden:
Diese soll als ein Satz (S) analysiert werden. Für das Nichtterminale S gibt es
hier nur eine Regel S  NP VP. Zu Anfang ist alles von der 0-ten bis zur 0-ten
Stelle analysiert, also n1 = n2 = 0. Der Punkt steht anfangs ganz links, da
noch nichts sicher analysiert und der weitere Verlauf noch Spekulation ist. Am
Anfang gilt dort also S  ∙ NP VP, und damit wird das Item 0 0 S  ∙ NP VP in
die Chart eingefügt.
0
Der
1
Mann
NP
2
sieht
3
die
4
Frau
5
VP
Im allgemeinen Fall müssen zum Start alle Items des Typs 0 0 V  ∙ w in die
Chart eingefügt werden. Dabei seien V das Startsymbol der Grammatik und V
 w alle Produktionen mit V als rechter Seite.
Schritte:
Im Wesentlichen besteht der Earley-Algorithmus aus drei Schritten, die immer
in einer geeigneten Reihenfolge wiederholt werden:
-
Expand oder Predict,
25
-
Scan,
-
Complete.
Abbruchbedingung:
Der Algorithmus terminiert, wenn ein Item 0 𝑛 𝑉 → 𝑤 ∙ aufgefunden wird.
Alternativ kann auch nach allen Items des Typs 0 𝑛 𝑉 → 𝑤 ∙ gesucht werden.
Dabei seien n die Länge des zu untersuchenden Wortes, V das Startsymbol
der Grammatik und V  w alle Produktionen mit V als rechter Seite.
Diese Schritte des Algorithmus werden anhand eines Beispiels näher erklärt.
Wir versuchen wieder, die Zeichenkette „der mann sieht die frau“ abzuleiten
und zwar mittels einer Grammatik mit dem Startsymbol S und den
Produktionsregeln
𝑆 → 𝑁𝑃 𝑉𝑃,
𝑁𝑃 → 𝐷𝑒𝑡 𝑁 ,
𝑉𝑃 → 𝑉 | 𝑉 𝑁𝑃 ,
𝑉 → 𝑖𝑠𝑠𝑡|𝑠𝑖𝑒ℎ𝑡 ,
𝑁 → 𝑚𝑎𝑛𝑛|𝑓𝑟𝑎𝑢 ,
𝐷𝑒𝑡 → 𝑑𝑒𝑟|𝑑𝑖𝑒.
Das Ergebnis des Algorithmus ist die folgende Chart. An der Existenz des
Items 30 ist zu sehen, dass die Ableitung erfolgreich war. Danach wird
erläutert
1. welches die drei Schritte des Algorithmus genau sind, und
2. wie die Reihenfolge ihrer Anwendung gesteuert wird.
ItemNr.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Bereich
0
0
0
0
0
0
1
1
1
0
0
2
2
2
2
0
0
0
0
1
1
1
1
2
2
2
2
2
2
2
Item
Konstruiert durch
S ∙ NP VP
NP  ∙ Det N
Det  ∙ der
Det  ∙ die
Det  der ∙
NP  Det ∙ N
N  ∙ mann
N  ∙ frau
N  mann ∙
NP  Det N ∙
S  NP ∙ VP
VP  ∙ V
VP  ∙ V NP
V  ∙ isst
V  ∙ sieht
Initial
Expand 1
Expand 2
Expand 2
Scan 3
Complete 5 + 2
Expand 6
Expand 6
Scan 7
Complete 9+6
Complete 10+1
Expand 11
Expand 11
Expand 12+13
Expand 12+13
Verbleibender
Text
der mann sieht die frau
der mann sieht die frau
der mann sieht die frau
der mann sieht die frau
mann sieht die frau
mann sieht die frau
mann sieht die frau
mann sieht die frau
sieht die frau
sieht die frau
sieht die frau
sieht die frau
sieht die frau
sieht die frau
sieht die frau
26
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
2
2
0
3
3
3
3
3
4
4
4
3
2
0
V  sieht ∙
VP  V ∙
VP  V ∙ NP
S  NP VP ∙
NP  ∙ Det N
Det  ∙ der
Det  ∙ die
Det  die ∙
NP  Det ∙ N
N  ∙ mann
N  ∙ frau
N  frau ∙
NP  Det N ∙
VP  V NP ∙
S  NP VP ∙
3
3
3
3
3
3
3
4
4
4
4
5
5
5
5
Scan 15
Complete 16+12
Complete 16+13
Complete 17+11
Expand 18
Expand 20
Expand 20
Scan 22
Complete 23+20
Expand 24
Expand 24
Scan 26
Complete 27+24
Complete 28+18
Complete 29+19
die frau
die frau
die frau
die frau
die frau
die frau
die frau
frau
frau
frau
frau
ε
ε
ε
ε
Tabelle: Earley-Algorithmus
Die Schritte des Verfahrens:
Expand
(Predict)
Wir brauchen in der Chart ein Item der Form n1 n2 𝛼 → 𝛽 ∙
𝑋 𝛾 mit 𝛼 ∈ 𝑁, 𝛽 ∈ (𝑁 ∪ 𝑇)∗ , 𝑥 ∈ ℕ, 𝛾 ∈ (𝑁 ∪ 𝑇)∗ und in P
muss es eine Produktionsregel X  δ geben.
Dann können wir zum Chart ein Item der Form n2 n2 X 
∙δ hinzufügen.
expand
vorhanden
i j
N →
Seq ● N’ Seq’
neu konstruiert
j j
N’→
● Seq’’ für alle möglichen Regeln
Bsp:
vorhanden
0 1
NP →
Det ● N
neu konstruiert
1 1
N →
● Mann
1 1
N →
● Frau
27
Wir brauchen in der Chart ein Item der Form n1 n2 𝛼 → 𝛽 ∙
𝑡 𝛾 mit ni, α, β, γ wie oben und tT; und im zu
analysierenden Satz, muss an Position n2+1 ein t stehen.
Dann kann dem Chart ein Item der Form n1 n2+1 𝛼 → 𝛽 𝑡 ∙
𝛾 hinzugefügt werden.
Scan
(Shift)
scan
vorhanden
i j
N →
Seq ● t Seq’
Wenn im Eingabewort an Position j, j+1 das t steht,
wird neu konstruiert:
i j+1
N→
Seq t ● Seq’
Bsp.:
vorhanden
im Chart
0 0 Det →
● der
im Wort an erster
Stelle
neu konstruiert
der
0 1 Det →
Complete
(Reduce)
der ●
Wir brauchen ein Item der Form n1 n2 𝛼 → 𝛽 ∙ und ein Item
der Form n3 n1 γ  δ ∙ α ε mit γ N, β T, α N, δ, ε  (N
∪ T)*. Dann kann ein Item der Form n3 n2 γ  δ α ∙ε
hinzugefügt werden.
complete
vorhanden
i j
N →
Seq ●
suche Partner der Form
k i
N’→
Seq’ ● N Seq’’
neu konstruiert
k j
N’→
Bsp:
vorhanden
0 1 Det →
Det ●
Partner
0 0
NP →
● Det N
Seq’ N ● Seq’’
28
Ergebnis
0 1
NP →
Det ● N
Der Ablauf des Verfahrens wird wie folgt gesteuert: Es gibt im Verlauf immer
ein aktuelles Item, das zur Produktion neuer Items benutzt wird, die dann
hinten an die Chart gehängt werden. Das Verfahren endet, wenn alle Items
schon zur Konstruktion neuer Items benutzt worden sind und kein neues
aktuelles Item gefunden werden kann.
Anfangs ist das aktuelle Item das Startitem.
Für die Konstruktion neuer Items gibt es drei Möglichkeiten, abhängig von der
Form des aktuellen Items.
1. ● vor Nichtterminalen:
expand
2. ● vor Terminalen:
scan
3. ● am Ende:
complete
Das heißt präziser:
1. Falls beim aktuellen Item hinter dem Punkt ein Nichtterminales n
steht, also ein Item der Form n 1
n2 𝛼 → 𝛽 ∙ 𝑛 𝛾 vorliegt, werden
Expand-Schritte durchgeführt: Für jede Produktionsregel der Form n 
δ wird ein Item der entsprechenden Form angehängt.
2. Falls beim aktuellen Item hinter dem Punkt ein Terminales t steht, also
ein Item der Form n1
n2 𝛼 → 𝛽 ∙ 𝑡 𝛾 vorliegt, wird ein Scan-Schritt
versucht. Findet sich an der Stelle n 2+1 im zu analysierenden Wort
ebenfalls ein t, wird ein Item der entsprechenden Form angehängt.
3. Falls beim aktuellen Item hinter dem Punkt nichts mehr steht, also ein
Item der Form n1
n2 𝛼 → 𝛽 ∙
vorliegt, wird ein Complete-Schritt
versucht. Es werden in der Chart Items der Form n3 n1 w1  δ ∙ α w2
gesucht und im Erfolgsfall die entsprechenden Items angehängt.
Übungsaufgabe: Wie muß der Algorithmus modifiziert werden, damit nicht nur
eine ja/nein-Entscheidung als Ergebnis herauskommt sondern ein
Ableitungsbaum?
4.2
Vermeiden von Backtracking
Die Frage, wie der zweite der oben genannten Ansätze zu realisieren ist,
nämlich die Frage, wie man Backtracking von vorneherein vermeidet, wird
vordergründig beantwortet durch
Definition:
29
Eine Grammatik heißt 𝑳𝑹(𝒏)- Grammatik, wenn bei der Analyse
durch die Betrachtung der nächsten 𝑛 Token (Lookahead)
entschieden werden kann, welche Produktionsregel zu wählen ist.
Definition:
Eine Sprache heißt 𝑳𝑹(𝒏)-Sprache, wenn es für sie eine 𝐿𝑅(𝑛)Grammatik gibt.
Satz:
Zu jeder 𝐿𝑅(𝑛)- Sprache L, existiert eine 𝐿𝑅(1)-Grammatik G mit
L(G)=L. Die Klasse der 𝐿𝑅(1)-Sprachen ist genau die Klasse 𝐿(𝐷𝑃𝐷𝐴)
Dabei steht DPDA für die deterministischen Kellerautomaten
(„deterministic pushdown automata“). Diese können genau die
deterministisch kontextfreien Sprachen erkennen.
Definition:
Eine kontextfreie Grammatik heißt 𝑳𝑳(𝒏)- Grammatik für eine natürliche Zahl
n, wenn jeder Ableitungsschritt eindeutig durch die nächsten n Symbole der
Eingabe (Lookahead) bestimmt ist.
Das bedeutet, die Frage, welches Nichtterminalsymbol mit welcher Regel als
nächstes expandiert werden soll, kann eindeutig mit Hilfe der nächsten k
Symbole der Eingabe bestimmt werden.
Achtung: Im Gegensatz zu den LR-Sprachen geht es hier nicht um Analysesondern um Ableitungsschritte. Der Unterschied besteht sozusagen in der
Richtung, in der die Ableitung gelesen wird.
Generell gilt, je größer k gewählt wird, umso mächtiger wird die Sprachklasse.
Das zeigt die folgende Inklusionskette, wo alle Inklusionen echt sind.
Dabei steht DPDA für die deterministischen Kellerautomaten. Diese können
genau die deterministisch kontextfreien Sprachen erkennen. Insgesamt bilden
die LL(i) im Gegensatz zu den LR(i) eine echte Hierarchie. gibt es kontextfreie
Sprachen, die für kein k von einer LL(k)-Grammatik erzeugt werden.
4.3
Attributierte Grammatiken
Eine Attributgrammatik ist eine kontextfreie Grammatik, die um Attribute
sowie Regeln und Bedingungen für diese Attribute erweitert ist. Angewandt
wird das Konzept im Compilerbau, um die Einhaltung von Regeln zu
überprüfen, die mit kontextfreien Grammatiken nicht formuliert werden
können. Solche Regeln sind z. B. die, daß jede Variable deklariert sein muß
und ihrem Datentyp entsprechend verwendet wird.
30
Ein Compiler überprüft die Einhaltung dieser Regeln während der
semantischen Analyse. Dabei hat er nur die Informationen zur Verfügung, die
im Syntaxbaum des Programms enthalten sind. Zusätzliche Informationen,
die die semantische Analyse erleichtern, kann man als Attribute in den
Syntaxbaum integrieren.
Zum Beispiel kann der Typ eines Ausdrucks als Attribut an den
entsprechenden Knoten im Syntaxbaum annotiert werden.
Durch
Attributregeln und -bedingungen können zusätzlich Abhängigkeiten von
anderen Attributen (auch anderer Knoten im Syntaxbaum) angegeben
werden. Die Arten der Abhängigkeit bestimmen den Durchlauf durch den
Baum, sobald die Attribute ausgewertet werden sollen.
Die Programmierung der betreffenden Teile des Compilers vereinfacht sich,
wenn die Produktionen der Grammatik selbst mit entsprechenden Attributen
versehen werden.
Typische Attribute:






Werte
Typ
Baum
Hoare-Tripel
Ausgabebefehle
F-Strukturen in der Computerlinguistik
Zum Beispiel:
Syntaktisch
Exp →
Semantisch
Exp1 op Exp2
Exp.Wert →
Exp1.Wert op Exp2.Wert
Exp 28
Exp 14
Exp 9
op
Exp 5
Zahl 9
+
Zahl 5
9 9
Attribut
op
Exp 2
*
Zahl 2
2 2
5 5
Schon an diesem Punkt ist zu sehen, daß es im allgemeinen schwierig ist, die
Reihen folge zu bestimmen, in der die Attribute ausgewertet werden, oft
31
sogar unmöglich (-> Zirkuläre Abhängigkeiten). Es werden je nach Form der
Abhängigkeiten im Compilerbau zwei Klassen von Attributen betrachtet:
-
synthetische oder abgeleitete Attribute, „S-Attribute“
ererbte Attribute, „I-Attribute“, von „inherited“
Ein S-Attribut wird streng induktiv („bottom up“) ausgewertet. Der
Attributwert zu einem Knoten bestimmt sich aus den Attributwerten zu seinen
Kindern. Der Wert von arithmetischen Ausdrücken ist ein S-Attribut, wie das
Beispiel eben zeigt.
S-Attributgrammatiken sind Attributgrammatiken, die nur auf synthetischen
Attributen arbeiten. So können sie direkt bei den Reduce-Schritten des ParseVorgang eines LR(k)-Parsers berechnet werden.
Ein I-Attributwert zu einem Knoten K darf neben den Attributwerten zu den
Kindern von K auch noch von den Attributwerten zu den linken Geschwistern
von K abhängen.
I-Attributgrammatiken können in einem Top-down-Durchgang von links nach
rechts durch den abstrakten Syntaxbaum ausgewertet werden. Sie können für
jede LL(k)-Grammatik ausgewertet werden.
Wir werden zur Programmanalyse wie folgt vorgehen:
-
Um ein Hoare-Tripel {F} P {G} als korrekt zu beweisen, benutzen wir
den Satz, daß es reicht, 𝐹 → 𝑤𝑙𝑝(𝑃, 𝐺) zu beweisen.
Die Nachbedingung ist dann das einzige Attribut; 𝑤𝑙𝑝(𝑃, 𝐺) kann aus
dem Knoten zu P und seinem Attribut {G} berechnet werden.
Die Nachbedingung ist ein I-Attribut, jedoch mit einer Änderung:
Alle Attribute müssen von rechts nach links ausgewertet werden.
(Folge der Zuweisungsregel !). Das erfordert aber nur minimale
Änderungen der Standardalgorithmen.
32
4.4
Rekursiver Abstieg
Ziel: Konstruiere Ableitungsbaum rekursiv ohne Backtracking. Dabei gibt es
Prozeduren der Form baum(NT), die einander wechselseitig aufrufen. Das
Argument NT ist ein Nichtterminales, das angibt, als was zumindest der
Anfangsteil der restlichen Zeichenkette analysiert werden soll.
Start mit dem Prozeduraufruf
baum(V).
Dabei sei V das Startsymbol der Grammatik.
Ablauf bei einem baum(NT):


Wähle eine Produktionsregel für NT gemäß einer vor den rekursiven
Aufrufen implementierten Funktion A (Auswahl).
Form der gewählten Produktionsregel:
NT → x1 … xn
Bearbeite nacheinander x1,…,xn.
Ist xi Nichtterminal, rufe baum(xi).
Ist xi Terminal, bearbeite xi direkt, füge also das Terminal als
neuen Knoten ein.
Die Auswahlfunktion A hängt von zwei Argumenten ab:
-
dem Nichtterminalen NT,
dem ersten noch unbearbeiteten token der Eingabezeichenkette.
Die Frage ist, wann es eine solche Auswahlfunktion gibt, denn sie ist es, die
den Nichtdeterminismus und damit das Backtracking verhindert.
Antwort: Man braucht eine 𝐿𝑅(1)- Grammatik.
Beispiel:
zu konstruieren: baum(Schleife)
per Rekursion existieren baum(bed) und baum(prog).
Der dann entstehende Baum hat die Form
33
Schleife
While
baum(bed)
DO
baum(prog)
OD
34
5
Praktikum
Implementieren Sie ein Programm zur Prüfung der Korrektheit
eingegebenen Programms anhand der Hoarsche Logik.
eines
Eingabe: Grammatik G, Hoaresches Tripel {F} P {H}
Ausgabe: Attributierter Baum
Verwenden Sie dafür eine Programmiersprache Ihrer Wahl. Es existiert eine
Referenzimplementierung in Mathematica.
Verwenden Sie die nachfolgende Grammatik:
prog →
zuweisung →
alternative →
schleife →
exp →
erest →
term →
trest →
faktor →
bed →
disj →
drest →
elembed →
SKIP | zuweisung | alternative | schleife |
SKIP:prog | zuweisung:prog |
alternative:prog | schleife:prog
var = exp
IF bed THEN prog ELSE prog FI | IF bed THEN
prog
WHILE bed DO prog OD
term erest
+ term erest | - term erest | e
faktor trest
* factor trest | / factor trest | e
zahl | var | (exp)
disj brest
elembed drest
&& elembd drest | e
exp vlg exp
Benutzen Sie zum Testen mindestens folgende 2 Programme:
fak =
WHILE
DO
fak =
n = n
OD
1;
n > 1
fak * n;
– 1;
WHILE x != y
DO
IF x < y
THEN
z = x;
x = y;
y = z;
FI;
x = x – y;
OD
35
Hinweise:







!
Schreiben Sie einen „Tokeniser“ der das Eingabeprogramm in eine
Zeichenfolge zerlegt.
Schreiben Sie eine manuelle Regelzuweisung.
Konstruiere I-Attributgrammatik G für WHILE-Programme mit der
Nachbedingung als Attribut
Entwickeln Sie einen Algorithmus, der aus dem Eingabeprogramm
einen Baum macht. (Rekursiver Abstieg)
Implementieren Sie einen Algorithmus, zur Ermittlung der
schwächsten Vorbedingung (wlp).
Wenn Sie den Baum in grafischer Form in Mathematica ausgeben
wollen, brauchen Sie eine numerierte Pfeilliste.
Bestimmung der Korrektheit von {F} P {H}
ACHTUNG
Es handelt sich um Ihre Lösung. Bei den Hinweisen handelt es
sich lediglich um Vorschläge. Experimentieren Sie ruhig etwas und
Hinterfragen Sie Lösungsansätze jederzeit.
Herunterladen