Generating Software from Specifications WS 2013/14 - File StructToJava.fw
@=~ ~p maximum_input_line_length = infinity Structure Generator This file specifies a DSL for a structure generator as described in the GSS book and in the GSS lecture material. There are only brief specifications in this file. Pleas refer to the lecture material or to the book for more elaborate explanations. The DSL specified here deviates slightly from that of the book and the lecture: There no type is predefined; each type is to be defined in the text or is to be imported. Here, two types are predefined; they are denoted by the keywords int and float. As in the lecture material and in the book the target language is Java. Input examples -------------- A structure description introduces a set of typed fields. ~O~<simple.str~>~{ Customer (addr: Address; account: int; ) Address (name: String; zip: int; city: String; ) import String from "java.lang" ~} There may be several descriptions of one structure. Their field descriptions are accumulated. The files ~{simple.1~} and ~{simple.2~} may be concatenated and processed together. If two field descriptions of a structure have the same name they must coincide in their type, too. ~O~<simple2.str~>~{ Address (zip: int; phone: int; ) ~} Fields may have a type that is defined by a structure description, or have a predefined type, or the implementation of the type is imported from a file stated in an import statement. Output example -------------- A Java file is generated. It contains a class declaration for each structure. For each field a declaration a set method and a get method is generated, and the constructor method is supplied with a parameter for initialization of the field. ~O~<simple.java~>~{~- import java.lang.String; class Customer { private Address addr_fld; private int account_fld; Customer (Address addr, int account) {addr_fld=addr; account_fld=account; } public void set_addr (Address addr) {addr_fld=addr;} public Address get_addr () {return addr_fld;} public void set_account (int account) {account_fld=account;} public int get_account () {return account_fld;} }; class Address { private String name_fld; private int zip_fld; private String city_fld; Address (String name, int zip, String city) {name_fld=name; zip_fld=zip; city_fld=city; } public void set_name (String name) {name_fld=name;} public String get_name () {return name_fld;} public void set_zip (int zip) {zip_fld=zip;} public int get_zip () {return zip_fld;} public void set_city (String city) {city_fld=city;} public String get_city () {return city_fld;} }; ~} Concrete syntax =============== The concrete syntax allows any number of the two constructs in any order. ~$~<Input structure~>==~{ Descriptions: (Import / Structure)*. Import: 'import' ImportNames 'from' FileName. ImportNames: ImportName // ','. Structure: StructureName '(' Fields ')'. Fields: Field*. Field: FieldName ':' Type ';'. Type: TypeName / 'int' / 'float'. ~} The four contexts of identifiers are distinguished by different nonterminals. ~$~<Roles of names~>==~{ StructureName: Ident. ImportName: Ident. FieldName: Ident. TypeName: Ident. ~} Token specification =================== FileNames are written in the input as C strings, e.g. "util"; the token processor stores the string value without the enclosing double quotes. ~$~<Token specification~>==~{ Ident: PASCAL_IDENTIFIER FileName: C_STRING_LIT [c_mkstr] C_COMMENT ~} Abstract syntax =============== ~$~<Abstract syntax~>==~{ RULE: Descriptions LISTOF Import | Structure END; RULE: Import ::= 'import' ImportNames 'from' FileName END; RULE: ImportNames LISTOF ImportName END; RULE: Structure ::= StructureName '(' Fields ')' END; RULE: Fields LISTOF Field END; RULE: Field ::= FieldName ':' Type ';' END; RULE: Type ::= TypeName END; RULE: Type ::= 'int' END; RULE: Type ::= 'float' END; RULE: StructureName ::= Ident END; RULE: ImportName ::= Ident END; RULE: FieldName ::= Ident END; RULE: TypeName ::= Ident END; ~} Name Analysis ============= The name analysis is specified directly on the base of Eli's environment module. Specification modules of Eli's specification library are not used. ~$~<Environment module~>==~{ $/Name/envmod.specs ~} Environment property -------------------- There may be many desciptions of a single structure. Each of them contributes fields to the structure entity. The structure has one environment which is referred to from every description of that structure. Hence, we make ~{Envir~} a property of the structure. ~$~<Environment property~>==~{ Envir: Environment; "envmod.h" ~} The ~{Envir~} property is set at exactly one occurrence of the name of the structure. Its scope is embedded in the root scope where the structure names a re defined in. The Environment is created in the root context: ~$~<Root environment~>==~{ SYMBOL Descriptions: Env: Environment; SYMBOL Descriptions INHERITS Range COMPUTE SYNT.Env = NewEnv (); END; ~} ~$~<Environment property computation~>==~{ SYMBOL StructureName: Env: Environment; SYMBOL StructureName COMPUTE SYNT.GotEnvir = IF (EQ (GetEnvir (THIS.Key, NoEnv), NoEnv), ResetEnvir (THIS.Key, NewScope (INCLUDING Descriptions.Env))); SYNT.Env = GetEnvir (THIS.Key, NoEnv) <- SYNT.GotEnvir; END; ~} Range role ---------- A range is an area of the program where the bindings of an environment hold. Ranges may be nested. ~$~<Range role~>==~{ CLASS SYMBOL Range: Env: Environment; SYMBOL Descriptions INHERITS Range END; SYMBOL Fields INHERITS Range END; RULE: Structure ::= StructureName '(' Fields ')' COMPUTE Fields.Env = StructureName.Env; END; ~} Identifier roles ---------------- Every identifier occurrence has three attributes. We introduce a symbol role ~{IdentOcc~} that comprises the attributes and computations which are identical for any identifier occurrence: ~$~<Name attributes~>==~{ CLASS SYMBOL IdentOcc: Sym: int, Bind: Binding, Key: DefTableKey; CLASS SYMBOL IdentOcc COMPUTE SYNT.Sym = TERM; SYNT.Key = KeyOf (SYNT.Bind); END; ~} Defining occurrences -------------------- A defining occurrence of an identifier establishes a binding between an identifier and an entity within the scope of the smallest enclosing range. Several defining occurrences of one identifier in one environment establish only one binding to one new entity. ~$~<Defining occurrence~>==~{ CLASS SYMBOL DefineAnIdent INHERITS IdentOcc COMPUTE SYNT.Bind = BindIdn (INCLUDING Range.Env, THIS.Sym); END; SYMBOL StructureName INHERITS DefineAnIdent END; SYMBOL ImportName INHERITS DefineAnIdent END; SYMBOL FieldName INHERITS DefineAnIdent END; ~} Applied occurrences ------------------- An applied occurrence of an identifier refers to a binding that is established by a defining occurrence of that identifier in the environment of the smallest enclosing range. If there is no such binding, an error is detected. ~$~<Applied occurrence~>==~{ CLASS SYMBOL UseAnIdent INHERITS IdentOcc COMPUTE SYNT.Bind = BindingInEnv (INCLUDING Range.Env, THIS.Sym) <- INCLUDING Range.GotBindings; IF (EQ (SYNT.Bind, NoBinding), message (ERROR, CatStrInd ("Name is not defined: ", THIS.Sym), 0, COORDREF)); END; SYMBOL TypeName INHERITS UseAnIdent END; ~} ~$~<Use String functions~>==~{ $/Tech/Strings.specs ~} Make sure that all relevant bindings are established before a lookup is initiated by ~{BindingInEnv~}. ~$~<Got bindings~>==~{ SYMBOL Descriptions COMPUTE SYNT.GotBindings = CONSTITUENTS DefineAnIdent.Bind SHIELD Range; END; SYMBOL Fields COMPUTE SYNT.GotBindings = CONSTITUENTS DefineAnIdent.Bind <- INCLUDING Range.GotBindings; END; ~} Semantic Checks =============== Check misused field identifiers ------------------------------- It is an error if the name of a field, say ~{Addr~}, of a structure occurs as the type of a field of that structure. That is erroneous even if there is a structure description for ~{Addr~}. We say the binding of the structure description for ~{Addr~} in the outer environment is hidden by the binding of ~{Addr~} to a field in the inner environment. ~$~<Field property~>==~{ IsField: int; ~} ~$~<Field check~>==~{ SYMBOL Descriptions COMPUTE SYNT.GotIsField = CONSTITUENTS FieldName.GotIsField; END; SYMBOL FieldName COMPUTE SYNT.GotIsField = ResetIsField (THIS.Key, 1); END; SYMBOL TypeName COMPUTE IF (GetIsField (THIS.Key, 0), message (ERROR, CatStrInd ("Field identifier not allowed here: ", THIS.Sym), 0, COORDREF)) <- INCLUDING Descriptions.GotIsField; END; ~} Set type property of fields and check consistency ------------------------------------------------- We associate a ~{Type~} property to fields. It is checked that multiple descriptions of a field coincide in their type. ~$~<Type property~>==~{ Type: DefTableKey [Is]; ErrorType; intType; floatType; ~} ~$~<Field types~>==~{ SYMBOL Type, FieldName: Type: DefTableKey; RULE: Field ::= FieldName ':' Type ';' COMPUTE FieldName.Type = Type.Type; END; RULE: Type ::= TypeName COMPUTE Type.Type = TypeName.Key; END; RULE: Type ::= 'int' COMPUTE Type.Type = intType; END; RULE: Type ::= 'float' COMPUTE Type.Type = floatType; END; SYMBOL FieldName COMPUTE SYNT.GotType = IsType (THIS.Key, THIS.Type, ErrorType); IF (EQ (ErrorType, GetType (THIS.Key, NoKey)), message (ERROR, "different types specified for this field", 0, COORDREF)) <- INCLUDING Descriptions.GotType; END; SYMBOL Descriptions COMPUTE SYNT.GotType = CONSTITUENTS FieldName.GotType; END; ~} Erroneous input --------------- The following input violates each of the conditions that are checked in the above specification: ~O~<error.str~>~{~- import String from "java.lang" Customer (Address: UndefType; account: Address; ) Address (name: String; name: int; ) float () ~} The following messages are issued for the above input: ~O~<error.in.msg~>~{ "error.in", line 11:1 ERROR: Syntax error "error.in", line 12:1 NOTE: Parsing resumed here "error.in", line 3:20 ERROR: Name is not defined: UndefType "error.in", line 4:20 ERROR: Field identifier not allowed here: Address "error.in", line 7:10 ERROR: different types specified for this field "error.in", line 8:10 ERROR: different types specified for this field ~} Target code generation ====================== Use source module and string module: ~$~<Source module~>==~{ #include "source.h" #include "csm.h" ~} Create header file ------------------ The generated Java file consists of the translation of the import statements, and the class declaration - one for each structure definition: ~$~<Header file patterns~>==~{ HeaderFile: $1 /*imports*/ "\n" $2 /*classes*/ Seq: $ $ ~} ~$~<Generate header file~>==~{ SYMBOL Descriptions COMPUTE PTGOutFile (CatStrStr (SRCFILE, ".java"), PTGHeaderFile (SYNT.IncludePtg, SYNT.ClassCode)); END; ~} DoIt role --------- The DoIt mechanism is used to translate fields and structures only once, although they may occur several times. ~$~<Done property~>==~{ Done: int; ~} ~$~<DoIt attribute~>==~{ ATTR DoIt: int; CLASS SYMBOL DoItRole COMPUTE SYNT.DoIt = IF (GetDone (THIS.Key, 0), 0, ORDER (ResetDone (THIS.Key, 1), 1)); END; ~} Type definitions ---------------- For Java forward definitions are not needed. ~$~<Type definition patterns~>==~{ ~} ~$~<Type definition~>==~{ ~} Structure translation --------------------- A structure is translated into a class: ~$~<Structure translation patterns~>==~{ StructureCode: "class " $1 string " {\n" $2 /*fields*/ "\n" " " $1 string " (" $3 /*signature*/ ")\n" " {" $4 /*field assignments*/ "}\n\n" $5 /*access methods*/ "};\n\n" ~} For each field four target constructs are generated: a definition, a formal parameter of the constructor function, an assignment in the constructor function, and access methods. ~$~<Structure translation properties~>==~{ FieldDefs: PTGNode; FieldParams: PTGNode; FieldAssigns: PTGNode; FieldAccess: PTGNode; "ptg_gen.h" ~} ~$~<Structure translation~>==~{ ATTR ClassCode: PTGNode; SYMBOL Descriptions COMPUTE SYNT.ClassCode = CONSTITUENTS StructureName.ClassCode WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull); SYNT.GotFieldTranslation = CONSTITUENTS FieldName.GotFieldTranslation; END; SYMBOL StructureName INHERITS DoItRole COMPUTE SYNT.ClassCode = IF (THIS.DoIt, PTGStructureCode (StringTable (THIS.Sym), GetFieldDefs (THIS.Key, PTGNULL), GetFieldParams (THIS.Key, PTGNULL), GetFieldAssigns (THIS.Key, PTGNULL), GetFieldAccess (THIS.Key, PTGNULL)), PTGNULL) <- INCLUDING Descriptions.GotFieldTranslation; END; ~} Type name property ------------------ The translation of type names is stored as a property. ~$~<Type name translation property~>==~{ PtgIdent: PTGNode; "ptg_gen.h" ~} ~$~<Type name translation~>==~{ SYMBOL TypeName COMPUTE SYNT.GotIdent = ResetPtgIdent (THIS.Key, PTGAsIs (StringTable (THIS.Sym))); END; SYMBOL Descriptions COMPUTE SYNT.GotIdent = ORDER (ResetPtgIdent (intType, PTGAsIs ("int")), ResetPtgIdent (floatType, PTGAsIs ("float"))) <- CONSTITUENTS TypeName.GotIdent; END; ~} Field definition translation ---------------------------- the four target constructs for the translation of a field are specified: ~$~<Field translation patterns~>==~{ FieldDef: " private " $2 " " $1 string "_fld;\n" FieldParam: $2 " " $1 string FieldAssign: $1 string "_fld=" $1 string "; " FieldAccess: " public void set_" $1 string " (" $2 " " $1 string ") {" $1 string "_fld=" $1 string ";}\n" " public " $2 " get_" $1 string " () {return " $1 string "_fld;}\n" AsIs: $ string CommaSeq: $ {", "} $ ~} ~$~<Field definition translation~>==~{ SYMBOL Structure: Key: DefTableKey; RULE: Structure ::= StructureName '(' Fields ')' COMPUTE Structure.Key = StructureName.Key; END; SYMBOL FieldName: TypePtg: PTGNode; SYMBOL FieldName INHERITS DoItRole COMPUTE SYNT.TypePtg = GetPtgIdent (THIS.Type, PTGNULL) <- INCLUDING Descriptions.GotIdent; SYNT.GotFieldTranslation = IF (THIS.DoIt, ORDER (ResetFieldDefs (INCLUDING Structure.Key, PTGSeq (GetFieldDefs (INCLUDING Structure.Key, PTGNULL), PTGFieldDef (StringTable (THIS.Sym), THIS.TypePtg))), ResetFieldParams (INCLUDING Structure.Key, PTGCommaSeq (GetFieldParams (INCLUDING Structure.Key, PTGNULL), PTGFieldParam (StringTable (THIS.Sym), THIS.TypePtg))), ResetFieldAssigns (INCLUDING Structure.Key, PTGSeq (GetFieldAssigns (INCLUDING Structure.Key, PTGNULL), PTGFieldAssign (StringTable (THIS.Sym)))), ResetFieldAccess (INCLUDING Structure.Key, PTGSeq (GetFieldAccess (INCLUDING Structure.Key, PTGNULL), PTGFieldAccess (StringTable (THIS.Sym), THIS.TypePtg))))); END; ~} Import translation ------------------ Imports are translated. In Java one can import a single type ($1) from a package ($2): ~$~<Import patterns~>==~{ Import: "import " $2 string "." $1 string ";\n" ~} An include directive is created for the given file if some of the type names mentioned are not defined explicitly. ~$~<Import translation~>==~{ ATTR IncludePtg: PTGNode; SYMBOL Descriptions COMPUTE SYNT.IncludePtg = CONSTITUENTS ImportName.IncludePtg WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull); END; ATTR File: int; RULE: Import ::= 'import' ImportNames 'from' FileName COMPUTE Import.File = FileName; END; SYMBOL ImportName: isDefined: int; RULE: ImportName ::= Ident COMPUTE ImportName.isDefined = NE (GetEnvir (ImportName.Key, NoEnv), NoEnv) <- INCLUDING Descriptions.GotType; ImportName.IncludePtg = IF (ImportName.isDefined, PTGNULL, PTGImport (StringTable (Ident), StringTable (INCLUDING Import.File))); END; ~} Composition of the specifications --------------------------------- ~O~<Name.specs~>~{ ~<Environment module~> ~<Use String functions~> ~} ~O~<Structure.head~>~{ ~<Source module~> ~} ~O~<Structure.con~>~{ ~<Input structure~> ~<Roles of names~> ~} ~O~<Structure.gla~>~{ ~<Token specification~> ~} ~O~<Structure.lido~>~{ ~<Abstract syntax~> ~} ~O~<Name.lido~>~{ ~<Range role~> ~<Environment property computation~> ~<Name attributes~> ~<Defining occurrence~> ~<Applied occurrence~> ~<Got bindings~> ~<Root environment~> ~} ~O~<Property.lido~>~{ ~<Field types~> ~<Field check~> ~} ~O~<Transform.lido~>~{ ~<Generate header file~> ~<DoIt attribute~> ~<Type definition~> ~<Structure translation~> ~<Type name translation~> ~<Field definition translation~> ~<Import translation~> ~} ~O~<Name.pdl~>~{ ~<Environment property~> ~} ~O~<Property.pdl~>~{ ~<Type property~> ~<Field property~> ~} ~O~<Transform.pdl~>~{ ~<Done property~> ~<Structure translation properties~> ~<Type name translation property~> ~} ~O~<Transform.ptg~>~{ ~<Header file patterns~> ~<Type definition patterns~> ~<Structure translation patterns~> ~<Field translation patterns~> ~<Import patterns~> ~}
Generiert mit Camelot | Probleme mit Camelot? | Geändert am: 15.01.2014