Zum Verzeichnisinhalt
Zur Musterlösung
(SHIFT + Linke Maustaste)
@=~
~p maximum_input_line_length = infinity
~p typesetter = none
~t title titlefont left "Eine Loesung der Projektaufgabe"
~A~<Funktionsweise des Prozessors~>
Ziel des zu entwickelnden Prozessors ist es, aus Verbundspezifikationen
C-Datenstrukturen, Zugriffsmakros und Allokationsfunktionen zu erzeugen.
Hier ist eine Beispieleingabe fuer unseren Prozessor:
~O~<simple~>~{
import "String.h"
Adresse
(name: String;
plz: int;
stadt: String;
)
~}
Daraus erzeugt der generierte Textprozessor eine .h-Datei und eine
.c-Datei. Die .h-Datei hat folgendes Aussehen:
~O~<simple.hout~>~{
#include "String.h"
typedef struct __Adresse *Adresse;
typedef struct __Adresse {
String name_fld;
int plz_fld;
String stadt_fld;
} _Adresse;
#define nameOfAdresse(x) ((x)->name_fld)
#define plzOfAdresse(x) ((x)->plz_fld)
#define stadtOfAdresse(x) ((x)->stadt_fld)
extern Adresse newAdresse ();
~}
Die .c-Datei sieht wie folgt aus:
~O~<simple.cout~>~{
#include <stdlib.h>
#include "Adresse.h"
Adresse newAdresse ()
{ return (Adresse)malloc(sizeof(_Adresse));
}
~}
~A~<Die syntaktische und lexikalische Struktur der Eingabesprache~>
~O~<Struct.con~>~{
Program: Import* StructDef*.
Import: 'import' FileName.
StructDef: StructName '(' Fields ')'.
Fields: Field+.
Field: FieldName ':' TypeName ';'.
StructName: Ident.
FieldName: Ident.
TypeName: Ident.
~}
Die verschiedenen fuer die Semantikanalyse und Transformation wichtigen
Rollen von Bezeichnern werden hier bereits in der konkreten Syntax
unterschieden.
Die nichtliteralen Terminalsymbole dieser Grammatik sind Bezeichner
(~{Ident~}) und Dateiname (~{FileName~}):
~O~<Struct.gla~>~{
Ident: PASCAL_IDENTIFIER
FileName: C_STRING_LIT
C_COMMENT
~}
~A~<Namensanalyse~>
Fuer die Namensanalyse benoetigen wir die Bibliotheksmodule
~{AlgScope~} und ~{Unique~}.
~O~<StructName.specs~>~{
$/Name/AlgScope.gnrc:inst
$/Prop/Unique.gnrc:inst
~}
~B~<Die Gueltigkeit der Namen~>
Die Symbolrollen aus diesen Bibliotheksmodulen ordnen wir wie folgt
den konkreten Symbolen unserer Grammatik zu:
~O~<StructName.lido~>~{
ATTR Sym: int;
CLASS SYMBOL IdentOcc COMPUTE SYNT.Sym = TERM; END;
SYMBOL Program INHERITS RootScope END;
SYMBOL Fields INHERITS RangeScope END;
SYMBOL StructName INHERITS IdentOcc, IdDefScope END;
SYMBOL FieldName INHERITS IdentOcc, IdDefScope END;
SYMBOL TypeName INHERITS IdentOcc, IdUseEnv END;
SYMBOL Program INHERITS RangeUnique END;
SYMBOL StructName INHERITS Unique COMPUTE
IF (NOT (THIS.Unique),
message (ERROR, CatStrInd("Structure name is multiply defined: ",
THIS.Sym),
0, COORDREF));
END;
SYMBOL FieldName INHERITS Unique COMPUTE
IF (NOT (THIS.Unique),
message (ERROR, CatStrInd("Field name is multiply defined: ",
THIS.Sym),
0, COORDREF));
END;
~}
~B~<Die Eigenschaften der Namen~>
Die Eigenschaft von Bezeichnern, die fuer die Ueberpruefung des
Eingabetextes wichtig ist, ist die Unterscheidung, ob sie als Feldnamen
(~{FieldName~}) oder Strukturname (~{StructName~}) definiert wurden:
~O~<NameKind.h~>~{
typedef enum {isFieldName, isStructName} NameProp;
~}
Definition der Eigenschaft ~{NameKind~}:
~O~<NameKind.pdl~>~{
NameKind: NameProp; "NameKind.h"
~}
Die folgenden Berechnungen stellen sicher, dass Bezeichner, die als Feldnamen
definiert sind, nicht als Typnamen verwendet werden. Wir haben diese
Berechnungen als Beispiel fuer semantische Ueberpruefungen eingefuegt. Sie
waeren fuer die vorliegende
Aufgabenstellung wegen der Art der Ausgabeerzeugung
(Feldnamen bekommen den Suffix ~{_fld~}) ansonsten nicht unbedingt noetig.
~O~<NameKind.lido~>~{
SYMBOL Program COMPUTE
SYNT.GotNameKind =
CONSTITUENTS (StructName.GotNameKind, FieldName.GotNameKind);
END;
SYMBOL StructName COMPUTE
SYNT.GotNameKind = ResetNameKind (THIS.Key, isStructName);
END;
SYMBOL FieldName COMPUTE
SYNT.GotNameKind = ResetNameKind (THIS.Key, isFieldName);
END;
SYMBOL TypeName COMPUTE
IF (EQ (GetNameKind (THIS.Key, isStructName), isFieldName),
message (ERROR,
CatStrInd
("Field identifier used as type name: ",
THIS.Sym),
0, COORDREF))
<- INCLUDING Program.GotNameKind;
END;
~}
~A~<Die Erzeugung der Ausgabe-Dateien~>
~B~<Die Erzeugung der .c-Datei~>
Die .c-Datei besteht aus einem Kopf, der die .h-Datei und
~{stdlib.h~} inkludiert und der Defintion der new-Funktionen
fuer die einzelnen Verbundtypen:
~O~<StructC.ptg~>~{
CStructFile:
"#include \"" $1 string "\"\n"
"#include <stdlib.h>\n\n"
$2
NewFct:
$1 string
" new" $1 string " ()\n"
"{ return ("
$1 string ")malloc(sizeof(_" $1 string "));\n"
"}\n\n"
~}
Die Ausgabeerzeugung benoetigt die Deklarationen des Source-Moduls
(~{source.h~}) zum Zugriff auf den Eingabedateinamen (~{SRCFILE~})
und die Deklarationen aus ~{csm.h~} zur Stringmanipulation.
~O~<TransC.head~>~{
#include "source.h"
#include "csm.h"
~}
Die Erzeugung der .c-Datei wird an der Wurzel des Strukturbaumes initiiert.
Auf die Programmtexte fuer die new-Funktionen wird dabei durch
Fern-Zugriff (~{CONSTITUENTS~}) zugegriffen.
~O~<TransC.lido~>~{
ATTR CPtg: PTGNode;
SYMBOL Program COMPUTE
PTGOutFile
(CatStrStr (SRCFILE, ".c"),
PTGCStructFile
(CatStrStr (SRCFILE, ".h"),
CONSTITUENTS StructDef.CPtg
WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull)));
END;
RULE: StructDef ::= StructName '(' Fields ')' COMPUTE
StructDef.CPtg = PTGNewFct (StringTable (StructName.Sym));
END;
~}
~B~<Die Erzeugung der .h-Datei~>
Die erzeugte .h-Datei besteht aus 3 Abschnitten: Import-Vereinbarungen,
Pointer-Typ-Definitionen und der Uebersetzung der
Verbundstrukur mit Vereinbarung der new-Funktionen und
Zugriffsmakros fuer die Verbund-Komponenten:
~O~<TransH.ptg~>~{
HStructFile:
$1 /*imports*/
"\n"
$2 /*ptr types*/
"\n"
$3 /*access*/
Import:
"#include " $ string "\n"
PtrType:
"typedef struct __" $1 string " *" $1 string ";\n"
StructTrans:
$1 /*struct type*/
"\n"
$2 /*field macros*/
"\n"
$3 /*extern new*/
"\n"
ExternNew:
"extern " $1 string " new" $1 string " ();\n"
StructType:
"typedef struct __" $1 string " {\n"
$2 /*fields*/
"} _" $1 string ";\n"
Field: " " $1 string " " $2 string "_fld;\n"
FieldMacro:
"#define " $1 string "Of" $2 string
"(x) ((x)->" $1 string "_fld)\n"
Seq: $ $
~}
Die Erzeugung der .h-Datei wird an der Wurzel des Strukturbaumes initiiert.
Auf die Programmtexte fuer die import-Klauseln, die
Pointer-Typ-Definitionen und die Struktur-Information wird durch 3
Fern-Zugriffe (~{CONSTITUENTS~}) zugegriffen.
~O~<TransH.lido~>~{
ATTR HPtg, PtrPtg, MacroPtg: PTGNode;
SYMBOL Program COMPUTE
PTGOutFile
(CatStrStr (SRCFILE, ".h"),
PTGHStructFile
(CONSTITUENTS Import.HPtg
WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull),
CONSTITUENTS StructDef.PtrPtg
WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull),
CONSTITUENTS StructDef.HPtg
WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull)));
END;
RULE: Import ::= 'import' FileName COMPUTE
Import.HPtg = PTGImport (StringTable (FileName));
END;
RULE: StructDef ::= StructName '(' Fields ')' COMPUTE
StructDef.PtrPtg =
PTGPtrType (StringTable (StructName.Sym));
StructDef.HPtg =
PTGStructTrans
(PTGStructType
(StringTable (StructName.Sym),
CONSTITUENTS Field.HPtg
WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull)),
CONSTITUENTS Field.MacroPtg
WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull),
PTGExternNew (StringTable (StructName.Sym)));
StructDef.Sym = StructName.Sym;
END;
RULE: Field ::= FieldName ':' TypeName ';' COMPUTE
Field.HPtg =
PTGField
(StringTable (TypeName.Sym),
StringTable (FieldName.Sym));
Field.MacroPtg =
PTGFieldMacro
(StringTable (FieldName.Sym),
StringTable (INCLUDING StructDef.Sym));
END;
~}