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;