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