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