mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-21 23:10:05 +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[] = {
|
CReservedWord reservedWords[] = {
|
||||||
{TOKEN_AND, "and", 3},
|
{TOKEN_AND, "and", 3},
|
||||||
|
{TOKEN_BREAK, "break", 5},
|
||||||
|
{TOKEN_CONTINUE, "continue", 8},
|
||||||
{TOKEN_DO, "do", 2},
|
{TOKEN_DO, "do", 2},
|
||||||
{TOKEN_ELSE, "else", 4},
|
{TOKEN_ELSE, "else", 4},
|
||||||
{TOKEN_ELSEIF, "elseif", 6},
|
{TOKEN_ELSEIF, "elseif", 6},
|
||||||
|
@ -44,6 +44,8 @@ typedef enum {
|
|||||||
|
|
||||||
// keywords & reserved words
|
// keywords & reserved words
|
||||||
TOKEN_AND,
|
TOKEN_AND,
|
||||||
|
TOKEN_BREAK,
|
||||||
|
TOKEN_CONTINUE,
|
||||||
TOKEN_DO,
|
TOKEN_DO,
|
||||||
TOKEN_ELSE,
|
TOKEN_ELSE,
|
||||||
TOKEN_ELSEIF,
|
TOKEN_ELSEIF,
|
||||||
|
102
src/cparse.c
102
src/cparse.c
@ -20,6 +20,14 @@ typedef struct {
|
|||||||
bool isLocal;
|
bool isLocal;
|
||||||
} Upvalue;
|
} 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 {
|
typedef enum {
|
||||||
FTYPE_FUNCTION,
|
FTYPE_FUNCTION,
|
||||||
FTYPE_METHOD, // a function bounded to an object (can use "this" identifer to access the current object :pog:)
|
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;
|
} FunctionType;
|
||||||
|
|
||||||
typedef struct CCompilerState {
|
typedef struct CCompilerState {
|
||||||
CObjFunction *function;
|
|
||||||
FunctionType type;
|
|
||||||
|
|
||||||
Local locals[256];
|
Local locals[256];
|
||||||
Upvalue upvalues[256];
|
Upvalue upvalues[256];
|
||||||
|
LoopState loop;
|
||||||
|
|
||||||
|
CObjFunction *function;
|
||||||
|
FunctionType type;
|
||||||
int localCount;
|
int localCount;
|
||||||
int scopeDepth;
|
int scopeDepth;
|
||||||
int pushedValues;
|
int pushedValues;
|
||||||
@ -98,6 +107,8 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func
|
|||||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||||
ccstate->function->module = pstate->module;
|
ccstate->function->module = pstate->module;
|
||||||
|
|
||||||
|
ccstate->loop.scope = -1; // there is no loop yet
|
||||||
|
|
||||||
if (type != FTYPE_SCRIPT)
|
if (type != FTYPE_SCRIPT)
|
||||||
ccstate->function->name = cosmoO_copyString(pstate->state, pstate->previous.start, pstate->previous.length);
|
ccstate->function->name = cosmoO_copyString(pstate->state, pstate->previous.start, pstate->previous.length);
|
||||||
else
|
else
|
||||||
@ -737,6 +748,8 @@ ParseRule ruleTable[] = {
|
|||||||
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
||||||
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
|
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
|
||||||
[TOKEN_AND] = {NULL, and_, PREC_AND},
|
[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_DO] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_ELSEIF] = {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) {
|
static void whileStatement(CParseState *pstate) {
|
||||||
|
LoopState cachedLoop = pstate->compiler->loop;
|
||||||
|
startLoop(pstate);
|
||||||
int jumpLocation = getChunk(pstate)->count;
|
int jumpLocation = getChunk(pstate)->count;
|
||||||
|
|
||||||
|
// get conditional
|
||||||
expression(pstate, 1, true);
|
expression(pstate, 1, true);
|
||||||
|
|
||||||
consume(pstate, TOKEN_DO, "expected 'do' after conditional expression.");
|
consume(pstate, TOKEN_DO, "expected 'do' after conditional expression.");
|
||||||
@ -996,8 +1031,11 @@ static void whileStatement(CParseState *pstate) {
|
|||||||
beginScope(pstate);
|
beginScope(pstate);
|
||||||
block(pstate); // parse until 'end'
|
block(pstate); // parse until 'end'
|
||||||
endScope(pstate);
|
endScope(pstate);
|
||||||
|
|
||||||
writeJmpBack(pstate, jumpLocation);
|
writeJmpBack(pstate, jumpLocation);
|
||||||
|
|
||||||
|
// patch all the breaks, and restore the previous loop state
|
||||||
|
endLoop(pstate);
|
||||||
|
pstate->compiler->loop = cachedLoop;
|
||||||
patchJmp(pstate, exitJump);
|
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
|
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;
|
int loopStart = getChunk(pstate)->count;
|
||||||
|
|
||||||
// OP_NEXT expected a uint8_t after the opcode for how many values __next is expected to return
|
// 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);
|
valuePushed(pstate, values);
|
||||||
|
|
||||||
// compile loop block
|
// compile loop block
|
||||||
beginScope(pstate);
|
|
||||||
block(pstate);
|
block(pstate);
|
||||||
endScope(pstate);
|
|
||||||
|
|
||||||
// pop all of the values, OP_NEXT will repopulate them
|
// pop all of the values, OP_NEXT will repopulate them
|
||||||
endScope(pstate);
|
endScope(pstate);
|
||||||
|
|
||||||
// write jmp back to the start of the loop
|
// write jmp back to the start of the loop
|
||||||
writeJmpBack(pstate, loopStart);
|
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
|
patchJmp(pstate, jmpPatch); // and finally, patch our OP_NEXT
|
||||||
|
|
||||||
// remove reserved local
|
// remove reserved local
|
||||||
@ -1170,6 +1214,9 @@ static void forLoop(CParseState *pstate) {
|
|||||||
consume(pstate, TOKEN_EOS, "Expected ';' after initializer!");
|
consume(pstate, TOKEN_EOS, "Expected ';' after initializer!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start loop scope
|
||||||
|
LoopState cachedLoop = pstate->compiler->loop;
|
||||||
|
startLoop(pstate);
|
||||||
int loopStart = getChunk(pstate)->count;
|
int loopStart = getChunk(pstate)->count;
|
||||||
|
|
||||||
// parse conditional
|
// parse conditional
|
||||||
@ -1186,6 +1233,10 @@ static void forLoop(CParseState *pstate) {
|
|||||||
if (!match(pstate, TOKEN_RIGHT_PAREN)) {
|
if (!match(pstate, TOKEN_RIGHT_PAREN)) {
|
||||||
int bodyJmp = writeJmp(pstate, OP_JMP);
|
int bodyJmp = writeJmp(pstate, OP_JMP);
|
||||||
|
|
||||||
|
// replace stale loop state
|
||||||
|
endLoop(pstate);
|
||||||
|
startLoop(pstate);
|
||||||
|
|
||||||
int iteratorStart = getChunk(pstate)->count;
|
int iteratorStart = getChunk(pstate)->count;
|
||||||
expression(pstate, 0, true);
|
expression(pstate, 0, true);
|
||||||
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after iterator");
|
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after iterator");
|
||||||
@ -1207,9 +1258,44 @@ static void forLoop(CParseState *pstate) {
|
|||||||
patchJmp(pstate, exitJmp);
|
patchJmp(pstate, exitJmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// patch all the breaks, and restore the previous loop state
|
||||||
|
endLoop(pstate);
|
||||||
|
pstate->compiler->loop = cachedLoop;
|
||||||
|
|
||||||
endScope(pstate);
|
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) {
|
static void synchronize(CParseState *pstate) {
|
||||||
pstate->panic = false;
|
pstate->panic = false;
|
||||||
|
|
||||||
@ -1265,6 +1351,10 @@ static void expressionStatement(CParseState *pstate) {
|
|||||||
functionDeclaration(pstate);
|
functionDeclaration(pstate);
|
||||||
} else if (match(pstate, TOKEN_PROTO)) {
|
} else if (match(pstate, TOKEN_PROTO)) {
|
||||||
_proto(pstate);
|
_proto(pstate);
|
||||||
|
} else if (match(pstate, TOKEN_BREAK)) {
|
||||||
|
breakStatement(pstate);
|
||||||
|
} else if (match(pstate, TOKEN_CONTINUE)) {
|
||||||
|
continueStatement(pstate);
|
||||||
} else if (match(pstate, TOKEN_RETURN)) {
|
} else if (match(pstate, TOKEN_RETURN)) {
|
||||||
returnStatement(pstate);
|
returnStatement(pstate);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user