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