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