jscript: Pass HRESULTs instead of IDSs to throw_* functions.
[wine] / dlls / jscript / lex.c
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 <math.h>
20 #include <limits.h>
21
22 #include "jscript.h"
23 #include "activscp.h"
24 #include "objsafe.h"
25 #include "engine.h"
26
27 #include "parser.tab.h"
28
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
33
34 #define LONGLONG_MAX (((LONGLONG)0x7fffffff<<32)|0xffffffff)
35
36 static const WCHAR breakW[] = {'b','r','e','a','k',0};
37 static const WCHAR caseW[] = {'c','a','s','e',0};
38 static const WCHAR catchW[] = {'c','a','t','c','h',0};
39 static const WCHAR continueW[] = {'c','o','n','t','i','n','u','e',0};
40 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
41 static const WCHAR deleteW[] = {'d','e','l','e','t','e',0};
42 static const WCHAR doW[] = {'d','o',0};
43 static const WCHAR elseW[] = {'e','l','s','e',0};
44 static const WCHAR falseW[] = {'f','a','l','s','e',0};
45 static const WCHAR finallyW[] = {'f','i','n','a','l','l','y',0};
46 static const WCHAR forW[] = {'f','o','r',0};
47 static const WCHAR functionW[] = {'f','u','n','c','t','i','o','n',0};
48 static const WCHAR ifW[] = {'i','f',0};
49 static const WCHAR inW[] = {'i','n',0};
50 static const WCHAR instanceofW[] = {'i','n','s','t','a','n','c','e','o','f',0};
51 static const WCHAR newW[] = {'n','e','w',0};
52 static const WCHAR nullW[] = {'n','u','l','l',0};
53 static const WCHAR returnW[] = {'r','e','t','u','r','n',0};
54 static const WCHAR switchW[] = {'s','w','i','t','c','h',0};
55 static const WCHAR thisW[] = {'t','h','i','s',0};
56 static const WCHAR throwW[] = {'t','h','r','o','w',0};
57 static const WCHAR trueW[] = {'t','r','u','e',0};
58 static const WCHAR tryW[] = {'t','r','y',0};
59 static const WCHAR typeofW[] = {'t','y','p','e','o','f',0};
60 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0};
61 static const WCHAR varW[] = {'v','a','r',0};
62 static const WCHAR voidW[] = {'v','o','i','d',0};
63 static const WCHAR whileW[] = {'w','h','i','l','e',0};
64 static const WCHAR withW[] = {'w','i','t','h',0};
65
66 static const struct {
67     const WCHAR *word;
68     int token;
69 } keywords[] = {
70     {breakW,       kBREAK},
71     {caseW,        kCASE},
72     {catchW,       kCATCH},
73     {continueW,    kCONTINUE},
74     {defaultW,     kDEFAULT},
75     {deleteW,      kDELETE},
76     {doW,          kDO},
77     {elseW,        kELSE},
78     {falseW,       kFALSE},
79     {finallyW,     kFINALLY},
80     {forW,         kFOR},
81     {functionW,    kFUNCTION},
82     {ifW,          kIF},
83     {inW,          kIN},
84     {instanceofW,  kINSTANCEOF},
85     {newW,         kNEW},
86     {nullW,        kNULL},
87     {returnW,      kRETURN},
88     {switchW,      kSWITCH},
89     {thisW,        kTHIS},
90     {throwW,       kTHROW},
91     {trueW,        kTRUE},
92     {tryW,         kTRY},
93     {typeofW,      kTYPEOF},
94     {varW,         kVAR},
95     {voidW,        kVOID},
96     {whileW,       kWHILE},
97     {withW,        kWITH}
98 };
99
100 static int lex_error(parser_ctx_t *ctx, HRESULT hres)
101 {
102     ctx->hres = MAKE_JSERROR(hres);
103     ctx->lexer_error = TRUE;
104     return -1;
105 }
106
107 /* ECMA-262 3rd Edition    7.6 */
108 static BOOL is_identifier_char(WCHAR c)
109 {
110     return isalnumW(c) || c == '$' || c == '_' || c == '\\';
111 }
112
113 static int check_keyword(parser_ctx_t *ctx, const WCHAR *word, const WCHAR **lval)
114 {
115     const WCHAR *p1 = ctx->ptr;
116     const WCHAR *p2 = word;
117
118     while(p1 < ctx->end && *p2) {
119         if(*p1 != *p2)
120             return *p1 - *p2;
121         p1++;
122         p2++;
123     }
124
125     if(*p2 || (p1 < ctx->end && is_identifier_char(*p1)))
126         return 1;
127
128     *lval = ctx->ptr;
129     ctx->ptr = p1;
130     return 0;
131 }
132
133 /* ECMA-262 3rd Edition    7.3 */
134 static BOOL is_endline(WCHAR c)
135 {
136     return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
137 }
138
139 static int hex_to_int(WCHAR c)
140 {
141     if('0' <= c && c <= '9')
142         return c-'0';
143
144     if('a' <= c && c <= 'f')
145         return c-'a'+10;
146
147     if('A' <= c && c <= 'F')
148         return c-'A'+10;
149
150     return -1;
151 }
152
153 static int check_keywords(parser_ctx_t *ctx, const WCHAR **lval)
154 {
155     int min = 0, max = sizeof(keywords)/sizeof(keywords[0])-1, r, i;
156
157     while(min <= max) {
158         i = (min+max)/2;
159
160         r = check_keyword(ctx, keywords[i].word, lval);
161         if(!r)
162             return keywords[i].token;
163
164         if(r > 0)
165             min = i+1;
166         else
167             max = i-1;
168     }
169
170     return 0;
171 }
172
173 static void skip_spaces(parser_ctx_t *ctx)
174 {
175     while(ctx->ptr < ctx->end && isspaceW(*ctx->ptr)) {
176         if(is_endline(*ctx->ptr++))
177             ctx->nl = TRUE;
178     }
179 }
180
181 static BOOL skip_html_comment(parser_ctx_t *ctx)
182 {
183     const WCHAR html_commentW[] = {'<','!','-','-',0};
184
185     if(!ctx->is_html || ctx->ptr+3 >= ctx->end ||
186         memcmp(ctx->ptr, html_commentW, sizeof(WCHAR)*4))
187         return FALSE;
188
189     ctx->nl = TRUE;
190     while(ctx->ptr < ctx->end && !is_endline(*ctx->ptr++));
191
192     return TRUE;
193 }
194
195 static BOOL skip_comment(parser_ctx_t *ctx)
196 {
197     if(ctx->ptr+1 >= ctx->end || *ctx->ptr != '/')
198         return FALSE;
199
200     switch(ctx->ptr[1]) {
201     case '*':
202         ctx->ptr += 2;
203         while(ctx->ptr+1 < ctx->end && (ctx->ptr[0] != '*' || ctx->ptr[1] != '/'))
204             ctx->ptr++;
205
206         if(ctx->ptr[0] == '*' && ctx->ptr[1] == '/') {
207             ctx->ptr += 2;
208         }else {
209             WARN("unexpected end of file (missing end of comment)\n");
210             ctx->ptr = ctx->end;
211         }
212         break;
213     case '/':
214         ctx->ptr += 2;
215         while(ctx->ptr < ctx->end && !is_endline(*ctx->ptr))
216             ctx->ptr++;
217         break;
218     default:
219         return FALSE;
220     }
221
222     return TRUE;
223 }
224
225 static BOOL unescape(WCHAR *str)
226 {
227     WCHAR *pd, *p, c;
228     int i;
229
230     pd = p = str;
231     while(*p) {
232         if(*p != '\\') {
233             *pd++ = *p++;
234             continue;
235         }
236
237         p++;
238         c = 0;
239
240         switch(*p) {
241         case '\'':
242         case '\"':
243         case '\\':
244             c = *p;
245             break;
246         case 'b':
247             c = '\b';
248             break;
249         case 't':
250             c = '\t';
251             break;
252         case 'n':
253             c = '\n';
254             break;
255         case 'v':
256             c = '\v';
257             break;
258         case 'f':
259             c = '\f';
260             break;
261         case 'r':
262             c = '\r';
263             break;
264         case 'x':
265             i = hex_to_int(*++p);
266             if(i == -1)
267                 return FALSE;
268             c = i << 4;
269
270             i = hex_to_int(*++p);
271             if(i == -1)
272                 return FALSE;
273             c += i;
274             break;
275         case 'u':
276             i = hex_to_int(*++p);
277             if(i == -1)
278                 return FALSE;
279             c = i << 12;
280
281             i = hex_to_int(*++p);
282             if(i == -1)
283                 return FALSE;
284             c += i << 8;
285
286             i = hex_to_int(*++p);
287             if(i == -1)
288                 return FALSE;
289             c += i << 4;
290
291             i = hex_to_int(*++p);
292             if(i == -1)
293                 return FALSE;
294             c += i;
295             break;
296         default:
297             if(isdigitW(*p)) {
298                 c = *p++ - '0';
299                 if(isdigitW(*p)) {
300                     c = c*8 + (*p++ - '0');
301                     if(isdigitW(*p))
302                         c = c*8 + (*p++ - '0');
303                 }
304                 p--;
305             }
306             else
307                 c = *p;
308         }
309
310         *pd++ = c;
311         p++;
312     }
313
314     *pd = 0;
315     return TRUE;
316 }
317
318 static int parse_identifier(parser_ctx_t *ctx, const WCHAR **ret)
319 {
320     const WCHAR *ptr = ctx->ptr++;
321     WCHAR *wstr;
322     int len;
323
324     while(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr))
325         ctx->ptr++;
326
327     len = ctx->ptr-ptr;
328
329     *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR));
330     memcpy(wstr, ptr, (len+1)*sizeof(WCHAR));
331     wstr[len] = 0;
332
333     /* FIXME: unescape */
334     return tIdentifier;
335 }
336
337 static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret, WCHAR endch)
338 {
339     const WCHAR *ptr = ++ctx->ptr;
340     WCHAR *wstr;
341     int len;
342
343     while(ctx->ptr < ctx->end && *ctx->ptr != endch) {
344         if(*ctx->ptr++ == '\\')
345             ctx->ptr++;
346     }
347
348     if(ctx->ptr == ctx->end)
349         return lex_error(ctx, IDS_UNTERMINATED_STR);
350
351     len = ctx->ptr-ptr;
352
353     *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR));
354     memcpy(wstr, ptr, (len+1)*sizeof(WCHAR));
355     wstr[len] = 0;
356
357     ctx->ptr++;
358
359     if(!unescape(wstr)) {
360         WARN("unescape failed\n");
361         return lex_error(ctx, E_FAIL);
362     }
363
364     return tStringLiteral;
365 }
366
367 static literal_t *alloc_int_literal(parser_ctx_t *ctx, LONG l)
368 {
369     literal_t *ret = parser_alloc(ctx, sizeof(literal_t));
370
371     ret->type = LT_INT;
372     ret->u.lval = l;
373
374     return ret;
375 }
376
377 static int parse_double_literal(parser_ctx_t *ctx, LONG int_part, literal_t **literal)
378 {
379     LONGLONG d, hlp;
380     int exp = 0;
381
382     if(ctx->ptr == ctx->end || (!isdigitW(*ctx->ptr) &&
383         *ctx->ptr!='.' && *ctx->ptr!='e' && *ctx->ptr!='E')) {
384         ERR("Illegal character\n");
385         return 0;
386     }
387
388     d = int_part;
389     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
390         hlp = d*10 + *(ctx->ptr++) - '0';
391         if(d>LONGLONG_MAX/10 || hlp<0) {
392             exp++;
393             break;
394         }
395         else
396             d = hlp;
397     }
398     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
399         exp++;
400         ctx->ptr++;
401     }
402
403     if(*ctx->ptr == '.') ctx->ptr++;
404
405     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
406         hlp = d*10 + *(ctx->ptr++) - '0';
407         if(d>LONGLONG_MAX/10 || hlp<0)
408             break;
409
410         d = hlp;
411         exp--;
412     }
413     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr))
414         ctx->ptr++;
415
416     if(ctx->ptr < ctx->end && (*ctx->ptr == 'e' || *ctx->ptr == 'E')) {
417         int sign = 1, e = 0;
418
419         ctx->ptr++;
420         if(ctx->ptr < ctx->end) {
421             if(*ctx->ptr == '+') {
422                 ctx->ptr++;
423             }else if(*ctx->ptr == '-') {
424                 sign = -1;
425                 ctx->ptr++;
426             }else if(!isdigitW(*ctx->ptr)) {
427                 WARN("Expected exponent part\n");
428                 return lex_error(ctx, E_FAIL);
429             }
430         }
431
432         if(ctx->ptr == ctx->end) {
433             WARN("unexpected end of file\n");
434             return lex_error(ctx, E_FAIL);
435         }
436
437         while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
438             if(e > INT_MAX/10 || (e = e*10 + *ctx->ptr++ - '0')<0)
439                 e = INT_MAX;
440         }
441         e *= sign;
442
443         if(exp<0 && e<0 && e+exp>0) exp = INT_MIN;
444         else if(exp>0 && e>0 && e+exp<0) exp = INT_MAX;
445         else exp += e;
446     }
447
448     *literal = parser_alloc(ctx, sizeof(literal_t));
449     (*literal)->type = LT_DOUBLE;
450     (*literal)->u.dval = (double)d*pow(10, exp);
451
452     return tNumericLiteral;
453 }
454
455 static int parse_numeric_literal(parser_ctx_t *ctx, literal_t **literal)
456 {
457     LONG l, d;
458
459     l = *ctx->ptr++ - '0';
460     if(ctx->ptr == ctx->end) {
461         *literal = alloc_int_literal(ctx, l);
462         return tNumericLiteral;
463     }
464
465     if(!l) {
466         if(*ctx->ptr == 'x' || *ctx->ptr == 'X') {
467             if(++ctx->ptr == ctx->end) {
468                 ERR("unexpexted end of file\n");
469                 return 0;
470             }
471
472             while(ctx->ptr < ctx->end && (d = hex_to_int(*ctx->ptr)) != -1) {
473                 l = l*16 + d;
474                 ctx->ptr++;
475             }
476
477             if(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr)) {
478                 WARN("unexpected identifier char\n");
479                 return lex_error(ctx, E_FAIL);
480             }
481
482             *literal = alloc_int_literal(ctx, l);
483             return tNumericLiteral;
484         }
485
486         if(isdigitW(*ctx->ptr) || is_identifier_char(*ctx->ptr)) {
487             WARN("wrong char after zero\n");
488             return lex_error(ctx, E_FAIL);
489         }
490
491         *literal = alloc_int_literal(ctx, 0);
492     }
493
494     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr))
495     {
496         d = l*10 + *(ctx->ptr)-'0';
497
498         /* Check for integer overflow */
499         if (l > INT_MAX/10 || d < 0)
500             return parse_double_literal(ctx, l, literal);
501
502         l = d;
503         ctx->ptr++;
504     }
505
506     if(ctx->ptr < ctx->end) {
507         if(*ctx->ptr == '.' || *ctx->ptr == 'e' || *ctx->ptr == 'E')
508             return parse_double_literal(ctx, l, literal);
509
510         if(is_identifier_char(*ctx->ptr)) {
511             WARN("unexpected identifier char\n");
512             return lex_error(ctx, E_FAIL);
513         }
514     }
515
516     *literal = alloc_int_literal(ctx, l);
517     return tNumericLiteral;
518 }
519
520 int parser_lex(void *lval, parser_ctx_t *ctx)
521 {
522     int ret;
523
524     ctx->nl = ctx->ptr == ctx->begin;
525
526     do {
527         skip_spaces(ctx);
528         if(ctx->ptr == ctx->end)
529             return 0;
530     }while(skip_comment(ctx) || skip_html_comment(ctx));
531
532     if(isalphaW(*ctx->ptr)) {
533         ret = check_keywords(ctx, lval);
534         if(ret)
535             return ret;
536
537         return parse_identifier(ctx, lval);
538     }
539
540     if(isdigitW(*ctx->ptr))
541         return parse_numeric_literal(ctx, lval);
542
543     switch(*ctx->ptr) {
544     case '{':
545     case '(':
546     case ')':
547     case '[':
548     case ']':
549     case ';':
550     case ',':
551     case '~':
552     case '?':
553     case ':':
554         return *ctx->ptr++;
555
556     case '}':
557         *(const WCHAR**)lval = ctx->ptr++;
558         return '}';
559
560     case '.':
561         if(++ctx->ptr < ctx->end && isdigitW(*ctx->ptr))
562             return parse_double_literal(ctx, 0, lval);
563         return '.';
564
565     case '<':
566         if(++ctx->ptr == ctx->end) {
567             *(int*)lval = EXPR_LESS;
568             return tRelOper;
569         }
570
571         switch(*ctx->ptr) {
572         case '=':  /* <= */
573             ctx->ptr++;
574             *(int*)lval = EXPR_LESSEQ;
575             return tRelOper;
576         case '<':  /* << */
577             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* <<= */
578                 ctx->ptr++;
579                 *(int*)lval = EXPR_ASSIGNLSHIFT;
580                 return tAssignOper;
581             }
582             *(int*)lval = EXPR_LSHIFT;
583             return tShiftOper;
584         default: /* < */
585             *(int*)lval = EXPR_LESS;
586             return tRelOper;
587         }
588
589     case '>':
590         if(++ctx->ptr == ctx->end) { /* > */
591             *(int*)lval = EXPR_GREATER;
592             return tRelOper;
593         }
594
595         switch(*ctx->ptr) {
596         case '=':  /* >= */
597             ctx->ptr++;
598             *(int*)lval = EXPR_GREATEREQ;
599             return tRelOper;
600         case '>':  /* >> */
601             if(++ctx->ptr < ctx->end) {
602                 if(*ctx->ptr == '=') {  /* >>= */
603                     ctx->ptr++;
604                     *(int*)lval = EXPR_ASSIGNRSHIFT;
605                     return tAssignOper;
606                 }
607                 if(*ctx->ptr == '>') {  /* >>> */
608                     if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* >>>= */
609                         ctx->ptr++;
610                         *(int*)lval = EXPR_ASSIGNRRSHIFT;
611                         return tAssignOper;
612                     }
613                     *(int*)lval = EXPR_RRSHIFT;
614                     return tRelOper;
615                 }
616             }
617             *(int*)lval = EXPR_RSHIFT;
618             return tShiftOper;
619         default:
620             *(int*)lval = EXPR_GREATER;
621             return tRelOper;
622         }
623
624     case '+':
625         ctx->ptr++;
626         if(ctx->ptr < ctx->end) {
627             switch(*ctx->ptr) {
628             case '+':  /* ++ */
629                 ctx->ptr++;
630                 return tINC;
631             case '=':  /* += */
632                 ctx->ptr++;
633                 *(int*)lval = EXPR_ASSIGNADD;
634                 return tAssignOper;
635             }
636         }
637         return '+';
638
639     case '-':
640         ctx->ptr++;
641         if(ctx->ptr < ctx->end) {
642             switch(*ctx->ptr) {
643             case '-':  /* -- or --> */
644                 ctx->ptr++;
645                 if(ctx->is_html && ctx->nl && ctx->ptr < ctx->end && *ctx->ptr == '>') {
646                     ctx->ptr++;
647                     return tHTMLCOMMENT;
648                 }
649                 return tDEC;
650             case '=':  /* -= */
651                 ctx->ptr++;
652                 *(int*)lval = EXPR_ASSIGNSUB;
653                 return tAssignOper;
654             }
655         }
656         return '-';
657
658     case '*':
659         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* *= */
660             ctx->ptr++;
661             *(int*)lval = EXPR_ASSIGNMUL;
662             return tAssignOper;
663         }
664         return '*';
665
666     case '%':
667         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* %= */
668             ctx->ptr++;
669             *(int*)lval = EXPR_ASSIGNMOD;
670             return tAssignOper;
671         }
672         return '%';
673
674     case '&':
675         if(++ctx->ptr < ctx->end) {
676             switch(*ctx->ptr) {
677             case '=':  /* &= */
678                 ctx->ptr++;
679                 *(int*)lval = EXPR_ASSIGNAND;
680                 return tAssignOper;
681             case '&':  /* && */
682                 ctx->ptr++;
683                 return tANDAND;
684             }
685         }
686         return '&';
687
688     case '|':
689         if(++ctx->ptr < ctx->end) {
690             switch(*ctx->ptr) {
691             case '=':  /* |= */
692                 ctx->ptr++;
693                 *(int*)lval = EXPR_ASSIGNOR;
694                 return tAssignOper;
695             case '|':  /* || */
696                 ctx->ptr++;
697                 return tOROR;
698             }
699         }
700         return '|';
701
702     case '^':
703         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* ^= */
704             ctx->ptr++;
705             *(int*)lval = EXPR_ASSIGNXOR;
706             return tAssignOper;
707         }
708         return '^';
709
710     case '!':
711         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* != */
712             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* !== */
713                 ctx->ptr++;
714                 *(int*)lval = EXPR_NOTEQEQ;
715                 return tEqOper;
716             }
717             *(int*)lval = EXPR_NOTEQ;
718             return tEqOper;
719         }
720         return '!';
721
722     case '=':
723         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* == */
724             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* === */
725                 ctx->ptr++;
726                 *(int*)lval = EXPR_EQEQ;
727                 return tEqOper;
728             }
729             *(int*)lval = EXPR_EQ;
730             return tEqOper;
731         }
732         return '=';
733
734     case '/':
735         if(++ctx->ptr < ctx->end) {
736             if(*ctx->ptr == '=') {  /* /= */
737                 ctx->ptr++;
738                 *(int*)lval = EXPR_ASSIGNDIV;
739                 return kDIVEQ;
740             }
741         }
742         return '/';
743
744     case '\"':
745     case '\'':
746         return parse_string_literal(ctx, lval, *ctx->ptr);
747
748     case '_':
749     case '$':
750         return parse_identifier(ctx, lval);
751     }
752
753     WARN("unexpected char '%c' %d\n", *ctx->ptr, *ctx->ptr);
754     return 0;
755 }
756
757 literal_t *parse_regexp(parser_ctx_t *ctx)
758 {
759     const WCHAR *re, *flags_ptr;
760     DWORD re_len, flags;
761     literal_t *ret;
762     HRESULT hres;
763
764     TRACE("\n");
765
766     while(*ctx->ptr != '/')
767         ctx->ptr--;
768
769     re = ++ctx->ptr;
770     while(ctx->ptr < ctx->end && *ctx->ptr != '/') {
771         if(*ctx->ptr++ == '\\' && ctx->ptr < ctx->end)
772             ctx->ptr++;
773     }
774
775     if(ctx->ptr == ctx->end) {
776         WARN("unexpected end of file\n");
777         return NULL;
778     }
779
780     re_len = ctx->ptr-re;
781
782     flags_ptr = ++ctx->ptr;
783     while(ctx->ptr < ctx->end && isalnumW(*ctx->ptr))
784         ctx->ptr++;
785
786     hres = parse_regexp_flags(flags_ptr, ctx->ptr-flags_ptr, &flags);
787     if(FAILED(hres))
788         return NULL;
789
790     ret = parser_alloc(ctx, sizeof(literal_t));
791     ret->type = LT_REGEXP;
792     ret->u.regexp.str = re;
793     ret->u.regexp.str_len = re_len;
794     ret->u.regexp.flags = flags;
795     return ret;
796 }