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

Generating Software from Specifications WS 2013/14 - File CalTable.fw

@=~
~p maximum_input_line_length = infinity

This file contains the specification of a calendar processor.
It reads a sequence of appointments, represents them by
a tree structure, checks some consistency conditions,
and outputs a table in HTML containing the input data.

The following file contains a correct input file for the 
processor such that almost every language construct occurs
at least once:
~O~<cal.ok~>~{~-
1.11.           20:00          "Theater"
Thu             11:15 - 12:45  "GSS lecture"
Weekday - Mon   12:05          "Dinner in Palmengarten"
Mon, Thu                       "Dean's office"
31.12.          23:59          "Jahresende"
12/31           23:59          "End of year"
25.5.           11:15          "GSS" -- Lecture assessment
May 1                          "Labour Day"
1.1.            12:00 - 12:01  "Flash1"
2/28            9:00           "end of Feb"
31.12.          23:59          "Schluss"
~}

The following input file exhibits an error in each line:
~O~<cal.err~>~{~-
1.1.   12:00 - 12:00 "wrong time span"
1.1.   12:00 - 11:59 "wrong time span"
2/29   9:00          "not this year"
31.4.  9:00          "not in April"
1.13.  9:00          "not in any year"
M 1                  "wrong month"
2.5.   24:00         "wrong hour"
4.5.   11:15         "wrong comment" -
~}

The input for our calendar processor is a sequence of entries.
Each entry consists of a date description and an event
that is to happen at that date. The dates may be specified in
several forms:

~$~<Entries con~>==~{
Calendar:       Entry+.
Entry:          DateDescr Event.
DateDescr:      Date / DayNames / GeneralPattern.
~}

Concrete dates specify a month and a day in that month.
Month and day can be given by numbers, using two notational variants.
The third variant allows to write the name of a month.
It will be checked that a day of the given number exists in the
given month:
~$~<Date con~>==~{
Date:           DayNum '.' MonNum '.' /
                MonNum '/' DayNum /
		Month DayNum.
DayNum:         Integer.
MonNum:         Integer.
~}

An event may occur at certain days of the week and obey the
same pattern every week. In these cases the date description
either enumerate the names of the days of the week, or
specify the days of the weekend or the non-weekend days.
In the latter cases further days may be added or subtracted:

~$~<Patterns con~>==~{
DayNames:       DayName / DayNames ',' DayName.
DayName:        Day.
GeneralPattern: SimplePattern / SimplePattern Modifier.
SimplePattern: 'Weekday' / 'Weekend'.
Modifier:       '+' DayNames / '-' DayNames.
~}

An event is described by a time or a time span and an
explaining string. The start time of a time span must be
earlier than the end time:

~$~<Event con~>==~{
Event:          When Description / Description.
When:           Time / Time '-' Time.
~}

The following file comprises the concrete syntax:
~O~<Calendar.con~>~{
~<Entries con~>
~<Date con~>
~<Patterns con~>
~<Event con~>
~}

The language has the following basic symbols.
Event descriptions are string literals in C notation.
Integral numbers are used to describe months and days.
The days of the week are named by 3-letter abbreviations.
They are processed like predefined identiers and stored
in the identifier table by mkidn. (On output they can be
copied from there.) The abbreviated names of months are
mapped to integral numbers by mkMonth.
The language allows to write line comments s in Ada.

The canned description C_STRING_LIT applies the token
processor mkstr; it is overridden by c_mkstr, in order to store
string values without delimiting double quotes.

~O~<Calendar.gla~>~{
Description: C_STRING_LIT [c_mkstr]
Integer:     PASCAL_INTEGER
Day:         $Mon|Tue|Wed|Thu|Fri|Sat|Son [mkidn]
Month:       $Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec [mkMonth]
Time:        $(([0-9]|1[0-9]|2[0-3]):[0-5][0-9]) [mkstr]
             ADA_COMMENT
~}

The following abstract syntax is systematically rewritten from the
concrete syntax. It deviates only in the following aspects:
Alternatives in the concrete syntax are separate rules in the
abstract syntax. The sequence of entries is expressed by a LISTOF
production. Two unifying mappings are described below.

This file containing the abstract syntax could be completely
omitted from the specification, Eli then would generate it from
the remaining specification. However, it is recommended to
keep it, and to use it as a reference when spcifying computations
in tree contexts. It is strongly recommended NOT to write
rule computations into this file. Instead, rules may be copied
to new fragments and extended by computatons there.
That for different computational tasks several copies of a
rule may be used, in order to separate the task from each other.

