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
23namespace YaccParser {
24#include <cstdio>
25#include <cctype>
26
27#include "y.tab.h"
28static YYSTYPE result;
29static string errMsg;
30
31void 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
44enum class State {
45 DEFAULT,
46 IDENTIFIER,
47 OPERATOR,
48 SPACE
49};
50
51static State state = State::DEFAULT;
52static const char *input, *c;
53
54// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
55const string& errorMessage()
56{
57 return errMsg;
58}
59
60// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61Expression* getResult()
62{
63 lastExp = nullptr;
64 return result.exp;
65}
66
67// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
68void setInput(const string& in)
69{
70 input = c = in.c_str();
71 state = State::DEFAULT;
72}
73
74// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
75int 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...
85inline bool is_base_prefix(char x)
86{
87 return ( (x=='\\' || x=='$' || x=='#') );
88}
89
90inline 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
98inline 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
113int 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
180CartMethod 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
194CpuMethod 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
228TiaMethod 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
253int 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