%{ #define YYLMAX 4096 #include #include "platform/platform.h" #include "core/stringTable.h" #include "console/console.h" #include "console/compiler.h" using namespace Compiler; #define YY_NEVER_INTERACTIVE 1 // Some basic parsing primitives... static int Sc_ScanString(int ret); static int Sc_ScanNum(); static int Sc_ScanVar(); static int Sc_ScanHex(); // Deal with debuggability of FLEX. #ifdef TORQUE_DEBUG #define FLEX_DEBUG 1 #else #define FLEX_DEBUG 0 #endif // Install our own input code... #undef CMDgetc int CMDgetc(); // Hack to make windows lex happy. #ifndef isatty inline int isatty(int) { return 0; } #endif // Wrap our getc, so that lex doesn't try to do its own buffering/file IO. #define YY_INPUT(buf,result,max_size) \ { \ int c = '*', n; \ for ( n = 0; n < max_size && \ (c = CMDgetc()) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ result = n; \ } // General helper stuff. static int lineIndex; // File state void CMDSetScanBuffer(const char *sb, const char *fn); const char * CMDgetFileLine(int &lineNumber); // Error reporting void CMDerror(char * s, ...); // Reset the parser. void CMDrestart(FILE *in); %} DIGIT [0-9] INTEGER {DIGIT}+ FLOAT ({INTEGER}\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{INTEGER})|(\.{INTEGER})|((\.{INTEGER})?[eE][+-]?{INTEGER}) LETTER [A-Za-z_] FILECHAR [A-Za-z_\.] VARMID [:A-Za-z0-9_] IDTAIL [A-Za-z0-9_] VARTAIL {VARMID}*{IDTAIL} VAR [$%]{LETTER}{VARTAIL}* ID {LETTER}{IDTAIL}* ILID [$%]{DIGIT}+{LETTER}{VARTAIL}* FILENAME {FILECHAR}+ SPACE [ \t\v\f] HEXDIGIT [a-fA-F0-9] %% ; {SPACE}+ { } "//"[^\n\r]* ; [\r] ; [\n] {lineIndex++;} \"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); } \'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); } "==" return(CMDlval.i = opEQ); "!=" return(CMDlval.i = opNE); ">=" return(CMDlval.i = opGE); "<=" return(CMDlval.i = opLE); "&&" return(CMDlval.i = opAND); "||" return(CMDlval.i = opOR); "::" return(CMDlval.i = opCOLONCOLON); "--" return(CMDlval.i = opMINUSMINUS); "++" return(CMDlval.i = opPLUSPLUS); "$=" return(CMDlval.i = opSTREQ); "!$=" return(CMDlval.i = opSTRNE); "<<" return(CMDlval.i = opSHL); ">>" return(CMDlval.i = opSHR); "+=" return(CMDlval.i = opPLASN); "-=" return(CMDlval.i = opMIASN); "*=" return(CMDlval.i = opMLASN); "/=" return(CMDlval.i = opDVASN); "%=" return(CMDlval.i = opMODASN); "&=" return(CMDlval.i = opANDASN); "^=" return(CMDlval.i = opXORASN); "|=" return(CMDlval.i = opORASN); "<<=" return(CMDlval.i = opSLASN); ">>=" return(CMDlval.i = opSRASN); "NL" {CMDlval.i = '\n'; return '@'; } "TAB" {CMDlval.i = '\t'; return '@'; } "SPC" {CMDlval.i = ' '; return '@'; } "@" {CMDlval.i = 0; return '@'; } "/*" { register int c = 0, l; for ( ; ; ) { l = c; c = yyinput(); // Is this an open comment? if ( c == EOF ) { CMDerror( "unexpected end of file found in comment" ); break; } // Increment line numbers. else if ( c == '\n' ) lineIndex++; // Did we find the end of the comment? else if ( l == '*' && c == '/' ) break; } } "?" | "[" | "]" | "(" | ")" | "+" | "-" | "*" | "/" | "<" | ">" | "|" | "." | "!" | ":" | ";" | "{" | "}" | "," | "&" | "%" | "^" | "~" | "=" { return(CMDlval.i = CMDtext[0]); } "or" { CMDlval.i = lineIndex; return(rwCASEOR); } "break" { CMDlval.i = lineIndex; return(rwBREAK); } "return" { CMDlval.i = lineIndex; return(rwRETURN); } "else" { CMDlval.i = lineIndex; return(rwELSE); } "while" { CMDlval.i = lineIndex; return(rwWHILE); } "do" { CMDlval.i = lineIndex; return(rwDO); } "if" { CMDlval.i = lineIndex; return(rwIF); } "for" { CMDlval.i = lineIndex; return(rwFOR); } "continue" { CMDlval.i = lineIndex; return(rwCONTINUE); } "function" { CMDlval.i = lineIndex; return(rwDEFINE); } "new" { CMDlval.i = lineIndex; return(rwDECLARE); } "datablock" { CMDlval.i = lineIndex; return(rwDATABLOCK); } "case" { CMDlval.i = lineIndex; return(rwCASE); } "switch$" { CMDlval.i = lineIndex; return(rwSWITCHSTR); } "switch" { CMDlval.i = lineIndex; return(rwSWITCH); } "default" { CMDlval.i = lineIndex; return(rwDEFAULT); } "package" { CMDlval.i = lineIndex; return(rwPACKAGE); } "namespace" { CMDlval.i = lineIndex; return(rwNAMESPACE); } "true" { CMDlval.i = 1; return INTCONST; } "false" { CMDlval.i = 0; return INTCONST; } {VAR} return(Sc_ScanVar()); {ID} { CMDtext[CMDleng] = 0; CMDlval.s = StringTable->insert(CMDtext); return(IDENT); } 0[xX]{HEXDIGIT}+ return(Sc_ScanHex()); {INTEGER} { CMDtext[CMDleng] = 0; CMDlval.i = dAtoi(CMDtext); return INTCONST; } {FLOAT} return Sc_ScanNum(); {ILID} return(ILLEGAL_TOKEN); . return(ILLEGAL_TOKEN); %% static const char *scanBuffer; static const char *fileName; static int scanIndex; const char * CMDGetCurrentFile() { return fileName; } int CMDGetCurrentLine() { return lineIndex; } extern bool gConsoleSyntaxError; void CMDerror(char *format, ...) { Compiler::gSyntaxError = true; const int BUFMAX = 1024; char tempBuf[BUFMAX]; va_list args; va_start( args, format ); #ifdef TORQUE_OS_WIN32 _vsnprintf( tempBuf, BUFMAX, format, args ); #else vsnprintf( tempBuf, BUFMAX, format, args ); #endif if(fileName) { Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - %s", fileName, lineIndex, tempBuf); #ifndef NO_ADVANCED_ERROR_REPORT // dhc - lineIndex is bogus. let's try to add some sanity back in. int i,j,n; char c; int linediv = 1; // first, walk the buffer, trying to detect line ending type. // this is imperfect, if inconsistant line endings exist... for (i=0; iBUFMAX>>2) break; // at least get a little data n++; i++; } // find next lineending while (n>1) // cap at half-buf-size forward { c = scanBuffer[scanIndex+j]; if (c==0) break; if ((c=='\r' || c=='\n') && j>BUFMAX>>2) break; // at least get a little data n++; j++; } if (i) i--; // chop off extra linefeed. if (j) j--; // chop off extra linefeed. // build our little text block if (i) dStrncpy(tempBuf,scanBuffer+scanIndex-i,i); dStrncpy(tempBuf+i,"##", 2); // bracketing. tempBuf[i+2] = scanBuffer[scanIndex]; // copy the halt character. dStrncpy(tempBuf+i+3,"##", 2); // bracketing. if (j) dStrncpy(tempBuf+i+5,scanBuffer+scanIndex+1,j); // +1 to go past current char. tempBuf[i+j+5] = 0; // null terminate for(n=0; n>> Advanced script error report. Line %d.", lineIndex); Con::warnf(ConsoleLogEntry::Script, ">>> Some error context, with ## on sides of error halt:"); Con::errorf(ConsoleLogEntry::Script, "%s", tempBuf); Con::warnf(ConsoleLogEntry::Script, ">>> Error report complete.\n"); #endif // Update the script-visible error buffer. const char *prevStr = Con::getVariable("$ScriptError"); if (prevStr[0]) dSprintf(tempBuf, sizeof(tempBuf), "%s\n%s Line: %d - Syntax error.", prevStr, fileName, lineIndex); else dSprintf(tempBuf, sizeof(tempBuf), "%s Line: %d - Syntax error.", fileName, lineIndex); Con::setVariable("$ScriptError", tempBuf); // We also need to mark that we came up with a new error. static S32 sScriptErrorHash=1000; Con::setIntVariable("$ScriptErrorHash", sScriptErrorHash++); } else Con::errorf(ConsoleLogEntry::Script, tempBuf); } void CMDSetScanBuffer(const char *sb, const char *fn) { scanBuffer = sb; fileName = fn; scanIndex = 0; lineIndex = 1; } int CMDgetc() { int ret = scanBuffer[scanIndex]; if(ret) scanIndex++; else ret = -1; return ret; } int CMDwrap() { return 1; } static int Sc_ScanVar() { // Truncate the temp buffer... CMDtext[CMDleng] = 0; // Make it a stringtable string! CMDlval.s = StringTable->insert(CMDtext); return(VAR); } static int charConv(int in) { switch(in) { case 'r': return '\r'; case 'n': return '\n'; case 't': return '\t'; default: return in; } } static int getHexDigit(char c) { if(c >= '0' && c <= '9') return c - '0'; if(c >= 'A' && c <= 'F') return c - 'A' + 10; if(c >= 'a' && c <= 'f') return c - 'a' + 10; return -1; } static int Sc_ScanString(int ret) { CMDtext[CMDleng - 1] = 0; if(!collapseEscape(CMDtext+1)) return -1; CMDlval.str = (char *) consoleAlloc(dStrlen(CMDtext)); dStrcpy(CMDlval.str, CMDtext + 1); return(ret); } void expandEscape(char *dest, const char *src) { U8 c; while((c = (U8) *src++) != 0) { if(c == '\"') { *dest++ = '\\'; *dest++ = '\"'; } else if(c == '\\') { *dest++ = '\\'; *dest++ = '\\'; } else if(c == '\r') { *dest++ = '\\'; *dest++ = 'r'; } else if(c == '\n') { *dest++ = '\\'; *dest++ = 'n'; } else if(c == '\t') { *dest++ = '\\'; *dest++ = 't'; } else if(c == '\'') { *dest++ = '\\'; *dest++ = '\''; } else if((c >= 1 && c <= 7) || (c >= 11 && c <= 12) || (c >= 14 && c <= 15)) { /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */ static U8 expandRemap[15] = { 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x0, 0x0, 0x0, 0x7, 0x8, 0x0, 0x9 }; *dest++ = '\\'; *dest++ = 'c'; if(c == 15) *dest++ = 'r'; else if(c == 16) *dest++ = 'p'; else if(c == 17) *dest++ = 'o'; else *dest++ = expandRemap[c] + '0'; } else if(c < 32) { *dest++ = '\\'; *dest++ = 'x'; S32 dig1 = c >> 4; S32 dig2 = c & 0xf; if(dig1 < 10) dig1 += '0'; else dig1 += 'A' - 10; if(dig2 < 10) dig2 += '0'; else dig2 += 'A' - 10; *dest++ = dig1; *dest++ = dig2; } else *dest++ = c; } *dest = '\0'; } bool collapseEscape(char *buf) { S32 len = dStrlen(buf) + 1; for(S32 i = 0; i < len;) { if(buf[i] == '\\') { if(buf[i+1] == 'x') { S32 dig1 = getHexDigit(buf[i+2]); if(dig1 == -1) return false; S32 dig2 = getHexDigit(buf[i+3]); if(dig2 == -1) return false; buf[i] = dig1 * 16 + dig2; dMemmove(buf + i + 1, buf + i + 4, len - i - 3); len -= 3; i++; } else if(buf[i+1] == 'c') { /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */ static U8 collapseRemap[10] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xb, 0xc, 0xe }; if(buf[i+2] == 'r') buf[i] = 15; else if(buf[i+2] == 'p') buf[i] = 16; else if(buf[i+2] == 'o') buf[i] = 17; else { int dig1 = buf[i+2] - '0'; if(dig1 < 0 || dig1 > 9) return false; buf[i] = collapseRemap[dig1]; } // Make sure we don't put 0x1 at the beginning of the string. if ((buf[i] == 0x1) && (i == 0)) { buf[i] = 0x2; buf[i+1] = 0x1; dMemmove(buf + i + 2, buf + i + 3, len - i - 1); len -= 1; } else { dMemmove(buf + i + 1, buf + i + 3, len - i - 2); len -= 2; } i++; } else { buf[i] = charConv(buf[i+1]); dMemmove(buf + i + 1, buf + i + 2, len - i - 1); len--; i++; } } else i++; } return true; } static int Sc_ScanNum() { CMDtext[CMDleng] = 0; CMDlval.f = dAtof(CMDtext); return(FLTCONST); } static int Sc_ScanHex() { S32 val = 0; dSscanf(CMDtext, "%x", &val); CMDlval.i = val; return INTCONST; } void CMD_reset() { CMDrestart(NULL); }