mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-21 15:00:06 +00:00
added "break" and "continue" statements
a LoopState was added to the CCompilerState struct which keeps track of breaks, start chunk index, and the start scope of the active loop. Also, break.cosmo was added to the examples directory, 'continue' and 'break' work as expected.
This commit is contained in:
parent
31a852a127
commit
a408353c25
26
examples/break.cosmo
Normal file
26
examples/break.cosmo
Normal file
@ -0,0 +1,26 @@
|
||||
// just testing continues and breaks
|
||||
|
||||
for (var x = 0; x < 700; x++) do
|
||||
for (var i = 0; true; i++) do
|
||||
var str = i .. "." .. x
|
||||
if (i == 998) then
|
||||
print(i .. " reached")
|
||||
break
|
||||
end
|
||||
|
||||
print("for cont- " .. str)
|
||||
continue
|
||||
end
|
||||
|
||||
var i = 0
|
||||
while true do
|
||||
var str = i .. "." .. x
|
||||
if (i++ == 1034) then
|
||||
print("done")
|
||||
break
|
||||
end
|
||||
|
||||
print("while cont- " .. str)
|
||||
continue
|
||||
end
|
||||
end
|
@ -5,6 +5,8 @@
|
||||
|
||||
CReservedWord reservedWords[] = {
|
||||
{TOKEN_AND, "and", 3},
|
||||
{TOKEN_BREAK, "break", 5},
|
||||
{TOKEN_CONTINUE, "continue", 8},
|
||||
{TOKEN_DO, "do", 2},
|
||||
{TOKEN_ELSE, "else", 4},
|
||||
{TOKEN_ELSEIF, "elseif", 6},
|
||||
|
@ -44,6 +44,8 @@ typedef enum {
|
||||
|
||||
// keywords & reserved words
|
||||
TOKEN_AND,
|
||||
TOKEN_BREAK,
|
||||
TOKEN_CONTINUE,
|
||||
TOKEN_DO,
|
||||
TOKEN_ELSE,
|
||||
TOKEN_ELSEIF,
|
||||
|
104
src/cparse.c
104
src/cparse.c
@ -20,6 +20,14 @@ typedef struct {
|
||||
bool isLocal;
|
||||
} Upvalue;
|
||||
|
||||
typedef struct {
|
||||
int *breaks; // this array is dynamically allocated
|
||||
int scope; // if -1, there is no loop
|
||||
int startBytecode; // start index in the chunk of the loop
|
||||
int breakCount; // # of breaks to patch
|
||||
int breakCapacity;
|
||||
} LoopState;
|
||||
|
||||
typedef enum {
|
||||
FTYPE_FUNCTION,
|
||||
FTYPE_METHOD, // a function bounded to an object (can use "this" identifer to access the current object :pog:)
|
||||
@ -27,11 +35,12 @@ typedef enum {
|
||||
} FunctionType;
|
||||
|
||||
typedef struct CCompilerState {
|
||||
CObjFunction *function;
|
||||
FunctionType type;
|
||||
|
||||
Local locals[256];
|
||||
Upvalue upvalues[256];
|
||||
LoopState loop;
|
||||
|
||||
CObjFunction *function;
|
||||
FunctionType type;
|
||||
int localCount;
|
||||
int scopeDepth;
|
||||
int pushedValues;
|
||||
@ -98,6 +107,8 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func
|
||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||
ccstate->function->module = pstate->module;
|
||||
|
||||
ccstate->loop.scope = -1; // there is no loop yet
|
||||
|
||||
if (type != FTYPE_SCRIPT)
|
||||
ccstate->function->name = cosmoO_copyString(pstate->state, pstate->previous.start, pstate->previous.length);
|
||||
else
|
||||
@ -737,6 +748,8 @@ ParseRule ruleTable[] = {
|
||||
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
||||
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
|
||||
[TOKEN_AND] = {NULL, and_, PREC_AND},
|
||||
[TOKEN_BREAK] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_CONTINUE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_DO] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_ELSEIF] = {NULL, NULL, PREC_NONE},
|
||||
@ -984,8 +997,30 @@ static void ifStatement(CParseState *pstate) {
|
||||
}
|
||||
}
|
||||
|
||||
static void startLoop(CParseState *pstate) {
|
||||
LoopState *lstate = &pstate->compiler->loop;
|
||||
lstate->scope = pstate->compiler->scopeDepth;
|
||||
lstate->breaks = cosmoM_xmalloc(pstate->state, sizeof(int) * ARRAY_START);
|
||||
lstate->breakCount = 0;
|
||||
lstate->breakCapacity = ARRAY_START;
|
||||
lstate->startBytecode = getChunk(pstate)->count;
|
||||
}
|
||||
|
||||
// this patches all the breaks
|
||||
static void endLoop(CParseState *pstate) {
|
||||
while (pstate->compiler->loop.breakCount > 0) {
|
||||
patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]);
|
||||
}
|
||||
|
||||
cosmoM_freearray(pstate->state, int, pstate->compiler->loop.breaks, pstate->compiler->loop.breakCapacity);
|
||||
}
|
||||
|
||||
static void whileStatement(CParseState *pstate) {
|
||||
LoopState cachedLoop = pstate->compiler->loop;
|
||||
startLoop(pstate);
|
||||
int jumpLocation = getChunk(pstate)->count;
|
||||
|
||||
// get conditional
|
||||
expression(pstate, 1, true);
|
||||
|
||||
consume(pstate, TOKEN_DO, "expected 'do' after conditional expression.");
|
||||
@ -996,8 +1031,11 @@ static void whileStatement(CParseState *pstate) {
|
||||
beginScope(pstate);
|
||||
block(pstate); // parse until 'end'
|
||||
endScope(pstate);
|
||||
|
||||
writeJmpBack(pstate, jumpLocation);
|
||||
|
||||
// patch all the breaks, and restore the previous loop state
|
||||
endLoop(pstate);
|
||||
pstate->compiler->loop = cachedLoop;
|
||||
patchJmp(pstate, exitJump);
|
||||
}
|
||||
|
||||
@ -1123,6 +1161,10 @@ static void forEachLoop(CParseState *pstate) {
|
||||
|
||||
writeu8(pstate, OP_ITER); // checks if stack[top] is iterable and pushes the __next method onto the stack for OP_NEXT to call
|
||||
|
||||
// start loop scope
|
||||
LoopState cachedLoop = pstate->compiler->loop;
|
||||
startLoop(pstate);
|
||||
pstate->compiler->loop.scope--; // scope should actually be 1 less than this
|
||||
int loopStart = getChunk(pstate)->count;
|
||||
|
||||
// OP_NEXT expected a uint8_t after the opcode for how many values __next is expected to return
|
||||
@ -1137,15 +1179,17 @@ static void forEachLoop(CParseState *pstate) {
|
||||
valuePushed(pstate, values);
|
||||
|
||||
// compile loop block
|
||||
beginScope(pstate);
|
||||
block(pstate);
|
||||
endScope(pstate);
|
||||
|
||||
// pop all of the values, OP_NEXT will repopulate them
|
||||
endScope(pstate);
|
||||
|
||||
// write jmp back to the start of the loop
|
||||
writeJmpBack(pstate, loopStart);
|
||||
|
||||
// patch all the breaks, and restore the previous loop state
|
||||
endLoop(pstate);
|
||||
pstate->compiler->loop = cachedLoop;
|
||||
patchJmp(pstate, jmpPatch); // and finally, patch our OP_NEXT
|
||||
|
||||
// remove reserved local
|
||||
@ -1170,8 +1214,11 @@ static void forLoop(CParseState *pstate) {
|
||||
consume(pstate, TOKEN_EOS, "Expected ';' after initializer!");
|
||||
}
|
||||
|
||||
// start loop scope
|
||||
LoopState cachedLoop = pstate->compiler->loop;
|
||||
startLoop(pstate);
|
||||
int loopStart = getChunk(pstate)->count;
|
||||
|
||||
|
||||
// parse conditional
|
||||
int exitJmp = -1;
|
||||
if (!match(pstate, TOKEN_EOS)) {
|
||||
@ -1186,6 +1233,10 @@ static void forLoop(CParseState *pstate) {
|
||||
if (!match(pstate, TOKEN_RIGHT_PAREN)) {
|
||||
int bodyJmp = writeJmp(pstate, OP_JMP);
|
||||
|
||||
// replace stale loop state
|
||||
endLoop(pstate);
|
||||
startLoop(pstate);
|
||||
|
||||
int iteratorStart = getChunk(pstate)->count;
|
||||
expression(pstate, 0, true);
|
||||
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after iterator");
|
||||
@ -1207,9 +1258,44 @@ static void forLoop(CParseState *pstate) {
|
||||
patchJmp(pstate, exitJmp);
|
||||
}
|
||||
|
||||
// patch all the breaks, and restore the previous loop state
|
||||
endLoop(pstate);
|
||||
pstate->compiler->loop = cachedLoop;
|
||||
|
||||
endScope(pstate);
|
||||
}
|
||||
|
||||
static void breakStatement(CParseState *pstate) {
|
||||
if (pstate->compiler->loop.scope == -1) {
|
||||
error(pstate, "'break' cannot be used inside of a loop body!");
|
||||
return;
|
||||
}
|
||||
|
||||
// pop active scoped locals in the loop scope
|
||||
int savedLocals = pstate->compiler->localCount;
|
||||
popLocals(pstate, pstate->compiler->loop.scope);
|
||||
pstate->compiler->localCount = savedLocals;
|
||||
|
||||
// add break to loop
|
||||
cosmoM_growarray(pstate->state, int, pstate->compiler->loop.breaks, pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity);
|
||||
pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP);
|
||||
}
|
||||
|
||||
static void continueStatement(CParseState *pstate) {
|
||||
if (pstate->compiler->loop.scope == -1) {
|
||||
error(pstate, "'continue' cannot be used inside of a loop body!");
|
||||
return;
|
||||
}
|
||||
|
||||
// pop active scoped locals in the loop scope
|
||||
int savedLocals = pstate->compiler->localCount;
|
||||
popLocals(pstate, pstate->compiler->loop.scope);
|
||||
pstate->compiler->localCount = savedLocals;
|
||||
|
||||
// jump to the start of the loop
|
||||
writeJmpBack(pstate, pstate->compiler->loop.startBytecode);
|
||||
}
|
||||
|
||||
static void synchronize(CParseState *pstate) {
|
||||
pstate->panic = false;
|
||||
|
||||
@ -1265,6 +1351,10 @@ static void expressionStatement(CParseState *pstate) {
|
||||
functionDeclaration(pstate);
|
||||
} else if (match(pstate, TOKEN_PROTO)) {
|
||||
_proto(pstate);
|
||||
} else if (match(pstate, TOKEN_BREAK)) {
|
||||
breakStatement(pstate);
|
||||
} else if (match(pstate, TOKEN_CONTINUE)) {
|
||||
continueStatement(pstate);
|
||||
} else if (match(pstate, TOKEN_RETURN)) {
|
||||
returnStatement(pstate);
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user