shithub: libmujs

Download patch

ref: d949e161184d8eeddd0d92edb68e617cb692c94f
parent: b7afd400f6fe637f73175b514a42b978fe62b74e
author: Tor Andersson <[email protected]>
date: Fri Jan 31 12:08:50 EST 2014

String.replace()

--- a/jsstring.c
+++ b/jsstring.c
@@ -5,6 +5,37 @@
 
 #include <regex.h>
 
+#define nelem(a) (sizeof (a) / sizeof (a)[0])
+
+struct sbuffer { int n, m; char s[64]; };
+
+static struct sbuffer *sb_putc(struct sbuffer *sb, int c)
+{
+	if (!sb) {
+		sb = malloc(sizeof *sb);
+		sb->n = 0;
+		sb->m = sizeof sb->s;
+	} else if (sb->n == sb->m) {
+		sb = realloc(sb, (sb->m *= 2) + offsetof(struct sbuffer, s));
+	}
+	sb->s[sb->n++] = c;
+	return sb;
+}
+
+static struct sbuffer *sb_puts(struct sbuffer *sb, const char *s)
+{
+	while (*s)
+		sb = sb_putc(sb, *s++);
+	return sb;
+}
+
+static struct sbuffer *sb_putm(struct sbuffer *sb, const char *s, const char *e)
+{
+	while (s < e)
+		sb = sb_putc(sb, *s++);
+	return sb;
+}
+
 static int jsB_new_String(js_State *J, int argc)
 {
 	js_newstring(J, argc > 0 ? js_tostring(J, 1) : "");
@@ -329,6 +360,159 @@
 	return 1;
 }
 
+static int Sp_replace_regexp(js_State *J, int argc)
+{
+	const char *source, *s, *r;
+	regmatch_t m[10];
+	regex_t *prog;
+	int flags;
+	struct sbuffer *sb = NULL;
+	int n, x;
+
+	source = js_tostring(J, 0);
+	prog = js_toregexp(J, 1, &flags);
+
+	if (regexec(prog, source, nelem(m), m, 0)) {
+		js_copy(J, 0);
+		return 1;
+	}
+
+loop:
+	s = source + m[0].rm_so;
+	n = m[0].rm_eo - m[0].rm_so;
+
+	if (js_iscallable(J, 2)) {
+		js_copy(J, 2);
+		js_pushglobal(J);
+		for (x = 0; m[x].rm_so >= 0; ++x) /* arg 0..x: substring and subexps that matched */
+			js_pushlstring(J, source + m[x].rm_so, m[x].rm_eo - m[x].rm_so);
+		js_pushnumber(J, s - source); /* arg x+2: offset within search string */
+		js_copy(J, 0); /* arg x+3: search string */
+		js_call(J, 2 + x);
+		r = js_tostring(J, -1);
+		sb = sb_putm(sb, source, s);
+		sb = sb_puts(sb, r);
+		js_pop(J, 1);
+	} else {
+		r = js_tostring(J, 2);
+		sb = sb_putm(sb, source, s);
+		while (*r) {
+			if (*r == '$') {
+				switch (*(++r)) {
+				case '$': sb = sb_putc(sb, '$'); break;
+				case '`': sb = sb_putm(sb, source, s); break;
+				case '\'': sb = sb_puts(sb, s + n); break;
+				case '&':
+					sb = sb_putm(sb, s, s + n);
+					break;
+				case '0': case '1': case '2': case '3': case '4':
+				case '5': case '6': case '7': case '8': case '9':
+					x = *r - '0';
+					if (m[x].rm_so >= 0) {
+						sb = sb_putm(sb, source + m[x].rm_so, source + m[x].rm_eo);
+					} else {
+						sb = sb_putc(sb, '$');
+						sb = sb_putc(sb, '0'+x);
+					}
+					break;
+				default:
+					sb = sb_putc(sb, '$');
+					sb = sb_putc(sb, *r);
+					break;
+				}
+				++r;
+			} else {
+				sb = sb_putc(sb, *r++);
+			}
+		}
+	}
+
+	if (flags & JS_REGEXP_G) {
+		source = source + m[0].rm_eo;
+		if (!regexec(prog, source, nelem(m), m, REG_NOTBOL))
+			goto loop;
+	}
+
+	sb = sb_puts(sb, s + n);
+	sb = sb_putc(sb, 0);
+
+	if (js_try(J)) {
+		free(sb);
+		js_throw(J);
+	}
+	js_pushstring(J, sb ? sb->s : "");
+	js_endtry(J);
+	free(sb);
+	return 1;
+}
+
+static int Sp_replace_string(js_State *J, int argc)
+{
+	const char *source, *needle, *s, *r;
+	struct sbuffer *sb = NULL;
+	int n;
+
+	source = js_tostring(J, 0);
+	needle = js_tostring(J, 1);
+
+	s = strstr(source, needle);
+	if (!s) {
+		js_copy(J, 0);
+		return 1;
+	}
+	n = strlen(needle);
+
+	if (js_iscallable(J, 2)) {
+		js_copy(J, 2);
+		js_pushglobal(J);
+		js_pushlstring(J, s, n); /* arg 1: substring that matched */
+		js_pushnumber(J, s - source); /* arg 2: offset within search string */
+		js_copy(J, 0); /* arg 3: search string */
+		js_call(J, 3);
+		r = js_tostring(J, -1);
+		sb = sb_putm(sb, source, s);
+		sb = sb_puts(sb, r);
+		sb = sb_puts(sb, s + n);
+		sb = sb_putc(sb, 0);
+		js_pop(J, 1);
+	} else {
+		r = js_tostring(J, 2);
+		sb = sb_putm(sb, source, s);
+		while (*r) {
+			if (*r == '$') {
+				switch (*(++r)) {
+				case '$': sb = sb_putc(sb, '$'); break;
+				case '&': sb = sb_putm(sb, s, s + n); break;
+				case '`': sb = sb_putm(sb, source, s); break;
+				case '\'': sb = sb_puts(sb, s + n); break;
+				default: sb = sb_putc(sb, '$'); sb = sb_putc(sb, *r); break;
+				}
+				++r;
+			} else {
+				sb = sb_putc(sb, *r++);
+			}
+		}
+		sb = sb_puts(sb, s + n);
+		sb = sb_putc(sb, 0);
+	}
+
+	if (js_try(J)) {
+		free(sb);
+		js_throw(J);
+	}
+	js_pushstring(J, sb ? sb->s : "");
+	js_endtry(J);
+	free(sb);
+	return 1;
+}
+
+static int Sp_replace(js_State *J, int argc)
+{
+	if (js_isregexp(J, 1))
+		return Sp_replace_regexp(J, argc);
+	return Sp_replace_string(J, argc);
+}
+
 void jsB_initstring(js_State *J)
 {
 	J->String_prototype->u.string = "";
@@ -344,7 +528,7 @@
 		jsB_propf(J, "lastIndexOf", Sp_lastIndexOf, 1);
 		jsB_propf(J, "localeCompare", Sp_localeCompare, 1);
 		jsB_propf(J, "match", Sp_match, 1);
-		// replace (uses regexp)
+		jsB_propf(J, "replace", Sp_replace, 2);
 		jsB_propf(J, "search", Sp_search, 1);
 		jsB_propf(J, "slice", Sp_slice, 2);
 		// split (uses regexp)