jscript: Fixed 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 = JSCRIPT_ERROR|hres;
104     ctx->lexer_error = TRUE;
105     return -1;
106 }
107
108 static int check_keyword(parser_ctx_t *ctx, const WCHAR *word, const WCHAR **lval)
109 {
110     const WCHAR *p1 = ctx->ptr;
111     const WCHAR *p2 = word;
112
113     while(p1 < ctx->end && *p2) {
114         if(*p1 != *p2)
115             return *p1 - *p2;
116         p1++;
117         p2++;
118     }
119
120     if(*p2 || (p1 < ctx->end && isalnumW(*p1)))
121         return 1;
122
123     *lval = ctx->ptr;
124     ctx->ptr = p1;
125     return 0;
126 }
127
128 /* ECMA-262 3rd Edition    7.3 */
129 static BOOL is_endline(WCHAR c)
130 {
131     return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
132 }
133
134 static BOOL is_identifier_char(WCHAR c)
135 {
136     return isalnumW(c) || c == '$' || c == '_' || c == '\\';
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 += 1 << 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                 while(isdigitW(*p))
300                     c = c*10 + (*p++ - '0');
301                 *pd++ = c;
302                 continue;
303             }
304
305             c = *p;
306         }
307
308         *pd++ = c;
309         p++;
310     }
311
312     *pd = 0;
313     return TRUE;
314 }
315
316 static int parse_identifier(parser_ctx_t *ctx, const WCHAR **ret)
317 {
318     const WCHAR *ptr = ctx->ptr++;
319     WCHAR *wstr;
320     int len;
321
322     while(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr))
323         ctx->ptr++;
324
325     len = ctx->ptr-ptr;
326
327     *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR));
328     memcpy(wstr, ptr, (len+1)*sizeof(WCHAR));
329     wstr[len] = 0;
330
331     /* FIXME: unescape */
332     return tIdentifier;
333 }
334
335 static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret, WCHAR endch)
336 {
337     const WCHAR *ptr = ++ctx->ptr;
338     WCHAR *wstr;
339     int len;
340
341     while(ctx->ptr < ctx->end && *ctx->ptr != endch) {
342         if(*ctx->ptr++ == '\\')
343             ctx->ptr++;
344     }
345
346     if(ctx->ptr == ctx->end)
347         return lex_error(ctx, IDS_UNTERMINATED_STR);
348
349     len = ctx->ptr-ptr;
350
351     *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR));
352     memcpy(wstr, ptr, (len+1)*sizeof(WCHAR));
353     wstr[len] = 0;
354
355     ctx->ptr++;
356
357     if(!unescape(wstr)) {
358         WARN("unescape failed\n");
359         return lex_error(ctx, E_FAIL);
360     }
361
362     return tStringLiteral;
363 }
364
365 static literal_t *alloc_int_literal(parser_ctx_t *ctx, LONG l)
366 {
367     literal_t *ret = parser_alloc(ctx, sizeof(literal_t));
368
369     ret->vt = VT_I4;
370     ret->u.lval = l;
371
372     return ret;
373 }
374
375 static int parse_double_literal(parser_ctx_t *ctx, LONG int_part, literal_t **literal)
376 {
377     LONGLONG d, hlp;
378     int exp = 0;
379
380     if(ctx->ptr == ctx->end || (!isdigitW(*ctx->ptr) &&
381         *ctx->ptr!='.' && *ctx->ptr!='e' && *ctx->ptr!='E')) {
382         ERR("Illegal character\n");
383         return 0;
384     }
385
386     d = int_part;
387     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
388         hlp = d*10 + *(ctx->ptr++) - '0';
389         if(d>LONGLONG_MAX/10 || hlp<0) {
390             exp++;
391             break;
392         }
393         else
394             d = hlp;
395     }
396     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
397         exp++;
398         ctx->ptr++;
399     }
400
401     if(*ctx->ptr == '.') ctx->ptr++;
402
403     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
404         hlp = d*10 + *(ctx->ptr++) - '0';
405         if(d>LONGLONG_MAX/10 || hlp<0)
406             break;
407
408         d = hlp;
409         exp--;
410     }
411     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr))
412         ctx->ptr++;
413
414     if(ctx->ptr < ctx->end && (*ctx->ptr == 'e' || *ctx->ptr == 'E')) {
415         int sign = 1, e = 0;
416
417         ctx->ptr++;
418         if(ctx->ptr < ctx->end) {
419             if(*ctx->ptr == '+') {
420                 ctx->ptr++;
421             }else if(*ctx->ptr == '-') {
422                 sign = -1;
423                 ctx->ptr++;
424             }else if(!isdigitW(*ctx->ptr)) {
425                 WARN("Expected exponent part\n");
426                 return lex_error(ctx, E_FAIL);
427             }
428         }
429
430         if(ctx->ptr == ctx->end) {
431             WARN("unexpected end of file\n");
432             return lex_error(ctx, E_FAIL);
433         }
434
435         while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
436             if(e > INT_MAX/10 || (e = e*10 + *ctx->ptr++ - '0')<0)
437                 e = INT_MAX;
438         }
439         e *= sign;
440
441         if(exp<0 && e<0 && e+exp>0) exp = INT_MIN;
442         else if(exp>0 && e>0 && e+exp<0) exp = INT_MAX;
443         else exp += e;
444     }
445
446     *literal = parser_alloc(ctx, sizeof(literal_t));
447     (*literal)->vt = VT_R8;
448     (*literal)->u.dval = (double)d*pow(10, exp);
449
450     return tNumericLiteral;
451 }
452
453 static int parse_numeric_literal(parser_ctx_t *ctx, literal_t **literal)
454 {
455     LONG l, d;
456
457     l = *ctx->ptr++ - '0';
458     if(ctx->ptr == ctx->end) {
459         *literal = alloc_int_literal(ctx, l);
460         return tNumericLiteral;
461     }
462
463     if(!l) {
464         if(*ctx->ptr == 'x' || *ctx->ptr == 'X') {
465             if(++ctx->ptr == ctx->end) {
466                 ERR("unexpexted end of file\n");
467                 return 0;
468             }
469
470             while(ctx->ptr < ctx->end && (d = hex_to_int(*ctx->ptr)) != -1) {
471                 l = l*16 + d;
472                 ctx->ptr++;
473             }
474
475             if(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr)) {
476                 WARN("unexpected identifier char\n");
477                 return lex_error(ctx, E_FAIL);
478             }
479
480             *literal = alloc_int_literal(ctx, l);
481             return tNumericLiteral;
482         }
483
484         if(isdigitW(*ctx->ptr) || is_identifier_char(*ctx->ptr)) {
485             WARN("wrong char after zero\n");
486             return lex_error(ctx, E_FAIL);
487         }
488
489         *literal = alloc_int_literal(ctx, 0);
490     }
491
492     while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr))
493     {
494         d = l*10 + *(ctx->ptr)-'0';
495
496         /* Check for integer overflow */
497         if (l > INT_MAX/10 || d < 0)
498             return parse_double_literal(ctx, l, literal);
499
500         l = d;
501         ctx->ptr++;
502     }
503
504     if(ctx->ptr < ctx->end) {
505         if(*ctx->ptr == '.' || *ctx->ptr == 'e' || *ctx->ptr == 'E')
506             return parse_double_literal(ctx, l, literal);
507
508         if(is_identifier_char(*ctx->ptr)) {
509             WARN("unexpected identifier char\n");
510             return lex_error(ctx, E_FAIL);
511         }
512     }
513
514     *literal = alloc_int_literal(ctx, l);
515     return tNumericLiteral;
516 }
517
518 int parser_lex(void *lval, parser_ctx_t *ctx)
519 {
520     int ret;
521
522     ctx->nl = ctx->ptr == ctx->begin;
523
524     do {
525         skip_spaces(ctx);
526         if(ctx->ptr == ctx->end)
527             return 0;
528     }while(skip_comment(ctx) || skip_html_comment(ctx));
529
530     if(isalphaW(*ctx->ptr)) {
531         ret = check_keywords(ctx, lval);
532         if(ret)
533             return ret;
534
535         return parse_identifier(ctx, lval);
536     }
537
538     if(isdigitW(*ctx->ptr))
539         return parse_numeric_literal(ctx, lval);
540
541     switch(*ctx->ptr) {
542     case '{':
543     case '(':
544     case ')':
545     case '[':
546     case ']':
547     case ';':
548     case ',':
549     case '~':
550     case '?':
551     case ':':
552         return *ctx->ptr++;
553
554     case '}':
555         *(const WCHAR**)lval = ctx->ptr++;
556         return '}';
557
558     case '.':
559         if(++ctx->ptr < ctx->end && isdigitW(*ctx->ptr))
560             return parse_double_literal(ctx, 0, lval);
561         return '.';
562
563     case '<':
564         if(++ctx->ptr == ctx->end) {
565             *(int*)lval = EXPR_LESS;
566             return tRelOper;
567         }
568
569         switch(*ctx->ptr) {
570         case '=':  /* <= */
571             ctx->ptr++;
572             *(int*)lval = EXPR_LESSEQ;
573             return tRelOper;
574         case '<':  /* << */
575             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* <<= */
576                 ctx->ptr++;
577                 *(int*)lval = EXPR_ASSIGNLSHIFT;
578                 return tAssignOper;
579             }
580             *(int*)lval = EXPR_LSHIFT;
581             return tShiftOper;
582         default: /* < */
583             *(int*)lval = EXPR_LESS;
584             return tRelOper;
585         }
586
587     case '>':
588         if(++ctx->ptr == ctx->end) { /* > */
589             *(int*)lval = EXPR_GREATER;
590             return tRelOper;
591         }
592
593         switch(*ctx->ptr) {
594         case '=':  /* >= */
595             ctx->ptr++;
596             *(int*)lval = EXPR_GREATEREQ;
597             return tRelOper;
598         case '>':  /* >> */
599             if(++ctx->ptr < ctx->end) {
600                 if(*ctx->ptr == '=') {  /* >>= */
601                     ctx->ptr++;
602                     *(int*)lval = EXPR_ASSIGNRSHIFT;
603                     return tAssignOper;
604                 }
605                 if(*ctx->ptr == '>') {  /* >>> */
606                     if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* >>>= */
607                         ctx->ptr++;
608                         *(int*)lval = EXPR_ASSIGNRRSHIFT;
609                         return tAssignOper;
610                     }
611                     *(int*)lval = EXPR_RRSHIFT;
612                     return tRelOper;
613                 }
614             }
615             *(int*)lval = EXPR_RSHIFT;
616             return tShiftOper;
617         default:
618             *(int*)lval = EXPR_GREATER;
619             return tRelOper;
620         }
621
622     case '+':
623         ctx->ptr++;
624         if(ctx->ptr < ctx->end) {
625             switch(*ctx->ptr) {
626             case '+':  /* ++ */
627                 ctx->ptr++;
628                 return tINC;
629             case '=':  /* += */
630                 ctx->ptr++;
631                 *(int*)lval = EXPR_ASSIGNADD;
632                 return tAssignOper;
633             }
634         }
635         return '+';
636
637     case '-':
638         ctx->ptr++;
639         if(ctx->ptr < ctx->end) {
640             switch(*ctx->ptr) {
641             case '-':  /* -- or --> */
642                 ctx->ptr++;
643                 if(ctx->is_html && ctx->nl && ctx->ptr < ctx->end && *ctx->ptr == '>') {
644                     ctx->ptr++;
645                     return tHTMLCOMMENT;
646                 }
647                 return tDEC;
648             case '=':  /* -= */
649                 ctx->ptr++;
650                 *(int*)lval = EXPR_ASSIGNSUB;
651                 return tAssignOper;
652             }
653         }
654         return '-';
655
656     case '*':
657         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* *= */
658             ctx->ptr++;
659             *(int*)lval = EXPR_ASSIGNMUL;
660             return tAssignOper;
661         }
662         return '*';
663
664     case '%':
665         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* %= */
666             ctx->ptr++;
667             *(int*)lval = EXPR_ASSIGNMOD;
668             return tAssignOper;
669         }
670         return '%';
671
672     case '&':
673         if(++ctx->ptr < ctx->end) {
674             switch(*ctx->ptr) {
675             case '=':  /* &= */
676                 ctx->ptr++;
677                 *(int*)lval = EXPR_ASSIGNAND;
678                 return tAssignOper;
679             case '&':  /* && */
680                 ctx->ptr++;
681                 return tANDAND;
682             }
683         }
684         return '&';
685
686     case '|':
687         if(++ctx->ptr < ctx->end) {
688             switch(*ctx->ptr) {
689             case '=':  /* |= */
690                 ctx->ptr++;
691                 *(int*)lval = EXPR_ASSIGNOR;
692                 return tAssignOper;
693             case '|':  /* || */
694                 ctx->ptr++;
695                 return tOROR;
696             }
697         }
698         return '|';
699
700     case '^':
701         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* ^= */
702             ctx->ptr++;
703             *(int*)lval = EXPR_ASSIGNXOR;
704             return tAssignOper;
705         }
706         return '^';
707
708     case '!':
709         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* != */
710             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* !== */
711                 ctx->ptr++;
712                 *(int*)lval = EXPR_NOTEQEQ;
713                 return tEqOper;
714             }
715             *(int*)lval = EXPR_NOTEQ;
716             return tEqOper;
717         }
718         return '!';
719
720     case '=':
721         if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* == */
722             if(++ctx->ptr < ctx->end && *ctx->ptr == '=') {  /* === */
723                 ctx->ptr++;
724                 *(int*)lval = EXPR_EQEQ;
725                 return tEqOper;
726             }
727             *(int*)lval = EXPR_EQ;
728             return tEqOper;
729         }
730         return '=';
731
732     case '/':
733         if(++ctx->ptr < ctx->end) {
734             if(*ctx->ptr == '=') {  /* /= */
735                 ctx->ptr++;
736                 *(int*)lval = EXPR_ASSIGNDIV;
737                 return tAssignOper;
738             }
739         }
740         return '/';
741
742     case '\"':
743     case '\'':
744         return parse_string_literal(ctx, lval, *ctx->ptr);
745
746     case '_':
747     case '$':
748         return parse_identifier(ctx, lval);
749     }
750
751     WARN("unexpected char '%c' %d\n", *ctx->ptr, *ctx->ptr);
752     return 0;
753 }
754
755 static void add_object_literal(parser_ctx_t *ctx, DispatchEx *obj)
756 {
757     obj_literal_t *literal = parser_alloc(ctx, sizeof(obj_literal_t));
758
759     literal->obj = obj;
760     literal->next = ctx->obj_literals;
761     ctx->obj_literals = literal;
762 }
763
764 literal_t *parse_regexp(parser_ctx_t *ctx)
765 {
766     const WCHAR *re, *flags;
767     DispatchEx *regexp;
768     literal_t *ret;
769     DWORD re_len;
770     HRESULT hres;
771
772     TRACE("\n");
773
774     re = ctx->ptr;
775     while(ctx->ptr < ctx->end && *ctx->ptr != '/') {
776         if(*ctx->ptr++ == '\\' && ctx->ptr < ctx->end)
777             ctx->ptr++;
778     }
779
780     if(ctx->ptr == ctx->end) {
781         WARN("unexpected end of file\n");
782         return NULL;
783     }
784
785     re_len = ctx->ptr-re;
786
787     flags = ++ctx->ptr;
788     while(ctx->ptr < ctx->end && isalnumW(*ctx->ptr))
789         ctx->ptr++;
790
791     hres = create_regexp_str(ctx->script, re, re_len, flags, ctx->ptr-flags, &regexp);
792     if(FAILED(hres))
793         return NULL;
794
795     add_object_literal(ctx, regexp);
796
797     ret = parser_alloc(ctx, sizeof(literal_t));
798     ret->vt = VT_DISPATCH;
799     ret->u.disp = (IDispatch*)_IDispatchEx_(regexp);
800     return ret;
801 }