ref: 288eef80e136441fe00bc618b523139cb6bb5bca
parent: 3715d6d145225f957de9f40d3154e0ef458b94e6
author: Tor Andersson <[email protected]>
date: Thu Jan 16 10:54:27 EST 2014
Clean up. Rearrange files. Rename functions.
--- a/js.h
+++ b/js.h
@@ -44,38 +44,16 @@
int js_loadstring(js_State *J, const char *s);
int js_loadfile(js_State *J, const char *filename);
-/* binding API */
+/* binding API: TODO: move from jsrun.h */
typedef int (*js_CFunction)(js_State *J, int argc);
-typedef struct js_Object js_Object;
-void js_pushundefined(js_State *J);
-void js_pushnull(js_State *J);
-void js_pushboolean(js_State *J, int v);
-void js_pushnumber(js_State *J, double v);
-void js_pushlstring(js_State *J, const char *v);
-void js_pushstring(js_State *J, const char *v);
-void js_pushobject(js_State *J, js_Object *v);
-
-int js_isundefined(js_State *J, int idx);
-int js_isstring(js_State *J, int idx);
-
-int js_toboolean(js_State *J, int idx);
-double js_tonumber(js_State *J, int idx);
-double js_tointeger(js_State *J, int idx);
-const char *js_tostring(js_State *J, int idx);
-js_Object *js_toobject(js_State *J, int idx);
-
-void js_pop(js_State *J, int n);
-void js_dup(js_State *J);
-
-void js_setglobal(js_State *J, const char *name);
-
/* private */
typedef struct js_Ast js_Ast;
typedef struct js_Environment js_Environment;
typedef struct js_Function js_Function;
+typedef struct js_Object js_Object;
typedef struct js_StringNode js_StringNode;
const char *js_intern(js_State *J, const char *s);
--- a/jsdump.c
+++ b/jsdump.c
@@ -655,3 +655,36 @@
}
}
}
+
+/* Runtime values */
+
+void js_dumpvalue(js_State *J, js_Value v)
+{
+ switch (v.type) {
+ case JS_TUNDEFINED: printf("undefined"); break;
+ case JS_TNULL: printf("null"); break;
+ case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
+ case JS_TNUMBER: printf("%.9g", v.u.number); break;
+ case JS_TSTRING: printf("'%s'", v.u.string); break;
+ case JS_TOBJECT: printf("<object %p>", v.u.object); break;
+ }
+}
+
+static void js_dumpproperty(js_State *J, js_Property *node)
+{
+ if (node->left->level)
+ js_dumpproperty(J, node->left);
+ printf("\t%s: ", node->name);
+ js_dumpvalue(J, node->value);
+ printf(",\n");
+ if (node->right->level)
+ js_dumpproperty(J, node->right);
+}
+
+void js_dumpobject(js_State *J, js_Object *obj)
+{
+ printf("{\n");
+ if (obj->properties->level)
+ js_dumpproperty(J, obj->properties);
+ printf("}\n");
+}
--- a/jslex.c
+++ b/jslex.c
@@ -1,6 +1,6 @@
#include "js.h"
-#include "jsstate.h"
#include "jslex.h"
+#include "jsstate.h"
#define nelem(a) (sizeof (a) / sizeof (a)[0])
--- a/jslex.h
+++ b/jslex.h
@@ -1,7 +1,8 @@
#ifndef js_lex_h
#define js_lex_h
-enum {
+enum
+{
TK_IDENTIFIER = 256,
TK_NUMBER,
TK_STRING,
@@ -65,9 +66,11 @@
TK_WITH,
};
+const char *jsP_tokenstring(int token);
+
void jsP_initlex(js_State *J, const char *filename, const char *source);
int jsP_lex(js_State *J);
-const char *jsP_tokenstring(int token);
+
JS_NORETURN int jsP_error(js_State *J, const char *fmt, ...);
void jsP_warning(js_State *J, const char *fmt, ...);
--- a/jsload.c
+++ b/jsload.c
@@ -1,6 +1,7 @@
#include "js.h"
#include "jsparse.h"
#include "jscompile.h"
+#include "jsobject.h"
#include "jsrun.h"
static int jsP_loadstring(js_State *J, const char *filename, const char *source)
--- a/jsobject.c
+++ b/jsobject.c
@@ -1,248 +1,38 @@
#include "js.h"
#include "jsobject.h"
-/*
- Use an AA-tree to quickly look up properties in objects:
-
- The level of every leaf node is one.
- The level of every left child is one less than its parent.
- The level of every right child is equal or one less than its parent.
- The level of every right grandchild is less than its grandparent.
- Every node of level greater than one has two children.
-
- A link where the child's level is equal to that of its parent is called a horizontal link.
- Individual right horizontal links are allowed, but consecutive ones are forbidden.
- Left horizontal links are forbidden.
-
- skew() fixes left horizontal links.
- split() fixes consecutive right horizontal links.
-*/
-
-static js_Property sentinel = { "", &sentinel, &sentinel, 0 };
-
-static js_Property *newproperty(const char *name)
+js_Object *jsR_newfunction(js_State *J, js_Function *function, js_Environment *scope)
{
- js_Property *node = malloc(sizeof(js_Property));
- node->name = strdup(name);
- node->left = node->right = &sentinel;
- node->level = 1;
- node->value.type = JS_TUNDEFINED;
- node->value.u.number = 0;
- node->flags = 0;
- return node;
-}
-
-static js_Property *lookup(js_Property *node, const char *name)
-{
- while (node != &sentinel) {
- int c = strcmp(name, node->name);
- if (c == 0)
- return node;
- else if (c < 0)
- node = node->left;
- else
- node = node->right;
- }
- return NULL;
-}
-
-static inline js_Property *skew(js_Property *node)
-{
- if (node->level != 0) {
- if (node->left->level == node->level) {
- js_Property *save = node;
- node = node->left;
- save->left = node->right;
- node->right = save;
- }
- node->right = skew(node->right);
- }
- return node;
-}
-
-static inline js_Property *split(js_Property *node)
-{
- if (node->level != 0 && node->right->right->level == node->level) {
- js_Property *save = node;
- node = node->right;
- save->right = node->left;
- node->left = save;
- node->level++;
- node->right = split(node->right);
- }
- return node;
-}
-
-static js_Property *insert(js_Property *node, const char *name, js_Property **result)
-{
- if (node != &sentinel) {
- int c = strcmp(name, node->name);
- if (c < 0)
- node->left = insert(node->left, name, result);
- else if (c > 0)
- node->right = insert(node->right, name, result);
- else
- return *result = node;
- node = skew(node);
- node = split(node);
- return node;
- }
- return *result = newproperty(name);
-}
-
-static js_Property *lookupfirst(js_Property *node)
-{
- while (node != &sentinel) {
- if (node->left == &sentinel)
- return node;
- node = node->left;
- }
- return NULL;
-}
-
-static js_Property *lookupnext(js_Property *node, const char *name)
-{
- js_Property *stack[100], *parent;
- int top = 0;
-
- stack[0] = NULL;
- while (node != &sentinel) {
- stack[++top] = node;
- int c = strcmp(name, node->name);
- if (c == 0)
- goto found;
- else if (c < 0)
- node = node->left;
- else
- node = node->right;
- }
- return NULL;
-
-found:
- if (node->right != &sentinel)
- return lookupfirst(node->right);
- parent = stack[--top];
- while (parent && node == parent->right) {
- node = parent;
- parent = stack[--top];
- }
- return parent;
-}
-
-js_Object *js_newobject(js_State *J, js_Class type)
-{
- js_Object *obj = malloc(sizeof(js_Object));
- obj->type = type;
- obj->properties = &sentinel;
- obj->prototype = NULL;
- obj->primitive.number = 0;
- obj->scope = NULL;
- obj->function = NULL;
- obj->cfunction = NULL;
- return obj;
-}
-
-js_Object *js_newfunction(js_State *J, js_Function *function, js_Environment *scope)
-{
- js_Object *obj = js_newobject(J, JS_CFUNCTION);
+ js_Object *obj = jsR_newobject(J, JS_CFUNCTION);
obj->function = function;
obj->scope = scope;
return obj;
}
-js_Object *js_newcfunction(js_State *J, js_CFunction cfunction)
+js_Object *jsR_newcfunction(js_State *J, js_CFunction cfunction)
{
- js_Object *obj = js_newobject(J, JS_CCFUNCTION);
+ js_Object *obj = jsR_newobject(J, JS_CCFUNCTION);
obj->cfunction = cfunction;
return obj;
}
-js_Environment *js_newenvironment(js_State *J, js_Environment *outer, js_Object *vars)
+js_Object *jsR_newboolean(js_State *J, int v)
{
- js_Environment *E = malloc(sizeof *E);
- E->outer = outer;
- E->variables = vars;
- return E;
+ js_Object *obj = jsR_newobject(J, JS_CBOOLEAN);
+ obj->primitive.boolean = v;
+ return obj;
}
-js_Property *js_decvar(js_State *J, js_Environment *E, const char *name)
+js_Object *jsR_newnumber(js_State *J, double v)
{
- return js_setproperty(J, E->variables, name);
+ js_Object *obj = jsR_newobject(J, JS_CNUMBER);
+ obj->primitive.number = v;
+ return obj;
}
-js_Property *js_getvar(js_State *J, js_Environment *E, const char *name)
+js_Object *jsR_newstring(js_State *J, const char *v)
{
- while (E) {
- js_Property *ref = js_getproperty(J, E->variables, name);
- if (ref)
- return ref;
- E = E->outer;
- }
- return NULL;
-}
-
-js_Property *js_setvar(js_State *J, js_Environment *E, const char *name)
-{
- while (1) {
- js_Property *ref = js_getproperty(J, E->variables, name);
- if (ref)
- return ref;
- if (!E->outer)
- break;
- E = E->outer;
- }
- return js_setproperty(J, E->variables, name);
-}
-
-js_Property *js_getproperty(js_State *J, js_Object *obj, const char *name)
-{
- return lookup(obj->properties, name);
-}
-
-js_Property *js_setproperty(js_State *J, js_Object *obj, const char *name)
-{
- js_Property *result;
- obj->properties = insert(obj->properties, name, &result);
- return result;
-}
-
-js_Property *js_firstproperty(js_State *J, js_Object *obj)
-{
- return lookupfirst(obj->properties);
-}
-
-js_Property *js_nextproperty(js_State *J, js_Object *obj, const char *name)
-{
- return lookupnext(obj->properties, name);
-}
-
-void js_dumpvalue(js_State *J, js_Value v)
-{
- switch (v.type) {
- case JS_TUNDEFINED: printf("undefined"); break;
- case JS_TNULL: printf("null"); break;
- case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
- case JS_TNUMBER: printf("%.9g", v.u.number); break;
- case JS_TSTRING: printf("'%s'", v.u.string); break;
- case JS_TOBJECT: printf("<object %p>", v.u.object); break;
- }
-}
-
-static void js_dumpproperty(js_State *J, js_Property *node)
-{
- if (node->left != &sentinel)
- js_dumpproperty(J, node->left);
- printf("\t%s: ", node->name);
- js_dumpvalue(J, node->value);
- printf(",\n");
- if (node->right != &sentinel)
- js_dumpproperty(J, node->right);
-}
-
-void js_dumpobject(js_State *J, js_Object *obj)
-{
- printf("{\n");
- if (obj->properties != &sentinel)
- js_dumpproperty(J, obj->properties);
- printf("}\n");
+ js_Object *obj = jsR_newobject(J, JS_CSTRING);
+ obj->primitive.string = v;
+ return obj;
}
--- a/jsobject.h
+++ b/jsobject.h
@@ -7,12 +7,6 @@
typedef enum js_Class js_Class;
typedef struct js_Property js_Property;
-struct js_Environment
-{
- js_Environment *outer;
- js_Object *variables;
-};
-
enum js_Type {
JS_TUNDEFINED,
JS_TNULL,
@@ -22,32 +16,6 @@
JS_TOBJECT,
};
-struct js_Value
-{
- js_Type type;
- union {
- int boolean;
- double number;
- const char *string;
- js_Object *object;
- } u;
-};
-
-enum {
- JS_PWRITABLE = 1,
- JS_PENUMERABLE = 2,
- JS_PCONFIGURABLE = 4,
-};
-
-struct js_Property
-{
- char *name;
- js_Property *left, *right;
- int level;
- js_Value value;
- int flags;
-};
-
enum js_Class {
JS_CARRAY,
JS_CBOOLEAN,
@@ -62,6 +30,23 @@
JS_CSTRING,
};
+enum {
+ JS_PWRITABLE = 1,
+ JS_PENUMERABLE = 2,
+ JS_PCONFIGURABLE = 4,
+};
+
+struct js_Value
+{
+ js_Type type;
+ union {
+ int boolean;
+ double number;
+ const char *string;
+ js_Object *object;
+ } u;
+};
+
struct js_Object
{
js_Class type;
@@ -77,23 +62,41 @@
js_CFunction cfunction;
};
-js_Object *js_newobject(js_State *J, js_Class type);
-js_Object *js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
-js_Object *js_newcfunction(js_State *J, js_CFunction cfunction);
+struct js_Property
+{
+ char *name;
+ js_Property *left, *right;
+ int level;
+ js_Value value;
+ int flags;
+};
-js_Environment *js_newenvironment(js_State *J, js_Environment *outer, js_Object *vars);
-js_Property *js_decvar(js_State *J, js_Environment *E, const char *name);
-js_Property *js_getvar(js_State *J, js_Environment *E, const char *name);
-js_Property *js_setvar(js_State *J, js_Environment *E, const char *name);
+/* jsvalue.c */
+int jsR_toboolean(js_State *J, const js_Value *v);
+double jsR_tonumber(js_State *J, const js_Value *v);
+const char *jsR_tostring(js_State *J, const js_Value *v);
+js_Object *jsR_toobject(js_State *J, const js_Value *v);
-js_Property *js_getproperty(js_State *J, js_Object *obj, const char *name);
-js_Property *js_setproperty(js_State *J, js_Object *obj, const char *name);
-void js_deleteproperty(js_State *J, js_Object *obj, const char *name);
+/* jsproperty.c */
+js_Object *jsR_newobject(js_State *J, js_Class type);
+js_Property *jsR_getproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsR_setproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsR_nextproperty(js_State *J, js_Object *obj, const char *name);
-js_Property *js_firstproperty(js_State *J, js_Object *obj);
-js_Property *js_nextproperty(js_State *J, js_Object *obj, const char *name);
+/* jsobject.c */
+js_Object *jsR_newfunction(js_State *J, js_Function *function, js_Environment *scope);
+js_Object *jsR_newcfunction(js_State *J, js_CFunction cfunction);
+js_Object *jsR_newboolean(js_State *J, int v);
+js_Object *jsR_newnumber(js_State *J, double v);
+js_Object *jsR_newstring(js_State *J, const char *v);
+/* jsrun.c */
+void jsR_pushobject(js_State *J, js_Object *v);
+js_Object *js_toobject(js_State *J, int idx);
+
void js_dumpobject(js_State *J, js_Object *obj);
void js_dumpvalue(js_State *J, js_Value v);
+
+JS_NORETURN void jsR_error(js_State *J, const char *fmt, ...);
#endif
--- a/jsparse.c
+++ b/jsparse.c
@@ -1,7 +1,7 @@
#include "js.h"
-#include "jsstate.h"
#include "jslex.h"
#include "jsparse.h"
+#include "jsstate.h"
#define nelem(a) (sizeof (a) / sizeof (a)[0])
--- a/jsparse.h
+++ b/jsparse.h
@@ -1,16 +1,6 @@
#ifndef js_parse_h
#define js_parse_h
-struct js_Ast
-{
- int type;
- int line;
- js_Ast *a, *b, *c, *d;
- double number;
- const char *string;
- js_Ast *next; /* next in alloc list */
-};
-
enum
{
AST_LIST,
@@ -123,10 +113,19 @@
STM_DEFAULT,
};
+struct js_Ast
+{
+ int type;
+ int line;
+ js_Ast *a, *b, *c, *d;
+ double number;
+ const char *string;
+ js_Ast *next; /* next in alloc list */
+};
js_Ast *jsP_parse(js_State *J, const char *filename, const char *source);
+void jsP_optimize(js_State *J, js_Ast *prog);
void jsP_freeparse(js_State *J);
-void jsP_optimize(js_State *J, js_Ast *prog);
void jsP_dumpsyntax(js_State *J, js_Ast *prog);
void jsP_dumplist(js_State *J, js_Ast *prog);
--- /dev/null
+++ b/jsproperty.c
@@ -1,0 +1,162 @@
+#include "js.h"
+#include "jsobject.h"
+
+/*
+ Use an AA-tree to quickly look up properties in objects:
+
+ The level of every leaf node is one.
+ The level of every left child is one less than its parent.
+ The level of every right child is equal or one less than its parent.
+ The level of every right grandchild is less than its grandparent.
+ Every node of level greater than one has two children.
+
+ A link where the child's level is equal to that of its parent is called a horizontal link.
+ Individual right horizontal links are allowed, but consecutive ones are forbidden.
+ Left horizontal links are forbidden.
+
+ skew() fixes left horizontal links.
+ split() fixes consecutive right horizontal links.
+*/
+
+static js_Property sentinel = { "", &sentinel, &sentinel, 0 };
+
+static js_Property *newproperty(const char *name)
+{
+ js_Property *node = malloc(sizeof(js_Property));
+ node->name = strdup(name);
+ node->left = node->right = &sentinel;
+ node->level = 1;
+ node->value.type = JS_TUNDEFINED;
+ node->value.u.number = 0;
+ node->flags = 0;
+ return node;
+}
+
+static js_Property *lookup(js_Property *node, const char *name)
+{
+ while (node != &sentinel) {
+ int c = strcmp(name, node->name);
+ if (c == 0)
+ return node;
+ else if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ return NULL;
+}
+
+static inline js_Property *skew(js_Property *node)
+{
+ if (node->level != 0) {
+ if (node->left->level == node->level) {
+ js_Property *save = node;
+ node = node->left;
+ save->left = node->right;
+ node->right = save;
+ }
+ node->right = skew(node->right);
+ }
+ return node;
+}
+
+static inline js_Property *split(js_Property *node)
+{
+ if (node->level != 0 && node->right->right->level == node->level) {
+ js_Property *save = node;
+ node = node->right;
+ save->right = node->left;
+ node->left = save;
+ node->level++;
+ node->right = split(node->right);
+ }
+ return node;
+}
+
+static js_Property *insert(js_Property *node, const char *name, js_Property **result)
+{
+ if (node != &sentinel) {
+ int c = strcmp(name, node->name);
+ if (c < 0)
+ node->left = insert(node->left, name, result);
+ else if (c > 0)
+ node->right = insert(node->right, name, result);
+ else
+ return *result = node;
+ node = skew(node);
+ node = split(node);
+ return node;
+ }
+ return *result = newproperty(name);
+}
+
+static js_Property *lookupfirst(js_Property *node)
+{
+ while (node != &sentinel) {
+ if (node->left == &sentinel)
+ return node;
+ node = node->left;
+ }
+ return NULL;
+}
+
+static js_Property *lookupnext(js_Property *node, const char *name)
+{
+ js_Property *stack[100], *parent;
+ int top = 0;
+
+ stack[0] = NULL;
+ while (node != &sentinel) {
+ stack[++top] = node;
+ int c = strcmp(name, node->name);
+ if (c == 0)
+ goto found;
+ else if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ return NULL;
+
+found:
+ if (node->right != &sentinel)
+ return lookupfirst(node->right);
+ parent = stack[--top];
+ while (parent && node == parent->right) {
+ node = parent;
+ parent = stack[--top];
+ }
+ return parent;
+}
+
+js_Object *jsR_newobject(js_State *J, js_Class type)
+{
+ js_Object *obj = malloc(sizeof(js_Object));
+ obj->type = type;
+ obj->properties = &sentinel;
+ obj->prototype = NULL;
+ obj->primitive.number = 0;
+ obj->scope = NULL;
+ obj->function = NULL;
+ obj->cfunction = NULL;
+ return obj;
+}
+
+js_Property *jsR_getproperty(js_State *J, js_Object *obj, const char *name)
+{
+ return lookup(obj->properties, name);
+}
+
+js_Property *jsR_setproperty(js_State *J, js_Object *obj, const char *name)
+{
+ js_Property *result;
+ obj->properties = insert(obj->properties, name, &result);
+ return result;
+}
+
+js_Property *jsR_nextproperty(js_State *J, js_Object *obj, const char *name)
+{
+ if (!name)
+ return lookupfirst(obj->properties);
+ return lookupnext(obj->properties, name);
+}
--- a/jsrun.c
+++ b/jsrun.c
@@ -81,17 +81,17 @@
++top;
}
-void js_pushlstring(js_State *J, const char *v)
+void js_pushstring(js_State *J, const char *v)
{
stack[top].type = JS_TSTRING;
- stack[top].u.string = v;
+ stack[top].u.string = js_intern(J, v);
++top;
}
-void js_pushstring(js_State *J, const char *v)
+void jsR_pushliteral(js_State *J, const char *v)
{
stack[top].type = JS_TSTRING;
- stack[top].u.string = js_intern(J, v);
+ stack[top].u.string = v;
++top;
}
@@ -102,6 +102,21 @@
++top;
}
+void js_newobject(js_State *J)
+{
+ js_pushobject(J, jsR_newobject(J, JS_COBJECT));
+}
+
+void js_newarray(js_State *J)
+{
+ js_pushobject(J, jsR_newobject(J, JS_CARRAY));
+}
+
+void js_pushcfunction(js_State *J, js_CFunction v)
+{
+ js_pushobject(J, jsR_newcfunction(J, v));
+}
+
static int stackidx(js_State *J, int idx)
{
if (idx < 0)
@@ -129,66 +144,26 @@
int js_toboolean(js_State *J, int idx)
{
- const char *s;
- double n;
idx = stackidx(J, idx);
- switch (stack[idx].type) {
- case JS_TUNDEFINED: return 0;
- case JS_TNULL: return 0;
- case JS_TBOOLEAN: return stack[idx].u.boolean;
- case JS_TNUMBER: n = stack[idx].u.number; return n != 0 || !isnan(n);
- case JS_TSTRING: s = stack[idx].u.string; return s[0] != 0;
- case JS_TOBJECT: return 0;
- }
- return 0;
+ return jsR_toboolean(J, &stack[idx]);
}
double js_tonumber(js_State *J, int idx)
{
idx = stackidx(J, idx);
- switch (stack[idx].type) {
- case JS_TUNDEFINED: return NAN;
- case JS_TNULL: return 0;
- case JS_TBOOLEAN: return stack[idx].u.boolean;
- case JS_TNUMBER: return stack[idx].u.number;
- case JS_TSTRING: return strtod(stack[idx].u.string, NULL);
- case JS_TOBJECT: return 0;
- }
- return 0;
+ return jsR_tonumber(J, &stack[idx]);
}
-double js_tointeger(js_State *J, int idx)
-{
- return toint32(js_tonumber(J, idx));
-}
-
const char *js_tostring(js_State *J, int idx)
{
- char buf[20];
idx = stackidx(J, idx);
- switch (stack[idx].type) {
- case JS_TUNDEFINED: return "undefined";
- case JS_TNULL: return "null";
- case JS_TBOOLEAN: return stack[idx].u.boolean ? "true" : "false";
- case JS_TNUMBER: sprintf(buf, "%.9g", stack[idx].u.number); return js_intern(J, buf);
- case JS_TSTRING: return stack[idx].u.string;
- case JS_TOBJECT: return "<object>";
- }
- return NULL;
+ return jsR_tostring(J, &stack[idx]);
}
js_Object *js_toobject(js_State *J, int idx)
{
idx = stackidx(J, idx);
- switch (stack[idx].type) {
- case JS_TUNDEFINED: jsR_error(J, "TypeError (undefined)");
- case JS_TNULL: jsR_error(J, "TypeError (null)");
- case JS_TBOOLEAN: jsR_error(J, "new Boolean()");
- case JS_TNUMBER: jsR_error(J, "new Number()");
- case JS_TSTRING: jsR_error(J, "new String()");
- case JS_TOBJECT: return stack[idx].u.object;
- }
- return NULL;
+ return jsR_toobject(J, &stack[idx]);
}
void js_pop(js_State *J, int n)
@@ -277,11 +252,11 @@
case OP_NUMBER_1: js_pushnumber(J, 1); break;
case OP_NUMBER_X: js_pushnumber(J, *pc++); break;
case OP_NUMBER: js_pushnumber(J, NT[*pc++]); break;
- case OP_STRING: js_pushlstring(J, ST[*pc++]); break;
+ case OP_STRING: jsR_pushliteral(J, ST[*pc++]); break;
- case OP_CLOSURE: js_pushobject(J, js_newfunction(J, FT[*pc++], E)); break;
- case OP_NEWOBJECT: js_pushobject(J, js_newobject(J, JS_COBJECT)); break;
- case OP_NEWARRAY: js_pushobject(J, js_newobject(J, JS_CARRAY)); break;
+ case OP_CLOSURE: js_pushobject(J, jsR_newfunction(J, FT[*pc++], E)); break;
+ case OP_NEWOBJECT: js_newobject(J); break;
+ case OP_NEWARRAY: js_newarray(J); break;
case OP_UNDEF: js_pushundefined(J); break;
case OP_NULL: js_pushnull(J); break;
@@ -321,7 +296,7 @@
case OP_IN:
str = js_tostring(J, -2);
obj = js_toobject(J, -1);
- ref = js_getproperty(J, obj, str);
+ ref = jsR_getproperty(J, obj, str);
js_pop(J, 2);
js_pushboolean(J, ref != NULL);
break;
@@ -329,7 +304,7 @@
case OP_GETPROP:
obj = js_toobject(J, -2);
str = js_tostring(J, -1);
- ref = js_getproperty(J, obj, str);
+ ref = jsR_getproperty(J, obj, str);
js_pop(J, 2);
if (ref)
js_pushvalue(J, ref->value);
@@ -340,7 +315,7 @@
case OP_SETPROP:
obj = js_toobject(J, -3);
str = js_tostring(J, -2);
- ref = js_setproperty(J, obj, str);
+ ref = jsR_setproperty(J, obj, str);
if (ref)
ref->value = js_tovalue(J, -1);
js_rot3pop2(J);
@@ -351,12 +326,12 @@
case OP_NEXTPROP:
obj = js_toobject(J, -2);
if (js_isundefined(J, -1))
- ref = js_firstproperty(J, obj);
+ ref = jsR_nextproperty(J, obj, NULL);
else
- ref = js_nextproperty(J, obj, js_tostring(J, -1));
+ ref = jsR_nextproperty(J, obj, js_tostring(J, -1));
if (ref) {
js_pop(J, 1);
- js_pushlstring(J, ref->name);
+ jsR_pushliteral(J, ref->name);
js_pushboolean(J, 1);
} else {
js_pop(J, 2);
@@ -568,7 +543,7 @@
F = obj->function;
if (F) {
- E = js_newenvironment(J, obj->scope, js_newobject(J, JS_COBJECT));
+ E = jsR_newenvironment(J, jsR_newobject(J, JS_COBJECT), obj->scope);
for (i = 0; i < F->numparams; ++i) {
ref = js_decvar(J, E, F->params[i]);
@@ -594,13 +569,50 @@
bot = savebot;
}
+js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer)
+{
+ js_Environment *E = malloc(sizeof *E);
+ E->outer = outer;
+ E->variables = vars;
+ return E;
+}
+
void js_setglobal(js_State *J, const char *name)
{
- js_Property *ref = js_setproperty(J, J->E->variables, name);
+ js_Property *ref = jsR_setproperty(J, J->G, name);
ref->value = js_tovalue(J, -1);
js_pop(J, 1);
}
+js_Property *js_decvar(js_State *J, js_Environment *E, const char *name)
+{
+ return jsR_setproperty(J, E->variables, name);
+}
+
+js_Property *js_getvar(js_State *J, js_Environment *E, const char *name)
+{
+ while (E) {
+ js_Property *ref = jsR_getproperty(J, E->variables, name);
+ if (ref)
+ return ref;
+ E = E->outer;
+ }
+ return NULL;
+}
+
+js_Property *js_setvar(js_State *J, js_Environment *E, const char *name)
+{
+ while (1) {
+ js_Property *ref = jsR_getproperty(J, E->variables, name);
+ if (ref)
+ return ref;
+ if (!E->outer)
+ break;
+ E = E->outer;
+ }
+ return jsR_setproperty(J, E->variables, name);
+}
+
void jsR_error(js_State *J, const char *fmt, ...)
{
va_list ap;
@@ -617,12 +629,12 @@
void jsR_runfunction(js_State *J, js_Function *F)
{
if (setjmp(J->jb)) {
- js_dumpobject(J, J->E->variables);
+ js_dumpobject(J, J->G);
return;
}
- runfun(J, F, J->E);
+ runfun(J, F, J->GE);
- js_dumpobject(J, J->E->variables);
+ js_dumpobject(J, J->G);
js_dumpstack(J);
}
--- a/jsrun.h
+++ b/jsrun.h
@@ -1,7 +1,36 @@
#ifndef js_run_h
#define js_run_h
-void jsR_error(js_State *J, const char *fmt, ...);
+struct js_Environment
+{
+ js_Environment *outer;
+ js_Object *variables;
+};
+
+void js_setglobal(js_State *J, const char *name);
+
+js_Environment *jsR_newenvironment(js_State *J, js_Object *variables, js_Environment *outer);
+js_Property *js_decvar(js_State *J, js_Environment *E, const char *name);
+js_Property *js_getvar(js_State *J, js_Environment *E, const char *name);
+js_Property *js_setvar(js_State *J, js_Environment *E, const char *name);
+
void jsR_runfunction(js_State *J, js_Function *F);
+
+void js_pushundefined(js_State *J);
+void js_pushnull(js_State *J);
+void js_pushboolean(js_State *J, int v);
+void js_pushnumber(js_State *J, double v);
+void js_pushstring(js_State *J, const char *v);
+void js_newobject(js_State *J);
+void js_newarray(js_State *J);
+void js_pushcfunction(js_State *J, js_CFunction v);
+int js_isundefined(js_State *J, int idx);
+int js_isstring(js_State *J, int idx);
+int js_toboolean(js_State *J, int idx);
+double js_tonumber(js_State *J, int idx);
+double js_tointeger(js_State *J, int idx);
+const char *js_tostring(js_State *J, int idx);
+void js_pop(js_State *J, int n);
+void js_dup(js_State *J);
#endif
--- a/jsstate.c
+++ b/jsstate.c
@@ -1,5 +1,6 @@
#include "js.h"
#include "jsobject.h"
+#include "jsrun.h"
#include "jsstate.h"
static int jsB_print(js_State *J, int argc)
@@ -19,10 +20,10 @@
js_State *J = malloc(sizeof *J);
memset(J, 0, sizeof(*J));
- J->global = js_newobject(J, JS_COBJECT);
- J->E = js_newenvironment(J, NULL, J->global);
+ J->G = jsR_newobject(J, JS_COBJECT);
+ J->GE = jsR_newenvironment(J, J->G, NULL);
- js_pushobject(J, js_newcfunction(J, jsB_print));
+ js_pushcfunction(J, jsB_print);
js_setglobal(J, "print");
return J;
--- a/jsstate.h
+++ b/jsstate.h
@@ -26,11 +26,12 @@
/* compiler */
js_Function *fun; /* list of allocated functions to free on errors */
+ int strict;
+
/* runtime */
- js_Environment *E;
- js_Object *global;
+ js_Object *G;
+ js_Environment *GE;
- int strict;
};
#endif
--- /dev/null
+++ b/jsvalue.c
@@ -1,0 +1,102 @@
+#include "js.h"
+#include "jsobject.h"
+
+enum {
+ JS_HNONE,
+ JS_HNUMBER,
+ JS_HSTRING,
+};
+
+static js_Value jsR_toprimitive(js_State *J, const js_Value *v, int preferred)
+{
+ js_Object *obj = v->u.object;
+
+ if (preferred == JS_HNONE)
+ preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER;
+
+ if (preferred == JS_HSTRING) {
+ // try "toString"
+ // if result is primitive, return result
+ // try "valueOf"
+ // if result is primitive, return result
+ } else {
+ // try "toString"
+ // if result is primitive, return result
+ // try "valueOf"
+ // if result is primitive, return result
+ }
+ jsR_error(J, "TypeError (ToPrimitive)");
+}
+
+int jsR_toboolean(js_State *J, const js_Value *v)
+{
+ switch (v->type) {
+ case JS_TUNDEFINED: return 0;
+ case JS_TNULL: return 0;
+ case JS_TBOOLEAN: return v->u.boolean;
+ case JS_TNUMBER: return v->u.number != 0 && !isnan(v->u.number);
+ case JS_TSTRING: return v->u.string[0] != 0;
+ case JS_TOBJECT: return 0;
+ }
+ return 0;
+}
+
+double jsR_tonumber(js_State *J, const js_Value *v)
+{
+ switch (v->type) {
+ case JS_TUNDEFINED: return NAN;
+ case JS_TNULL: return 0;
+ case JS_TBOOLEAN: return v->u.boolean;
+ case JS_TNUMBER: return v->u.number;
+ case JS_TSTRING:
+ {
+ /* TODO: use lexer to parse string grammar */
+ return strtod(v->u.string, NULL);
+ }
+ case JS_TOBJECT:
+ {
+ js_Value vv = jsR_toprimitive(J, v, JS_HNUMBER);
+ return jsR_tonumber(J, &vv);
+ }
+ }
+ return 0;
+}
+
+const char *jsR_tostring(js_State *J, const js_Value *v)
+{
+ switch (v->type) {
+ case JS_TUNDEFINED: return "undefined";
+ case JS_TNULL: return "null";
+ case JS_TBOOLEAN: return v->u.boolean ? "true" : "false";
+ case JS_TNUMBER:
+ {
+ char buf[32];
+ double n = v->u.number;
+ if (isnan(n)) return "NaN";
+ if (isinf(n)) return n < 0 ? "-Infinity" : "Infinity";
+ if (n == 0) return "0";
+ sprintf(buf, "%.17g", n); /* DBL_DECIMAL_DIG == 17 */
+ return js_intern(J, buf);
+ }
+ case JS_TSTRING: return v->u.string;
+ case JS_TOBJECT:
+ {
+ js_Value vv = jsR_toprimitive(J, v, JS_HSTRING);
+ return jsR_tostring(J, &vv);
+ }
+ }
+ return NULL;
+}
+
+js_Object *jsR_toobject(js_State *J, const js_Value *v)
+{
+ switch (v->type) {
+ case JS_TUNDEFINED: jsR_error(J, "TypeError (ToObject(undefined))");
+ case JS_TNULL: jsR_error(J, "TypeError (ToObject(null))");
+ case JS_TBOOLEAN: return jsR_newboolean(J, v->u.boolean);
+ case JS_TNUMBER: return jsR_newnumber(J, v->u.number);
+ case JS_TSTRING: return jsR_newstring(J, v->u.string);
+ case JS_TOBJECT: return v->u.object;
+ }
+ return NULL;
+}