1 | /*
|
---|
2 | This example comes from a short article series in the Linux
|
---|
3 | Gazette by Richard A. Sevenich and Christopher Lopes, titled
|
---|
4 | "Compiler Construction Tools". The article series starts at
|
---|
5 |
|
---|
6 | http://www.linuxgazette.com/issue39/sevenich.html
|
---|
7 |
|
---|
8 | Small changes and updates to newest JFlex+Cup versions
|
---|
9 | by Gerwin Klein
|
---|
10 | */
|
---|
11 |
|
---|
12 | /*
|
---|
13 | Commented By: Christopher Lopes
|
---|
14 | File Name: ycalc.cup
|
---|
15 | To Create: > java java_cup.Main < ycalc.cup
|
---|
16 | */
|
---|
17 |
|
---|
18 |
|
---|
19 | /* ----------------------Preliminary Declarations Section--------------------*/
|
---|
20 |
|
---|
21 | /* Import the class java_cup.runtime.* */
|
---|
22 | import java_cup.runtime.*;
|
---|
23 |
|
---|
24 | /* Parser code to change the way the parser reports errors (include
|
---|
25 | line and column number of the error). */
|
---|
26 | parser code {:
|
---|
27 |
|
---|
28 | /* Change the method report_error so it will display the line and
|
---|
29 | column of where the error occurred in the input as well as the
|
---|
30 | reason for the error which is passed into the method in the
|
---|
31 | String 'message'. */
|
---|
32 | public void report_error(String message, Object info) {
|
---|
33 |
|
---|
34 | /* Create a StringBuffer called 'm' with the string 'Error' in it. */
|
---|
35 | StringBuffer m = new StringBuffer("Error");
|
---|
36 |
|
---|
37 | /* Check if the information passed to the method is the same
|
---|
38 | type as the type java_cup.runtime.Symbol. */
|
---|
39 | if (info instanceof java_cup.runtime.Symbol) {
|
---|
40 | /* Declare a java_cup.runtime.Symbol object 's' with the
|
---|
41 | information in the object info that is being typecasted
|
---|
42 | as a java_cup.runtime.Symbol object. */
|
---|
43 | java_cup.runtime.Symbol s = ((java_cup.runtime.Symbol) info);
|
---|
44 |
|
---|
45 | /* Check if the line number in the input is greater or
|
---|
46 | equal to zero. */
|
---|
47 | if (s.left >= 0) {
|
---|
48 | /* Add to the end of the StringBuffer error message
|
---|
49 | the line number of the error in the input. */
|
---|
50 | m.append(" in line "+(s.left+1));
|
---|
51 | /* Check if the column number in the input is greater
|
---|
52 | or equal to zero. */
|
---|
53 | if (s.right >= 0)
|
---|
54 | /* Add to the end of the StringBuffer error message
|
---|
55 | the column number of the error in the input. */
|
---|
56 | m.append(", column "+(s.right+1));
|
---|
57 | }
|
---|
58 | }
|
---|
59 |
|
---|
60 | /* Add to the end of the StringBuffer error message created in
|
---|
61 | this method the message that was passed into this method. */
|
---|
62 | m.append(" : "+message);
|
---|
63 |
|
---|
64 | /* Print the contents of the StringBuffer 'm', which contains
|
---|
65 | an error message, out on a line. */
|
---|
66 | System.err.println(m);
|
---|
67 | }
|
---|
68 |
|
---|
69 | /* Change the method report_fatal_error so when it reports a fatal
|
---|
70 | error it will display the line and column number of where the
|
---|
71 | fatal error occurred in the input as well as the reason for the
|
---|
72 | fatal error which is passed into the method in the object
|
---|
73 | 'message' and then exit.*/
|
---|
74 | public void report_fatal_error(String message, Object info) {
|
---|
75 | report_error(message, info);
|
---|
76 | System.exit(1);
|
---|
77 | }
|
---|
78 | :};
|
---|
79 |
|
---|
80 |
|
---|
81 |
|
---|
82 | /* ------------Declaration of Terminals and Non Terminals Section----------- */
|
---|
83 |
|
---|
84 | /* Terminals (tokens returned by the scanner).
|
---|
85 |
|
---|
86 | Terminals that have no value are listed first and then terminals
|
---|
87 | that do have an value, in this case an integer value, are listed on
|
---|
88 | the next line down. */
|
---|
89 | terminal SEMI, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN;
|
---|
90 | terminal Integer NUMBER, ID;
|
---|
91 |
|
---|
92 | /* Non terminals used in the grammar section.
|
---|
93 |
|
---|
94 | Non terminals that have an object value are listed first and then
|
---|
95 | non terminals that have an integer value are listed. An object
|
---|
96 | value means that it can be any type, it isn't set to a specific
|
---|
97 | type. So it could be an Integer or a String or whatever. */
|
---|
98 | non terminal Object expr_list, expr_part;
|
---|
99 | non terminal Integer expr, factor, term;
|
---|
100 |
|
---|
101 |
|
---|
102 | /* -------------Precedence and Associatively of Terminals Section----------- */
|
---|
103 |
|
---|
104 | /*
|
---|
105 | Precedence of non terminals could be defined here. If you do define
|
---|
106 | precedence here you won't need to worry about precedence in the
|
---|
107 | Grammar Section, i.e. that TIMES should have a higher precedence
|
---|
108 | than PLUS.
|
---|
109 |
|
---|
110 | The precedence defined here would look something like this where the
|
---|
111 | lower line always will have higher precedence than the line before it.
|
---|
112 |
|
---|
113 | precedence left PLUS, MINUS;
|
---|
114 | precedence left TIMES, DIVIDE;
|
---|
115 | */
|
---|
116 |
|
---|
117 |
|
---|
118 | /* ----------------------------Grammar Section-------------------- */
|
---|
119 |
|
---|
120 | /* The grammar for our parser.
|
---|
121 |
|
---|
122 | expr_list ::= expr_list expr_part
|
---|
123 | | expr_part
|
---|
124 | expr_part ::= expr SEMI
|
---|
125 | expr ::= expr PLUS factor
|
---|
126 | | expr MINUS factor
|
---|
127 | | factor
|
---|
128 | factor ::= factor TIMES term
|
---|
129 | | factor DIVIDE term
|
---|
130 | | term
|
---|
131 | term ::= LPAREN expr RPAREN
|
---|
132 | | NUMBER
|
---|
133 | | ID
|
---|
134 | */
|
---|
135 |
|
---|
136 | /* 'expr_list' is the start of our grammar. It can lead to another
|
---|
137 | 'expr_list' followed by an 'expr_part' or it can just lead to an
|
---|
138 | 'expr_part'. The lhs of the non terminals 'expr_list' and
|
---|
139 | 'expr_part' that are in the rhs side of the production below need
|
---|
140 | to be found. Then the rhs sides of those non terminals need to be
|
---|
141 | followed in a similar manner, i.e. if there are any non terminals
|
---|
142 | in the rhs of those productions then the productions with those non
|
---|
143 | terminals need to be found and those rhs's followed. This process
|
---|
144 | keeps continuing until only terminals are found in the rhs of a
|
---|
145 | production. Then we can work our way back up the grammar bringing
|
---|
146 | any values that might have been assigned from a terminal. */
|
---|
147 |
|
---|
148 | expr_list ::= expr_list expr_part
|
---|
149 | |
|
---|
150 | expr_part;
|
---|
151 |
|
---|
152 | /* 'expr_part' is an 'expr' followed by the terminal 'SEMI'. The ':e'
|
---|
153 | after the non terminal 'expr' is a label an is used to access the
|
---|
154 | value of 'expr' which will be an integer. The action for the
|
---|
155 | production lies between {: and :}. This action will print out the
|
---|
156 | line " = + e" where e is the value of 'expr'. Before the action
|
---|
157 | takes places we need to go deeper into the grammar since 'expr' is
|
---|
158 | a non terminal. Whenever a non terminal is encountered on the rhs
|
---|
159 | of a production we need to find the rhs of that non terminal until
|
---|
160 | there are no more non terminals in the rhs. So when we finish
|
---|
161 | going through the grammar and don't encounter any more non
|
---|
162 | terminals in the rhs productions will return until we get back to
|
---|
163 | 'expr' and at that point 'expr' will contain an integer value. */
|
---|
164 |
|
---|
165 | expr_part ::= expr:e
|
---|
166 | {: System.out.println(" = " + e); :}
|
---|
167 | SEMI
|
---|
168 | ;
|
---|
169 |
|
---|
170 | /* 'expr' can lead to 'expr PLUS factor', 'expr MINUS factor', or
|
---|
171 | 'factor'. The 'TIMES' and 'DIVIDE' productions are not at this
|
---|
172 | level. They are at a lower level in the grammar which in affect
|
---|
173 | makes them have higher precedence. Actions for the rhs of the non
|
---|
174 | terminal 'expr' return a value to 'expr'. This value that is
|
---|
175 | created is an integer and gets stored in 'RESULT' in the action.
|
---|
176 | RESULT is the label that is assigned automatically to the rhs, in
|
---|
177 | this case 'expr'. If the rhs is just 'factor' then 'f' refers to
|
---|
178 | the non terminal 'factor'. The value of 'f' is retrieved with the
|
---|
179 | function 'intValue()' and will be stored in 'RESULT'. In the other
|
---|
180 | two cases 'f' and 'e' refers to the non terminals 'factor' and
|
---|
181 | 'expr' respectively with a terminal between them, either 'PLUS' or
|
---|
182 | 'MINUS'. The value of each is retrieved with the same function
|
---|
183 | 'intValue'. The values will be added or subtracted and then the
|
---|
184 | new integer will be stored in 'RESULT'.*/
|
---|
185 |
|
---|
186 | expr ::= expr:e PLUS factor:f
|
---|
187 | {: RESULT = new Integer(e.intValue() + f.intValue()); :}
|
---|
188 | |
|
---|
189 | expr:e MINUS factor:f
|
---|
190 | {: RESULT = new Integer(e.intValue() - f.intValue()); :}
|
---|
191 | |
|
---|
192 | factor:f
|
---|
193 | {: RESULT = new Integer(f.intValue()); :}
|
---|
194 | ;
|
---|
195 |
|
---|
196 | /* 'factor' can lead to 'factor TIMES term', 'factor DIVIDE term', or
|
---|
197 | 'term'. Since the productions for TIMES and DIVIDE are lower in
|
---|
198 | the grammar than 'PLUS' and 'MINUS' they will have higher
|
---|
199 | precedence. The same sort of actions take place in the rhs of
|
---|
200 | 'factor' as in 'expr'. The only difference is the operations that
|
---|
201 | takes place on the values retrieved with 'intValue()', 'TIMES' and
|
---|
202 | 'DIVIDE' here instead of 'PLUS' and 'MINUS'. */
|
---|
203 |
|
---|
204 | factor ::= factor:f TIMES term:t
|
---|
205 | {: RESULT = new Integer(f.intValue() * t.intValue()); :}
|
---|
206 | |
|
---|
207 | factor:f DIVIDE term:t
|
---|
208 | {: RESULT = new Integer(f.intValue() / t.intValue()); :}
|
---|
209 | |
|
---|
210 | term:t
|
---|
211 | {: RESULT = new Integer(t.intValue()); :}
|
---|
212 | ;
|
---|
213 |
|
---|
214 | /* 'term' can lead to 'LPAREN expr RPAREN', 'NUMBER', or 'ID'. The
|
---|
215 | first production has the non terminal 'expr' in it so the
|
---|
216 | production with its lhs side needs to be found and followed. The
|
---|
217 | next rhs has no non terminals. So the grammar ends here and can go
|
---|
218 | back up. When it goes back up it will bring the value that was
|
---|
219 | retrieved when the scanner encounter the token 'NUMBER'. 'RESULT'
|
---|
220 | is assigned 'n', which refers to 'NUMBER', as the action for this
|
---|
221 | production. The same action occurs for 'ID', except the 'i' is
|
---|
222 | used to refer to 'ID'. 'ID' is also the only thing on the rhs of
|
---|
223 | the production. And since 'ID' is a terminal the grammar will end
|
---|
224 | here and go back up. */
|
---|
225 |
|
---|
226 | term ::= LPAREN expr:e RPAREN
|
---|
227 | {: RESULT = e; :}
|
---|
228 | |
|
---|
229 | NUMBER:n
|
---|
230 | {: RESULT = n; :}
|
---|
231 | |
|
---|
232 | ID:i
|
---|
233 | {: RESULT = i; :}
|
---|
234 | ;
|
---|