2 * Copyright 2008 Jacek Caban for CodeWeavers
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.
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.
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
29 #include "parser.tab.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(jscript
);
41 {L
"break", kBREAK
, TRUE
},
45 {L
"continue", kCONTINUE
, TRUE
},
46 {L
"default", kDEFAULT
},
51 {L
"finally", kFINALLY
},
53 {L
"function", kFUNCTION
},
54 {L
"get", kGET
, FALSE
, SCRIPTLANGUAGEVERSION_ES5
},
57 {L
"instanceof", kINSTANCEOF
},
58 {L
"let", kLET
, FALSE
, SCRIPTLANGUAGEVERSION_ES5
},
61 {L
"return", kRETURN
, TRUE
},
62 {L
"set", kSET
, FALSE
, SCRIPTLANGUAGEVERSION_ES5
},
75 static int lex_error(parser_ctx_t
*ctx
, HRESULT hres
)
78 ctx
->lexer_error
= TRUE
;
82 /* ECMA-262 3rd Edition 7.6 */
83 BOOL
is_identifier_char(WCHAR c
)
85 return iswalnum(c
) || c
== '$' || c
== '_' || c
== '\\';
88 static BOOL
is_identifier_first_char(WCHAR c
)
90 return iswalpha(c
) || c
== '$' || c
== '_' || c
== '\\';
93 static int check_keyword(parser_ctx_t
*ctx
, const WCHAR
*word
, const WCHAR
**lval
)
95 const WCHAR
*p1
= ctx
->ptr
;
96 const WCHAR
*p2
= word
;
98 while(p1
< ctx
->end
&& *p2
) {
105 if(*p2
|| (p1
< ctx
->end
&& is_identifier_char(*p1
)))
114 /* ECMA-262 3rd Edition 7.3 */
115 static BOOL
is_endline(WCHAR c
)
117 return c
== '\n' || c
== '\r' || c
== 0x2028 || c
== 0x2029;
120 static int hex_to_int(WCHAR c
)
122 if('0' <= c
&& c
<= '9')
125 if('a' <= c
&& c
<= 'f')
128 if('A' <= c
&& c
<= 'F')
134 static int check_keywords(parser_ctx_t
*ctx
, const WCHAR
**lval
)
136 int min
= 0, max
= ARRAY_SIZE(keywords
)-1, r
, i
;
141 r
= check_keyword(ctx
, keywords
[i
].word
, lval
);
143 if(ctx
->script
->version
< keywords
[i
].min_version
) {
144 TRACE("ignoring keyword %s in incompatible mode\n",
145 debugstr_w(keywords
[i
].word
));
146 ctx
->ptr
-= lstrlenW(keywords
[i
].word
);
149 ctx
->implicit_nl_semicolon
= keywords
[i
].no_nl
;
150 return keywords
[i
].token
;
162 static BOOL
skip_html_comment(parser_ctx_t
*ctx
)
164 if(!ctx
->is_html
|| ctx
->ptr
+3 >= ctx
->end
||
165 memcmp(ctx
->ptr
, L
"<!--", sizeof(WCHAR
)*4))
169 while(ctx
->ptr
< ctx
->end
&& !is_endline(*ctx
->ptr
++));
174 static BOOL
skip_comment(parser_ctx_t
*ctx
)
176 if(ctx
->ptr
+1 >= ctx
->end
)
179 if(*ctx
->ptr
!= '/') {
180 if(*ctx
->ptr
== '@' && ctx
->ptr
+2 < ctx
->end
&& ctx
->ptr
[1] == '*' && ctx
->ptr
[2] == '/') {
188 switch(ctx
->ptr
[1]) {
191 if(ctx
->ptr
+2 < ctx
->end
&& *ctx
->ptr
== '@' && is_identifier_char(ctx
->ptr
[1]))
193 while(ctx
->ptr
+1 < ctx
->end
&& (ctx
->ptr
[0] != '*' || ctx
->ptr
[1] != '/'))
196 if(ctx
->ptr
[0] == '*' && ctx
->ptr
[1] == '/') {
199 WARN("unexpected end of file (missing end of comment)\n");
205 if(ctx
->ptr
+2 < ctx
->end
&& *ctx
->ptr
== '@' && is_identifier_char(ctx
->ptr
[1]))
207 while(ctx
->ptr
< ctx
->end
&& !is_endline(*ctx
->ptr
))
217 static BOOL
skip_spaces(parser_ctx_t
*ctx
)
219 while(ctx
->ptr
< ctx
->end
&& (iswspace(*ctx
->ptr
) || *ctx
->ptr
== 0xFEFF /* UTF16 BOM */)) {
220 if(is_endline(*ctx
->ptr
++))
224 return ctx
->ptr
!= ctx
->end
;
227 BOOL
unescape(WCHAR
*str
, size_t *len
)
229 WCHAR
*pd
, *p
, c
, *end
= str
+ *len
;
266 i
= hex_to_int(*++p
);
271 i
= hex_to_int(*++p
);
279 i
= hex_to_int(*++p
);
284 i
= hex_to_int(*++p
);
289 i
= hex_to_int(*++p
);
294 i
= hex_to_int(*++p
);
302 if(p
< end
&& is_digit(*p
)) {
303 c
= c
*8 + (*p
++ - '0');
304 if(p
< end
&& is_digit(*p
))
305 c
= c
*8 + (*p
++ - '0');
321 static int parse_identifier(parser_ctx_t
*ctx
, const WCHAR
**ret
)
323 const WCHAR
*ptr
= ctx
->ptr
++;
327 while(ctx
->ptr
< ctx
->end
&& is_identifier_char(*ctx
->ptr
))
332 *ret
= wstr
= parser_alloc(ctx
, (len
+1)*sizeof(WCHAR
));
333 memcpy(wstr
, ptr
, len
*sizeof(WCHAR
));
336 /* FIXME: unescape */
340 static int parse_string_literal(parser_ctx_t
*ctx
, jsstr_t
**ret
, WCHAR endch
)
342 const WCHAR
*ptr
= ++ctx
->ptr
, *ret_str
= ptr
;
343 BOOL needs_unescape
= FALSE
;
347 while(ctx
->ptr
< ctx
->end
&& *ctx
->ptr
!= endch
) {
348 if(*ctx
->ptr
++ == '\\') {
350 needs_unescape
= TRUE
;
354 if(ctx
->ptr
== ctx
->end
)
355 return lex_error(ctx
, JS_E_UNTERMINATED_STRING
);
357 len
= ctx
->ptr
- ptr
;
361 ret_str
= unescape_str
= parser_alloc(ctx
, len
* sizeof(WCHAR
));
363 return lex_error(ctx
, E_OUTOFMEMORY
);
364 memcpy(unescape_str
, ptr
, len
* sizeof(WCHAR
));
365 if(!unescape(unescape_str
, &len
)) {
366 WARN("unescape failed\n");
367 return lex_error(ctx
, E_FAIL
);
371 if(!(*ret
= compiler_alloc_string_len(ctx
->compiler
, ret_str
, len
)))
372 return lex_error(ctx
, E_OUTOFMEMORY
);
374 /* FIXME: leaking string */
375 return tStringLiteral
;
378 static literal_t
*new_double_literal(parser_ctx_t
*ctx
, DOUBLE d
)
380 literal_t
*ret
= parser_alloc(ctx
, sizeof(literal_t
));
382 ret
->type
= LT_DOUBLE
;
387 literal_t
*new_boolean_literal(parser_ctx_t
*ctx
, BOOL bval
)
389 literal_t
*ret
= parser_alloc(ctx
, sizeof(literal_t
));
397 HRESULT
parse_decimal(const WCHAR
**iter
, const WCHAR
*end
, double *ret
)
399 const WCHAR
*ptr
= *iter
;
403 while(ptr
< end
&& is_digit(*ptr
)) {
404 hlp
= d
*10 + *(ptr
++) - '0';
405 if(d
>MAXLONGLONG
/10 || hlp
<0) {
412 while(ptr
< end
&& is_digit(*ptr
)) {
420 while(ptr
< end
&& is_digit(*ptr
)) {
421 hlp
= d
*10 + *(ptr
++) - '0';
422 if(d
>MAXLONGLONG
/10 || hlp
<0)
428 while(ptr
< end
&& is_digit(*ptr
))
432 if(ptr
< end
&& (*ptr
== 'e' || *ptr
== 'E')) {
438 }else if(*ptr
== '-') {
441 }else if(!is_digit(*ptr
)) {
442 WARN("Expected exponent part\n");
448 WARN("unexpected end of file\n");
452 while(ptr
< end
&& is_digit(*ptr
)) {
453 if(e
> INT_MAX
/10 || (e
= e
*10 + *ptr
++ - '0')<0)
458 if(exp
<0 && e
<0 && e
+exp
>0) exp
= INT_MIN
;
459 else if(exp
>0 && e
>0 && e
+exp
<0) exp
= INT_MAX
;
463 if(is_identifier_char(*ptr
)) {
464 WARN("wrong char after zero\n");
465 return JS_E_MISSING_SEMICOLON
;
468 *ret
= exp
>=0 ? d
*pow(10, exp
) : d
/pow(10, -exp
);
473 static BOOL
parse_numeric_literal(parser_ctx_t
*ctx
, double *ret
)
477 if(*ctx
->ptr
== '0') {
480 if(*ctx
->ptr
== 'x' || *ctx
->ptr
== 'X') {
483 if(++ctx
->ptr
== ctx
->end
) {
484 ERR("unexpected end of file\n");
488 while(ctx
->ptr
< ctx
->end
&& (d
= hex_to_int(*ctx
->ptr
)) != -1) {
493 if(ctx
->ptr
< ctx
->end
&& is_identifier_char(*ctx
->ptr
)) {
494 WARN("unexpected identifier char\n");
495 lex_error(ctx
, JS_E_MISSING_SEMICOLON
);
503 if(is_digit(*ctx
->ptr
)) {
508 for(ptr
= ctx
->ptr
; ptr
< ctx
->end
&& is_digit(*ptr
); ptr
++) {
516 val
= val
*base
+ *ctx
->ptr
-'0';
517 }while(++ctx
->ptr
< ctx
->end
&& is_digit(*ctx
->ptr
));
519 /* FIXME: Do we need it here? */
520 if(ctx
->ptr
< ctx
->end
&& (is_identifier_char(*ctx
->ptr
) || *ctx
->ptr
== '.')) {
521 WARN("wrong char after octal literal: '%c'\n", *ctx
->ptr
);
522 lex_error(ctx
, JS_E_MISSING_SEMICOLON
);
530 if(is_identifier_char(*ctx
->ptr
)) {
531 WARN("wrong char after zero\n");
532 lex_error(ctx
, JS_E_MISSING_SEMICOLON
);
537 hres
= parse_decimal(&ctx
->ptr
, ctx
->end
, ret
);
539 lex_error(ctx
, hres
);
546 static int next_token(parser_ctx_t
*ctx
, unsigned *loc
, void *lval
)
549 if(!skip_spaces(ctx
)) {
550 *loc
= ctx
->ptr
- ctx
->begin
;
553 }while(skip_comment(ctx
) || skip_html_comment(ctx
));
554 *loc
= ctx
->ptr
- ctx
->begin
;
556 if(ctx
->implicit_nl_semicolon
) {
559 ctx
->implicit_nl_semicolon
= FALSE
;
562 if(iswalpha(*ctx
->ptr
)) {
563 int ret
= check_keywords(ctx
, lval
);
567 return parse_identifier(ctx
, lval
);
570 if(is_digit(*ctx
->ptr
)) {
573 if(!parse_numeric_literal(ctx
, &n
))
576 *(literal_t
**)lval
= new_double_literal(ctx
, n
);
577 return tNumericLiteral
;
594 if(ctx
->ptr
+1 < ctx
->end
&& is_digit(ctx
->ptr
[1])) {
597 hres
= parse_decimal(&ctx
->ptr
, ctx
->end
, &n
);
599 lex_error(ctx
, hres
);
602 *(literal_t
**)lval
= new_double_literal(ctx
, n
);
603 return tNumericLiteral
;
609 if(++ctx
->ptr
== ctx
->end
) {
610 *(int*)lval
= EXPR_LESS
;
617 *(int*)lval
= EXPR_LESSEQ
;
620 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* <<= */
622 *(int*)lval
= EXPR_ASSIGNLSHIFT
;
625 *(int*)lval
= EXPR_LSHIFT
;
628 *(int*)lval
= EXPR_LESS
;
633 if(++ctx
->ptr
== ctx
->end
) { /* > */
634 *(int*)lval
= EXPR_GREATER
;
641 *(int*)lval
= EXPR_GREATEREQ
;
644 if(++ctx
->ptr
< ctx
->end
) {
645 if(*ctx
->ptr
== '=') { /* >>= */
647 *(int*)lval
= EXPR_ASSIGNRSHIFT
;
650 if(*ctx
->ptr
== '>') { /* >>> */
651 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* >>>= */
653 *(int*)lval
= EXPR_ASSIGNRRSHIFT
;
656 *(int*)lval
= EXPR_RRSHIFT
;
660 *(int*)lval
= EXPR_RSHIFT
;
663 *(int*)lval
= EXPR_GREATER
;
669 if(ctx
->ptr
< ctx
->end
) {
676 *(int*)lval
= EXPR_ASSIGNADD
;
684 if(ctx
->ptr
< ctx
->end
) {
686 case '-': /* -- or --> */
688 if(ctx
->is_html
&& ctx
->nl
&& ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '>') {
695 *(int*)lval
= EXPR_ASSIGNSUB
;
702 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* *= */
704 *(int*)lval
= EXPR_ASSIGNMUL
;
710 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* %= */
712 *(int*)lval
= EXPR_ASSIGNMOD
;
718 if(++ctx
->ptr
< ctx
->end
) {
722 *(int*)lval
= EXPR_ASSIGNAND
;
732 if(++ctx
->ptr
< ctx
->end
) {
736 *(int*)lval
= EXPR_ASSIGNOR
;
746 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* ^= */
748 *(int*)lval
= EXPR_ASSIGNXOR
;
754 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* != */
755 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* !== */
757 *(int*)lval
= EXPR_NOTEQEQ
;
760 *(int*)lval
= EXPR_NOTEQ
;
766 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* == */
767 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== '=') { /* === */
769 *(int*)lval
= EXPR_EQEQ
;
772 *(int*)lval
= EXPR_EQ
;
778 if(++ctx
->ptr
< ctx
->end
) {
779 if(*ctx
->ptr
== '=') { /* /= */
781 *(int*)lval
= EXPR_ASSIGNDIV
;
788 if(++ctx
->ptr
< ctx
->end
&& *ctx
->ptr
== ':') {
796 return parse_string_literal(ctx
, lval
, *ctx
->ptr
);
800 return parse_identifier(ctx
, lval
);
806 WARN("unexpected char '%c' %d\n", *ctx
->ptr
, *ctx
->ptr
);
812 struct _cc_var_t
*next
;
817 void release_cc(cc_ctx_t
*cc
)
819 cc_var_t
*iter
, *next
;
821 for(iter
= cc
->vars
; iter
; iter
= next
) {
829 static BOOL
new_cc_var(cc_ctx_t
*cc
, const WCHAR
*name
, int len
, ccval_t v
)
834 len
= lstrlenW(name
);
836 new_v
= malloc(sizeof(cc_var_t
) + (len
+1)*sizeof(WCHAR
));
841 memcpy(new_v
->name
, name
, (len
+1)*sizeof(WCHAR
));
842 new_v
->name_len
= len
;
843 new_v
->next
= cc
->vars
;
848 static cc_var_t
*find_cc_var(cc_ctx_t
*cc
, const WCHAR
*name
, unsigned name_len
)
852 for(iter
= cc
->vars
; iter
; iter
= iter
->next
) {
853 if(iter
->name_len
== name_len
&& !memcmp(iter
->name
, name
, name_len
*sizeof(WCHAR
)))
860 static BOOL
init_cc(parser_ctx_t
*ctx
)
867 cc
= malloc(sizeof(cc_ctx_t
));
869 lex_error(ctx
, E_OUTOFMEMORY
);
875 if(!new_cc_var(cc
, L
"_jscript", -1, ccval_bool(TRUE
))
876 || !new_cc_var(cc
, sizeof(void*) == 8 ? L
"_win64" : L
"_win32", -1, ccval_bool(TRUE
))
877 || !new_cc_var(cc
, sizeof(void*) == 8 ? L
"_amd64" : L
"_x86", -1, ccval_bool(TRUE
))
878 || !new_cc_var(cc
, L
"_jscript_version", -1, ccval_num(JSCRIPT_MAJOR_VERSION
+ (DOUBLE
)JSCRIPT_MINOR_VERSION
/10.0))
879 || !new_cc_var(cc
, L
"_jscript_build", -1, ccval_num(JSCRIPT_BUILD_VERSION
))) {
881 lex_error(ctx
, E_OUTOFMEMORY
);
885 ctx
->script
->cc
= cc
;
889 static BOOL
parse_cc_identifier(parser_ctx_t
*ctx
, const WCHAR
**ret
, unsigned *ret_len
)
891 if(*ctx
->ptr
!= '@') {
892 lex_error(ctx
, JS_E_EXPECTED_AT
);
896 if(!is_identifier_first_char(*++ctx
->ptr
)) {
897 lex_error(ctx
, JS_E_EXPECTED_IDENTIFIER
);
902 while(++ctx
->ptr
< ctx
->end
&& is_identifier_char(*ctx
->ptr
));
903 *ret_len
= ctx
->ptr
- *ret
;
907 int try_parse_ccval(parser_ctx_t
*ctx
, ccval_t
*r
)
909 if(!skip_spaces(ctx
))
912 if(is_digit(*ctx
->ptr
)) {
915 if(!parse_numeric_literal(ctx
, &n
))
922 if(*ctx
->ptr
== '@') {
927 if(!parse_cc_identifier(ctx
, &ident
, &ident_len
))
930 cc_var
= find_cc_var(ctx
->script
->cc
, ident
, ident_len
);
931 *r
= cc_var
? cc_var
->val
: ccval_num(NAN
);
935 if(!check_keyword(ctx
, L
"true", NULL
)) {
936 *r
= ccval_bool(TRUE
);
940 if(!check_keyword(ctx
, L
"false", NULL
)) {
941 *r
= ccval_bool(FALSE
);
948 static int skip_code(parser_ctx_t
*ctx
, BOOL exec_else
)
954 ptr
= wcschr(ctx
->ptr
, '@');
957 return lex_error(ctx
, JS_E_EXPECTED_CCEND
);
961 if(!check_keyword(ctx
, L
"end", NULL
)) {
967 if(exec_else
&& !check_keyword(ctx
, L
"elif", NULL
)) {
971 if(!skip_spaces(ctx
) || *ctx
->ptr
!= '(')
972 return lex_error(ctx
, JS_E_MISSING_LBRACKET
);
974 if(!parse_cc_expr(ctx
))
977 if(!get_ccbool(ctx
->ccval
))
978 continue; /* skip block of code */
980 /* continue parsing */
985 if(exec_else
&& !check_keyword(ctx
, L
"else", NULL
)) {
989 /* parse else block */
994 if(!check_keyword(ctx
, L
"if", NULL
)) {
1003 static int cc_token(parser_ctx_t
*ctx
, void *lval
)
1005 unsigned id_len
= 0;
1010 if(!check_keyword(ctx
, L
"cc_on", NULL
))
1011 return init_cc(ctx
) ? 0 : -1;
1013 if(!check_keyword(ctx
, L
"set", NULL
)) {
1021 if(!skip_spaces(ctx
))
1022 return lex_error(ctx
, JS_E_EXPECTED_AT
);
1024 if(!parse_cc_identifier(ctx
, &ident
, &ident_len
))
1027 if(!skip_spaces(ctx
) || *ctx
->ptr
!= '=')
1028 return lex_error(ctx
, JS_E_EXPECTED_ASSIGN
);
1031 if(!parse_cc_expr(ctx
)) {
1032 WARN("parsing CC expression failed\n");
1036 var
= find_cc_var(ctx
->script
->cc
, ident
, ident_len
);
1038 var
->val
= ctx
->ccval
;
1040 if(!new_cc_var(ctx
->script
->cc
, ident
, ident_len
, ctx
->ccval
))
1041 return lex_error(ctx
, E_OUTOFMEMORY
);
1047 if(!check_keyword(ctx
, L
"if", NULL
)) {
1051 if(!skip_spaces(ctx
) || *ctx
->ptr
!= '(')
1052 return lex_error(ctx
, JS_E_MISSING_LBRACKET
);
1054 if(!parse_cc_expr(ctx
))
1057 if(get_ccbool(ctx
->ccval
)) {
1058 /* continue parsing block inside if */
1063 return skip_code(ctx
, TRUE
);
1066 if(!check_keyword(ctx
, L
"elif", NULL
) || !check_keyword(ctx
, L
"else", NULL
)) {
1067 if(!ctx
->cc_if_depth
)
1068 return lex_error(ctx
, JS_E_SYNTAX
);
1070 return skip_code(ctx
, FALSE
);
1073 if(!check_keyword(ctx
, L
"end", NULL
)) {
1074 if(!ctx
->cc_if_depth
)
1075 return lex_error(ctx
, JS_E_SYNTAX
);
1081 if(!ctx
->script
->cc
)
1082 return lex_error(ctx
, JS_E_DISABLED_CC
);
1084 while(ctx
->ptr
+id_len
< ctx
->end
&& is_identifier_char(ctx
->ptr
[id_len
]))
1089 TRACE("var %s\n", debugstr_wn(ctx
->ptr
, id_len
));
1091 var
= find_cc_var(ctx
->script
->cc
, ctx
->ptr
, id_len
);
1093 if(!var
|| var
->val
.is_num
) {
1094 *(literal_t
**)lval
= new_double_literal(ctx
, var
? var
->val
.u
.n
: NAN
);
1095 return tNumericLiteral
;
1098 *(literal_t
**)lval
= new_boolean_literal(ctx
, var
->val
.u
.b
);
1099 return tBooleanLiteral
;
1102 int parser_lex(void *lval
, unsigned *loc
, parser_ctx_t
*ctx
)
1106 ctx
->nl
= ctx
->ptr
== ctx
->begin
;
1109 ret
= next_token(ctx
, loc
, lval
);
1110 } while(ret
== '@' && !(ret
= cc_token(ctx
, lval
)));
1115 literal_t
*parse_regexp(parser_ctx_t
*ctx
)
1117 const WCHAR
*re
, *flags_ptr
;
1118 BOOL in_class
= FALSE
;
1119 DWORD re_len
, flags
;
1124 while(*--ctx
->ptr
!= '/');
1126 /* Simple regexp pre-parser; '/' if used in char class does not terminate regexp literal */
1128 while(ctx
->ptr
< ctx
->end
) {
1129 if(*ctx
->ptr
== '\\') {
1130 if(++ctx
->ptr
== ctx
->end
)
1132 }else if(in_class
) {
1133 if(*ctx
->ptr
== '\n')
1135 if(*ctx
->ptr
== ']')
1138 if(*ctx
->ptr
== '/')
1141 if(*ctx
->ptr
== '[')
1147 if(ctx
->ptr
== ctx
->end
|| *ctx
->ptr
!= '/') {
1148 WARN("pre-parsing failed\n");
1149 ctx
->hres
= JS_E_SYNTAX
;
1153 re_len
= ctx
->ptr
-re
;
1155 flags_ptr
= ++ctx
->ptr
;
1156 while(ctx
->ptr
< ctx
->end
&& iswalnum(*ctx
->ptr
))
1159 ctx
->hres
= parse_regexp_flags(flags_ptr
, ctx
->ptr
-flags_ptr
, &flags
);
1160 if(FAILED(ctx
->hres
))
1163 ret
= parser_alloc(ctx
, sizeof(literal_t
));
1164 ret
->type
= LT_REGEXP
;
1165 ret
->u
.regexp
.str
= compiler_alloc_string_len(ctx
->compiler
, re
, re_len
);
1166 ret
->u
.regexp
.flags
= flags
;