CMDscan.l 17 KB

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