added while and do-while loops

This commit is contained in:
cozis
2021-11-03 01:25:18 +00:00
parent 035649bd63
commit c0d788656e
7 changed files with 328 additions and 7 deletions
+1
View File
@@ -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}},
};
+1
View File
@@ -40,6 +40,7 @@ typedef enum {
OPCODE_PUSHNNE,
OPCODE_PUSHFUN,
OPCODE_RETURN,
OPCODE_JUMPIFANDPOP,
OPCODE_JUMPIFNOTANDPOP,
OPCODE_JUMP,
} Opcode;
+18 -4
View File
@@ -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;
+86
View File
@@ -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
View File
@@ -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;
if(expr == NULL)
return NULL;
// The final statement doesn't need a ';'.
if(done(ctx)) return expr;
if(done(ctx))
{
Error_Report(ctx->error, 0, "Source ended right after an expression, where a ';' was expected");
return NULL;
}
if(current(ctx) != ';')
{
@@ -1377,4 +1400,152 @@ 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;
}
+32
View File
@@ -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);