§5 Semantische Analyse Symboltabelle Compiler Benutzerdialog Scanner Parser Symboltabelle Codegenerator Attribute Variablen erhalten Attribute wie • Name mit dem sie angesprochen wird • Typ der die Sorte bestimmt • Platz Bedarf an Speicher für den Typ • Scope Deklarationsumgebung • Range Bedingungen an die Werte in der Variablen • Adresse wo sie im Speicher abgelegt wird (Offset) Compilerbau §5 2 Kontrollaufgaben Der Compiler hat viele Kontrollaufgaben zu erfüllen • Welchen Typ hat ein Bezeichner (Const, Var, Type, Function, Procedure)? • Deklaration eintragen • Platzbedarf • Adresse (Offset von einer Basisadresse) • Ist ein Bezeichner schon deklariert? • Auflösung von Überladung • Typprüfungen • Fehlerbehandlung 3 Compilerbau §5 Zwischenablage • VAR x,y,z : A_Typ; Problem: Verarbeitung erst nach Lesen des Typs ! • Lösung: Temporäre Speicherung in einer Zwischenablage UNIT NameBuffer; INTERFACE USES Global; PROCEDURE Ablegen(Name:Identifier); FUNCTION Entnehmen : Identifier; • Je nach Anwendung muß die Zwischenablage wie hier als Stack oder bei anderen Problemen als Queue realisiert werden. Compilerbau §5 4 Symboltabelle Zugriffe • Zugriffe der Symboltabelle fürVariablen elem. Typs FUNCTION new_name(Entry : SymTabRec) : BOOLEAN; • Trägt einen neuen Namen mit Typ und Adresse in die Symboltabelle ein und liefert TRUE zurück. FALSE bei ERROR_duplicate_identifier. FUNCTION get_name(Name : Identifier VAR Entry : SymTabRec) : BOOLEAN; • Sucht einen Namen in der Tabelle und legt die zugehörigen Angaben in Entry ab. Gibt FALSE aus, wenn der Name nicht gefunden wird. 5 Compilerbau §5 Suchbaum • Form: binärer Baum realisiert als – Zeiger auf einen Knoten – Jeder Knoten enthält neben der eigentlichen Information Zeiger auf zwei Teilbäume links und rechts. • Bedingungen: – KnotenInfo "größer" als alle KnotenInfo's im linken Teilbaum – KnotenInfo "kleiner" als alle KnotenInfo's im rechten Teilbaum • Zugriffe: – Suche nach einer Info : Vergleiche in einem Knoten und gehe nach Ergebnis rechts oder links – Einfügen: Nach erfolgloser Suche steht man an einer Stelle, wo die Info korrekt eigefügt werden kann. Compilerbau §5 6 Suchbaum Beispiel 8 Zeiger auf Nichts 6 20 4 25 10 1 5 Hier endet die Suche 21 17 soll eingefügt werden 7 Compilerbau §5 Suchbaum Aufzählen Auflisten der Baum-Inhalte links - Wurzel - rechts : 1 4 5 6 8 10 20 21 25 natürliche Ordnung 8 6 4 1 20 25 10 5 Auflisten der Baum-Inhalte Wurzel - links - rechts : 8 6 4 1 5 20 10 25 21 z.B. Prüf. der Bedingung 21 Auflisten der Baum-Inhalte links - rechts - Wurzel : 1 5 4 6 10 21 25 20 8 z.B. beim Löschen Compilerbau §5 8 Feldtypen • Unsere Grammatik läßt zusätzlich ARRAY-Typen zu — Type ::= — Types ::= id (* benannter Typ *) | ARRAY [IntConst] OF Type (* unbenannter Array type *) [TYPE id = Type ; {id = Type ;}] • Die Symboltabelle hat nun außer den Symbolarten VAR und CONST noch die Symbolart TYPE — SymKind = (VarSym, ConstSym, TypeSym) • Jede Symbolart erhält ihren eigenen Description Record Compilerbau §5 9 SIMPL-Deklaration • CONST Dim = 10 • TYPE • VAR Index Element Vector Matrix v v1 M i j x : : : : : : = = = = Integer; Integer; ARRAY[Dim] OF Element; ARRAY[Dim] OF ARRAY[Dim]OF Element; Vektor; ARRAY[Dim] OF Element; Matrix; Index; Integer; Element Compilerbau §5 10 Der zugehörige Suchbaum INTEGER BOOLEAN Vektor CHAR x Matrix j Dim Index v M v1 Element i Compilerbau §5 11 Platzbedarf von Typen • Berechnung der Adresse (Anfangsadresse) von Variablen: – Ein Zeiger FreeDataSpace : PData zeigt auf den nächsten freien Platz im Datensegment, er wird mit 0 initialisiert. – Jede neue Variable bekommt diesen Zeiger als Adresse zugeordnet, danach wird dem Zeiger der Platzbedarf des Variablen-Typs aufaddiert. • Berechnung des Platzbedarfs eines Typs – Für die Elementaren Typen wird jeweils der Platzbedarf festgehalten (im Beispiel 1 für alle drei elementaren Typen) – Der Platzbedarf für einen User-Typ ist derselbe, wie der für den Typ in der zugehörigen Definition – Der Platzbedarf für einen Array-Typ errechnet sich aus dem Platzbedarf für den Element-Typ multipliziert mit dem Limit. – Der Platzbedarf muß im Typ-Descriptor in einem weiteren Feld Size festgehalten werden, Die Berechnung erfolgt rückwärts über die Typdefinition (Zwischenablage der Deskriptorenzeiger notwendig!) Compilerbau §5 12 Typprüfung • Beispiel: – { { { { 1 2 3 4 } } } } { { { 5 } 6 } 7 } { 8 } { 9 } { 10 } { 11 } x i x x := := := := v[ 5 ]; x; v[ i ]; v[ i ] + 15; x := j; x := i; x := i + 1; v v v1 v[i] := := := := v1; M[ 1 ]; M[ 1 ]; v1[i]; Linke Seite Rechte Seite Ok? Element Index Element Element Element Element Element Element +Integer Integer Index Index +Integer V2.2 (ARRAY) T7.2 (ARRAY) T7.2 (ARRAY) Element Element Element Element Vektor Vektor V2.2 (ARRAY) Element j n j ? ? n ? n n n j – j : Typen stimmen überein – n : Typen sind verschieden – ? : Integer ist "generisch" für Element ( bzw. Index) 13 Compilerbau §5 Typgleichheit • identisch : sind Typen, die genau denselben Descriptor besitzen (equal). • generisch sind Typen, wenn einer durch eine Folge von gleich : User-Umbenennungen aus dem anderen entsteht (Generischer Abkömling : eqivalent). • vergleichbar : sind Typen, die generische Abkömmlinge desselben Typs sind (compatible). • ähnlich : sind Typen, die bis auf Umbenennungen dieselbe Definition besitzen(similar). • gleichmächtig : sind Typen, die denselben Speicherplatz benötigen(equipotent). Compilerbau §5 14 Typgleichheit • equal, eqivalent und compatible sind die einzigen Typgleichheitsbegriffe, die für Compileroptionen zur Verfügung stehen sollten. TYPE Equality = ( NotEqual, Compatible, Equivalent, Equal ); FUNCTION CompareTypes( a, b : TypeDesrcRec ): Equality; VAR Erfolgswert : Equality; BEGIN IF a = b THEN BEGIN CompareTypes := Equal; exit END; IF ( a^.TypeKind <> UserTyp ) OR ( b^.TypeKind <> UserTyp ) THEN Erfolgswert := Equivalent ELSE Erfolgswert := Compatible; { reduziere Anwendertyp auf nächste Grundform } WHILE a^.TypeKind = UserTyp DO a := a^.TypeDescr; WHILE b^.TypeKind = UserTyp DO b := b^.TypeDescr; IF a = b THEN CompareTypes := Erfolgswert ELSE CompareTypes := NotEqual END; { FUNCTION CompareTypes } 15 Compilerbau §5 Type-Casting Veränderung des Typs ohne Veränderung der Daten : in Form eines Funktionsaufrufs mit den Typnamen des neu zuzuweisenden Typs: • VAR x : A_typ; y : B_typ; {vom gleichem Platzbedarf} : BEGIN : .... A_typ(y); {ist derselbe Speicherbereich wie y, kann aber nach den Regeln von A_typ behandelt, z.B. x zugewiesen, werden.} : END; Die Verantwortung für die Datenintegrität liegt nun ganz beim Benutzer! Compilerbau §5 16 Prozeduren und Funktionen • Program ::= PROGRAM Id ";" Vars Routines Block "." • Routines ::= { Funcdecl | Procdecl } • Funcdecl ::= FUNCTION Id "(" Parlist ")" ":" Type ";" Vars Routines Block ";" | EXTERN FUNCTION Id "(" Parlist ")" ":" Type ";" • Procdecl ::= PROCEDURE Id "(" Parlist ")" ";" Vars Routines Block ";" | EXTERN PROCEDURE Id "(" Parlist ")" ";" • Parlist ::= { [ VAR ] Decl } • Call ::= Id "(" Callpara ")" • Callpara ::= empty | Expr { "," Expr } • Return ::= RETURN [ Expr ] • Stmt ::= While | If | Assign | Call | Return Compilerbau §5 17