Generating Software from Specifications WS 2013/14 - File CalendarCheckSol.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, and performs some checks and computations
as required in Assignment 4, Exercise 11.
The following files contain correct input for the processor
~O~<cal.ok~>~{~-
1.11. 20:00 "Theater"
Thu 14:15 "GSS lecture"
1.5. 9:15 "Meeting"
Weekday 12:05 "Dinner in Palmengarten"
Mon, Thu "Dean's office"
31.12. 23:59 "Jahresende"
12/31 23:59 "End of year"
May 1 "Labour Day"
Weekday+Sat "Work"
-- end of file
~}
~O~<cal1.ok~>~{~-
Thu 10:00 - 10:15 "Have a break"
~}
The following files contain erroneous input for the processor
~O~<cal1.err~>~{~-
1.11 20:00 "A"
1.11. 24:01 "B"
1.11. 20:00 "aa
thu 20:00 "A"
33.12. 20:00 "A"
~}
~O~<cal2.err~>~{~-
12.13. 20:00 "A"
12/33 20:00 "A"
13/12 20:00 "A"
Mai 1 "Labour Day"
-
~}
~O~<cal3.err~>~{~-
31.4. 9:00 "not in April"
1.13. 9:00 "not in any year"
~}
The following file specifies the concrete syntax:
~O~<Calendar.con~>~{
Calendar: Entries.
Entries: Entries Entry / Entry.
Entry: DateDescr When Description.
DateDescr: Date / DayNames / GeneralPattern.
Date: DayNum '.' MonNum '.' /
MonNum '/' DayNum /
Month DayNum.
DayNum: Integer.
MonNum: Integer.
DayNames: DayNames ',' DayName / DayName.
DayName: Day.
GeneralPattern: SimplePattern Modifier / SimplePattern.
SimplePattern: 'Weekday' / 'Weekend'.
Modifier: '+' DayNames / '-' DayNames.
When: Time / Time '-' Time / .
~}
The non-literal tokens are specified as follows:
~O~<Calendar.gla~>~{
Description: C_STRING_LIT
Integer: PASCAL_INTEGER
Day: $Mon|Tue|Wed|Thu|Fri|Sat|Sun [mkDay]
Time: $(([0-9]|1[0-9]|2[0-3]):[0-5][0-9]) [mkTime]
ADA_COMMENT
Month: $Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec [mkMonth]
~}
A mapping specifications as described in the lecture material:
~O~<CalendarChecks.map~>~{
MAPSYM
Pattern ::= GeneralPattern SimplePattern.
MAPRULE
Date: DayNum '.' MonNum '.' < $1 $2 >.
Date: MonNum '/' DayNum < $2 $1 >.
~}
The target rule of the rule mapping:
~O~<MapTarget.lido~>~{
RULE: Date ::= DayNum MonNum END;
~}
The correctness of time spans is checked.
~O~<CalendarChecks1.lido~>~{
RULE: When ::= Time '-' Time COMPUTE
IF (LE (Time[2], Time[1]),
message (ERROR, "wrong time span", 0, COORDREF));
END;
~}
Attributes <code>DayNum.val</code> and <code>MonNum.val</code> are
introduced to propagate the values of the
terminal <code>Integer</code> into a context where they can be compared:
~O~<CalendarChecks2.lido~>~{
ATTR val:int;
RULE: DayNum ::= Integer COMPUTE DayNum.val = Integer; END;
RULE: MonNum ::= Integer COMPUTE MonNum.val = Integer; END;
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;
~}
INSERT SOLUTIONS OF ASSIGNMENT 4 HERE:
Use a single FunnelWeb macro for each subtask.
Subtask 1:
Store and print the number of days in the year:
THe computation has to be inserted into two RULE contexts:
~O~<CalCheck-1.nolido~>~{
SYMBOL Date: dayNum: int;
RULE: Date ::= DayNum MonNum COMPUTE
Date.dayNum = dayInYear (DayNum.val, MonNum.val);
printf ("Day number %d in line %d\n", Date.dayNum, LINE);
END;
RULE: Date ::= Month DayNum COMPUTE
Date.dayNum = dayInYear (DayNum.val, Month);
printf ("Day number %d in line %d\n", Date.dayNum, LINE);
END;
~}
The output for cal.ok is
~O~<cal.out1~>~{
Day number 306 in line 1
Day number 122 in line 3
Day number 366 in line 6
Day number 366 in line 7
Day number 122 in line 8
~}
Subtask 2:
Compute the minimal date that occurs in the input,
by explicit bottom-up propagation:
The suggested macro definitions for MIN and I400 are
inserted into Calendar.h further down.
A attribute name minDate is introduced.
It is associated to each nonterminal from which Date can be derived, i.e.
DateDescr, Entry, Entries.
Each rule with one of these nonterminals on the left-hand side
that attribute is computed.
2 of the 3 alternative rules for DateDescr do NOT derive Date;
there the attribute is set to a big default value, calling I400.
The minimum day is printed in the Calendar context:
~O~<CalCheck-2.nolido~>~{
ATTR minDate: int;
RULE: DateDescr ::= Date COMPUTE
DateDescr.minDate = Date.dayNum;
END;
RULE: DateDescr ::= DayNames COMPUTE
DateDescr.minDate = I400(); /* big number indicates no day in year */
END;
RULE: DateDescr ::= Pattern COMPUTE
DateDescr.minDate = I400(); /* big number indicates no day in year */
END;
RULE: Entry ::= DateDescr When Description COMPUTE
Entry.minDate = DateDescr.minDate;
END;
RULE: Entries ::= Entry COMPUTE
Entries.minDate = Entry.minDate;
END;
RULE: Entries ::= Entries Entry COMPUTE
Entries[1].minDate = MIN (Entries[2].minDate, Entry.minDate);
END;
RULE: Calendar ::= Entries COMPUTE
IF (EQ (Entries.minDate, I400()),
printf ("There is no concrete day in the list.\n"),
printf ("The earliest day in the list is %d\n",
Entries.minDate));
END;
~}
The output for cal.ok is
~O~<cal.out2~>~{
Day number 306 in line 1
Day number 122 in line 3
Day number 366 in line 6
Day number 366 in line 7
Day number 122 in line 8
The earliest day in the list is 122
~}
Subtask 3:
Deactivate the solution of subtask 2.
Use a CONSTITUENTS-WITH construct to compute the earliest date
~O~<CalCheck-3.nolido~>~{~-
ATTR minDate: int;
RULE: Calendar ::= Entries COMPUTE
Calendar.minDate = CONSTITUENTS Date.dayNum
WITH (int, MIN, IDENTICAL, I400);
IF (EQ (Calendar.minDate, I400()),
printf ("There is no concrete day in the list.\n"),
printf ("The earliest day in the list is %d\n",
Calendar.minDate));
END;
~}
The output for cal.ok is the same as for subtask 2.
Subtask 4:
Propagate the minimal date down to each Date and output it there:
~O~<CalCheck-4.nolido~>~{
ATTR earliestDate: int;
RULE: Calendar ::= Entries COMPUTE
Entries.earliestDate = Calendar.minDate;
END;
RULE: Entries ::= Entries Entry COMPUTE
Entries[2].earliestDate = Entries[1].earliestDate;
Entry.earliestDate = Entries[1].earliestDate;
END;
RULE: Entries ::= Entry COMPUTE
Entry.earliestDate = Entries[1].earliestDate;
END;
RULE: Entry ::= DateDescr When Description COMPUTE
DateDescr.earliestDate = Entry.earliestDate;
END;
RULE: DateDescr ::= Date COMPUTE
Date.earliestDate = DateDescr.earliestDate;
END;
RULE: Date ::= DayNum MonNum COMPUTE
IF (EQ (Date.dayNum, Date.earliestDate),
printf ("Ealiest day %d in line %d\n", Date.dayNum, LINE));
END;
RULE: Date ::= Month DayNum COMPUTE
IF (EQ (Date.dayNum, Date.earliestDate),
printf ("Ealiest day %d in line %d\n", Date.dayNum, LINE));
END;
~}
The output for cal.ok is
~O~<cal.out4~>~{
Day number 306 in line 1
Day number 122 in line 3
Day number 366 in line 6
Day number 366 in line 7
Day number 122 in line 8
The earliest day in the list is 122
Ealiest day 122 in line 3
Ealiest day 122 in line 8
~}
Subtask 5:
Deactivate the solution of subtask 4.
Propagate the minimal date down to every Date using an
INCLUDING construct, and output it there:
The print computation has to be specified in both rules for Date:
~O~<CalCheck-5.nolido~>~{
RULE: Date ::= DayNum MonNum COMPUTE
IF (EQ (Date.dayNum, INCLUDING Calendar.minDate),
printf ("Ealiest day %d in line %d\n", Date.dayNum, LINE));
END;
RULE: Date ::= Month DayNum COMPUTE
IF (EQ (Date.dayNum, INCLUDING Calendar.minDate),
printf ("Ealiest day %d in line %d\n", Date.dayNum, LINE));
END;
~}
The output for cal.ok is the same as for subtask 4.
Subtask 6:
Deactivate the solutions of subtasks 1 to 5.
Use Symbol computations wherever possible.
~O~<CalCheck-6.lido~>~{
SYMBOL Date: dayNum: int;
RULE: Date ::= DayNum MonNum COMPUTE
Date.dayNum = dayInYear (DayNum.val, MonNum.val);
END;
RULE: Date ::= Month DayNum COMPUTE
Date.dayNum = dayInYear (DayNum.val, Month);
END;
SYMBOL Date COMPUTE
printf ("Day number %d in line %d\n", THIS.dayNum, LINE);
END;
ATTR minDate: int;
SYMBOL Calendar COMPUTE
SYNT.minDate = CONSTITUENTS Date.dayNum
WITH (int, MIN, IDENTICAL, I400);
IF (EQ (SYNT.minDate, I400()),
printf ("There is no concrete day in the list.\n"),
printf ("The earliest day in the list is %d\n",
SYNT.minDate));
END;
SYMBOL Date COMPUTE
IF (EQ (THIS.dayNum, INCLUDING Calendar.minDate),
printf ("Ealiest day %d in line %d\n", THIS.dayNum, LINE));
END;
~}
The output for cal.ok is the same as for subtask 5.
~O~<Calendar.head~>==~{
#include "csm.h"
#include "Calendar.h"
~}
~O~<Calendar.h~>==~{
extern void mkDay (char *, int, int *, int*);
extern void mkTime (char *, int, int *, int*);
extern void mkMonth (char *, int, int *, int*);
extern int dayInYear (int day, int mon);
extern int wrongDate (int day, int mon);
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#define I400() (400)
#endif
~}
~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 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;
}
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;
}
}
/* day in year = day + (month-1)*28 + delta[month-1]
Delta values for a
non-leap year:
int delta[] = {0,3,3,6,8,11,13,16,19,21,24,26,29};
leap year:
int delta[] = {0,3,4,7,9,12,14,17,20,22,25,27,30};
*/
int delta[] = {0,3,4,7,9,12,14,17,20,22,25,27,30};
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;
}
~}
Generiert mit Camelot | Probleme mit Camelot? | Geändert am: 24.11.2013


