shithub: libmujs

Download patch

ref: 8e10640e9e1ffbfc402a53d127c50eec8a8a8b05
parent: 01cab639cb79d1835b2531604aaa8bf8290ff55d
author: Tor Andersson <tor@ccxvii.net>
date: Tue Jan 28 12:15:10 EST 2014

Add RegExp class (with no actual regex implementation hooked up).

--- a/js.h
+++ b/js.h
@@ -118,6 +118,7 @@
 void js_newcfunction(js_State *J, js_CFunction fun, int length);
 void js_newcconstructor(js_State *J, js_CFunction fun, js_CFunction con, int length);
 void js_newuserdata(js_State *J, const char *tag, void *data);
+void js_newregexp(js_State *J, const char *pattern, int flags);
 
 int js_isundefined(js_State *J, int idx);
 int js_isnull(js_State *J, int idx);
@@ -127,6 +128,7 @@
 int js_isprimitive(js_State *J, int idx);
 int js_isobject(js_State *J, int idx);
 int js_isarray(js_State *J, int idx);
+int js_isregexp(js_State *J, int idx);
 int js_iscallable(js_State *J, int idx);
 int js_isuserdata(js_State *J, const char *tag, int idx);
 
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -75,6 +75,7 @@
 	J->Boolean_prototype = jsV_newobject(J, JS_CBOOLEAN, J->Object_prototype);
 	J->Number_prototype = jsV_newobject(J, JS_CNUMBER, J->Object_prototype);
 	J->String_prototype = jsV_newobject(J, JS_CSTRING, J->Object_prototype);
+	J->RegExp_prototype = jsV_newobject(J, JS_COBJECT, J->Object_prototype);
 	J->Date_prototype = jsV_newobject(J, JS_CDATE, J->Object_prototype);
 
 	/* All the native error types */
@@ -93,6 +94,7 @@
 	jsB_initboolean(J);
 	jsB_initnumber(J);
 	jsB_initstring(J);
+	jsB_initregexp(J);
 	jsB_initerror(J);
 	jsB_initmath(J);
 	jsB_initdate(J);
--- a/jsbuiltin.h
+++ b/jsbuiltin.h
@@ -8,6 +8,7 @@
 void jsB_initboolean(js_State *J);
 void jsB_initnumber(js_State *J);
 void jsB_initstring(js_State *J);
+void jsB_initregexp(js_State *J);
 void jsB_initerror(js_State *J);
 void jsB_initmath(js_State *J);
 void jsB_initdate(js_State *J);
--- a/jscompile.c
+++ b/jscompile.c
@@ -402,6 +402,12 @@
 	case EXP_FALSE: emit(J, F, OP_FALSE); break;
 	case EXP_THIS: emit(J, F, OP_THIS); break;
 
+	case AST_REGEXP:
+		emit(J, F, OP_NEWREGEXP);
+		emitraw(J, F, addstring(J, F, exp->string));
+		emitraw(J, F, exp->number);
+		break;
+
 	case EXP_OBJECT:
 		emit(J, F, OP_NEWOBJECT);
 		cobject(J, F, exp->a);
--- a/jscompile.h
+++ b/jscompile.h
@@ -26,6 +26,7 @@
 
 	OP_NEWARRAY,
 	OP_NEWOBJECT,
+	OP_NEWREGEXP,
 
 	OP_UNDEF,
 	OP_NULL,
--- a/jsdump.c
+++ b/jsdump.c
@@ -642,6 +642,11 @@
 			pc(' ');
 			pstr(F->strtab[*p++]);
 			break;
+		case OP_NEWREGEXP:
+			pc(' ');
+			pregexp(F->strtab[p[0]], p[1]);
+			p += 2;
+			break;
 
 		case OP_FUNDEC:
 		case OP_VARDEC:
--- a/jsi.h
+++ b/jsi.h
@@ -38,6 +38,8 @@
 void js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
 void js_newscript(js_State *J, js_Function *function);
 
+void *js_toregexp(js_State *J, int idx, int *flags);
+
 void js_dup(js_State *J);
 void js_rot2(js_State *J);
 void js_rot3(js_State *J);
@@ -97,6 +99,7 @@
 	js_Object *Boolean_prototype;
 	js_Object *Number_prototype;
 	js_Object *String_prototype;
+	js_Object *RegExp_prototype;
 	js_Object *Date_prototype;
 
 	js_Object *Error_prototype;
