[Chapter23&Conditionals] - Adding control flow
This commit is contained in:
parent
8bce027f37
commit
59ba0f61b4
@ -20,6 +20,9 @@ typedef enum {
|
|||||||
OP_LESS,
|
OP_LESS,
|
||||||
OP_NEGATE,
|
OP_NEGATE,
|
||||||
OP_PRINT,
|
OP_PRINT,
|
||||||
|
OP_JUMP,
|
||||||
|
OP_JUMP_IF_FALSE,
|
||||||
|
OP_LOOP,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
OP_ADD,
|
OP_ADD,
|
||||||
OP_SUBTRACT,
|
OP_SUBTRACT,
|
||||||
|
BIN
clox/clox
BIN
clox/clox
Binary file not shown.
140
clox/compiler.c
140
clox/compiler.c
@ -122,6 +122,23 @@ static void emitBytes(uint8_t byte1, uint8_t byte2) {
|
|||||||
emitByte(byte2);
|
emitByte(byte2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void emitLoop(int loopStart) {
|
||||||
|
emitByte(OP_LOOP);
|
||||||
|
|
||||||
|
int offset = currentChunk()->count - loopStart + 2;
|
||||||
|
if (offset > UINT16_MAX) error("Loop body too large.");
|
||||||
|
|
||||||
|
emitByte((offset >> 8) & 0xff);
|
||||||
|
emitByte(offset & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int emitJump(uint8_t instruction) {
|
||||||
|
emitByte(instruction);
|
||||||
|
emitByte(0xff);
|
||||||
|
emitByte(0xff);
|
||||||
|
return currentChunk()->count - 2;
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t makeConstant(Value value) {
|
static uint8_t makeConstant(Value value) {
|
||||||
int constant = addConstant(currentChunk(), value);
|
int constant = addConstant(currentChunk(), value);
|
||||||
if (constant > UINT8_MAX) {
|
if (constant > UINT8_MAX) {
|
||||||
@ -140,6 +157,18 @@ static void emitConstant(Value value) {
|
|||||||
emitBytes(OP_CONSTANT, makeConstant(value));
|
emitBytes(OP_CONSTANT, makeConstant(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void patchJump(int offset) {
|
||||||
|
// -2 to adjust for the bytecode for the jump offset itself.
|
||||||
|
int jump = currentChunk()->count - offset - 2;
|
||||||
|
|
||||||
|
if (jump > UINT16_MAX) {
|
||||||
|
error("Too much code to jump over.");
|
||||||
|
}
|
||||||
|
|
||||||
|
currentChunk()->code[offset] = (jump >> 8) & 0xff;
|
||||||
|
currentChunk()->code[offset + 1] = jump & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
static void initCompiler(Compiler* compiler) {
|
static void initCompiler(Compiler* compiler) {
|
||||||
compiler->localCount = 0;
|
compiler->localCount = 0;
|
||||||
compiler->scopeDepth = 0;
|
compiler->scopeDepth = 0;
|
||||||
@ -243,6 +272,15 @@ static void defineVariable(uint8_t global) {
|
|||||||
emitBytes(OP_DEFINE_GLOBAL, global);
|
emitBytes(OP_DEFINE_GLOBAL, global);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void and_(bool canAssign) {
|
||||||
|
int endJump = emitJump(OP_JUMP_IF_FALSE);
|
||||||
|
|
||||||
|
emitByte(OP_POP);
|
||||||
|
parsePrecedence(PREC_AND);
|
||||||
|
|
||||||
|
patchJump(endJump);
|
||||||
|
}
|
||||||
|
|
||||||
static void binary(bool canAssign) {
|
static void binary(bool canAssign) {
|
||||||
TokenType operatorType = parser.previous.type;
|
TokenType operatorType = parser.previous.type;
|
||||||
ParseRule* rule = getRule(operatorType);
|
ParseRule* rule = getRule(operatorType);
|
||||||
@ -305,12 +343,87 @@ static void expressionStatement() {
|
|||||||
emitByte(OP_POP);
|
emitByte(OP_POP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void forStatement() {
|
||||||
|
beginScope();
|
||||||
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'for'.");
|
||||||
|
if (match(TOKEN_SEMICOLON)) {
|
||||||
|
// No initializer.
|
||||||
|
} else if (match(TOKEN_VAR)) {
|
||||||
|
varDeclaration();
|
||||||
|
} else {
|
||||||
|
expressionStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
int loopStart = currentChunk()->count;
|
||||||
|
int exitJump = -1;
|
||||||
|
if (!match(TOKEN_SEMICOLON)) {
|
||||||
|
expression();
|
||||||
|
consume(TOKEN_SEMICOLON, "Expect ';' after loop condition.");
|
||||||
|
|
||||||
|
// Jump out of the loop if the condition is false.
|
||||||
|
exitJump = emitJump(OP_JUMP_IF_FALSE);
|
||||||
|
emitByte(OP_POP); // Condition.
|
||||||
|
}
|
||||||
|
if (!match(TOKEN_RIGHT_PAREN)) {
|
||||||
|
int bodyJump = emitJump(OP_JUMP);
|
||||||
|
int incrementStart = currentChunk()->count;
|
||||||
|
expression();
|
||||||
|
emitByte(OP_POP);
|
||||||
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after for clauses.");
|
||||||
|
|
||||||
|
emitLoop(loopStart);
|
||||||
|
loopStart = incrementStart;
|
||||||
|
patchJump(bodyJump);
|
||||||
|
}
|
||||||
|
statement();
|
||||||
|
emitLoop(loopStart);
|
||||||
|
|
||||||
|
if (exitJump != -1) {
|
||||||
|
patchJump(exitJump);
|
||||||
|
emitByte(OP_POP); // Condition.
|
||||||
|
}
|
||||||
|
|
||||||
|
endScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ifStatement() {
|
||||||
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'.");
|
||||||
|
expression();
|
||||||
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
|
||||||
|
|
||||||
|
int thenJump = emitJump(OP_JUMP_IF_FALSE);
|
||||||
|
emitByte(OP_POP);
|
||||||
|
statement();
|
||||||
|
|
||||||
|
int elseJump = emitJump(OP_JUMP);
|
||||||
|
patchJump(thenJump);
|
||||||
|
emitByte(OP_POP);
|
||||||
|
|
||||||
|
if (match(TOKEN_ELSE)) statement();
|
||||||
|
patchJump(elseJump);
|
||||||
|
}
|
||||||
|
|
||||||
static void printStatement() {
|
static void printStatement() {
|
||||||
expression();
|
expression();
|
||||||
consume(TOKEN_SEMICOLON, "Expect ';' after value.");
|
consume(TOKEN_SEMICOLON, "Expect ';' after value.");
|
||||||
emitByte(OP_PRINT);
|
emitByte(OP_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void whileStatement() {
|
||||||
|
int loopStart = currentChunk()->count;
|
||||||
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
|
||||||
|
expression();
|
||||||
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
|
||||||
|
|
||||||
|
int exitJump = emitJump(OP_JUMP_IF_FALSE);
|
||||||
|
emitByte(OP_POP);
|
||||||
|
statement();
|
||||||
|
emitLoop(loopStart);
|
||||||
|
|
||||||
|
patchJump(exitJump);
|
||||||
|
emitByte(OP_POP);
|
||||||
|
}
|
||||||
|
|
||||||
static void synchronize() {
|
static void synchronize() {
|
||||||
parser.panicMode = false;
|
parser.panicMode = false;
|
||||||
|
|
||||||
@ -349,6 +462,18 @@ static void statement() {
|
|||||||
if (match(TOKEN_PRINT)) {
|
if (match(TOKEN_PRINT)) {
|
||||||
printStatement();
|
printStatement();
|
||||||
}
|
}
|
||||||
|
else if (match(TOKEN_FOR)) {
|
||||||
|
forStatement();
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (match(TOKEN_IF)) {
|
||||||
|
ifStatement();
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (match(TOKEN_WHILE)) {
|
||||||
|
whileStatement();
|
||||||
|
|
||||||
|
}
|
||||||
else if (match(TOKEN_LEFT_BRACE)) {
|
else if (match(TOKEN_LEFT_BRACE)) {
|
||||||
beginScope();
|
beginScope();
|
||||||
block();
|
block();
|
||||||
@ -369,6 +494,17 @@ static void number(bool canAssign) {
|
|||||||
emitConstant(NUMBER_VAL(value));
|
emitConstant(NUMBER_VAL(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void or_(bool canAssign) {
|
||||||
|
int elseJump = emitJump(OP_JUMP_IF_FALSE);
|
||||||
|
int endJump = emitJump(OP_JUMP);
|
||||||
|
|
||||||
|
patchJump(elseJump);
|
||||||
|
emitByte(OP_POP);
|
||||||
|
|
||||||
|
parsePrecedence(PREC_OR);
|
||||||
|
patchJump(endJump);
|
||||||
|
}
|
||||||
|
|
||||||
static void string(bool canAssign) {
|
static void string(bool canAssign) {
|
||||||
emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
|
emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
|
||||||
parser.previous.length - 2)));
|
parser.previous.length - 2)));
|
||||||
@ -458,7 +594,7 @@ ParseRule rules[] = {
|
|||||||
[TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE},
|
[TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE},
|
||||||
[TOKEN_STRING] = {string, NULL, PREC_NONE},
|
[TOKEN_STRING] = {string, NULL, PREC_NONE},
|
||||||
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
|
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
|
||||||
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
|
[TOKEN_AND] = {NULL, and_, PREC_AND},
|
||||||
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
|
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
|
||||||
@ -466,7 +602,7 @@ ParseRule rules[] = {
|
|||||||
[TOKEN_FUN] = {NULL, NULL, PREC_NONE},
|
[TOKEN_FUN] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_NIL] = {literal, NULL, PREC_NONE},
|
[TOKEN_NIL] = {literal, NULL, PREC_NONE},
|
||||||
[TOKEN_OR] = {NULL, NULL, PREC_NONE},
|
[TOKEN_OR] = {NULL, or_, PREC_OR},
|
||||||
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
||||||
|
BIN
clox/compiler.o
BIN
clox/compiler.o
Binary file not shown.
15
clox/debug.c
15
clox/debug.c
@ -29,6 +29,15 @@ static int byteInstruction(const char* name, Chunk* chunk,
|
|||||||
return offset + 2;
|
return offset + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int jumpInstruction(const char* name, int sign,
|
||||||
|
Chunk* chunk, int offset) {
|
||||||
|
uint16_t jump = (uint16_t)(chunk->code[offset + 1] << 8);
|
||||||
|
jump |= chunk->code[offset + 2];
|
||||||
|
printf("%-16s %4d -> %d\n", name, offset,
|
||||||
|
offset + 3 + sign * jump);
|
||||||
|
return offset + 3;
|
||||||
|
}
|
||||||
|
|
||||||
int disassembleInstruction(Chunk* chunk, int offset) {
|
int disassembleInstruction(Chunk* chunk, int offset) {
|
||||||
printf("%04d ", offset);
|
printf("%04d ", offset);
|
||||||
|
|
||||||
@ -81,6 +90,12 @@ int disassembleInstruction(Chunk* chunk, int offset) {
|
|||||||
return simpleInstruction("OP_NOT", offset);
|
return simpleInstruction("OP_NOT", offset);
|
||||||
case OP_PRINT:
|
case OP_PRINT:
|
||||||
return simpleInstruction("OP_PRINT", offset);
|
return simpleInstruction("OP_PRINT", offset);
|
||||||
|
case OP_JUMP:
|
||||||
|
return jumpInstruction("OP_JUMP", 1, chunk, offset);
|
||||||
|
case OP_JUMP_IF_FALSE:
|
||||||
|
return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset);
|
||||||
|
case OP_LOOP:
|
||||||
|
return jumpInstruction("OP_LOOP", -1, chunk, offset);
|
||||||
case OP_RETURN:
|
case OP_RETURN:
|
||||||
return simpleInstruction("OP_RETURN", offset);
|
return simpleInstruction("OP_RETURN", offset);
|
||||||
default:
|
default:
|
||||||
|
BIN
clox/debug.o
BIN
clox/debug.o
Binary file not shown.
20
clox/vm.c
20
clox/vm.c
@ -72,6 +72,8 @@ static void concatenate() {
|
|||||||
static InterpretResult run() {
|
static InterpretResult run() {
|
||||||
#define READ_BYTE() (*vm.ip++)
|
#define READ_BYTE() (*vm.ip++)
|
||||||
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
|
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
|
||||||
|
#define READ_SHORT() \
|
||||||
|
(vm.ip += 2, (uint16_t)((vm.ip[-2] << 8) | vm.ip[-1]))
|
||||||
#define READ_STRING() AS_STRING(READ_CONSTANT())
|
#define READ_STRING() AS_STRING(READ_CONSTANT())
|
||||||
#define BINARY_OP(valueType, op) \
|
#define BINARY_OP(valueType, op) \
|
||||||
do { \
|
do { \
|
||||||
@ -183,6 +185,21 @@ static InterpretResult run() {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_JUMP: {
|
||||||
|
uint16_t offset = READ_SHORT();
|
||||||
|
vm.ip += offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_JUMP_IF_FALSE: {
|
||||||
|
uint16_t offset = READ_SHORT();
|
||||||
|
if (isFalsey(peek(0))) vm.ip += offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_LOOP: {
|
||||||
|
uint16_t offset = READ_SHORT();
|
||||||
|
vm.ip -= offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
// printValue(pop());
|
// printValue(pop());
|
||||||
// printf("\n");
|
// printf("\n");
|
||||||
@ -191,10 +208,11 @@ static InterpretResult run() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#undef READ_BYTE
|
||||||
|
#undef READ_SHORT
|
||||||
#undef READ_CONSTANT
|
#undef READ_CONSTANT
|
||||||
#undef READ_STRING
|
#undef READ_STRING
|
||||||
#undef BINARY_OP
|
#undef BINARY_OP
|
||||||
#undef READ_BYTE
|
|
||||||
}
|
}
|
||||||
InterpretResult interpret(const char* source) {
|
InterpretResult interpret(const char* source) {
|
||||||
Chunk chunk;
|
Chunk chunk;
|
||||||
|
BIN
clox/vm.o
BIN
clox/vm.o
Binary file not shown.
Loading…
Reference in New Issue
Block a user