1 | #include "jsi.h" |
2 | #include "jsvalue.h" |
3 | #include "jsbuiltin.h" |
4 | #include "regexp.h" |
5 | |
6 | void js_newregexp(js_State *J, const char *pattern, int flags) |
7 | { |
8 | const char *error; |
9 | js_Object *obj; |
10 | Reprog *prog; |
11 | int opts; |
12 | |
13 | obj = jsV_newobject(J, JS_CREGEXP, J->RegExp_prototype); |
14 | |
15 | opts = 0; |
16 | if (flags & JS_REGEXP_I) opts |= REG_ICASE; |
17 | if (flags & JS_REGEXP_M) opts |= REG_NEWLINE; |
18 | |
19 | prog = js_regcompx(J->alloc, J->actx, pattern, opts, &error); |
20 | if (!prog) |
21 | js_syntaxerror(J, "regular expression: %s" , error); |
22 | |
23 | obj->u.r.prog = prog; |
24 | obj->u.r.source = js_strdup(J, pattern); |
25 | obj->u.r.flags = flags; |
26 | obj->u.r.last = 0; |
27 | js_pushobject(J, obj); |
28 | } |
29 | |
30 | void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text) |
31 | { |
32 | int result; |
33 | int i; |
34 | int opts; |
35 | Resub m; |
36 | |
37 | opts = 0; |
38 | if (re->flags & JS_REGEXP_G) { |
39 | if (re->last > strlen(text)) { |
40 | re->last = 0; |
41 | js_pushnull(J); |
42 | return; |
43 | } |
44 | if (re->last > 0) { |
45 | text += re->last; |
46 | opts |= REG_NOTBOL; |
47 | } |
48 | } |
49 | |
50 | result = js_regexec(re->prog, text, &m, opts); |
51 | if (result < 0) |
52 | js_error(J, "regexec failed" ); |
53 | if (result == 0) { |
54 | js_newarray(J); |
55 | js_pushstring(J, text); |
56 | js_setproperty(J, -2, "input" ); |
57 | js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp)); |
58 | js_setproperty(J, -2, "index" ); |
59 | for (i = 0; i < m.nsub; ++i) { |
60 | js_pushlstring(J, m.sub[i].sp, m.sub[i].ep - m.sub[i].sp); |
61 | js_setindex(J, -2, i); |
62 | } |
63 | if (re->flags & JS_REGEXP_G) |
64 | re->last = re->last + (m.sub[0].ep - text); |
65 | return; |
66 | } |
67 | |
68 | if (re->flags & JS_REGEXP_G) |
69 | re->last = 0; |
70 | |
71 | js_pushnull(J); |
72 | } |
73 | |
74 | static void Rp_test(js_State *J) |
75 | { |
76 | js_Regexp *re; |
77 | const char *text; |
78 | int result; |
79 | int opts; |
80 | Resub m; |
81 | |
82 | re = js_toregexp(J, 0); |
83 | text = js_tostring(J, 1); |
84 | |
85 | opts = 0; |
86 | if (re->flags & JS_REGEXP_G) { |
87 | if (re->last > strlen(text)) { |
88 | re->last = 0; |
89 | js_pushboolean(J, 0); |
90 | return; |
91 | } |
92 | if (re->last > 0) { |
93 | text += re->last; |
94 | opts |= REG_NOTBOL; |
95 | } |
96 | } |
97 | |
98 | result = js_regexec(re->prog, text, &m, opts); |
99 | if (result < 0) |
100 | js_error(J, "regexec failed" ); |
101 | if (result == 0) { |
102 | if (re->flags & JS_REGEXP_G) |
103 | re->last = re->last + (m.sub[0].ep - text); |
104 | js_pushboolean(J, 1); |
105 | return; |
106 | } |
107 | |
108 | if (re->flags & JS_REGEXP_G) |
109 | re->last = 0; |
110 | |
111 | js_pushboolean(J, 0); |
112 | } |
113 | |
114 | static void jsB_new_RegExp(js_State *J) |
115 | { |
116 | js_Regexp *old; |
117 | const char *pattern; |
118 | int flags; |
119 | |
120 | if (js_isregexp(J, 1)) { |
121 | if (js_isdefined(J, 2)) |
122 | js_typeerror(J, "cannot supply flags when creating one RegExp from another" ); |
123 | old = js_toregexp(J, 1); |
124 | pattern = old->source; |
125 | flags = old->flags; |
126 | } else if (js_isundefined(J, 1)) { |
127 | pattern = "(?:)" ; |
128 | flags = 0; |
129 | } else { |
130 | pattern = js_tostring(J, 1); |
131 | flags = 0; |
132 | } |
133 | |
134 | if (strlen(pattern) == 0) |
135 | pattern = "(?:)" ; |
136 | |
137 | if (js_isdefined(J, 2)) { |
138 | const char *s = js_tostring(J, 2); |
139 | int g = 0, i = 0, m = 0; |
140 | while (*s) { |
141 | if (*s == 'g') ++g; |
142 | else if (*s == 'i') ++i; |
143 | else if (*s == 'm') ++m; |
144 | else js_syntaxerror(J, "invalid regular expression flag: '%c'" , *s); |
145 | ++s; |
146 | } |
147 | if (g > 1) js_syntaxerror(J, "invalid regular expression flag: 'g'" ); |
148 | if (i > 1) js_syntaxerror(J, "invalid regular expression flag: 'i'" ); |
149 | if (m > 1) js_syntaxerror(J, "invalid regular expression flag: 'm'" ); |
150 | if (g) flags |= JS_REGEXP_G; |
151 | if (i) flags |= JS_REGEXP_I; |
152 | if (m) flags |= JS_REGEXP_M; |
153 | } |
154 | |
155 | js_newregexp(J, pattern, flags); |
156 | } |
157 | |
158 | static void jsB_RegExp(js_State *J) |
159 | { |
160 | if (js_isregexp(J, 1)) |
161 | return; |
162 | jsB_new_RegExp(J); |
163 | } |
164 | |
165 | static void Rp_toString(js_State *J) |
166 | { |
167 | js_Regexp *re; |
168 | char *out; |
169 | |
170 | re = js_toregexp(J, 0); |
171 | |
172 | out = js_malloc(J, strlen(re->source) + 6); /* extra space for //gim */ |
173 | strcpy(out, "/" ); |
174 | strcat(out, re->source); |
175 | strcat(out, "/" ); |
176 | if (re->flags & JS_REGEXP_G) strcat(out, "g" ); |
177 | if (re->flags & JS_REGEXP_I) strcat(out, "i" ); |
178 | if (re->flags & JS_REGEXP_M) strcat(out, "m" ); |
179 | |
180 | if (js_try(J)) { |
181 | js_free(J, out); |
182 | js_throw(J); |
183 | } |
184 | js_pop(J, 0); |
185 | js_pushstring(J, out); |
186 | js_endtry(J); |
187 | js_free(J, out); |
188 | } |
189 | |
190 | static void Rp_exec(js_State *J) |
191 | { |
192 | js_RegExp_prototype_exec(J, js_toregexp(J, 0), js_tostring(J, 1)); |
193 | } |
194 | |
195 | void jsB_initregexp(js_State *J) |
196 | { |
197 | js_pushobject(J, J->RegExp_prototype); |
198 | { |
199 | jsB_propf(J, "RegExp.prototype.toString" , Rp_toString, 0); |
200 | jsB_propf(J, "RegExp.prototype.test" , Rp_test, 0); |
201 | jsB_propf(J, "RegExp.prototype.exec" , Rp_exec, 0); |
202 | } |
203 | js_newcconstructor(J, jsB_RegExp, jsB_new_RegExp, "RegExp" , 1); |
204 | js_defglobal(J, "RegExp" , JS_DONTENUM); |
205 | } |
206 | |