Clean up. Rearrange files. Rename functions.

This commit is contained in:
Tor Andersson
2014-01-16 15:54:27 +01:00
parent 3715d6d145
commit 288eef80e1
15 changed files with 497 additions and 383 deletions

26
js.h
View File

@@ -44,38 +44,16 @@ int js_error(js_State *J, const char *fmt, ...);
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);

View File

@@ -655,3 +655,36 @@ void jsC_dumpfunction(js_State *J, js_Function *F)
}
}
}
/* 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");
}

View File

@@ -1,6 +1,6 @@
#include "js.h"
#include "jsstate.h"
#include "jslex.h"
#include "jsstate.h"
#define nelem(a) (sizeof (a) / sizeof (a)[0])

View File

@@ -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 @@ enum {
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, ...);

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -7,12 +7,6 @@ typedef struct js_Value js_Value;
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 @@ enum js_Type {
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 @@ enum js_Class {
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 @@ struct js_Object
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

View File

@@ -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])

View File

@@ -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 @@ enum
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);

162
jsproperty.c Normal file
View File

@@ -0,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);
}

144
jsrun.c
View File

@@ -81,13 +81,6 @@ void js_pushnumber(js_State *J, double v)
++top;
}
void js_pushlstring(js_State *J, const char *v)
{
stack[top].type = JS_TSTRING;
stack[top].u.string = v;
++top;
}
void js_pushstring(js_State *J, const char *v)
{
stack[top].type = JS_TSTRING;
@@ -95,6 +88,13 @@ void js_pushstring(js_State *J, const char *v)
++top;
}
void jsR_pushliteral(js_State *J, const char *v)
{
stack[top].type = JS_TSTRING;
stack[top].u.string = v;
++top;
}
void js_pushobject(js_State *J, js_Object *v)
{
stack[top].type = JS_TOBJECT;
@@ -102,6 +102,21 @@ void js_pushobject(js_State *J, js_Object *v)
++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 @@ js_Value js_tovalue(js_State *J, int idx)
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;
}
double js_tointeger(js_State *J, int idx)
{
return toint32(js_tonumber(J, idx));
return jsR_tonumber(J, &stack[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 @@ static void runfun(js_State *J, js_Function *F, js_Environment *E)
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 @@ static void runfun(js_State *J, js_Function *F, js_Environment *E)
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 @@ static void runfun(js_State *J, js_Function *F, js_Environment *E)
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 @@ static void runfun(js_State *J, js_Function *F, js_Environment *E)
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 @@ static void runfun(js_State *J, js_Function *F, js_Environment *E)
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 @@ static void js_call(js_State *J, int argc)
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 @@ static void js_call(js_State *J, int argc)
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_error(js_State *J, const char *fmt, ...)
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);
}

31
jsrun.h
View File

@@ -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

View File

@@ -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 *js_newstate(void)
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;

View File

@@ -26,11 +26,12 @@ struct js_State
/* compiler */
js_Function *fun; /* list of allocated functions to free on errors */
/* runtime */
js_Environment *E;
js_Object *global;
int strict;
/* runtime */
js_Object *G;
js_Environment *GE;
};
#endif

102
jsvalue.c Normal file
View File

@@ -0,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;
}