From 8e10640e9e1ffbfc402a53d127c50eec8a8a8b05 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 28 Jan 2014 17:15:10 +0100 Subject: [PATCH] Add RegExp class (with no actual regex implementation hooked up). --- js.h | 2 + jsbuiltin.c | 2 + jsbuiltin.h | 1 + jscompile.c | 6 +++ jscompile.h | 1 + jsdump.c | 5 ++ jsi.h | 3 ++ jsregexp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++ jsrun.c | 15 ++++++ jsvalue.h | 4 ++ 10 files changed, 168 insertions(+) create mode 100644 jsregexp.c diff --git a/js.h b/js.h index 88e097a..1704df4 100644 --- a/js.h +++ b/js.h @@ -118,6 +118,7 @@ void js_newerror(js_State *J, const char *message); 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_isstring(js_State *J, int idx); 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); diff --git a/jsbuiltin.c b/jsbuiltin.c index f8849f3..cd0cb12 100644 --- a/jsbuiltin.c +++ b/jsbuiltin.c @@ -75,6 +75,7 @@ void jsB_init(js_State *J) 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 @@ void jsB_init(js_State *J) jsB_initboolean(J); jsB_initnumber(J); jsB_initstring(J); + jsB_initregexp(J); jsB_initerror(J); jsB_initmath(J); jsB_initdate(J); diff --git a/jsbuiltin.h b/jsbuiltin.h index 5d19d8a..c27ff47 100644 --- a/jsbuiltin.h +++ b/jsbuiltin.h @@ -8,6 +8,7 @@ void jsB_initfunction(js_State *J); 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); diff --git a/jscompile.c b/jscompile.c index f2a1387..a0b09a4 100644 --- a/jscompile.c +++ b/jscompile.c @@ -402,6 +402,12 @@ static void cexp(JF, js_Ast *exp) 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); diff --git a/jscompile.h b/jscompile.h index 9174b6c..39670b5 100644 --- a/jscompile.h +++ b/jscompile.h @@ -26,6 +26,7 @@ enum js_OpCode OP_NEWARRAY, OP_NEWOBJECT, + OP_NEWREGEXP, OP_UNDEF, OP_NULL, diff --git a/jsdump.c b/jsdump.c index c3a69cd..9a84647 100644 --- a/jsdump.c +++ b/jsdump.c @@ -642,6 +642,11 @@ void jsC_dumpfunction(js_State *J, js_Function *F) 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: diff --git a/jsi.h b/jsi.h index 332bc00..29b5e0b 100644 --- a/jsi.h +++ b/jsi.h @@ -38,6 +38,8 @@ void jsS_freestrings(js_State *J); 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 @@ struct js_State js_Object *Boolean_prototype; js_Object *Number_prototype; js_Object *String_prototype; + js_Object *RegExp_prototype; js_Object *Date_prototype; js_Object *Error_prototype; diff --git a/jsregexp.c b/jsregexp.c new file mode 100644 index 0000000..d1d4f86 --- /dev/null +++ b/jsregexp.c @@ -0,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); +} diff --git a/jsrun.c b/jsrun.c index a5efb0d..0cf3489 100644 --- a/jsrun.c +++ b/jsrun.c @@ -104,6 +104,12 @@ int js_isarray(js_State *J, int idx) 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 @@ js_Value js_toprimitive(js_State *J, int idx, int hint) 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 @@ static void jsR_run(js_State *J, js_Function *F) 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; diff --git a/jsvalue.h b/jsvalue.h index 4d89f9b..f6698e3 100644 --- a/jsvalue.h +++ b/jsvalue.h @@ -65,6 +65,10 @@ struct js_Object js_CFunction function; js_CFunction constructor; } c; + struct { + void *prog; + int flags; + } r; struct { js_Object *target; js_Iterator *head;