nseel-eval.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /*
  2. Expression Evaluator Library (NS-EEL) v2
  3. Copyright (C) 2004-2013 Cockos Incorporated
  4. Copyright (C) 1999-2003 Nullsoft, Inc.
  5. nseel-eval.c
  6. This software is provided 'as-is', without any express or implied
  7. warranty. In no event will the authors be held liable for any damages
  8. arising from the use of this software.
  9. Permission is granted to anyone to use this software for any purpose,
  10. including commercial applications, and to alter it and redistribute it
  11. freely, subject to the following restrictions:
  12. 1. The origin of this software must not be misrepresented; you must not
  13. claim that you wrote the original software. If you use this software
  14. in a product, an acknowledgment in the product documentation would be
  15. appreciated but is not required.
  16. 2. Altered source versions must be plainly marked as such, and must not be
  17. misrepresented as being the original software.
  18. 3. This notice may not be removed or altered from any source distribution.
  19. */
  20. #include <string.h>
  21. #include <ctype.h>
  22. #include "ns-eel-int.h"
  23. #include "wdlcstring.h"
  24. static const char *nseel_skip_space_and_comments(const char *p, const char *endptr)
  25. {
  26. for (;;)
  27. {
  28. while (p < endptr && isspace(p[0])) p++;
  29. if (p >= endptr-1 || *p != '/') return p;
  30. if (p[1]=='/')
  31. {
  32. while (p < endptr && *p != '\r' && *p != '\n') p++;
  33. }
  34. else if (p[1] == '*')
  35. {
  36. p+=2;
  37. while (p < endptr-1 && (p[0] != '*' || p[1] != '/')) p++;
  38. p+=2;
  39. if (p>=endptr) return endptr;
  40. }
  41. else return p;
  42. }
  43. }
  44. // removes any escaped characters, also will convert pairs delim_char into single delim_chars
  45. int nseel_filter_escaped_string(char *outbuf, int outbuf_sz, const char *rdptr, size_t rdptr_size, char delim_char)
  46. {
  47. int outpos = 0;
  48. const char *rdptr_end = rdptr + rdptr_size;
  49. while (rdptr < rdptr_end && outpos < outbuf_sz-1)
  50. {
  51. char thisc=*rdptr;
  52. if (thisc == '\\' && rdptr < rdptr_end-1)
  53. {
  54. const char nc = rdptr[1];
  55. if (nc == 'r' || nc == 'R') { thisc = '\r'; }
  56. else if (nc == 'n' || nc == 'N') { thisc = '\n'; }
  57. else if (nc == 't' || nc == 'T') { thisc = '\t'; }
  58. else if (nc == 'b' || nc == 'B') { thisc = '\b'; }
  59. else if ((nc >= '0' && nc <= '9') || nc == 'x' || nc == 'X')
  60. {
  61. unsigned char c=0;
  62. char base_shift = 3;
  63. char num_top = '7';
  64. rdptr++; // skip backslash
  65. if (nc > '9') // implies xX
  66. {
  67. base_shift = 4;
  68. num_top = '9';
  69. rdptr ++; // skip x
  70. }
  71. while (rdptr < rdptr_end)
  72. {
  73. char tc=*rdptr;
  74. if (tc >= '0' && tc <= num_top)
  75. {
  76. c = (c<<base_shift) + tc - '0';
  77. }
  78. else if (base_shift==4)
  79. {
  80. if (tc >= 'a' && tc <= 'f')
  81. {
  82. c = (c<<base_shift) + (tc - 'a' + 10);
  83. }
  84. else if (tc >= 'A' && tc <= 'F')
  85. {
  86. c = (c<<base_shift) + (tc - 'A' + 10);
  87. }
  88. else break;
  89. }
  90. else break;
  91. rdptr++;
  92. }
  93. outbuf[outpos++] = (char)c;
  94. continue;
  95. }
  96. else // \c where c is an unknown character drops the backslash -- works for \, ', ", etc
  97. {
  98. thisc = nc;
  99. }
  100. rdptr+=2;
  101. }
  102. else
  103. {
  104. if (thisc == delim_char) break;
  105. rdptr++;
  106. }
  107. outbuf[outpos++] = thisc;
  108. }
  109. outbuf[outpos]=0;
  110. return outpos;
  111. }
  112. int nseel_stringsegments_tobuf(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list) // call with NULL to calculate size, or non-null to generate to buffer (returning size used)
  113. {
  114. int pos=0;
  115. while (list)
  116. {
  117. if (!bufOut)
  118. {
  119. pos += list->str_len;
  120. }
  121. else if (list->str_len > 1)
  122. {
  123. if (pos >= bufout_sz) break;
  124. pos += nseel_filter_escaped_string(bufOut + pos, bufout_sz-pos, list->str_start+1, list->str_len-1, list->str_start[0]);
  125. }
  126. list = list->_next;
  127. }
  128. return pos;
  129. }
  130. // state can be NULL, it will be set if finished with unterminated thing: 1 for multiline comment, ' or " for string
  131. const char *nseel_simple_tokenizer(const char **ptr, const char *endptr, int *lenOut, int *state)
  132. {
  133. const char *p = *ptr;
  134. const char *rv = p;
  135. if (state) // if state set, returns comments as tokens
  136. {
  137. if (*state == 1) goto in_comment;
  138. #ifndef NSEEL_EEL1_COMPAT_MODE
  139. if (*state == '\'' || *state == '\"')
  140. {
  141. delim = (char)*state;
  142. goto in_string;
  143. }
  144. #endif
  145. // skip any whitespace
  146. while (p < endptr && isspace(p[0])) p++;
  147. }
  148. else
  149. {
  150. // state not passed, skip comments (do not return them as tokens)
  151. p = nseel_skip_space_and_comments(p,endptr);
  152. }
  153. if (p >= endptr)
  154. {
  155. *ptr = endptr;
  156. *lenOut = 0;
  157. return NULL;
  158. }
  159. rv=p;
  160. if (*p == '$' && p+3 < endptr && p[1] == '\'' && p[3] == '\'')
  161. {
  162. p+=4;
  163. }
  164. else if (state && *p == '/' && p < endptr-1 && (p[1] == '/' || p[1] == '*'))
  165. {
  166. if (p[1] == '/')
  167. {
  168. while (p < endptr && *p != '\r' && *p != '\n') p++; // advance to end of line
  169. }
  170. else
  171. {
  172. if (state) *state=1;
  173. p+=2;
  174. in_comment:
  175. while (p < endptr)
  176. {
  177. const char c = *p++;
  178. if (c == '*' && p < endptr && *p == '/')
  179. {
  180. p++;
  181. if (state) *state=0;
  182. break;
  183. }
  184. }
  185. }
  186. }
  187. else if (isalnum(*p) || *p == '_' || *p == '#' || *p == '$')
  188. {
  189. if (*p == '$' && p < endptr-1 && p[1] == '~') p++;
  190. p++;
  191. while (p < endptr && (isalnum(*p) || *p == '_' || *p == '.')) p++;
  192. }
  193. #ifndef NSEEL_EEL1_COMPAT_MODE
  194. else if (*p == '\'' || *p == '\"')
  195. {
  196. delim = *p++;
  197. if (state) *state=delim;
  198. in_string:
  199. while (p < endptr)
  200. {
  201. const char c = *p++;
  202. if (p < endptr && c == '\\') p++; // skip escaped characters
  203. else if (c == delim)
  204. {
  205. if (state) *state=0;
  206. break;
  207. }
  208. }
  209. }
  210. #endif
  211. else
  212. {
  213. p++;
  214. }
  215. *ptr = p;
  216. *lenOut = (int) (p - rv);
  217. return p>rv ? rv : NULL;
  218. }
  219. #ifdef NSEEL_SUPER_MINIMAL_LEXER
  220. int nseellex(opcodeRec **output, YYLTYPE * yylloc_param, compileContext *scctx)
  221. {
  222. int rv=0,toklen=0;
  223. const char *rdptr = scctx->rdbuf;
  224. const char *endptr = scctx->rdbuf_end;
  225. const char *tok = nseel_simple_tokenizer(&rdptr,endptr,&toklen,NULL);
  226. *output = 0;
  227. if (tok)
  228. {
  229. rv = tok[0];
  230. if (rv == '$')
  231. {
  232. if (rdptr != tok+1)
  233. {
  234. *output = nseel_translate(scctx,tok,rdptr-tok);
  235. if (*output) rv=VALUE;
  236. }
  237. }
  238. #ifndef NSEEL_EEL1_COMPAT_MODE
  239. else if (rv == '#' && scctx->onNamedString)
  240. {
  241. *output = nseel_translate(scctx,tok,rdptr-tok);
  242. if (*output) rv=STRING_IDENTIFIER;
  243. }
  244. else if (rv == '\'')
  245. {
  246. if (toklen > 1 && tok[toklen-1] == '\'')
  247. {
  248. *output = nseel_translate(scctx, tok, toklen);
  249. if (*output) rv = VALUE;
  250. }
  251. else scctx->gotEndOfInput|=8;
  252. }
  253. else if (rv == '\"' && scctx->onString)
  254. {
  255. if (toklen > 1 && tok[toklen-1] == '\"')
  256. {
  257. *output = (opcodeRec *)nseel_createStringSegmentRec(scctx,tok,toklen);
  258. if (*output) rv = STRING_LITERAL;
  259. }
  260. else scctx->gotEndOfInput|=16;
  261. }
  262. #endif
  263. else if (isalpha(rv) || rv == '_')
  264. {
  265. // toklen already valid
  266. char buf[NSEEL_MAX_VARIABLE_NAMELEN*2];
  267. if (toklen > sizeof(buf) - 1) toklen=sizeof(buf) - 1;
  268. memcpy(buf,tok,toklen);
  269. buf[toklen]=0;
  270. *output = nseel_createCompiledValuePtr(scctx, NULL, buf);
  271. if (*output) rv = IDENTIFIER;
  272. }
  273. else if ((rv >= '0' && rv <= '9') || (rv == '.' && (rdptr < endptr && rdptr[0] >= '0' && rdptr[0] <= '9')))
  274. {
  275. if (rv == '0' && rdptr < endptr && (rdptr[0] == 'x' || rdptr[0] == 'X'))
  276. {
  277. rdptr++;
  278. while (rdptr < endptr && (rv=rdptr[0]) && ((rv>='0' && rv<='9') || (rv>='a' && rv<='f') || (rv>='A' && rv<='F'))) rdptr++;
  279. }
  280. else
  281. {
  282. int pcnt=rv == '.';
  283. while (rdptr < endptr && (rv=rdptr[0]) && ((rv>='0' && rv<='9') || (rv == '.' && !pcnt++))) rdptr++;
  284. }
  285. *output = nseel_translate(scctx,tok,rdptr-tok);
  286. if (*output) rv=VALUE;
  287. }
  288. else if (rv == '<')
  289. {
  290. const char nc=*rdptr;
  291. if (nc == '<')
  292. {
  293. rdptr++;
  294. rv=TOKEN_SHL;
  295. }
  296. else if (nc == '=')
  297. {
  298. rdptr++;
  299. rv=TOKEN_LTE;
  300. }
  301. }
  302. else if (rv == '>')
  303. {
  304. const char nc=*rdptr;
  305. if (nc == '>')
  306. {
  307. rdptr++;
  308. rv=TOKEN_SHR;
  309. }
  310. else if (nc == '=')
  311. {
  312. rdptr++;
  313. rv=TOKEN_GTE;
  314. }
  315. }
  316. else if (rv == '&' && *rdptr == '&')
  317. {
  318. rdptr++;
  319. rv = TOKEN_LOGICAL_AND;
  320. }
  321. else if (rv == '|' && *rdptr == '|')
  322. {
  323. rdptr++;
  324. rv = TOKEN_LOGICAL_OR;
  325. }
  326. else if (*rdptr == '=')
  327. {
  328. switch (rv)
  329. {
  330. case '+': rv=TOKEN_ADD_OP; rdptr++; break;
  331. case '-': rv=TOKEN_SUB_OP; rdptr++; break;
  332. case '%': rv=TOKEN_MOD_OP; rdptr++; break;
  333. case '|': rv=TOKEN_OR_OP; rdptr++; break;
  334. case '&': rv=TOKEN_AND_OP; rdptr++; break;
  335. case '~': rv=TOKEN_XOR_OP; rdptr++; break;
  336. case '/': rv=TOKEN_DIV_OP; rdptr++; break;
  337. case '*': rv=TOKEN_MUL_OP; rdptr++; break;
  338. case '^': rv=TOKEN_POW_OP; rdptr++; break;
  339. case '!':
  340. rdptr++;
  341. if (rdptr < endptr && *rdptr == '=')
  342. {
  343. rdptr++;
  344. rv=TOKEN_NE_EXACT;
  345. }
  346. else
  347. rv=TOKEN_NE;
  348. break;
  349. case '=':
  350. rdptr++;
  351. if (rdptr < endptr && *rdptr == '=')
  352. {
  353. rdptr++;
  354. rv=TOKEN_EQ_EXACT;
  355. }
  356. else
  357. rv=TOKEN_EQ;
  358. break;
  359. }
  360. }
  361. }
  362. scctx->rdbuf = rdptr;
  363. yylloc_param->first_column = (int)(tok - scctx->rdbuf_start);
  364. return rv;
  365. }
  366. void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str)
  367. {
  368. ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start);
  369. }
  370. #else
  371. int nseel_gets(compileContext *ctx, char *buf, size_t sz)
  372. {
  373. int n=0;
  374. const char *endptr = ctx->rdbuf_end;
  375. const char *rdptr = ctx->rdbuf;
  376. if (!rdptr) return 0;
  377. while (n < sz && rdptr < endptr) buf[n++] = *rdptr++;
  378. ctx->rdbuf=rdptr;
  379. return n;
  380. }
  381. //#define EEL_TRACE_LEX
  382. #ifdef EEL_TRACE_LEX
  383. #define nseellex nseellex2
  384. #endif
  385. #include "lex.nseel.c"
  386. #ifdef EEL_TRACE_LEX
  387. #undef nseellex
  388. int nseellex(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
  389. {
  390. int a=nseellex2(yylval_param,yylloc_param,yyscanner);
  391. char buf[512];
  392. sprintf(buf,"tok: %c (%d)\n",a,a);
  393. OutputDebugString(buf);
  394. return a;
  395. }
  396. #endif//EEL_TRACE_LEX
  397. void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str)
  398. {
  399. ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start);
  400. }
  401. #endif // !NSEEL_SUPER_MINIMAL_LEXER