7 int js_runeat(js_State *J, const char *s, int i)
11 rune = *(unsigned char*)s;
12 if (rune < Runeself) {
17 s += chartorune(&rune, s);
22 const char *js_utfidxtoptr(const char *s, int i)
26 rune = *(unsigned char*)s;
27 if (rune < Runeself) {
32 s += chartorune(&rune, s);
37 int js_utfptrtoidx(const char *s, const char *p)
42 if (*(unsigned char *)s < Runeself)
45 s += chartorune(&rune, s);
51 static void jsB_new_String(js_State *J)
53 js_newstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
56 static void jsB_String(js_State *J)
58 js_pushstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
61 static void Sp_toString(js_State *J)
63 js_Object *self = js_toobject(J, 0);
64 if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
65 js_pushliteral(J, self->u.s.string);
68 static void Sp_valueOf(js_State *J)
70 js_Object *self = js_toobject(J, 0);
71 if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
72 js_pushliteral(J, self->u.s.string);
75 static void Sp_charAt(js_State *J)
78 const char *s = js_tostring(J, 0);
79 int pos = js_tointeger(J, 1);
80 Rune rune = js_runeat(J, s, pos);
82 buf[runetochar(buf, &rune)] = 0;
83 js_pushstring(J, buf);
85 js_pushliteral(J, "");
89 static void Sp_charCodeAt(js_State *J)
91 const char *s = js_tostring(J, 0);
92 int pos = js_tointeger(J, 1);
93 Rune rune = js_runeat(J, s, pos);
95 js_pushnumber(J, rune);
97 js_pushnumber(J, NAN);
100 static void Sp_concat(js_State *J)
102 unsigned int i, top = js_gettop(J);
110 s = js_tostring(J, 0);
112 out = js_malloc(J, n + 1);
120 for (i = 1; i < top; ++i) {
121 s = js_tostring(J, i);
123 out = realloc(out, n + 1);
127 js_pushstring(J, out);
132 static void Sp_indexOf(js_State *J)
134 const char *haystack = js_tostring(J, 0);
135 const char *needle = js_tostring(J, 1);
136 int pos = js_tointeger(J, 2);
137 int len = strlen(needle);
141 if (k >= pos && !strncmp(haystack, needle, len)) {
145 haystack += chartorune(&rune, haystack);
148 js_pushnumber(J, -1);
151 static void Sp_lastIndexOf(js_State *J)
153 const char *haystack = js_tostring(J, 0);
154 const char *needle = js_tostring(J, 1);
155 int pos = js_isdefined(J, 2) ? js_tointeger(J, 2) : strlen(haystack);
156 int len = strlen(needle);
157 int k = 0, last = -1;
159 while (*haystack && k <= pos) {
160 if (!strncmp(haystack, needle, len))
162 haystack += chartorune(&rune, haystack);
165 js_pushnumber(J, last);
168 static void Sp_localeCompare(js_State *J)
170 const char *a = js_tostring(J, 0);
171 const char *b = js_tostring(J, 1);
172 js_pushnumber(J, strcmp(a, b));
175 static void Sp_slice(js_State *J)
177 const char *str = js_tostring(J, 0);
179 int len = utflen(str);
180 int s = js_tointeger(J, 1);
181 int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
183 s = s < 0 ? s + len : s;
184 e = e < 0 ? e + len : e;
186 s = s < 0 ? 0 : s > len ? len : s;
187 e = e < 0 ? 0 : e > len ? len : e;
190 ss = js_utfidxtoptr(str, s);
191 ee = js_utfidxtoptr(ss, e - s);
193 ss = js_utfidxtoptr(str, e);
194 ee = js_utfidxtoptr(ss, s - e);
197 js_pushlstring(J, ss, ee - ss);
200 static void Sp_substring(js_State *J)
202 const char *str = js_tostring(J, 0);
204 int len = utflen(str);
205 int s = js_tointeger(J, 1);
206 int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
208 s = s < 0 ? 0 : s > len ? len : s;
209 e = e < 0 ? 0 : e > len ? len : e;
212 ss = js_utfidxtoptr(str, s);
213 ee = js_utfidxtoptr(ss, e - s);
215 ss = js_utfidxtoptr(str, e);
216 ee = js_utfidxtoptr(ss, s - e);
219 js_pushlstring(J, ss, ee - ss);
222 static void Sp_toLowerCase(js_State *J)
224 const char *src = js_tostring(J, 0);
225 char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
230 s += chartorune(&rune, s);
231 rune = tolowerrune(rune);
232 d += runetochar(d, &rune);
239 js_pushstring(J, dst);
244 static void Sp_toUpperCase(js_State *J)
246 const char *src = js_tostring(J, 0);
247 char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
252 s += chartorune(&rune, s);
253 rune = toupperrune(rune);
254 d += runetochar(d, &rune);
261 js_pushstring(J, dst);
266 static int istrim(int c)
268 return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF ||
269 c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
272 static void Sp_trim(js_State *J)
275 s = js_tostring(J, 0);
279 while (e > s && istrim(e[-1]))
281 js_pushlstring(J, s, e - s);
284 static void S_fromCharCode(js_State *J)
286 unsigned int i, top = js_gettop(J);
290 s = p = js_malloc(J, (top-1) * UTFmax + 1);
297 for (i = 1; i < top; ++i) {
298 c = js_touint16(J, i);
299 p += runetochar(p, &c);
308 static void Sp_match(js_State *J)
313 const char *a, *b, *c, *e;
316 text = js_tostring(J, 0);
318 if (js_isregexp(J, 1))
320 else if (js_isundefined(J, 1))
321 js_newregexp(J, "", 0);
323 js_newregexp(J, js_tostring(J, 1), 0);
325 re = js_toregexp(J, -1);
326 if (!(re->flags & JS_REGEXP_G)) {
327 js_RegExp_prototype_exec(J, re, text);
337 e = text + strlen(text);
339 if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0))
345 js_pushlstring(J, b, c - b);
346 js_setindex(J, -2, len++);
354 static void Sp_search(js_State *J)
360 text = js_tostring(J, 0);
362 if (js_isregexp(J, 1))
364 else if (js_isundefined(J, 1))
365 js_newregexp(J, "", 0);
367 js_newregexp(J, js_tostring(J, 1), 0);
369 re = js_toregexp(J, -1);
371 if (!js_regexec(re->prog, text, &m, 0))
372 js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
374 js_pushnumber(J, -1);
377 static void Sp_replace_regexp(js_State *J)
380 const char *source, *s, *r;
381 js_Buffer *sb = NULL;
385 source = js_tostring(J, 0);
386 re = js_toregexp(J, 1);
388 if (js_regexec(re->prog, source, &m, 0)) {
397 n = m.sub[0].ep - m.sub[0].sp;
399 if (js_iscallable(J, 2)) {
401 js_pushundefinedthis(J);
402 for (x = 0; m.sub[x].sp; ++x) /* arg 0..x: substring and subexps that matched */
403 js_pushlstring(J, m.sub[x].sp, m.sub[x].ep - m.sub[x].sp);
404 js_pushnumber(J, s - source); /* arg x+2: offset within search string */
405 js_copy(J, 0); /* arg x+3: search string */
407 r = js_tostring(J, -1);
408 js_putm(J, &sb, source, s);
412 r = js_tostring(J, 2);
413 js_putm(J, &sb, source, s);
417 case '$': js_putc(J, &sb, '$'); break;
418 case '`': js_putm(J, &sb, source, s); break;
419 case '\'': js_puts(J, &sb, s + n); break;
421 js_putm(J, &sb, s, s + n);
423 case '0': case '1': case '2': case '3': case '4':
424 case '5': case '6': case '7': case '8': case '9':
426 if (r[1] >= '0' && r[1] <= '9')
427 x = x * 10 + *(++r) - '0';
428 if (x > 0 && x < m.nsub) {
429 js_putm(J, &sb, m.sub[x].sp, m.sub[x].ep);
431 js_putc(J, &sb, '$');
433 js_putc(J, &sb, '0' + x / 10);
434 js_putc(J, &sb, '0' + x % 10);
436 js_putc(J, &sb, '0' + x);
441 js_putc(J, &sb, '$');
447 js_putc(J, &sb, *r++);
452 if (re->flags & JS_REGEXP_G) {
453 source = m.sub[0].ep;
456 js_putc(J, &sb, *source++);
460 if (!js_regexec(re->prog, source, &m, REG_NOTBOL))
465 js_puts(J, &sb, s + n);
472 js_pushstring(J, sb ? sb->s : "");
477 static void Sp_replace_string(js_State *J)
479 const char *source, *needle, *s, *r;
480 js_Buffer *sb = NULL;
483 source = js_tostring(J, 0);
484 needle = js_tostring(J, 1);
486 s = strstr(source, needle);
493 if (js_iscallable(J, 2)) {
495 js_pushundefinedthis(J);
496 js_pushlstring(J, s, n); /* arg 1: substring that matched */
497 js_pushnumber(J, s - source); /* arg 2: offset within search string */
498 js_copy(J, 0); /* arg 3: search string */
500 r = js_tostring(J, -1);
501 js_putm(J, &sb, source, s);
503 js_puts(J, &sb, s + n);
507 r = js_tostring(J, 2);
508 js_putm(J, &sb, source, s);
512 case '$': js_putc(J, &sb, '$'); break;
513 case '&': js_putm(J, &sb, s, s + n); break;
514 case '`': js_putm(J, &sb, source, s); break;
515 case '\'': js_puts(J, &sb, s + n); break;
516 default: js_putc(J, &sb, '$'); js_putc(J, &sb, *r); break;
520 js_putc(J, &sb, *r++);
523 js_puts(J, &sb, s + n);
531 js_pushstring(J, sb ? sb->s : "");
536 static void Sp_replace(js_State *J)
538 if (js_isregexp(J, 1))
539 Sp_replace_regexp(J);
541 Sp_replace_string(J);
544 static void Sp_split_regexp(js_State *J)
548 unsigned int limit, len, k;
549 const char *p, *a, *b, *c, *e;
552 text = js_tostring(J, 0);
553 re = js_toregexp(J, 1);
554 limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30;
559 e = text + strlen(text);
561 /* splitting the empty string */
563 if (js_regexec(re->prog, text, &m, 0)) {
564 if (len == limit) return;
565 js_pushliteral(J, "");
566 js_setindex(J, -2, 0);
573 if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0))
574 break; /* no match */
579 /* empty string at end of last match */
585 if (len == limit) return;
586 js_pushlstring(J, p, b - p);
587 js_setindex(J, -2, len++);
589 for (k = 1; k < m.nsub; ++k) {
590 if (len == limit) return;
591 js_pushlstring(J, m.sub[k].sp, m.sub[k].ep - m.sub[k].sp);
592 js_setindex(J, -2, len++);
598 if (len == limit) return;
600 js_setindex(J, -2, len);
603 static void Sp_split_string(js_State *J)
605 const char *str = js_tostring(J, 0);
606 const char *sep = js_tostring(J, 1);
607 unsigned int limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30;
617 for (i = 0; *str && i < limit; ++i) {
618 n = chartorune(&rune, str);
619 js_pushlstring(J, str, n);
620 js_setindex(J, -2, i);
626 for (i = 0; str && i < limit; ++i) {
627 const char *s = strstr(str, sep);
629 js_pushlstring(J, str, s-str);
630 js_setindex(J, -2, i);
633 js_pushstring(J, str);
634 js_setindex(J, -2, i);
640 static void Sp_split(js_State *J)
642 if (js_isundefined(J, 1)) {
645 js_setindex(J, -2, 0);
646 } else if (js_isregexp(J, 1)) {
653 void jsB_initstring(js_State *J)
655 J->String_prototype->u.s.string = "";
656 J->String_prototype->u.s.length = 0;
658 js_pushobject(J, J->String_prototype);
660 jsB_propf(J, "toString", Sp_toString, 0);
661 jsB_propf(J, "valueOf", Sp_valueOf, 0);
662 jsB_propf(J, "charAt", Sp_charAt, 1);
663 jsB_propf(J, "charCodeAt", Sp_charCodeAt, 1);
664 jsB_propf(J, "concat", Sp_concat, 1);
665 jsB_propf(J, "indexOf", Sp_indexOf, 1);
666 jsB_propf(J, "lastIndexOf", Sp_lastIndexOf, 1);
667 jsB_propf(J, "localeCompare", Sp_localeCompare, 1);
668 jsB_propf(J, "match", Sp_match, 1);
669 jsB_propf(J, "replace", Sp_replace, 2);
670 jsB_propf(J, "search", Sp_search, 1);
671 jsB_propf(J, "slice", Sp_slice, 2);
672 jsB_propf(J, "split", Sp_split, 2);
673 jsB_propf(J, "substring", Sp_substring, 2);
674 jsB_propf(J, "toLowerCase", Sp_toLowerCase, 0);
675 jsB_propf(J, "toLocaleLowerCase", Sp_toLowerCase, 0);
676 jsB_propf(J, "toUpperCase", Sp_toUpperCase, 0);
677 jsB_propf(J, "toLocaleUpperCase", Sp_toUpperCase, 0);
680 jsB_propf(J, "trim", Sp_trim, 0);
682 js_newcconstructor(J, jsB_String, jsB_new_String, "String", 1);
684 jsB_propf(J, "fromCharCode", S_fromCharCode, 1);
686 js_defglobal(J, "String", JS_DONTENUM);