1#include "jsi.h"
2#include "jsvalue.h"
3#include "jsbuiltin.h"
4#include "regexp.h"
5
6void 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
30void 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
74static 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
114static 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
158static void jsB_RegExp(js_State *J)
159{
160 if (js_isregexp(J, 1))
161 return;
162 jsB_new_RegExp(J);
163}
164
165static 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
190static void Rp_exec(js_State *J)
191{
192 js_RegExp_prototype_exec(J, js_toregexp(J, 0), js_tostring(J, 1));
193}
194
195void 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