added break statement
This commit is contained in:
+21
-5
@@ -9,8 +9,11 @@ fun is_digit(c)
|
|||||||
and unicode(c) >= unicode('0');
|
and unicode(c) >= unicode('0');
|
||||||
|
|
||||||
fun skip(ctx, test)
|
fun skip(ctx, test)
|
||||||
while not ended(ctx) and test(current(ctx)):
|
while not ended(ctx): {
|
||||||
|
if not test(current(ctx)):
|
||||||
|
break;
|
||||||
next(ctx);
|
next(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
fun ended(ctx)
|
fun ended(ctx)
|
||||||
return ctx.i == count(ctx.str);
|
return ctx.i == count(ctx.str);
|
||||||
@@ -20,8 +23,10 @@ fun next(ctx) {
|
|||||||
ctx.i = ctx.i + 1;
|
ctx.i = ctx.i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun current(ctx)
|
fun current(ctx) {
|
||||||
|
assert(ctx.i < count(ctx.str));
|
||||||
return ctx.str[ctx.i];
|
return ctx.str[ctx.i];
|
||||||
|
}
|
||||||
|
|
||||||
fun parse(str) {
|
fun parse(str) {
|
||||||
|
|
||||||
@@ -32,9 +37,10 @@ fun parse(str) {
|
|||||||
|
|
||||||
skip(ctx, is_space);
|
skip(ctx, is_space);
|
||||||
|
|
||||||
if ended(ctx):
|
if ended(ctx): {
|
||||||
# JSON source string only contains whitespace.
|
print('Source only contains whitespace');
|
||||||
return none;
|
return none;
|
||||||
|
}
|
||||||
|
|
||||||
return parse_any_value(ctx);
|
return parse_any_value(ctx);
|
||||||
}
|
}
|
||||||
@@ -75,4 +81,14 @@ fun parse_any_value(ctx) {
|
|||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse();
|
|
||||||
|
tests = [
|
||||||
|
'',
|
||||||
|
'1'
|
||||||
|
];
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while i < count(tests): {
|
||||||
|
parse(tests[i]);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ typedef enum {
|
|||||||
NODE_FUNC,
|
NODE_FUNC,
|
||||||
NODE_ARG,
|
NODE_ARG,
|
||||||
NODE_WHILE,
|
NODE_WHILE,
|
||||||
|
NODE_BREAK,
|
||||||
NODE_DOWHILE,
|
NODE_DOWHILE,
|
||||||
} NodeKind;
|
} NodeKind;
|
||||||
|
|
||||||
|
|||||||
+50
-26
@@ -75,7 +75,7 @@ static Opcode exprkind_to_opcode(ExprKind kind)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_dest, Error *error)
|
||||||
{
|
{
|
||||||
assert(node != NULL);
|
assert(node != NULL);
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
OperExprNode *oper = (OperExprNode*) expr;
|
OperExprNode *oper = (OperExprNode*) expr;
|
||||||
|
|
||||||
for(Node *operand = oper->head; operand; operand = operand->next)
|
for(Node *operand = oper->head; operand; operand = operand->next)
|
||||||
if(!emit_instr_for_node(exeb, operand, error))
|
if(!emit_instr_for_node(exeb, operand, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return ExeBuilder_Append(exeb, error,
|
return ExeBuilder_Append(exeb, error,
|
||||||
@@ -121,7 +121,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
lop = oper->head;
|
lop = oper->head;
|
||||||
rop = lop->next;
|
rop = lop->next;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, rop, error))
|
if(!emit_instr_for_node(exeb, rop, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(((ExprNode*) lop)->kind == EXPR_IDENT)
|
if(((ExprNode*) lop)->kind == EXPR_IDENT)
|
||||||
@@ -137,10 +137,10 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
Node *idx = ((IndexSelectionExprNode*) lop)->idx;
|
Node *idx = ((IndexSelectionExprNode*) lop)->idx;
|
||||||
Node *set = ((IndexSelectionExprNode*) lop)->set;
|
Node *set = ((IndexSelectionExprNode*) lop)->set;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, set, error))
|
if(!emit_instr_for_node(exeb, set, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, idx, error))
|
if(!emit_instr_for_node(exeb, idx, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT2, NULL, 0, node->offset, node->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT2, NULL, 0, node->offset, node->length))
|
||||||
@@ -207,7 +207,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHINT, &op, 1, item->offset, item->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHINT, &op, 1, item->offset, item->length))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, item, error))
|
if(!emit_instr_for_node(exeb, item, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT, NULL, 0, item->offset, item->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT, NULL, 0, item->offset, item->length))
|
||||||
@@ -234,10 +234,10 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
|
|
||||||
while(item)
|
while(item)
|
||||||
{
|
{
|
||||||
if(!emit_instr_for_node(exeb, key, error))
|
if(!emit_instr_for_node(exeb, key, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, item, error))
|
if(!emit_instr_for_node(exeb, item, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT, NULL, 0, item->offset, item->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT, NULL, 0, item->offset, item->length))
|
||||||
@@ -258,13 +258,13 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
|
|
||||||
while(arg)
|
while(arg)
|
||||||
{
|
{
|
||||||
if(!emit_instr_for_node(exeb, arg, error))
|
if(!emit_instr_for_node(exeb, arg, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
arg = arg->next;
|
arg = arg->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, p->func, error))
|
if(!emit_instr_for_node(exeb, p->func, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Operand op = { .type = OPTP_INT, .as_int = p->argc };
|
Operand op = { .type = OPTP_INT, .as_int = p->argc };
|
||||||
@@ -275,10 +275,10 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
{
|
{
|
||||||
IndexSelectionExprNode *sel = (IndexSelectionExprNode*) expr;
|
IndexSelectionExprNode *sel = (IndexSelectionExprNode*) expr;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, sel->set, error))
|
if(!emit_instr_for_node(exeb, sel->set, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, sel->idx, error))
|
if(!emit_instr_for_node(exeb, sel->idx, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return ExeBuilder_Append(exeb, error, OPCODE_SELECT, NULL, 0, node->offset, node->length);
|
return ExeBuilder_Append(exeb, error, OPCODE_SELECT, NULL, 0, node->offset, node->length);
|
||||||
@@ -300,11 +300,24 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case NODE_BREAK:
|
||||||
|
{
|
||||||
|
if(break_dest == NULL)
|
||||||
|
{
|
||||||
|
Error_Report(error, 0, "Break not inside a loop");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Operand op = (Operand) { .type = OPTP_PROMISE, .as_promise = break_dest };
|
||||||
|
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, node->offset, node->length))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
case NODE_IFELSE:
|
case NODE_IFELSE:
|
||||||
{
|
{
|
||||||
IfElseNode *ifelse = (IfElseNode*) node;
|
IfElseNode *ifelse = (IfElseNode*) node;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, ifelse->condition, error))
|
if(!emit_instr_for_node(exeb, ifelse->condition, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(ifelse->false_branch)
|
if(ifelse->false_branch)
|
||||||
@@ -322,7 +335,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, node->offset, node->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, node->offset, node->length))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, ifelse->true_branch, error))
|
if(!emit_instr_for_node(exeb, ifelse->true_branch, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(ifelse->true_branch->kind == NODE_EXPR)
|
if(ifelse->true_branch->kind == NODE_EXPR)
|
||||||
@@ -339,7 +352,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||||
Promise_Resolve(else_offset, &temp, sizeof(temp));
|
Promise_Resolve(else_offset, &temp, sizeof(temp));
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, ifelse->false_branch, error))
|
if(!emit_instr_for_node(exeb, ifelse->false_branch, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(ifelse->false_branch->kind == NODE_EXPR)
|
if(ifelse->false_branch->kind == NODE_EXPR)
|
||||||
@@ -368,7 +381,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &(Operand) { .type = OPTP_PROMISE, .as_promise = done_offset }, 1, node->offset, node->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &(Operand) { .type = OPTP_PROMISE, .as_promise = done_offset }, 1, node->offset, node->length))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, ifelse->true_branch, error))
|
if(!emit_instr_for_node(exeb, ifelse->true_branch, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(ifelse->true_branch->kind == NODE_EXPR)
|
if(ifelse->true_branch->kind == NODE_EXPR)
|
||||||
@@ -412,14 +425,14 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||||
Promise_Resolve(start_offset, &temp, sizeof(temp));
|
Promise_Resolve(start_offset, &temp, sizeof(temp));
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, whl->condition, error))
|
if(!emit_instr_for_node(exeb, whl->condition, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Operand op = { .type = OPTP_PROMISE, .as_promise = end_offset };
|
Operand op = { .type = OPTP_PROMISE, .as_promise = end_offset };
|
||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, whl->condition->offset, whl->condition->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, whl->condition->offset, whl->condition->length))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, whl->body, error))
|
if(!emit_instr_for_node(exeb, whl->body, end_offset, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(whl->body->kind == NODE_EXPR)
|
if(whl->body->kind == NODE_EXPR)
|
||||||
@@ -452,9 +465,16 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
* JUMPIFANDPOP start
|
* JUMPIFANDPOP start
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Promise *end_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||||
|
if(end_offset == NULL)
|
||||||
|
{
|
||||||
|
Error_Report(error, 1, "No memory");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
long long int start = ExeBuilder_InstrCount(exeb);
|
long long int start = ExeBuilder_InstrCount(exeb);
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, dowhl->body, error))
|
if(!emit_instr_for_node(exeb, dowhl->body, end_offset, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(dowhl->body->kind == NODE_EXPR)
|
if(dowhl->body->kind == NODE_EXPR)
|
||||||
@@ -464,12 +484,16 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, dowhl->condition, error))
|
if(!emit_instr_for_node(exeb, dowhl->condition, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Operand op = { .type = OPTP_INT, .as_int = start };
|
Operand op = { .type = OPTP_INT, .as_int = start };
|
||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFANDPOP, &op, 1, dowhl->condition->offset, dowhl->condition->length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFANDPOP, &op, 1, dowhl->condition->offset, dowhl->condition->length))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||||
|
Promise_Resolve(end_offset, &temp, sizeof(temp));
|
||||||
|
Promise_Free(end_offset);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,7 +505,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
|
|
||||||
while(stmt)
|
while(stmt)
|
||||||
{
|
{
|
||||||
if(!emit_instr_for_node(exeb, stmt, error))
|
if(!emit_instr_for_node(exeb, stmt, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(stmt->kind == NODE_EXPR)
|
if(stmt->kind == NODE_EXPR)
|
||||||
@@ -501,7 +525,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
{
|
{
|
||||||
ReturnNode *ret = (ReturnNode*) node;
|
ReturnNode *ret = (ReturnNode*) node;
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, ret->val, error))
|
if(!emit_instr_for_node(exeb, ret->val, break_dest, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, ret->base.offset, ret->base.length))
|
if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, ret->base.offset, ret->base.length))
|
||||||
@@ -558,7 +582,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
// Assign the arguments.
|
// Assign the arguments.
|
||||||
|
|
||||||
if(func->argv)
|
if(func->argv)
|
||||||
assert(func->argv->kind == NODE_ARG);
|
{ assert(func->argv->kind == NODE_ARG); }
|
||||||
|
|
||||||
ArgumentNode *arg = (ArgumentNode*) func->argv;
|
ArgumentNode *arg = (ArgumentNode*) func->argv;
|
||||||
|
|
||||||
@@ -573,12 +597,12 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(arg->base.next)
|
if(arg->base.next)
|
||||||
assert(arg->base.next->kind == NODE_ARG);
|
{ assert(arg->base.next->kind == NODE_ARG); }
|
||||||
|
|
||||||
arg = (ArgumentNode*) arg->base.next;
|
arg = (ArgumentNode*) arg->base.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!emit_instr_for_node(exeb, func->body, error))
|
if(!emit_instr_for_node(exeb, func->body, NULL, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Write a return instruction, just
|
// Write a return instruction, just
|
||||||
@@ -644,7 +668,7 @@ Executable *compile(AST *ast, BPAlloc *alloc, Error *error)
|
|||||||
|
|
||||||
if(exeb != NULL)
|
if(exeb != NULL)
|
||||||
{
|
{
|
||||||
if(!emit_instr_for_node(exeb, ast->root, error))
|
if(!emit_instr_for_node(exeb, ast->root, NULL, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, Source_GetSize(ast->src), 0))
|
if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, Source_GetSize(ast->src), 0))
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ typedef enum {
|
|||||||
TKWFALSE,
|
TKWFALSE,
|
||||||
TKWRETURN,
|
TKWRETURN,
|
||||||
TKWWHILE,
|
TKWWHILE,
|
||||||
|
TKWBREAK,
|
||||||
TKWDO,
|
TKWDO,
|
||||||
|
|
||||||
TEQL,
|
TEQL,
|
||||||
@@ -235,6 +236,7 @@ static Token *tokenize(Source *src, BPAlloc *alloc, Error *error)
|
|||||||
{ TKWFALSE, 5, "false" },
|
{ TKWFALSE, 5, "false" },
|
||||||
{ TKWRETURN, 6, "return" },
|
{ TKWRETURN, 6, "return" },
|
||||||
{ TKWWHILE, 5, "while" },
|
{ TKWWHILE, 5, "while" },
|
||||||
|
{ TKWBREAK, 5, "break" },
|
||||||
{ TKWDO, 2, "do" },
|
{ TKWDO, 2, "do" },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -567,6 +569,35 @@ static Node *parse_statement(Context *ctx)
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TKWBREAK:
|
||||||
|
{
|
||||||
|
Token *token = current_token(ctx);
|
||||||
|
|
||||||
|
next(ctx); // Consume the "break".
|
||||||
|
|
||||||
|
if(current(ctx) != ';')
|
||||||
|
{
|
||||||
|
Error_Report(ctx->error, 0, "Got token \"%.*s\" where \";\" was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(ctx); // Consume the ';'.
|
||||||
|
|
||||||
|
Node *node = BPAlloc_Malloc(ctx->alloc, sizeof(Node));
|
||||||
|
|
||||||
|
if(node == NULL)
|
||||||
|
{
|
||||||
|
Error_Report(ctx->error, 1, "No memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->kind = NODE_BREAK;
|
||||||
|
node->next = NULL;
|
||||||
|
node->offset = token->offset;
|
||||||
|
node->length = token->length;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
case TKWRETURN:
|
case TKWRETURN:
|
||||||
{
|
{
|
||||||
int offset = current_token(ctx)->offset;
|
int offset = current_token(ctx)->offset;
|
||||||
|
|||||||
Reference in New Issue
Block a user