1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #include "Base.hxx" |
19 | #include "DebuggerExpressions.hxx" |
20 | |
21 | #include "YaccParser.hxx" |
22 | |
23 | namespace YaccParser { |
24 | #include <cstdio> |
25 | #include <cctype> |
26 | |
27 | #include "y.tab.h" |
28 | static YYSTYPE result; |
29 | static string errMsg; |
30 | |
31 | void yyerror(const char* e); |
32 | |
33 | #ifdef __clang__ |
34 | #pragma clang diagnostic push |
35 | #pragma clang diagnostic ignored "-Wold-style-cast" |
36 | #pragma clang diagnostic ignored "-Wimplicit-fallthrough" |
37 | #pragma clang diagnostic ignored "-Wmissing-variable-declarations" |
38 | #include "y.tab.c" |
39 | #pragma clang diagnostic pop |
40 | #else |
41 | #include "y.tab.c" |
42 | #endif |
43 | |
44 | enum class State { |
45 | DEFAULT, |
46 | IDENTIFIER, |
47 | OPERATOR, |
48 | SPACE |
49 | }; |
50 | |
51 | static State state = State::DEFAULT; |
52 | static const char *input, *c; |
53 | |
54 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
55 | const string& errorMessage() |
56 | { |
57 | return errMsg; |
58 | } |
59 | |
60 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
61 | Expression* getResult() |
62 | { |
63 | lastExp = nullptr; |
64 | return result.exp; |
65 | } |
66 | |
67 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
68 | void setInput(const string& in) |
69 | { |
70 | input = c = in.c_str(); |
71 | state = State::DEFAULT; |
72 | } |
73 | |
74 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
75 | int parse(const string& in) |
76 | { |
77 | lastExp = nullptr; |
78 | errMsg = "(no error)" ; |
79 | setInput(in); |
80 | return yyparse(); |
81 | } |
82 | |
83 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
84 | // hand-rolled lexer. Hopefully faster than flex... |
85 | inline bool is_base_prefix(char x) |
86 | { |
87 | return ( (x=='\\' || x=='$' || x=='#') ); |
88 | } |
89 | |
90 | inline bool is_identifier(char x) |
91 | { |
92 | return ( (x>='0' && x<='9') || |
93 | (x>='a' && x<='z') || |
94 | (x>='A' && x<='Z') || |
95 | x=='.' || x=='_' ); |
96 | } |
97 | |
98 | inline bool is_operator(char x) |
99 | { |
100 | return ( (x=='+' || x=='-' || x=='*' || |
101 | x=='/' || x=='<' || x=='>' || |
102 | x=='|' || x=='&' || x=='^' || |
103 | x=='!' || x=='~' || x=='(' || |
104 | x==')' || x=='=' || x=='%' || |
105 | x=='[' || x==']' ) ); |
106 | } |
107 | |
108 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
109 | // const_to_int converts a string into a number, in either the |
110 | // current base, or (if there's a base override) the selected base. |
111 | // Returns -1 on error, since negative numbers are the parser's |
112 | // responsibility, not the lexer's |
113 | int const_to_int(char* ch) |
114 | { |
115 | // what base is the input in? |
116 | Common::Base::Format format = Common::Base::format(); |
117 | |
118 | switch(*ch) { |
119 | case '\\': |
120 | format = Common::Base::F_2; |
121 | ch++; |
122 | break; |
123 | |
124 | case '#': |
125 | format = Common::Base::F_10; |
126 | ch++; |
127 | break; |
128 | |
129 | case '$': |
130 | format = Common::Base::F_16; |
131 | ch++; |
132 | break; |
133 | |
134 | default: // not a base_prefix, use default base |
135 | break; |
136 | } |
137 | |
138 | int ret = 0; |
139 | switch(format) { |
140 | case Common::Base::F_2: |
141 | while(*ch) { |
142 | if(*ch != '0' && *ch != '1') |
143 | return -1; |
144 | ret *= 2; |
145 | ret += (*ch - '0'); |
146 | ch++; |
147 | } |
148 | return ret; |
149 | |
150 | case Common::Base::F_10: |
151 | while(*ch) { |
152 | if(!isdigit(*ch)) |
153 | return -1; |
154 | ret *= 10; |
155 | ret += (*ch - '0'); |
156 | ch++; |
157 | } |
158 | return ret; |
159 | |
160 | case Common::Base::F_16: |
161 | while(*ch) { // FIXME: error check! |
162 | if(!isxdigit(*ch)) |
163 | return -1; |
164 | int dig = (*ch - '0'); |
165 | if(dig > 9) dig = tolower(*ch) - 'a' + 10; |
166 | ret *= 16; |
167 | ret += dig; |
168 | ch++; |
169 | } |
170 | return ret; |
171 | |
172 | default: |
173 | cerr << "INVALID BASE in lexer!" << endl; |
174 | return 0; |
175 | } |
176 | } |
177 | |
178 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
179 | // special methods that get Cart RAM/ROM internal state |
180 | CartMethod getCartSpecial(char* ch) |
181 | { |
182 | if(BSPF::equalsIgnoreCase(ch, "_bank" )) |
183 | return &CartDebug::getPCBank; |
184 | else if(BSPF::equalsIgnoreCase(ch, "__lastread" )) |
185 | return &CartDebug::lastReadBaseAddress; |
186 | else if(BSPF::equalsIgnoreCase(ch, "__lastwrite" )) |
187 | return &CartDebug::lastWriteBaseAddress; |
188 | else |
189 | return nullptr; |
190 | } |
191 | |
192 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
193 | // special methods that get e.g. CPU registers |
194 | CpuMethod getCpuSpecial(char* ch) |
195 | { |
196 | if(BSPF::equalsIgnoreCase(ch, "a" )) |
197 | return &CpuDebug::a; |
198 | else if(BSPF::equalsIgnoreCase(ch, "x" )) |
199 | return &CpuDebug::x; |
200 | else if(BSPF::equalsIgnoreCase(ch, "y" )) |
201 | return &CpuDebug::y; |
202 | else if(BSPF::equalsIgnoreCase(ch, "pc" )) |
203 | return &CpuDebug::pc; |
204 | else if(BSPF::equalsIgnoreCase(ch, "sp" )) |
205 | return &CpuDebug::sp; |
206 | else if(BSPF::equalsIgnoreCase(ch, "c" )) |
207 | return &CpuDebug::c; |
208 | else if(BSPF::equalsIgnoreCase(ch, "z" )) |
209 | return &CpuDebug::z; |
210 | else if(BSPF::equalsIgnoreCase(ch, "n" )) |
211 | return &CpuDebug::n; |
212 | else if(BSPF::equalsIgnoreCase(ch, "v" )) |
213 | return &CpuDebug::v; |
214 | else if(BSPF::equalsIgnoreCase(ch, "d" )) |
215 | return &CpuDebug::d; |
216 | else if(BSPF::equalsIgnoreCase(ch, "i" )) |
217 | return &CpuDebug::i; |
218 | else if(BSPF::equalsIgnoreCase(ch, "b" )) |
219 | return &CpuDebug::b; |
220 | else if(BSPF::equalsIgnoreCase(ch, "_icycles" )) |
221 | return &CpuDebug::icycles; |
222 | else |
223 | return nullptr; |
224 | } |
225 | |
226 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
227 | // special methods that get TIA internal state |
228 | TiaMethod getTiaSpecial(char* ch) |
229 | { |
230 | if(BSPF::equalsIgnoreCase(ch, "_scan" )) |
231 | return &TIADebug::scanlines; |
232 | if(BSPF::equalsIgnoreCase(ch, "_scycles" )) |
233 | return &TIADebug::cyclesThisLine; |
234 | else if(BSPF::equalsIgnoreCase(ch, "_fcount" )) |
235 | return &TIADebug::frameCount; |
236 | else if(BSPF::equalsIgnoreCase(ch, "_fcycles" )) |
237 | return &TIADebug::frameCycles; |
238 | else if(BSPF::equalsIgnoreCase(ch, "_cyclesLo" )) |
239 | return &TIADebug::cyclesLo; |
240 | else if(BSPF::equalsIgnoreCase(ch, "_cyclesHi" )) |
241 | return &TIADebug::cyclesHi; |
242 | else if(BSPF::equalsIgnoreCase(ch, "_cclocks" )) |
243 | return &TIADebug::clocksThisLine; |
244 | else if(BSPF::equalsIgnoreCase(ch, "_vsync" )) |
245 | return &TIADebug::vsyncAsInt; |
246 | else if(BSPF::equalsIgnoreCase(ch, "_vblank" )) |
247 | return &TIADebug::vblankAsInt; |
248 | else |
249 | return nullptr; |
250 | } |
251 | |
252 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
253 | int yylex() { |
254 | static char idbuf[255]; |
255 | char o, p; |
256 | yylval.val = 0; |
257 | while(*c != '\0') { |
258 | switch(state) { |
259 | case State::SPACE: |
260 | yylval.val = 0; |
261 | if(isspace(*c)) { |
262 | c++; |
263 | } else if(is_identifier(*c) || is_base_prefix(*c)) { |
264 | state = State::IDENTIFIER; |
265 | } else if(is_operator(*c)) { |
266 | state = State::OPERATOR; |
267 | } else { |
268 | state = State::DEFAULT; |
269 | } |
270 | |
271 | break; |
272 | |
273 | case State::IDENTIFIER: |
274 | { |
275 | CartMethod cartMeth; |
276 | CpuMethod cpuMeth; |
277 | TiaMethod tiaMeth; |
278 | |
279 | char *bufp = idbuf; |
280 | *bufp++ = *c++; // might be a base prefix |
281 | while(is_identifier(*c)) { // may NOT be base prefixes |
282 | *bufp++ = *c++; |
283 | } |
284 | *bufp = '\0'; |
285 | state = State::DEFAULT; |
286 | |
287 | // Note: specials (like "a" for accumulator) have priority over |
288 | // numbers. So "a" always means accumulator, not hex 0xa. User |
289 | // is welcome to use a base prefix ("$a"), or a capital "A", |
290 | // to mean 0xa. |
291 | |
292 | // Also, labels have priority over specials, so Bad Things will |
293 | // happen if the user defines a label that matches one of |
294 | // the specials. Who would do that, though? |
295 | |
296 | if(Debugger::debugger().cartDebug().getAddress(idbuf) > -1) { |
297 | yylval.Equate = idbuf; |
298 | return EQUATE; |
299 | } else if( (cpuMeth = getCpuSpecial(idbuf)) ) { |
300 | yylval.cpuMethod = cpuMeth; |
301 | return CPU_METHOD; |
302 | } else if( (cartMeth = getCartSpecial(idbuf)) ) { |
303 | yylval.cartMethod = cartMeth; |
304 | return CART_METHOD; |
305 | } else if( (tiaMeth = getTiaSpecial(idbuf)) ) { |
306 | yylval.tiaMethod = tiaMeth; |
307 | return TIA_METHOD; |
308 | } else if( Debugger::debugger().getFunctionDef(idbuf) != EmptyString ) { |
309 | yylval.DefinedFunction = idbuf; |
310 | return FUNCTION; |
311 | } else { |
312 | yylval.val = const_to_int(idbuf); |
313 | if(yylval.val >= 0) |
314 | return NUMBER; |
315 | else |
316 | return ERR; |
317 | } |
318 | } |
319 | |
320 | case State::OPERATOR: |
321 | o = *c++; |
322 | if(!*c) return o; |
323 | if(isspace(*c)) { |
324 | state = State::SPACE; |
325 | return o; |
326 | } else if(is_identifier(*c) || is_base_prefix(*c)) { |
327 | state = State::IDENTIFIER; |
328 | return o; |
329 | } else { |
330 | state = State::DEFAULT; |
331 | p = *c++; |
332 | if(o == '>' && p == '=') |
333 | return GTE; |
334 | else if(o == '<' && p == '=') |
335 | return LTE; |
336 | else if(o == '!' && p == '=') |
337 | return NE; |
338 | else if(o == '=' && p == '=') |
339 | return EQ; |
340 | else if(o == '|' && p == '|') |
341 | return LOG_OR; |
342 | else if(o == '&' && p == '&') |
343 | return LOG_AND; |
344 | else if(o == '<' && p == '<') |
345 | return SHL; |
346 | else if(o == '>' && p == '>') |
347 | return SHR; |
348 | else { |
349 | c--; |
350 | return o; |
351 | } |
352 | } |
353 | // break; Never executed |
354 | |
355 | case State::DEFAULT: |
356 | yylval.val = 0; |
357 | if(isspace(*c)) { |
358 | state = State::SPACE; |
359 | } else if(is_identifier(*c) || is_base_prefix(*c)) { |
360 | state = State::IDENTIFIER; |
361 | } else if(is_operator(*c)) { |
362 | state = State::OPERATOR; |
363 | } else { |
364 | yylval.val = *c++; |
365 | return yylval.val; |
366 | } |
367 | break; |
368 | } |
369 | } |
370 | |
371 | return 0; // hit NUL, end of input. |
372 | } |
373 | |
374 | } // namespace YaccParser |
375 | |