ref: 659e55976532f9b390541139b486441f28f56b7e
parent: 58d1b727e4cd8686feb5dd366180b43c22d04ded
author: Tor Andersson <tor@ccxvii.net>
date: Thu Jan 9 18:42:23 EST 2014
Parse statements.
--- a/js-ast.c
+++ b/js-ast.c
@@ -53,6 +53,7 @@
case AST_NUMBER: return "NUMBER";
case AST_STRING: return "STRING";
case AST_REGEXP: return "REGEXP";
+ case AST_INIT: return "INIT";
case EXP_NULL: return "NULL";
case EXP_TRUE: return "TRUE";
case EXP_FALSE: return "FALSE";
@@ -62,11 +63,11 @@
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 "EXP_INDEX";
- case EXP_MEMBER: return "EXP_MEMBER";
+ case EXP_INDEX: return "INDEX";
+ case EXP_MEMBER: return "MEMBER";
case EXP_NEW: return "new";
- case EXP_CALL: return "EXP_CALL";
- case EXP_FUNC: return "EXP_FUNC";
+ case EXP_CALL: return "CALL";
+ case EXP_FUNC: return "function";
case EXP_COND: return "?:";
case EXP_COMMA: return ",";
case EXP_DELETE: return "delete";
@@ -115,16 +116,17 @@
case EXP_ASS_BITAND: return "&=";
case EXP_ASS_BITXOR: return "^=";
case EXP_ASS_BITOR: return "|=";
- case STM_NOP: return "STM_NOP";
- case STM_EXP: return "STM_EXP";
- case STM_VAR: return "STM_VAR";
+ 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_EXP: return "for_3";
- case STM_FOR_VAR_EXP: return "for_var_3";
+ case STM_FOR: return "for";
+ case STM_FOR_VAR: return "for_var";
case STM_FOR_IN: return "for_in";
- case STM_FOR_VAR_IN: return "for_var_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";
@@ -140,18 +142,35 @@
}
}
+void printlist(js_Ast *n)
+{
+ while (n) {
+ printast(n->a);
+ n = n->b;
+ if (n)
+ putchar(' ');
+ }
+}
+
void printast(js_Ast *n)
{
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;
- default: printf("(%s", strast(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);
+ putchar(']');
+ break;
+ default:
+ printf("(%s", strast(n->type));
+ if (n->a) { putchar(' '); printast(n->a); }
+ if (n->b) { putchar(' '); printast(n->b); }
+ if (n->c) { putchar(' '); printast(n->c); }
+ if (n->d) { putchar(' '); printast(n->d); }
+ putchar(')');
+ break;
}
- if (n->a) { putchar(' '); printast(n->a); }
- if (n->b) { putchar(' '); printast(n->b); }
- if (n->c) { putchar(' '); printast(n->c); }
- if (n->d) { putchar(' '); printast(n->d); }
- putchar(')');
}
--- a/js-ast.h
+++ b/js-ast.h
@@ -14,6 +14,7 @@
enum
{
AST_LIST,
+ AST_INIT,
AST_IDENTIFIER,
AST_NUMBER,
@@ -37,7 +38,7 @@
EXP_MEMBER,
EXP_NEW,
EXP_CALL,
- EXP_FUNC,
+ EXP_FUNC, /* function expression */
EXP_COND,
EXP_COMMA,
@@ -91,16 +92,17 @@
EXP_ASS_BITOR,
/* statements */
+ STM_BLOCK,
+ STM_FUNC, /* function declaration */
STM_NOP,
- STM_EXP,
STM_VAR,
STM_IF,
STM_DO,
STM_WHILE,
- STM_FOR_EXP,
- STM_FOR_VAR_EXP,
+ STM_FOR,
+ STM_FOR_VAR,
STM_FOR_IN,
- STM_FOR_VAR_IN,
+ STM_FOR_IN_VAR,
STM_CONTINUE,
STM_BREAK,
STM_RETURN,
@@ -118,5 +120,7 @@
js_Ast *jsP_newsnode(js_State *J, int type, const char *s);
js_Ast *jsP_newnnode(js_State *J, int type, double n);
void jsP_freeast(js_State *J);
+
+void printast(js_Ast *n);
#endif
--- a/js-parse.c
+++ b/js-parse.c
@@ -6,9 +6,7 @@
#define A2(x,a,b) jsP_newnode(J, x, a, b, 0, 0)
#define A3(x,a,b,c) jsP_newnode(J, x, a, b, c, 0)
-#define LIST(h,t) jsP_newnode(J, AST_LIST, h, t, 0, 0);
-#define HEAD(h) jsP_newnode(J, AST_LIST, h, 0, 0, 0);
-#define TAIL(t) jsP_newnode(J, AST_LIST, 0, t, 0, 0);
+#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)
@@ -15,6 +13,7 @@
#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)
@@ -23,7 +22,8 @@
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 *block(js_State *J);
+static js_Ast *statement(js_State *J);
+static js_Ast *functionbody(js_State *J);
static const char *tokenstring[] = {
"(end-of-file)",
@@ -111,6 +111,13 @@
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) {
@@ -124,16 +131,16 @@
static js_Ast *arguments(js_State *J)
{
- js_Ast *head, *node;
+ js_Ast *head, *tail, *node;
if (J->lookahead == ')')
return NULL;
node = assignment(J, 0);
- head = HEAD(node);
+ head = tail = LIST(node);
while (accept(J, ',')) {
node = assignment(J, 0);
- head = LIST(head, node);
+ tail = tail->b = LIST(node);
}
return head;
}
@@ -140,7 +147,7 @@
static js_Ast *arrayliteral(js_State *J)
{
- js_Ast *head, *node;
+ js_Ast *head, *tail, *node;
while (J->lookahead == ',')
next(J);
@@ -149,7 +156,7 @@
return NULL;
node = assignment(J, 0);
- head = HEAD(node);
+ head = tail = LIST(node);
while (accept(J, ',')) {
while (J->lookahead == ',')
next(J);
@@ -156,7 +163,7 @@
if (J->lookahead == ']')
break;
node = assignment(J, 0);
- head = LIST(head, node);
+ tail = tail->b = LIST(node);
}
return head;
}
@@ -185,7 +192,7 @@
name = propname(J);
expect(J, '(');
expect(J, ')');
- body = block(J);
+ body = functionbody(J);
return EXP2(PROP_GET, name, body);
}
@@ -195,7 +202,7 @@
expect(J, '(');
arg = identifier(J);
expect(J, ')');
- body = block(J);
+ body = functionbody(J);
return EXP3(PROP_SET, name, arg, body);
}
@@ -207,18 +214,18 @@
static js_Ast *objectliteral(js_State *J)
{
- js_Ast *head, *node;
+ js_Ast *head, *tail, *node;
if (J->lookahead == '}')
return NULL;
node = propassign(J);
- head = HEAD(node);
+ head = tail = LIST(node);
while (accept(J, ',')) {
if (J->lookahead == '}')
break;
node = propassign(J);
- head = LIST(head, node);
+ tail = tail->b = LIST(node);
}
return head;
}
@@ -252,9 +259,25 @@
return NULL;
}
+static js_Ast *paramlist(js_State *J)
+{
+ js_Ast *head, *tail, *node;
+
+ if (J->lookahead == ')')
+ return NULL;
+
+ node = identifier(J);
+ head = tail = LIST(node);
+ while (accept(J, ',')) {
+ node = identifier(J);
+ tail = tail->b = LIST(node);
+ }
+ return head;
+}
+
static js_Ast *newexp(js_State *J)
{
- js_Ast *a, *b;
+ js_Ast *a, *b, *c;
if (accept(J, TK_NEW)) {
a = memberexp(J);
if (accept(J, '(')) {
@@ -264,6 +287,14 @@
}
return EXP1(NEW, a);
}
+ if (accept(J, TK_FUNCTION)) {
+ a = identifieropt(J);
+ expect(J, '(');
+ b = paramlist(J);
+ expect(J, ')');
+ c = functionbody(J);
+ return EXP3(FUNC, a, b, c);
+ }
return primary(J);
}
@@ -440,29 +471,326 @@
return a;
}
-void statement(js_State *J)
+static js_Ast *vardec(js_State *J, int notin)
{
- js_Ast *exp = expression(J, 1);
- semicolon(J);
- printast(exp);
- putchar(';');
- putchar('\n');
+ js_Ast *a, *b;
+
+ a = identifier(J);
+ if (accept(J, '='))
+ b = assignment(J, notin);
+ else
+ b = NULL;
+
+ return A2(AST_INIT, a, b);
+
}
+static js_Ast *vardeclist(js_State *J, int notin)
+{
+ js_Ast *head, *tail, *node;
+
+ node = vardec(J, notin);
+ head = tail = LIST(node);
+ while (accept(J, ',')) {
+ node = vardec(J, notin);
+ tail = tail->b = LIST(node);
+ }
+ return head;
+}
+
+static js_Ast *expopt(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 *casestatementlist(js_State *J)
+{
+ js_Ast *head, *tail, *node;
+
+ if (J->lookahead == '}' || J->lookahead == TK_CASE || J->lookahead == TK_DEFAULT)
+ return NULL;
+
+ node = statement(J);
+ head = tail = LIST(node);
+ while (J->lookahead != '}' && J->lookahead != TK_CASE && J->lookahead != TK_DEFAULT) {
+ node = statement(J);
+ tail = tail->b = LIST(node);
+ }
+
+ 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 = casestatementlist(J);
+ return STM2(CASE, a, b);
+ }
+
+ if (accept(J, TK_DEFAULT)) {
+ expect(J, ':');
+ a = casestatementlist(J);
+ return STM1(DEFAULT, a);
+ }
+
+ jsP_error(J, "unexpected token in switch: %s", tokenstring[J->lookahead]);
+ return NULL;
+}
+
+static js_Ast *caseblock(js_State *J)
+{
+ js_Ast *head, *tail, *node;
+
+ expect(J, '{');
+ if (accept(J, '}'))
+ return NULL;
+
+ node = caseclause(J);
+ head = tail = LIST(node);
+ while (J->lookahead != '}') {
+ node = caseclause(J);
+ tail = tail->b = LIST(node);
+ }
+
+ expect(J, '}');
+ return STM1(BLOCK, head);
+}
+
static js_Ast *block(js_State *J)
{
+ js_Ast *head, *tail, *node;
+
expect(J, '{');
+ if (accept(J, '}'))
+ return NULL;
+
+ node = statement(J);
+ head = tail = LIST(node);
+ while (J->lookahead != '}') {
+ node = statement(J);
+ tail = tail->b = LIST(node);
+ }
+
expect(J, '}');
+ return STM1(BLOCK, head);
+}
+
+static js_Ast *statement(js_State *J)
+{
+ js_Ast *a, *b, *c, *d;
+
+ if (J->lookahead == '{') {
+ return block(J);
+ }
+
+ if (accept(J, ';')) {
+ return STM0(NOP);
+ }
+
+ 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_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: %s (expected 'catch' or 'finally')", tokenstring[J->lookahead]);
+ return STM4(TRY, a, b, c, d);
+ }
+
+ if (accept(J, TK_DEBUGGER)) {
+ semicolon(J);
+ return STM0(DEBUGGER);
+ }
+
+ 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_VAR)) {
+ a = vardeclist(J, 0);
+ semicolon(J);
+ return STM1(VAR, a);
+ }
+
+ if (accept(J, TK_FOR)) {
+ expect(J, '(');
+ if (accept(J, TK_VAR)) {
+ a = vardeclist(J, 1);
+ if (accept(J, ';')) {
+ b = expopt(J, ';');
+ c = expopt(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", tokenstring[J->lookahead]);
+ return NULL;
+ }
+
+ if (J->lookahead != ';') {
+ a = expression(J, 1);
+ }
+ if (accept(J, ';')) {
+ b = expopt(J, ';');
+ c = expopt(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", tokenstring[J->lookahead]);
+ return NULL;
+ }
+
+ if (accept(J, TK_SWITCH)) {
+ expect(J, '(');
+ a = expression(J, 0);
+ expect(J, ')');
+ b = caseblock(J);
+ return STM2(SWITCH, a, b);
+ }
+
+ if (J->lookahead != TK_FUNCTION) {
+ a = expression(J, 0);
+ semicolon(J);
+ return a;
+ }
+
+ jsP_error(J, "unexpected token in statement: %s", tokenstring[J->lookahead]);
return NULL;
}
-void program(js_State *J)
+static js_Ast *fundec(js_State *J)
{
- next(J);
- while (J->lookahead != 0)
- statement(J);
+ js_Ast *a, *b, *c;
+ a = identifier(J);
+ expect(J, '(');
+ b = paramlist(J);
+ expect(J, ')');
+ c = functionbody(J);
+ return STM3(FUNC, a, b, c);
}
+static js_Ast *sourceelement(js_State *J)
+{
+ if (accept(J, TK_FUNCTION))
+ return fundec(J);
+ return statement(J);
+}
+
+static js_Ast *sourcelist(js_State *J)
+{
+ js_Ast *head, *tail, *node;
+
+ if (J->lookahead == '}' || J->lookahead == 0)
+ return NULL;
+
+ node = sourceelement(J);
+ head = tail = LIST(node);
+ while (J->lookahead != '}' && J->lookahead != 0) {
+ node = sourceelement(J);
+ tail = tail->b = LIST(node);
+ }
+
+ return STM1(BLOCK, head);
+}
+
+static js_Ast *functionbody(js_State *J)
+{
+ js_Ast *a;
+
+ expect(J, '{');
+ a = sourcelist(J);
+ expect(J, '}');
+
+ return a;
+}
+
int jsP_error(js_State *J, const char *fmt, ...)
{
va_list ap;
@@ -486,7 +814,9 @@
return 1;
}
- program(J);
+ next(J);
+ printast(sourcelist(J));
+ putchar('\n');
// TODO: compile to bytecode