shithub: libmujs

ref: b7afd400f6fe637f73175b514a42b978fe62b74e
dir: /jsstring.c/

View raw version
#include "jsi.h"
#include "jsvalue.h"
#include "jsbuiltin.h"
#include "utf.h"

#include <regex.h>

static int jsB_new_String(js_State *J, int argc)
{
	js_newstring(J, argc > 0 ? js_tostring(J, 1) : "");
	return 1;
}

static int jsB_String(js_State *J, int argc)
{
	js_pushliteral(J, argc > 0 ? js_tostring(J, 1) : "");
	return 1;
}

static int Sp_toString(js_State *J, int argc)
{
	js_Object *self = js_toobject(J, 0);
	if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
	js_pushliteral(J, self->u.string);
	return 1;
}

static int Sp_valueOf(js_State *J, int argc)
{
	js_Object *self = js_toobject(J, 0);
	if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
	js_pushliteral(J, self->u.string);
	return 1;
}

static inline Rune runeAt(const char *s, int i)
{
	Rune rune = 0;
	while (i-- >= 0) {
		rune = *(unsigned char*)s;
		if (rune < Runeself) {
			if (rune == 0)
				return 0;
			++s;
		} else
			s += chartorune(&rune, s);
	}
	return rune;
}

static inline const char *utfidx(const char *s, int i)
{
	Rune rune = 0;
	while (i-- > 0) {
		rune = *(unsigned char*)s;
		if (rune < Runeself) {
			if (rune == 0)
				return NULL;
			++s;
		} else
			s += chartorune(&rune, s);
	}
	return s;
}

static int Sp_charAt(js_State *J, int argc)
{
	char buf[UTFmax + 1];
	const char *s = js_tostring(J, 0);
	int pos = js_tointeger(J, 1);
	Rune rune = runeAt(s, pos);
	if (rune > 0) {
		buf[runetochar(buf, &rune)] = 0;
		js_pushstring(J, buf);
	} else {
		js_pushliteral(J, "");
	}
	return 1;
}

static int Sp_charCodeAt(js_State *J, int argc)
{
	const char *s = js_tostring(J, 0);
	int pos = js_tointeger(J, 1);
	Rune rune = runeAt(s, pos);
	if (rune > 0)
		js_pushnumber(J, rune);
	else
		js_pushnumber(J, NAN);
	return 1;
}

static int Sp_concat(js_State *J, int argc)
{
	char * volatile out;
	const char *s;
	int i, n;

	if (argc == 0)
		return 1;

	s = js_tostring(J, 0);
	n = strlen(s);
	out = malloc(n + 1);
	strcpy(out, s);

	if (js_try(J)) {
		free(out);
		js_throw(J);
	}

	for (i = 1; i <= argc; ++i) {
		s = js_tostring(J, i);
		n += strlen(s);
		out = realloc(out, n + 1);
		strcat(out, s);
	}

	js_pushstring(J, out);
	js_endtry(J);
	free(out);
	return 1;
}

static int Sp_indexOf(js_State *J, int argc)
{
	const char *haystack = js_tostring(J, 0);
	const char *needle = js_tostring(J, 1);
	int pos = js_tointeger(J, 2);
	int len = strlen(needle);
	int k = 0;
	Rune rune;
	while (*haystack) {
		if (k >= pos && !strncmp(haystack, needle, len)) {
			js_pushnumber(J, k);
			return 1;
		}
		haystack += chartorune(&rune, haystack);
		++k;
	}
	js_pushnumber(J, -1);
	return 1;
}

static int Sp_lastIndexOf(js_State *J, int argc)
{
	const char *haystack = js_tostring(J, 0);
	const char *needle = js_tostring(J, 1);
	int pos = argc > 1 ? js_tointeger(J, 2) : strlen(haystack);
	int len = strlen(needle);
	int k = 0, last = -1;
	Rune rune;
	while (*haystack && k <= pos) {
		if (!strncmp(haystack, needle, len))
			last = k;
		haystack += chartorune(&rune, haystack);
		++k;
	}
	js_pushnumber(J, last);
	return 1;
}

static int Sp_localeCompare(js_State *J, int argc)
{
	const char *a = js_tostring(J, 0);
	const char *b = js_tostring(J, 1);
	return strcmp(a, b);
}

static int Sp_slice(js_State *J, int argc)
{
	const char *str = js_tostring(J, 0);
	const char *ss, *ee;
	int len = utflen(str);
	int s = js_tointeger(J, 1);
	int e = argc > 1 ? js_tointeger(J, 2) : len;

	s = s < 0 ? s + len : s;
	e = e < 0 ? e + len : e;

	s = s < 0 ? 0 : s > len ? len : s;
	e = e < 0 ? 0 : e > len ? len : e;

	if (s < e) {
		ss = utfidx(str, s);
		ee = utfidx(ss, e - s);
	} else {
		ss = utfidx(str, e);
		ee = utfidx(ss, s - e);
	}

	js_pushlstring(J, ss, ee - ss);
	return 1;
}

