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