home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / spezial / 02 / texte / pascomp.1 < prev    next >
Encoding:
Text File  |  1988-09-19  |  43.6 KB  |  1,008 lines

  1.  
  2.  
  3.                     PASCOMP
  4.  
  5.            PASCAL baut einen Pascal-Compiler
  6.  
  7. Teil 2: Einführung und Grundlagen zum Compilerbau
  8.         von Johannes Velmans
  9.  
  10. Nach  dem  in  der  letzten Ausgabe unser Pascal-Compiler PASCOMP
  11. sowie  der 'Schlachtplan' zu dieser Serie vorgestellt wurde, soll
  12. sich  nun  dieses  Kapitel  mit  einer  Einführung  'Rund  um den
  13. Compilerbau'  befassen.  Sind  erst  einmal  die  Grundlagen  des
  14. Compilerbaus vermittelt - das Drumherum, das Wie, Warum und Wieso
  15. geklärt, kann es mit vollem Elan an's 'Eingemachte' gehen.
  16.  
  17. Nun  gut.  Was  ein Compiler (zu deutsch: Übersetzer) ist, dürfte
  18. eigentlich  jeder  unserer  Leser im Prinzip wissen - es sei hier
  19. deshalb  auch nur der Vollständigkeit halber noch einmal erwähnt:
  20. Ein  Compiler  ist  ein  Werkzeug,  mit  dessen Hilfe Sätze einer
  21. Quellsprache  (Programmiersprache  mit  im  allgemeinen  für  uns
  22. Menschen    einigermaßen    verständlichen   Formulierungen)   in
  23. gleichbeutende  Sätze einer Zielsprache (im allgemein die Maschin
  24. ensprache  eines  Computers)  transformiert  werden  können.  Die
  25. Zielsprache    muß   dabei   allerdings   nicht   unbedingt   die
  26. Befehlssprache einer solchen, real existierenten Maschine sein.
  27.  
  28. Wird    nämlich    die   Quellsprache   auf   eine   theoretische
  29. Zwischensprache   einer   abstrakten   Maschine  als  Zielsprache
  30. abgebildet,    so   benötigt   man   zur   'Abarbeitung'   dieser
  31. Zwischensprache einen Interpreter, der die Implementierung dieser
  32. abstrakten   Maschine   auf  dem  wirklich  vorhandenen  Computer
  33. darstellt   und   das   Zwischensprachenprogramm  anweisungsweise
  34. ausführt.  Ein  Beispiel hierfür ist das UCSD-PASCAL, welches auf
  35. einen   P-Code   (Pseudo-Code)   hin  compiliert,  der  dann  zur
  36. Programmausführung  einen Interpreter für diesen P-Code benötigt.
  37. Diesem  Prinzip  wird  auch  PASCOMP  folgen, wodurch er auf eine
  38. Vielzahl von Maschinen leicht implementiert werden kann.
  39.  
  40. Noch  eine  Anmerkung zur generellen Implementierung von modernen
  41. Programmiersprachen: Compiler werden heutzutage nicht mehr in Ma-
  42. schinensprache  (man  erinnere  sich  an FORTRAN-Zeiten), sondern
  43. sehr  oft in höheren Programmiersprachen ausgeführt. Dabei werden
  44. schon  bestehende  Programmiersprachen  zum 'Bau' eines minimalen
  45. 'Kerns'  der neuen Sprache verwendet, so daß aufbauend auf diesen
  46. Kern  der vollständige Sprachumfang mit all seinen Besonderheiten
  47. und  Raffinessen in der neuen Sprache selbst implementiert werden
  48. kann. Diese Vorgehensweise hat, wie alles in der Informatik, auch
  49. eine  entsprechende englische Bezeichnung: 'Bootstrapping' - oder
  50. frei  übersetzt: die Sprache zieht sich á la Münchhausen am eige-
  51. nen  Schopf aus dem Sumpf. Für dieses Verfahren gibt es auch eine
  52. besondere  Darstellungsweise, welche diese Technik kurz und präg-
  53. nant veranschaulicht: die T-Diagramme (Abb. 1).
  54.  
  55.  
  56.     ┌─────────┐
  57.     │ Q     Z │
  58.     └──┐   ┌──┘
  59.        │   │
  60.        │ i │
  61.        └───┘
  62.  
  63.  
  64. Das  T-Symbol hat dabei die folgende Bedeutung: Eine Quellsprache
  65. Q   wird  von  einem  Compiler,  der  selbst  in  der  Sprache  I
  66. implementiert  ist,  in die Zielsprache Z überführt. Wenn man nun
  67. einen  Compiler  für  eine  Sprache  Q  in  der  Sprache  Y der Z
  68. ielmaschine  implementieren  möchte,  so  geschieht dies ziemlich
  69. einfach   mit   Hilfe   eines  schon  bestehenden  Compilers  für
  70. beispielsweise  eine  Quellsprache  H. Es braucht also nicht mehr
  71. der  gesamte  Compiler  von Hand in der Zielsprache - das kann im
  72. Extremfall  Maschinensprache  sein  -  geschrieben werden. Die T-
  73. Diagramme in der Abbildung 2 verdeutlichen diese Vorgehensweise.
  74.  
  75.  
  76.  
  77. Compiler - Implementierung
  78.  
  79.  ┌─────────┐     ┌─────────┐     ┌─────────┐
  80.  │H       Y│     │Q       Y│     │Q       Y│
  81.  └──┐   ┌──┘     └──┐   ┌──┘     └──┐   ┌──┘
  82.     │   │           │   │           │   │┌─────────┐
  83.     │ Y │           │ H │           │ H ││H       Y│
  84.     └───┘           └───┘           └───┘└──┐   ┌──┘
  85.                                             │   │
  86.   existiert       herstellen                │ Y │
  87.                                             └───┘
  88.  
  89.  
  90.                       ┌─────────┐
  91.                       │Q       Y│
  92.                       └──┐   ┌──┘
  93.        ========>         │   │
  94.                          │ Y │
  95.                          └───┘
  96.  
  97.  
  98.  
  99. Unabhängig  von der gewählten Programmiersprache zur Implementie-
  100. rung  des  Compilers  haben  sich im Laufe der Zeit immer stärker
  101. zwei  für  die  Programmentwicklung wichtige Kriterien herauskri-
  102. stallisiert:
  103. Zum  einen  sollte sich die grammatikalisch e Struktur einer Pro-
  104. grammiersprache in der Struktur ihres Compilers widerspiegeln -
  105.  und zum anderen die Tatsache, daß eine komplizierte Programmier-
  106. sprache  einen  komplizierten  Compiler  mit  sich  bringt  (nach
  107. N.Wirth).  Gerade  letzteres wird se hr deutlich, wenn man Imple-
  108. mentierungen  von  PASCAL  mit  denen von ADA vergleicht. PASCAL-
  109. Implementierungen  kommen  im  Durchschnitt  auf 3000-5000 Zeilen
  110. Programmtext, eine ADA-Implementierung benötigt dagegen ein Viel-
  111. faches  davon. Der Untersc hied ist auch anhand der Größe der je-
  112. weiligen  Systeme  zu sehen: Pascal gibt es mittlerweile fast auf
  113. jedem  auch  noch  so kleinen Heim-Computer, ADA ist dagegen erst
  114. auf  den neuen 16/32-Bit- Rechnern mit entsprechender Speicherka-
  115. pazität einsetzbar.
  116.  
  117.  
  118. Vorzüge und Nachteile der Übersetzung bzw. Interpretation
  119.  
  120. Was  ist  denn  da nun besser? Compiler oder Interpreter? Gibt es
  121. überhaupt einen Favoriten? Am besten kann man diese tiefgreifende
  122. Frage  mit der Antwort eines Schülers während einer Abiturprüfung
  123. beantworten:  'Das  kommt  ganz darauf an'. Nun, einen generellen
  124. Vorteil  des  Compilers  gegenüber dem Interpreter gibt es nicht:
  125. Beide   können  in  Abhängigkeit  der  jeweiligen  spezifizierten
  126. Fragestellung  die  besseren  Lösungen bieten. Mit solchen Fragen
  127. schlagen wir uns aber nicht herum, sondern verweisen nur kurz auf
  128. einige Vor- und Nachteile:
  129.  
  130. Die    Nachteile    der    Interpretation   klar.   Die   längere
  131. Ausführungszeit   lassen   die   Benutzer   von  rechenintensiven
  132. Programmen  meist lieber auf einen Compiler umsteigen, da für sie
  133. nur die schnelle Ausführung eines Programmes zählt. Außerdem sind
  134. ie Programmentwicklungszeiten für solche Mathe-Programme meist so
  135. klein,  daß sich auch in der Entwicklungsphase keine gravierenden
  136. Unterschiede  in  der  Verwendung  von Compilern und Interpretern
  137. ergeben.  Der  Grund für die geringere Ausführungsgeschwindigkeit
  138. eines Interpreters ist einleuchtend:
  139.  
  140. Er  übersetzt  bzw.  interpretiert  ein Programm stückchenweise -
  141. Anweisung für Anweisung wird analysiert und ausgeführt.
  142.  
  143. Gerade darin liegt aber auch schon eine der Stärken des Interpre-
  144. ters:  Er  bietet ein höheres Maß an Flexibilität gegenüber über-
  145. setzten  Sprachen  - Programmteile können schnell getestet, geän-
  146. dert  und  schrittweise  ausgeführt  werden. Da sind außerdem die
  147. größere  Absturzsicherheit  des Systems sowie die einfache Imple-
  148. mentierung  von  Post-Mortem-Dumps (PMD) bei Programm-Fehlern zur
  149. Fehlererkennung  und  -behebung zu nennen. Ein einfaches schritt-
  150. weises  Ausführen und verfolgen eines Programms auf der Ebene der
  151. Quellsprache  ist bei compilierten Sprachen in dieser Form derart
  152. einfach nicht möglich.
  153.  
  154. Noch  eines  zur langsameren Ausführungsgeschwindigkeit: Sie wird
  155. nicht  nur  durch den relativ langsamen Vorgang des schrittweisen
  156. interpretierens  hervorgerufen, sondern auch durch einen Umstand,
  157. der am besten durch ein Beispiel verdeutlicht wird. Gegeben seien
  158. folgende PASCAL-Stücke:
  159.  
  160.  
  161.        var  x : array(1..1000)of t
  162.             y : t;
  163.             BEGIN ....
  164.             x[5] := y;
  165.             ....
  166.             END.
  167.  
  168. Die  Zuweisung  des  Wertes der Variablen y an das fünfte Element
  169. des  Feldes  x  erzwingt  generell  eine Grenzbereichsüberprüfung
  170. durch einen Interpreter. Ein halbwegs intelligenter Compiler wür-
  171. de  bei  dieser Zuweisung aber schon während der Übersetzungszeit
  172. feststellen,  daß eine solche Prüfung während der Programmausfüh-
  173. rung  überflüssig  ist:  Zur  Indizierung wird eine Konstante und
  174. keine  Variable  verwendet - und der Wert dieser Konstanten liegt
  175. im  definierten  Bereich  des Feldes. Ein Interpreter kann dieses
  176. nicht  erkennen  und müßte bei jeder Indizierung eines Feldes mit
  177. oder  ohne  Konstante eine 'Laufzeitüberprüfung' durchführen. Ein
  178. erheblicher  Zeitverlust  tritt natürlich auf, wenn die obige Zu-
  179. weisung noch zusätzlich in einer Schleife liegen würde.
  180.  
  181. Bei  all  den  Nachteilen  eines Interpreters darf man aber nicht
  182. vergessen,  daß er in der Regel den Quelltext sofort ausführt und
  183. somit die langen Übersetzungszeiten eines Compilers wegfallen. In
  184. der  Tat  ist  dies  in  der  Test-  und  Entwicklungsphase eines
  185. Programms  von großer Bedeutung. Profitieren doch gerade hier die
  186. Anwender  des  Interpreters  von den Möglichkeiten der Trace- und
  187. Debug-Optionen.   Daher   wird   oftmals   ein   Programm   quasi
  188. 'interaktiv'   implementiert,  getestet  und  nach  erfolgreichem
  189. Abschluß  mit  einem Compiler auf Geschwindigkeit optimiert. Dies
  190. wird   in   den   Ohren   unzähliger   Turbo-Pascal-Programmierer
  191. befremdend  klingen,  da  hier  die  Entwicklungszeit  mit  einem
  192. Compiler   wohl   schneller   vonstatten  geht  als  mit  manchem
  193. Interpreter,  doch  ist  die  Programmiersprache Pascal nicht das
  194. 'non plus ultra' für jedes Implementierungsproblem.
  195.  
  196. Schließlich  gibt es ja auch noch andere Sprachen als Pascal. Man
  197. denke  da beispielsweise an die funktionalen Programmiersprachen,
  198. deren lange Reihe von LISP angeführt wird. Zu PROLOG-losen Zeiten
  199. waren  LISP-Interpreter  die  Sprache  für die Entwickler von KI-
  200. Programmen  (Expertensysteme,  automatische Beweiser, ....). LISP
  201. hat ein weit über Pascal hinausgehendes, sehr mächtiges Konzept -
  202. nämlich  Funktionen als Ergebnisse von Funktionsaufrufen zu über-
  203. geben.  In  der KI verdrängte LISP imperative Programmiersprachen
  204. wie Pascal für derartige Problemstellungen von der Liste der mög-
  205. lichen  Implementierungssprachen.  Es  ist bekannt, daß auch LISP
  206. mittlerweile  erfolgreich  übersetzt wurde und kommerziell zu be-
  207. ziehen  ist,  so daß auch hier nach dem Motto 'Entwickeln mit In-
  208. terpreter, compiliert anwenden' verfahren werden kann.
  209.  
  210. PROLOG  wurde  bereits  erwähnt.  Diese  Sprache  gehört  zu  den
  211. objektorientierten  Programmiersprachen.  Die  Sprache ist dabei,
  212. die  Stellung von LISP in der KI zu übernehmen. Obwohl PROLOG für
  213. Informatikverhältnisse  relativ alt ist (LISP übrigens auch), hat
  214. die  Sprache die Stellung, die ihr in der Programmiersprachenwelt
  215. eigentlich gebührt, noch nicht erreicht. Gründe dafür gibt's vie-
  216. le.  Compiler  gibt es für PROLOG zwar schon, aber nur mit großen
  217. Einschränkungen.  Den  mit  Turbo-PROLOG  übersetzten  Programmen
  218. fehlt  beispielsweise  die Eigenschaft, sich während der Laufzeit
  219. zu  verändern  -  was  für einen Interpreter eine der leichtesten
  220. Übungen  ist.  Das heißt, es können keine neuen Regeln und Fakten
  221. zur bestehenden Regelmenge hinzugefügt werden. Damit hat man aber
  222. auf  eines der mächtigsten Konzepte von PROLOG verzichten müssen.
  223. Für  vollständiges PROLOG bleibt bis jetzt also nur der Interpre-
  224. ter. An diesem Beispiel sieht man deutlich, daß Interpreter nicht
  225. unbedingt ein Stiefkind der Sprachenimplementierung sind, sondern
  226. auch eine echte Alternative zum Compiler darstellen können.
  227.  
  228.  
  229. Aufbau eines Compilers
  230.  
  231.                          ┌────────────────┐
  232.   (Programmtext)         │    Symbol      │
  233.   ──────┬───────     ┌─> │                ├─>─────────────┐
  234.         │            │   │   Tabelle      │               │
  235.         │            │   └────────────────┘               │
  236.         │            │           │                        │
  237.         │            │           │                        │
  238.         │            │           │                        │
  239.  ┌──────────────┐    │   ┌────────────────┐          ┌─────────┐
  240.  │ lexikalische │    │   │ syntaktische   │          │ seman-  │
  241.  │              ├────┴─> │                ├────────> │  tische │
  242.  │ Analyse      │        │ Analyse        │          │ Analyse │
  243.  └──────────────┘        └────────────────┘          └─────────┘
  244.                                                           │
  245.                                                           │
  246.                                                           │
  247.                                                     ┌───────────┐
  248.                                                     │   Code-   │
  249.                                                     │           │
  250.                                                     │ erzeugung │
  251.                                                     └───────────┘
  252.  
  253.  
  254.  
  255. 1. Der Scanner
  256.  
  257. Der  Scanner stellt die erste Verarbeitungsphase in der Compilie-
  258. rung eines Quellprogramms dar. Kurze Anmerkung: Eine Phase (engl.
  259. Pass)  ist  ein  gesamter Zyklus in der Verarbeitung einer Datei,
  260. der Eingabe, Verarbeitung und Ausgabe beinhaltet. Die Aufgabe des
  261. Scanners besteht darin, die Grundsymbole des Programms - das sind
  262. Bezeichner  (von  Variablen, Prozeduren etc.), Literale (Zeichen-
  263. ketten  wie  z.B.  'Was nun ?'), Schlüsselwörter (z.B. BEGIN, END
  264. usw.) und Separatoren (Semikolon, Leerzeichen...) zu erkennen und
  265. auf eine Symbolfolge (Tokenfolge) abzubilden. Dabei wird außerdem
  266. noch  analysiert,  ob der Quelltext lexikalisch korrekt ist, d.h.
  267. entsprechen  die unterschiedlichen Bestandteile einer Programman-
  268. weisung  der Sprachdefinition und sindkeine undefinierten Zeichen
  269. enthalten.
  270.  
  271. Durch  die  Abbildung  von  ganzen  Wörtern  auf  ein Symbol wird
  272. erheblich  Speicherplatz  gespart  und  die  Zugriffszeit auf ein
  273. beliebiges   Symbol  des  Quelltextes  im  Verlauf  der  weiteren
  274. Übersetzungsschritte  deutlich  verkürzt.  Ein  weiterer  Vorteil
  275. dieser Vorgehensweise ist, daß für die Übersetzung nicht relevan-
  276. te Zeichenfolgen (Kommentare, Leerzeichen) eliminiert werden kön-
  277. nen.  Nach  der  Scanner-Phase  ist der Quelltext für die weitere
  278. Übersetzung dann nicht mehr notwendig, da sämtliche weiteren Ope-
  279. rationen  auf die vom Scanner hergestellte Symbolfolge angewendet
  280. werden.  Eine  Ausnahme bilden hier protokollierte Fehlerausgaben
  281. mit Quelltext-Listings.
  282.  
  283. Oft wird die Frage gestellt, ob es überhaupt lohnenswert ist, die
  284. lexikalische  Analyse separat zu betrachten. Warum wird sie nicht
  285. direkt  mit  in die syntaktische Analyse (s. Parser) aufgenommen?
  286. Die  Antwort  ist  relativ  einfach:  Die  Trennung dieser beiden
  287. Verarbeitungschritte  erlaubt  eine Optimierung bezüglich der je-
  288. weiligen  Aufgabenstellung, so daß insgesamt eine Zweiteilung des
  289. Problems  den  Compiler  schneller macht als eine Vermischung der
  290. beiden Aufgaben.
  291.  
  292. Denn:  Die lexikalische Analyse ist in der Lage, effizientere Er-
  293. kennungsstrategien (z.B. Erkennung von längsten Mustern) durchzu-
  294. führen als die Syntaxanalyse. Außerdem arbeitet die Syntaxanalyse
  295. dann auf einer Symbolfolge, die keine Kommentare oder Separatoren
  296. mehr  enthält,  wodurch  der Leseprozeß der vom Scanner erzeugten
  297. 'Token'-Datei  beschleunigt  und  der  Speicherplatzbedarf gering
  298. gehalten  wird.  Im  Übrigen  gilt  auch hier die Devise, daß ein
  299. stark  modularisiertes  Programm die Lesbarkeit und spätere Wart-
  300. barkeit des Programms sehr fördert.
  301.  
  302. Die  Trennung  in  diese  beiden Verarbeitungsphasen im Sinne von
  303. Datei  lesen,  verarbeiten  und  Ergebnis ausgeben ist aber nicht
  304. unbedingt  notwendig.  Eine  Modularisierung  muß  nämlich  nicht
  305. unbedingt einen Mehrphasen-Algorithmus zur Folge haben. In vielen
  306. Compilerimplementierungen  ist  es  so, daß die Syntaxanalyse die
  307. gesamte Übersetzung steuert und nach Bedarf jeweils die lexikali-
  308. sche  Analyse  'anstößt' - und das alles in einer Phase der Über-
  309. setzung.
  310.  
  311. Dies  trifft  auch für PASCOMP zu. Für diejenigen, die sich schon
  312. damit auskennen: in ihm ist ein 'recursiv descent Parser' (LL(1))
  313. implementiert, der als Steueralgorithmus für die einzelnen Verar-
  314. beitungsschritte  durch  den  Aufruf der dazugehörigen Prozeduren
  315. zuständig ist.
  316.  
  317.  
  318. Der Parser (Zerteiler)
  319.  
  320. Wie die Syntax einer lebendigen Sprache (z.B. Englisch) wird auch
  321. die  Syntax  einer  Programmiersprache  (in  unserem Fall Pascal)
  322. durch  eine Grammatik spezifiziert. Der Parser hat nun die Synta-
  323. xanalyse  zur  Aufgabe: Ist der eingegebene Satz (in unserem Fall
  324. der  Quelltext) aus dieser Grammatik ableitbar?. Er muß sich also
  325. anhand  des Satzes durch die Grammatikregeln wühlen und entschei-
  326. den,  ob  eine Folge von Regeln existiert, die diesen Satz erzeu-
  327. gen. Ein Beispiel für einen (un)typi schen Syntaxfehler in PASCAL
  328. wäre etwa:
  329.  
  330.  ...  VAR  a  :  ARRAY  (1..10)  OF  INTEGER;
  331.       î syntax  error ('['expected) ...
  332.  
  333. Schaut  man  sich  diezugehörige  Grammatikregel im User-Manual &
  334. Report an, so steht da:
  335.  
  336. <array type> ::= ARRAY [ <indextype> { , <indextype> } ] OF <com-
  337.                  ponent type>.
  338.  
  339.  Das  heißt  also,  daß dem ARRAY-Symbol ein [-Symbol folgen muß,
  340. wenn der obigen Grammatikregel entsprochen werden soll. Eine run-
  341. de Klammer führt hier somit zum Syntaxfehler.
  342.  
  343. Mehr zu den Grundlagen von Grammatiken gibt es etwas später im
  344. Abschnitt 'Elemente formaler Systeme'.
  345.  
  346. Bleibt  noch  zu  berichten, was denn nun eigentlich das Ergebnis
  347. der  Parser-Arbeit  ist. Nun, es ist im Grunde genommen ganz ein-
  348. fach.  Wurde  für einen Satz eine Ableitung gefunden, so gibt der
  349. Parser  diese in der Form eines Baumes für den nächsten Verarbei-
  350. tungsschritt  aus. Diese Ausgabe kann eine Folge von Regelnummern
  351. sein, wobei jede Grammatikregel genau eine Regelnummer erhält. Es
  352. kann  aber  auch  der  gesamte Ableitungsbaum mit seinen Symbolen
  353. sein.
  354.  
  355. Man  beschränkt sich dabei allerdings nur noch auf die relevanten
  356. Symbole  und  Informationen,  die für die weiteren Verarbeitungs-
  357. schritte  im Compiler benötigt werden. Symbole wie :=, [, ], etc.
  358. werden  dann  nämlich nicht mehr benötigt und einfach aus dem Ab-
  359. leitungsbaum  gestrichen.  So  ein  gestutzter Baum wird auch als
  360. Strukturbaum  bezeichnet und hat gegenüber dem Ableitungsbaum den
  361. Vorteil,  daß  er nur noch semantisch wichtige Informationen ent-
  362. hält  und somit redundante (mehrfach vorhandene , gleiche) Symbo-
  363. le bei der weiteren Verarbeitung nicht mehr berücksichtigt werden
  364. müssen.  Dieser  Strukturbaum  repräsentiert  nun natürlich nicht
  365. mehr  eine  korrekte  Ableitung  gemäß der in der Sprachgrammatik
  366. definierten  Syntax.  Die  Syntax, die dem Strukturbaum an dieser
  367. Stelle der Übersetzung zu Grunde liegt, ist eine abgemagerte Form
  368. der  konkreten  Syntax. Sie wird mit 'abstrakter Syntax' bezeich-
  369. net. Zur Erläuterung der abstrakten Syntax sei die Pascal-Gramma-
  370. tikregel für die CASE-Anweisung gewählt.
  371.  
  372. Konkrete Syntax :
  373. <case statement> ::= CASE <expression> OF <case list element> { ;
  374. <case  list element> } END.
  375. abstrakte  Syntax :
  376.   ,,l <case statement> ::= <expression> <caselist element>
  377.      { <ca se list element> }.
  378.  
  379. Die Symbole CASE, OF und ; sind nach der Syntaxanalyse nicht mehr
  380. relevant  und treten daher auch nicht mehr in der abstrakten Syn-
  381. tax auf.
  382.  
  383. Wenn  das  Quellprogramm die Übersetzung bis zu diesem Punkt ohne
  384. Fehlermeldungen  überstanden haben sollte, dann geht's nun in die
  385. letzte Phase der Analyse.
  386.  
  387.  
  388. Semantische Analyse
  389.  
  390. Die  Aufgabe  der semantischen Analyse besteht in der Überprüfung
  391. der  statischen  Semantik von Programmen. Auch hier soll die Pro-
  392. blemstellung  durch ein Beispiel in einem Pascalprogramm verdeut-
  393. licht  werden.  Gegeben  sei das folgende lexikalisch und syntak-
  394. tisch korrekte PASCAL-Programm:
  395.  
  396.  
  397. PROGRAM demo;
  398. VAR a : ARRAY [Real] OF Integer;
  399. BEGIN
  400. a[1.23] := 0;
  401. END.
  402.  
  403. Laut den Grammatikregeln ist syntaktisch zwar alles korrekt, aber
  404. die  Indizierung  eines  Feldes  mit Realzahlen ist dennoch nicht
  405. möglich.  Ein  weiterer Blick in das User-Manual & Report schafft
  406. da sofort Klarheit. Dort steht nämlich:
  407.  
  408. TYPE  A = ARRAY [T1] OF T2;
  409.  
  410. ......  and  T1  is a scalar or subrange type (where type integer
  411. and real are not allowable index type)....
  412.  
  413.  
  414. Alles  klar? Neben solchen Kleinigkeiten wird in der semantischen
  415. Analyse  auch  die  Gültigkeit von Definitionen und Typregeln ge-
  416. prüft.  Man  kennt's:  Typ  A ist kompatibel zu Typ B genau dann,
  417. wenn... usw. Neben der statischen Semantik, die während der Über-
  418. setzungszeit  geprüft  werden  kann,  gibt es noch die dynamische
  419. Semantik, deren Überprüfung während der Laufzeit stattfinden muß.
  420. Hier  ist es die Aufgabe der Codeerzeugung, entsprechende Codese-
  421. quenzen  für die Prüfungen während der Programmausführung bereit-
  422. zustellen. Beispiele für dynamische Semantik sind:
  423.  
  424. (i) x(k)
  425. Liegt der Wert innerhalb der vereinbarten Indexgrenzen des Arrays
  426. x ?
  427.  
  428. (ii) x/y:
  429.  Ist der Wert von y gleich 0 ?
  430.  
  431. (iii) VAR y : INTEGER;
  432.            x: 1..100;
  433.            x := y:
  434.  
  435. Liegt der Wert von y innerhalb des für x festgelegten Bereichs?
  436.  
  437. Prinzipiell  geschieht in der semantischen Analyse folgendes: Der
  438. vom  Parser  übergebene Strukturbaum wird mit einer Reihe von At-
  439. tributen  'geschmückt'.  Das  können Typattribute, Grenzwerte für
  440. Ausschnittstypen  usw.  sein. Mit diesem attributierten Struktur-
  441. baum  und  unter  Zuhilfenahme von verschiedenen Tabellen, die in
  442. früheren Schritten der Übersetzung angelegt wurden (Konstantenta-
  443. belle,  Symboltabelle  usw.),  ist die Codeerzeugung in der Lage,
  444. Code für die Zwischensprache herzustellen.
  445.  
  446.  
  447. Codeerzeugung
  448.  
  449. Als Schnittstelle zwischen der semantischen Analyse und der Code-
  450. Erzeugung  steht der sogenannte Berechnungsgraph, der aus dem at-
  451. tributierten  Strukturbaum  hergestellt  wird, indem die Struktur
  452. dieses Baumes vereinfacht wird. Dies geschieht z.B. durch Weglas-
  453. sen von deklarativen Teilen des Strukturbaumes. Deklarationen von
  454. Variablen tragen ja nicht direkt zur Codeerzeugung bei. Durch das
  455. Hinzufügen der Ausführungsreihenfolge (z.B. von geklammerten Aus-
  456. drücken)  entsteht so aus dem attributierten Strukturbaum ein Be-
  457. rechnungsgraph,  aus dem dann der Maschinencode für die reale Ma-
  458. schine gewonnen werden kann.
  459.  
  460. Wie anfangs schon erwähnt, erzeugt PASCOMP einen Zwischencode (P-
  461. Code),  der  später von einem Interpreter ausgeführt werden soll.
  462. Wie  diese  virtuelle  P-Code-Maschine im Gegensatz zu den realen
  463. Maschinen  funktioniert wird natürlich noch deutl ich ausgeführt.
  464. Soviel vorweg: Sie besitzt keine ('Prozessor'-)Register und führt
  465. sämtliche Operationen auf dem Laufzeitkeller (Stack, Stapel) aus.
  466. Ansonsten  arbeitet  die  P-Code-Maschine  vergleichbar mit einer
  467. realen Maschine.
  468.  
  469. Bevor  jedoch  der Maschinencode erzeugt wird, ist es zweckmäßig,
  470. sich  die  Eigenschaften  der realen Maschine vor Augen zu führen
  471. (Maschinenbefehle,  Register, Adressierungsarten, Speicherklassen
  472. usw.). In den meisten Fällen reicht es aus, wenn nur ein Teil der
  473. zu  Verfügung stehenden Maschinen-Befehle Verwendung finden. Dazu
  474. wird meist ein Dokument erstellt, das die Abbildung der Sprache L
  475. (Zwischensprache  aus  dem Berechnungsgraphen) auf die Maschine M
  476. spezifiziert. In diesem Dokument sollten folgende Punkte beachtet
  477. werden:
  478.  
  479. 1.  Die  Eigenschaften der realen Maschine M:
  480.     - Speicherklassen
  481.     - Adressierungsarten
  482.     - Maschinenbefehle
  483.  
  484. 2. Speicherabbildungen:
  485.    Wiewerden Standardtypen (INTEGER, REAL, BOOLEAN etc.) abgebil-
  486.    det?
  487.  
  488. 3. Maschinenzustände,  Register  und Speicherverwendung:
  489.    - Codespeicher
  490.    -  globale Daten
  491.    - Laufzeitkeller
  492.    - Halde (Heap)
  493.    - Vorbelegung von Registern (Keller- pegel, aktuelle Laufzeit-
  494.      schachtel, Adresse des Displayvektors, ... )
  495.  
  496. Wer  sich  von  den  einzelnen  Fachbegriffen  überrumpelt fühlt,
  497. braucht  keine  Sorge  zu  haben - in den entsprechenden Kapiteln
  498. wird noch näher auf diese Begriffe eingegangen.
  499.  
  500.  
  501. Elemente Formaler Systeme
  502.  
  503. Die Überschrift dieses Abschnitts deutet es schon an: jetzt kommt
  504. ein  bißchen  Theorie.  Wer bis hierhin durchgehalten hat, sollte
  505. unbedingt  weiterlesen, da die folgenden theoretischen Grundlagen
  506. unentbehrlich für das Verständnis der weiteren Folgen sind.
  507.  
  508. Außerdem:  Ein  wenig Theorie am Anfang erspart später mühevolles
  509. Ausprobieren  und  Neuentwickeln.  Denn wem ist es nicht schon so
  510. ergangen,  daß  er  tagelang ein Problem in den Griff zu bekommen
  511. suchte,  dessen Lösung schon seit Jahren in der obersten Ecke des
  512. Bücherregals  verstaubte.  Ein kleines bißchen Vorbereitung hilft
  513. da schon enorm...
  514.  
  515. Den  Anfang unserer Theoriestunde soll der Begriff des 'endlichen
  516. Automaten'  machen. Endlicher Automat? Jeder, der sich intensiver
  517. mit  der  Computerei  beschäftigt,  hat diesen Begriff sicherlich
  518. schon  einmal  gehört.  Für diejenigen, die nichts damit anfangen
  519. können, sei er hier kurz erklärt:
  520.  
  521. Man  kann  sich  einen endlichen Automaten als eine Maschine vor-
  522. stellen, die bei einem gegebenem Eingabestring jedesmal genau ein
  523. Zeichen aus dem Eingabepuffer in ihr 'endliches Gedächtnis' liest
  524. und - abhängig vom jeweiligen internen Zustand und dem eingelese-
  525. nen Zeichen - ihren Zustand verändert (s.Abb.4).
  526.  
  527.  
  528.    endlicher Automat
  529.  
  530.  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬──────────────┐
  531.  │ x │ y │ z │ y │ y │ z │...│...│...│...│ ............ │
  532.  └─┬─┴─┬─┴───┴───┴───┴───┴───┴───┴───┴───┴──────────────┘
  533.    │                                  Eingabestring
  534.    │   │
  535.    │
  536.    │   │
  537.    │
  538.    │   │         ┌─────────────────┬─────────> neuer interner
  539.    │      ┌──────┴─────────┐┌──────┴───────┐          Zustand
  540.    │   └> │  endliches     ││  Interner    │
  541.    └────> │                ││              │
  542.           │  Gedächtnis    ││  Zustand     │
  543.           └────────────────┘└──────────────┘
  544.  
  545.  
  546.  
  547. Wenn mit dem Paar (interner Zustand, gelesenes Zeichen) ein neuer
  548. interner  Zustand bestimmt wurde, so wird der Zeiger auf den Ein-
  549. gabepuffer  genau  um eine Position weiter gesetzt, so daß dieser
  550. Zeiger auf das nächste Element der Eingabe verweist.
  551.  
  552. Wer  die Informatik ein wenig kennt, weiß, daß das hier in Worten
  553. dargestellte Modell des endlichen Automaten nicht nur sehr schön,
  554. sondern  vor allen Dingen auch kurz in formaler Art und Weise be-
  555. schrieben werden kann:
  556.  
  557. Ein endlicher Automat A, auch Akzeptor genannt, ist als ein 5-Tu-
  558. pel wie folgt definiert:
  559.  
  560. A = (T,Q,R,q[0],F)
  561.  
  562. Dabei haben T, Q, R, q[0], n und F folgende Bedeutung:
  563.  
  564. - T ist ein festes, aber beliebiges Alphabet. Es darf nur endlich
  565. viele  Element  enthalten  und kann nicht leer sein. Ein Alphabet
  566. kann zum Beispiel der Zeichensatz einer Maschine, die Grundsymbo-
  567. le  einer  Programmiersprache,  syntaktische  Begriffe usw. sein.
  568.  
  569. Fügt  man eine endliche Anzahl von Zeichen aus T zusammen, so er-
  570. hält  man  eine Zeichenreihe (String) über T. Die Menge, die alle
  571. diese Zeichenreihen enthält, wird mit T,,h+,,n bezeichnet und ist
  572. formal  definiert durch :
  573.  
  574. T+ := {x[t1]...x[n] |n>= 1;  x[i] aus T}
  575.  
  576. Die  leere  Zeichenreihe  wird  mit  e bezeichnet und
  577. T* := {x[1]...x[n]| n >= 0;  x[i] aus T} = T+ U {e}
  578.  
  579. Die  Länge  einer  Zeichenreihe  x[1]...x[n] wird mit |x| = n no-
  580. tiert. Spezialfall |e| = 0.
  581.  
  582. -  Q Ist eine endliche Menge von internen Zuständen des endlichen
  583. Automaten,  deren  Elemente  im  Folgenden immer mit q[i],i >= 0,
  584. bezeichnet werden.
  585.  
  586. Interne  Zustände  werden  im  weiteren Verlauf zur Vereinfachung
  587. auch 'Zustände' genannt.
  588.  
  589. Befindet  sich der endliche Automat im Zustand q[i] aus Q und ist
  590. die  noch nicht abgearbeitete Zeichenfolge y[1]...y[n] =: y, y[i]
  591. aus T, so schreibt man dies auch als q[i]y.
  592.  
  593. -  R ist eine endliche Regelmenge. Sie gibt an, auf welchen neuen
  594. Zustand das Paar aus Eingabezeichen und aktuellen interen Zustand
  595. abgebildet  wird.  Wenn der endlicher Automat zum Beispiel im Zu-
  596. stand q[1], das gelesene Zeichen ein 'x' ist und das Paar (q[1]x)
  597. auf q[4] abgebildet wird, so schreibt man dies als:
  598.  
  599.   q[1]x  ->  q[4]
  600.  
  601. Das  heißt: Ein Automat A geht genau dann von Zustand q[i] in den
  602. Zustand  q[j]  über  (bei Eingabe x, q[i] -> q[j], wenn x = ty (t
  603. aus  T)  und  q[i]t -> q[j] ein Element der Regelmenge R ist. Zur
  604. besseren  Übersicht  wird  diese Regelmenge im allgemeinen in Ta-
  605. bellenform dargestellt (Beispiel siehe weiter unten).
  606.  
  607. -  q[0] ist der Anfangszustand des Automaten. Darin befindet sich
  608. der  Automat,  bevor  er  das erste Zeichen gelesen hat. Man kann
  609. auch  an  dieser Stelle mehrere Anfangszustände zulassen, aber es
  610. soll ja nicht noch schwerer werden ....
  611.  
  612. -  F  ist eine echte Teilmenge von Q. Sie enthält die Endzustände
  613. des   endlichen   Automaten.  Wenn  es  bei  einer  Eingabe  x  =
  614. x[1]...x[n],  x[i]  aus  T  vorkommt, daß sie Schritt für Schritt
  615. abgearbeitet  wurde (d.h. bis kein neues Zeichen mehr in das end-
  616. liche  Gedächtnis  des Automaten übernommen werden kann), und der
  617. aktuelle  interne  Zustand q[j] auch in F liegt, so sagt man, der
  618. Automat  A  akzeptiert das Eingabewort x (Daher auch die Bezeich-
  619. nung  Akzeptor  anstelle  von endl icher Automat). Alle von einem
  620. Automaten akzepierten Wörter werden zur Sprache L(A) (L = Langua-
  621. ge) zusammengefaßt. Formal schreibt man dies so:
  622.  
  623. L(A):= {x aus T* | q[0]x ->*q[j], q[j] aus F}
  624.  
  625. Der  * über dem ->-Symbol soll andeuten, daß der Übergang in end-
  626. lich vielen Schritten stattfinden muß (es muß nicht unbedingt nur
  627. ein Übergang sein ).
  628.  
  629.  
  630. So,  dies war nun eine ganze Menge Theorie - es wird Zeit für ein
  631. paar Beispiele....
  632.  
  633. Diejenigen, die schon einmal etwas von endlichen Automaten gehört
  634. haben,  werden sich sicherlich in diesem Zusammenhang an das Bei-
  635. spiel des Zigarettenautomaten erinnern (es seien hiermit auch die
  636. Raucher  unter den Lesern begrüßt!). Bei den n achfolgenden Über-
  637. legungen wird davon ausgegangen, daß beim Erscheinen dieses Arti-
  638. kels  eine  Packung Zigaretten immer noch 4,- DM kosten wird (für
  639. etwaige Preissteigerungen übernehmen wir keine Haftung!).
  640.  
  641. Also  -  Sie  stehen  vor  so einem Zigarettenautomaten mit einer
  642. Handvoll  des nötigen Kleingeldes und wollen diesem Gerät die er-
  643. sehnten  Glimmstengel  abgewinnen.  Vorausgesetzt, daß es sich um
  644. ein funktionstüchtiges Gerät handelt, welches nicht die eingewor-
  645. fenen  Münzen  ohne Gegenleistung trotz lautstarken Beschwörungen
  646. und massiven Androhungen in seinem Geldschlund verschwinden läßt,
  647. widerfährt Ihnen doch als ungeduldiger Raucher im Normalfall fol-
  648. gendes:  Sie  geben  dem  Automaten 4 deutsche Mark, er Ihnen die
  649. Zigaretten.  Die  Reihenfolge,  in  der 1,- DM oder 2,- DM Stücke
  650. eingeworfen werden, ist dabei für Sie uninteressant.
  651.  
  652. Wendet  man nun den Formalismus des endlichen Automaten auf unse-
  653. ren  Zigarettenautomaten  an, so ist bei einem Einwurf von 4,- DM
  654. der endliche Automat
  655.  
  656. Zigarettenautomat = (T,Q,R,q[0],F)
  657.  
  658. ...  im Endzustand - es gibt Zigaretten. Solange jedoch nicht ge-
  659. nügend  Münzen  eingeworfen  wurden, ist der Endzustand nicht er-
  660. reicht - es gibt nichts!
  661.  
  662. Die  von  unserem  Beispielautomaten akzeptierte Sprache ist ganz
  663. einfach  zu bestimmen: L(A) ist eine beliebige Folge von 1 und 2,
  664. deren Summe 4 ergeben muß.
  665.  
  666. Eine  vollständige  Anwendung des formalen Konzepts des endlichen
  667. Automaten  auf unser Beispiel ergibt dann:
  668.  
  669. Zigarettenautomat: A =(T,Q,R,q[0],,n,F)
  670.   mit
  671.   T = {1,2}  (1,-  u. 2,- DM Stücke)
  672.   Q = {q[0],q[1],q[2],q[3],q[4]} (interne Zustände)
  673.   F = {q[4]}
  674.   und
  675.   R = {q[0]1 -> q[1],q[0]2 -> q[2],
  676.        q[1]1 -> q[2],q[1]2 -> q[3],
  677.        q[2]1 -> q[3],q[2]2 -> q[4],
  678.        q[3]1 -> q[4]}
  679.  
  680.  
  681. Man sieht: Man sieht nichts!
  682.  
  683. Die  Notation  der Regelmenge ist zwar sehr konkret und platzspa-
  684. rend, aber eben dadurch auch sehr un- übersichtlich. Im allgemei-
  685. nen  beschreibt  man  daher die Elemente der Regelmenge (das sind
  686. Abbildungen)  durch  einen  Übergangsgraphen, der den Vorteil der
  687. Übersichtlichkeit bietet. Ein solcher Graph sieht dann, auf unser
  688. Beispiel bezogen, wie in Abbildung 5 aus.
  689.  
  690.  Zigarettenautomat
  691.                                  2
  692.                         ┌───────────>──────────┐
  693.                         │                      │
  694.                         │                      │
  695.                  1            1          1           1   ┌──────┐
  696. Start >  (q[0])─────>(q[1])─────>(q[2])─────>(q[3])─────>│ q[4] │
  697.                                                          └──────┘
  698.            │                        │
  699.            └───────────>────────────┘
  700.                          2
  701.  
  702.  
  703.  
  704. Diesen  Graphen bezeichnet man häufig auch als Transitionsgraphen
  705. (ein  Wort, das in Insidergesprächen immer sehr gut ankommt). Zum
  706. besseren  Verständnis  enthält Abbildung 6 noch ganz kurz die Er-
  707. klärung der verwendeten Symbole.
  708.  
  709.  
  710. Legende der benutzten Symbole
  711.  ┌───────┐
  712.  │       │
  713.  │       │   Endzustand
  714.  └───────┘
  715.  
  716.  
  717.   (  )       Zwischenzustand
  718.  
  719.  
  720.            m
  721.   (q[i]) ─────> (q[j])  (q[i]m ──>q[j]) aus R mit q ,q aus Q
  722.                                                und   m aus T
  723.  
  724.  
  725.  
  726. Aus dem Graphen wird auch leicht ersichtlich, daß es in q[4] kei-
  727. nen Übergang zu einem anderen Zustand gibt. Endliche Automa- ten,
  728. die  diese Eigenschaft besitzen, werden als unvollständige endli-
  729. che  Automaten bezeichnet. Übrigens: Natürlich kann jeder unvoll-
  730. ständige endliche Automat durch Hinzufügen eines neuen 'Fehlerzu-
  731. standes' in einen endlichen Automaten überführt werden.
  732.  
  733. Soweit  zum  Zigarettenautomaten. Im zweiten Beispiel geht es be-
  734. sonders  für die Programmierer um ein wesentlich praxisorientier-
  735. teres  Thema - und zwar um den Kommentarautomaten für Pascal-Kom-
  736. mentare. Hier gilt ja bekanntlich folgende Regel:
  737.  
  738.  
  739. -  Kommentare werden durch ("*" eingeleitet und mit "*") beendet.
  740. Dazwischen können beliebige Zeichen stehen.
  741.  
  742. Entwickelt  man den Automaten direkt nach "Gefühl", so erhält man
  743. in etwa den Graphen in Abbildung 7.
  744.  
  745. Der Pascal - Kommentarautomat
  746.  
  747.                                   S \ {*}
  748.                                   ┌─<┐
  749.                                   │  │
  750.                                   │  │
  751.                                   │  │
  752.                 (          *         │ *            )   ┌──────┐
  753.  Start > (q[0])────>(q[1])────>(q[2])└────>(q[3])─────> │ q[4] │
  754.                                               │         └──────┘
  755.                                   │           │
  756.                                   └<────────<─┘
  757.                                     S \ {)}
  758.  
  759.  
  760.  
  761. S  vertritt  hier  den gesamten Zeichenvorrat der verwendeten Ma-
  762. schine.   Formalisiert   ergibt   dies:
  763.  
  764.  Kommentarautomat:  A  = (T,Q,R,q[0],F)
  765.   mit
  766.   T  =  S (Zeichenvorrat der Maschine)
  767.   Q = {q[0],..,q[4]}
  768.   F = {q[4]}
  769.   und
  770.   R (diesmal als Tabelle) in Abbildung 8.
  771.  
  772.  
  773. Regelmenge R in Tabellenform
  774.  
  775.   ┌────┬────┬─────┬────┬───┐
  776.   │    │  k │  (  │ *  │ ) │         k ist S \{(,),* }
  777.   ├────┼────┼─────┼────┼───┤
  778.   │q0  │  / │  q1 │ /  │ / │
  779.   ├────┼────┼─────┼────┼───┤
  780.   │q1  │  / │  /  │q2  │ / │
  781.   ├────┼────┼─────┼────┼───┤
  782.   │q2  │ q2 │ q2  │q3  │q2 │
  783.   ├────┼────┼─────┼────┼───┤
  784.   │q3  │ q2 │ q2  │q2  │q2 │
  785.   ├────┼────┼─────┼────┼───┤
  786.   │q4  │  / │  /  │ /  │ / │
  787.   └────┴────┴─────┴────┴───┘
  788.  
  789. Leuchtet doch alles irgendwie ein, oder?
  790.  
  791. Es bleibt zum Schluß noch zu klären, wie man in der Praxis so ei-
  792. nen  endlichen  Automaten  implementiert.  Zwei mögliche Lösungen
  793. bieten  sich  da  sofort  an. Einmal kann der Automat sehr leicht
  794. ausprogrammiert  werden. Eine Schleife wird konstruiert, die eine
  795. Fallunterscheidung  über  sämtliche Zeichen des Alphabets macht -
  796. fertig. Für Automaten mit vielen Übergängen und großen Alphabeten
  797. wird  es  dann  natürlich sehr aufwendig. Eine andere Möglichkeit
  798. besteht  darin,  die  Regelmenge als Matrix irgend wo im Speicher
  799. abzulegen. Einen neuen Zustand erhält man dann, indem man jeweils
  800. die  Matrix  mit aktuellem Zustand und eingelesenem Zeichen indi-
  801. ziert. In Pascal sähe ein e solche Matrix etwa so aus:
  802.  
  803.  
  804. TYPE  Zustände  =  (q0,q1,q2,....);
  805.       Alphabet  = (a,b,c,d,.....);
  806.        Übergang = ARRAY[Zustände,Alphabet] OF Zustände;
  807.  
  808. Allerdings  gibt es in der Regel Probleme mit der Matriximplemen-
  809. tie-  rung.  Aufgrund  der Tatsache, daß die Alphabete doch recht
  810. umfan-  greich  sein  können, und die Matrix im allgemeinen nicht
  811. sehr  stark besetzt ist, wird ein Großteil des zu Verfüg ung ste-
  812. henden  Speicherraumes verschwendet. (Abhilfe können hier dynami-
  813. sche Arrays bieten (kein Pascal !)).
  814.  
  815. Das  war's auch schon zum Thema Automaten. Ein weiteres Werkzeug,
  816. das  man in der Informatik fast immer benötigt und das uns späte-
  817. stens  im  Parser des Compilers von großem Nutzen sein wird, sind
  818. die  Grammatiken.  Grammatiken  stellen  ein Ersetzungssystem für
  819. Zeichenreihen  dar. Mit Hilfe einer Grammatik ist es möglich, be-
  820. ginnend  von  einem Startsymbol und unter Zuhilfenahme von Erset-
  821. zungsregeln, Sätze einer Sprache zu erzeugen, die der Syntax die-
  822. ser Sprache genügen.
  823.  
  824. Dabei  werden  solange Nicht-Terminale (durch Grammatikregeln er-
  825. setzbare  Zeichen)  substituiert,  bis ein vollständiger Satz aus
  826. Terminalen  (nicht  weiter ersetzbaren Zeichen) vorliegt. Den von
  827. einem  Startsymbol  bis  zum fertigen Satz mit Hilfe der entspre-
  828. chenden  Grammatikregeln  stattfindenden  Ersetzungsvorgang nennt
  829. man  Ableitung.  Derartige  Ableitungen werden normalerweise gra-
  830. phisch  in  einer Baumform dargestellt (Ableitungsbaum). Ohne auf
  831. die formale Definition der Grammatiken ein zugehen, soll hier die
  832. dunkle Theorie durch ein Beispiel etwas erhellt werden.
  833.  
  834. Gegeben  sei dazu eine Sprache, die durch die folgende Syntax de-
  835. finiert ist:
  836.  
  837.  (i) Satz      ::= Subjekt Prädikat Objekt.
  838.  
  839. (ii) Subjekt   ::= 'Studenten' |
  840.                    'Programmierer' |
  841.                    'Weihnachtsmänner'.
  842.  
  843. (iii) Prädikat ::= 'lieben' |
  844.                    'kaufen' |
  845.                    'essen'.
  846.  
  847. (iv) Objekt ::=    'Computer' |
  848.                    'Müsli' |
  849.                    'Weihnachtsbäume'.
  850.  
  851.  
  852. Die vier Regeln haben dabei die folgende Bedeutung :
  853.  
  854. (i)
  855. Ein  Satz  besteht aus einem Subjekt, gefolgt von einem Prädikat,
  856. gefolgt von einem Objekt.
  857.  
  858. (ii)
  859. Das  Subjekt  besteht  aus  dem Wort 'Studenten', 'Programmierer'
  860. oder 'Weihnachtsmänner'.
  861.  
  862. (iii)
  863. Das Prädikat kann 'lieben', 'kaufen' oder 'essen' sein.
  864.  
  865.  
  866. (iv)
  867. Das  Objekt ist entweder 'Computer', 'Weihnachtsbäume' oder 'Müs-
  868. li'.
  869.  
  870.  
  871. Die Syntax dieser Sprache ist somit durch die Regeln (i) bis (iv)
  872. fest definiert. Die Nicht-Terminal-Symbole - das sind zur Erinne-
  873. rung  die  Symbole,  die  noch ersetzt werden können - sind Satz,
  874. Subjekt,  Prädikat  und Objekt. Alle übrigen Symbo le sind Termi-
  875. nal-Symbole (Studenten, Programmierer, Weihnachtsmänner,...).
  876.  
  877. Bei allen Regeln unserer Beispielgrammatik fällt auf, daß auf der
  878. linken  Seite  einer  jeden  Regel  immer genau ein Nichtterminal
  879. steht.  Diese  Art  von  Regeln haben einen besonderen Namen. Sie
  880. werden kontextfreie Regeln genannt. Das heißt: Ein Nicht-Terminal
  881. kann  immer  unabhängig  von  seinem  linken und rechten Nachbarn
  882. durch  die  entsprechende Regel der Grammatik ersetzt werden. Der
  883. Vorteil  von  kontextfreien  Grammatiken ist, daß relativ schnell
  884. festgestellt  werden  kann,  ob  ein Satz zur Sprache gehört oder
  885. nicht. Bei einem Compiler bedeutet dies: Ist das eingegebene Pro-
  886. gramm,  das  hier einem Satz der Grammatik der gewählten Program-
  887. miersprache entspricht, syntaktisch korrekt oder nicht?
  888.  
  889. Wir werden spätestens beim Parser und in der semantischen Analyse
  890. sehen,  daß  die Verwendung von kontextfreien Grammatiken alleine
  891. nicht ausreicht, um Programmiersprachen wie Pascal vollständig zu
  892. beschreiben. Wir werden dann weiter erörtern , welche Möglichkei-
  893. ten  es  gibt,  die Vorteile von kontextfreien Sprachen zu nutzen
  894. und  die Nachteile (geringere Mächtigkeit gegenüber kontextabhän-
  895. gige Sprachen) durch etwaige 'Tricks' zu beseitigen.
  896.  
  897. Zurück  zum  Beispiel.  Versuchen  wir den Satz 'Studenten lieben
  898. Müsli'  abzuleiten. Das geschieht ganz einfach, indem wir die Re-
  899. geln  (i) bis (iv) nacheinander anwenden, und zwar folgendermaßen
  900. ('-->'  soll  immer  genau ein Ableitungsschritt sein ):
  901.  
  902. Satz --> Subjekt Prädikat Objekt (mit Regel (i))
  903.  
  904. --> 'Studenten' Prädikat Objekt (mit Regel (ii))
  905.  
  906. --> 'Studenten' 'lieben' Objekt (mit Regel (iii))
  907.  
  908. -->  'Studenten' 'lieben' 'Müsli' (mit Regel (iv))
  909.  
  910. Der  zugehörige  Ableitungsbaum  sieht wie in Abbildung 9 gezeigt
  911. aus.
  912.  
  913.   Ableitungsbaum
  914.                           ───────
  915.                          ( Start )
  916.                           ───────
  917.                              │
  918.         ┌────────────────────┼────────────────────┐
  919.         │                    │                    │
  920.         │                    │                    │
  921.      ───┴─────           ────┴─────            ───┴────
  922.     ( Subjekt )         ( Prädikat )          ( Objekt )
  923.      ─────────           ──────────            ────────
  924.         │                    │                    │
  925.         │                    │                    │
  926.         │                    │                    │
  927.         │                    │                    │
  928.      ───┴───────         ────┴───              ───┴───
  929.     ( Studenten )       ( lieben )            ( Müsli )
  930.      ───────────         ────────              ───────
  931.  
  932.  
  933.  
  934.  
  935. Der  Ersetzungsprozeß  startet  an  der Wurzel des Baumes mit dem
  936. Nicht-Terminal 'Satz'. Gehört der zu überprüfende Satz zu der von
  937. der  Grammatik erzeugten Sprache, so ergeben die Blätter des Bau-
  938. mes  von  links  nach rechts gelesen gerade eben die sen Satz. In
  939. diesem  Beispiel  ist der Satz 'Studenten lieben Müsli' somit ein
  940. gültiger Satz der Sprache, die von dieser Grammatik erzeugt wird.
  941. Soweit das Beispiel - und jetzt kurz das Ganze formal:
  942.  
  943. Eine Grammatik G ist ein 4-Tupel G = (T,N,P,Z) mit:
  944.  
  945. - T ist die Menge der Terminal- Symbole.
  946.  
  947. - N ist die Menge der Nicht-Terminal- Symbole.
  948.  
  949. - Z aus N ist das Startsymbol der Grammatik.
  950.   Das Startsymbol bildet die Wurzel des Ableitungsbaumes.
  951.  
  952. -  P ist eine Menge von Ersetzungzregeln. In einigen Grammatikde-
  953. finitionen  steht  hier das Wort 'Produktionen' für Ersetzungsre-
  954. geln, womit das Gleiche gemeint ist.
  955.  
  956. -  Bezüglich T und N gilt die Vereinbarung, daß beide Mengen dis-
  957. junkt sein müssen.
  958.  
  959.  
  960. Die  Sprache  L(G),  die  von einer Grammatik G erzeugt wird, ist
  961. definiert  als:
  962.  
  963.   -  L(G) := {x | Z =>*x; x aus T*}
  964.  
  965. Z=>*x bedeutet dabei, daß x aus Z in endlich vielen Schritten ab-
  966. leitbar (Ableitungsbaum von der Wurzel zu den Blättern be- trach-
  967. ten),  bzw.  x auf Z in endlich vielen Schritten reduzierbar sein
  968. muß  (Ableitungsbaum  von  den Blättern zur Wurzel verfolgen). In
  969. unserem  Beispiel  ist  L(G)  sogar  endlich  und kann leicht be-
  970. stimmt werden.
  971.  
  972. Zum  Schluß noch zur Notation der Grammatikregeln. In aller Regel
  973. wird  zur Darstellung von Grammatiken die Backus- Naur-Form (BNF)
  974. verwendet.  Sie wurde zur Definition der Program- miersprache Al-
  975. gol60  erstmals  benutzt.  Auch in unserem 'Müsli'-Beispiel wurde
  976. diese  Notation verwendet. Die BNF-Struktur ist recht einfach und
  977. leuchtet  sofort  ein.  Linke und rechte Regelseiten werden durch
  978. '::='  getrennt,  Terminal-Symbole werden in Hochkom- mata einge-
  979. schlossen,  Alternativen  stellt  man  durc h '|' dar. Jede Regel
  980. wird  mit  einem  '.'  beendet.  Zur BNF-Notation existieren noch
  981. zahlreiche Erweiterungen hinsichtlich Klammerung, Optional- klam-
  982. merung,  Konkatenation  usw. Diese Spezialform wird mit EBNF (Ex-
  983. tended Backus-Naur-Form) bezeichnet. Auf sie soll hier jedoch nur
  984. hingewiesen werden.
  985.  
  986. Ebenfalls erinnert werden soll an die Möglichkeit der Darstellung
  987. von  Grammatiken  mit  Hilfe  von  Syntax-Diagrammen. Sie dürften
  988. ebenfalls jedem Leser ausreichend bekannt sein. Von ihnen wird im
  989. weiteren Verlauf der Serie allerdings kein Gebrauch gemacht.
  990.  
  991.  
  992. Wir  hoffen, daß Sie nun nicht zwischen Automaten und Grammatiken
  993. 'verdurstet'  sind  -  mit  der  Theorie  ist jetzt auch erst mal
  994. Schluß!  In der nächsten Folge geht es dann an's Eingemachte: Der
  995. Scanner  wird  besprochen  und wegen seinem im Vergleich zu manch
  996. anderen  Komponnenten  geringen  Umfang auch komplett als Listing
  997. abgedruckt  und  erläutert.  Dazu wäre es ganz gut, wenn ein paar
  998. Dinge über die Automatentheorie 'kleben' bleiben würden.
  999.  
  1000.  
  1001. Literatur:
  1002.  
  1003. N. Wirth Compilerbau Teubner Stuttgart 1981
  1004.  
  1005. N. Wirth Systematisches Programmieren Teubner Stuttgart 1985
  1006.  
  1007. K. Jensen/N. Wirth Pascal user manual & report Springer Verlag
  1008.