From fdd94dc5ddb48d9998a33e4154fa76a11be3515e Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 22 Jan 2014 14:23:07 +0100 Subject: [PATCH] Break and continue to labelled statements. --- jscompile.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++------ jscompile.h | 1 + jsparse.c | 28 +++++++++++----- jsparse.h | 2 +- 4 files changed, 105 insertions(+), 19 deletions(-) diff --git a/jscompile.c b/jscompile.c index 8e883a9..626abb8 100644 --- a/jscompile.c +++ b/jscompile.c @@ -552,7 +552,7 @@ static void cexp(JF, js_Ast *exp) 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 cexp(JF, js_Ast *exp) 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 void labelexit(JF, js_Ast *top, js_Ast *node, js_AstType T, int addr) } } +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 @@ static void cstm(JF, js_Ast *stm) 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 @@ static void cstm(JF, js_Ast *stm) 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 @@ static void cstm(JF, js_Ast *stm) 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; diff --git a/jscompile.h b/jscompile.h index c92206c..636827c 100644 --- a/jscompile.h +++ b/jscompile.h @@ -74,6 +74,7 @@ enum OP_THROW, OP_TRY, + OP_ENDTRY, OP_CATCH, OP_ENDCATCH, OP_WITH, diff --git a/jsparse.c b/jsparse.c index 4b5f54f..205fe3e 100644 --- a/jsparse.c +++ b/jsparse.c @@ -80,6 +80,18 @@ static js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast * 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 @@ static js_Ast *arrayliteral(js_State *J) 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 @@ static js_Ast *objectliteral(js_State *J) break; tail = tail->b = LIST(propassign(J)); } - return head; + return jsP_list(head); } /* Functions */ @@ -278,7 +290,7 @@ static js_Ast *parameters(js_State *J) 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 @@ static js_Ast *arguments(js_State *J) 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 @@ static js_Ast *vardeclist(js_State *J, int notin) 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 @@ static js_Ast *statementlist(js_State *J) 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 @@ static js_Ast *caselist(js_State *J) 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 @@ static js_Ast *script(js_State *J) 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) diff --git a/jsparse.h b/jsparse.h index 5d5d2ab..53c959a 100644 --- a/jsparse.h +++ b/jsparse.h @@ -118,7 +118,7 @@ struct js_Ast { 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 */