~O~<Calendar.lido~>~{
RULE: Calendar    LISTOF Entry			END;
RULE: Entry          ::= DateDescr Event	END;
RULE: DateDescr      ::= Date			END;
RULE: DateDescr      ::= DayNames		END;
RULE: DateDescr      ::= Pattern		END;

RULE: Date           ::= DayNum MonNum		END;
RULE: Date           ::= Month DayNum		END;
RULE: DayNum         ::= Integer		END;
RULE: MonNum         ::= Integer		END;

RULE: DayNames       ::= DayName		END;
RULE: DayNames       ::= DayNames ',' DayName	END;
RULE: DayName        ::= Day			END;
RULE: Pattern        ::= Pattern Modifier	END;
RULE: Pattern        ::= 'Weekday'		END;
RULE: Pattern        ::= 'Weekend'		END;
RULE: Modifier       ::= '+' DayNames		END;
RULE: Modifier       ::= '-' DayNames		END;

RULE: Event          ::= When Description	END;
RULE: Event          ::= Description		END;
RULE: When           ::= Time			END;
RULE: When           ::= Time '-' Time		END;
~}

The abstract syntax unifies the two pattern nonterminals
of the concrete syntax, and it unifies the European and
the US way of writing the day of a month:
~O~<Calendar.map~>~{
MAPSYM
Pattern ::= GeneralPattern SimplePattern.

MAPRULE
Date:   DayNum '.' MonNum '.' < $1 $2 >.
Date:   MonNum '/' DayNum     < $2 $1 >.
~}

