|
@@ -36,6 +36,14 @@ struct Return {
|
|
|
bool hasArgument;
|
|
bool hasArgument;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+typedef struct BreakContinue BreakContinue;
|
|
|
|
|
+
|
|
|
|
|
+struct BreakContinue {
|
|
|
|
|
+ BreakContinue* next;
|
|
|
|
|
+ size_t address;
|
|
|
|
|
+ bool stop;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
typedef struct {
|
|
typedef struct {
|
|
|
Tokenizer* tokenizer;
|
|
Tokenizer* tokenizer;
|
|
|
Code* code;
|
|
Code* code;
|
|
@@ -45,7 +53,9 @@ typedef struct {
|
|
|
Variable* variables;
|
|
Variable* variables;
|
|
|
Function* functions;
|
|
Function* functions;
|
|
|
Return* returns;
|
|
Return* returns;
|
|
|
|
|
+ BreakContinue* loopJumps;
|
|
|
bool inFunction;
|
|
bool inFunction;
|
|
|
|
|
+ i32 inLoop;
|
|
|
} Context;
|
|
} Context;
|
|
|
|
|
|
|
|
#define THROW_ERROR(format, ...) \
|
|
#define THROW_ERROR(format, ...) \
|
|
@@ -191,6 +201,17 @@ static void addReturn(Context* c, i32 address, bool hasArgument) {
|
|
|
c->returns = r;
|
|
c->returns = r;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static void addLoopJump(Context* c, size_t address, bool stop) {
|
|
|
|
|
+ BreakContinue* r = memoryAllocate(sizeof(BreakContinue));
|
|
|
|
|
+ if(r == nullptr) {
|
|
|
|
|
+ THROW_ERROR("Too less memory for break/continue");
|
|
|
|
|
+ }
|
|
|
|
|
+ r->next = c->loopJumps;
|
|
|
|
|
+ r->stop = stop;
|
|
|
|
|
+ r->address = address;
|
|
|
|
|
+ c->loopJumps = r;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
#define CLEAN_LIST(Type, field) \
|
|
#define CLEAN_LIST(Type, field) \
|
|
|
do { \
|
|
do { \
|
|
|
Type* v = c->field; \
|
|
Type* v = c->field; \
|
|
@@ -210,6 +231,7 @@ static void cleanContext(Context* c) {
|
|
|
CLEAN_LIST(Variable, variables);
|
|
CLEAN_LIST(Variable, variables);
|
|
|
CLEAN_LIST(Function, functions);
|
|
CLEAN_LIST(Function, functions);
|
|
|
cleanReturns(c);
|
|
cleanReturns(c);
|
|
|
|
|
+ CLEAN_LIST(BreakContinue, loopJumps);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[[noreturn]] static void unexpectedToken(Context* c, Token token, int line) {
|
|
[[noreturn]] static void unexpectedToken(Context* c, Token token, int line) {
|
|
@@ -379,6 +401,47 @@ static void compileSetVariable(Context* c, const char* name, bool forceGlobal) {
|
|
|
codePushInstructionI32(c, SET_VARIABLE, addVariable(c, name, forceGlobal));
|
|
codePushInstructionI32(c, SET_VARIABLE, addVariable(c, name, forceGlobal));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static void compileFor(Context* c) {
|
|
|
|
|
+ c->inLoop++;
|
|
|
|
|
+ Token t = consumeToken(c, TT_LITERAL);
|
|
|
|
|
+ compileSetVariable(c, t.stringValue, false);
|
|
|
|
|
+ consumeToken(c, TT_TO);
|
|
|
|
|
+ size_t checkStart = codeGetWritePosition(c);
|
|
|
|
|
+ compileExpression(c);
|
|
|
|
|
+ consumeNewline(c);
|
|
|
|
|
+ compileReadVariable(c, t, false);
|
|
|
|
|
+ codePushInstruction(c, GREATER_OR_EQUAL);
|
|
|
|
|
+ size_t end = codePushInstructionI32(c, JUMP_ON_0, 0);
|
|
|
|
|
+
|
|
|
|
|
+ BreakContinue* currentJumps = c->loopJumps;
|
|
|
|
|
+ while(!peekToken(c, TT_END)) {
|
|
|
|
|
+ compileLine(c, tokenizerNext(c->tokenizer));
|
|
|
|
|
+ }
|
|
|
|
|
+ consumeToken(c, TT_END);
|
|
|
|
|
+ consumeNewline(c);
|
|
|
|
|
+
|
|
|
|
|
+ i32 continueStart = (i32)codeGetWritePosition(c);
|
|
|
|
|
+ compileReadVariable(c, t, false);
|
|
|
|
|
+ codePushInstructionI32(c, PUSH_INT32, 1);
|
|
|
|
|
+ codePushInstruction(c, ADD);
|
|
|
|
|
+ codePushInstructionI32(
|
|
|
|
|
+ c, SET_VARIABLE, addVariable(c, t.stringValue, false));
|
|
|
|
|
+
|
|
|
|
|
+ codePushInstructionI32(c, JUMP, (i32)checkStart);
|
|
|
|
|
+ i32 endStart = (i32)codeGetWritePosition(c);
|
|
|
|
|
+ codeRewriteI32(c, end, endStart);
|
|
|
|
|
+
|
|
|
|
|
+ while(c->loopJumps != currentJumps) {
|
|
|
|
|
+ BreakContinue* next = c->loopJumps->next;
|
|
|
|
|
+ codeRewriteI32(
|
|
|
|
|
+ c, c->loopJumps->address,
|
|
|
|
|
+ c->loopJumps->stop ? endStart : continueStart);
|
|
|
|
|
+ memoryFree(c->loopJumps);
|
|
|
|
|
+ c->loopJumps = next;
|
|
|
|
|
+ }
|
|
|
|
|
+ c->inLoop--;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static i32 compileFunctionArguments(Context* c) {
|
|
static i32 compileFunctionArguments(Context* c) {
|
|
|
consumeToken(c, TT_OPEN_ROUND_BRACKET);
|
|
consumeToken(c, TT_OPEN_ROUND_BRACKET);
|
|
|
i32 vars = 0;
|
|
i32 vars = 0;
|
|
@@ -500,6 +563,22 @@ static void compileLine(Context* c, Token token) {
|
|
|
c->line++;
|
|
c->line++;
|
|
|
} else if(token.type == TT_IF) {
|
|
} else if(token.type == TT_IF) {
|
|
|
compileIf(c);
|
|
compileIf(c);
|
|
|
|
|
+ } else if(token.type == TT_BREAK) {
|
|
|
|
|
+ if(c->inLoop <= 0) {
|
|
|
|
|
+ THROW_ERROR("break without loop");
|
|
|
|
|
+ }
|
|
|
|
|
+ size_t pos = codePushInstructionI32(c, JUMP, 9999);
|
|
|
|
|
+ addLoopJump(c, pos, true);
|
|
|
|
|
+ consumeNewline(c);
|
|
|
|
|
+ } else if(token.type == TT_CONTINUE) {
|
|
|
|
|
+ if(c->inLoop <= 0) {
|
|
|
|
|
+ THROW_ERROR("continue without loop");
|
|
|
|
|
+ }
|
|
|
|
|
+ size_t pos = codePushInstructionI32(c, JUMP, 9999);
|
|
|
|
|
+ addLoopJump(c, pos, false);
|
|
|
|
|
+ consumeNewline(c);
|
|
|
|
|
+ } else if(token.type == TT_FOR) {
|
|
|
|
|
+ compileFor(c);
|
|
|
} else if(token.type == TT_FUNCTION) {
|
|
} else if(token.type == TT_FUNCTION) {
|
|
|
compileFunction(c);
|
|
compileFunction(c);
|
|
|
} else if(token.type == TT_RETURN) {
|
|
} else if(token.type == TT_RETURN) {
|