Universität Paderborn - Home Universität Paderborn
Die Universität der Informationsgesellschaft

Generating Software from Specifications WS 2013/14 - File StructToCplpl.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 C++.

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 "std"
~}

There may be several descriptions of one structure.
Their field descriptions are accumulated.
The files ~{simple.str~} and ~{simple2.str~} 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
--------------

For the input in simple.str 
a C++ file named simple.cc is generated.
Its content is shown in the macro simple.plpl.

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.

You can compile a generated C++ file by
   g++ -c simple.cc

~O~<simple.plpl~>~{~-
#include <string>
using std::string;

typedef class Customer_Cl *Customer;
typedef class Address_Cl *Address;

class Customer_Cl {
private:
  Address addr_fld;
  int account_fld;
public:
  Customer_Cl (Address addr, int account)
  {addr_fld=addr; account_fld=account; }

  void set_addr (Address addr) {addr_fld=addr;}
  Address get_addr () {return addr_fld;}
  void set_account (int account) {account_fld=account;}
  int get_account () {return account_fld;}
};

class Address_Cl {
private:
  string name_fld;
  int zip_fld;
  string city_fld;
public:
  Address_Cl (string name, int zip, string city)
  {name_fld=name; zip_fld=zip; city_fld=city; }

  void set_name (string name) {name_fld=name;}
  string get_name () {return name_fld;}
  void set_zip (int zip) {zip_fld=zip;}
  int get_zip () {return zip_fld;}
  void set_city (string city) {city_fld=city;}
  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 "std"

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 C++ file consists of the translation of 
the import statements, of the forward references for the
class pointer types, and the class declaration - one for
each structure definition:

~$~<Header file patterns~>==~{
HeaderFile:
        $1 /*imports*/
        "\n"
        $2 /*ptr types*/
        "\n"
        $3 /*classes*/
~}

~$~<Generate header file~>==~{
SYMBOL Descriptions COMPUTE
  PTGOutFile
    (CatStrStr (SRCFILE, ".cc"),
     PTGHeaderFile 
       (SYNT.IncludePtg,
        SYNT.TypeDefCode,
        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 each structure a forward definition is created:
~$~<Type definition patterns~>==~{
TypeDef:
        "typedef class " $1 string "_Cl *" $1 string ";\n"

Seq: $ $
~}

~$~<Type definition~>==~{
ATTR TypeDefCode: PTGNode;

SYMBOL Descriptions COMPUTE
  SYNT.TypeDefCode =
    CONSTITUENTS StructureName.TypeDefCode
      WITH (PTGNode, PTGSeq, IDENTICAL, PTGNull);
END;

SYMBOL StructureName INHERITS DoItRole COMPUTE
  SYNT.TypeDefCode = 
    IF (THIS.DoIt,
       PTGTypeDef (StringTable (THIS.Sym)), PTGNULL);
END;
~}

Structure translation
---------------------

A structure is translated into a class:
~$~<Structure translation patterns~>==~{
StructureCode:
        "class " $1 string "_Cl {\n"
        "private:\n"
        $2 /*fields*/
        "public:\n"
        "  " $1 string "_Cl (" $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 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:
     "  " $2 " " $1 string "_fld;\n"

FieldParam:
     $2 " " $1 string

FieldAssign:
     $1 string "_fld=" $1 string "; "

FieldAccess:
     "  void set_" $1 string " (" $2 " " $1 string ") {" 
            $1 string "_fld=" $1 string ";}\n"
     "  " $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.
The following translation is only correct in C++ if the parameter $2
is the name std of the standard library.
~$~<Import patterns~>==~{
Import:
        "#include <"$1 string ">\n"
        "using "$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.

For each ImportName, which is not explicitly defined,
an include directive and a using construct is created.
The include directive works in this form only for the
standard library std.

~$~<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: 08.01.2014