ref: fdd94dc5ddb48d9998a33e4154fa76a11be3515e
parent: 14043f0e11d6a464df9688a67c239e666b4d84c3
author: Tor Andersson <tor@ccxvii.net>
date: Wed Jan 22 09:23:07 EST 2014
Break and continue to labelled statements.
--- a/jscompile.c
+++ b/jscompile.c
@@ -552,7 +552,7 @@
break;
default:
- jsC_error(J, exp, "unknown expression");
+ jsC_error(J, exp, "unknown expression: (%s)", jsP_aststring(exp->type));
}
}
@@ -560,10 +560,8 @@
static void labelexit(JF, js_Ast *top, js_Ast *node, js_AstType T, int addr)
{
- if (node->type == T) {
- // TODO: check if top is our real target
- if (F->code[node->inst] == 0)
- labelto(J, F, node->inst, addr);
+ if (node->type == T && node->target == top) {
+ labelto(J, F, node->inst, addr);
} else if (node->type >= STM_BLOCK || node->type == AST_LIST) {
if (node->a) labelexit(J, F, top, node->a, T, addr);
if (node->b) labelexit(J, F, top, node->b, T, addr);
@@ -572,11 +570,57 @@
}
}
+static int isloop(js_AstType T)
+{
+ return T == STM_DO || T == STM_WHILE ||
+ T == STM_FOR || T == STM_FOR_VAR ||
+ T == STM_FOR_IN || T == STM_FOR_IN_VAR;
+}
+
+static int matchlabel(js_Ast *node, const char *label)
+{
+ while (node && node->type == STM_LABEL) {
+ if (!strcmp(node->a->string, label))
+ return 1;
+ node = node->parent;
+ }
+ return 0;
+}
+
+static js_Ast *breaktarget(JF, js_Ast *node, const char *label)
+{
+ while (node) {
+ if (!label) {
+ if (isloop(node->type) || node->type == STM_SWITCH)
+ return node;
+ } else {
+ if (matchlabel(node->parent, label))
+ return node;
+ }
+ node = node->parent;
+ }
+ return NULL;
+}
+
+static js_Ast *continuetarget(JF, js_Ast *node, const char *label)
+{
+ while (node) {
+ if (isloop(node->type)) {
+ if (!label)
+ return node;
+ else if (matchlabel(node->parent, label))
+ return node;
+ }
+ node = node->parent;
+ }
+ return NULL;
+}
+
/* Statements */
static void cstm(JF, js_Ast *stm)
{
- int loop, then, end;
+ int loop, cont, then, end;
switch (stm->type) {
case AST_FUNDEC:
@@ -584,8 +628,6 @@
case STM_BLOCK:
cstmlist(J, F, stm->a);
- if (stm->parent && stm->parent->type == STM_LABEL)
- labelexit(J, F, stm, stm->a, STM_BREAK, here(J, F));
break;
case STM_NOP:
@@ -644,11 +686,12 @@
cexp(J, F, stm->b);
end = jump(J, F, OP_JFALSE);
cstm(J, F, stm->d);
+ cont = here(J, F);
cexp(J, F, stm->c);
emit(J, F, OP_POP);
jumpto(J, F, OP_JUMP, loop);
label(J, F, end);
- labelexit(J, F, stm, stm->d, STM_CONTINUE, loop);
+ labelexit(J, F, stm, stm->d, STM_CONTINUE, cont);
labelexit(J, F, stm, stm->d, STM_BREAK, here(J, F));
break;
@@ -673,13 +716,43 @@
labelexit(J, F, stm, stm->c, STM_BREAK, here(J, F));
break;
- // label
+ case STM_LABEL:
+ cstm(J, F, stm->b);
+ /* skip consecutive labels */
+ while (stm->type == STM_LABEL)
+ stm = stm->b;
+ /* loops and switches have already been labelled */
+ if (!isloop(stm->type) && stm->type != STM_SWITCH) {
+ if (stm->a) labelexit(J, F, stm, stm->a, STM_BREAK, here(J, F));
+ if (stm->b) labelexit(J, F, stm, stm->b, STM_BREAK, here(J, F));
+ if (stm->c) labelexit(J, F, stm, stm->c, STM_BREAK, here(J, F));
+ if (stm->d) labelexit(J, F, stm, stm->d, STM_BREAK, here(J, F));
+ }
+ break;
case STM_BREAK:
+ if (stm->a) {
+ stm->target = breaktarget(J, F, stm, stm->a->string);
+ if (!stm->target)
+ jsC_error(J, stm, "break label not found: %s", stm->a->string);
+ } else {
+ stm->target = breaktarget(J, F, stm, NULL);
+ if (!stm->target)
+ jsC_error(J, stm, "unlabelled break must be inside loop or switch");
+ }
stm->inst = jump(J, F, OP_JUMP);
break;
case STM_CONTINUE:
+ if (stm->a) {
+ stm->target = continuetarget(J, F, stm, stm->a->string);
+ if (!stm->target)
+ jsC_error(J, stm, "continue label not found: %s", stm->a->string);
+ } else {
+ stm->target = continuetarget(J, F, stm, NULL);
+ if (!stm->target)
+ jsC_error(J, stm, "continue must be inside loop");
+ }
stm->inst = jump(J, F, OP_JUMP);
break;
--- a/jscompile.h
+++ b/jscompile.h
@@ -74,6 +74,7 @@
OP_THROW,
OP_TRY,
+ OP_ENDTRY,
OP_CATCH,
OP_ENDCATCH,
OP_WITH,
--- a/jsparse.c
+++ b/jsparse.c
@@ -80,6 +80,18 @@
return node;
}
+static js_Ast *jsP_list(js_Ast *head)
+{
+ /* set parent pointers in list nodes */
+ js_Ast *prev = head, *node = head->b;
+ while (node) {
+ node->parent = prev;
+ prev = node;
+ node = node->b;
+ }
+ return head;
+}
+
static js_Ast *jsP_newstrnode(js_State *J, int type, const char *s)
{
js_Ast *node = jsP_newnode(J, type, 0, 0, 0, 0);
@@ -206,7 +218,7 @@
if (J->lookahead != ']')
tail = tail->b = LIST(arrayelement(J));
}
- return head;
+ return jsP_list(head);
}
static js_Ast *propname(js_State *J)
@@ -264,7 +276,7 @@
break;
tail = tail->b = LIST(propassign(J));
}
- return head;
+ return jsP_list(head);
}
/* Functions */
@@ -278,7 +290,7 @@
while (accept(J, ',')) {
tail = tail->b = LIST(identifier(J));
}
- return head;
+ return jsP_list(head);
}
static js_Ast *fundec(js_State *J)
@@ -364,7 +376,7 @@
while (accept(J, ',')) {
tail = tail->b = LIST(assignment(J, 0));
}
- return head;
+ return jsP_list(head);
}
static js_Ast *newexp(js_State *J)
@@ -576,7 +588,7 @@
head = tail = LIST(vardec(J, notin));
while (accept(J, ','))
tail = tail->b = LIST(vardec(J, notin));
- return head;
+ return jsP_list(head);
}
static js_Ast *statementlist(js_State *J)
@@ -587,7 +599,7 @@
head = tail = LIST(statement(J));
while (J->lookahead != '}' && J->lookahead != TK_CASE && J->lookahead != TK_DEFAULT)
tail = tail->b = LIST(statement(J));
- return head;
+ return jsP_list(head);
}
static js_Ast *caseclause(js_State *J)
@@ -619,7 +631,7 @@
head = tail = LIST(caseclause(J));
while (J->lookahead != '}')
tail = tail->b = LIST(caseclause(J));
- return head;
+ return jsP_list(head);
}
static js_Ast *block(js_State *J)
@@ -844,7 +856,7 @@
head = tail = LIST(scriptelement(J));
while (J->lookahead != '}' && J->lookahead != 0)
tail = tail->b = LIST(scriptelement(J));
- return head;
+ return jsP_list(head);
}
static js_Ast *funbody(js_State *J)
--- a/jsparse.h
+++ b/jsparse.h
@@ -118,7 +118,7 @@
{
int type;
int line;
- js_Ast *parent, *a, *b, *c, *d;
+ js_Ast *parent, *target, *a, *b, *c, *d;
double number;
const char *string;
int inst; /* for patching jumps */