ref: 8342452b13a5df602dbb85ecc6602e8eaa4f0206
parent: 8c21bb0411c9180520f65a113a9237c4235c6d2a
author: Tor Andersson <tor@ccxvii.net>
date: Fri Jan 10 06:37:33 EST 2014
Rename files and put stuff in the correct headers.
--- a/js-ast.c
+++ /dev/null
@@ -1,554 +1,0 @@
-#include "js.h"
-#include "js-ast.h"
-
-js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast *c, js_Ast *d)
-{
- js_Ast *node = malloc(sizeof(js_Ast));
-
- node->type = type;
- node->line = J->line;
- node->a = a;
- node->b = b;
- node->c = c;
- node->d = d;
- node->n = 0;
- node->s = NULL;
-
- node->next = J->ast;
- J->ast = node;
-
- return node;
-}
-
-js_Ast *jsP_newstrnode(js_State *J, int type, const char *s)
-{
- js_Ast *node = jsP_newnode(J, type, 0, 0, 0, 0);
- node->s = s;
- return node;
-}
-
-js_Ast *jsP_newnumnode(js_State *J, int type, double n)
-{
- js_Ast *node = jsP_newnode(J, type, 0, 0, 0, 0);
- node->n = n;
- return node;
-}
-
-void jsP_freeast(js_State *J)
-{
- js_Ast *node = J->ast;
- while (node) {
- js_Ast *next = node->next;
- free(node);
- node = next;
- }
- J->ast = NULL;
-}
-
-static const char *strast(int type)
-{
- switch (type) {
- case AST_LIST: return "LIST";
- case AST_IDENTIFIER: return "IDENTIFIER";
- case AST_NUMBER: return "NUMBER";
- case AST_STRING: return "STRING";
- case AST_REGEXP: return "REGEXP";
- case EXP_VAR: return "VAR";
- case EXP_NULL: return "NULL";
- case EXP_TRUE: return "TRUE";
- case EXP_FALSE: return "FALSE";
- case EXP_THIS: return "THIS";
- case EXP_ARRAY: return "ARRAY";
- case EXP_OBJECT: return "OBJECT";
- case EXP_PROP_VAL: return "PROP_VAL";
- case EXP_PROP_GET: return "PROP_GET";
- case EXP_PROP_SET: return "PROP_SET";
- case EXP_INDEX: return "INDEX";
- case EXP_MEMBER: return "MEMBER";
- case EXP_NEW: return "new";
- case EXP_CALL: return "CALL";
- case EXP_FUNC: return "function";
- case EXP_COND: return "?:";
- case EXP_COMMA: return ",";
- case EXP_DELETE: return "delete";
- case EXP_VOID: return "void";
- case EXP_TYPEOF: return "typeof";
- case EXP_PREINC: return "PRE++";
- case EXP_PREDEC: return "PRE--";
- case EXP_POSTINC: return "POST++";
- case EXP_POSTDEC: return "POST--";
- case EXP_POS: return "+";
- case EXP_NEG: return "-";
- case EXP_BITNOT: return "~";
- case EXP_LOGNOT: return "!";
- case EXP_LOGOR: return "||";
- case EXP_LOGAND: return "&&";
- case EXP_BITOR: return "|";
- case EXP_BITXOR: return "^";
- case EXP_BITAND: return "&";
- case EXP_EQ: return "==";
- case EXP_NE: return "!=";
- case EXP_EQ3: return "===";
- case EXP_NE3: return "!==";
- case EXP_LT: return "<";
- case EXP_GT: return ">";
- case EXP_LE: return "<=";
- case EXP_GE: return ">=";
- case EXP_INSTANCEOF: return "instanceof";
- case EXP_IN: return "in";
- case EXP_SHL: return "<<";
- case EXP_SHR: return ">>";
- case EXP_USHR: return ">>>";
- case EXP_ADD: return "+";
- case EXP_SUB: return "-";
- case EXP_MUL: return "*";
- case EXP_DIV: return "/";
- case EXP_MOD: return "%";
- case EXP_ASS: return "=";
- case EXP_ASS_MUL: return "*=";
- case EXP_ASS_DIV: return "/=";
- case EXP_ASS_MOD: return "%=";
- case EXP_ASS_ADD: return "+=";
- case EXP_ASS_SUB: return "-=";
- case EXP_ASS_SHL: return "<<=";
- case EXP_ASS_SHR: return ">>=";
- case EXP_ASS_USHR: return ">>>=";
- case EXP_ASS_BITAND: return "&=";
- case EXP_ASS_BITXOR: return "^=";
- case EXP_ASS_BITOR: return "|=";
- case STM_BLOCK: return "BLOCK";
- case STM_FUNC: return "function-decl";
- case STM_NOP: return "NOP";
- case STM_VAR: return "var";
- case STM_IF: return "if";
- case STM_DO: return "do-while";
- case STM_WHILE: return "while";
- case STM_FOR: return "for";
- case STM_FOR_VAR: return "for_var";
- case STM_FOR_IN: return "for_in";
- case STM_FOR_IN_VAR: return "for_in_var";
- case STM_CONTINUE: return "continue";
- case STM_BREAK: return "break";
- case STM_RETURN: return "return";
- case STM_WITH: return "with";
- case STM_SWITCH: return "switch";
- case STM_THROW: return "throw";
- case STM_TRY: return "try";
- case STM_LABEL: return "label";
- case STM_CASE: return "case";
- case STM_DEFAULT: return "default";
- case STM_DEBUGGER: return "debugger";
- default: return "(unknown)";
- }
-}
-
-static void indent(int level)
-{
- while (level--)
- putchar('\t');
-}
-
-static void printlist(js_Ast *n, int level, const char *sep)
-{
- while (n) {
- printast(n->a, level);
- n = n->b;
- if (n)
- fputs(sep, stdout);
- }
-}
-
-void printblock(js_Ast *n, int level)
-{
- while (n) {
- indent(level);
- printast(n->a, level);
- if (n->a->type < STM_BLOCK) // expression
- putchar(';');
- n = n->b;
- if (n)
- putchar('\n');
- }
-}
-
-static void printstm(js_Ast *n, int level)
-{
- if (n->type == STM_BLOCK) {
- printf(" {\n");
- printblock(n->a, level + 1);
- putchar('\n');
- indent(level);
- printf("}");
- } else {
- putchar('\n');
- indent(level + 1);
- printast(n, level + 1);
- if (n->type < STM_BLOCK) // expression
- putchar(';');
- }
-}
-
-static void printunary(int level, js_Ast *n, const char *pre, const char *suf)
-{
- printf(pre);
- printast(n, level);
- printf(suf);
-}
-
-static void printbinary(int level, js_Ast *a, js_Ast *b, const char *op)
-{
- printf("(");
- printast(a, level);
- printf(" %s ", op);
- printast(b, level);
- printf(")");
-}
-
-void printast(js_Ast *n, int level)
-{
- switch (n->type) {
- case AST_IDENTIFIER: printf("%s", n->s); return;
- case AST_NUMBER: printf("%g", n->n); return;
- case AST_STRING: printf("'%s'", n->s); return;
- case AST_REGEXP: printf("/%s/", n->s); return;
- case AST_LIST:
- putchar('[');
- printlist(n, level, " ");
- putchar(']');
- break;
-
- case STM_BLOCK:
- putchar('{');
- putchar('\n');
- printblock(n->a, level + 1);
- putchar('\n');
- indent(level);
- putchar('}');
- break;
-
- case STM_FOR:
- printf("for (");
- printast(n->a, level); printf("; ");
- printast(n->b, level); printf("; ");
- printast(n->c, level); printf(")");
- printstm(n->d, level);
- break;
- case STM_FOR_VAR:
- printf("for (var ");
- printlist(n->a, level, ", "); printf("; ");
- printast(n->b, level); printf("; ");
- printast(n->c, level); printf(")");
- printstm(n->d, level);
- break;
- case STM_FOR_IN:
- printf("for (");
- printast(n->a, level); printf(" in ");
- printast(n->b, level); printf(")");
- printstm(n->c, level);
- break;
- case STM_FOR_IN_VAR:
- printf("for (var ");
- printlist(n->a, level, ", "); printf(" in ");
- printast(n->b, level); printf(")");
- printstm(n->c, level);
- break;
-
- case STM_NOP:
- putchar(';');
- break;
-
- case STM_VAR:
- printf("var ");
- printlist(n->a, level, ", ");
- putchar(';');
- break;
-
- case EXP_VAR:
- printast(n->a, level);
- if (n->b) {
- printf(" = ");
- printast(n->b, level);
- }
- break;
-
- case STM_IF:
- printf("if (");
- printast(n->a, level);
- printf(")");
- printstm(n->b, level);
- if (n->c) {
- putchar('\n');
- indent(level);
- printf("else");
- printstm(n->c, level);
- }
- break;
-
- case STM_DO:
- printf("do");
- printstm(n->a, level);
- if (n->a->type == STM_BLOCK) {
- putchar(' ');
- } else {
- putchar('\n');
- indent(level);
- }
- printf("while (");
- printast(n->b, level);
- printf(");");
- break;
-
- case STM_WHILE:
- printf("while (");
- printast(n->a, level);
- printf(")");
- printstm(n->b, level);
- break;
-
- case STM_CONTINUE:
- if (n->a) {
- printf("continue ");
- printast(n->a, level);
- printf(";");
- } else {
- printf("continue;");
- }
- break;
-
- case STM_BREAK:
- if (n->a) {
- printf("break ");
- printast(n->a, level);
- printf(";");
- } else {
- printf("break;");
- }
- break;
-
- case STM_RETURN:
- if (n->a) {
- printf("return ");
- printast(n->a, level);
- printf(";");
- } else {
- printf("return;");
- }
- break;
-
- case STM_THROW:
- printf("throw ");
- printast(n->a, level);
- printf(";");
- break;
-
- case STM_SWITCH:
- printf("switch (");
- printast(n->a, level);
- printf(") {\n");
- printblock(n->b, level);
- putchar('\n');
- indent(level);
- printf("}");
- break;
-
- case STM_CASE:
- printf("case ");
- printast(n->a, level);
- printf(":");
- if (n->b) {
- printf("\n");
- printblock(n->b, level + 1);
- }
- break;
-
- case STM_DEFAULT:
- printf("default:");
- if (n->a) {
- printf("\n");
- printblock(n->a, level + 1);
- }
- break;
-
- case STM_LABEL:
- printast(n->a, level);
- printf(":");
- printstm(n->b, level - 1);
- break;
-
- case STM_WITH:
- printf("with (");
- printast(n->a, level);
- printf(")");
- printstm(n->b, level);
- break;
-
- case STM_TRY:
- printf("try");
- printstm(n->a, level);
- if (n->b && n->c) {
- printf(" catch (");
- printast(n->b, level);
- printf(")");
- printstm(n->c, level);
- }
- if (n->d) {
- printf(" finally");
- printstm(n->d, level);
- }
- break;
-
- case STM_DEBUGGER:
- printf("debugger");
- break;
-
- case STM_FUNC:
- printf("function ");
- printast(n->a, level);
- printf("(");
- printlist(n->b, level, ", ");
- printf(")\n");
- indent(level);
- printf("{\n");
- printblock(n->c, level + 1);
- printf("\n");
- indent(level);
- printf("}");
- break;
-
- case EXP_FUNC:
- printf("(function ");
- if (n->a)
- printast(n->a, level);
- printf("(");
- printlist(n->b, level, ", ");
- printf(") {\n");
- printblock(n->c, level + 1);
- printf("\n");
- indent(level);
- printf("})");
- break;
-
- case EXP_OBJECT:
- printf("{ ");
- printlist(n->a, level, ", ");
- printf(" }");
- break;
-
- case EXP_PROP_VAL:
- printast(n->a, level);
- printf(": ");
- printast(n->b, level);
- break;
-
- case EXP_ARRAY:
- printf("[ ");
- printlist(n->a, level, ", ");
- printf(" ]");
- break;
-
- case EXP_NEW:
- printf("(new ");
- printast(n->a, level);
- printf("(");
- printlist(n->b, level, ", ");
- printf("))");
- break;
-
- case EXP_CALL:
- printf("(");
- printast(n->a, level);
- printf("(");
- printlist(n->b, level, ", ");
- printf("))");
- break;
-
- case EXP_MEMBER:
- printf("(");
- printast(n->a, level);
- printf(".");
- printast(n->b, level);
- printf(")");
- break;
-
- case EXP_INDEX:
- printf("(");
- printast(n->a, level);
- printf("[");
- printast(n->b, level);
- printf("])");
- break;
-
- case EXP_COND:
- printf("(");
- printast(n->a, level);
- printf(" ? ");
- printast(n->b, level);
- printf(" : ");
- printast(n->c, level);
- printf(")");
- break;
-
- case EXP_NULL: printf("null"); break;
- case EXP_TRUE: printf("true"); break;
- case EXP_FALSE: printf("false"); break;
- case EXP_THIS: printf("this"); break;
-
- case EXP_DELETE: printunary(level, n->a, "(delete ", ")"); break;
- case EXP_VOID: printunary(level, n->a, "(void ", ")"); break;
- case EXP_TYPEOF: printunary(level, n->a, "(typeof ", ")"); break;
- case EXP_PREINC: printunary(level, n->a, "(++", ")"); break;
- case EXP_PREDEC: printunary(level, n->a, "(--", ")"); break;
- case EXP_POSTINC: printunary(level, n->a, "(", "++)"); break;
- case EXP_POSTDEC: printunary(level, n->a, "(", "--)"); break;
- case EXP_POS: printunary(level, n->a, "(+", ")"); break;
- case EXP_NEG: printunary(level, n->a, "(-", ")"); break;
- case EXP_BITNOT: printunary(level, n->a, "(~", ")"); break;
- case EXP_LOGNOT: printunary(level, n->a, "(!", ")"); break;
-
- case EXP_COMMA: printbinary(level, n->a, n->b, ","); break;
- case EXP_LOGOR: printbinary(level, n->a, n->b, "||"); break;
- case EXP_LOGAND: printbinary(level, n->a, n->b, "&&"); break;
- case EXP_BITOR: printbinary(level, n->a, n->b, "|"); break;
- case EXP_BITXOR: printbinary(level, n->a, n->b, "^"); break;
- case EXP_BITAND: printbinary(level, n->a, n->b, "&"); break;
- case EXP_EQ: printbinary(level, n->a, n->b, "=="); break;
- case EXP_NE: printbinary(level, n->a, n->b, "!="); break;
- case EXP_EQ3: printbinary(level, n->a, n->b, "==="); break;
- case EXP_NE3: printbinary(level, n->a, n->b, "!=="); break;
- case EXP_LT: printbinary(level, n->a, n->b, "<"); break;
- case EXP_GT: printbinary(level, n->a, n->b, ">"); break;
- case EXP_LE: printbinary(level, n->a, n->b, "<="); break;
- case EXP_GE: printbinary(level, n->a, n->b, ">="); break;
- case EXP_INSTANCEOF: printbinary(level, n->a, n->b, "instanceof"); break;
- case EXP_IN: printbinary(level, n->a, n->b, "in"); break;
- case EXP_SHL: printbinary(level, n->a, n->b, "<<"); break;
- case EXP_SHR: printbinary(level, n->a, n->b, ">>"); break;
- case EXP_USHR: printbinary(level, n->a, n->b, ">>>"); break;
- case EXP_ADD: printbinary(level, n->a, n->b, "+"); break;
- case EXP_SUB: printbinary(level, n->a, n->b, "-"); break;
- case EXP_MUL: printbinary(level, n->a, n->b, "*"); break;
- case EXP_DIV: printbinary(level, n->a, n->b, "/"); break;
- case EXP_MOD: printbinary(level, n->a, n->b, "%"); break;
- case EXP_ASS: printbinary(level, n->a, n->b, "="); break;
- case EXP_ASS_MUL: printbinary(level, n->a, n->b, "*="); break;
- case EXP_ASS_DIV: printbinary(level, n->a, n->b, "/="); break;
- case EXP_ASS_MOD: printbinary(level, n->a, n->b, "%="); break;
- case EXP_ASS_ADD: printbinary(level, n->a, n->b, "+="); break;
- case EXP_ASS_SUB: printbinary(level, n->a, n->b, "-="); break;
- case EXP_ASS_SHL: printbinary(level, n->a, n->b, "<<="); break;
- case EXP_ASS_SHR: printbinary(level, n->a, n->b, ">>="); break;
- case EXP_ASS_USHR: printbinary(level, n->a, n->b, ">>>="); break;
- case EXP_ASS_BITAND: printbinary(level, n->a, n->b, "&="); break;
- case EXP_ASS_BITXOR: printbinary(level, n->a, n->b, "^="); break;
- case EXP_ASS_BITOR: printbinary(level, n->a, n->b, "|="); break;
-
- default:
- printf("(%s", strast(n->type));
- if (n->a) { putchar(' '); printast(n->a, level); }
- if (n->b) { putchar(' '); printast(n->b, level); }
- if (n->c) { putchar(' '); printast(n->c, level); }
- if (n->d) { putchar(' '); printast(n->d, level); }
- putchar(')');
- break;
- }
-}
--- a/js-ast.h
+++ /dev/null
@@ -1,130 +1,0 @@
-#ifndef js_ast_h
-#define js_ast_h
-
-struct js_Ast
-{
- int type;
- int line;
- js_Ast *a, *b, *c, *d;
- double n;
- const char *s;
- js_Ast *next; /* next in alloc list */
-};
-
-enum
-{
- AST_LIST,
-
- AST_IDENTIFIER,
- AST_NUMBER,
- AST_STRING,
- AST_REGEXP,
-
- /* literals */
- EXP_NULL,
- EXP_TRUE,
- EXP_FALSE,
- EXP_THIS,
-
- EXP_ARRAY,
- EXP_OBJECT,
- EXP_PROP_VAL,
- EXP_PROP_GET,
- EXP_PROP_SET,
-
- /* expressions */
- EXP_INDEX,
- EXP_MEMBER,
- EXP_CALL,
- EXP_NEW,
- EXP_FUNC, /* function expression */
-
- EXP_DELETE,
- EXP_VOID,
- EXP_TYPEOF,
- EXP_PREINC,
- EXP_PREDEC,
- EXP_POSTINC,
- EXP_POSTDEC,
- EXP_POS,
- EXP_NEG,
- EXP_BITNOT,
- EXP_LOGNOT,
- EXP_LOGOR,
-
- EXP_LOGAND,
- EXP_BITOR,
- EXP_BITXOR,
- EXP_BITAND,
- EXP_EQ,
- EXP_NE,
- EXP_EQ3,
- EXP_NE3,
- EXP_LT,
- EXP_GT,
- EXP_LE,
- EXP_GE,
- EXP_INSTANCEOF,
- EXP_IN,
- EXP_SHL,
- EXP_SHR,
- EXP_USHR,
- EXP_ADD,
- EXP_SUB,
- EXP_MUL,
- EXP_DIV,
- EXP_MOD,
-
- EXP_COND,
-
- EXP_ASS,
- EXP_ASS_MUL,
- EXP_ASS_DIV,
- EXP_ASS_MOD,
- EXP_ASS_ADD,
- EXP_ASS_SUB,
- EXP_ASS_SHL,
- EXP_ASS_SHR,
- EXP_ASS_USHR,
- EXP_ASS_BITAND,
- EXP_ASS_BITXOR,
- EXP_ASS_BITOR,
-
- EXP_COMMA,
-
- EXP_VAR, /* var initializer */
-
- /* statements */
- STM_BLOCK,
- STM_FUNC, /* function declaration */
- STM_NOP,
- STM_VAR,
- STM_IF,
- STM_DO,
- STM_WHILE,
- STM_FOR,
- STM_FOR_VAR,
- STM_FOR_IN,
- STM_FOR_IN_VAR,
- STM_CONTINUE,
- STM_BREAK,
- STM_RETURN,
- STM_WITH,
- STM_SWITCH,
- STM_THROW,
- STM_TRY,
- STM_LABEL,
- STM_CASE,
- STM_DEFAULT,
- STM_DEBUGGER,
-};
-
-js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast *c, js_Ast *d);
-js_Ast *jsP_newstrnode(js_State *J, int type, const char *s);
-js_Ast *jsP_newnumnode(js_State *J, int type, double n);
-void jsP_freeast(js_State *J);
-
-void printast(js_Ast *n, int level);
-void printblock(js_Ast *n, int level);
-
-#endif
--- a/js-lex.c
+++ /dev/null
@@ -1,608 +1,0 @@
-#include "js.h"
-#include "js-parse.h"
-
-#define nelem(a) (sizeof (a) / sizeof (a)[0])
-
-static const char *tokenstring[] = {
- "(end-of-file)",
- "'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'",
- "'\\x08'", "'\\x09'", "'\\x0A'", "'\\x0B'", "'\\x0C'", "'\\x0D'", "'\\x0E'", "'\\x0F'",
- "'\\x10'", "'\\x11'", "'\\x12'", "'\\x13'", "'\\x14'", "'\\x15'", "'\\x16'", "'\\x17'",
- "'\\x18'", "'\\x19'", "'\\x1A'", "'\\x1B'", "'\\x1C'", "'\\x1D'", "'\\x1E'", "'\\x1F'",
- "' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'\\''",
- "'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'",
- "'0'", "'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'",
- "'8'", "'9'", "':'", "';'", "'<'", "'='", "'>'", "'?'",
- "'@'", "'A'", "'B'", "'C'", "'D'", "'E'", "'F'", "'G'",
- "'H'", "'I'", "'J'", "'K'", "'L'", "'M'", "'N'", "'O'",
- "'P'", "'Q'", "'R'", "'S'", "'T'", "'U'", "'V'", "'W'",
- "'X'", "'Y'", "'Z'", "'['", "'\'", "']'", "'^'", "'_'",
- "'`'", "'a'", "'b'", "'c'", "'d'", "'e'", "'f'", "'g'",
- "'h'", "'i'", "'j'", "'k'", "'l'", "'m'", "'n'", "'o'",
- "'p'", "'q'", "'r'", "'s'", "'t'", "'u'", "'v'", "'w'",
- "'x'", "'y'", "'z'", "'{'", "'|'", "'}'", "'~'", "'\\x7F'",
-
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
- 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-
- "(identifier)", "(number)", "(string)", "(regexp)",
-
- "'<='", "'>='", "'=='", "'!='", "'==='", "'!=='",
- "'<<'", "'>>'", "'>>>'", "'&&'", "'||'",
- "'+='", "'-='", "'*='", "'/='", "'%='",
- "'<<='", "'>>='", "'>>>='", "'&='", "'|='", "'^='",
- "'++'", "'--'",
-
- "'break'", "'case'", "'catch'", "'continue'", "'debugger'",
- "'default'", "'delete'", "'do'", "'else'", "'false'", "'finally'", "'for'",
- "'function'", "'if'", "'in'", "'instanceof'", "'new'", "'null'", "'return'",
- "'switch'", "'this'", "'throw'", "'true'", "'try'", "'typeof'", "'var'",
- "'void'", "'while'", "'with'",
-};
-
-const char *jsP_tokenstring(int token)
-{
- if (token >= 0 && token < nelem(tokenstring))
- if (tokenstring[token])
- return tokenstring[token];
- return "<unknown>";
-}
-
-static const char *keywords[] = {
- "break", "case", "catch", "continue", "debugger", "default", "delete",
- "do", "else", "false", "finally", "for", "function", "if", "in",
- "instanceof", "new", "null", "return", "switch", "this", "throw",
- "true", "try", "typeof", "var", "void", "while", "with",
-};
-
-static const char *futurewords[] = {
- "class", "const", "enum", "export", "extends", "import", "super",
-};
-
-static const char *strictfuturewords[] = {
- "implements", "interface", "let", "package", "private", "protected",
- "public", "static", "yield",
-};
-
-static inline int findword(const char *s, const char **list, int num)
-{
- int l = 0;
- int r = num - 1;
- while (l <= r) {
- int m = (l + r) >> 1;
- int c = strcmp(s, list[m]);
- if (c < 0)
- r = m - 1;
- else if (c > 0)
- l = m + 1;
- else
- return m;
- }
- return -1;
-}
-
-static inline int findkeyword(js_State *J, const char *s)
-{
- int i = findword(s, keywords, nelem(keywords));
- if (i >= 0) {
- J->text = keywords[i];
- return TK_BREAK + i; /* first keyword + i */
- }
-
- if (findword(s, futurewords, nelem(futurewords)) >= 0)
- return jsP_error(J, "'%s' is a future reserved word", s);
- if (J->strict && findword(s, strictfuturewords, nelem(strictfuturewords)) >= 0)
- return jsP_error(J, "'%s' is a strict mode future reserved word", s);
-
- J->text = js_intern(J, s);
- return TK_IDENTIFIER;
-}
-
-#define GET() (*(*sp)++)
-#define UNGET() ((*sp)--)
-#define PEEK() (**sp)
-#define NEXT() ((*sp)++)
-#define NEXTPEEK() (NEXT(), PEEK())
-#define LOOK(x) (PEEK() == x ? (NEXT(), 1) : 0)
-
-static void textinit(js_State *J)
-{
- if (!J->buf.text) {
- J->buf.cap = 4096;
- J->buf.text = malloc(J->buf.cap);
- }
- J->buf.len = 0;
-}
-
-static inline void textpush(js_State *J, int c)
-{
- if (J->buf.len >= J->buf.cap) {
- J->buf.cap = J->buf.cap * 2;
- J->buf.text = realloc(J->buf.text, J->buf.cap);
- }
- J->buf.text[J->buf.len++] = c;
-}
-
-static inline char *textend(js_State *J)
-{
- textpush(J, 0);
- return J->buf.text;
-}
-
-static inline int iswhite(int c)
-{
- return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF;
-}
-
-static inline int isnewline(c)
-{
- return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
-}
-
-static inline int isidentifierstart(int c)
-{
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '$' || c == '_';
-}
-
-static inline int isidentifierpart(int c)
-{
- return (c >= '0' && c <= '9') || isidentifierstart(c);
-}
-
-static inline int isdec(int c)
-{
- return (c >= '0' && c <= '9');
-}
-
-static inline int ishex(int c)
-{
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-static inline int tohex(int c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 0xA;
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 0xA;
- return 0;
-}
-
-static inline void lexlinecomment(const char **sp)
-{
- int c = PEEK();
- while (c && !isnewline(c)) {
- c = NEXTPEEK();
- }
-}
-
-static inline int lexcomment(const char **sp)
-{
- while (1) {
- int c = GET();
- if (c == '*') {
- while (c == '*')
- c = GET();
- if (c == '/')
- return 0;
- } else if (c == 0) {
- return -1;
- }
- }
-}
-
-static inline double lexhex(const char **sp)
-{
- double n = 0;
- int c = PEEK();
- while (ishex(c)) {
- n = n * 16 + tohex(c);
- c = NEXTPEEK();
- }
- return n;
-}
-
-static inline double lexinteger(const char **sp)
-{
- double n = 0;
- int c = PEEK();
- while (isdec(c)) {
- n = n * 10 + (c - '0');
- c = NEXTPEEK();
- }
- return n;
-}
-
-static inline double lexfraction(const char **sp)
-{
- double n = 0;
- double d = 1;
- int c = PEEK();
- while (isdec(c)) {
- n = n * 10 + (c - '0');
- d = d * 10;
- c = NEXTPEEK();
- }
- return n / d;
-}
-
-static inline double lexexponent(const char **sp)
-{
- if (LOOK('e') || LOOK('E')) {
- if (LOOK('-'))
- return -lexinteger(sp);
- else if (LOOK('+'))
- return lexinteger(sp);
- else
- return lexinteger(sp);
- }
- return 0;
-}
-
-static inline int lexnumber(js_State *J, const char **sp)
-{
- double n;
-
- if ((*sp)[0] == '0' && ((*sp)[1] == 'x' || (*sp)[1] == 'X')) {
- *sp += 2;
- if (!ishex(PEEK()))
- return jsP_error(J, "0x not followed by hexademical digit");
- J->number = lexhex(sp);
- return TK_NUMBER;
- }
-
- if ((*sp)[0] == '0' && isdec((*sp)[1]))
- return jsP_error(J, "number with leading zero");
-
- n = lexinteger(sp);
- if (LOOK('.'))
- n += lexfraction(sp);
- n *= pow(10, lexexponent(sp));
-
- if (isidentifierstart(PEEK()))
- return jsP_error(J, "number with letter suffix");
-
- J->number = n;
- return TK_NUMBER;
-}
-
-static inline int lexescape(js_State *J, const char **sp)
-{
- int c = GET();
- int x = 0;
-
- if (isnewline(c)) {
- if (c == '\r' && PEEK() == '\n')
- NEXT();
- return 0;
- }
-
- switch (c) {
- case 'u':
- if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 12;
- if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 8;
- if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 4;
- if (!ishex(PEEK())) return 1; else x |= NEXTPEEK();
- textpush(J, x);
- break;
- case 'x':
- if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 4;
- if (!ishex(PEEK())) return 1; else x |= NEXTPEEK();
- textpush(J, x);
- break;
- case '0': textpush(J, 0); break;
- case '\\': textpush(J, '\\'); break;
- case '\'': textpush(J, '\''); break;
- case '"': textpush(J, '"'); break;
- case 'b': textpush(J, '\b'); break;
- case 'f': textpush(J, '\f'); break;
- case 'n': textpush(J, '\n'); break;
- case 'r': textpush(J, '\r'); break;
- case 't': textpush(J, '\t'); break;
- case 'v': textpush(J, '\v'); break;
- default: textpush(J, c); break;
- }
- return 0;
-}
-
-static inline int lexstring(js_State *J, const char **sp, int q)
-{
- const char *s;
- int c = GET();
-
- textinit(J);
-
- while (c != q) {
- if (c == 0 || isnewline(c))
- return jsP_error(J, "string not terminated");
- if (c == '\\') {
- if (lexescape(J, sp))
- return jsP_error(J, "malformed escape sequence");
- } else {
- textpush(J, c);
- }
- c = GET();
- }
-
- s = textend(J);
-
- J->text = js_intern(J, s);
- return TK_STRING;
-}
-
-/* the ugliest language wart ever... */
-static int isregexpcontext(int last)
-{
- switch (last) {
- case ']':
- case ')':
- case TK_IDENTIFIER:
- case TK_NUMBER:
- case TK_STRING:
- case TK_FALSE:
- case TK_NULL:
- case TK_THIS:
- case TK_TRUE:
- return 0;
- default:
- return 1;
- }
-}
-
-static int lexregexp(js_State *J, const char **sp)
-{
- const char *s;
- int c;
-
- textinit(J);
-
- /* regexp body */
- c = GET();
- while (c != '/') {
- if (c == 0 || isnewline(c)) {
- return jsP_error(J, "regular expression not terminated");
- } else if (c == '\\') {
- textpush(J, c);
- c = GET();
- if (c == 0 || isnewline(c))
- return jsP_error(J, "regular expression not terminated");
- textpush(J, c);
- c = GET();
- } else {
- textpush(J, c);
- c = GET();
- }
- }
-
- s = textend(J);
-
- /* regexp flags */
- J->flags.g = J->flags.i = J->flags.m = 0;
-
- c = PEEK();
- while (isidentifierpart(c)) {
- if (c == 'g') J->flags.g ++;
- else if (c == 'i') J->flags.i ++;
- else if (c == 'm') J->flags.m ++;
- else return jsP_error(J, "illegal flag in regular expression: %c", c);
- c = NEXTPEEK();
- }
-
- if (J->flags.g > 1 || J->flags.i > 1 || J->flags.m > 1)
- return jsP_error(J, "duplicated flag in regular expression");
-
- J->text = js_intern(J, s);
- return TK_REGEXP;
-}
-
-/* simple "return [no Line Terminator here] ..." contexts */
-static inline int isnlthcontext(int last)
-{
- switch (last) {
- case TK_BREAK:
- case TK_CONTINUE:
- case TK_RETURN:
- case TK_THROW:
- return 1;
- default:
- return 0;
- }
-}
-
-static int lex(js_State *J, const char **sp)
-{
- J->newline = 0;
-
- while (1) {
- int c = GET();
-
- while (iswhite(c))
- c = GET();
-
- if (isnewline(c)) {
- /* consume CR LF as one unit */
- if (c == '\r' && PEEK() == '\n')
- NEXT();
- J->line++;
- J->newline = 1;
- if (isnlthcontext(J->lasttoken))
- return ';';
- continue;
- }
-
- if (c == '/') {
- if (LOOK('/')) {
- lexlinecomment(sp);
- continue;
- } else if (LOOK('*')) {
- if (lexcomment(sp))
- return jsP_error(J, "multi-line comment not terminated");
- continue;
- } else if (isregexpcontext(J->lasttoken)) {
- return lexregexp(J, sp);
- } else if (LOOK('=')) {
- return TK_DIV_ASS;
- } else {
- return '/';
- }
- }
-
- if (isidentifierstart(c)) {
- textinit(J);
- textpush(J, c);
-
- c = PEEK();
- while (isidentifierpart(c)) {
- textpush(J, c);
- c = NEXTPEEK();
- }
-
- textend(J);
-
- return findkeyword(J, J->buf.text);
- }
-
- if (c >= '0' && c <= '9') {
- UNGET();
- return lexnumber(J, sp);
- }
-
- switch (c) {
- case '(':
- case ')':
- case ',':
- case ':':
- case ';':
- case '?':
- case '[':
- case ']':
- case '{':
- case '}':
- case '~':
- return c;
-
- case '\'':
- case '"':
- return lexstring(J, sp, c);
-
- case '.':
- if (isdec(PEEK())) {
- UNGET();
- return lexnumber(J, sp);
- }
- return '.';
-
- case '<':
- if (LOOK('<')) {
- if (LOOK('='))
- return TK_SHL_ASS;
- return TK_SHL;
- }
- if (LOOK('='))
- return TK_LE;
- return '<';
-
- case '>':
- if (LOOK('>')) {
- if (LOOK('>')) {
- if (LOOK('='))
- return TK_USHR_ASS;
- return TK_USHR;
- }
- if (LOOK('='))
- return TK_SHR_ASS;
- return TK_SHR;
- }
- if (LOOK('='))
- return TK_GE;
- return '>';
-
- case '=':
- if (LOOK('=')) {
- if (LOOK('='))
- return TK_EQ3;
- return TK_EQ;
- }
- return '=';
-
- case '!':
- if (LOOK('=')) {
- if (LOOK('='))
- return TK_NE3;
- return TK_NE;
- }
- return '!';
-
- case '+':
- if (LOOK('+'))
- return TK_INC;
- if (LOOK('='))
- return TK_ADD_ASS;
- return '+';
-
- case '-':
- if (LOOK('-'))
- return TK_DEC;
- if (LOOK('='))
- return TK_SUB_ASS;
- return '-';
-
- case '*':
- if (LOOK('='))
- return TK_MUL_ASS;
- return '*';
-
- case '%':
- if (LOOK('='))
- return TK_MOD_ASS;
- return '%';
-
- case '&':
- if (LOOK('&'))
- return TK_AND;
- if (LOOK('='))
- return TK_AND_ASS;
- return '&';
-
- case '|':
- if (LOOK('|'))
- return TK_OR;
- if (LOOK('='))
- return TK_OR_ASS;
- return '|';
-
- case '^':
- if (LOOK('='))
- return TK_XOR_ASS;
- return '^';
-
- case 0:
- return 0; /* EOF */
- }
-
- if (c >= 0x20 && c <= 0x7E)
- return jsP_error(J, "unexpected character: '%c'", c);
- return jsP_error(J, "unexpected character: \\u%04X", c);
- }
-}
-
-void jsP_initlex(js_State *J, const char *filename, const char *source)
-{
- J->filename = filename;
- J->source = source;
- J->line = 1;
- J->lasttoken = 0;
-}
-
-int jsP_lex(js_State *J)
-{
- return J->lasttoken = lex(J, &J->source);
-}
--- a/js-load.c
+++ /dev/null
@@ -1,48 +1,0 @@
-#include "js.h"
-#include "js-parse.h"
-
-static int jsP_loadstring(js_State *J, const char *filename, const char *source)
-{
- return jsP_parse(J, filename, source);
-}
-
-int js_loadstring(js_State *J, const char *source)
-{
- return jsP_loadstring(J, "(string)", source);
-}
-
-int js_loadfile(js_State *J, const char *filename)
-{
- FILE *f;
- char *s;
- int n, t;
-
- f = fopen(filename, "r");
- if (!f)
- return js_error(J, "cannot open file: '%s'", filename);
-
- fseek(f, 0, SEEK_END);
- n = ftell(f);
- fseek(f, 0, SEEK_SET);
-
- s = malloc(n + 1); /* add space for string terminator */
- if (!s) {
- fclose(f);
- return js_error(J, "cannot allocate storage for file contents: '%s'", filename);
- }
-
- t = fread(s, 1, n, f);
- if (t != n) {
- free(s);
- fclose(f);
- return js_error(J, "cannot read data from file: '%s'", filename);
- }
-
- s[n] = 0; /* zero-terminate string containing file data */
-
- t = jsP_loadstring(J, filename, s);
-
- free(s);
- fclose(f);
- return t;
-}
--- a/js-parse.c
+++ /dev/null
@@ -1,760 +1,0 @@
-#include "js.h"
-#include "js-parse.h"
-#include "js-ast.h"
-
-#define LIST(h) jsP_newnode(J, AST_LIST, h, 0, 0, 0);
-
-#define EXP0(x) jsP_newnode(J, EXP_ ## x, 0, 0, 0, 0)
-#define EXP1(x,a) jsP_newnode(J, EXP_ ## x, a, 0, 0, 0)
-#define EXP2(x,a,b) jsP_newnode(J, EXP_ ## x, a, b, 0, 0)
-#define EXP3(x,a,b,c) jsP_newnode(J, EXP_ ## x, a, b, c, 0)
-
-#define STM0(x) jsP_newnode(J, STM_ ## x, 0, 0, 0, 0)
-#define STM1(x,a) jsP_newnode(J, STM_ ## x, a, 0, 0, 0)
-#define STM2(x,a,b) jsP_newnode(J, STM_ ## x, a, b, 0, 0)
-#define STM3(x,a,b,c) jsP_newnode(J, STM_ ## x, a, b, c, 0)
-#define STM4(x,a,b,c,d) jsP_newnode(J, STM_ ## x, a, b, c, d)
-
-#define TOKSTR jsP_tokenstring(J->lookahead)
-
-static js_Ast *expression(js_State *J, int notin);
-static js_Ast *assignment(js_State *J, int notin);
-static js_Ast *memberexp(js_State *J);
-static js_Ast *statement(js_State *J);
-static js_Ast *funcbody(js_State *J);
-
-static inline void next(js_State *J)
-{
- J->lookahead = jsP_lex(J);
-}
-
-static inline int accept(js_State *J, int t)
-{
- if (J->lookahead == t) {
- next(J);
- return 1;
- }
- return 0;
-}
-
-static inline void expect(js_State *J, int t)
-{
- if (accept(J, t))
- return;
- jsP_error(J, "unexpected token: %s (expected %s)", TOKSTR, jsP_tokenstring(t));
-}
-
-static void semicolon(js_State *J)
-{
- if (J->lookahead == ';') {
- next(J);
- return;
- }
- if (J->newline || J->lookahead == '}' || J->lookahead == 0)
- return;
- jsP_error(J, "unexpected token: %s (expected ';')", TOKSTR);
-}
-
-/* Literals */
-
-static js_Ast *identifier(js_State *J)
-{
- if (J->lookahead == TK_IDENTIFIER) {
- js_Ast *a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
- next(J);
- return a;
- }
- jsP_error(J, "unexpected token: %s (expected identifier)", TOKSTR);
- return NULL;
-}
-
-static js_Ast *identifieropt(js_State *J)
-{
- if (J->lookahead == TK_IDENTIFIER)
- return identifier(J);
- return NULL;
-}
-
-static js_Ast *identifiername(js_State *J)
-{
- if (J->lookahead == TK_IDENTIFIER || J->lookahead >= TK_BREAK) {
- js_Ast *a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
- next(J);
- return a;
- }
- jsP_error(J, "unexpected token: %s (expected identifier or keyword)", TOKSTR);
- return NULL;
-}
-
-static js_Ast *arrayelement(js_State *J)
-{
- if (J->lookahead == ',')
- return EXP0(NULL); /* TODO: should be 'undefined' */
- return assignment(J, 0);
-}
-
-static js_Ast *arrayliteral(js_State *J)
-{
- js_Ast *head, *tail;
- if (J->lookahead == ']')
- return NULL;
- head = tail = LIST(arrayelement(J));
- while (accept(J, ',')) {
- if (J->lookahead != ']')
- tail = tail->b = LIST(arrayelement(J));
- }
- return head;
-}
-
-static js_Ast *propname(js_State *J)
-{
- js_Ast *name;
- if (J->lookahead == TK_NUMBER) {
- name = jsP_newnumnode(J, AST_NUMBER, J->number);
- next(J);
- } else if (J->lookahead == TK_STRING) {
- name = jsP_newstrnode(J, AST_STRING, J->text);
- next(J);
- } else {
- name = identifiername(J);
- }
- return name;
-}
-
-static js_Ast *propassign(js_State *J)
-{
- js_Ast *name, *value, *arg, *body;
-
- if (J->lookahead == TK_IDENTIFIER && !strcmp(J->text, "get")) {
- next(J);
- name = propname(J);
- expect(J, '(');
- expect(J, ')');
- body = funcbody(J);
- return EXP2(PROP_GET, name, body);
- }
-
- if (J->lookahead == TK_IDENTIFIER && !strcmp(J->text, "set")) {
- next(J);
- name = propname(J);
- expect(J, '(');
- arg = identifier(J);
- expect(J, ')');
- body = funcbody(J);
- return EXP3(PROP_SET, name, arg, body);
- }
-
- name = propname(J);
- expect(J, ':');
- value = assignment(J, 0);
- return EXP2(PROP_VAL, name, value);
-}
-
-static js_Ast *objectliteral(js_State *J)
-{
- js_Ast *head, *tail;
- if (J->lookahead == '}')
- return NULL;
- head = tail = LIST(propassign(J));
- while (accept(J, ',')) {
- if (J->lookahead == '}')
- break;
- tail = tail->b = LIST(propassign(J));
- }
- return head;
-}
-
-/* Expressions */
-
-static js_Ast *primary(js_State *J)
-{
- js_Ast *a;
-
- if (J->lookahead == TK_IDENTIFIER) {
- a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
- next(J);
- return a;
- }
- if (J->lookahead == TK_STRING) {
- a = jsP_newstrnode(J, AST_STRING, J->text);
- next(J);
- return a;
- }
- if (J->lookahead == TK_REGEXP) {
- a = jsP_newstrnode(J, AST_REGEXP, J->text);
- // TODO: flags
- next(J);
- return a;
- }
- if (J->lookahead == TK_NUMBER) {
- a = jsP_newnumnode(J, AST_NUMBER, J->number);
- next(J);
- return a;
- }
-
- if (accept(J, TK_THIS)) return EXP0(THIS);
- if (accept(J, TK_NULL)) return EXP0(NULL);
- if (accept(J, TK_TRUE)) return EXP0(TRUE);
- if (accept(J, TK_FALSE)) return EXP0(FALSE);
- if (accept(J, '{')) { a = EXP1(OBJECT, objectliteral(J)); expect(J, '}'); return a; }
- if (accept(J, '[')) { a = EXP1(ARRAY, arrayliteral(J)); expect(J, ']'); return a; }
- if (accept(J, '(')) { a = expression(J, 0); expect(J, ')'); return a; }
-
- jsP_error(J, "unexpected token in expression: %s", TOKSTR);
- return NULL;
-}
-
-static js_Ast *arguments(js_State *J)
-{
- js_Ast *head, *tail;
- if (J->lookahead == ')')
- return NULL;
- head = tail = LIST(assignment(J, 0));
- while (accept(J, ',')) {
- tail = tail->b = LIST(assignment(J, 0));
- }
- return head;
-}
-
-static js_Ast *parameters(js_State *J)
-{
- js_Ast *head, *tail;
- if (J->lookahead == ')')
- return NULL;
- head = tail = LIST(identifier(J));
- while (accept(J, ',')) {
- tail = tail->b = LIST(identifier(J));
- }
- return head;
-}
-
-static js_Ast *newexp(js_State *J)
-{
- js_Ast *a, *b, *c;
-
- if (accept(J, TK_NEW)) {
- a = memberexp(J);
- if (accept(J, '(')) {
- b = arguments(J);
- expect(J, ')');
- return EXP2(NEW, a, b);
- }
- return EXP1(NEW, a);
- }
-
- if (accept(J, TK_FUNCTION)) {
- a = identifieropt(J);
- expect(J, '(');
- b = parameters(J);
- expect(J, ')');
- c = funcbody(J);
- return EXP3(FUNC, a, b, c);
- }
-
- return primary(J);
-}
-
-static js_Ast *memberexp(js_State *J)
-{
- js_Ast *a = newexp(J);
-loop:
- if (accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; }
- if (accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); expect(J, ']'); goto loop; }
- return a;
-}
-
-static js_Ast *callexp(js_State *J)
-{
- js_Ast *a = newexp(J);
-loop:
- if (accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; }
- if (accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); expect(J, ']'); goto loop; }
- if (accept(J, '(')) { a = EXP2(CALL, a, arguments(J)); expect(J, ')'); goto loop; }
- return a;
-}
-
-static js_Ast *postfix(js_State *J)
-{
- js_Ast *a = callexp(J);
- if (!J->newline && accept(J, TK_INC)) return EXP1(POSTINC, a);
- if (!J->newline && accept(J, TK_DEC)) return EXP1(POSTDEC, a);
- return a;
-}
-
-static js_Ast *unary(js_State *J)
-{
- if (accept(J, TK_DELETE)) return EXP1(DELETE, unary(J));
- if (accept(J, TK_VOID)) return EXP1(VOID, unary(J));
- if (accept(J, TK_TYPEOF)) return EXP1(TYPEOF, unary(J));
- if (accept(J, TK_INC)) return EXP1(PREINC, unary(J));
- if (accept(J, TK_DEC)) return EXP1(PREDEC, unary(J));
- if (accept(J, '+')) return EXP1(POS, unary(J));
- if (accept(J, '-')) return EXP1(NEG, unary(J));
- if (accept(J, '~')) return EXP1(BITNOT, unary(J));
- if (accept(J, '!')) return EXP1(LOGNOT, unary(J));
- return postfix(J);
-}
-
-static js_Ast *multiplicative(js_State *J)
-{
- js_Ast *a = unary(J);
-loop:
- if (accept(J, '*')) { a = EXP2(MUL, a, unary(J)); goto loop; }
- if (accept(J, '/')) { a = EXP2(DIV, a, unary(J)); goto loop; }
- if (accept(J, '%')) { a = EXP2(MOD, a, unary(J)); goto loop; }
- return a;
-}
-
-static js_Ast *additive(js_State *J)
-{
- js_Ast *a = multiplicative(J);
-loop:
- if (accept(J, '+')) { a = EXP2(ADD, a, multiplicative(J)); goto loop; }
- if (accept(J, '-')) { a = EXP2(SUB, a, multiplicative(J)); goto loop; }
- return a;
-}
-
-static js_Ast *shift(js_State *J)
-{
- js_Ast *a = additive(J);
-loop:
- if (accept(J, TK_SHL)) { a = EXP2(SHL, a, additive(J)); goto loop; }
- if (accept(J, TK_SHR)) { a = EXP2(SHR, a, additive(J)); goto loop; }
- if (accept(J, TK_USHR)) { a = EXP2(USHR, a, additive(J)); goto loop; }
- return a;
-}
-
-static js_Ast *relational(js_State *J, int notin)
-{
- js_Ast *a = shift(J);
-loop:
- if (accept(J, '<')) { a = EXP2(LT, a, shift(J)); goto loop; }
- if (accept(J, '>')) { a = EXP2(GT, a, shift(J)); goto loop; }
- if (accept(J, TK_LE)) { a = EXP2(LE, a, shift(J)); goto loop; }
- if (accept(J, TK_GE)) { a = EXP2(GE, a, shift(J)); goto loop; }
- if (accept(J, TK_INSTANCEOF)) { a = EXP2(INSTANCEOF, a, shift(J)); goto loop; }
- if (!notin && accept(J, TK_IN)) { a = EXP2(IN, a, shift(J)); goto loop; }
- return a;
-}
-
-static js_Ast *equality(js_State *J, int notin)
-{
- js_Ast *a = relational(J, notin);
-loop:
- if (accept(J, TK_EQ)) { a = EXP2(EQ, a, relational(J, notin)); goto loop; }
- if (accept(J, TK_NE)) { a = EXP2(NE, a, relational(J, notin)); goto loop; }
- if (accept(J, TK_EQ3)) { a = EXP2(EQ3, a, relational(J, notin)); goto loop; }
- if (accept(J, TK_NE3)) { a = EXP2(NE3, a, relational(J, notin)); goto loop; }
- return a;
-}
-
-static js_Ast *bitand(js_State *J, int notin)
-{
- js_Ast *a = equality(J, notin);
- while (accept(J, '&'))
- a = EXP2(BITAND, a, equality(J, notin));
- return a;
-}
-
-static js_Ast *bitxor(js_State *J, int notin)
-{
- js_Ast *a = bitand(J, notin);
- while (accept(J, '^'))
- a = EXP2(BITXOR, a, bitand(J, notin));
- return a;
-}
-
-static js_Ast *bitor(js_State *J, int notin)
-{
- js_Ast *a = bitxor(J, notin);
- while (accept(J, '|'))
- a = EXP2(BITOR, a, bitxor(J, notin));
- return a;
-}
-
-static js_Ast *logand(js_State *J, int notin)
-{
- js_Ast *a = bitor(J, notin);
- while (accept(J, TK_AND))
- a = EXP2(LOGAND, a, bitor(J, notin));
- return a;
-}
-
-static js_Ast *logor(js_State *J, int notin)
-{
- js_Ast *a = logand(J, notin);
- while (accept(J, TK_OR))
- a = EXP2(LOGOR, a, logand(J, notin));
- return a;
-}
-
-static js_Ast *conditional(js_State *J, int notin)
-{
- js_Ast *a, *b, *c;
- a = logor(J, notin);
- if (accept(J, '?')) {
- b = assignment(J, notin);
- expect(J, ':');
- c = assignment(J, notin);
- return EXP3(COND, a, b, c);
- }
- return a;
-}
-
-static js_Ast *assignment(js_State *J, int notin)
-{
- js_Ast *a = conditional(J, notin);
- if (accept(J, '=')) return EXP2(ASS, a, assignment(J, notin));
- if (accept(J, TK_MUL_ASS)) return EXP2(ASS_MUL, a, assignment(J, notin));
- if (accept(J, TK_DIV_ASS)) return EXP2(ASS_DIV, a, assignment(J, notin));
- if (accept(J, TK_MOD_ASS)) return EXP2(ASS_MOD, a, assignment(J, notin));
- if (accept(J, TK_ADD_ASS)) return EXP2(ASS_ADD, a, assignment(J, notin));
- if (accept(J, TK_SUB_ASS)) return EXP2(ASS_SUB, a, assignment(J, notin));
- if (accept(J, TK_SHL_ASS)) return EXP2(ASS_SHL, a, assignment(J, notin));
- if (accept(J, TK_SHR_ASS)) return EXP2(ASS_SHR, a, assignment(J, notin));
- if (accept(J, TK_USHR_ASS)) return EXP2(ASS_USHR, a, assignment(J, notin));
- if (accept(J, TK_AND_ASS)) return EXP2(ASS_BITAND, a, assignment(J, notin));
- if (accept(J, TK_XOR_ASS)) return EXP2(ASS_BITXOR, a, assignment(J, notin));
- if (accept(J, TK_OR_ASS)) return EXP2(ASS_BITOR, a, assignment(J, notin));
- return a;
-}
-
-static js_Ast *expression(js_State *J, int notin)
-{
- js_Ast *a = assignment(J, notin);
- while (accept(J, ','))
- a = EXP2(COMMA, a, assignment(J, notin));
- return a;
-}
-
-/* Statements */
-
-static js_Ast *vardec(js_State *J, int notin)
-{
- js_Ast *a = identifier(J);
- if (accept(J, '='))
- return EXP2(VAR, a, assignment(J, notin));
- return EXP1(VAR, a);
-}
-
-static js_Ast *vardeclist(js_State *J, int notin)
-{
- js_Ast *head, *tail;
- head = tail = LIST(vardec(J, notin));
- while (accept(J, ','))
- tail = tail->b = LIST(vardec(J, notin));
- return head;
-}
-
-static js_Ast *statementlist(js_State *J)
-{
- js_Ast *head, *tail;
- if (J->lookahead == '}' || J->lookahead == TK_CASE || J->lookahead == TK_DEFAULT)
- return NULL;
- head = tail = LIST(statement(J));
- while (J->lookahead != '}' && J->lookahead != TK_CASE && J->lookahead != TK_DEFAULT)
- tail = tail->b = LIST(statement(J));
- return head;
-}
-
-static js_Ast *caseclause(js_State *J)
-{
- js_Ast *a, *b;
-
- if (accept(J, TK_CASE)) {
- a = expression(J, 0);
- expect(J, ':');
- b = statementlist(J);
- return STM2(CASE, a, b);
- }
-
- if (accept(J, TK_DEFAULT)) {
- expect(J, ':');
- a = statementlist(J);
- return STM1(DEFAULT, a);
- }
-
- jsP_error(J, "unexpected token in switch: %s (expected 'case' or 'default')", TOKSTR);
- return NULL;
-}
-
-static js_Ast *caselist(js_State *J)
-{
- js_Ast *head, *tail;
- if (J->lookahead == '}')
- return NULL;
- head = tail = LIST(caseclause(J));
- while (J->lookahead != '}')
- tail = tail->b = LIST(caseclause(J));
- return head;
-}
-
-static js_Ast *block(js_State *J)
-{
- js_Ast *a;
- expect(J, '{');
- a = statementlist(J);
- expect(J, '}');
- return STM1(BLOCK, a);
-}
-
-static js_Ast *forexpression(js_State *J, int end)
-{
- js_Ast *a = NULL;
- if (J->lookahead != end)
- a = expression(J, 0);
- expect(J, end);
- return a;
-}
-
-static js_Ast *forstatement(js_State *J)
-{
- js_Ast *a, *b, *c, *d;
- expect(J, '(');
- if (accept(J, TK_VAR)) {
- a = vardeclist(J, 1);
- if (accept(J, ';')) {
- b = forexpression(J, ';');
- c = forexpression(J, ')');
- d = statement(J);
- return STM4(FOR_VAR, a, b, c, d);
- }
- if (accept(J, TK_IN)) {
- b = expression(J, 0);
- expect(J, ')');
- c = statement(J);
- return STM3(FOR_IN_VAR, a, b, c);
- }
- jsP_error(J, "unexpected token in for-var-statement: %s", TOKSTR);
- return NULL;
- }
-
- if (J->lookahead != ';') {
- a = expression(J, 1);
- }
- if (accept(J, ';')) {
- b = forexpression(J, ';');
- c = forexpression(J, ')');
- d = statement(J);
- return STM4(FOR, a, b, c, d);
- }
- if (accept(J, TK_IN)) {
- b = expression(J, 0);
- expect(J, ')');
- c = statement(J);
- return STM3(FOR_IN, a, b, c);
- }
- jsP_error(J, "unexpected token in for-statement: %s", TOKSTR);
- return NULL;
-}
-
-static js_Ast *statement(js_State *J)
-{
- js_Ast *a, *b, *c, *d;
-
- if (J->lookahead == '{') {
- return block(J);
- }
-
- if (accept(J, TK_VAR)) {
- a = vardeclist(J, 0);
- semicolon(J);
- return STM1(VAR, a);
- }
-
- /* empty statement */
- if (accept(J, ';')) {
- return STM0(NOP);
- }
-
- if (accept(J, TK_IF)) {
- expect(J, '(');
- a = expression(J, 0);
- expect(J, ')');
- b = statement(J);
- if (accept(J, TK_ELSE))
- c = statement(J);
- else
- c = NULL;
- return STM3(IF, a, b, c);
- }
-
- if (accept(J, TK_DO)) {
- a = statement(J);
- expect(J, TK_WHILE);
- expect(J, '(');
- b = expression(J, 0);
- expect(J, ')');
- semicolon(J);
- return STM2(DO, a, b);
- }
-
- if (accept(J, TK_WHILE)) {
- expect(J, '(');
- a = expression(J, 0);
- expect(J, ')');
- b = statement(J);
- return STM2(WHILE, a, b);
- }
-
- if (accept(J, TK_FOR)) {
- return forstatement(J);
- }
-
- if (accept(J, TK_CONTINUE)) {
- a = identifieropt(J);
- semicolon(J);
- return STM1(CONTINUE, a);
- }
-
- if (accept(J, TK_BREAK)) {
- a = identifieropt(J);
- semicolon(J);
- return STM1(BREAK, a);
- }
-
- if (accept(J, TK_RETURN)) {
- if (J->lookahead != ';' && J->lookahead != '}' && J->lookahead != 0)
- a = expression(J, 0);
- else
- a = NULL;
- semicolon(J);
- return STM1(RETURN, a);
- }
-
- if (accept(J, TK_WITH)) {
- expect(J, '(');
- a = expression(J, 0);
- expect(J, ')');
- b = statement(J);
- return STM2(WITH, a, b);
- }
-
- if (accept(J, TK_SWITCH)) {
- expect(J, '(');
- a = expression(J, 0);
- expect(J, ')');
- expect(J, '{');
- b = caselist(J);
- expect(J, '}');
- return STM2(SWITCH, a, b);
- }
-
- if (accept(J, TK_THROW)) {
- a = expression(J, 0);
- semicolon(J);
- return STM1(THROW, a);
- }
-
- if (accept(J, TK_TRY)) {
- a = block(J);
- b = c = d = NULL;
- if (accept(J, TK_CATCH)) {
- expect(J, '(');
- b = identifier(J);
- expect(J, ')');
- c = block(J);
- }
- if (accept(J, TK_FINALLY)) {
- d = block(J);
- }
- if (!b && !d)
- jsP_error(J, "unexpected token in try: %s (expected 'catch' or 'finally')", TOKSTR);
- return STM4(TRY, a, b, c, d);
- }
-
- if (accept(J, TK_DEBUGGER)) {
- semicolon(J);
- return STM0(DEBUGGER);
- }
-
- /* labelled statement or expression statement */
- if (J->lookahead == TK_IDENTIFIER) {
- a = expression(J, 0);
- if (a->type == AST_IDENTIFIER && accept(J, ':')) {
- b = statement(J);
- return STM2(LABEL, a, b);
- }
- semicolon(J);
- return a;
- }
-
- /* expression statement */
- if (J->lookahead != TK_FUNCTION) {
- a = expression(J, 0);
- semicolon(J);
- return a;
- }
-
- jsP_error(J, "unexpected token in statement: %s", TOKSTR);
- return NULL;
-}
-
-/* Program */
-
-static js_Ast *chunkelement(js_State *J)
-{
- js_Ast *a, *b, *c;
- if (accept(J, TK_FUNCTION)) {
- a = identifier(J);
- expect(J, '(');
- b = parameters(J);
- expect(J, ')');
- c = funcbody(J);
- return STM3(FUNC, a, b, c);
- }
- return statement(J);
-}
-
-static js_Ast *chunklist(js_State *J)
-{
- js_Ast *head, *tail;
- if (J->lookahead == '}' || J->lookahead == 0)
- return NULL;
- head = tail = LIST(chunkelement(J));
- while (J->lookahead != '}' && J->lookahead != 0)
- tail = tail->b = LIST(chunkelement(J));
- return head;
-}
-
-static js_Ast *funcbody(js_State *J)
-{
- js_Ast *a;
- expect(J, '{');
- a = chunklist(J);
- expect(J, '}');
- return a;
-}
-
-int jsP_error(js_State *J, const char *fmt, ...)
-{
- va_list ap;
-
- fprintf(stderr, "syntax error: %s:%d: ", J->filename, J->line);
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
-
- longjmp(J->jb, 1);
- return 0;
-}
-
-int jsP_parse(js_State *J, const char *filename, const char *source)
-{
- jsP_initlex(J, filename, source);
-
- if (setjmp(J->jb)) {
- jsP_freeast(J);
- return 1;
- }
-
- next(J);
- printblock(chunklist(J), 0);
- putchar('\n');
-
- // TODO: compile to bytecode
-
- jsP_freeast(J);
- return 0;
-}
--- a/js-parse.h
+++ /dev/null
@@ -1,68 +1,0 @@
-#ifndef js_parse_h
-#define js_parse_h
-
-enum {
- TK_IDENTIFIER = 256,
- TK_NUMBER,
- TK_STRING,
- TK_REGEXP,
-
- /* multi-character punctuators */
- TK_LE,
- TK_GE,
- TK_EQ,
- TK_NE,
- TK_EQ3,
- TK_NE3,
- TK_SHL,
- TK_SHR,
- TK_USHR,
- TK_AND,
- TK_OR,
- TK_ADD_ASS,
- TK_SUB_ASS,
- TK_MUL_ASS,
- TK_DIV_ASS,
- TK_MOD_ASS,
- TK_SHL_ASS,
- TK_SHR_ASS,
- TK_USHR_ASS,
- TK_AND_ASS,
- TK_OR_ASS,
- TK_XOR_ASS,
- TK_INC,
- TK_DEC,
-
- /* keywords */
- TK_BREAK,
- TK_CASE,
- TK_CATCH,
- TK_CONTINUE,
- TK_DEBUGGER,
- TK_DEFAULT,
- TK_DELETE,
- TK_DO,
- TK_ELSE,
- TK_FALSE,
- TK_FINALLY,
- TK_FOR,
- TK_FUNCTION,
- TK_IF,
- TK_IN,
- TK_INSTANCEOF,
- TK_NEW,
- TK_NULL,
- TK_RETURN,
- TK_SWITCH,
- TK_THIS,
- TK_THROW,
- TK_TRUE,
- TK_TRY,
- TK_TYPEOF,
- TK_VAR,
- TK_VOID,
- TK_WHILE,
- TK_WITH,
-};
-
-#endif
--- a/js-state.c
+++ /dev/null
@@ -1,29 +1,0 @@
-#include "js.h"
-
-js_State *js_newstate(void)
-{
- js_State *J = malloc(sizeof *J);
- memset(J, 0, sizeof(*J));
- return J;
-}
-
-void js_close(js_State *J)
-{
- free(J->buf.text);
- free(J);
-}
-
-int js_error(js_State *J, const char *fmt, ...)
-{
- va_list ap;
-
- fprintf(stderr, "error: ");
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- fprintf(stderr, "\n");
-
- return 0;
-}
--- a/js-string.c
+++ /dev/null
@@ -1,107 +1,0 @@
-#include "js.h"
-
-/* Use an AA-tree to quickly look up interned strings. */
-
-struct js_StringNode
-{
- const char *string;
- js_StringNode *left, *right;
- int level;
-};
-
-static js_StringNode sentinel = { "", &sentinel, &sentinel, 0 };
-
-static js_StringNode *makestringnode(const char *string, const char **out)
-{
- js_StringNode *node = malloc(sizeof(js_StringNode));
- node->string = *out = strdup(string);
- node->left = node->right = &sentinel;
- node->level = 1;
- return node;
-}
-
-static const char *lookup(js_StringNode *node, const char *string)
-{
- if (node && node != &sentinel) {
- int c = strcmp(string, node->string);
- if (c == 0)
- return node->string;
- else if (c < 0)
- return lookup(node->left, string);
- else
- return lookup(node->right, string);
- }
- return NULL;
-}
-
-static js_StringNode *skew(js_StringNode *node)
-{
- if (node->level != 0) {
- if (node->left->level == node->level) {
- js_StringNode *save = node;
- node = node->left;
- save->left = node->right;
- node->right = save;
- }
- node->right = skew(node->right);
- }
- return node;
-}
-
-static js_StringNode *split(js_StringNode *node)
-{
- if (node->level != 0 && node->right->right->level == node->level) {
- js_StringNode *save = node;
- node = node->right;
- save->right = node->left;
- node->left = save;
- node->level++;
- node->right = split(node->right);
- }
- return node;
-}
-
-static js_StringNode *insert(js_StringNode *node, const char *string, const char **out)
-{
- if (node && node != &sentinel) {
- int c = strcmp(string, node->string);
- if (c < 0)
- node->left = insert(node->left, string, out);
- else
- node->right = insert(node->right, string, out);
- node = skew(node);
- node = split(node);
- return node;
- } else {
- return makestringnode(string, out);
- }
-}
-
-static void printstringnode(js_StringNode *node, int level)
-{
- int i;
- if (node->left != &sentinel)
- printstringnode(node->left, level + 1);
- for (i = 0; i < level; i++)
- putchar(' ');
- printf("'%s' (%d)\n", node->string, node->level);
- if (node->right != &sentinel)
- printstringnode(node->right, level + 1);
-}
-
-void js_printstringtree(js_State *J)
-{
- js_StringNode *root = J->strings;
- printf("--- string dump ---\n");
- if (root && root != &sentinel)
- printstringnode(root, 0);
- printf("---\n");
-}
-
-const char *js_intern(js_State *J, const char *s)
-{
- const char *a = lookup(J->strings, s);
- if (!a)
- J->strings = insert(J->strings, s, &a);
- return a;
-}
--- a/js.h
+++ b/js.h
@@ -24,42 +24,6 @@
const char *js_intern(js_State *J, const char *s);
-/* private */
-
-typedef struct js_Ast js_Ast;
-
-void jsP_initlex(js_State *J, const char *filename, const char *source);
-int jsP_lex(js_State *J);
-const char *jsP_tokenstring(int token);
-int jsP_parse(js_State *J, const char *filename, const char *source);
-int jsP_error(js_State *J, const char *fmt, ...);
-
void js_printstringtree(js_State *J);
-
-struct js_State
-{
- jmp_buf jb; /* setjmp buffer for error handling in parser */
-
- js_StringNode *strings;
-
- /* input */
- const char *filename;
- const char *source;
- int line;
-
- /* lexer */
- struct { char *text; size_t len, cap; } buf;
- int lasttoken;
- int newline;
-
- /* parser */
- int lookahead;
- const char *text;
- double number;
- struct { char g, i, m; } flags;
- js_Ast *ast; /* list of allocated nodes to free after parsing */
-
- int strict;
-};
#endif
--- /dev/null
+++ b/jsintern.c
@@ -1,0 +1,108 @@
+#include "js.h"
+#include "jsstate.h"
+
+/* Use an AA-tree to quickly look up interned strings. */
+
+struct js_StringNode
+{
+ const char *string;
+ js_StringNode *left, *right;
+ int level;
+};
+
+static js_StringNode sentinel = { "", &sentinel, &sentinel, 0 };
+
+static js_StringNode *makestringnode(const char *string, const char **out)
+{
+ js_StringNode *node = malloc(sizeof(js_StringNode));
+ node->string = *out = strdup(string);
+ node->left = node->right = &sentinel;
+ node->level = 1;
+ return node;
+}
+
+static const char *lookup(js_StringNode *node, const char *string)
+{
+ if (node && node != &sentinel) {
+ int c = strcmp(string, node->string);
+ if (c == 0)
+ return node->string;
+ else if (c < 0)
+ return lookup(node->left, string);
+ else
+ return lookup(node->right, string);
+ }
+ return NULL;
+}
+
+static js_StringNode *skew(js_StringNode *node)
+{
+ if (node->level != 0) {
+ if (node->left->level == node->level) {
+ js_StringNode *save = node;
+ node = node->left;
+ save->left = node->right;
+ node->right = save;
+ }
+ node->right = skew(node->right);
+ }
+ return node;
+}
+
+static js_StringNode *split(js_StringNode *node)
+{
+ if (node->level != 0 && node->right->right->level == node->level) {
+ js_StringNode *save = node;
+ node = node->right;
+ save->right = node->left;
+ node->left = save;
+ node->level++;
+ node->right = split(node->right);
+ }
+ return node;
+}
+
+static js_StringNode *insert(js_StringNode *node, const char *string, const char **out)
+{
+ if (node && node != &sentinel) {
+ int c = strcmp(string, node->string);
+ if (c < 0)
+ node->left = insert(node->left, string, out);
+ else
+ node->right = insert(node->right, string, out);
+ node = skew(node);
+ node = split(node);
+ return node;
+ } else {
+ return makestringnode(string, out);
+ }
+}
+
+static void printstringnode(js_StringNode *node, int level)
+{
+ int i;
+ if (node->left != &sentinel)
+ printstringnode(node->left, level + 1);
+ for (i = 0; i < level; i++)
+ putchar(' ');
+ printf("'%s' (%d)\n", node->string, node->level);
+ if (node->right != &sentinel)
+ printstringnode(node->right, level + 1);
+}
+
+void js_printstringtree(js_State *J)
+{
+ js_StringNode *root = J->strings;
+ printf("--- string dump ---\n");
+ if (root && root != &sentinel)
+ printstringnode(root, 0);
+ printf("---\n");
+}
+
+const char *js_intern(js_State *J, const char *s)
+{
+ const char *a = lookup(J->strings, s);
+ if (!a)
+ J->strings = insert(J->strings, s, &a);
+ return a;
+}
--- /dev/null
+++ b/jslex.c
@@ -1,0 +1,609 @@
+#include "js.h"
+#include "jsstate.h"
+#include "jslex.h"
+
+#define nelem(a) (sizeof (a) / sizeof (a)[0])
+
+static const char *tokenstring[] = {
+ "(end-of-file)",
+ "'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'",
+ "'\\x08'", "'\\x09'", "'\\x0A'", "'\\x0B'", "'\\x0C'", "'\\x0D'", "'\\x0E'", "'\\x0F'",
+ "'\\x10'", "'\\x11'", "'\\x12'", "'\\x13'", "'\\x14'", "'\\x15'", "'\\x16'", "'\\x17'",
+ "'\\x18'", "'\\x19'", "'\\x1A'", "'\\x1B'", "'\\x1C'", "'\\x1D'", "'\\x1E'", "'\\x1F'",
+ "' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'\\''",
+ "'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'",
+ "'0'", "'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'",
+ "'8'", "'9'", "':'", "';'", "'<'", "'='", "'>'", "'?'",
+ "'@'", "'A'", "'B'", "'C'", "'D'", "'E'", "'F'", "'G'",
+ "'H'", "'I'", "'J'", "'K'", "'L'", "'M'", "'N'", "'O'",
+ "'P'", "'Q'", "'R'", "'S'", "'T'", "'U'", "'V'", "'W'",
+ "'X'", "'Y'", "'Z'", "'['", "'\'", "']'", "'^'", "'_'",
+ "'`'", "'a'", "'b'", "'c'", "'d'", "'e'", "'f'", "'g'",
+ "'h'", "'i'", "'j'", "'k'", "'l'", "'m'", "'n'", "'o'",
+ "'p'", "'q'", "'r'", "'s'", "'t'", "'u'", "'v'", "'w'",
+ "'x'", "'y'", "'z'", "'{'", "'|'", "'}'", "'~'", "'\\x7F'",
+
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+
+ "(identifier)", "(number)", "(string)", "(regexp)",
+
+ "'<='", "'>='", "'=='", "'!='", "'==='", "'!=='",
+ "'<<'", "'>>'", "'>>>'", "'&&'", "'||'",
+ "'+='", "'-='", "'*='", "'/='", "'%='",
+ "'<<='", "'>>='", "'>>>='", "'&='", "'|='", "'^='",
+ "'++'", "'--'",
+
+ "'break'", "'case'", "'catch'", "'continue'", "'debugger'",
+ "'default'", "'delete'", "'do'", "'else'", "'false'", "'finally'", "'for'",
+ "'function'", "'if'", "'in'", "'instanceof'", "'new'", "'null'", "'return'",
+ "'switch'", "'this'", "'throw'", "'true'", "'try'", "'typeof'", "'var'",
+ "'void'", "'while'", "'with'",
+};
+
+const char *jsP_tokenstring(int token)
+{
+ if (token >= 0 && token < nelem(tokenstring))
+ if (tokenstring[token])
+ return tokenstring[token];
+ return "<unknown>";
+}
+
+static const char *keywords[] = {
+ "break", "case", "catch", "continue", "debugger", "default", "delete",
+ "do", "else", "false", "finally", "for", "function", "if", "in",
+ "instanceof", "new", "null", "return", "switch", "this", "throw",
+ "true", "try", "typeof", "var", "void", "while", "with",
+};
+
+static const char *futurewords[] = {
+ "class", "const", "enum", "export", "extends", "import", "super",
+};
+
+static const char *strictfuturewords[] = {
+ "implements", "interface", "let", "package", "private", "protected",
+ "public", "static", "yield",
+};
+
+static inline int findword(const char *s, const char **list, int num)
+{
+ int l = 0;
+ int r = num - 1;
+ while (l <= r) {
+ int m = (l + r) >> 1;
+ int c = strcmp(s, list[m]);
+ if (c < 0)
+ r = m - 1;
+ else if (c > 0)
+ l = m + 1;
+ else
+ return m;
+ }
+ return -1;
+}
+
+static inline int findkeyword(js_State *J, const char *s)
+{
+ int i = findword(s, keywords, nelem(keywords));
+ if (i >= 0) {
+ J->text = keywords[i];
+ return TK_BREAK + i; /* first keyword + i */
+ }
+
+ if (findword(s, futurewords, nelem(futurewords)) >= 0)
+ return jsP_error(J, "'%s' is a future reserved word", s);
+ if (J->strict && findword(s, strictfuturewords, nelem(strictfuturewords)) >= 0)
+ return jsP_error(J, "'%s' is a strict mode future reserved word", s);
+
+ J->text = js_intern(J, s);
+ return TK_IDENTIFIER;
+}
+
+#define GET() (*(*sp)++)
+#define UNGET() ((*sp)--)
+#define PEEK() (**sp)
+#define NEXT() ((*sp)++)
+#define NEXTPEEK() (NEXT(), PEEK())
+#define LOOK(x) (PEEK() == x ? (NEXT(), 1) : 0)
+
+static void textinit(js_State *J)
+{
+ if (!J->buf.text) {
+ J->buf.cap = 4096;
+ J->buf.text = malloc(J->buf.cap);
+ }
+ J->buf.len = 0;
+}
+
+static inline void textpush(js_State *J, int c)
+{
+ if (J->buf.len >= J->buf.cap) {
+ J->buf.cap = J->buf.cap * 2;
+ J->buf.text = realloc(J->buf.text, J->buf.cap);
+ }
+ J->buf.text[J->buf.len++] = c;
+}
+
+static inline char *textend(js_State *J)
+{
+ textpush(J, 0);
+ return J->buf.text;
+}
+
+static inline int iswhite(int c)
+{
+ return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF;
+}
+
+static inline int isnewline(c)
+{
+ return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
+}
+
+static inline int isidentifierstart(int c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '$' || c == '_';
+}
+
+static inline int isidentifierpart(int c)
+{
+ return (c >= '0' && c <= '9') || isidentifierstart(c);
+}
+
+static inline int isdec(int c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+static inline int ishex(int c)
+{
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+
+static inline int tohex(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 0xA;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 0xA;
+ return 0;
+}
+
+static inline void lexlinecomment(const char **sp)
+{
+ int c = PEEK();
+ while (c && !isnewline(c)) {
+ c = NEXTPEEK();
+ }
+}
+
+static inline int lexcomment(const char **sp)
+{
+ while (1) {
+ int c = GET();
+ if (c == '*') {
+ while (c == '*')
+ c = GET();
+ if (c == '/')
+ return 0;
+ } else if (c == 0) {
+ return -1;
+ }
+ }
+}
+
+static inline double lexhex(const char **sp)
+{
+ double n = 0;
+ int c = PEEK();
+ while (ishex(c)) {
+ n = n * 16 + tohex(c);
+ c = NEXTPEEK();
+ }
+ return n;
+}
+
+static inline double lexinteger(const char **sp)
+{
+ double n = 0;
+ int c = PEEK();
+ while (isdec(c)) {
+ n = n * 10 + (c - '0');
+ c = NEXTPEEK();
+ }
+ return n;
+}
+
+static inline double lexfraction(const char **sp)
+{
+ double n = 0;
+ double d = 1;
+ int c = PEEK();
+ while (isdec(c)) {
+ n = n * 10 + (c - '0');
+ d = d * 10;
+ c = NEXTPEEK();
+ }
+ return n / d;
+}
+
+static inline double lexexponent(const char **sp)
+{
+ if (LOOK('e') || LOOK('E')) {
+ if (LOOK('-'))
+ return -lexinteger(sp);
+ else if (LOOK('+'))
+ return lexinteger(sp);
+ else
+ return lexinteger(sp);
+ }
+ return 0;
+}
+
+static inline int lexnumber(js_State *J, const char **sp)
+{
+ double n;
+
+ if ((*sp)[0] == '0' && ((*sp)[1] == 'x' || (*sp)[1] == 'X')) {
+ *sp += 2;
+ if (!ishex(PEEK()))
+ return jsP_error(J, "0x not followed by hexademical digit");
+ J->number = lexhex(sp);
+ return TK_NUMBER;
+ }
+
+ if ((*sp)[0] == '0' && isdec((*sp)[1]))
+ return jsP_error(J, "number with leading zero");
+
+ n = lexinteger(sp);
+ if (LOOK('.'))
+ n += lexfraction(sp);
+ n *= pow(10, lexexponent(sp));
+
+ if (isidentifierstart(PEEK()))
+ return jsP_error(J, "number with letter suffix");
+
+ J->number = n;
+ return TK_NUMBER;
+}
+
+static inline int lexescape(js_State *J, const char **sp)
+{
+ int c = GET();
+ int x = 0;
+
+ if (isnewline(c)) {
+ if (c == '\r' && PEEK() == '\n')
+ NEXT();
+ return 0;
+ }
+
+ switch (c) {
+ case 'u':
+ if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 12;
+ if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 8;
+ if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 4;
+ if (!ishex(PEEK())) return 1; else x |= NEXTPEEK();
+ textpush(J, x);
+ break;
+ case 'x':
+ if (!ishex(PEEK())) return 1; else x |= NEXTPEEK() << 4;
+ if (!ishex(PEEK())) return 1; else x |= NEXTPEEK();
+ textpush(J, x);
+ break;
+ case '0': textpush(J, 0); break;
+ case '\\': textpush(J, '\\'); break;
+ case '\'': textpush(J, '\''); break;
+ case '"': textpush(J, '"'); break;
+ case 'b': textpush(J, '\b'); break;
+ case 'f': textpush(J, '\f'); break;
+ case 'n': textpush(J, '\n'); break;
+ case 'r': textpush(J, '\r'); break;
+ case 't': textpush(J, '\t'); break;
+ case 'v': textpush(J, '\v'); break;
+ default: textpush(J, c); break;
+ }
+ return 0;
+}
+
+static inline int lexstring(js_State *J, const char **sp, int q)
+{
+ const char *s;
+ int c = GET();
+
+ textinit(J);
+
+ while (c != q) {
+ if (c == 0 || isnewline(c))
+ return jsP_error(J, "string not terminated");
+ if (c == '\\') {
+ if (lexescape(J, sp))
+ return jsP_error(J, "malformed escape sequence");
+ } else {
+ textpush(J, c);
+ }
+ c = GET();
+ }
+
+ s = textend(J);
+
+ J->text = js_intern(J, s);
+ return TK_STRING;
+}
+
+/* the ugliest language wart ever... */
+static int isregexpcontext(int last)
+{
+ switch (last) {
+ case ']':
+ case ')':
+ case TK_IDENTIFIER:
+ case TK_NUMBER:
+ case TK_STRING:
+ case TK_FALSE:
+ case TK_NULL:
+ case TK_THIS:
+ case TK_TRUE:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static int lexregexp(js_State *J, const char **sp)
+{
+ const char *s;
+ int c;
+
+ textinit(J);
+
+ /* regexp body */
+ c = GET();
+ while (c != '/') {
+ if (c == 0 || isnewline(c)) {
+ return jsP_error(J, "regular expression not terminated");
+ } else if (c == '\\') {
+ textpush(J, c);
+ c = GET();
+ if (c == 0 || isnewline(c))
+ return jsP_error(J, "regular expression not terminated");
+ textpush(J, c);
+ c = GET();
+ } else {
+ textpush(J, c);
+ c = GET();
+ }
+ }
+
+ s = textend(J);
+
+ /* regexp flags */
+ J->flags.g = J->flags.i = J->flags.m = 0;
+
+ c = PEEK();
+ while (isidentifierpart(c)) {
+ if (c == 'g') J->flags.g ++;
+ else if (c == 'i') J->flags.i ++;
+ else if (c == 'm') J->flags.m ++;
+ else return jsP_error(J, "illegal flag in regular expression: %c", c);
+ c = NEXTPEEK();
+ }
+
+ if (J->flags.g > 1 || J->flags.i > 1 || J->flags.m > 1)
+ return jsP_error(J, "duplicated flag in regular expression");
+
+ J->text = js_intern(J, s);
+ return TK_REGEXP;
+}
+
+/* simple "return [no Line Terminator here] ..." contexts */
+static inline int isnlthcontext(int last)
+{
+ switch (last) {
+ case TK_BREAK:
+ case TK_CONTINUE:
+ case TK_RETURN:
+ case TK_THROW:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int lex(js_State *J, const char **sp)
+{
+ J->newline = 0;
+
+ while (1) {
+ int c = GET();
+
+ while (iswhite(c))
+ c = GET();
+
+ if (isnewline(c)) {
+ /* consume CR LF as one unit */
+ if (c == '\r' && PEEK() == '\n')
+ NEXT();
+ J->line++;
+ J->newline = 1;
+ if (isnlthcontext(J->lasttoken))
+ return ';';
+ continue;
+ }
+
+ if (c == '/') {
+ if (LOOK('/')) {
+ lexlinecomment(sp);
+ continue;
+ } else if (LOOK('*')) {
+ if (lexcomment(sp))
+ return jsP_error(J, "multi-line comment not terminated");
+ continue;
+ } else if (isregexpcontext(J->lasttoken)) {
+ return lexregexp(J, sp);
+ } else if (LOOK('=')) {
+ return TK_DIV_ASS;
+ } else {
+ return '/';
+ }
+ }
+
+ if (isidentifierstart(c)) {
+ textinit(J);
+ textpush(J, c);
+
+ c = PEEK();
+ while (isidentifierpart(c)) {
+ textpush(J, c);
+ c = NEXTPEEK();
+ }
+
+ textend(J);
+
+ return findkeyword(J, J->buf.text);
+ }
+
+ if (c >= '0' && c <= '9') {
+ UNGET();
+ return lexnumber(J, sp);
+ }
+
+ switch (c) {
+ case '(':
+ case ')':
+ case ',':
+ case ':':
+ case ';':
+ case '?':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '~':
+ return c;
+
+ case '\'':
+ case '"':
+ return lexstring(J, sp, c);
+
+ case '.':
+ if (isdec(PEEK())) {
+ UNGET();
+ return lexnumber(J, sp);
+ }
+ return '.';
+
+ case '<':
+ if (LOOK('<')) {
+ if (LOOK('='))
+ return TK_SHL_ASS;
+ return TK_SHL;
+ }
+ if (LOOK('='))
+ return TK_LE;
+ return '<';
+
+ case '>':
+ if (LOOK('>')) {
+ if (LOOK('>')) {
+ if (LOOK('='))
+ return TK_USHR_ASS;
+ return TK_USHR;
+ }
+ if (LOOK('='))
+ return TK_SHR_ASS;
+ return TK_SHR;
+ }
+ if (LOOK('='))
+ return TK_GE;
+ return '>';
+
+ case '=':
+ if (LOOK('=')) {
+ if (LOOK('='))
+ return TK_EQ3;
+ return TK_EQ;
+ }
+ return '=';
+
+ case '!':
+ if (LOOK('=')) {
+ if (LOOK('='))
+ return TK_NE3;
+ return TK_NE;
+ }
+ return '!';
+
+ case '+':
+ if (LOOK('+'))
+ return TK_INC;
+ if (LOOK('='))
+ return TK_ADD_ASS;
+ return '+';
+
+ case '-':
+ if (LOOK('-'))
+ return TK_DEC;
+ if (LOOK('='))
+ return TK_SUB_ASS;
+ return '-';
+
+ case '*':
+ if (LOOK('='))
+ return TK_MUL_ASS;
+ return '*';
+
+ case '%':
+ if (LOOK('='))
+ return TK_MOD_ASS;
+ return '%';
+
+ case '&':
+ if (LOOK('&'))
+ return TK_AND;
+ if (LOOK('='))
+ return TK_AND_ASS;
+ return '&';
+
+ case '|':
+ if (LOOK('|'))
+ return TK_OR;
+ if (LOOK('='))
+ return TK_OR_ASS;
+ return '|';
+
+ case '^':
+ if (LOOK('='))
+ return TK_XOR_ASS;
+ return '^';
+
+ case 0:
+ return 0; /* EOF */
+ }
+
+ if (c >= 0x20 && c <= 0x7E)
+ return jsP_error(J, "unexpected character: '%c'", c);
+ return jsP_error(J, "unexpected character: \\u%04X", c);
+ }
+}
+
+void jsP_initlex(js_State *J, const char *filename, const char *source)
+{
+ J->filename = filename;
+ J->source = source;
+ J->line = 1;
+ J->lasttoken = 0;
+}
+
+int jsP_lex(js_State *J)
+{
+ return J->lasttoken = lex(J, &J->source);
+}
--- /dev/null
+++ b/jslex.h
@@ -1,0 +1,73 @@
+#ifndef js_lex_h
+#define js_lex_h
+
+enum {
+ TK_IDENTIFIER = 256,
+ TK_NUMBER,
+ TK_STRING,
+ TK_REGEXP,
+
+ /* multi-character punctuators */
+ TK_LE,
+ TK_GE,
+ TK_EQ,
+ TK_NE,
+ TK_EQ3,
+ TK_NE3,
+ TK_SHL,
+ TK_SHR,
+ TK_USHR,
+ TK_AND,
+ TK_OR,
+ TK_ADD_ASS,
+ TK_SUB_ASS,
+ TK_MUL_ASS,
+ TK_DIV_ASS,
+ TK_MOD_ASS,
+ TK_SHL_ASS,
+ TK_SHR_ASS,
+ TK_USHR_ASS,
+ TK_AND_ASS,
+ TK_OR_ASS,
+ TK_XOR_ASS,
+ TK_INC,
+ TK_DEC,
+
+ /* keywords */
+ TK_BREAK,
+ TK_CASE,
+ TK_CATCH,
+ TK_CONTINUE,
+ TK_DEBUGGER,
+ TK_DEFAULT,
+ TK_DELETE,
+ TK_DO,
+ TK_ELSE,
+ TK_FALSE,
+ TK_FINALLY,
+ TK_FOR,
+ TK_FUNCTION,
+ TK_IF,
+ TK_IN,
+ TK_INSTANCEOF,
+ TK_NEW,
+ TK_NULL,
+ TK_RETURN,
+ TK_SWITCH,
+ TK_THIS,
+ TK_THROW,
+ TK_TRUE,
+ TK_TRY,
+ TK_TYPEOF,
+ TK_VAR,
+ TK_VOID,
+ TK_WHILE,
+ TK_WITH,
+};
+
+void jsP_initlex(js_State *J, const char *filename, const char *source);
+int jsP_lex(js_State *J);
+const char *jsP_tokenstring(int token);
+int jsP_error(js_State *J, const char *fmt, ...);
+
+#endif
--- /dev/null
+++ b/jsload.c
@@ -1,0 +1,48 @@
+#include "js.h"
+#include "jsparse.h"
+
+static int jsP_loadstring(js_State *J, const char *filename, const char *source)
+{
+ return jsP_parse(J, filename, source);
+}
+
+int js_loadstring(js_State *J, const char *source)
+{
+ return jsP_loadstring(J, "(string)", source);
+}
+
+int js_loadfile(js_State *J, const char *filename)
+{
+ FILE *f;
+ char *s;
+ int n, t;
+
+ f = fopen(filename, "r");
+ if (!f)
+ return js_error(J, "cannot open file: '%s'", filename);
+
+ fseek(f, 0, SEEK_END);
+ n = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ s = malloc(n + 1); /* add space for string terminator */
+ if (!s) {
+ fclose(f);
+ return js_error(J, "cannot allocate storage for file contents: '%s'", filename);
+ }
+
+ t = fread(s, 1, n, f);
+ if (t != n) {
+ free(s);
+ fclose(f);
+ return js_error(J, "cannot read data from file: '%s'", filename);
+ }
+
+ s[n] = 0; /* zero-terminate string containing file data */
+
+ t = jsP_loadstring(J, filename, s);
+
+ free(s);
+ fclose(f);
+ return t;
+}
--- /dev/null
+++ b/jsparse.c
@@ -1,0 +1,761 @@
+#include "js.h"
+#include "jsstate.h"
+#include "jslex.h"
+#include "jsparse.h"
+
+#define LIST(h) jsP_newnode(J, AST_LIST, h, 0, 0, 0);
+
+#define EXP0(x) jsP_newnode(J, EXP_ ## x, 0, 0, 0, 0)
+#define EXP1(x,a) jsP_newnode(J, EXP_ ## x, a, 0, 0, 0)
+#define EXP2(x,a,b) jsP_newnode(J, EXP_ ## x, a, b, 0, 0)
+#define EXP3(x,a,b,c) jsP_newnode(J, EXP_ ## x, a, b, c, 0)
+
+#define STM0(x) jsP_newnode(J, STM_ ## x, 0, 0, 0, 0)
+#define STM1(x,a) jsP_newnode(J, STM_ ## x, a, 0, 0, 0)
+#define STM2(x,a,b) jsP_newnode(J, STM_ ## x, a, b, 0, 0)
+#define STM3(x,a,b,c) jsP_newnode(J, STM_ ## x, a, b, c, 0)
+#define STM4(x,a,b,c,d) jsP_newnode(J, STM_ ## x, a, b, c, d)
+
+#define TOKSTR jsP_tokenstring(J->lookahead)
+
+static js_Ast *expression(js_State *J, int notin);
+static js_Ast *assignment(js_State *J, int notin);
+static js_Ast *memberexp(js_State *J);
+static js_Ast *statement(js_State *J);
+static js_Ast *funcbody(js_State *J);
+
+static inline void next(js_State *J)
+{
+ J->lookahead = jsP_lex(J);
+}
+
+static inline int accept(js_State *J, int t)
+{
+ if (J->lookahead == t) {
+ next(J);
+ return 1;
+ }
+ return 0;
+}
+
+static inline void expect(js_State *J, int t)
+{
+ if (accept(J, t))
+ return;
+ jsP_error(J, "unexpected token: %s (expected %s)", TOKSTR, jsP_tokenstring(t));
+}
+
+static void semicolon(js_State *J)
+{
+ if (J->lookahead == ';') {
+ next(J);
+ return;
+ }
+ if (J->newline || J->lookahead == '}' || J->lookahead == 0)
+ return;
+ jsP_error(J, "unexpected token: %s (expected ';')", TOKSTR);
+}
+
+/* Literals */
+
+static js_Ast *identifier(js_State *J)
+{
+ if (J->lookahead == TK_IDENTIFIER) {
+ js_Ast *a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
+ next(J);
+ return a;
+ }
+ jsP_error(J, "unexpected token: %s (expected identifier)", TOKSTR);
+ return NULL;
+}
+
+static js_Ast *identifieropt(js_State *J)
+{
+ if (J->lookahead == TK_IDENTIFIER)
+ return identifier(J);
+ return NULL;
+}
+
+static js_Ast *identifiername(js_State *J)
+{
+ if (J->lookahead == TK_IDENTIFIER || J->lookahead >= TK_BREAK) {
+ js_Ast *a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
+ next(J);
+ return a;
+ }
+ jsP_error(J, "unexpected token: %s (expected identifier or keyword)", TOKSTR);
+ return NULL;
+}
+
+static js_Ast *arrayelement(js_State *J)
+{
+ if (J->lookahead == ',')
+ return EXP0(NULL); /* TODO: should be 'undefined' */
+ return assignment(J, 0);
+}
+
+static js_Ast *arrayliteral(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == ']')
+ return NULL;
+ head = tail = LIST(arrayelement(J));
+ while (accept(J, ',')) {
+ if (J->lookahead != ']')
+ tail = tail->b = LIST(arrayelement(J));
+ }
+ return head;
+}
+
+static js_Ast *propname(js_State *J)
+{
+ js_Ast *name;
+ if (J->lookahead == TK_NUMBER) {
+ name = jsP_newnumnode(J, AST_NUMBER, J->number);
+ next(J);
+ } else if (J->lookahead == TK_STRING) {
+ name = jsP_newstrnode(J, AST_STRING, J->text);
+ next(J);
+ } else {
+ name = identifiername(J);
+ }
+ return name;
+}
+
+static js_Ast *propassign(js_State *J)
+{
+ js_Ast *name, *value, *arg, *body;
+
+ if (J->lookahead == TK_IDENTIFIER && !strcmp(J->text, "get")) {
+ next(J);
+ name = propname(J);
+ expect(J, '(');
+ expect(J, ')');
+ body = funcbody(J);
+ return EXP2(PROP_GET, name, body);
+ }
+
+ if (J->lookahead == TK_IDENTIFIER && !strcmp(J->text, "set")) {
+ next(J);
+ name = propname(J);
+ expect(J, '(');
+ arg = identifier(J);
+ expect(J, ')');
+ body = funcbody(J);
+ return EXP3(PROP_SET, name, arg, body);
+ }
+
+ name = propname(J);
+ expect(J, ':');
+ value = assignment(J, 0);
+ return EXP2(PROP_VAL, name, value);
+}
+
+static js_Ast *objectliteral(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == '}')
+ return NULL;
+ head = tail = LIST(propassign(J));
+ while (accept(J, ',')) {
+ if (J->lookahead == '}')
+ break;
+ tail = tail->b = LIST(propassign(J));
+ }
+ return head;
+}
+
+/* Expressions */
+
+static js_Ast *primary(js_State *J)
+{
+ js_Ast *a;
+
+ if (J->lookahead == TK_IDENTIFIER) {
+ a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
+ next(J);
+ return a;
+ }
+ if (J->lookahead == TK_STRING) {
+ a = jsP_newstrnode(J, AST_STRING, J->text);
+ next(J);
+ return a;
+ }
+ if (J->lookahead == TK_REGEXP) {
+ a = jsP_newstrnode(J, AST_REGEXP, J->text);
+ // TODO: flags
+ next(J);
+ return a;
+ }
+ if (J->lookahead == TK_NUMBER) {
+ a = jsP_newnumnode(J, AST_NUMBER, J->number);
+ next(J);
+ return a;
+ }
+
+ if (accept(J, TK_THIS)) return EXP0(THIS);
+ if (accept(J, TK_NULL)) return EXP0(NULL);
+ if (accept(J, TK_TRUE)) return EXP0(TRUE);
+ if (accept(J, TK_FALSE)) return EXP0(FALSE);
+ if (accept(J, '{')) { a = EXP1(OBJECT, objectliteral(J)); expect(J, '}'); return a; }
+ if (accept(J, '[')) { a = EXP1(ARRAY, arrayliteral(J)); expect(J, ']'); return a; }
+ if (accept(J, '(')) { a = expression(J, 0); expect(J, ')'); return a; }
+
+ jsP_error(J, "unexpected token in expression: %s", TOKSTR);
+ return NULL;
+}
+
+static js_Ast *arguments(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == ')')
+ return NULL;
+ head = tail = LIST(assignment(J, 0));
+ while (accept(J, ',')) {
+ tail = tail->b = LIST(assignment(J, 0));
+ }
+ return head;
+}
+
+static js_Ast *parameters(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == ')')
+ return NULL;
+ head = tail = LIST(identifier(J));
+ while (accept(J, ',')) {
+ tail = tail->b = LIST(identifier(J));
+ }
+ return head;
+}
+
+static js_Ast *newexp(js_State *J)
+{
+ js_Ast *a, *b, *c;
+
+ if (accept(J, TK_NEW)) {
+ a = memberexp(J);
+ if (accept(J, '(')) {
+ b = arguments(J);
+ expect(J, ')');
+ return EXP2(NEW, a, b);
+ }
+ return EXP1(NEW, a);
+ }
+
+ if (accept(J, TK_FUNCTION)) {
+ a = identifieropt(J);
+ expect(J, '(');
+ b = parameters(J);
+ expect(J, ')');
+ c = funcbody(J);
+ return EXP3(FUNC, a, b, c);
+ }
+
+ return primary(J);
+}
+
+static js_Ast *memberexp(js_State *J)
+{
+ js_Ast *a = newexp(J);
+loop:
+ if (accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; }
+ if (accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); expect(J, ']'); goto loop; }
+ return a;
+}
+
+static js_Ast *callexp(js_State *J)
+{
+ js_Ast *a = newexp(J);
+loop:
+ if (accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; }
+ if (accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); expect(J, ']'); goto loop; }
+ if (accept(J, '(')) { a = EXP2(CALL, a, arguments(J)); expect(J, ')'); goto loop; }
+ return a;
+}
+
+static js_Ast *postfix(js_State *J)
+{
+ js_Ast *a = callexp(J);
+ if (!J->newline && accept(J, TK_INC)) return EXP1(POSTINC, a);
+ if (!J->newline && accept(J, TK_DEC)) return EXP1(POSTDEC, a);
+ return a;
+}
+
+static js_Ast *unary(js_State *J)
+{
+ if (accept(J, TK_DELETE)) return EXP1(DELETE, unary(J));
+ if (accept(J, TK_VOID)) return EXP1(VOID, unary(J));
+ if (accept(J, TK_TYPEOF)) return EXP1(TYPEOF, unary(J));
+ if (accept(J, TK_INC)) return EXP1(PREINC, unary(J));
+ if (accept(J, TK_DEC)) return EXP1(PREDEC, unary(J));
+ if (accept(J, '+')) return EXP1(POS, unary(J));
+ if (accept(J, '-')) return EXP1(NEG, unary(J));
+ if (accept(J, '~')) return EXP1(BITNOT, unary(J));
+ if (accept(J, '!')) return EXP1(LOGNOT, unary(J));
+ return postfix(J);
+}
+
+static js_Ast *multiplicative(js_State *J)
+{
+ js_Ast *a = unary(J);
+loop:
+ if (accept(J, '*')) { a = EXP2(MUL, a, unary(J)); goto loop; }
+ if (accept(J, '/')) { a = EXP2(DIV, a, unary(J)); goto loop; }
+ if (accept(J, '%')) { a = EXP2(MOD, a, unary(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *additive(js_State *J)
+{
+ js_Ast *a = multiplicative(J);
+loop:
+ if (accept(J, '+')) { a = EXP2(ADD, a, multiplicative(J)); goto loop; }
+ if (accept(J, '-')) { a = EXP2(SUB, a, multiplicative(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *shift(js_State *J)
+{
+ js_Ast *a = additive(J);
+loop:
+ if (accept(J, TK_SHL)) { a = EXP2(SHL, a, additive(J)); goto loop; }
+ if (accept(J, TK_SHR)) { a = EXP2(SHR, a, additive(J)); goto loop; }
+ if (accept(J, TK_USHR)) { a = EXP2(USHR, a, additive(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *relational(js_State *J, int notin)
+{
+ js_Ast *a = shift(J);
+loop:
+ if (accept(J, '<')) { a = EXP2(LT, a, shift(J)); goto loop; }
+ if (accept(J, '>')) { a = EXP2(GT, a, shift(J)); goto loop; }
+ if (accept(J, TK_LE)) { a = EXP2(LE, a, shift(J)); goto loop; }
+ if (accept(J, TK_GE)) { a = EXP2(GE, a, shift(J)); goto loop; }
+ if (accept(J, TK_INSTANCEOF)) { a = EXP2(INSTANCEOF, a, shift(J)); goto loop; }
+ if (!notin && accept(J, TK_IN)) { a = EXP2(IN, a, shift(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *equality(js_State *J, int notin)
+{
+ js_Ast *a = relational(J, notin);
+loop:
+ if (accept(J, TK_EQ)) { a = EXP2(EQ, a, relational(J, notin)); goto loop; }
+ if (accept(J, TK_NE)) { a = EXP2(NE, a, relational(J, notin)); goto loop; }
+ if (accept(J, TK_EQ3)) { a = EXP2(EQ3, a, relational(J, notin)); goto loop; }
+ if (accept(J, TK_NE3)) { a = EXP2(NE3, a, relational(J, notin)); goto loop; }
+ return a;
+}
+
+static js_Ast *bitand(js_State *J, int notin)
+{
+ js_Ast *a = equality(J, notin);
+ while (accept(J, '&'))
+ a = EXP2(BITAND, a, equality(J, notin));
+ return a;
+}
+
+static js_Ast *bitxor(js_State *J, int notin)
+{
+ js_Ast *a = bitand(J, notin);
+ while (accept(J, '^'))
+ a = EXP2(BITXOR, a, bitand(J, notin));
+ return a;
+}
+
+static js_Ast *bitor(js_State *J, int notin)
+{
+ js_Ast *a = bitxor(J, notin);
+ while (accept(J, '|'))
+ a = EXP2(BITOR, a, bitxor(J, notin));
+ return a;
+}
+
+static js_Ast *logand(js_State *J, int notin)
+{
+ js_Ast *a = bitor(J, notin);
+ while (accept(J, TK_AND))
+ a = EXP2(LOGAND, a, bitor(J, notin));
+ return a;
+}
+
+static js_Ast *logor(js_State *J, int notin)
+{
+ js_Ast *a = logand(J, notin);
+ while (accept(J, TK_OR))
+ a = EXP2(LOGOR, a, logand(J, notin));
+ return a;
+}
+
+static js_Ast *conditional(js_State *J, int notin)
+{
+ js_Ast *a, *b, *c;
+ a = logor(J, notin);
+ if (accept(J, '?')) {
+ b = assignment(J, notin);
+ expect(J, ':');
+ c = assignment(J, notin);
+ return EXP3(COND, a, b, c);
+ }
+ return a;
+}
+
+static js_Ast *assignment(js_State *J, int notin)
+{
+ js_Ast *a = conditional(J, notin);
+ if (accept(J, '=')) return EXP2(ASS, a, assignment(J, notin));
+ if (accept(J, TK_MUL_ASS)) return EXP2(ASS_MUL, a, assignment(J, notin));
+ if (accept(J, TK_DIV_ASS)) return EXP2(ASS_DIV, a, assignment(J, notin));
+ if (accept(J, TK_MOD_ASS)) return EXP2(ASS_MOD, a, assignment(J, notin));
+ if (accept(J, TK_ADD_ASS)) return EXP2(ASS_ADD, a, assignment(J, notin));
+ if (accept(J, TK_SUB_ASS)) return EXP2(ASS_SUB, a, assignment(J, notin));
+ if (accept(J, TK_SHL_ASS)) return EXP2(ASS_SHL, a, assignment(J, notin));
+ if (accept(J, TK_SHR_ASS)) return EXP2(ASS_SHR, a, assignment(J, notin));
+ if (accept(J, TK_USHR_ASS)) return EXP2(ASS_USHR, a, assignment(J, notin));
+ if (accept(J, TK_AND_ASS)) return EXP2(ASS_BITAND, a, assignment(J, notin));
+ if (accept(J, TK_XOR_ASS)) return EXP2(ASS_BITXOR, a, assignment(J, notin));
+ if (accept(J, TK_OR_ASS)) return EXP2(ASS_BITOR, a, assignment(J, notin));
+ return a;
+}
+
+static js_Ast *expression(js_State *J, int notin)
+{
+ js_Ast *a = assignment(J, notin);
+ while (accept(J, ','))
+ a = EXP2(COMMA, a, assignment(J, notin));
+ return a;
+}
+
+/* Statements */
+
+static js_Ast *vardec(js_State *J, int notin)
+{
+ js_Ast *a = identifier(J);
+ if (accept(J, '='))
+ return EXP2(VAR, a, assignment(J, notin));
+ return EXP1(VAR, a);
+}
+
+static js_Ast *vardeclist(js_State *J, int notin)
+{
+ js_Ast *head, *tail;
+ head = tail = LIST(vardec(J, notin));
+ while (accept(J, ','))
+ tail = tail->b = LIST(vardec(J, notin));
+ return head;
+}
+
+static js_Ast *statementlist(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == '}' || J->lookahead == TK_CASE || J->lookahead == TK_DEFAULT)
+ return NULL;
+ head = tail = LIST(statement(J));
+ while (J->lookahead != '}' && J->lookahead != TK_CASE && J->lookahead != TK_DEFAULT)
+ tail = tail->b = LIST(statement(J));
+ return head;
+}
+
+static js_Ast *caseclause(js_State *J)
+{
+ js_Ast *a, *b;
+
+ if (accept(J, TK_CASE)) {
+ a = expression(J, 0);
+ expect(J, ':');
+ b = statementlist(J);
+ return STM2(CASE, a, b);
+ }
+
+ if (accept(J, TK_DEFAULT)) {
+ expect(J, ':');
+ a = statementlist(J);
+ return STM1(DEFAULT, a);
+ }
+
+ jsP_error(J, "unexpected token in switch: %s (expected 'case' or 'default')", TOKSTR);
+ return NULL;
+}
+
+static js_Ast *caselist(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == '}')
+ return NULL;
+ head = tail = LIST(caseclause(J));
+ while (J->lookahead != '}')
+ tail = tail->b = LIST(caseclause(J));
+ return head;
+}
+
+static js_Ast *block(js_State *J)
+{
+ js_Ast *a;
+ expect(J, '{');
+ a = statementlist(J);
+ expect(J, '}');
+ return STM1(BLOCK, a);
+}
+
+static js_Ast *forexpression(js_State *J, int end)
+{
+ js_Ast *a = NULL;
+ if (J->lookahead != end)
+ a = expression(J, 0);
+ expect(J, end);
+ return a;
+}
+
+static js_Ast *forstatement(js_State *J)
+{
+ js_Ast *a, *b, *c, *d;
+ expect(J, '(');
+ if (accept(J, TK_VAR)) {
+ a = vardeclist(J, 1);
+ if (accept(J, ';')) {
+ b = forexpression(J, ';');
+ c = forexpression(J, ')');
+ d = statement(J);
+ return STM4(FOR_VAR, a, b, c, d);
+ }
+ if (accept(J, TK_IN)) {
+ b = expression(J, 0);
+ expect(J, ')');
+ c = statement(J);
+ return STM3(FOR_IN_VAR, a, b, c);
+ }
+ jsP_error(J, "unexpected token in for-var-statement: %s", TOKSTR);
+ return NULL;
+ }
+
+ if (J->lookahead != ';') {
+ a = expression(J, 1);
+ }
+ if (accept(J, ';')) {
+ b = forexpression(J, ';');
+ c = forexpression(J, ')');
+ d = statement(J);
+ return STM4(FOR, a, b, c, d);
+ }
+ if (accept(J, TK_IN)) {
+ b = expression(J, 0);
+ expect(J, ')');
+ c = statement(J);
+ return STM3(FOR_IN, a, b, c);
+ }
+ jsP_error(J, "unexpected token in for-statement: %s", TOKSTR);
+ return NULL;
+}
+
+static js_Ast *statement(js_State *J)
+{
+ js_Ast *a, *b, *c, *d;
+
+ if (J->lookahead == '{') {
+ return block(J);
+ }
+
+ if (accept(J, TK_VAR)) {
+ a = vardeclist(J, 0);
+ semicolon(J);
+ return STM1(VAR, a);
+ }
+
+ /* empty statement */
+ if (accept(J, ';')) {
+ return STM0(NOP);
+ }
+
+ if (accept(J, TK_IF)) {
+ expect(J, '(');
+ a = expression(J, 0);
+ expect(J, ')');
+ b = statement(J);
+ if (accept(J, TK_ELSE))
+ c = statement(J);
+ else
+ c = NULL;
+ return STM3(IF, a, b, c);
+ }
+
+ if (accept(J, TK_DO)) {
+ a = statement(J);
+ expect(J, TK_WHILE);
+ expect(J, '(');
+ b = expression(J, 0);
+ expect(J, ')');
+ semicolon(J);
+ return STM2(DO, a, b);
+ }
+
+ if (accept(J, TK_WHILE)) {
+ expect(J, '(');
+ a = expression(J, 0);
+ expect(J, ')');
+ b = statement(J);
+ return STM2(WHILE, a, b);
+ }
+
+ if (accept(J, TK_FOR)) {
+ return forstatement(J);
+ }
+
+ if (accept(J, TK_CONTINUE)) {
+ a = identifieropt(J);
+ semicolon(J);
+ return STM1(CONTINUE, a);
+ }
+
+ if (accept(J, TK_BREAK)) {
+ a = identifieropt(J);
+ semicolon(J);
+ return STM1(BREAK, a);
+ }
+
+ if (accept(J, TK_RETURN)) {
+ if (J->lookahead != ';' && J->lookahead != '}' && J->lookahead != 0)
+ a = expression(J, 0);
+ else
+ a = NULL;
+ semicolon(J);
+ return STM1(RETURN, a);
+ }
+
+ if (accept(J, TK_WITH)) {
+ expect(J, '(');
+ a = expression(J, 0);
+ expect(J, ')');
+ b = statement(J);
+ return STM2(WITH, a, b);
+ }
+
+ if (accept(J, TK_SWITCH)) {
+ expect(J, '(');
+ a = expression(J, 0);
+ expect(J, ')');
+ expect(J, '{');
+ b = caselist(J);
+ expect(J, '}');
+ return STM2(SWITCH, a, b);
+ }
+
+ if (accept(J, TK_THROW)) {
+ a = expression(J, 0);
+ semicolon(J);
+ return STM1(THROW, a);
+ }
+
+ if (accept(J, TK_TRY)) {
+ a = block(J);
+ b = c = d = NULL;
+ if (accept(J, TK_CATCH)) {
+ expect(J, '(');
+ b = identifier(J);
+ expect(J, ')');
+ c = block(J);
+ }
+ if (accept(J, TK_FINALLY)) {
+ d = block(J);
+ }
+ if (!b && !d)
+ jsP_error(J, "unexpected token in try: %s (expected 'catch' or 'finally')", TOKSTR);
+ return STM4(TRY, a, b, c, d);
+ }
+
+ if (accept(J, TK_DEBUGGER)) {
+ semicolon(J);
+ return STM0(DEBUGGER);
+ }
+
+ /* labelled statement or expression statement */
+ if (J->lookahead == TK_IDENTIFIER) {
+ a = expression(J, 0);
+ if (a->type == AST_IDENTIFIER && accept(J, ':')) {
+ b = statement(J);
+ return STM2(LABEL, a, b);
+ }
+ semicolon(J);
+ return a;
+ }
+
+ /* expression statement */
+ if (J->lookahead != TK_FUNCTION) {
+ a = expression(J, 0);
+ semicolon(J);
+ return a;
+ }
+
+ jsP_error(J, "unexpected token in statement: %s", TOKSTR);
+ return NULL;
+}
+
+/* Program */
+
+static js_Ast *chunkelement(js_State *J)
+{
+ js_Ast *a, *b, *c;
+ if (accept(J, TK_FUNCTION)) {
+ a = identifier(J);
+ expect(J, '(');
+ b = parameters(J);
+ expect(J, ')');
+ c = funcbody(J);
+ return STM3(FUNC, a, b, c);
+ }
+ return statement(J);
+}
+
+static js_Ast *chunklist(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == '}' || J->lookahead == 0)
+ return NULL;
+ head = tail = LIST(chunkelement(J));
+ while (J->lookahead != '}' && J->lookahead != 0)
+ tail = tail->b = LIST(chunkelement(J));
+ return head;
+}
+
+static js_Ast *funcbody(js_State *J)
+{
+ js_Ast *a;
+ expect(J, '{');
+ a = chunklist(J);
+ expect(J, '}');
+ return a;
+}
+
+int jsP_error(js_State *J, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "syntax error: %s:%d: ", J->filename, J->line);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+
+ longjmp(J->jb, 1);
+ return 0;
+}
+
+int jsP_parse(js_State *J, const char *filename, const char *source)
+{
+ jsP_initlex(J, filename, source);
+
+ if (setjmp(J->jb)) {
+ jsP_freeast(J);
+ return 1;
+ }
+
+ next(J);
+ printblock(chunklist(J), 0);
+ putchar('\n');
+
+ // TODO: compile to bytecode
+
+ jsP_freeast(J);
+ return 0;
+}
--- /dev/null
+++ b/jsparse.h
@@ -1,0 +1,134 @@
+#ifndef js_parse_h
+#define js_parse_h
+
+typedef struct js_Ast js_Ast;
+
+struct js_Ast
+{
+ int type;
+ int line;
+ js_Ast *a, *b, *c, *d;
+ double n;
+ const char *s;
+ js_Ast *next; /* next in alloc list */
+};
+
+enum
+{
+ AST_LIST,
+
+ AST_IDENTIFIER,
+ AST_NUMBER,
+ AST_STRING,
+ AST_REGEXP,
+
+ /* literals */
+ EXP_NULL,
+ EXP_TRUE,
+ EXP_FALSE,
+ EXP_THIS,
+
+ EXP_ARRAY,
+ EXP_OBJECT,
+ EXP_PROP_VAL,
+ EXP_PROP_GET,
+ EXP_PROP_SET,
+
+ /* expressions */
+ EXP_INDEX,
+ EXP_MEMBER,
+ EXP_CALL,
+ EXP_NEW,
+ EXP_FUNC, /* function expression */
+
+ EXP_DELETE,
+ EXP_VOID,
+ EXP_TYPEOF,
+ EXP_PREINC,
+ EXP_PREDEC,
+ EXP_POSTINC,
+ EXP_POSTDEC,
+ EXP_POS,
+ EXP_NEG,
+ EXP_BITNOT,
+ EXP_LOGNOT,
+ EXP_LOGOR,
+
+ EXP_LOGAND,
+ EXP_BITOR,
+ EXP_BITXOR,
+ EXP_BITAND,
+ EXP_EQ,
+ EXP_NE,
+ EXP_EQ3,
+ EXP_NE3,
+ EXP_LT,
+ EXP_GT,
+ EXP_LE,
+ EXP_GE,
+ EXP_INSTANCEOF,
+ EXP_IN,
+ EXP_SHL,
+ EXP_SHR,
+ EXP_USHR,
+ EXP_ADD,
+ EXP_SUB,
+ EXP_MUL,
+ EXP_DIV,
+ EXP_MOD,
+
+ EXP_COND,
+
+ EXP_ASS,
+ EXP_ASS_MUL,
+ EXP_ASS_DIV,
+ EXP_ASS_MOD,
+ EXP_ASS_ADD,
+ EXP_ASS_SUB,
+ EXP_ASS_SHL,
+ EXP_ASS_SHR,
+ EXP_ASS_USHR,
+ EXP_ASS_BITAND,
+ EXP_ASS_BITXOR,
+ EXP_ASS_BITOR,
+
+ EXP_COMMA,
+
+ EXP_VAR, /* var initializer */
+
+ /* statements */
+ STM_BLOCK,
+ STM_FUNC, /* function declaration */
+ STM_NOP,
+ STM_VAR,
+ STM_IF,
+ STM_DO,
+ STM_WHILE,
+ STM_FOR,
+ STM_FOR_VAR,
+ STM_FOR_IN,
+ STM_FOR_IN_VAR,
+ STM_CONTINUE,
+ STM_BREAK,
+ STM_RETURN,
+ STM_WITH,
+ STM_SWITCH,
+ STM_THROW,
+ STM_TRY,
+ STM_LABEL,
+ STM_CASE,
+ STM_DEFAULT,
+ STM_DEBUGGER,
+};
+
+int jsP_parse(js_State *J, const char *filename, const char *source);
+
+js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast *c, js_Ast *d);
+js_Ast *jsP_newstrnode(js_State *J, int type, const char *s);
+js_Ast *jsP_newnumnode(js_State *J, int type, double n);
+void jsP_freeast(js_State *J);
+
+void printast(js_Ast *n, int level);
+void printblock(js_Ast *n, int level);
+
+#endif
--- /dev/null
+++ b/jspretty.c
@@ -1,0 +1,555 @@
+#include "js.h"
+#include "jsparse.h"
+#include "jsstate.h"
+
+js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast *c, js_Ast *d)
+{
+ js_Ast *node = malloc(sizeof(js_Ast));
+
+ node->type = type;
+ node->line = J->line;
+ node->a = a;
+ node->b = b;
+ node->c = c;
+ node->d = d;
+ node->n = 0;
+ node->s = NULL;
+
+ node->next = J->ast;
+ J->ast = node;
+
+ return node;
+}
+
+js_Ast *jsP_newstrnode(js_State *J, int type, const char *s)
+{
+ js_Ast *node = jsP_newnode(J, type, 0, 0, 0, 0);
+ node->s = s;
+ return node;
+}
+
+js_Ast *jsP_newnumnode(js_State *J, int type, double n)
+{
+ js_Ast *node = jsP_newnode(J, type, 0, 0, 0, 0);
+ node->n = n;
+ return node;
+}
+
+void jsP_freeast(js_State *J)
+{
+ js_Ast *node = J->ast;
+ while (node) {
+ js_Ast *next = node->next;
+ free(node);
+ node = next;
+ }
+ J->ast = NULL;
+}
+
+static const char *strast(int type)
+{
+ switch (type) {
+ case AST_LIST: return "LIST";
+ case AST_IDENTIFIER: return "IDENTIFIER";
+ case AST_NUMBER: return "NUMBER";
+ case AST_STRING: return "STRING";
+ case AST_REGEXP: return "REGEXP";
+ case EXP_VAR: return "VAR";
+ case EXP_NULL: return "NULL";
+ case EXP_TRUE: return "TRUE";
+ case EXP_FALSE: return "FALSE";
+ case EXP_THIS: return "THIS";
+ case EXP_ARRAY: return "ARRAY";
+ case EXP_OBJECT: return "OBJECT";
+ case EXP_PROP_VAL: return "PROP_VAL";
+ case EXP_PROP_GET: return "PROP_GET";
+ case EXP_PROP_SET: return "PROP_SET";
+ case EXP_INDEX: return "INDEX";
+ case EXP_MEMBER: return "MEMBER";
+ case EXP_NEW: return "new";
+ case EXP_CALL: return "CALL";
+ case EXP_FUNC: return "function";
+ case EXP_COND: return "?:";
+ case EXP_COMMA: return ",";
+ case EXP_DELETE: return "delete";
+ case EXP_VOID: return "void";
+ case EXP_TYPEOF: return "typeof";
+ case EXP_PREINC: return "PRE++";
+ case EXP_PREDEC: return "PRE--";
+ case EXP_POSTINC: return "POST++";
+ case EXP_POSTDEC: return "POST--";
+ case EXP_POS: return "+";
+ case EXP_NEG: return "-";
+ case EXP_BITNOT: return "~";
+ case EXP_LOGNOT: return "!";
+ case EXP_LOGOR: return "||";
+ case EXP_LOGAND: return "&&";
+ case EXP_BITOR: return "|";
+ case EXP_BITXOR: return "^";
+ case EXP_BITAND: return "&";
+ case EXP_EQ: return "==";
+ case EXP_NE: return "!=";
+ case EXP_EQ3: return "===";
+ case EXP_NE3: return "!==";
+ case EXP_LT: return "<";
+ case EXP_GT: return ">";
+ case EXP_LE: return "<=";
+ case EXP_GE: return ">=";
+ case EXP_INSTANCEOF: return "instanceof";
+ case EXP_IN: return "in";
+ case EXP_SHL: return "<<";
+ case EXP_SHR: return ">>";
+ case EXP_USHR: return ">>>";
+ case EXP_ADD: return "+";
+ case EXP_SUB: return "-";
+ case EXP_MUL: return "*";
+ case EXP_DIV: return "/";
+ case EXP_MOD: return "%";
+ case EXP_ASS: return "=";
+ case EXP_ASS_MUL: return "*=";
+ case EXP_ASS_DIV: return "/=";
+ case EXP_ASS_MOD: return "%=";
+ case EXP_ASS_ADD: return "+=";
+ case EXP_ASS_SUB: return "-=";
+ case EXP_ASS_SHL: return "<<=";
+ case EXP_ASS_SHR: return ">>=";
+ case EXP_ASS_USHR: return ">>>=";
+ case EXP_ASS_BITAND: return "&=";
+ case EXP_ASS_BITXOR: return "^=";
+ case EXP_ASS_BITOR: return "|=";
+ case STM_BLOCK: return "BLOCK";
+ case STM_FUNC: return "function-decl";
+ case STM_NOP: return "NOP";
+ case STM_VAR: return "var";
+ case STM_IF: return "if";
+ case STM_DO: return "do-while";
+ case STM_WHILE: return "while";
+ case STM_FOR: return "for";
+ case STM_FOR_VAR: return "for_var";
+ case STM_FOR_IN: return "for_in";
+ case STM_FOR_IN_VAR: return "for_in_var";
+ case STM_CONTINUE: return "continue";
+ case STM_BREAK: return "break";
+ case STM_RETURN: return "return";
+ case STM_WITH: return "with";
+ case STM_SWITCH: return "switch";
+ case STM_THROW: return "throw";
+ case STM_TRY: return "try";
+ case STM_LABEL: return "label";
+ case STM_CASE: return "case";
+ case STM_DEFAULT: return "default";
+ case STM_DEBUGGER: return "debugger";
+ default: return "(unknown)";
+ }
+}
+
+static void indent(int level)
+{
+ while (level--)
+ putchar('\t');
+}
+
+static void printlist(js_Ast *n, int level, const char *sep)
+{
+ while (n) {
+ printast(n->a, level);
+ n = n->b;
+ if (n)
+ fputs(sep, stdout);
+ }
+}
+
+void printblock(js_Ast *n, int level)
+{
+ while (n) {
+ indent(level);
+ printast(n->a, level);
+ if (n->a->type < STM_BLOCK) // expression
+ putchar(';');
+ n = n->b;
+ if (n)
+ putchar('\n');
+ }
+}
+
+static void printstm(js_Ast *n, int level)
+{
+ if (n->type == STM_BLOCK) {
+ printf(" {\n");
+ printblock(n->a, level + 1);
+ putchar('\n');
+ indent(level);
+ printf("}");
+ } else {
+ putchar('\n');
+ indent(level + 1);
+ printast(n, level + 1);
+ if (n->type < STM_BLOCK) // expression
+ putchar(';');
+ }
+}
+
+static void printunary(int level, js_Ast *n, const char *pre, const char *suf)
+{
+ printf(pre);
+ printast(n, level);
+ printf(suf);
+}
+
+static void printbinary(int level, js_Ast *a, js_Ast *b, const char *op)
+{
+ printf("(");
+ printast(a, level);
+ printf(" %s ", op);
+ printast(b, level);
+ printf(")");
+}
+
+void printast(js_Ast *n, int level)
+{
+ switch (n->type) {
+ case AST_IDENTIFIER: printf("%s", n->s); return;
+ case AST_NUMBER: printf("%g", n->n); return;
+ case AST_STRING: printf("'%s'", n->s); return;
+ case AST_REGEXP: printf("/%s/", n->s); return;
+ case AST_LIST:
+ putchar('[');
+ printlist(n, level, " ");
+ putchar(']');
+ break;
+
+ case STM_BLOCK:
+ putchar('{');
+ putchar('\n');
+ printblock(n->a, level + 1);
+ putchar('\n');
+ indent(level);
+ putchar('}');
+ break;
+
+ case STM_FOR:
+ printf("for (");
+ printast(n->a, level); printf("; ");
+ printast(n->b, level); printf("; ");
+ printast(n->c, level); printf(")");
+ printstm(n->d, level);
+ break;
+ case STM_FOR_VAR:
+ printf("for (var ");
+ printlist(n->a, level, ", "); printf("; ");
+ printast(n->b, level); printf("; ");
+ printast(n->c, level); printf(")");
+ printstm(n->d, level);
+ break;
+ case STM_FOR_IN:
+ printf("for (");
+ printast(n->a, level); printf(" in ");
+ printast(n->b, level); printf(")");
+ printstm(n->c, level);
+ break;
+ case STM_FOR_IN_VAR:
+ printf("for (var ");
+ printlist(n->a, level, ", "); printf(" in ");
+ printast(n->b, level); printf(")");
+ printstm(n->c, level);
+ break;
+
+ case STM_NOP:
+ putchar(';');
+ break;
+
+ case STM_VAR:
+ printf("var ");
+ printlist(n->a, level, ", ");
+ putchar(';');
+ break;
+
+ case EXP_VAR:
+ printast(n->a, level);
+ if (n->b) {
+ printf(" = ");
+ printast(n->b, level);
+ }
+ break;
+
+ case STM_IF:
+ printf("if (");
+ printast(n->a, level);
+ printf(")");
+ printstm(n->b, level);
+ if (n->c) {
+ putchar('\n');
+ indent(level);
+ printf("else");
+ printstm(n->c, level);
+ }
+ break;
+
+ case STM_DO:
+ printf("do");
+ printstm(n->a, level);
+ if (n->a->type == STM_BLOCK) {
+ putchar(' ');
+ } else {
+ putchar('\n');
+ indent(level);
+ }
+ printf("while (");
+ printast(n->b, level);
+ printf(");");
+ break;
+
+ case STM_WHILE:
+ printf("while (");
+ printast(n->a, level);
+ printf(")");
+ printstm(n->b, level);
+ break;
+
+ case STM_CONTINUE:
+ if (n->a) {
+ printf("continue ");
+ printast(n->a, level);
+ printf(";");
+ } else {
+ printf("continue;");
+ }
+ break;
+
+ case STM_BREAK:
+ if (n->a) {
+ printf("break ");
+ printast(n->a, level);
+ printf(";");
+ } else {
+ printf("break;");
+ }
+ break;
+
+ case STM_RETURN:
+ if (n->a) {
+ printf("return ");
+ printast(n->a, level);
+ printf(";");
+ } else {
+ printf("return;");
+ }
+ break;
+
+ case STM_THROW:
+ printf("throw ");
+ printast(n->a, level);
+ printf(";");
+ break;
+
+ case STM_SWITCH:
+ printf("switch (");
+ printast(n->a, level);
+ printf(") {\n");
+ printblock(n->b, level);
+ putchar('\n');
+ indent(level);
+ printf("}");
+ break;
+
+ case STM_CASE:
+ printf("case ");
+ printast(n->a, level);
+ printf(":");
+ if (n->b) {
+ printf("\n");
+ printblock(n->b, level + 1);
+ }
+ break;
+
+ case STM_DEFAULT:
+ printf("default:");
+ if (n->a) {
+ printf("\n");
+ printblock(n->a, level + 1);
+ }
+ break;
+
+ case STM_LABEL:
+ printast(n->a, level);
+ printf(":");
+ printstm(n->b, level - 1);
+ break;
+
+ case STM_WITH:
+ printf("with (");
+ printast(n->a, level);
+ printf(")");
+ printstm(n->b, level);
+ break;
+
+ case STM_TRY:
+ printf("try");
+ printstm(n->a, level);
+ if (n->b && n->c) {
+ printf(" catch (");
+ printast(n->b, level);
+ printf(")");
+ printstm(n->c, level);
+ }
+ if (n->d) {
+ printf(" finally");
+ printstm(n->d, level);
+ }
+ break;
+
+ case STM_DEBUGGER:
+ printf("debugger");
+ break;
+
+ case STM_FUNC:
+ printf("function ");
+ printast(n->a, level);
+ printf("(");
+ printlist(n->b, level, ", ");
+ printf(")\n");
+ indent(level);
+ printf("{\n");
+ printblock(n->c, level + 1);
+ printf("\n");
+ indent(level);
+ printf("}");
+ break;
+
+ case EXP_FUNC:
+ printf("(function ");
+ if (n->a)
+ printast(n->a, level);
+ printf("(");
+ printlist(n->b, level, ", ");
+ printf(") {\n");
+ printblock(n->c, level + 1);
+ printf("\n");
+ indent(level);
+ printf("})");
+ break;
+
+ case EXP_OBJECT:
+ printf("{ ");
+ printlist(n->a, level, ", ");
+ printf(" }");
+ break;
+
+ case EXP_PROP_VAL:
+ printast(n->a, level);
+ printf(": ");
+ printast(n->b, level);
+ break;
+
+ case EXP_ARRAY:
+ printf("[ ");
+ printlist(n->a, level, ", ");
+ printf(" ]");
+ break;
+
+ case EXP_NEW:
+ printf("(new ");
+ printast(n->a, level);
+ printf("(");
+ printlist(n->b, level, ", ");
+ printf("))");
+ break;
+
+ case EXP_CALL:
+ printf("(");
+ printast(n->a, level);
+ printf("(");
+ printlist(n->b, level, ", ");
+ printf("))");
+ break;
+
+ case EXP_MEMBER:
+ printf("(");
+ printast(n->a, level);
+ printf(".");
+ printast(n->b, level);
+ printf(")");
+ break;
+
+ case EXP_INDEX:
+ printf("(");
+ printast(n->a, level);
+ printf("[");
+ printast(n->b, level);
+ printf("])");
+ break;
+
+ case EXP_COND:
+ printf("(");
+ printast(n->a, level);
+ printf(" ? ");
+ printast(n->b, level);
+ printf(" : ");
+ printast(n->c, level);
+ printf(")");
+ break;
+
+ case EXP_NULL: printf("null"); break;
+ case EXP_TRUE: printf("true"); break;
+ case EXP_FALSE: printf("false"); break;
+ case EXP_THIS: printf("this"); break;
+
+ case EXP_DELETE: printunary(level, n->a, "(delete ", ")"); break;
+ case EXP_VOID: printunary(level, n->a, "(void ", ")"); break;
+ case EXP_TYPEOF: printunary(level, n->a, "(typeof ", ")"); break;
+ case EXP_PREINC: printunary(level, n->a, "(++", ")"); break;
+ case EXP_PREDEC: printunary(level, n->a, "(--", ")"); break;
+ case EXP_POSTINC: printunary(level, n->a, "(", "++)"); break;
+ case EXP_POSTDEC: printunary(level, n->a, "(", "--)"); break;
+ case EXP_POS: printunary(level, n->a, "(+", ")"); break;
+ case EXP_NEG: printunary(level, n->a, "(-", ")"); break;
+ case EXP_BITNOT: printunary(level, n->a, "(~", ")"); break;
+ case EXP_LOGNOT: printunary(level, n->a, "(!", ")"); break;
+
+ case EXP_COMMA: printbinary(level, n->a, n->b, ","); break;
+ case EXP_LOGOR: printbinary(level, n->a, n->b, "||"); break;
+ case EXP_LOGAND: printbinary(level, n->a, n->b, "&&"); break;
+ case EXP_BITOR: printbinary(level, n->a, n->b, "|"); break;
+ case EXP_BITXOR: printbinary(level, n->a, n->b, "^"); break;
+ case EXP_BITAND: printbinary(level, n->a, n->b, "&"); break;
+ case EXP_EQ: printbinary(level, n->a, n->b, "=="); break;
+ case EXP_NE: printbinary(level, n->a, n->b, "!="); break;
+ case EXP_EQ3: printbinary(level, n->a, n->b, "==="); break;
+ case EXP_NE3: printbinary(level, n->a, n->b, "!=="); break;
+ case EXP_LT: printbinary(level, n->a, n->b, "<"); break;
+ case EXP_GT: printbinary(level, n->a, n->b, ">"); break;
+ case EXP_LE: printbinary(level, n->a, n->b, "<="); break;
+ case EXP_GE: printbinary(level, n->a, n->b, ">="); break;
+ case EXP_INSTANCEOF: printbinary(level, n->a, n->b, "instanceof"); break;
+ case EXP_IN: printbinary(level, n->a, n->b, "in"); break;
+ case EXP_SHL: printbinary(level, n->a, n->b, "<<"); break;
+ case EXP_SHR: printbinary(level, n->a, n->b, ">>"); break;
+ case EXP_USHR: printbinary(level, n->a, n->b, ">>>"); break;
+ case EXP_ADD: printbinary(level, n->a, n->b, "+"); break;
+ case EXP_SUB: printbinary(level, n->a, n->b, "-"); break;
+ case EXP_MUL: printbinary(level, n->a, n->b, "*"); break;
+ case EXP_DIV: printbinary(level, n->a, n->b, "/"); break;
+ case EXP_MOD: printbinary(level, n->a, n->b, "%"); break;
+ case EXP_ASS: printbinary(level, n->a, n->b, "="); break;
+ case EXP_ASS_MUL: printbinary(level, n->a, n->b, "*="); break;
+ case EXP_ASS_DIV: printbinary(level, n->a, n->b, "/="); break;
+ case EXP_ASS_MOD: printbinary(level, n->a, n->b, "%="); break;
+ case EXP_ASS_ADD: printbinary(level, n->a, n->b, "+="); break;
+ case EXP_ASS_SUB: printbinary(level, n->a, n->b, "-="); break;
+ case EXP_ASS_SHL: printbinary(level, n->a, n->b, "<<="); break;
+ case EXP_ASS_SHR: printbinary(level, n->a, n->b, ">>="); break;
+ case EXP_ASS_USHR: printbinary(level, n->a, n->b, ">>>="); break;
+ case EXP_ASS_BITAND: printbinary(level, n->a, n->b, "&="); break;
+ case EXP_ASS_BITXOR: printbinary(level, n->a, n->b, "^="); break;
+ case EXP_ASS_BITOR: printbinary(level, n->a, n->b, "|="); break;
+
+ default:
+ printf("(%s", strast(n->type));
+ if (n->a) { putchar(' '); printast(n->a, level); }
+ if (n->b) { putchar(' '); printast(n->b, level); }
+ if (n->c) { putchar(' '); printast(n->c, level); }
+ if (n->d) { putchar(' '); printast(n->d, level); }
+ putchar(')');
+ break;
+ }
+}
--- /dev/null
+++ b/jsstate.c
@@ -1,0 +1,30 @@
+#include "js.h"
+#include "jsstate.h"
+
+js_State *js_newstate(void)
+{
+ js_State *J = malloc(sizeof *J);
+ memset(J, 0, sizeof(*J));
+ return J;
+}
+
+void js_close(js_State *J)
+{
+ free(J->buf.text);
+ free(J);
+}
+
+int js_error(js_State *J, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "error: ");
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+
+ return 0;
+}
--- /dev/null
+++ b/jsstate.h
@@ -1,0 +1,32 @@
+#ifndef js_state_h
+#define js_state_h
+
+typedef struct js_Ast js_Ast;
+
+struct js_State
+{
+ jmp_buf jb; /* setjmp buffer for error handling in parser */
+
+ js_StringNode *strings;
+
+ /* input */
+ const char *filename;
+ const char *source;
+ int line;
+
+ /* lexer */
+ struct { char *text; size_t len, cap; } buf;
+ int lasttoken;
+ int newline;
+
+ /* parser */
+ int lookahead;
+ const char *text;
+ double number;
+ struct { char g, i, m; } flags;
+ js_Ast *ast; /* list of allocated nodes to free after parsing */
+
+ int strict;
+};
+
+#endif