home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / spezial / 02 / texte / pascomp.2 < prev    next >
Encoding:
Text File  |  1980-01-03  |  29.6 KB  |  857 lines

  1.  
  2. Pascomp
  3.  
  4. Pascal baut einen Compiler
  5.  
  6.  
  7.  
  8. Teil 3:  Scanner
  9.  
  10.  
  11. Nachdem wir uns in der letzten Folge fundamentale Grundkenntnisse
  12. zum Bau eines Compilers erarbeitet haben, wollen wir nun die ein-
  13. zelnen Komponenten des Compilers genauer beleuchten und dabei mit
  14. dem  Scanner beginnen. Aufgaben und Arbeitsweise dieses wichtigen
  15. Compiler-  Bestandteiles  sollen  im Folgenden genauer betrachtet
  16. werden.
  17.  
  18. Altmeister  Wirth umschreibt den Aufgabenbereich des Scanners wie
  19. folgt: " Die Aufgabe des Scanners ist das Herausgreifen des näch-
  20. sten  Symbols  aus  dem Quelltextprogramm nach den Regeln der ge-
  21. wählten lexikographischen Darstellung. Es ist üblich, daß gewisse
  22. Symbole  aus mehreren Zeichen bestehen. Der Scanner bildet sodann
  23. Zeichenfolgen in Symbole ab. Zeichen sind somit atomare Einheiten
  24. im  Eingabetext des Rechners, Symbole dagegen sind Grundbausteine
  25. der  durch die Syntax unabhängig von bestimmten Zeichensätzen de-
  26. finierten Sprache. "
  27.  
  28. Prinzipiell  ist  die  lexikalische  Analyse eine Teilaufgabe der
  29. syntaktischen  Analyse (Parser) und könnte in einem normalen Par-
  30. sing- Algorithmus eingebaut werden. Über die Vorteile der separa-
  31. ten  Konvertierung des aus einer Zeichenfolge bestehenden Source-
  32. programms  in eine Folge von semantisch relevanten Symbolen wurde
  33. in  der letzten Folge eingehend berichtet. Zur besseren Übersicht
  34. sind  die  Aufgaben  des  Scanners in der nachfolgenden Abbildung
  35. nochmals graphisch dargestellt und zusammengefaßt :
  36.  
  37. Abbildung 1 : Scanner Interface.
  38.  
  39. ┌────────────┐                                 ┌─────────┐
  40. │   Source   │       ┌──────────────>──────────┤ Symbol  │
  41. │ Programmm  │       │       ┌──────<──────────┤         │
  42. └──────┬─────┘       │       │                 │         │
  43.        │          ┌──┴───────┴──┐              │         │
  44.        └─>────────┤ lexikalische│              │         │
  45.        ┌─<────────┤    Analyse  │              │         │
  46.        │          └────────┬────┘              │ Tabelle │
  47.        │                   │                   └─────────┘
  48.  ┌─────┴─────┐             │
  49.  │  Parser   │             │
  50.  │           │             │
  51.  └───────────┘             │
  52.                            │
  53.                ┌───────────┴──────────────────────┐
  54.                │                                  │
  55.                │       Protokollierer             │
  56.                │                                  │
  57.                └──────────────────────────────────┘
  58.  
  59.  
  60.  
  61. Spezifikation und Realisierung der lexikalischen Analyse
  62.  
  63. Als  Eingabe für den Symbolentschlüsseler (deutscher Ausdruck für
  64. Scanner) dient das in einer Programmiersprache (hier PASCAL) ver-
  65. faßte  Quelltextprogramm.  In aller Regel besteht dieser Text aus
  66. Buchstaben, Ziffern und Sonderzeichen (+,-,*,/,....). Die Ausgabe
  67. stellt  eine Folge von Symbolen dar, die das Quelltextprogramm in
  68. komprimierter  und  abstrakter  Darstellungsweise  enthält. Jedes
  69. einzelne Symbol ist dabei ein Tripel der folgenden Gestalt :
  70.  
  71. S = (Symbolklasse, Merkmal, Position)
  72.  
  73. Es bedeuten hierbei :
  74. Symbolklasse  : trägt die syntaktischen Informationen. Jedes Ele-
  75. ment  der Symbolklassen-Menge entspricht genau einem Terminal der
  76. Zerteiler-  Grammatik.  Der  Typ  'Symbolklasse' könnte in PASCAL
  77. etwa folgendermaßen definiert  werden :
  78.  
  79.  Type Symbolklasse=
  80.  (Bezeichner, Ganze_Zahl, Real_Zahl, Zeichen,  ....,  Beginsymb,
  81.   Endsymb, Assignsymb,..);
  82.  
  83. -Merkmal:
  84. identifiziert  die  Symbole  einer Klasse. So kann beispielsweise
  85. eine  Indexnummer  ein  Merkmal  genau  dann für einen Bezeichner
  86. sein,  wenn  der Index die Stelle in einer Tabelle angibt, an der
  87. der  Bezeichner  vermerkt  ist.  Im Gegensatz dazu ist bei ganzen
  88. Zahlen  (Integer-Zahlen)  eben  diese Zahl selbst das Merkmal des
  89. Symbols.
  90.  
  91. -Position:
  92. spiegelt  im  Symboltripel  die Position des aktuellen Symbols im
  93. Quelltext wider. Dieser Eintrag wird benötigt, um bei eventuellen
  94. Fehlern im Sourceprogramm positionsbezogene Fehlerausgaben machen
  95. zu können.
  96.  
  97. Beispiele für Symboltripel sind :
  98.  
  99.      (Bezeichner, 42, (10, 20))
  100.      (Progsymb,    /, ( 1,  1))
  101.      (Ganze_Zahl,111, (20, 21))
  102.      ......
  103.  
  104.  
  105. Grundsymbole und spezielle Zeichenfolgen
  106.  
  107. Unter  dem  Begriff  Grundsymbol versteht man die Zusammenfassung
  108. von Bezeichnern, Zahlen und Sondersymbolen.
  109.  
  110. Dabei bedeuten :
  111.  
  112. Bezeichner(Identifier)  : Bezeichner bestehen aus einer Folge von
  113. Buchstaben  und  Ziffern,  die mit einem Buchstaben beginnen muß.
  114. Bezeichner  dienen zur Identifizierung von Objekten in Programmen
  115. wie  beispielsweise  Variablen, Label, Typen, Konstanten usw. Zur
  116. sinnvollen  Benennung  von Objekten sollte die Länge von Bezeich-
  117. nern  weder  begrenzt  noch  auf  signifikante Zeichen beschränkt
  118. sein.  An die im Zusammenhang mit der Längenbegrenzung auftreten-
  119. den  Probleme  erinnern  sich  sicherlich die FORTRAN- und BASIC-
  120. Programmierer,  während die User von ALGOL und ADA nie mit derar-
  121. tigen Schwierigkeiten konfrontiert wurden.
  122.  
  123. Wie  bereits  in  der letzten Folge ausführlich behandelt, lassen
  124. sich  auch  Bezeichner  mit  Hilfe des Automatenschemas graphisch
  125. darstellen und erläutern. Der Bezeichnerautomat sieht in den mei-
  126. sten Fällen folgendermaßen aus :
  127.  
  128.  
  129. Abbildung 2 : Bezeichnerautomat.
  130.                                               Bu = Buchstaben-
  131.                           ┌───<─┐                  menge
  132.                           │     │ Bu, Zi
  133.                         ┌─┴──┐  │             Zi = Ziffernmenge
  134.    (q[1]) ────────────> │q[2]├>─┘
  135.                         └────┘
  136.  
  137.  
  138. Dabei  variiert  der  Umfang der Buchstabenmenge von Programmier-
  139. sprache zu Programmiersprache. So ist in der Sprachdefinition von
  140. MODULA-2  vereinbart, daß Groß- und Kleinbuchstaben als verschie-
  141. dene  Zeichen  zu betrachten sind. Der PASCAL-Report läßt dagegen
  142. diese  Frage  für  PASCAL  offen. So gilt bei Berkeley-PASCAL die
  143. gleiche  Regelung  wie bei MODULA- 2. Turbo-PASCAL macht, wie die
  144. meisten  Leser wohl wissen, keinen Unterschied zwischen Groß- und
  145. Kleinschreibung.  Wieder  andere  Pascal-  Versionen erlauben nur
  146. großgeschriebene Schlüsselwörter. Einige Beispiele für Bezeichner
  147. sind :
  148.  
  149.      BEZEICHNER, E41, Hallo, .....
  150.  
  151.  
  152. Zahlen :
  153. Eine Zahl ist eine Folge von Ziffern, die eventuell Sonderzeichen
  154. ('+','-','.')  und Buchstaben ('E','e') enthalten kann. Beispiele
  155. für Zahlen sind :
  156.  
  157.      +0, .14, +1.23e-13, .....
  158.  
  159. Sondersymbole :
  160. Die  Sondersymbole werden in der Literatur üblicherweiser in drei
  161. Klassen eingeteilt :
  162.  
  163.   -Wortsymbole  , die der gleichen Syntax wie Bezeichner genügen,
  164.   und  die eine in der Programmiersprache fest vorgegebene Bedeu-
  165.   tung  besitzen.  Unter  den Wortsymbolen unterscheidet man noch
  166.   weiterhin
  167.  
  168.        -reservierte Wörter und vordefinierte Bezeichner .
  169.        Reservierte Wörter dürfen nur in der Weise benutzt werden,
  170.        wie  sie  in  der Programmiersprache vorgesehen sind ('be-
  171.        gin',  'end',  'repeat', ... in PASCAL). Im Gegensatz dazu
  172.        kann  vordefinierten Bezeichnern ('put', 'get', 'new', ...
  173.        in  PASCAL  ) in Programmen eine neue Bedeutung zugewiesen
  174.        werden.
  175.  
  176.   -Einfache Symbole, die durch einzelne Sonderzeichen dargestellt
  177.   werden.  Beispiele einfacher Symbole sind : '+', '-' (beide als
  178.   diadische Operatoren fungierend), '*', '/', .... ü
  179.  
  180.   -Zusammengesetzte Symbole werden durch eine Folge von zwei oder
  181.   mehreren  Sonderzeichen  dargestellt, wie beispielsweise : '=',
  182.   '**', '<=', '>=', '<>', ...
  183.  
  184.  
  185. Eine  wichtige Rolle in der lexikalischen Analyse spielt darüber-
  186. hinaus  das  Leerzeichen  (Blank) . Diesem kommt in verschiedenen
  187. Programmiersprachen  eine  unterschiedlich große Bedeutung zu, so
  188. daß  man darüber informiert sein sollte. Während das Blank in den
  189. meisten Programmiersprachen die Aufgabe eines Separators erfüllt,
  190. d.h.  aufeinanderfolgende Bezeichner, Zahlen oder Schlüsselwörter
  191. trennt,  ist  das Leerzeichen in einigen Programmiersprachen, wie
  192. beispielsweise  FORTRAN, völlig bedeutungslos. Es kann hier sogar
  193. in  einem  Grundsymbol  stehen.  Folgendes  FORTRAN-Statement mag
  194. hierfür als Beispiel dienen :
  195.  
  196.         DO 10 I = 1.5
  197.         A(I)=X+B(I)
  198.         10 CONTINUE
  199.  
  200. Auf  den  ersten Blick könnte man meinen, es handele sich hier um
  201. eine  FORTRAN-DO  Schleife, in der an die Variable I nacheinander
  202. die  Werte 1,2,3,4 und 5 zugewiesen werden. In diesem Falle müßte
  203. jedoch  anstelle  des Punktes ein Komma stehen. Eine Programmier-
  204. sprache  mit  wohldefinierter Syntax würde solche 'mißverständli-
  205. chen'  Statements  nicht zulassen. FORTRAN akzeptiert dies jedoch
  206. als korrektes Zuweisungs-Statement, in der die Variable DO10I den
  207. Wert  1.5  erhält.  Man erzählt sich in diesem Zusammenhang immer
  208. wieder  gerne  die  Geschichte von dem unbenannten US-Raumschiff,
  209. das  eigentlich  zur  Venus sollte, aber doch sein Ziel verfehlte
  210. und  verloren  ging,  weil  der Bordrechner einen Software-Fehler
  211. hatte, der auf eben diesen Fall zurückzuführen war.
  212.  
  213.  
  214. Erkennung von Grundsymbolen
  215.  
  216. Die  Erkennug von Grundsymbolen wird mitunter auch als Ausblenden
  217. von  Symbolen  bezeichnet. Der Symbolentschlüsseler (Scanner) muß
  218. in  der  Lage  sein, das Ende jedes Symbols zu erkennen. Das hört
  219. sich  simpel  an,  ist  es aber in bestimmten Spezialfällen nicht
  220. immer. Betrachten wir dazu das folgende PASCAL-Programmstück :
  221.  
  222.    .....
  223.    var a:real;
  224.    begin
  225.       a:=2
  226.    end;
  227.    .....
  228.  
  229.  
  230. Das  vorliegende  Problem  ist klar : An die Real-Variable a wird
  231. eine Integer-Konstante (2) zugewiesen.
  232. Daß  es  sich  aber um eine Integer- Konstante handelt, kann erst
  233. beim  Lesen  des  'n'  (von 'end') entschieden werden, da das 'e'
  234. (von  'end') auch den Anfang des Exponenten einer Real-Konstanten
  235. kennzeichnen  kann.  Der Scanner muß demnach zwei Zeichen voraus-
  236. schauen, um eine Entscheidung treffen zu können.
  237.  
  238. Symbole werden daher in aller Regel nach dem Prinzip des längsten
  239. Musters ausgeblendet. Der Scannerautomat liest dabei solange Zei-
  240. chen für Zeichen ein, bis es im Zustand q keinen Übergang mit dem
  241. nächsten  Zeichen mehr gibt. Dann ist der aktuelle Zustand q ent-
  242. weder  ein  Endzustand des Automaten, an dem die eingelesene Zei-
  243. chenfolge  als  Symbol akzeptiert wird, oder der Automat wird auf
  244. den  zuletzt  durchlaufenen Endzustand zurückgesetzt, und die bis
  245. dahin  gelesene  Zeichenfolge wird akzeptiert. Zur Programmierung
  246. des  Automaten  gilt  auch  hier das bereits in der letzten Folge
  247. Gesagte :
  248.  
  249. -  entweder der Scannerautomat wird als Übergangsmatrix implemen-
  250. tiert, was in PASCAL-Struktur etwa so aussähe :
  251.  
  252.          while  symbol noch nicht erkannt do
  253.          zustand:=matrix[zustand,nächstes_zeichen]
  254.         .......
  255.  
  256. - oder die Übergangsmatrix wird ausprogrammiert :
  257.  
  258.          while  symbol noch nicht erkannt do
  259.          begin
  260.           .....
  261.           case  zustand  of
  262.             0 :  case  nächstes_Zeichen  of
  263.                  buchstabe : zustand := .....
  264.              ... end ;
  265.            .....
  266.            end ;
  267.           .....
  268.  
  269.          end;
  270.  
  271.  
  272. Verwaltung von Symbolen
  273.  
  274. Daß  einmal  erkannte  Bezeichner nicht irgendwo im Speicher ver-
  275. schwinden  dürfen,  ist klar. Es muß in späteren Phasen der Über-
  276. setzung immer wieder möglich sein, auf die bereits gelesenen Sym-
  277. bole  zurückzugreifen.  Dies ist zum Beispiel bei der Anfertigung
  278. eines  protokollierten  Listings erforderlich. Dazu müssen einmal
  279. gelesene  und erkannte Symbole in irgendeiner Art und Weise gele-
  280. sen  und  abgespeichert werden. Die dafür geeignete Datenstruktur
  281. ist die Symboltabelle, deren Aufgabe es ist, Bezeichner und Wort-
  282. symbole zu codieren.
  283.  
  284. Diese  Codierung  muß eine bijektive Abbildung vom Bezeichnertext
  285. auf  ein  zur Indizierung geeignetes Merkmal sein (diese Merkmale
  286. können  Ausschnitte  aus  der  Menge der ganzen Zahlen sein). Die
  287. Operationen, die auf der Datenstruktur 'Symboltabelle' mindestens
  288. verfügbar sein sollten, sind :
  289.  
  290. - Initialisierung:
  291.   Eintragung der Standardbezeichner und Wortsymbole.
  292.  
  293. - Codierung:
  294.   Abbildung  des Bezeichnertextes auf das Merkmal. Dies geschieht
  295.   durch Suchen des Bezeichnertextes in der Tabelle und Neueintra-
  296.   gung, falls der Bezeichner noch nicht in der Symboltabelle ent-
  297.   halten  ist. Dieses bedingt, daß ein wiederholtes Auftreten des
  298.   gleichen   Bezeichners   keinen   Neueintrag   zur  Folge  hat.
  299.  
  300. - Decodierung:
  301.   Abbildung des Merkmals auf den Bezeichnertext.
  302.  
  303. - Sortierung:
  304.   Ordnen der Symboltabelle in lexikographischer Reihenfolge.
  305.  
  306. - nächstes_Symbol:
  307.    Bestimmung des nachfolgenden Symbols.
  308.  
  309. Die  Operation  'Decodierung'  findet bei der Protokollierung und
  310. beim Crossreferencing Anwendung, während die Operationen 'Sortie-
  311. rung'  und  'nächstes  Symbol' nur beim Crossreferencing benötigt
  312. werden.
  313.  
  314.  
  315. Zur Implementierung der Symboltabelle
  316.  
  317. Die beiden bekanntesten Verfahren, Symboltabellen zu implementie-
  318. ren,  sind  die Darstellung als binärer Baum und die Organisation
  319. der  Symboltabelle  durch  Hashing.  Beide Methoden werden in der
  320. Praxis  etwa  gleichermaßen  benutzt,  so daß auf beide hier kurz
  321. eingegangen werden soll.
  322.  
  323.  
  324. Darstellung der Symboltabelle durch Hashing
  325.  
  326. Beim  Hashing  geht man von einer totalen Funktion h aus. h heißt
  327. die  Hashfunktion  und  ordnet jedem Bezeichner A eindeutig einen
  328. Wert  h(A), den Hashcode von A, zu. h(A) muß innerhalb eines vor-
  329. gegebenen Intervalls [0..hashmax] liegen und kann als Index einer
  330. Hashtabelle  benutzt werden. Hierin kann ein zum Bezeichner A ge-
  331. hörendes Element abgespeichert bzw. gesucht werden.
  332. Beispiel einer Hashfunktion:
  333.  
  334. Sei  A  ein  Bezeichnertext und A der i-te Buchstabe des Bezeich-
  335. ners, dann könnte
  336.  
  337.       h(A)= (ord(A,,t1)+ord(ALaenge(A)))  mod  (hashmax+1)
  338.  
  339. eine mögliche Hashfunktion sein.
  340.  
  341. Durch  die  Abbildung  eines Bezeichnertextes auf einen Index des
  342. Hashvektors  können  mitunter  auch  Probleme auftreten : Gibt es
  343. nämlich in einer Symboltabelle Einträge zu Bezeichnern mit unter-
  344. schiedlichen  Bezeichnertexten, aber gleichem Hashcode, so treten
  345. Kollisionen  auf. Kollisionen können aufgelöst werden, indem alle
  346. Bezeichner  mit  gleichem Hashcode in eine Liste aufgenommen wer-
  347. den.  Der Anfang einer Liste kann über den Hashcode bestimmt wer-
  348. den. Man spricht dann von einer Kollisionsauflösung durch Verket-
  349. tung (chaining), die in dem folgenden Bildchen erläutert wird :
  350.  
  351. Abbildung 3 : Implementierung der Symboltabelle durch Hashing mit
  352.               Kollisionsauflösung durch Verkettung.
  353.  
  354.               ┌────────────────────────────────────────┐
  355.   Quelltext : │ while    x1    y1    abc    xy1  ....  │
  356.               └────────────────────────────────────────┘
  357.  
  358.  
  359.  
  360.  
  361. Type Stentry = record
  362.                           ┌────┬─────┬────┬────┬────┬─────┬─────┐
  363.       Anfang : Textindex; │    │     │    │    │    │     │     │
  364.                           │    │     │    │    │    │     │     │
  365.                           ├────┼─────┼────┼────┼────┼─────┼─────┤
  366.       Länge  : Textindex; │    │  5  │  2 │    │    │     │     │
  367.                           │    │whsym│ bez│ bez│ bez│  bez│     │
  368.                           ├────┼─────┼────┼────┼────┼─────┼─────┤
  369.       Ki : Symbolklasse;  │    │     │    │    │    │     │     │
  370.                           │    │     │    │    │    │     │     │
  371.                           ├────┼─────┼────┼────┼────┼─────┼─────┤
  372.       nächster: Stindex;  │    │     │    │    │    │     │     │
  373.                           │    │     │    │    │    │     │     │
  374.                           ├────┼─────┼────┼────┼────┼─────┼─────┤
  375.       Kollision:Stindex;  │    │     │    │    │    │     │     │
  376.                           │    │  ~  │  . │  ~ │  ~ │  ~  │     │
  377.                           └────┴┬────┴┬───┴─┬──┴─┬──┴──┬──┴─────┘
  378.     end;                        │     │ │   │    │     │
  379.                                 │     │ └───┼────┼─────┘
  380.                                 │     │     │    │
  381.                                 │  ┌──┘     │    │
  382.                           ┌─────┘  │   ┌────┘    │
  383.                           │   ┌────┘   │     ┌───┘
  384.                 ┌──┬──┬──┬┴─┬─┴┬──┬──┬─┴┬──┬─┴┬──┬──┬──┬──┬──┐
  385.     Hashvektor: │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  386.                 └──┴──┴──┴──┴┬┬┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘...
  387.                              ││
  388.                       x1 ──>─┘└─<─ xy1
  389.  
  390.  
  391. Verwendete Hashfunktion : (ord(Text[1] + ord(Text[länge(Text)])
  392.  
  393.                                    mod (hashmax + 1)
  394.  
  395.  
  396.  
  397. Eine  weitere  Möglichkeit der Kollisionsauflösung ist das Rehas-
  398. hing  . Dabei werden bei Kollisionen solange weitere Hashfunktio-
  399. nen auf den Bezeichnertext angewendet, bis keine Kollisionen mehr
  400. auftreten.
  401.  
  402.  
  403. Darstellung der Symboltabelle als Heap.
  404.  
  405. Bei dieser Implementierungsstrategie wird ein binärer Baum aufge-
  406. baut,  der  als Heap verwaltet wird. Zur Erinnerung : Ein binärer
  407. Baum  ist  genau  dann ein Heap, wenn für jeden Knoten des Baumes
  408. gilt : Der Wert des linken Sohnes ist kleiner und der des rechten
  409. Sohnes  größer als der Wert des Vater-Knotens. Die Symbole werden
  410. in  der  Reihenfolge ihres Eintreffens in den Baum eingefügt. Der
  411. zugehörige  Einfüge-Algorithmus  sieht  in  PASCAL- Notation dann
  412. etwa folgendermaßen aus :
  413.  
  414.  
  415.       type  heap=  ^heappointer;
  416.           heappointer= record
  417.                          s:symbol;
  418.                          left_son,right_son:heap;
  419.                        end;
  420.      ........
  421.  
  422.      procedure  insert_symbol(s:symbol; var  h:heap);
  423.      var  h1:heap;
  424.      begin
  425.        if h=nil  then
  426.        (* der Baum ist durchsucht worden; s wird als neuer Knoten
  427.        eingefügt *)
  428.          begin
  429.             new(h1);
  430.             h1^.s:=s;
  431.             h:=h1;
  432.          end
  433.        else if  h^.s<s then
  434.            insert_symbol(s,h^.left_son)
  435.        else if h^.s>s then
  436.            insert_symbol(s,h^.right_son)
  437.        else
  438.        (*  nix.  h^.s=s  -->  s ist bereits im Baum enthalten und
  439.        wird nicht mehr eingefügt *)
  440.      end ; (* of insert_symbol *)
  441.  
  442.  
  443. Unter der Voraussetzung des zufälligen Eintreffens von Elementen,
  444. liegt bei der Organisation der Symboltabelle als binärer Baum der
  445. Zeitaufwand  für Suchen und Einfügen gleichermaßen in der Größen-
  446. ordnung  O(log2n).  Das beschriebene Heap-Verfahren ist das effi-
  447. zienteste, das auf Vergleichen basiert und findet auch in unserem
  448. Übersetzer Verwendung.
  449.  
  450. Wem  die  hier  beschriebenen Erläuterungen für die Symboltabelle
  451. nicht  ausreichen  sollten,  der findet in der DATA-BOX noch zwei
  452. Implementierungen zu diesem Thema.
  453.  
  454.  
  455. Reduzierte Automaten
  456.  
  457. Es  existiert  ein  Algorithmus, der zu jedem endlichen Automaten
  458. einen  reduzierten endlichen Automaten konstruiert, der die glei-
  459. che  Sprache  akzeptiert.  Der Vorteil, der sich aus der Existenz
  460. eines  solchen Algorithmus ergibt, liegt darin, sich bei der Pla-
  461. nung  und  Entwicklung  eines Automaten auf funktionelle Gesicht-
  462. spunkte beschränken zu können, ohne auf die Effizienz des Automa-
  463. ten (z.B. Größe der Zustandsmenge ) achten zu müssen.
  464.  
  465. Die  dem  Verfahren des reduzierten endlichen Automaten zugrunde-
  466. liegende  Idee besteht darin, die Menge der Zustände zu partitio-
  467. nieren  (aufzuteilen). Dazu geht man wie folgt vor: In der ersten
  468. Partitionierung  teilt  man die Menge der Zustände in zwei Blöcke
  469. von  Endzuständen und Nicht-Endzuständen auf. Man ersetzt nun die
  470. Ergebnisse  der  Abbildungen von Zustand und Zeichen in der Über-
  471. gangstabelle  durch  die  zugehörigen Blöcke. Wenn sich in diesem
  472. Block nach diesem Vorgang gleiche Spalten gebildet haben, so faßt
  473. man  diese  Spalten zu einem neuen Block zusammen. Dieser Vorgang
  474. wird  solange durchgeführt, bis sich keine neuen Blöcke mehr bil-
  475. den  lassen. Aus den so konstruierten Partitionen werden dann die
  476. Zustände des reduzierten endlichen Automaten gebildet.
  477.  
  478.    
  479. Zuguterletzt sei hier noch der vollständige Scanner unseres Über-
  480. setzers abgedruckt. Mit den geschaffenen Grundlagen dürfte es ein
  481. Leichtes  sein,  seinen Aufbau zu verstehen. Viel Spaß beim Nach-
  482. vollziehen!
  483.  
  484.  
  485. procedure insymbol;
  486. (* bestimmt das naechste Symbol aus der Eingabe *)
  487. label 1,2,3;
  488. var i,k:integer;
  489.   digit : packed array[1..strglgth] of char;
  490.                                  (* Buffer fuer Ziffern *)
  491.   string:  packed  array[1..strglgth]  of  char;  (*  Buffer fuer
  492.                                              Stringkonstante *)
  493.   lvp   : csp;
  494.   test  : boolean;
  495.   wert  : long_integer;
  496.  
  497.  procedure nextch;
  498.  (* liest das naechste Zeichen aus der Eingabe und weist es an ch
  499.   zu *)
  500.  begin
  501.   if eol then
  502.    begin
  503.      if list
  504.          (* komplettes Compiler-Listing *) then writeln(output);
  505.      endofline;
  506.      linepos:=1; (* Zeiger auf den Zeilenpuffer 'line', der immer
  507.                     genau eine Eingabezeile aufnimmt *)
  508.    end;
  509.    if not eof(input) then
  510.      begin
  511.        eol:=eoln(input);
  512.        read(input,ch);
  513.        enterline(ch);  (*  fuegt  das neu gelesene Zeichen in den
  514.                            Zeilenpuffer 'line' ein *)
  515.        if list then write(output,ch);
  516.        chcnt:=chcnt+1 (* 'charactercounter' *)
  517.      end
  518.    else
  519.      begin
  520.        writeln(output,'  *** eof encountered');
  521.        test:=false
  522.      end;
  523.  end;
  524.  
  525. procedure options;
  526. (* erkennt Compileroptionen, die mit (*$<Option>{+,-} eingeleitet
  527.     werden.
  528.    Optionen sind :
  529.      l : kommentiertes Compiler-Listing
  530.      d  :Debug-Option  :  Wenn diese eingeschaltet ist, wird Code
  531.          zur  Ueber- pruefung von Grenzbereichen, Aufzaehlungsty-
  532.          pen ,... erzeugt.
  533.      c  : Compiler-Option. Eingeschaltet erzeugt diese Option in-
  534.           terpretierbaren  P-Code *)
  535.  
  536.  
  537. begin
  538.   repeat
  539.     nextch;
  540.     if (ch<>'*') and (ch<>'}') then
  541.     begin
  542.       if ch='l' then
  543.           begin
  544.             nextch;
  545.             list:=ch='+';
  546.             if not list then writeln(output)
  547.           end
  548.         else
  549.       if ch='d' then
  550.           begin
  551.             nextch;
  552.             debug:=ch='+'
  553.           end
  554.         else
  555.       if ch='c' then
  556.           begin
  557.             nextch;
  558.             prcode:=ch='+'
  559.           end;
  560.       nextch
  561.     end
  562.   until ch<>',';
  563. end;
  564.  
  565. begin (* of insymbol *)
  566. 1:
  567.   repeat
  568.     while   (ch='   ')  and  not  eol  do  nextch;  (*  ueberlese
  569.                                                  Blanks..... *)
  570.     test:=eol;
  571.     if test then nextch (* und Separatoren. *)
  572.   until not test;
  573.   if chartp[ch]=illegal then
  574.    (*  chartp  gibt den typ jedes Zeichens an. Es wird dabei nach
  575.    Buchstaben, Ziffern und Sonderzeichen unterschieden *)
  576.     begin
  577.       (* illegales Zeichen erkannt. *)
  578.       sy:=othersy;
  579.       op:=noop;
  580.       error(399);
  581.       nextch
  582.     end
  583.   else
  584.     case chartp[ch] of
  585.       letter: (* Bezeichner einlesen *)
  586.         begin
  587.           k:=0;
  588.           repeat
  589.             if k<8 then (* Aufnahme des Bezeichners in einen Zwi-
  590.                          schenpuffer *)
  591.               begin k:=k+1;
  592.                     id[k]:=ch
  593.               end;
  594.             nextch
  595.             (* Das Ende eines Bezeichners ist durch einen Separa-
  596.               tor bestimmt *)
  597.           until chartp[ch] in [special,illegal,chstrquo,chcolon,
  598.                           chperiod,chlt,chgt,chlparen,chspace];
  599.            (* kk gibt die Laenge des letzten Bezeichners,
  600.                k die Laenge des neuen Bezeichners an *)
  601.           if k>=kk then kk:=k
  602.           else
  603.             repeat
  604.               (* Fuelle die unbenutzten Stellen des id-arrays mit
  605.                  Blanks auf *)
  606.               id[kk]:=' ';
  607.               kk:=kk-1
  608.             until kk=k;
  609.           (* Der Bezeichner ist nun in id eingelesen. *)
  610.           for i:=frw[k] to frw[k+1]-1 do
  611.             if rw[i]=id then (* ist id ein reserviertes Wort??*)
  612.               begin
  613.                 (* id ist reserviertes Wort *)
  614.                 sy:=rsy[i];
  615.                 op:=rop[i];
  616.                 goto 2
  617.               end;
  618.             (*  Nee,  id ist kein reserviertes Wort !. id ist ein
  619.             neudefinierter Bezeichner *)
  620.             sy:=ident;op:=noop;
  621.       2:end;
  622.  
  623.       number: (* Zahl einlesen *)
  624.         begin
  625.           op:=noop;
  626.           i:=0;
  627.           repeat (* Einlesen der Ziffern in den Ziffernpuffer *)
  628.             i:=i+1;
  629.             if i<=digmax then digit[i]:=ch;
  630.             nextch
  631.           until chartp[ch]<>number;
  632.           if (ch='.') or (ch='e') then
  633.             begin
  634.               (* Realzahl einlesen *)
  635.               k:=i;
  636.               if ch='.' then
  637.                 begin
  638.                   k:=k+1;
  639.                   if k<=digmax then digit[k]:=ch;
  640.                   nextch;
  641.                   if ch='.' then
  642.                     begin
  643.                       ch:=':';
  644.                       goto 3
  645.                     end;
  646.                   if chartp[ch]<>number then error(201)
  647.                   else
  648.                     repeat
  649.                       k:=k+1;
  650.                       if k<=digmax then digit[k]:=ch;
  651.                       nextch
  652.                       until chartp[ch]<>number
  653.                 end;
  654.               if ch='e' then
  655.                 begin
  656.                   (* Exponent einlesen *)
  657.                   k:=k+1;
  658.                   if k<=digmax then digit[k]:=ch;
  659.                   nextch;
  660.                   if(ch='+') or(ch='-') then
  661.                     begin
  662.                       k:=k+1;
  663.                       if k<=digmax then digit[k]:=ch;
  664.                       nextch
  665.                     end;
  666.                   if chartp[ch]<>number then error(201)
  667.                   else
  668.                    repeat
  669.                      k:=k+1;
  670.                      if k<=digmax then digit[k]:=ch;
  671.                      nextch
  672.                    until chartp[ch]<>number
  673.                 end;
  674.               (* Zahl ist in Ziffernpuffer eingelesen *)
  675.               new(lvp);
  676.               sy:=realconst;
  677.               lvp^.cclass:=reel;
  678.               with lvp^ do
  679.                 begin
  680.                   for i:=1 to strglgth do rval[i]:=' ';
  681.                   if k<=digmax then
  682.                     for i:=2 to k+1 do rval[i]:=digit[i-1]
  683.                   else
  684.                     begin
  685.                       error(203);
  686.                       rval[2]:='0';
  687.                       rval[3]:='.';
  688.                       rval[4]:='0'
  689.                     end
  690.                 end;
  691.               val.valp:=lvp
  692.             end
  693.           else
  694.       3:begin (* Konstante ist vom Typ (long)integer *)
  695.           if i>digmax then
  696.             begin
  697.               error(203);
  698.               val.ival:=0
  699.             end
  700.           else
  701.             with val do
  702.               begin
  703.                 wert:=0;
  704.                 for k:=1 to i do
  705.                   wert:=wert*10+ordint[digit[k]];
  706.                 if wert<=maxint then
  707.                   begin
  708.                     (* integer-Konstante *)
  709.                     sy:=intconst;
  710.                     ival:=int(wert);
  711.                   end
  712.                 else
  713.                   begin
  714.                     (* longinteger-Konstante *)
  715.                     sy:=longconst;
  716.                     new(lvp);
  717.                     lvp^.cclass:=long;
  718.                     lvp^.lval:=wert;
  719.                     val.valp:=lvp;
  720.                   end;
  721.               end
  722.          end
  723.       end;
  724.  chstrquo: (* Stringkonstante einlesen *)
  725.    begin
  726.      lgth:=0;
  727.      sy:=stringconst;
  728.      op:=noop;
  729.      repeat
  730.        repeat
  731.          nextch;
  732.          lgth:=lgth+1;
  733.          if lgth<=strglgth then string[lgth]:=ch
  734.           (* ^^ String in Stringpuffer zwischenspeichern *)
  735.        until (eol) or (ch='''');
  736.        if eol then error(202) else nextch
  737.      until ch<>'''';
  738.      lgth:=lgth-1;
  739.      if lgth=0 then error(205) else
  740.      if  lgth=1  then val.ival:=ord(string[1]) (* string ist ein-
  741.                                               zelner Character*)
  742.        else
  743.          begin
  744.            new(lvp);
  745.            lvp^.cclass:=strg;
  746.            if lgth>strglgth then
  747.              begin
  748.                (* String zu lang *)
  749.                error(399);
  750.                lgth:=strglgth;
  751.                (*  Rest  der Stringskonstanten wird einfach abge-
  752.                    schnitten *)
  753.              end;
  754.            with lvp^ do
  755.              begin
  756.                slgth:=lgth;
  757.                for i:=1 to lgth do sval[i]:=string[i]
  758.              end;
  759.            val.valp:=lvp;
  760.          end
  761.     end;
  762.   (* SONDERZEICHEN *)
  763.   chcolon :
  764.     begin
  765.       op:=noop;
  766.       nextch;
  767.       if ch='=' then
  768.         begin
  769.           sy:=becomes;
  770.           nextch
  771.         end
  772.       else
  773.         sy:=colon
  774.     end;
  775.   chperiod:
  776.     begin
  777.       op:=noop;
  778.       nextch;
  779.       if ch='.' then
  780.         begin
  781.           sy:=colon;
  782.           nextch
  783.         end
  784.       else
  785.         sy:=period
  786.     end;
  787.   chlt:
  788.     begin
  789.       nextch;
  790.       sy:=relop;
  791.       if ch='=' then
  792.         begin
  793.           op:=leop;
  794.           nextch
  795.         end
  796.       else
  797.         if ch='>' then
  798.           begin
  799.             op:=neop;
  800.             nextch
  801.           end
  802.         else
  803.           op:=ltop
  804.     end;
  805.   chgt:
  806.     begin
  807.       nextch;
  808.       sy:=relop;
  809.       if ch='=' then
  810.         begin
  811.           op:=geop;
  812.           nextch
  813.         end
  814.       else
  815.         op:=gtop
  816.     end;
  817.   chlparen:
  818.   (* Kommentare *)
  819.     begin
  820.       nextch;
  821.       if ch='*' then
  822.         begin
  823.           nextch;
  824.           if ch='$' then options;
  825.           repeat
  826.             while (ch<>'*') and not eof(input) do nextch;
  827.             nextch
  828.           until (ch=')') or eof(input);
  829.           nextch;
  830.           goto 1
  831.         end;
  832.       sy:=lparent;
  833.       op:=noop
  834.     end;
  835.   special :
  836.     begin
  837.       sy:=ssy[ch];
  838.       op:=sop[ch];
  839.       nextch
  840.     end;
  841.   chspace:
  842.     sy:=othersy;
  843.   (* Kommentare mit { *)
  844.   lbrace:
  845.     begin
  846.       nextch;
  847.       if ch='$' then options;
  848.       while (ch<>'}') and not eof(input) do nextch;
  849.       nextch;
  850.       goto 1
  851.     end;
  852.   end;
  853. end;
  854.  
  855. So, das war's für's Erste! In der nächsten Folge beschäftigen wir
  856. uns  mit  dem  Parsing  und  der semantischen Analyse.
  857.