The following C module implements the three token coding functions
mkTime, mkMonth, and mkDay which compute an interger for the
given string (The number of the minute in the day, of the month in the
year, and of the day in the week. Only mkMonth is used in the
scanner specification above, because times are stored as strings and
days are stored as identifiers. The function Time2Int 
computes the number of the minute in the day, as mkTime does;
it is only used to simplify the comparison of times.

~O~<Calendar.HEAD~>==~{
#include "Calendar.h"
~}
~O~<Calendar.h~>==~{
extern void mkTime (char *, int, int *, int*);
extern void mkMonth (char *, int, int *, int*);
extern void mkDay (char *, int, int *, int*);
extern int Time2Int (char *t);
~}
~O~<Calendar.c~>==~{
#include <string.h>
#include "Calendar.h"

void mkDay (char *d, int l, int *c, int *i)
{
    switch (d[0]) {
        case 'F': *i = 5; break;
        case 'M': *i = 1; break;
        case 'W': *i = 3; break;
        case 'S': *i = (d[1] == 'a'? 6 : 7); break;
        case 'T': *i = (d[1] == 'u' ? 2 : 4); break;
    }
}

void mkMonth (char *m, int l, int *c, int *i)
{
    switch (m[0]) {
        case 'A': *i = (m[1] == 'p' ? 4 : 8); break;
        case 'D': *i = 12; break;
        case 'F': *i = 2; break;
        case 'J': *i = (m[1] == 'a' ? 1 : (m[2] == 'n' ? 6 : 7)); break;
        case 'M': *i = (m[2] == 'r' ? 3 : 5); break;
        case 'N': *i = 11; break;
        case 'O': *i = 10; break;
        case 'S': *i = 9; break;
    }
}

int Time2Int (char *t)
{
   char *colon = strchr (t, ':');
   int hours, mins;

   *colon = '\0';
   hours = atoi (t);
   mins = atoi (colon + 1);
   *colon = ':';
   return hours*60 + mins;
}

void mkTime (char *t, int l, int *c, int *i)
{
   char *colon = strchr (t, ':');
   int hours, mins;

   *colon = '\0';
   hours = atoi (t);
   mins = atoi (colon + 1);
   *colon = ':';
   *i = hours*60 + mins;
}
~}

An eror message is reported if the end time of a time span
is not later than the start time:

~O~<TimeRange.lido~>==~{
RULE:	When ::= Time '-' Time COMPUTE
  IF (LE (Time2Int (StringTable(Time[2])), 
          Time2Int (StringTable(Time[1]))),
  message (ERROR, "end must later than begin", 0, COORDREF));
END;
~}

An eror message is reported if the specified month does not exist
in a year or if the specified day does not exist in the given
month:

~O~<DateChk.lido~>==~{
RULE: Date ::= DayNum MonNum COMPUTE
  IF (wrongDate (DayNum.val, MonNum.val),
  message (ERROR, "wrong date", 0, COORDREF));
END;

RULE: Date ::= Month DayNum COMPUTE
  IF (wrongDate (DayNum.val, Month),
  message (ERROR, "wrong date", 0, COORDREF));
END;

SYMBOL DayNum, MonNum: val: int;
RULE: DayNum ::= Integer COMPUTE
  DayNum.val = Integer;
END;
RULE: MonNum ::= Integer COMPUTE
  MonNum.val = Integer;
END;
~}

The following C module implements two functions wrongDate and 
dayInYear. The latter maps a month number and a day number 
to the number of the day in the year; the former checks whether
the given day number and month number describe an existing day.
Both functions use a formula that assumes that every month has
at least 28 days; the differences to the reals number of days
are accumultaed in the array delta:
~O~<DateChk.head~>==~{
#include "DateChk.h"
~}
~O~<DateChk.h~>==~{
extern int wrongDate (int day, int mon);
extern int dayInYear (int day, int mon);
~}
~O~<DateChk.c~>==~{
#include "DateChk.h"

/* day in year =  day + (month-1)*28 + delta[month-1] */
int delta[] = {0,3,3,6,8,11,13,16,19,21,24,26,29};

int dayInYear (int day, int month)
{
  if (day <= 0 || day > 31 || month <= 0 || month > 12)
    return 1;
  return day + (month-1)*28 + delta[month-1];
}

int wrongDate (int day, int month)
{ 
  if (day <= 0 || day > 31 || month <= 0 || month > 12)
    return 1;

  if (dayInYear (day, month) > month*28 + delta[month])
    return 1;

  return 0;
}
~}

This calendar processor is to produce output in form of an HTML
file. The input data is to be presented as a table that has one 
row per entry. The table has three columns, containing the date,
the time or time span, if any, and the event description.
Here is the output for the correct input file given above:

~O~<cal.html~>~{
<html>
<body>
<h3>Calendar Events</h3>
<table border=1>
  <tr>
    <td><b>Date</b></td><td><b>Time</b></td><td><b>Description</b></td>
  </tr>
  <tr>
    <td>1.11.</td><td>20:00</td><td>Theater</td>
  </tr>
  <tr>
    <td>Thu</td><td>11:15 - 12:45</td><td>GSS lecture</td>
  </tr>
  <tr>
    <td>Werktage - Mon</td><td>12:05</td><td>Dinner in Palmengarten</td>
  </tr>
  <tr>
    <td>Mon, Thu</td><td></td><td>Dean's office</td>
  </tr>
  <tr>
    <td>31.12.</td><td>23:59</td><td>Jahresende</td>
  </tr>
  <tr>
    <td>31.12.</td><td>23:59</td><td>End of year</td>
  </tr>
  <tr>
    <td>25.5.</td><td>11:15</td><td>GSS</td>
  </tr>
  <tr>
    <td>1.5.</td><td></td><td>Labour Day</td>
  </tr>
  <tr>
    <td>1.1.</td><td>12:00 - 12:01</td><td>Flash1</td>
  </tr>
  <tr>
    <td>28.2.</td><td>9:00</td><td>end of Feb</td>
  </tr>
  <tr>
    <td>31.12.</td><td>23:59</td><td>Schluss</td>
  </tr>
</table>
</body>
</html>
~}

The output is produced using PTG functions in computations in tree
contexts. 

In order to make useful prefined PTG patterns, and some
functions on strings available, we use the following components 
of Eli's libraries:

~O~<LibModules.specs~>~{
$/Tech/Strings.specs
$/Output/PtgCommon.fw
~}

TASK: Specify PTG patterns for the intended output.

The following pattern describes the overall structure
of the output file. The rows of the table are inserted
at the only insertion point:

~O~<Frame.ptg~>~{
~}

The following patterns are used to compose rows from
data elements:

~O~<Table.ptg~>~{
~}

The notations of days in a month, time spans, and modifiers
are described by the following patterns:

~O~<DateTime.ptg~>~{
~}

Most symbols of the grammar will have an attribute named code
which will take a PTG node as value. The following ATTR construct
allows to just use such attributes without further specification
of their type.

The attribute Calendar.Code, finally holds the PTG structure 
representing the complete output. The call of PTGOutFile
prints that output to a file, the name of which is formed by
concatenating ".html" to the name of the input file:

~O~<PtgOutput.lido~>~{
ATTR Code: PTGNode;

SYMBOL Calendar COMPUTE
  PTGOutFile (CatStrStr (SRCFILE, ".html"), SYNT.Code);
END;
~}

The following specification is needed to make the name of
the input file available by SRCFILE:

~O~<InputFileName.head~>~{
#include "source.h"
~}

TASK: Specify computations in tree contexts that use the
functions of the specified PTG patterns to solve the following
subtasks:

All table rows are collected and inserted into an instance of the
Frame pattern:

~O~<Rows.lido~>~{
SYMBOL Calendar COMPUTE
  SYNT.Code = PTGNULL; /* to be replaced */
END;
~}

The date is transformed into a data element:

~O~<Dates.lido~>~{
~}

Sequences of DayNames are collected in a single data element.
The name of a day is read as a string from the StringTable:

~O~<DayNames.lido~>~{
~}

Weekly patterns are composed:
~O~<Patterns.lido~>~{
~}

Two data elements are produced for the time or time span
and for the event description:

~O~<Events.lido~>~{
~}

Test your specification comprehensively and use a browser to
check the resulting HTML files.

Generiert mit Camelot | Probleme mit Camelot? | Geändert am: 11.12.2013