xref: /reactos/dll/win32/jscript/lex.c (revision 19f6fc25)
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "config.h"
20 #include "wine/port.h"
21 
22 #include <limits.h>
23 
24 #include "jscript.h"
25 #include "activscp.h"
26 #include "objsafe.h"
27 #include "engine.h"
28 #include "parser.h"
29 
30 #include "parser.tab.h"
31 
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34 
35 #ifdef __REACTOS__
36 /* FIXME: Inspect - For some reason these exist in the generated header but are not picked up */
37 #define kGET (270)
38 #define kSET (272)
39 #endif
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
42 
43 static const WCHAR breakW[] = {'b','r','e','a','k',0};
44 static const WCHAR caseW[] = {'c','a','s','e',0};
45 static const WCHAR catchW[] = {'c','a','t','c','h',0};
46 static const WCHAR continueW[] = {'c','o','n','t','i','n','u','e',0};
47 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
48 static const WCHAR deleteW[] = {'d','e','l','e','t','e',0};
49 static const WCHAR doW[] = {'d','o',0};
50 static const WCHAR elseW[] = {'e','l','s','e',0};
51 static const WCHAR falseW[] = {'f','a','l','s','e',0};
52 static const WCHAR finallyW[] = {'f','i','n','a','l','l','y',0};
53 static const WCHAR forW[] = {'f','o','r',0};
54 static const WCHAR functionW[] = {'f','u','n','c','t','i','o','n',0};
55 static const WCHAR getW[] = {'g','e','t',0};
56 static const WCHAR ifW[] = {'i','f',0};
57 static const WCHAR inW[] = {'i','n',0};
58 static const WCHAR instanceofW[] = {'i','n','s','t','a','n','c','e','o','f',0};
59 static const WCHAR newW[] = {'n','e','w',0};
60 static const WCHAR nullW[] = {'n','u','l','l',0};
61 static const WCHAR returnW[] = {'r','e','t','u','r','n',0};
62 static const WCHAR setW[] = {'s','e','t',0};
63 static const WCHAR switchW[] = {'s','w','i','t','c','h',0};
64 static const WCHAR thisW[] = {'t','h','i','s',0};
65 static const WCHAR throwW[] = {'t','h','r','o','w',0};
66 static const WCHAR trueW[] = {'t','r','u','e',0};
67 static const WCHAR tryW[] = {'t','r','y',0};
68 static const WCHAR typeofW[] = {'t','y','p','e','o','f',0};
69 static const WCHAR varW[] = {'v','a','r',0};
70 static const WCHAR voidW[] = {'v','o','i','d',0};
71 static const WCHAR whileW[] = {'w','h','i','l','e',0};
72 static const WCHAR withW[] = {'w','i','t','h',0};
73 
74 static const WCHAR elifW[] = {'e','l','i','f',0};
75 static const WCHAR endW[] = {'e','n','d',0};
76 
77 static const struct {
78     const WCHAR *word;
79     int token;
80     BOOL no_nl;
81     unsigned min_version;
82 } keywords[] = {
83     {breakW,       kBREAK,       TRUE},
84     {caseW,        kCASE},
85     {catchW,       kCATCH},
86     {continueW,    kCONTINUE,    TRUE},
87     {defaultW,     kDEFAULT},
88     {deleteW,      kDELETE},
89     {doW,          kDO},
90     {elseW,        kELSE},
91     {falseW,       kFALSE},
92     {finallyW,     kFINALLY},
93     {forW,         kFOR},
94     {functionW,    kFUNCTION},
95     {getW,         kGET,         FALSE, SCRIPTLANGUAGEVERSION_ES5},
96     {ifW,          kIF},
97     {inW,          kIN},
98     {instanceofW,  kINSTANCEOF},
99     {newW,         kNEW},
100     {nullW,        kNULL},
101     {returnW,      kRETURN,      TRUE},
102     {setW,         kSET,         FALSE, SCRIPTLANGUAGEVERSION_ES5},
103     {switchW,      kSWITCH},
104     {thisW,        kTHIS},
105     {throwW,       kTHROW},
106     {trueW,        kTRUE},
107     {tryW,         kTRY},
108     {typeofW,      kTYPEOF},
109     {varW,         kVAR},
110     {voidW,        kVOID},
111     {whileW,       kWHILE},
112     {withW,        kWITH}
113 };
114 
115 static int lex_error(parser_ctx_t *ctx, HRESULT hres)
116 {
117     ctx->hres = hres;
118     ctx->lexer_error = TRUE;
119     return -1;
120 }
121 
122 /* ECMA-262 3rd Edition    7.6 */
123 BOOL is_identifier_char(WCHAR c)
124 {
125     return isalnumW(c) || c == '$' || c == '_' || c == '\\';
126 }
127 
128 static BOOL is_identifier_first_char(WCHAR c)
129 {
130     return isalphaW(c) || c == '$' || c == '_' || c == '\\';
131 }
132 
133 static int check_keyword(parser_ctx_t *ctx, const WCHAR *word, const WCHAR **lval)
134 {
135     const WCHAR *p1 = ctx->ptr;
136     const WCHAR *p2 = word;
137 
138     while(p1 < ctx->end && *p2) {
139         if(*p1 != *p2)
140             return *p1 - *p2;
141         p1++;
142         p2++;
143     }
144 
145     if(*p2 || (p1 < ctx->end && is_identifier_char(*p1)))
146         return 1;
147 
148     if(lval)
149         *lval = word;
150     ctx->ptr = p1;
151     return 0;
152 }
153 
154 /* ECMA-262 3rd Edition    7.3 */
155 static BOOL is_endline(WCHAR c)
156 {
157     return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
158 }
159 
160 static int hex_to_int(WCHAR c)
161 {
162     if('0' <= c && c <= '9')
163         return c-'0';
164 
165     if('a' <= c && c <= 'f')
166         return c-'a'+10;
167 
168     if('A' <= c && c <= 'F')
169         return c-'A'+10;
170 
171     return -1;
172 }
173 
174 static int check_keywords(parser_ctx_t *ctx, const WCHAR **lval)
175 {
176     int min = 0, max = ARRAY_SIZE(keywords)-1, r, i;
177 
178     while(min <= max) {
179         i = (min+max)/2;
180 
181         r = check_keyword(ctx, keywords[i].word, lval);
182         if(!r) {
183             if(ctx->script->version < keywords[i].min_version) {
184                 TRACE("ignoring keyword %s in incompatible mode\n",
185                       debugstr_w(keywords[i].word));
186                 ctx->ptr -= strlenW(keywords[i].word);
187                 return 0;
188             }
189             ctx->implicit_nl_semicolon = keywords[i].no_nl;
190             return keywords[i].token;
191         }
192 
193         if(r > 0)
194             min = i+1;
195         else
196             max = i-1;
197     }
198 
199     return 0;
200 }
201 
202 static BOOL skip_html_comment(parser_ctx_t *ctx)
203 {
204     const WCHAR html_commentW[] = {'<','!','-','-',0};
205 
206     if(!ctx->is_html || ctx->ptr+3 >= ctx->end ||
207         memcmp(ctx->ptr, html_commentW, sizeof(WCHAR)*4))
208         return FALSE;
209 
210     ctx->nl = TRUE;
211     while(ctx->ptr < ctx->end && !is_endline(*ctx->ptr++));
212 
213     return TRUE;
214 }
215 
216 static BOOL skip_comment(parser_ctx_t *ctx)
217 {
218     if(ctx->ptr+1 >= ctx->end)
219         return FALSE;
220 
221     if(*ctx->ptr != '/') {
222         if(*ctx->ptr == '@' && ctx->ptr+2 < ctx->end && ctx->ptr[1] == '*' && ctx->ptr[2] == '/') {
223             ctx->ptr += 3;
224             return TRUE;
225         }
226 
227         return FALSE;
228     }
229 
230     switch(ctx->ptr[1]) {
231     case '*':
232         ctx->ptr += 2;
233         if(ctx->ptr+2 < ctx->end && *ctx->ptr == '@' && is_identifier_char(ctx->ptr[1]))
234             return FALSE;
235         while(ctx->ptr+1 < ctx->end && (ctx->ptr[0] != '*' || ctx->ptr[1] != '/'))
236             ctx->ptr++;
237 
238         if(ctx->ptr[0] == '*' && ctx->ptr[1] == '/') {
239             ctx->ptr += 2;
240         }else {
241             WARN("unexpected end of file (missing end of comment)\n");
242             ctx->ptr = ctx->end;
243         }
244         break;
245     case '/':
246         ctx->ptr += 2;
247         if(ctx->ptr+2 < ctx->end && *ctx->ptr == '@' && is_identifier_char(ctx->ptr[1]))
248             return FALSE;
249         while(ctx->ptr < ctx->end && !is_endline(*ctx->ptr))
250             ctx->ptr++;
251         break;
252     default:
253         return FALSE;
254     }
255 
256     return TRUE;
257 }
258 
259 static BOOL skip_spaces(parser_ctx_t *ctx)
260 {
261     while(ctx->ptr < ctx->end && (isspaceW(*ctx->ptr) || *ctx->ptr == 0xFEFF /* UTF16 BOM */)) {
262         if(is_endline(*ctx->ptr++))
263             ctx->nl = TRUE;
264     }
265 
266     return ctx->ptr != ctx->end;
267 }
268 
269 BOOL unescape(WCHAR *str)
270 {
271     WCHAR *pd, *p, c;
272     int i;
273 
274     pd = p = str;
275     while(*p) {
276         if(*p != '\\') {
277             *pd++ = *p++;
278             continue;
279         }
280 
281         p++;
282 
283         switch(*p) {
284         case '\'':
285         case '\"':
286         case '\\':
287             c = *p;
288             break;
289         case 'b':
290             c = '\b';
291             break;
292         case 't':
293             c = '\t';
294             break;
295         case 'n':
296             c = '\n';
297             break;
298         case 'f':
299             c = '\f';
300             break;
301         case 'r':
302             c = '\r';
303             break;
304         case 'x':
305             i = hex_to_int(*++p);
306             if(i == -1)
307                 return FALSE;
308             c = i << 4;
309 
310             i = hex_to_int(*++p);
311             if(i == -1)
312                 return FALSE;
313             c += i;
314             break;
315         case 'u':
316             i = hex_to_int(*++p);
317             if(i == -1)
318                 return FALSE;
319             c = i << 12;
320 
321             i = hex_to_int(*++p);
322             if(i == -1)
323                 return FALSE;
324             c += i << 8;
325 
326             i = hex_to_int(*++p);
327             if(i == -1)
328                 return FALSE;
329             c += i << 4;
330 
331             i = hex_to_int(*++p);
332             if(i == -1)
333                 return FALSE;
334             c += i;
335             break;
336         default:
337             if(isdigitW(*p)) {
338                 c = *p++ - '0';
339                 if(isdigitW(*p)) {
340                     c = c*8 + (*p++ - '0');
341                     if(isdigitW(*p))
342                         c = c*8 + (*p++ - '0');
343                 }
344                 p--;
345             }
346             else
347                 c = *p;
348         }
349 
350         *pd++ = c;
351         p++;
352     }
353 
354     *pd = 0;
355     return TRUE;
356 }
357 
358 static int parse_identifier(parser_ctx_t *ctx, const WCHAR **ret)
359 {
360     const WCHAR *ptr = ctx->ptr++;
361     WCHAR *wstr;
362     int len;
363 
364     while(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr))
365         ctx->ptr++;
366 
367     len = ctx->ptr-ptr;
368 
369     *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR));
370     memcpy(wstr, ptr, len*sizeof(WCHAR));
371     wstr[len] = 0;
372 
373     /* FIXME: unescape */
374     return tIdentifier;
375 }
376 
377 static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret, WCHAR endch)
378 {
379     const WCHAR *ptr = ++ctx->ptr;
380     WCHAR *wstr;
381     int len;
382 
383     while(ctx->ptr < ctx->end && *ctx->ptr != endch) {
384         if(*ctx->ptr++ == '\\')
385             ctx->ptr++;
386     }
387 
388     if(ctx->ptr == ctx->end)
389         return lex_error(ctx, JS_E_UNTERMINATED_STRING);
390 
391     len = ctx->ptr-ptr;
392 
393     *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR));
394     memcpy(wstr, ptr, len*sizeof(WCHAR));
395     wstr[len] = 0;
396 
397     ctx->ptr++;
398 
399     if(!unescape(wstr)) {
400         WARN("unescape failed\n");
401         return lex_error(ctx, E_FAIL);
402     }
403 
404     return tStringLiteral;
405 }
406 
407 static literal_t *new_double_literal(parser_ctx_t *ctx, DOUBLE d)
408 {
409     literal_t *ret = parser_alloc(ctx, sizeof(literal_t));
410 
411     ret->type = LT_DOUBLE;
412     ret->u.dval = d;
413     return ret;
414 }
415 
416 literal_t *new_boolean_literal(parser_ctx_t *ctx, BOOL bval)
417 {
418     literal_t *ret = parser_alloc(ctx, sizeof(literal_t));
419 
420     ret->type = LT_BOOL;
421     ret->u.bval = bval;
422 
423     return ret;
424 }
425 
426 HRESULT parse_decimal(const WCHAR **iter, const WCHAR *end, double *ret)
427 {
428     const WCHAR *ptr = *iter;
429     LONGLONG d = 0, hlp;
430     int exp = 0;
431 
432     while(ptr < end && isdigitW(*ptr)) {
433         hlp = d*10 + *(ptr++) - '0';
434         if(d>MAXLONGLONG/10 || hlp<0) {
435             exp++;
436             break;
437         }
438         else
439             d = hlp;
440     }
441     while(ptr < end && isdigitW(*ptr)) {
442         exp++;
443         ptr++;
444     }
445 
446     if(*ptr == '.') {
447         ptr++;
448 
449         while(ptr < end && isdigitW(*ptr)) {
450             hlp = d*10 + *(ptr++) - '0';
451             if(d>MAXLONGLONG/10 || hlp<0)
452                 break;
453 
454             d = hlp;
455             exp--;
456         }
457         while(ptr < end && isdigitW(*ptr))
458             ptr++;
459     }
460 
461     if(ptr < end && (*ptr == 'e' || *ptr == 'E')) {
462         int sign = 1, e = 0;
463 
464         if(++ptr < end) {
465             if(*ptr == '+') {
466                 ptr++;
467             }else if(*ptr == '-') {
468                 sign = -1;
469                 ptr++;
470             }else if(!isdigitW(*ptr)) {
471                 WARN("Expected exponent part\n");
472                 return E_FAIL;
473             }
474         }
475 
476         if(ptr == end) {
477             WARN("unexpected end of file\n");
478             return E_FAIL;
479         }
480 
481         while(ptr < end && isdigitW(*ptr)) {
482             if(e > INT_MAX/10 || (e = e*10 + *ptr++ - '0')<0)
483                 e = INT_MAX;
484         }
485         e *= sign;
486 
487         if(exp<0 && e<0 && e+exp>0) exp = INT_MIN;
488         else if(exp>0 && e>0 && e+exp<0) exp = INT_MAX;
489         else exp += e;
490     }
491 
492     if(is_identifier_char(*ptr)) {
493         WARN("wrong char after zero\n");
494         return JS_E_MISSING_SEMICOLON;
495     }
496 
497     *ret = exp>=0 ? d*pow(10, exp) : d/pow(10, -exp);
498     *iter = ptr;
499     return S_OK;
500 }
501 
502 static BOOL parse_numeric_literal(parser_ctx_t *ctx, double *ret)
503 {
504     HRESULT hres;
505 
506     if(*ctx->ptr == '0') {
507         ctx->ptr++;
508 
509         if(*ctx->ptr == 'x' || *ctx->ptr == 'X') {
510             double r = 0;
511             int d;
512             if(++ctx->ptr == ctx->end) {
513                 ERR("unexpected end of file\n");
514                 return FALSE;
515             }
516 
517             while(ctx->ptr < ctx->end && (d = hex_to_int(*ctx->ptr)) != -1) {
518                 r = r*16 + d;
519                 ctx->ptr++;
520             }
521 
522             if(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr)) {
523                 WARN("unexpected identifier char\n");
524                 lex_error(ctx, JS_E_MISSING_SEMICOLON);
525                 return FALSE;
526             }
527 
528             *ret = r;
529             return TRUE;
530         }
531 
532         if(isdigitW(*ctx->ptr)) {
533             unsigned base = 8;
534             const WCHAR *ptr;
535             double val = 0;
536 
537             for(ptr = ctx->ptr; ptr < ctx->end && isdigitW(*ptr); ptr++) {
538                 if(*ptr > '7') {
539                     base = 10;
540                     break;
541                 }
542             }
543 
544             do {
545                 val = val*base + *ctx->ptr-'0';
546             }while(++ctx->ptr < ctx->end && isdigitW(*ctx->ptr));
547 
548             /* FIXME: Do we need it here? */
549             if(ctx->ptr < ctx->end && (is_identifier_char(*ctx->ptr) || *ctx->ptr == '.')) {
550                 WARN("wrong char after octal literal: '%c'\n", *ctx->ptr);
551                 lex_error(ctx, JS_E_MISSING_SEMICOLON);
552                 return FALSE;
553             }
554 
555             *ret = val;
556             return TRUE;
557         }
558 
559         if(is_identifier_char(*ctx->ptr)) {
560             WARN("wrong char after zero\n");
561             lex_error(ctx, JS_E_MISSING_SEMICOLON);
562             return FALSE;
563         }
564     }
565 
566     hres = parse_decimal(&ctx->ptr, ctx->end, ret);
567     if(FAILED(hres)) {
568         lex_error(ctx, hres);
569         return FALSE;
570     }
571 
572     return TRUE;
573 }
574 
575 static int next_token(parser_ctx_t *ctx, void *lval)
576 {
577     do {
578         if(!skip_spaces(ctx))
579             return tEOF;
580     }while(skip_comment(ctx) || skip_html_comment(ctx));
581 
582     if(ctx->implicit_nl_semicolon) {
583         if(ctx->nl)
584             return ';';
585         ctx->implicit_nl_semicolon = FALSE;
586     }
587 
588     if(isalphaW(*ctx->ptr)) {
589         int ret = check_keywords(ctx, lval);
590         if(ret)
591             return ret;
592 
593         return parse_identifier(ctx, lval);
594     }
595 
596     if(isdigitW(*ctx->ptr)) {
597         double n;
598 
599         if(!parse_numeric_literal(ctx, &n))
600             return -1;
601 
602         *(literal_t**)lval = new_double_literal(ctx, n);
603         return tNumericLiteral;
604     }
605 
606     switch(*ctx->ptr) {
607     case '{':
608     case '(':
609     case ')':
610     case '[':
611     case ']':
612     case ';':
613     case ',':
614     case '~':
615     case '?':
616         return *ctx->ptr++;
617 
618     case '}':
619         *(const WCHAR**)lval = ctx->ptr++;
620         return '}';
621 
622     case '.':
623         if(ctx->ptr+1 < ctx->end && isdigitW(ctx->ptr[1])) {
624             double n;
625             HRESULT hres;
626             hres = parse_decimal(&ctx->ptr, ctx->end, &n);
627             if(FAILED(hres)) {
628                 lex_error(ctx, hres);
629                 return -1;
630             }
631             *(literal_t**)lval = new_double_literal(ctx, n);
632             return tNumericLiteral;
633         }
634         ctx->ptr++;
635         return '.';
636 
637     case '<':
638         if(++ctx->ptr == ctx->end) {
639             *(int*)lval = EXPR_LESS;
640             return tRelOper;
641         }
642 
643         switch(*ctx->ptr) {
644         case '=':  /* <= */
645             ctx->ptr++;
646             *(int*)lval = EXPR_LESSEQ;
647             return tRelOper;
648         case '<':  /* << */
649             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* <<= */
650                 ctx->ptr++;
651                 *(int*)lval = EXPR_ASSIGNLSHIFT;
652                 return tAssignOper;
653             }
654             *(int*)lval = EXPR_LSHIFT;
655             return tShiftOper;
656         default: /* < */
657             *(int*)lval = EXPR_LESS;
658             return tRelOper;
659         }
660 
661     case '>':
662         if(++ctx->ptr == ctx->end) { /* > */
663             *(int*)lval = EXPR_GREATER;
664             return tRelOper;
665         }
666 
667         switch(*ctx->ptr) {
668         case '=':  /* >= */
669             ctx->ptr++;
670             *(int*)lval = EXPR_GREATEREQ;
671             return tRelOper;
672         case '>':  /* >> */
673             if(++ctx->ptr < ctx->end) {
674                 if(*ctx->ptr == '=') {  /* >>= */
675                     ctx->ptr++;
676                     *(int*)lval = EXPR_ASSIGNRSHIFT;
677                     return tAssignOper;
678                 }
679                 if(*ctx->ptr == '>') {  /* >>> */
680                     if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* >>>= */
681                         ctx->ptr++;
682                         *(int*)lval = EXPR_ASSIGNRRSHIFT;
683                         return tAssignOper;
684                     }
685                     *(int*)lval = EXPR_RRSHIFT;
686                     return tRelOper;
687                 }
688             }
689             *(int*)lval = EXPR_RSHIFT;
690             return tShiftOper;
691         default:
692             *(int*)lval = EXPR_GREATER;
693             return tRelOper;
694         }
695 
696     case '+':
697         ctx->ptr++;
698         if(ctx->ptr < ctx->end) {
699             switch(*ctx->ptr) {
700             case '+':  /* ++ */
701                 ctx->ptr++;
702                 return tINC;
703             case '=':  /* += */
704                 ctx->ptr++;
705                 *(int*)lval = EXPR_ASSIGNADD;
706                 return tAssignOper;
707             }
708         }
709         return '+';
710 
711     case '-':
712         ctx->ptr++;
713         if(ctx->ptr < ctx->end) {
714             switch(*ctx->ptr) {
715             case '-':  /* -- or --> */
716                 ctx->ptr++;
717                 if(ctx->is_html && ctx->nl && ctx->ptr < ctx->end && *ctx->ptr == '>') {
718                     ctx->ptr++;
719                     return tHTMLCOMMENT;
720                 }
721                 return tDEC;
722             case '=':  /* -= */
723                 ctx->ptr++;
724                 *(int*)lval = EXPR_ASSIGNSUB;
725                 return tAssignOper;
726             }
727         }
728         return '-';
729 
730     case '*':
731         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* *= */
732             ctx->ptr++;
733             *(int*)lval = EXPR_ASSIGNMUL;
734             return tAssignOper;
735         }
736         return '*';
737 
738     case '%':
739         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* %= */
740             ctx->ptr++;
741             *(int*)lval = EXPR_ASSIGNMOD;
742             return tAssignOper;
743         }
744         return '%';
745 
746     case '&':
747         if(++ctx->ptr < ctx->end) {
748             switch(*ctx->ptr) {
749             case '=':  /* &= */
750                 ctx->ptr++;
751                 *(int*)lval = EXPR_ASSIGNAND;
752                 return tAssignOper;
753             case '&':  /* && */
754                 ctx->ptr++;
755                 return tANDAND;
756             }
757         }
758         return '&';
759 
760     case '|':
761         if(++ctx->ptr < ctx->end) {
762             switch(*ctx->ptr) {
763             case '=':  /* |= */
764                 ctx->ptr++;
765                 *(int*)lval = EXPR_ASSIGNOR;
766                 return tAssignOper;
767             case '|':  /* || */
768                 ctx->ptr++;
769                 return tOROR;
770             }
771         }
772         return '|';
773 
774     case '^':
775         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* ^= */
776             ctx->ptr++;
777             *(int*)lval = EXPR_ASSIGNXOR;
778             return tAssignOper;
779         }
780         return '^';
781 
782     case '!':
783         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* != */
784             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* !== */
785                 ctx->ptr++;
786                 *(int*)lval = EXPR_NOTEQEQ;
787                 return tEqOper;
788             }
789             *(int*)lval = EXPR_NOTEQ;
790             return tEqOper;
791         }
792         return '!';
793 
794     case '=':
795         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* == */
796             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* === */
797                 ctx->ptr++;
798                 *(int*)lval = EXPR_EQEQ;
799                 return tEqOper;
800             }
801             *(int*)lval = EXPR_EQ;
802             return tEqOper;
803         }
804         return '=';
805 
806     case '/':
807         if(++ctx->ptr < ctx->end) {
808             if(*ctx->ptr == '=') {  /* /= */
809                 ctx->ptr++;
810                 *(int*)lval = EXPR_ASSIGNDIV;
811                 return kDIVEQ;
812             }
813         }
814         return '/';
815 
816     case ':':
817         if(++ctx->ptr < ctx->end && *ctx->ptr == ':') {
818             ctx->ptr++;
819             return kDCOL;
820         }
821         return ':';
822 
823     case '\"':
824     case '\'':
825         return parse_string_literal(ctx, lval, *ctx->ptr);
826 
827     case '_':
828     case '$':
829         return parse_identifier(ctx, lval);
830 
831     case '@':
832         return '@';
833     }
834 
835     WARN("unexpected char '%c' %d\n", *ctx->ptr, *ctx->ptr);
836     return 0;
837 }
838 
839 struct _cc_var_t {
840     ccval_t val;
841     struct _cc_var_t *next;
842     unsigned name_len;
843     WCHAR name[0];
844 };
845 
846 void release_cc(cc_ctx_t *cc)
847 {
848     cc_var_t *iter, *next;
849 
850     for(iter = cc->vars; iter; iter = next) {
851         next = iter->next;
852         heap_free(iter);
853     }
854 
855     heap_free(cc);
856 }
857 
858 static BOOL new_cc_var(cc_ctx_t *cc, const WCHAR *name, int len, ccval_t v)
859 {
860     cc_var_t *new_v;
861 
862     if(len == -1)
863         len = strlenW(name);
864 
865     new_v = heap_alloc(sizeof(cc_var_t) + (len+1)*sizeof(WCHAR));
866     if(!new_v)
867         return FALSE;
868 
869     new_v->val = v;
870     memcpy(new_v->name, name, (len+1)*sizeof(WCHAR));
871     new_v->name_len = len;
872     new_v->next = cc->vars;
873     cc->vars = new_v;
874     return TRUE;
875 }
876 
877 static cc_var_t *find_cc_var(cc_ctx_t *cc, const WCHAR *name, unsigned name_len)
878 {
879     cc_var_t *iter;
880 
881     for(iter = cc->vars; iter; iter = iter->next) {
882         if(iter->name_len == name_len && !memcmp(iter->name, name, name_len*sizeof(WCHAR)))
883             return iter;
884     }
885 
886     return NULL;
887 }
888 
889 static BOOL init_cc(parser_ctx_t *ctx)
890 {
891     cc_ctx_t *cc;
892 
893     static const WCHAR _win32W[] = {'_','w','i','n','3','2',0};
894     static const WCHAR _win64W[] = {'_','w','i','n','6','4',0};
895     static const WCHAR _x86W[] = {'_','x','8','6',0};
896     static const WCHAR _amd64W[] = {'_','a','m','d','6','4',0};
897     static const WCHAR _jscriptW[] = {'_','j','s','c','r','i','p','t',0};
898     static const WCHAR _jscript_buildW[] = {'_','j','s','c','r','i','p','t','_','b','u','i','l','d',0};
899     static const WCHAR _jscript_versionW[] = {'_','j','s','c','r','i','p','t','_','v','e','r','s','i','o','n',0};
900 
901     if(ctx->script->cc)
902         return TRUE;
903 
904     cc = heap_alloc(sizeof(cc_ctx_t));
905     if(!cc) {
906         lex_error(ctx, E_OUTOFMEMORY);
907         return FALSE;
908     }
909 
910     cc->vars = NULL;
911 
912     if(!new_cc_var(cc, _jscriptW, -1, ccval_bool(TRUE))
913        || !new_cc_var(cc, sizeof(void*) == 8 ? _win64W : _win32W, -1, ccval_bool(TRUE))
914        || !new_cc_var(cc, sizeof(void*) == 8 ? _amd64W : _x86W, -1, ccval_bool(TRUE))
915        || !new_cc_var(cc, _jscript_versionW, -1, ccval_num(JSCRIPT_MAJOR_VERSION + (DOUBLE)JSCRIPT_MINOR_VERSION/10.0))
916        || !new_cc_var(cc, _jscript_buildW, -1, ccval_num(JSCRIPT_BUILD_VERSION))) {
917         release_cc(cc);
918         lex_error(ctx, E_OUTOFMEMORY);
919         return FALSE;
920     }
921 
922     ctx->script->cc = cc;
923     return TRUE;
924 }
925 
926 static BOOL parse_cc_identifier(parser_ctx_t *ctx, const WCHAR **ret, unsigned *ret_len)
927 {
928     if(*ctx->ptr != '@') {
929         lex_error(ctx, JS_E_EXPECTED_AT);
930         return FALSE;
931     }
932 
933     if(!is_identifier_first_char(*++ctx->ptr)) {
934         lex_error(ctx, JS_E_EXPECTED_IDENTIFIER);
935         return FALSE;
936     }
937 
938     *ret = ctx->ptr;
939     while(++ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr));
940     *ret_len = ctx->ptr - *ret;
941     return TRUE;
942 }
943 
944 int try_parse_ccval(parser_ctx_t *ctx, ccval_t *r)
945 {
946     if(!skip_spaces(ctx))
947         return -1;
948 
949     if(isdigitW(*ctx->ptr)) {
950         double n;
951 
952         if(!parse_numeric_literal(ctx, &n))
953             return -1;
954 
955         *r = ccval_num(n);
956         return 1;
957     }
958 
959     if(*ctx->ptr == '@') {
960         const WCHAR *ident;
961         unsigned ident_len;
962         cc_var_t *cc_var;
963 
964         if(!parse_cc_identifier(ctx, &ident, &ident_len))
965             return -1;
966 
967         cc_var = find_cc_var(ctx->script->cc, ident, ident_len);
968         *r = cc_var ? cc_var->val : ccval_num(NAN);
969         return 1;
970     }
971 
972     if(!check_keyword(ctx, trueW, NULL)) {
973         *r = ccval_bool(TRUE);
974         return 1;
975     }
976 
977     if(!check_keyword(ctx, falseW, NULL)) {
978         *r = ccval_bool(FALSE);
979         return 1;
980     }
981 
982     return 0;
983 }
984 
985 static int skip_code(parser_ctx_t *ctx, BOOL exec_else)
986 {
987     int if_depth = 1;
988     const WCHAR *ptr;
989 
990     while(1) {
991         ptr = strchrW(ctx->ptr, '@');
992         if(!ptr) {
993             WARN("No @end\n");
994             return lex_error(ctx, JS_E_EXPECTED_CCEND);
995         }
996         ctx->ptr = ptr+1;
997 
998         if(!check_keyword(ctx, endW, NULL)) {
999             if(--if_depth)
1000                 continue;
1001             return 0;
1002         }
1003 
1004         if(exec_else && !check_keyword(ctx, elifW, NULL)) {
1005             if(if_depth > 1)
1006                 continue;
1007 
1008             if(!skip_spaces(ctx) || *ctx->ptr != '(')
1009                 return lex_error(ctx, JS_E_MISSING_LBRACKET);
1010 
1011             if(!parse_cc_expr(ctx))
1012                 return -1;
1013 
1014             if(!get_ccbool(ctx->ccval))
1015                 continue; /* skip block of code */
1016 
1017             /* continue parsing */
1018             ctx->cc_if_depth++;
1019             return 0;
1020         }
1021 
1022         if(exec_else && !check_keyword(ctx, elseW, NULL)) {
1023             if(if_depth > 1)
1024                 continue;
1025 
1026             /* parse else block */
1027             ctx->cc_if_depth++;
1028             return 0;
1029         }
1030 
1031         if(!check_keyword(ctx, ifW, NULL)) {
1032             if_depth++;
1033             continue;
1034         }
1035 
1036         ctx->ptr++;
1037     }
1038 }
1039 
1040 static int cc_token(parser_ctx_t *ctx, void *lval)
1041 {
1042     unsigned id_len = 0;
1043     cc_var_t *var;
1044 
1045     static const WCHAR cc_onW[] = {'c','c','_','o','n',0};
1046     static const WCHAR setW[] = {'s','e','t',0};
1047 
1048     ctx->ptr++;
1049 
1050     if(!check_keyword(ctx, cc_onW, NULL))
1051         return init_cc(ctx) ? 0 : -1;
1052 
1053     if(!check_keyword(ctx, setW, NULL)) {
1054         const WCHAR *ident;
1055         unsigned ident_len;
1056         cc_var_t *var;
1057 
1058         if(!init_cc(ctx))
1059             return -1;
1060 
1061         if(!skip_spaces(ctx))
1062             return lex_error(ctx, JS_E_EXPECTED_AT);
1063 
1064         if(!parse_cc_identifier(ctx, &ident, &ident_len))
1065             return -1;
1066 
1067         if(!skip_spaces(ctx) || *ctx->ptr != '=')
1068             return lex_error(ctx, JS_E_EXPECTED_ASSIGN);
1069         ctx->ptr++;
1070 
1071         if(!parse_cc_expr(ctx)) {
1072             WARN("parsing CC expression failed\n");
1073             return -1;
1074         }
1075 
1076         var = find_cc_var(ctx->script->cc, ident, ident_len);
1077         if(var) {
1078             var->val = ctx->ccval;
1079         }else {
1080             if(!new_cc_var(ctx->script->cc, ident, ident_len, ctx->ccval))
1081                 return lex_error(ctx, E_OUTOFMEMORY);
1082         }
1083 
1084         return 0;
1085     }
1086 
1087     if(!check_keyword(ctx, ifW, NULL)) {
1088         if(!init_cc(ctx))
1089             return -1;
1090 
1091         if(!skip_spaces(ctx) || *ctx->ptr != '(')
1092             return lex_error(ctx, JS_E_MISSING_LBRACKET);
1093 
1094         if(!parse_cc_expr(ctx))
1095             return -1;
1096 
1097         if(get_ccbool(ctx->ccval)) {
1098             /* continue parsing block inside if */
1099             ctx->cc_if_depth++;
1100             return 0;
1101         }
1102 
1103         return skip_code(ctx, TRUE);
1104     }
1105 
1106     if(!check_keyword(ctx, elifW, NULL) || !check_keyword(ctx, elseW, NULL)) {
1107         if(!ctx->cc_if_depth)
1108             return lex_error(ctx, JS_E_SYNTAX);
1109 
1110         return skip_code(ctx, FALSE);
1111     }
1112 
1113     if(!check_keyword(ctx, endW, NULL)) {
1114         if(!ctx->cc_if_depth)
1115             return lex_error(ctx, JS_E_SYNTAX);
1116 
1117         ctx->cc_if_depth--;
1118         return 0;
1119     }
1120 
1121     if(!ctx->script->cc)
1122         return lex_error(ctx, JS_E_DISABLED_CC);
1123 
1124     while(ctx->ptr+id_len < ctx->end && is_identifier_char(ctx->ptr[id_len]))
1125         id_len++;
1126     if(!id_len)
1127         return '@';
1128 
1129     TRACE("var %s\n", debugstr_wn(ctx->ptr, id_len));
1130 
1131     var = find_cc_var(ctx->script->cc, ctx->ptr, id_len);
1132     ctx->ptr += id_len;
1133     if(!var || var->val.is_num) {
1134         *(literal_t**)lval = new_double_literal(ctx, var ? var->val.u.n : NAN);
1135         return tNumericLiteral;
1136     }
1137 
1138     *(literal_t**)lval = new_boolean_literal(ctx, var->val.u.b);
1139     return tBooleanLiteral;
1140 }
1141 
1142 int parser_lex(void *lval, parser_ctx_t *ctx)
1143 {
1144     int ret;
1145 
1146     ctx->nl = ctx->ptr == ctx->begin;
1147 
1148     do {
1149         ret = next_token(ctx, lval);
1150     } while(ret == '@' && !(ret = cc_token(ctx, lval)));
1151 
1152     return ret;
1153 }
1154 
1155 literal_t *parse_regexp(parser_ctx_t *ctx)
1156 {
1157     const WCHAR *re, *flags_ptr;
1158     BOOL in_class = FALSE;
1159     DWORD re_len, flags;
1160     literal_t *ret;
1161     HRESULT hres;
1162 
1163     TRACE("\n");
1164 
1165     while(*--ctx->ptr != '/');
1166 
1167     /* Simple regexp pre-parser; '/' if used in char class does not terminate regexp literal */
1168     re = ++ctx->ptr;
1169     while(ctx->ptr < ctx->end) {
1170         if(*ctx->ptr == '\\') {
1171             if(++ctx->ptr == ctx->end)
1172                 break;
1173         }else if(in_class) {
1174             if(*ctx->ptr == '\n')
1175                 break;
1176             if(*ctx->ptr == ']')
1177                 in_class = FALSE;
1178         }else {
1179             if(*ctx->ptr == '/')
1180                 break;
1181 
1182             if(*ctx->ptr == '[')
1183                 in_class = TRUE;
1184         }
1185         ctx->ptr++;
1186     }
1187 
1188     if(ctx->ptr == ctx->end || *ctx->ptr != '/') {
1189         WARN("pre-parsing failed\n");
1190         return NULL;
1191     }
1192 
1193     re_len = ctx->ptr-re;
1194 
1195     flags_ptr = ++ctx->ptr;
1196     while(ctx->ptr < ctx->end && isalnumW(*ctx->ptr))
1197         ctx->ptr++;
1198 
1199     hres = parse_regexp_flags(flags_ptr, ctx->ptr-flags_ptr, &flags);
1200     if(FAILED(hres))
1201         return NULL;
1202 
1203     ret = parser_alloc(ctx, sizeof(literal_t));
1204     ret->type = LT_REGEXP;
1205     ret->u.regexp.str = re;
1206     ret->u.regexp.str_len = re_len;
1207     ret->u.regexp.flags = flags;
1208     return ret;
1209 }
1210