2005-07-03 14:18:54 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// SSSS tt lll lll
|
|
|
|
// SS SS tt ll ll
|
|
|
|
// SS tttttt eeee ll ll aaaa
|
|
|
|
// SSSS tt ee ee ll ll aa
|
|
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
|
|
// SS SS tt ee ll ll aa aa
|
|
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
|
|
//
|
2010-01-10 03:23:32 +00:00
|
|
|
// Copyright (c) 1995-2010 by Bradford W. Mott and the Stella Team
|
2005-07-03 14:18:54 +00:00
|
|
|
//
|
2010-01-10 03:23:32 +00:00
|
|
|
// See the file "License.txt" for information on usage and redistribution of
|
2005-07-03 14:18:54 +00:00
|
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
|
|
//
|
2009-05-13 13:55:40 +00:00
|
|
|
// $Id$
|
2005-07-03 14:18:54 +00:00
|
|
|
//
|
|
|
|
// Based on code from ScummVM - Scumm Interpreter
|
|
|
|
// Copyright (C) 2002-2004 The ScummVM project
|
|
|
|
//============================================================================
|
2005-07-01 04:29:18 +00:00
|
|
|
|
|
|
|
//#include "YaccParser.hxx"
|
|
|
|
|
2005-07-13 02:54:13 +00:00
|
|
|
//#ifdef __cplusplus
|
|
|
|
//extern "C" {
|
|
|
|
//#endif
|
|
|
|
|
|
|
|
#include "Expression.hxx"
|
2005-07-15 02:19:07 +00:00
|
|
|
#include "CpuDebug.hxx"
|
2009-11-08 16:46:10 +00:00
|
|
|
#include "RamDebug.hxx"
|
2005-07-19 00:05:23 +00:00
|
|
|
#include "TIADebug.hxx"
|
2005-07-13 04:49:19 +00:00
|
|
|
|
2006-12-23 15:11:19 +00:00
|
|
|
#include "DebuggerExpressions.hxx"
|
2005-07-03 14:18:54 +00:00
|
|
|
|
2005-07-01 04:29:18 +00:00
|
|
|
namespace YaccParser {
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
2005-07-13 02:54:13 +00:00
|
|
|
#include "y.tab.h"
|
|
|
|
yystype result;
|
OK, we no longer segfault on parse error.
However, we do leak memory: yyparse() happily allocates Expressions as
it parses, but when it hits a parse error, it doesn't return a valid
pointer to the top of the Expression tree. From what I can tell, the
so-called Expression* result is the int value of the last lexer token
cast to an Expression* (due to yacc's use of a union).
I know how to avoid the leak: we need to keep a vector of Expression
pointers in YaccParser. If there's a parse error, yyerror() can delete
all the Expressions using the vector. If not, we clear the vector (er,
calling .clear() on a vector doesn't delete all its elements, too,
does it?). Every time yacc says "$$ = new WhateverExpression", it also
should vector.push_back($$).
Will implement this tomorrow; am getting tired & flaky.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@655 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2005-07-15 03:47:26 +00:00
|
|
|
string errMsg;
|
2005-07-01 04:29:18 +00:00
|
|
|
#include "y.tab.c"
|
|
|
|
|
OK, we no longer segfault on parse error.
However, we do leak memory: yyparse() happily allocates Expressions as
it parses, but when it hits a parse error, it doesn't return a valid
pointer to the top of the Expression tree. From what I can tell, the
so-called Expression* result is the int value of the last lexer token
cast to an Expression* (due to yacc's use of a union).
I know how to avoid the leak: we need to keep a vector of Expression
pointers in YaccParser. If there's a parse error, yyerror() can delete
all the Expressions using the vector. If not, we clear the vector (er,
calling .clear() on a vector doesn't delete all its elements, too,
does it?). Every time yacc says "$$ = new WhateverExpression", it also
should vector.push_back($$).
Will implement this tomorrow; am getting tired & flaky.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@655 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2005-07-15 03:47:26 +00:00
|
|
|
const string& errorMessage() {
|
|
|
|
return errMsg;
|
|
|
|
}
|
2005-07-01 04:29:18 +00:00
|
|
|
|
2005-07-13 02:54:13 +00:00
|
|
|
Expression *getResult() {
|
|
|
|
return result.exp;
|
2005-07-23 21:16:57 +00:00
|
|
|
lastExp = 0;
|
2005-07-01 04:29:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *input, *c;
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ST_DEFAULT,
|
2005-07-14 15:13:58 +00:00
|
|
|
ST_IDENTIFIER,
|
2005-07-01 04:29:18 +00:00
|
|
|
ST_OPERATOR,
|
|
|
|
ST_SPACE
|
|
|
|
};
|
|
|
|
|
|
|
|
int state = ST_DEFAULT;
|
|
|
|
|
|
|
|
//extern int yylval; // bison provides this
|
|
|
|
|
|
|
|
void setInput(const char *in) {
|
|
|
|
input = c = in;
|
|
|
|
state = ST_DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse(const char *in) {
|
2005-07-23 21:16:57 +00:00
|
|
|
lastExp = 0;
|
OK, we no longer segfault on parse error.
However, we do leak memory: yyparse() happily allocates Expressions as
it parses, but when it hits a parse error, it doesn't return a valid
pointer to the top of the Expression tree. From what I can tell, the
so-called Expression* result is the int value of the last lexer token
cast to an Expression* (due to yacc's use of a union).
I know how to avoid the leak: we need to keep a vector of Expression
pointers in YaccParser. If there's a parse error, yyerror() can delete
all the Expressions using the vector. If not, we clear the vector (er,
calling .clear() on a vector doesn't delete all its elements, too,
does it?). Every time yacc says "$$ = new WhateverExpression", it also
should vector.push_back($$).
Will implement this tomorrow; am getting tired & flaky.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@655 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2005-07-15 03:47:26 +00:00
|
|
|
errMsg = "(no error)";
|
2005-07-01 04:29:18 +00:00
|
|
|
setInput(in);
|
|
|
|
return yyparse();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* hand-rolled lexer. Hopefully faster than flex... */
|
2005-07-15 01:20:11 +00:00
|
|
|
inline bool is_base_prefix(char x) { return ( (x=='\\' || x=='$' || x=='#') ); }
|
2005-07-14 15:13:58 +00:00
|
|
|
|
2005-07-15 01:20:11 +00:00
|
|
|
inline bool is_identifier(char x) {
|
|
|
|
return ( (x>='0' && x<='9') ||
|
|
|
|
(x>='a' && x<='z') ||
|
|
|
|
(x>='A' && x<='Z') ||
|
|
|
|
x=='.' || x=='_' );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline bool is_operator(char x) {
|
|
|
|
return ( (x=='+' || x=='-' || x=='*' ||
|
|
|
|
x=='/' || x=='<' || x=='>' ||
|
|
|
|
x=='|' || x=='&' || x=='^' ||
|
|
|
|
x=='!' || x=='~' || x=='(' ||
|
2005-07-19 01:31:37 +00:00
|
|
|
x==')' || x=='=' || x=='%' ||
|
|
|
|
x=='[' || x==']' ) );
|
2005-07-15 01:20:11 +00:00
|
|
|
}
|
|
|
|
|
2005-07-16 23:46:37 +00:00
|
|
|
// const_to_int converts a string into a number, in either the
|
|
|
|
// current base, or (if there's a base override) the selected base.
|
|
|
|
// Returns -1 on error, since negative numbers are the parser's
|
|
|
|
// responsibility, not the lexer's
|
2005-07-15 01:20:11 +00:00
|
|
|
int const_to_int(char *c) {
|
|
|
|
// what base is the input in?
|
2008-03-23 17:43:22 +00:00
|
|
|
BaseFormat base = Debugger::debugger().parser().base();
|
2005-07-15 01:20:11 +00:00
|
|
|
|
|
|
|
switch(*c) {
|
|
|
|
case '\\':
|
|
|
|
base = kBASE_2;
|
|
|
|
c++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '#':
|
|
|
|
base = kBASE_10;
|
|
|
|
c++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '$':
|
|
|
|
base = kBASE_16;
|
|
|
|
c++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // not a base_prefix, use default base
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
switch(base) {
|
|
|
|
case kBASE_2:
|
|
|
|
while(*c) {
|
2005-07-16 23:46:37 +00:00
|
|
|
if(*c != '0' && *c != '1')
|
|
|
|
return -1;
|
2005-07-15 01:20:11 +00:00
|
|
|
ret *= 2;
|
2005-07-16 23:46:37 +00:00
|
|
|
ret += (*c - '0');
|
2005-07-15 01:20:11 +00:00
|
|
|
c++;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
case kBASE_10:
|
|
|
|
while(*c) {
|
2005-07-16 23:46:37 +00:00
|
|
|
if(!isdigit(*c))
|
|
|
|
return -1;
|
2005-07-15 01:20:11 +00:00
|
|
|
ret *= 10;
|
2005-07-16 23:46:37 +00:00
|
|
|
ret += (*c - '0');
|
2005-07-15 01:20:11 +00:00
|
|
|
c++;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
case kBASE_16:
|
|
|
|
while(*c) { // FIXME: error check!
|
2005-07-16 23:46:37 +00:00
|
|
|
if(!isxdigit(*c))
|
|
|
|
return -1;
|
2005-07-15 01:20:11 +00:00
|
|
|
int dig = (*c - '0');
|
2005-07-16 23:46:37 +00:00
|
|
|
if(dig > 9) dig = tolower(*c) - 'a' + 10;
|
2005-07-15 01:20:11 +00:00
|
|
|
ret *= 16;
|
|
|
|
ret += dig;
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "INVALID BASE in lexer!");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2005-07-01 04:29:18 +00:00
|
|
|
|
2005-07-15 02:59:00 +00:00
|
|
|
// TODO: store in a map or something
|
2009-11-08 16:46:10 +00:00
|
|
|
// special methods that get e.g. CPU registers
|
2005-07-18 23:50:28 +00:00
|
|
|
CPUDEBUG_INT_METHOD getCpuSpecial(char *c) {
|
2005-07-15 02:19:07 +00:00
|
|
|
if(strcmp(c, "a") == 0)
|
|
|
|
return &CpuDebug::a;
|
|
|
|
|
2005-07-15 02:59:00 +00:00
|
|
|
if(strcmp(c, "x") == 0)
|
|
|
|
return &CpuDebug::x;
|
|
|
|
|
|
|
|
if(strcmp(c, "y") == 0)
|
|
|
|
return &CpuDebug::y;
|
|
|
|
|
|
|
|
if(strcmp(c, "pc") == 0)
|
|
|
|
return &CpuDebug::pc;
|
|
|
|
|
|
|
|
if(strcmp(c, "sp") == 0)
|
|
|
|
return &CpuDebug::sp;
|
|
|
|
|
|
|
|
if(strcmp(c, "c") == 0)
|
|
|
|
return &CpuDebug::c;
|
|
|
|
|
|
|
|
if(strcmp(c, "z") == 0)
|
|
|
|
return &CpuDebug::z;
|
|
|
|
|
|
|
|
if(strcmp(c, "n") == 0)
|
|
|
|
return &CpuDebug::n;
|
|
|
|
|
|
|
|
if(strcmp(c, "v") == 0)
|
|
|
|
return &CpuDebug::v;
|
|
|
|
|
|
|
|
if(strcmp(c, "d") == 0)
|
|
|
|
return &CpuDebug::d;
|
|
|
|
|
|
|
|
if(strcmp(c, "i") == 0)
|
|
|
|
return &CpuDebug::i;
|
|
|
|
|
|
|
|
if(strcmp(c, "b") == 0)
|
|
|
|
return &CpuDebug::b;
|
|
|
|
|
2005-07-19 01:31:37 +00:00
|
|
|
if(strcmp(c, "_bank") == 0)
|
|
|
|
return &CpuDebug::getBank;
|
|
|
|
|
2005-07-15 02:19:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-08 16:46:10 +00:00
|
|
|
// special methods that get RAM internal state
|
|
|
|
RAMDEBUG_INT_METHOD getRamSpecial(char *c) {
|
|
|
|
if(strcmp(c, "_rwport") == 0)
|
|
|
|
return &RamDebug::readFromWritePort;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-07-19 00:05:23 +00:00
|
|
|
// special methods that get TIA internal state
|
|
|
|
TIADEBUG_INT_METHOD getTiaSpecial(char *c) {
|
|
|
|
if(strcmp(c, "_scan") == 0)
|
|
|
|
return &TIADebug::scanlines;
|
|
|
|
|
2009-01-12 15:11:55 +00:00
|
|
|
if(strcmp(c, "_fcount") == 0)
|
|
|
|
return &TIADebug::frameCount;
|
|
|
|
|
|
|
|
if(strcmp(c, "_cclocks") == 0)
|
|
|
|
return &TIADebug::clocksThisLine;
|
|
|
|
|
|
|
|
if(strcmp(c, "_vsync") == 0)
|
|
|
|
return &TIADebug::vsyncAsInt;
|
|
|
|
|
|
|
|
if(strcmp(c, "_vblank") == 0)
|
|
|
|
return &TIADebug::vblankAsInt;
|
|
|
|
|
2005-07-19 00:05:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-07-01 04:29:18 +00:00
|
|
|
int yylex() {
|
2005-07-14 15:13:58 +00:00
|
|
|
static char idbuf[255];
|
2005-07-01 04:29:18 +00:00
|
|
|
char o, p;
|
2005-07-13 02:54:13 +00:00
|
|
|
yylval.val = 0;
|
2005-07-01 04:29:18 +00:00
|
|
|
while(*c != '\0') {
|
|
|
|
//fprintf(stderr, "looking at %c, state %d\n", *c, state);
|
|
|
|
switch(state) {
|
|
|
|
case ST_SPACE:
|
2005-07-13 02:54:13 +00:00
|
|
|
yylval.val = 0;
|
2005-07-01 04:29:18 +00:00
|
|
|
if(isspace(*c)) {
|
|
|
|
c++;
|
2005-07-15 01:20:11 +00:00
|
|
|
} else if(is_identifier(*c) || is_base_prefix(*c)) {
|
2005-07-14 15:13:58 +00:00
|
|
|
state = ST_IDENTIFIER;
|
2005-07-01 04:29:18 +00:00
|
|
|
} else if(is_operator(*c)) {
|
|
|
|
state = ST_OPERATOR;
|
|
|
|
} else {
|
|
|
|
state = ST_DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2005-07-14 15:13:58 +00:00
|
|
|
case ST_IDENTIFIER:
|
|
|
|
{
|
2005-07-18 23:50:28 +00:00
|
|
|
CPUDEBUG_INT_METHOD cpuMeth;
|
2009-11-08 16:46:10 +00:00
|
|
|
RAMDEBUG_INT_METHOD ramMeth;
|
2005-07-19 00:05:23 +00:00
|
|
|
TIADEBUG_INT_METHOD tiaMeth;
|
2005-07-15 02:19:07 +00:00
|
|
|
|
2005-07-14 15:13:58 +00:00
|
|
|
char *bufp = idbuf;
|
2005-07-15 01:20:11 +00:00
|
|
|
*bufp++ = *c++; // might be a base prefix
|
|
|
|
while(is_identifier(*c)) { // may NOT be base prefixes
|
2005-07-14 15:13:58 +00:00
|
|
|
*bufp++ = *c++;
|
|
|
|
//fprintf(stderr, "yylval==%d, *c==%c\n", yylval, *c);
|
|
|
|
}
|
|
|
|
*bufp = '\0';
|
2005-07-01 04:29:18 +00:00
|
|
|
state = ST_DEFAULT;
|
|
|
|
|
2005-07-16 23:46:37 +00:00
|
|
|
// Note: specials (like "a" for accumulator) have priority over
|
|
|
|
// numbers. So "a" always means accumulator, not hex 0xa. User
|
|
|
|
// is welcome to use a base prefix ("$a"), or a capital "A",
|
|
|
|
// to mean 0xa.
|
|
|
|
|
|
|
|
// Also, labels have priority over specials, so Bad Things will
|
|
|
|
// happen if the user defines a label that matches one of
|
|
|
|
// the specials. Who would do that, though?
|
|
|
|
|
2008-03-23 17:43:22 +00:00
|
|
|
if(Debugger::debugger().equates().getAddress(idbuf) > -1) {
|
2005-07-14 15:13:58 +00:00
|
|
|
yylval.equate = idbuf;
|
|
|
|
return EQUATE;
|
2005-07-18 23:50:28 +00:00
|
|
|
} else if( (cpuMeth = getCpuSpecial(idbuf)) ) {
|
|
|
|
yylval.cpuMethod = cpuMeth;
|
|
|
|
return CPU_METHOD;
|
2009-11-08 16:46:10 +00:00
|
|
|
} else if( (ramMeth = getRamSpecial(idbuf)) ) {
|
|
|
|
yylval.ramMethod = ramMeth;
|
|
|
|
return RAM_METHOD;
|
2005-07-19 00:05:23 +00:00
|
|
|
} else if( (tiaMeth = getTiaSpecial(idbuf)) ) {
|
|
|
|
yylval.tiaMethod = tiaMeth;
|
|
|
|
return TIA_METHOD;
|
2005-07-21 03:26:59 +00:00
|
|
|
} else if( Debugger::debugger().getFunction(idbuf) != 0) {
|
|
|
|
yylval.function = idbuf;
|
|
|
|
return FUNCTION;
|
2005-07-14 15:13:58 +00:00
|
|
|
} else {
|
2005-07-15 01:20:11 +00:00
|
|
|
yylval.val = const_to_int(idbuf);
|
2005-07-16 23:46:37 +00:00
|
|
|
if(yylval.val >= 0)
|
|
|
|
return NUMBER;
|
|
|
|
else
|
|
|
|
return ERR;
|
2005-07-14 15:13:58 +00:00
|
|
|
}
|
|
|
|
}
|
2005-07-01 04:29:18 +00:00
|
|
|
|
|
|
|
case ST_OPERATOR:
|
|
|
|
o = *c++;
|
|
|
|
if(!*c) return o;
|
|
|
|
if(isspace(*c)) {
|
|
|
|
state = ST_SPACE;
|
|
|
|
return o;
|
2005-07-15 01:20:11 +00:00
|
|
|
} else if(is_identifier(*c) || is_base_prefix(*c)) {
|
2005-07-14 15:13:58 +00:00
|
|
|
state = ST_IDENTIFIER;
|
2005-07-01 04:29:18 +00:00
|
|
|
return o;
|
|
|
|
} else {
|
|
|
|
state = ST_DEFAULT;
|
|
|
|
p = *c++;
|
|
|
|
//fprintf(stderr, "o==%c, p==%c\n", o, p);
|
|
|
|
if(o == '>' && p == '=')
|
|
|
|
return GTE;
|
|
|
|
else if(o == '<' && p == '=')
|
|
|
|
return LTE;
|
|
|
|
else if(o == '!' && p == '=')
|
|
|
|
return NE;
|
|
|
|
else if(o == '=' && p == '=')
|
|
|
|
return EQ;
|
|
|
|
else if(o == '|' && p == '|')
|
|
|
|
return LOG_OR;
|
|
|
|
else if(o == '&' && p == '&')
|
|
|
|
return LOG_AND;
|
|
|
|
else if(o == '<' && p == '<')
|
|
|
|
return SHL;
|
|
|
|
else if(o == '>' && p == '>')
|
|
|
|
return SHR;
|
|
|
|
else {
|
|
|
|
c--;
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ST_DEFAULT:
|
|
|
|
default:
|
2005-07-13 02:54:13 +00:00
|
|
|
yylval.val = 0;
|
2005-07-01 04:29:18 +00:00
|
|
|
if(isspace(*c)) {
|
|
|
|
state = ST_SPACE;
|
2005-07-15 01:20:11 +00:00
|
|
|
} else if(is_identifier(*c) || is_base_prefix(*c)) {
|
2005-07-14 15:13:58 +00:00
|
|
|
state = ST_IDENTIFIER;
|
2005-07-01 04:29:18 +00:00
|
|
|
} else if(is_operator(*c)) {
|
|
|
|
state = ST_OPERATOR;
|
|
|
|
} else {
|
2005-07-13 02:54:13 +00:00
|
|
|
yylval.val = *c++;
|
|
|
|
return yylval.val;
|
2005-07-01 04:29:18 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//fprintf(stderr, "end of input\n");
|
|
|
|
return 0; // hit NUL, end of input.
|
|
|
|
}
|
|
|
|
|
2005-07-15 01:20:11 +00:00
|
|
|
|
2005-07-01 04:29:18 +00:00
|
|
|
#if 0
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
int l;
|
|
|
|
|
|
|
|
set_input(argv[1]);
|
|
|
|
while( (l = yylex()) != 0 )
|
|
|
|
printf("ret %d, %d\n", l, yylval);
|
|
|
|
|
|
|
|
printf("%d\n", yylval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2005-07-03 14:18:54 +00:00
|
|
|
|
2005-07-13 02:54:13 +00:00
|
|
|
//#ifdef __cplusplus
|
|
|
|
//}
|
|
|
|
//#endif
|