[Chapter23&Conditionals] - Adding control flow

This commit is contained in:
adnanioricce 2024-11-23 19:52:48 -03:00
parent 8bce027f37
commit 59ba0f61b4
8 changed files with 175 additions and 3 deletions

@ -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

Binary file not shown.

@ -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},

Binary file not shown.

@ -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:

Binary file not shown.

@ -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

Binary file not shown.