comparison tests/agcl/parsifal/date_p1.syn @ 0:13d2b8934445

Import AnaGram (near-)release tree into Mercurial.
author David A. Holland
date Sat, 22 Dec 2007 17:52:45 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:13d2b8934445
1 {/*
2 Date Translator
3 Copyright (c) 1995-1999, Parsifal Software
4 All Rights Reserved
5 See the file COPYING for license and usage terms.
6
7 This program illustrates the use of AnaGram to translate a number of
8 different representations of data into a common format. The example
9 used shows how to translate any of a number of standard formats for
10 entering a date into a single format for further processing.
11
12 The parser illustrated here recognizes six basic date formats, and
13 for each, will supply the current year if the year is not given. If
14 months are entered by name, they may be spelled out in full, or
15 abbreviated in the customary manner. Text may be upper or lower case.
16 Spaces or tabs may be used freely between the elements of the dates.
17 Some examples of the six formats, as applied to the date June 26,
18 1999, are as follows:
19
20 June 26 jun 26 June 26, 1999 JUN 26, 99 jun26,1999
21 26 June 26 jun 26 June 1999 26 JUN 99 26jun99
22 26-June 26-Jun 26-June-99 26-JUN-1999
23 6/26 6/26/99 6/26/1999
24 6-26 6-26-99 6-26-1999
25 6.26 6.26.99 6-26.1999
26 6/26/'99 6-26-'99 6.26.'99
27 6/26 '99 6-26 '99 6.26 '99
28 26/6 26/6/99 26/6/1999
29 26-6 26-6-99 26-6-1999
30 26.6 26.6.99 26.6.1999
31 26/6 '99 26-6 '99 26.6 '99
32 26 vi 26 vi 99 26 vi 1999 26 VI 99 26VI99
33 26 vi '99
34
35 If CHKDATE encounters a date of the form 2/3/99, it interrogates a
36 switch to determine whether to interpret this in the European manner
37 (March 2, 1999) or the American manner (February 3, 1999). Where the
38 form is obvious, as in 6/26/98 or 26/6/98 it ignores the switch.
39
40 CHKDATE also recognizes dates consisting of a month and year only.
41 Where month and year cannot be distinguished from month and day,
42 CHKDATE will assume month and day. When the year is given as a two
43 digit number, 0 to 49 are assumed to refer to the coming 21st century
44 and 50-99 are 20th century dates. To force recognition as month and
45 year, use an apostrophe or use more than 2 digits for the year: Aug
46 14 is the 14th of the month, Aug '14 is August 2014. For the
47 beginning of WWI, use Aug 1914
48
49 CHKDATE operates on a string in memory and stores the month, day and
50 year in the variables mon, day, and yr respectively.
51
52 checkDate() sets up the input pointer for CHKDATE and calls it.
53 checkDate() then checks for error and adds 2000 to the year if the
54 year specified was less than 50, otherwise it adds 1900 to the year
55 if the year specified was less than 100. It returns non-zero in case
56 of error and zero otherwise.
57
58 main() simply reads a string from stdin, and passes it to
59 checkDate(). If there is no error, it prints the date in a standard
60 format and loops forever.
61
62 */}
63
64 [
65 pointer input // input string in memory
66 ~case sensitive // ignore case
67 disregard white space // skip blanks and tabs
68 lexeme {month, roman, number} // except inside names and numbers
69 ~diagnose errors // diagnostics not necessary
70 parser name = chkdate
71 ]
72
73
74 eof = 0 // standard asciz string terminator
75 white space = ' ' + '\t' // blanks and tabs
76 punctuation = '-' + '/' + '.'
77 letter = 'a-z'
78
79 i = 'i'
80 v = 'v'
81 x = 'x'
82
83 date string $
84 -> date, eof
85
86 date
87 // Feb 23, Feb 98
88 -> month:m, number:d ={
89 if (d > days[m] || d==0) day=0, yr=d; // (change to accommodate d = 0, made Dec. 11/99)
90 else day=d, mon = m, yr = thisYear;
91 }
92
93 // Feb '23
94 -> month:m, '\'', number:y =day = 0, mon = m, yr = y;
95 -> month:m, '/', number:y =day = 0, mon = m, yr = y; // Feb/99 (change made Dec. 11/99)
96
97 // Feb 17, 98
98 -> month:m, number:d, ',', number:y =day = d, mon = m, yr = y;
99 -> month:m, number:d, '/', number:y =day = d, mon = m, yr = y; // Feb 17/99 (change made Dec. 11/99)
100
101 // 18 Aug
102 -> number:d, month =day = d, yr = thisYear;
103
104 // 18-Aug
105 -> number:d, punct, month =day = d, yr = thisYear;
106
107 // 18 aug '95
108 -> number:d, month, '\''?, number:y =day = d, yr = y;
109 -> number:d, month, '/'?, number:y =day = d, yr = y; // 18 feb /95 (change made Dec. 11/99)
110
111
112 // 18-aug 75 or //18-aug 1923
113 -> number:d, punct, month,
114 matchPunctuation, '\''?, number:y =day = d, yr = y;
115
116 // 6-11
117 -> number:m, punct, number:d =monthDay(m,d);
118
119 // 6 '99 or 6-'99
120 -> number:m, punct?, '\'', number:y =day = 0, mon=m, yr = y;
121
122 // 6-6-44 or 6-6-'44
123 -> number:m, punct, number:d,
124 matchPunctuation, '\''?, number:y =monthDayYear(m,d,y);
125
126 // 6-11'44
127 -> number:m, punct, number:d,
128 '\'', number:y =monthDayYear(m,d,y);
129
130 // 6 ix
131 -> number:d, roman:m =mon = m, day = d, yr = thisYear;
132
133 // 6 ix 94
134 -> number:d, roman:m, number:y =mon = m, day = d, yr = y;
135
136 // vi 44 or vi '44
137 -> roman:m, '\''?, number:y =day = 0, mon = m, yr = y;
138
139 (int) month
140 month
141 -> "jan", letter?... =1;
142 -> "feb", letter?... =2;
143 -> "mar", letter?... =3;
144 -> "apr", letter?... =4;
145 -> "may" =5;
146 -> "jun", letter?... =6;
147 -> "jul", letter?... =7;
148 -> "aug", letter?... =8;
149 -> "sep", letter?... =9;
150 -> "oct", letter?... =10;
151 -> "nov", letter?... =11;
152 -> "dec", letter?... =12;
153
154
155 punct
156 -> punctuation: c =matchChar = c;
157
158 matchPunctuation
159 -> punctuation:c ={
160 if (matchChar != c) PCB.exit_flag = AG_SYNTAX_ERROR_CODE;
161 }
162
163 (int) number
164 -> '0-9':d =d-'0';
165 -> number:n, '0-9':d =10*n + d-'0';
166
167
168 (int) roman
169 -> i, x =9;
170 -> i, v =4;
171 -> units
172
173
174 (int) units
175 -> i =1;
176 -> v =5;
177 -> x =10;
178 -> units:x, i =x+1;
179
180 {
181
182 #include <time.h>
183
184 #define SYNTAX_ERROR
185
186 int days[13] = {0,31,29,31,30,31,30,31,31,30,31,30,31};
187 char *monthName[13] = {NULL, "January", "February", "March", "April",
188 "May", "June", "July", "August", "September", "October",
189 "November", "December"};
190 int mon = 0, day = 0, yr = 0;
191 int thisYear;
192 int european = 0;
193 int matchChar = 0;
194
195 void monthDay(int m, int d) {
196 if (m <= 12 && d > days[m]) day=0, mon = m, yr = d;
197 else if (m > 12 || european ) day = m, mon =d , yr = thisYear;
198 else mon=m, day=d, yr=thisYear;
199 }
200
201 void monthDayYear(int m, int d, int y) {
202 if (m > 12 || european) day = m, mon = d;
203 else mon = m, day = d;
204 yr = y;
205 }
206
207 int checkDate(char *input) {
208 PCB.pointer = (unsigned char *) input;
209 chkdate();
210 if (PCB.exit_flag != AG_SUCCESS_CODE) return 1; /* fail on error */
211 if (mon > 12) return 1;
212 if (day > days[mon]) return 1;
213 if (yr < 50) yr += 2000;
214 else if (yr < 100 ) yr += 1900;
215 return 0;
216 }
217
218 int main(int argc, char *argv[]) {
219 char input[82];
220 time_t timeOfDay;
221 int k;
222
223 for (k = 1; k < argc; k++) {
224 switch (*argv[k]++) {
225 case '/':
226 case '-':
227 if (*argv[k] == 'e') european = 1;
228 break;
229 }
230 }
231 /* Determine current year */
232
233 timeOfDay = time(NULL);
234 thisYear = localtime(&timeOfDay)->tm_year;
235
236 /* Loop forever, reading input strings and converting them */
237 while (1) {
238 gets(input);
239 if (feof(stdin)) break;
240 if (checkDate(input)) printf("%-30s Bad date\n", input);
241 else if (day) printf("%-30s %s %d, %d\n", input, monthName[mon], day, yr);
242 else printf("%-30s %s %d\n", input, monthName[mon], yr);
243 }
244 return 0;
245 }
246 }