--- /dev/null
+++ b/jsregexp.c
@@ -1,0 +1,129 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+void js_newregexp(js_State *J, const char *pattern, int flags)
+{
+	js_Object *obj;
+
+	obj = jsV_newobject(J, JS_CREGEXP, J->RegExp_prototype);
+	obj->u.r.prog = NULL;
+	obj->u.r.flags = flags;
+	js_pushobject(J, obj);
+
+	js_pushstring(J, pattern);
+	js_defproperty(J, -2, "source", JS_READONLY | JS_DONTENUM | JS_DONTDELETE);
+	js_pushboolean(J, flags & JS_REGEXP_G);
+	js_defproperty(J, -2, "global", JS_READONLY | JS_DONTENUM | JS_DONTDELETE);
+	js_pushboolean(J, flags & JS_REGEXP_I);
+	js_defproperty(J, -2, "ignoreCase", JS_READONLY | JS_DONTENUM | JS_DONTDELETE);
+	js_pushboolean(J, flags & JS_REGEXP_M);
+	js_defproperty(J, -2, "multiline", JS_READONLY | JS_DONTENUM | JS_DONTDELETE);
+
+	// TODO: lastIndex
+}
+
+static int jsB_new_RegExp(js_State *J, int argc)
+{
+	const char *pattern;
+	int flags;
+
+	if (js_isregexp(J, 1)) {
+		if (argc > 1)
+			js_typeerror(J, "cannot supply flags when creating one RegExp from another");
+		js_toregexp(J, 1, &flags);
+		js_getproperty(J, 1, "source");
+		pattern = js_tostring(J, -1);
+		js_pop(J, 1);
+	} else if (js_isundefined(J, 1)) {
+		pattern = "";
+	} else {
+		pattern = js_tostring(J, 1);
+	}
+
+	if (argc > 1) {
+		const char *s = js_tostring(J, 2);
+		int g = 0, i = 0, m = 0;
+		while (*s) {
+			if (*s == 'g') ++g;
+			else if (*s == 'i') ++i;
+			else if (*s == 'm') ++m;
+			else js_syntaxerror(J, "invalid regular expression flag: '%c'", *s);
+			++s;
+		}
+		if (g > 1) js_syntaxerror(J, "invalid regular expression flag: 'g'");
+		if (i > 1) js_syntaxerror(J, "invalid regular expression flag: 'i'");
+		if (m > 1) js_syntaxerror(J, "invalid regular expression flag: 'm'");
+		if (g) flags |= JS_REGEXP_G;
+		if (i) flags |= JS_REGEXP_I;
+		if (m) flags |= JS_REGEXP_M;
+	}
+
+	js_newregexp(J, pattern, flags);
+	return 1;
+}
+
+static int jsB_RegExp(js_State *J, int argc)
+{
+	if (js_isregexp(J, 1) && argc == 1)
+		return 1;
+	return jsB_new_RegExp(J, argc);
+}
+
+static int Rp_toString(js_State *J, int argc)
+{
+	const char *source;
+	int flags;
+	char *out;
+
+	js_Object *self = js_toobject(J, 0);
+	if (self->type != JS_CREGEXP)
+		js_typeerror(J, "not a regexp");
+
+	flags = self->u.r.flags;
+
+	js_getproperty(J, 0, "source");
+	source = js_tostring(J, -1);
+
+	out = malloc(strlen(source) + 6); /* extra space for //gim */
+	strcpy(out, "/");
+	strcat(out, source);
+	strcat(out, "/");
+	if (flags & JS_REGEXP_G) strcat(out, "g");
+	if (flags & JS_REGEXP_I) strcat(out, "i");
+	if (flags & JS_REGEXP_M) strcat(out, "m");
+
+	if (js_try(J)) {
+		free(out);
+		js_throw(J);
+	}
+	js_pop(J, 0);
+	js_pushstring(J, out);
+	js_endtry(J);
+	free(out);
+	return 1;
+}
+
+static int Rp_exec(js_State *J, int argc)
+{
+	js_pushnull(J);
+	return 1;
+}
+
+static int Rp_test(js_State *J, int argc)
+{
+	js_pushboolean(J, 0);
+	return 1;
+}
+
+void jsB_initregexp(js_State *J)
+{
+	js_pushobject(J, J->RegExp_prototype);
+	{
+		jsB_propf(J, "toString", Rp_toString, 0);
+		jsB_propf(J, "test", Rp_test, 0);
+		jsB_propf(J, "exec", Rp_exec, 0);
+	}
+	js_newcconstructor(J, jsB_RegExp, jsB_new_RegExp, 1);
+	js_defglobal(J, "RegExp", JS_DONTENUM);
+}
--- a/jsrun.c
+++ b/jsrun.c
@@ -104,6 +104,12 @@
 	return v->type == JS_TOBJECT && v->u.object->type == JS_CARRAY;
 }
 
+int js_isregexp(js_State *J, int idx)
+{
+	const js_Value *v = stackidx(J, idx);
+	return v->type == JS_TOBJECT && v->u.object->type == JS_CREGEXP;
+}
+
 int js_isiterator(js_State *J, int idx)
 {
 	const js_Value *v = stackidx(J, idx);
@@ -176,6 +182,14 @@
 	return jsV_toprimitive(J, stackidx(J, idx), hint);
 }
 
+void *js_toregexp(js_State *J, int idx, int *flags)
+{
+	const js_Value *v = stackidx(J, idx);
+	if (v->type == JS_TOBJECT && v->u.object->type == JS_CREGEXP)
+		return *flags = v->u.object->u.r.flags, v->u.object->u.r.prog;
+	js_typeerror(J, "not a regexp");
+}
+
 void *js_touserdata(js_State *J, const char *tag, int idx)
 {
 	const js_Value *v = stackidx(J, idx);
@@ -743,6 +757,7 @@
 		case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break;
 		case OP_NEWOBJECT: js_newobject(J); break;
 		case OP_NEWARRAY: js_newarray(J); break;
+		case OP_NEWREGEXP: js_newregexp(J, ST[pc[0]], pc[1]); pc += 2; break;
 
 		case OP_UNDEF: js_pushundefined(J); break;
 		case OP_NULL: js_pushnull(J); break;
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -66,6 +66,10 @@
 			js_CFunction constructor;
 		} c;
 		struct {
+			void *prog;
+			int flags;
+		} r;
+		struct {
 			js_Object *target;
 			js_Iterator *head;
 		} iter;