static int Sp_substring(js_State *J, int argc)
{
	const char *str = js_tostring(J, 0);
	const char *ss, *ee;
	int len = utflen(str);
	int s = js_tointeger(J, 1);
	int e = argc > 1 ? js_tointeger(J, 2) : len;

	s = s < 0 ? 0 : s > len ? len : s;
	e = e < 0 ? 0 : e > len ? len : e;

	if (s < e) {
		ss = utfidx(str, s);
		ee = utfidx(ss, e - s);
	} else {
		ss = utfidx(str, e);
		ee = utfidx(ss, s - e);
	}

	js_pushlstring(J, ss, ee - ss);
	return 1;
}

static int Sp_toLowerCase(js_State *J, int argc)
{
	const char *src = js_tostring(J, 0);
	char *dst = malloc(UTFmax * strlen(src) + 1);
	const char *s = src;
	char *d = dst;
	Rune rune;
	while (*s) {
		s += chartorune(&rune, s);
		rune = tolowerrune(rune);
		d += runetochar(d, &rune);
	}
	if (js_try(J)) {
		free(dst);
		js_throw(J);
	}
	js_pushstring(J, dst);
	js_endtry(J);
	free(dst);
	return 1;
}

static int Sp_toUpperCase(js_State *J, int argc)
{
	const char *src = js_tostring(J, 0);
	char *dst = malloc(UTFmax * strlen(src) + 1);
	const char *s = src;
	char *d = dst;
	Rune rune;
	while (*s) {
		s += chartorune(&rune, s);
		rune = toupperrune(rune);
		d += runetochar(d, &rune);
	}
	if (js_try(J)) {
		free(dst);
		js_throw(J);
	}
	js_pushstring(J, dst);
	js_endtry(J);
	free(dst);
	return 1;
}

static int S_fromCharCode(js_State *J, int argc)
{
	int i;
	Rune c;
	char *s, *p;

	s = p = malloc(argc * UTFmax + 1);

	if (js_try(J)) {
		free(s);
		js_throw(J);
	}

	for (i = 1; i <= argc; ++i) {
		c = js_tointeger(J, i); // TODO: ToUInt16()
		p += runetochar(p, &c);
	}
	*p = 0;
	js_pushstring(J, s);

	js_endtry(J);
	free(s);
	return 1;
}

static int Sp_match(js_State *J, int argc)
{
	const char *text;

	text = js_tostring(J, 0);

	if (js_isregexp(J, 1))
		js_copy(J, 1);
	else if (js_isundefined(J, 1))
		js_newregexp(J, "", 0);
	else
		js_newregexp(J, js_tostring(J, 1), 0);

	// TODO: JS_REGEXP_G looping

	return js_RegExp_prototype_exec(J, -1, text);
}

static int Sp_search(js_State *J, int argc)
{
	const char *text;
	regmatch_t m;
	regex_t *prog;
	int flags;

	text = js_tostring(J, 0);

	if (js_isregexp(J, 1))
		js_copy(J, 1);
	else if (js_isundefined(J, 1))
		js_newregexp(J, "", 0);
	else
		js_newregexp(J, js_tostring(J, 1), 0);

	prog = js_toregexp(J, -1, &flags);

	if (!regexec(prog, text, 1, &m, 0))
		js_pushnumber(J, m.rm_so); // TODO: convert to utf-8 index offset
	else
		js_pushnumber(J, -1);

	return 1;
}

void jsB_initstring(js_State *J)
{
	J->String_prototype->u.string = "";

	js_pushobject(J, J->String_prototype);
	{
		jsB_propf(J, "toString", Sp_toString, 0);
		jsB_propf(J, "valueOf", Sp_valueOf, 0);
		jsB_propf(J, "charAt", Sp_charAt, 1);
		jsB_propf(J, "charCodeAt", Sp_charCodeAt, 1);
		jsB_propf(J, "concat", Sp_concat, 1);
		jsB_propf(J, "indexOf", Sp_indexOf, 1);
		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, "search", Sp_search, 1);
		jsB_propf(J, "slice", Sp_slice, 2);
		// split (uses regexp)
		jsB_propf(J, "substring", Sp_substring, 2);
		jsB_propf(J, "toLowerCase", Sp_toLowerCase, 0);
		jsB_propf(J, "toLocaleLowerCase", Sp_toLowerCase, 0);
		jsB_propf(J, "toUpperCase", Sp_toUpperCase, 0);
		jsB_propf(J, "toLocaleUpperCase", Sp_toUpperCase, 0);
		// trim (ES5)
	}
	js_newcconstructor(J, jsB_String, jsB_new_String, 1);
	{
		jsB_propf(J, "fromCharCode", S_fromCharCode, 1);
	}
	js_defglobal(J, "String", JS_DONTENUM);
}