added while and do-while loops
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
|
||||
p = true;
|
||||
|
||||
while p:
|
||||
{
|
||||
print('Hello!\n');
|
||||
p = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
do {
|
||||
|
||||
print('Hello 2!\n');
|
||||
|
||||
} while true;
|
||||
@@ -62,6 +62,7 @@ static const InstrInfo instr_table[] = {
|
||||
[OPCODE_RETURN] = {"RETURN", 0, NULL},
|
||||
|
||||
[OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_JUMPIFANDPOP] = {"JUMPIFANDPOP", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_JUMP] = {"JUMP", 1, (OperandType[]) {OPTP_INT}},
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ typedef enum {
|
||||
OPCODE_PUSHNNE,
|
||||
OPCODE_PUSHFUN,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_JUMPIFANDPOP,
|
||||
OPCODE_JUMPIFNOTANDPOP,
|
||||
OPCODE_JUMP,
|
||||
} Opcode;
|
||||
|
||||
+18
-4
@@ -11,6 +11,8 @@ typedef enum {
|
||||
NODE_RETURN,
|
||||
NODE_FUNC,
|
||||
NODE_ARG,
|
||||
NODE_WHILE,
|
||||
NODE_DOWHILE,
|
||||
} NodeKind;
|
||||
|
||||
typedef enum {
|
||||
@@ -82,18 +84,30 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
Node *func;
|
||||
Node *argv;
|
||||
int argc;
|
||||
Node *func;
|
||||
Node *argv;
|
||||
int argc;
|
||||
} CallExprNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node base;
|
||||
Node *condition;
|
||||
Node *true_branch;
|
||||
Node *false_branch;
|
||||
} IfElseNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *condition;
|
||||
Node *body;
|
||||
} WhileNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *body;
|
||||
Node *condition;
|
||||
} DoWhileNode;
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
Node *head;
|
||||
|
||||
@@ -225,6 +225,92 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_WHILE:
|
||||
{
|
||||
WhileNode *whl = (WhileNode*) node;
|
||||
|
||||
/*
|
||||
* start:
|
||||
* <condition>
|
||||
* JUMPIFNOTANDPOP end
|
||||
* <body>
|
||||
* JUMP start
|
||||
* end:
|
||||
*/
|
||||
|
||||
Promise *start_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
Promise *end_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
|
||||
if(start_offset == NULL || end_offset == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(start_offset, &temp, sizeof(temp));
|
||||
|
||||
if(!emit_instr_for_node(exeb, whl->condition, error))
|
||||
return 0;
|
||||
|
||||
Operand op = { .type = OPTP_PROMISE, .as_promise = end_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, whl->condition->offset, whl->condition->length))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, whl->body, error))
|
||||
return 0;
|
||||
|
||||
if(whl->body->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, whl->body->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
op = (Operand) { .type = OPTP_PROMISE, .as_promise = start_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(end_offset, &temp, sizeof(temp));
|
||||
|
||||
Promise_Free(start_offset);
|
||||
Promise_Free( end_offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_DOWHILE:
|
||||
{
|
||||
DoWhileNode *dowhl = (DoWhileNode*) node;
|
||||
|
||||
/*
|
||||
* start:
|
||||
* <body>
|
||||
* <condition>
|
||||
* JUMPIFANDPOP start
|
||||
*/
|
||||
|
||||
long long int start = ExeBuilder_InstrCount(exeb);
|
||||
|
||||
if(!emit_instr_for_node(exeb, dowhl->body, error))
|
||||
return 0;
|
||||
|
||||
if(dowhl->body->kind == NODE_EXPR)
|
||||
{
|
||||
Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, dowhl->body->offset, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!emit_instr_for_node(exeb, dowhl->condition, error))
|
||||
return 0;
|
||||
|
||||
Operand op = { .type = OPTP_INT, .as_int = start };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFANDPOP, &op, 1, dowhl->condition->offset, dowhl->condition->length))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case NODE_COMP:
|
||||
{
|
||||
CompoundNode *comp = (CompoundNode*) node;
|
||||
|
||||
+174
-3
@@ -31,6 +31,8 @@ typedef enum {
|
||||
TKWTRUE,
|
||||
TKWFALSE,
|
||||
TKWRETURN,
|
||||
TKWWHILE,
|
||||
TKWDO,
|
||||
} TokenKind;
|
||||
|
||||
typedef struct Token Token;
|
||||
@@ -54,6 +56,8 @@ static Node *parse_ifelse_statement(Context *ctx);
|
||||
static Node *parse_compound_statement(Context *ctx, TokenKind end);
|
||||
static Node *parse_function_definition(Context *ctx);
|
||||
static Node *parse_postfix_expression(Context *ctx);
|
||||
static Node *parse_while_statement(Context *ctx);
|
||||
static Node *parse_dowhile_statement(Context *ctx);
|
||||
|
||||
static inline _Bool isoper(char c)
|
||||
{
|
||||
@@ -152,6 +156,14 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
||||
{
|
||||
tok->kind = TKWRETURN;
|
||||
}
|
||||
else if(matchstr(str + tok->offset, tok->length, "while"))
|
||||
{
|
||||
tok->kind = TKWWHILE;
|
||||
}
|
||||
else if(matchstr(str + tok->offset, tok->length, "do"))
|
||||
{
|
||||
tok->kind = TKWDO;
|
||||
}
|
||||
|
||||
#undef matchstr
|
||||
}
|
||||
@@ -437,6 +449,12 @@ static Node *parse_statement(Context *ctx)
|
||||
|
||||
case TKWFUN:
|
||||
return parse_function_definition(ctx);
|
||||
|
||||
case TKWWHILE:
|
||||
return parse_while_statement(ctx);
|
||||
|
||||
case TKWDO:
|
||||
return parse_dowhile_statement(ctx);
|
||||
}
|
||||
|
||||
Error_Report(ctx->error, 0, "Got token \"%.*s\" where the start of a statement was expected",
|
||||
@@ -449,10 +467,15 @@ static Node *parse_expression_statement(Context *ctx)
|
||||
assert(ctx != NULL);
|
||||
|
||||
Node *expr = parse_expression(ctx);
|
||||
if(expr == NULL) return NULL;
|
||||
|
||||
// The final statement doesn't need a ';'.
|
||||
if(done(ctx)) return expr;
|
||||
if(expr == NULL)
|
||||
return NULL;
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended right after an expression, where a ';' was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) != ';')
|
||||
{
|
||||
@@ -1378,3 +1401,151 @@ static Node *parse_function_definition(Context *ctx)
|
||||
|
||||
return (Node*) func;
|
||||
}
|
||||
|
||||
static Node *parse_while_statement(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended where a while statement was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) != TKWWHILE)
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a while statement was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Token *while_token = current_token(ctx);
|
||||
assert(while_token != NULL);
|
||||
|
||||
next(ctx); // Consume the "while" keyword.
|
||||
|
||||
Node *condition = parse_expression(ctx);
|
||||
|
||||
if(condition == NULL)
|
||||
return NULL;
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended right after a while loop condition, where a ':' was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) != ':')
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" after a while loop condition, where a ':' was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
next(ctx); // Skip the ':'.
|
||||
|
||||
Node *body = parse_statement(ctx);
|
||||
|
||||
if(body == NULL)
|
||||
return NULL;
|
||||
|
||||
WhileNode *whl;
|
||||
{
|
||||
whl = BPAlloc_Malloc(ctx->alloc, sizeof(WhileNode));
|
||||
|
||||
if(whl == NULL)
|
||||
{
|
||||
// ERROR: No memory.
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
whl->base.kind = NODE_WHILE;
|
||||
whl->base.next = NULL;
|
||||
whl->base.offset = while_token->offset;
|
||||
whl->base.length = ctx->token->offset + ctx->token->length - while_token->offset;
|
||||
whl->condition = condition;
|
||||
whl->body = body;
|
||||
}
|
||||
|
||||
return (Node*) whl;
|
||||
}
|
||||
|
||||
static Node *parse_dowhile_statement(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended where a do-while statement was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) != TKWDO)
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a do-while statement was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Token *do_token = current_token(ctx);
|
||||
assert(do_token != NULL);
|
||||
|
||||
next(ctx); // Consume the "do" keyword.
|
||||
|
||||
Node *body = parse_statement(ctx);
|
||||
|
||||
if(body == NULL)
|
||||
return NULL;
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended right after a do-while body, where the \"while\" keyword was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) != TKWWHILE)
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" after a do-while body, where the \"while\" keyword was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
next(ctx); // Consume the "while" keyword.
|
||||
|
||||
Node *condition = parse_expression(ctx);
|
||||
|
||||
if(condition == NULL)
|
||||
return NULL;
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended right after a do-while condition, where a ';' was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) != ';')
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" after a do-while conditnion, where a ';' was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
next(ctx); // Skip the ';'.
|
||||
|
||||
DoWhileNode *dowhl;
|
||||
{
|
||||
dowhl = BPAlloc_Malloc(ctx->alloc, sizeof(DoWhileNode));
|
||||
|
||||
if(dowhl == NULL)
|
||||
{
|
||||
// ERROR: No memory.
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dowhl->base.kind = NODE_DOWHILE;
|
||||
dowhl->base.next = NULL;
|
||||
dowhl->base.offset = do_token->offset;
|
||||
dowhl->base.length = ctx->token->offset + ctx->token->length - do_token->offset;
|
||||
dowhl->condition = condition;
|
||||
dowhl->body = body;
|
||||
}
|
||||
|
||||
return (Node*) dowhl;
|
||||
}
|
||||
@@ -633,6 +633,38 @@ static _Bool step(Runtime *runtime, Error *error)
|
||||
runtime->frame->index = ops[0].as_int;
|
||||
return 1;
|
||||
|
||||
case OPCODE_JUMPIFANDPOP:
|
||||
{
|
||||
assert(opc == 1);
|
||||
assert(ops[0].type == OPTP_INT);
|
||||
|
||||
long long int target = ops[0].as_int;
|
||||
|
||||
if(runtime->frame->used == 0)
|
||||
{
|
||||
Error_Report(error, 1, "Frame doesn't have enough items on the stack to execute JUMPIFNOTANDPOP");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Object *top = Stack_Top(runtime->stack, 0);
|
||||
|
||||
if(!Runtime_Pop(runtime, error, 1))
|
||||
return 0;
|
||||
|
||||
assert(top != NULL);
|
||||
|
||||
if(!Object_IsBool(top))
|
||||
{
|
||||
Error_Report(error, 1, "JUMPIFNOTANDPOP expected a boolean on the stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Object_ToBool(top, error)) // This can't fail because we know it's a bool.
|
||||
runtime->frame->index = target;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
case OPCODE_JUMPIFNOTANDPOP:
|
||||
{
|
||||
assert(opc == 1);
|
||||
|
||||
Reference in New Issue
Block a user