comparison examples/rcalc/rcalc.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 * AnaGram, a System for Syntax Directed Parsing
3 * RCALC.SYN: A Roman Numeral Calculator Example
4 *
5 * Copyright MCMXCIII, MCMXCVI, Parsifal Software. All Rights Reserved.
6 * Copyright MMVII David A. Holland. All Rights Reserved.
7 *
8 * This software is provided 'as-is', without any express or implied
9 * warranty. In no event will the authors be held liable for any damages
10 * arising from the use of this software.
11 *
12 * Permission is granted to anyone to use this software for any purpose,
13 * including commercial applications, and to alter it and redistribute it
14 * freely, subject to the following restrictions:
15 *
16 * 1. The origin of this software must not be misrepresented; you must not
17 * claim that you wrote the original software. If you use this software
18 * in a product, an acknowledgment in the product documentation would be
19 * appreciated but is not required.
20 * 2. Altered source versions must be plainly marked as such, and must not be
21 * misrepresented as being the original software.
22 * 3. This notice may not be removed or altered from any source distribution.
23 */}
24
25 c = 'c'
26 d = 'd'
27 i = 'i'
28 l = 'l'
29 m = 'm'
30 v = 'v'
31 x = 'x'
32
33 eof = 0 + '\n' // Input is a one-line string
34
35 ws = ' ' + '\t' + '\r' // Blanks, tabs, and stray CRs
36
37 [
38 ~allow macros // Simplify debugging
39 disregard ws // Ignore blanks and tabs
40 lexeme { roman numeral} // No blanks inside a number
41 ~case sensitive // Allow upper/lower input
42 context type = int // Track context
43 default token type = long // All arithmetic uses longs
44 pointer input // Input is array in memory
45 ]
46
47
48 // Grammar definition
49
50 (void) calculation $
51 -> expression:x, eof =print_roman(x);
52
53
54 // Expression logic
55
56 expression
57 -> term // Value of expression is value of term
58 -> expression:x, '+', term:y =x+y;
59 -> expression:x, '-', term:y =x-y;
60
61 term
62 -> factor // Value of term is value of factor
63 -> term:x, '*', factor:y =x*y;
64 -> term:x, '/', factor:y =y ? x/y : divide_error(RULE_CONTEXT[2]);
65
66 factor
67 -> roman numeral // Value of factor is value of roman numeral
68 -> "nihil" =0;
69 -> '(', expression:x, ')' =x;
70 -> '-', factor:x =-x;
71
72
73 // Roman Numeral Syntax
74
75 roman numeral
76 -> thousands:x, hundreds?:y =x+y;
77 -> hundreds // Value of roman numeral is value of hundreds
78
79 thousands
80 -> m =1000;
81 -> thousands:x, m =x+1000;
82
83 hundreds
84 -> hundreds field:x, tens?:y =x+y;
85 -> tens // Value of hundreds is value of tens
86
87 hundreds field
88 -> c, m =900;
89 -> c, d =400;
90 -> count hundreds
91
92 count hundreds
93 -> c =100;
94 -> d =500;
95 -> count hundreds:x, c =x+100;
96
97 tens
98 -> tens field:x, units?:y =x+y;
99 -> units // Value of tens is value of units
100
101 tens field
102 -> x, m =990;
103 -> x, d =490;
104 -> x, c =90;
105 -> x, l =40;
106 -> count tens
107
108 count tens
109 -> x =10;
110 -> l =50;
111 -> count tens:x, x =x+10;
112
113 units
114 -> i, m =999;
115 -> i, d =499;
116 -> i, c =99;
117 -> i, l =49;
118 -> i, x =9;
119 -> i, v =4;
120 -> count units // Value of "units" is value of "count units"
121
122 count units
123 -> i =1;
124 -> v =5;
125 -> count units:x, i =x+1;
126
127
128 { /* Embedded C */
129 #include <stdio.h>
130
131
132 /* Macro Definitions */
133
134 #define GET_CONTEXT CONTEXT = PCB.column
135 #define SYNTAX_ERROR syntax_error()
136
137 int semantic_error = 0; /* Divide by zero flag */
138
139
140 /*
141
142 syntax_error() positions a '^' character under the input line at the
143 point where the syntax error was discovered and then writes the error
144 message.
145
146 */
147
148 void syntax_error(void) {
149 int k = PCB.column;
150 while (k-- > 0) putchar(' ');
151 printf("^? %s\n","ERRARE HUMANUM EST\n");
152 }
153
154
155 /*
156
157 divide_error() is called when an attempt is made to divide by zero. The
158 entire divisor is marked with '^' characters. "semantic_error" is set
159 in order to disable printing of a result.
160
161 */
162
163 int divide_error(int cn) {
164 int k = PCB.column - cn;
165 while (cn--) putchar(' ');
166 while (k--) putchar('^');
167 puts(" DIVISOR NIHIL EST");
168 semantic_error = 1;
169 return 0;
170 }
171
172
173 /*
174
175 print_roman() prints a signed integer in upper case Roman numerals.
176
177 */
178
179 void print_roman(long k) {
180 if (semantic_error) {
181 semantic_error = 0;
182 return;
183 }
184 printf(" = ");
185 if (k == 0) {printf("NIHIL\n"); return;}
186
187 if (k < 0) putchar('-'), k=-k;
188
189 while (k >= 1000) putchar('M'), k-=1000;
190
191 if (k >= 900) printf("CM"), k-=900;
192 if (k >= 500) putchar('D'), k-=500;
193 if (k >= 400) printf("CD"), k-=400;
194
195 while (k >= 100) putchar('C'), k-=100;
196
197 if (k >= 90) printf("XC"), k-=90;
198 if (k >= 50) putchar('L'), k-=50;
199 if (k >= 40) printf("XL"), k-=40;
200
201 while (k >= 10) putchar('X'), k-=10;
202
203 if (k >= 9) printf("IX"), k -= 9;
204 if (k >= 5) putchar('V'), k-=5;
205 if (k >= 4) printf("IV"), k-=4;
206
207 while (k >= 1) putchar('I'), k--;
208
209 putchar('\n');
210 }
211
212
213 /* Main Program -- reads a line from stdin and calls parser */
214
215 int main(void) {
216 char line[1024];
217 while (1) {
218 printf("#");
219 fflush(stdout);
220 if (fgets(line, sizeof(line), stdin) == NULL) break;
221 rcalc_pcb.pointer = (unsigned char *) line;
222 rcalc();
223 }
224 return 0;
225 }
226
227 } // End of Embedded C