mirror of
https://github.com/ccxvii/mujs.git
synced 2026-02-06 18:35:54 +08:00
Clean up. Rearrange files. Rename functions.
This commit is contained in:
26
js.h
26
js.h
@@ -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);
|
||||
|
||||
33
jsdump.c
33
jsdump.c
@@ -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");
|
||||
}
|
||||
|
||||
2
jslex.c
2
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])
|
||||
|
||||
|
||||
7
jslex.h
7
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 @@ 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, ...);
|
||||
|
||||
|
||||
1
jsload.c
1
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)
|
||||
|
||||
242
jsobject.c
242
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;
|
||||
}
|
||||
|
||||
91
jsobject.h
91
jsobject.h
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
21
jsparse.h
21
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 @@ 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
162
jsproperty.c
Normal 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
144
jsrun.c
@@ -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
31
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
102
jsvalue.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user