CMDscan.l 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. %{
  2. #define YYLMAX 4096
  3. #include <stdio.h>
  4. #include "platform/platform.h"
  5. #include "core/stringTable.h"
  6. #include "console/console.h"
  7. #include "console/compiler.h"
  8. using namespace Compiler;
  9. #define YY_NEVER_INTERACTIVE 1
  10. // Some basic parsing primitives...
  11. static int Sc_ScanDocBlock();
  12. static int Sc_ScanString(int ret);
  13. static int Sc_ScanNum();
  14. static int Sc_ScanVar();
  15. static int Sc_ScanHex();
  16. // Deal with debuggability of FLEX.
  17. #ifdef TORQUE_DEBUG
  18. #define FLEX_DEBUG 1
  19. #else
  20. #define FLEX_DEBUG 0
  21. #endif
  22. // Install our own input code...
  23. #undef CMDgetc
  24. int CMDgetc();
  25. // Hack to make windows lex happy.
  26. #ifndef isatty
  27. inline int isatty(int) { return 0; }
  28. #endif
  29. // Wrap our getc, so that lex doesn't try to do its own buffering/file IO.
  30. #define YY_INPUT(buf,result,max_size) \
  31. { \
  32. int c = '*', n; \
  33. for ( n = 0; n < max_size && \
  34. (c = CMDgetc()) != EOF && c != '\n'; ++n ) \
  35. buf[n] = (char) c; \
  36. if ( c == '\n' ) \
  37. buf[n++] = (char) c; \
  38. result = n; \
  39. }
  40. // General helper stuff.
  41. static int lineIndex;
  42. // File state
  43. void CMDSetScanBuffer(const char *sb, const char *fn);
  44. const char * CMDgetFileLine(int &lineNumber);
  45. // Error reporting
  46. void CMDerror(char * s, ...);
  47. // Reset the parser.
  48. void CMDrestart(FILE *in);
  49. %}
  50. DIGIT [0-9]
  51. INTEGER {DIGIT}+
  52. FLOAT ({INTEGER}\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{INTEGER})
  53. LETTER [A-Za-z_]
  54. FILECHAR [A-Za-z_\.]
  55. VARMID [:A-Za-z0-9_]
  56. IDTAIL [A-Za-z0-9_]
  57. VARTAIL {VARMID}*{IDTAIL}
  58. VAR [$%]{LETTER}{VARTAIL}*
  59. ID {LETTER}{IDTAIL}*
  60. ILID [$%]{DIGIT}+{LETTER}{VARTAIL}*
  61. FILENAME {FILECHAR}+
  62. SPACE [ \t\v\f]
  63. HEXDIGIT [a-fA-F0-9]
  64. %%
  65. ;
  66. {SPACE}+ { }
  67. ("///"[^/][^\n\r]*[\n\r]*)+ { return(Sc_ScanDocBlock()); }
  68. "//"[^\n\r]* ;
  69. [\r] ;
  70. [\n] {lineIndex++;}
  71. \"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
  72. \'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
  73. "==" return(CMDlval.i = opEQ);
  74. "!=" return(CMDlval.i = opNE);
  75. ">=" return(CMDlval.i = opGE);
  76. "<=" return(CMDlval.i = opLE);
  77. "&&" return(CMDlval.i = opAND);
  78. "||" return(CMDlval.i = opOR);
  79. "::" return(CMDlval.i = opCOLONCOLON);
  80. "--" return(CMDlval.i = opMINUSMINUS);
  81. "++" return(CMDlval.i = opPLUSPLUS);
  82. "$=" return(CMDlval.i = opSTREQ);
  83. "!$=" return(CMDlval.i = opSTRNE);
  84. "<<" return(CMDlval.i = opSHL);
  85. ">>" return(CMDlval.i = opSHR);
  86. "+=" return(CMDlval.i = opPLASN);
  87. "-=" return(CMDlval.i = opMIASN);
  88. "*=" return(CMDlval.i = opMLASN);
  89. "/=" return(CMDlval.i = opDVASN);
  90. "%=" return(CMDlval.i = opMODASN);
  91. "&=" return(CMDlval.i = opANDASN);
  92. "^=" return(CMDlval.i = opXORASN);
  93. "|=" return(CMDlval.i = opORASN);
  94. "<<=" return(CMDlval.i = opSLASN);
  95. ">>=" return(CMDlval.i = opSRASN);
  96. "->" return(CMDlval.i = opINTNAME);
  97. "-->" return(CMDlval.i = opINTNAMER);
  98. "NL" {CMDlval.i = '\n'; return '@'; }
  99. "TAB" {CMDlval.i = '\t'; return '@'; }
  100. "SPC" {CMDlval.i = ' '; return '@'; }
  101. "@" {CMDlval.i = 0; return '@'; }
  102. "/*" {
  103. int c = 0, l;
  104. for ( ; ; )
  105. {
  106. l = c;
  107. c = yyinput();
  108. // Is this an open comment?
  109. if ( c == EOF )
  110. {
  111. CMDerror( "unexpected end of file found in comment" );
  112. break;
  113. }
  114. // Increment line numbers.
  115. else if ( c == '\n' )
  116. lineIndex++;
  117. // Did we find the end of the comment?
  118. else if ( l == '*' && c == '/' )
  119. break;
  120. }
  121. }
  122. "?" |
  123. "[" |
  124. "]" |
  125. "(" |
  126. ")" |
  127. "+" |
  128. "-" |
  129. "*" |
  130. "/" |
  131. "<" |
  132. ">" |
  133. "|" |
  134. "." |
  135. "!" |
  136. ":" |
  137. ";" |
  138. "{" |
  139. "}" |
  140. "," |
  141. "&" |
  142. "%" |
  143. "^" |
  144. "~" |
  145. "=" { return(CMDlval.i = CMDtext[0]); }
  146. "or" { CMDlval.i = lineIndex; return(rwCASEOR); }
  147. "break" { CMDlval.i = lineIndex; return(rwBREAK); }
  148. "return" { CMDlval.i = lineIndex; return(rwRETURN); }
  149. "else" { CMDlval.i = lineIndex; return(rwELSE); }
  150. "while" { CMDlval.i = lineIndex; return(rwWHILE); }
  151. "do" { CMDlval.i = lineIndex; return(rwDO); }
  152. "if" { CMDlval.i = lineIndex; return(rwIF); }
  153. "for" { CMDlval.i = lineIndex; return(rwFOR); }
  154. "continue" { CMDlval.i = lineIndex; return(rwCONTINUE); }
  155. "function" { CMDlval.i = lineIndex; return(rwDEFINE); }
  156. "new" { CMDlval.i = lineIndex; return(rwDECLARE); }
  157. "datablock" { CMDlval.i = lineIndex; return(rwDATABLOCK); }
  158. "newmsg" { CMDlval.i = lineIndex; return(rwMESSAGE); }
  159. "case" { CMDlval.i = lineIndex; return(rwCASE); }
  160. "switch$" { CMDlval.i = lineIndex; return(rwSWITCHSTR); }
  161. "switch" { CMDlval.i = lineIndex; return(rwSWITCH); }
  162. "default" { CMDlval.i = lineIndex; return(rwDEFAULT); }
  163. "package" { CMDlval.i = lineIndex; return(rwPACKAGE); }
  164. "namespace" { CMDlval.i = lineIndex; return(rwNAMESPACE); }
  165. "true" { CMDlval.i = 1; return INTCONST; }
  166. "false" { CMDlval.i = 0; return INTCONST; }
  167. {VAR} return(Sc_ScanVar());
  168. {ID} { CMDtext[CMDleng] = 0; CMDlval.s = StringTable->insert(CMDtext); return(IDENT); }
  169. 0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
  170. {INTEGER} { CMDtext[CMDleng] = 0; CMDlval.i = dAtoi(CMDtext); return INTCONST; }
  171. {FLOAT} return Sc_ScanNum();
  172. {ILID} return(ILLEGAL_TOKEN);
  173. . return(ILLEGAL_TOKEN);
  174. %%
  175. static const char *scanBuffer;
  176. static const char *fileName;
  177. static int scanIndex;
  178. const char * CMDGetCurrentFile()
  179. {
  180. return fileName;
  181. }
  182. int CMDGetCurrentLine()
  183. {
  184. return lineIndex;
  185. }
  186. extern bool gConsoleSyntaxError;
  187. void CMDerror(char *format, ...)
  188. {
  189. Compiler::gSyntaxError = true;
  190. const int BUFMAX = 1024;
  191. char tempBuf[BUFMAX];
  192. va_list args;
  193. va_start( args, format );
  194. #ifdef TORQUE_OS_WIN32
  195. _vsnprintf( tempBuf, BUFMAX, format, args );
  196. #else
  197. vsnprintf( tempBuf, BUFMAX, format, args );
  198. #endif
  199. if(fileName)
  200. {
  201. Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - %s", fileName, lineIndex, tempBuf);
  202. #ifndef NO_ADVANCED_ERROR_REPORT
  203. // dhc - lineIndex is bogus. let's try to add some sanity back in.
  204. int i,j,n;
  205. char c;
  206. int linediv = 1;
  207. // first, walk the buffer, trying to detect line ending type.
  208. // this is imperfect, if inconsistant line endings exist...
  209. for (i=0; i<scanIndex; i++)
  210. {
  211. c = scanBuffer[i];
  212. if (c=='\r' && scanBuffer[i+1]=='\n') linediv = 2; // crlf detected
  213. if (c=='\r' || c=='\n' || c==0) break; // enough for us to stop.
  214. }
  215. // grab some of the chars starting at the error location - lineending.
  216. i = 1; j = 0; n = 1;
  217. // find prev lineending
  218. while (n<BUFMAX-8 && i<scanIndex) // cap at file start
  219. {
  220. c = scanBuffer[scanIndex-i];
  221. if ((c=='\r' || c=='\n') && i>BUFMAX>>2) break; // at least get a little data
  222. n++; i++;
  223. }
  224. // find next lineending
  225. while (n<BUFMAX-8 && j<BUFMAX>>1) // cap at half-buf-size forward
  226. {
  227. c = scanBuffer[scanIndex+j];
  228. if (c==0) break;
  229. if ((c=='\r' || c=='\n') && j>BUFMAX>>2) break; // at least get a little data
  230. n++; j++;
  231. }
  232. if (i) i--; // chop off extra linefeed.
  233. if (j) j--; // chop off extra linefeed.
  234. // build our little text block
  235. if (i) dStrncpy(tempBuf,scanBuffer+scanIndex-i,i);
  236. dStrncpy(tempBuf+i,"##", 2); // bracketing.
  237. tempBuf[i+2] = scanBuffer[scanIndex]; // copy the halt character.
  238. dStrncpy(tempBuf+i+3,"##", 2); // bracketing.
  239. if (j) dStrncpy(tempBuf+i+5,scanBuffer+scanIndex+1,j); // +1 to go past current char.
  240. tempBuf[i+j+5] = 0; // null terminate
  241. for(n=0; n<i+j+5; n++) // convert CR to LF if alone...
  242. if (tempBuf[n]=='\r' && tempBuf[n+1]!='\n') tempBuf[n] = '\n';
  243. // write out to console the advanced error report
  244. Con::warnf(ConsoleLogEntry::Script, ">>> Advanced script error report. Line %d.", lineIndex);
  245. Con::warnf(ConsoleLogEntry::Script, ">>> Some error context, with ## on sides of error halt:");
  246. Con::errorf(ConsoleLogEntry::Script, "%s", tempBuf);
  247. Con::warnf(ConsoleLogEntry::Script, ">>> Error report complete.\n");
  248. #endif
  249. // Update the script-visible error buffer.
  250. const char *prevStr = Con::getVariable("$ScriptError");
  251. if (prevStr[0])
  252. dSprintf(tempBuf, sizeof(tempBuf), "%s\n%s Line: %d - Syntax error.", prevStr, fileName, lineIndex);
  253. else
  254. dSprintf(tempBuf, sizeof(tempBuf), "%s Line: %d - Syntax error.", fileName, lineIndex);
  255. Con::setVariable("$ScriptError", tempBuf);
  256. // We also need to mark that we came up with a new error.
  257. static S32 sScriptErrorHash=1000;
  258. Con::setIntVariable("$ScriptErrorHash", sScriptErrorHash++);
  259. }
  260. else
  261. Con::errorf(ConsoleLogEntry::Script, tempBuf);
  262. }
  263. void CMDSetScanBuffer(const char *sb, const char *fn)
  264. {
  265. scanBuffer = sb;
  266. fileName = fn;
  267. scanIndex = 0;
  268. lineIndex = 1;
  269. }
  270. int CMDgetc()
  271. {
  272. int ret = scanBuffer[scanIndex];
  273. if(ret)
  274. scanIndex++;
  275. else
  276. ret = -1;
  277. return ret;
  278. }
  279. int CMDwrap()
  280. {
  281. return 1;
  282. }
  283. static int Sc_ScanVar()
  284. {
  285. // Truncate the temp buffer...
  286. CMDtext[CMDleng] = 0;
  287. // Make it a stringtable string!
  288. CMDlval.s = StringTable->insert(CMDtext);
  289. return(VAR);
  290. }
  291. static int charConv(int in)
  292. {
  293. switch(in)
  294. {
  295. case 'r':
  296. return '\r';
  297. case 'n':
  298. return '\n';
  299. case 't':
  300. return '\t';
  301. default:
  302. return in;
  303. }
  304. }
  305. static int getHexDigit(char c)
  306. {
  307. if(c >= '0' && c <= '9')
  308. return c - '0';
  309. if(c >= 'A' && c <= 'F')
  310. return c - 'A' + 10;
  311. if(c >= 'a' && c <= 'f')
  312. return c - 'a' + 10;
  313. return -1;
  314. }
  315. static int Sc_ScanDocBlock()
  316. {
  317. S32 len = dStrlen(CMDtext);
  318. char* text = (char *) consoleAlloc(len + 1);
  319. for( S32 i = 0, j = 0; j <= len; j++ )
  320. {
  321. if( ( j <= (len - 2) ) && ( CMDtext[j] == '/' ) && ( CMDtext[j + 1] == '/' ) && ( CMDtext[j + 2] == '/' ) )
  322. {
  323. j += 2;
  324. continue;
  325. }
  326. if( CMDtext[j] == '\r' )
  327. continue;
  328. if( CMDtext[j] == '\n' )
  329. lineIndex++;
  330. text[i++] = CMDtext[j];
  331. }
  332. CMDlval.str = text;
  333. return(DOCBLOCK);
  334. }
  335. static int Sc_ScanString(int ret)
  336. {
  337. CMDtext[CMDleng - 1] = 0;
  338. if(!collapseEscape(CMDtext+1))
  339. return -1;
  340. CMDlval.str = (char *) consoleAlloc(dStrlen(CMDtext));
  341. dStrcpy(CMDlval.str, CMDtext + 1);
  342. return(ret);
  343. }
  344. void expandEscape(char *dest, const char *src)
  345. {
  346. U8 c;
  347. while((c = (U8) *src++) != 0)
  348. {
  349. if(c == '\"')
  350. {
  351. *dest++ = '\\';
  352. *dest++ = '\"';
  353. }
  354. else if(c == '\\')
  355. {
  356. *dest++ = '\\';
  357. *dest++ = '\\';
  358. }
  359. else if(c == '\r')
  360. {
  361. *dest++ = '\\';
  362. *dest++ = 'r';
  363. }
  364. else if(c == '\n')
  365. {
  366. *dest++ = '\\';
  367. *dest++ = 'n';
  368. }
  369. else if(c == '\t')
  370. {
  371. *dest++ = '\\';
  372. *dest++ = 't';
  373. }
  374. else if(c == '\'')
  375. {
  376. *dest++ = '\\';
  377. *dest++ = '\'';
  378. }
  379. else if((c >= 1 && c <= 7) ||
  380. (c >= 11 && c <= 12) ||
  381. (c >= 14 && c <= 15))
  382. {
  383. /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */
  384. static U8 expandRemap[15] = { 0x0,
  385. 0x0,
  386. 0x1,
  387. 0x2,
  388. 0x3,
  389. 0x4,
  390. 0x5,
  391. 0x6,
  392. 0x0,
  393. 0x0,
  394. 0x0,
  395. 0x7,
  396. 0x8,
  397. 0x0,
  398. 0x9 };
  399. *dest++ = '\\';
  400. *dest++ = 'c';
  401. if(c == 15)
  402. *dest++ = 'r';
  403. else if(c == 16)
  404. *dest++ = 'p';
  405. else if(c == 17)
  406. *dest++ = 'o';
  407. else
  408. *dest++ = expandRemap[c] + '0';
  409. }
  410. else if(c < 32)
  411. {
  412. *dest++ = '\\';
  413. *dest++ = 'x';
  414. S32 dig1 = c >> 4;
  415. S32 dig2 = c & 0xf;
  416. if(dig1 < 10)
  417. dig1 += '0';
  418. else
  419. dig1 += 'A' - 10;
  420. if(dig2 < 10)
  421. dig2 += '0';
  422. else
  423. dig2 += 'A' - 10;
  424. *dest++ = dig1;
  425. *dest++ = dig2;
  426. }
  427. else
  428. *dest++ = c;
  429. }
  430. *dest = '\0';
  431. }
  432. bool collapseEscape(char *buf)
  433. {
  434. S32 len = dStrlen(buf) + 1;
  435. for(S32 i = 0; i < len;)
  436. {
  437. if(buf[i] == '\\')
  438. {
  439. if(buf[i+1] == 'x')
  440. {
  441. S32 dig1 = getHexDigit(buf[i+2]);
  442. if(dig1 == -1)
  443. return false;
  444. S32 dig2 = getHexDigit(buf[i+3]);
  445. if(dig2 == -1)
  446. return false;
  447. buf[i] = dig1 * 16 + dig2;
  448. dMemmove(buf + i + 1, buf + i + 4, len - i - 3);
  449. len -= 3;
  450. i++;
  451. }
  452. else if(buf[i+1] == 'c')
  453. {
  454. /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */
  455. static U8 collapseRemap[10] = { 0x1,
  456. 0x2,
  457. 0x3,
  458. 0x4,
  459. 0x5,
  460. 0x6,
  461. 0x7,
  462. 0xb,
  463. 0xc,
  464. 0xe };
  465. if(buf[i+2] == 'r')
  466. buf[i] = 15;
  467. else if(buf[i+2] == 'p')
  468. buf[i] = 16;
  469. else if(buf[i+2] == 'o')
  470. buf[i] = 17;
  471. else
  472. {
  473. int dig1 = buf[i+2] - '0';
  474. if(dig1 < 0 || dig1 > 9)
  475. return false;
  476. buf[i] = collapseRemap[dig1];
  477. }
  478. // Make sure we don't put 0x1 at the beginning of the string.
  479. if ((buf[i] == 0x1) && (i == 0))
  480. {
  481. buf[i] = 0x2;
  482. buf[i+1] = 0x1;
  483. dMemmove(buf + i + 2, buf + i + 3, len - i - 1);
  484. len -= 1;
  485. }
  486. else
  487. {
  488. dMemmove(buf + i + 1, buf + i + 3, len - i - 2);
  489. len -= 2;
  490. }
  491. i++;
  492. }
  493. else
  494. {
  495. buf[i] = charConv(buf[i+1]);
  496. dMemmove(buf + i + 1, buf + i + 2, len - i - 1);
  497. len--;
  498. i++;
  499. }
  500. }
  501. else
  502. i++;
  503. }
  504. return true;
  505. }
  506. static int Sc_ScanNum()
  507. {
  508. CMDtext[CMDleng] = 0;
  509. CMDlval.f = dAtof(CMDtext);
  510. return(FLTCONST);
  511. }
  512. static int Sc_ScanHex()
  513. {
  514. S32 val = 0;
  515. dSscanf(CMDtext, "%x", &val);
  516. CMDlval.i = val;
  517. return INTCONST;
  518. }
  519. void CMD_reset()
  520. {
  521. CMDrestart(NULL);
  522. }