Chapter 21.1 - 21.3
This commit is contained in:
parent
aa41f26a26
commit
fcbea5f617
6 changed files with 132 additions and 3 deletions
|
@ -9,6 +9,8 @@ typedef enum {
|
||||||
OP_NIL,
|
OP_NIL,
|
||||||
OP_TRUE,
|
OP_TRUE,
|
||||||
OP_FALSE,
|
OP_FALSE,
|
||||||
|
OP_POP,
|
||||||
|
OP_DEFINE_GLOBAL,
|
||||||
OP_EQUAL,
|
OP_EQUAL,
|
||||||
OP_GREATER,
|
OP_GREATER,
|
||||||
OP_LESS,
|
OP_LESS,
|
||||||
|
@ -18,6 +20,7 @@ typedef enum {
|
||||||
OP_DIVIDE,
|
OP_DIVIDE,
|
||||||
OP_NOT,
|
OP_NOT,
|
||||||
OP_NEGATE,
|
OP_NEGATE,
|
||||||
|
OP_PRINT,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,15 @@ static void consume(TokenType type, const char *message) {
|
||||||
errorAtCurrent(message);
|
errorAtCurrent(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool check(TokenType type) { return parser.current.type == type; }
|
||||||
|
|
||||||
|
static bool match(TokenType type) {
|
||||||
|
if (!check(type))
|
||||||
|
return false;
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void emitByte(uint8_t byte) {
|
static void emitByte(uint8_t byte) {
|
||||||
writeChunk(currentChunk(), byte, parser.previous.line);
|
writeChunk(currentChunk(), byte, parser.previous.line);
|
||||||
}
|
}
|
||||||
|
@ -123,8 +132,12 @@ static void endCompiler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expression();
|
static void expression();
|
||||||
|
static void statement();
|
||||||
|
static void declaration();
|
||||||
static ParseRule *getRule(TokenType type);
|
static ParseRule *getRule(TokenType type);
|
||||||
static void parsePrecedence(Precedence precedence);
|
static void parsePrecedence(Precedence precedence);
|
||||||
|
static uint8_t parseVariable(const char *name);
|
||||||
|
static void defineVariable(uint8_t global);
|
||||||
|
|
||||||
static void binary() {
|
static void binary() {
|
||||||
TokenType operatorType = parser.previous.type;
|
TokenType operatorType = parser.previous.type;
|
||||||
|
@ -185,6 +198,74 @@ static void literal() {
|
||||||
|
|
||||||
static void expression() { parsePrecedence(PREC_ASSIGNMENT); }
|
static void expression() { parsePrecedence(PREC_ASSIGNMENT); }
|
||||||
|
|
||||||
|
static void varDeclaration() {
|
||||||
|
uint8_t global = parseVariable("Expect variable name.");
|
||||||
|
|
||||||
|
if (match(TOKEN_EQUAL)) {
|
||||||
|
expression();
|
||||||
|
} else {
|
||||||
|
emitByte(OP_NIL);
|
||||||
|
}
|
||||||
|
consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration.");
|
||||||
|
|
||||||
|
defineVariable(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expressionStatement() {
|
||||||
|
expression();
|
||||||
|
consume(TOKEN_SEMICOLON, "Expect ';' after expression.");
|
||||||
|
emitByte(OP_POP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printStatement() {
|
||||||
|
expression();
|
||||||
|
consume(TOKEN_SEMICOLON, "Expect ';' after value.");
|
||||||
|
emitByte(OP_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void synchronize() {
|
||||||
|
parser.panicMode = false;
|
||||||
|
|
||||||
|
while (parser.current.type != TOKEN_EOF) {
|
||||||
|
if (parser.previous.type == TOKEN_SEMICOLON)
|
||||||
|
return;
|
||||||
|
switch (parser.current.type) {
|
||||||
|
case TOKEN_CLASS:
|
||||||
|
case TOKEN_FUN:
|
||||||
|
case TOKEN_VAR:
|
||||||
|
case TOKEN_FOR:
|
||||||
|
case TOKEN_IF:
|
||||||
|
case TOKEN_WHILE:
|
||||||
|
case TOKEN_PRINT:
|
||||||
|
case TOKEN_RETURN:
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:; /* Do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void declaration() {
|
||||||
|
if (match(TOKEN_VAR)) {
|
||||||
|
varDeclaration();
|
||||||
|
} else {
|
||||||
|
statement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.panicMode)
|
||||||
|
synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void statement() {
|
||||||
|
if (match(TOKEN_PRINT)) {
|
||||||
|
printStatement();
|
||||||
|
} else {
|
||||||
|
expressionStatement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void number() {
|
static void number() {
|
||||||
double value = strtod(parser.previous.start, NULL);
|
double value = strtod(parser.previous.start, NULL);
|
||||||
emitConstant(NUMBER_VAL(value));
|
emitConstant(NUMBER_VAL(value));
|
||||||
|
@ -279,6 +360,19 @@ static void parsePrecedence(Precedence precedence) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t identifierConstant(Token *name) {
|
||||||
|
return makeConstant(OBJ_VAL(copyString(name->start, name->length)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t parseVariable(const char *errorMessage) {
|
||||||
|
consume(TOKEN_IDENTIFIER, errorMessage);
|
||||||
|
return identifierConstant(&parser.previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void defineVariable(uint8_t global) {
|
||||||
|
emitBytes(OP_DEFINE_GLOBAL, global);
|
||||||
|
}
|
||||||
|
|
||||||
static ParseRule *getRule(TokenType type) { return &rules[type]; }
|
static ParseRule *getRule(TokenType type) { return &rules[type]; }
|
||||||
|
|
||||||
bool compile(const char *source, Chunk *chunk) {
|
bool compile(const char *source, Chunk *chunk) {
|
||||||
|
@ -289,8 +383,11 @@ bool compile(const char *source, Chunk *chunk) {
|
||||||
parser.panicMode = false;
|
parser.panicMode = false;
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
expression();
|
|
||||||
consume(TOKEN_EOF, "Expect end of expression.");
|
while (!match(TOKEN_EOF)) {
|
||||||
|
declaration();
|
||||||
|
}
|
||||||
|
|
||||||
endCompiler();
|
endCompiler();
|
||||||
return !parser.hadError;
|
return !parser.hadError;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,10 @@ int disassembleInstruction(Chunk *chunk, int offset) {
|
||||||
return simpleInstruction("OP_TRUE", offset);
|
return simpleInstruction("OP_TRUE", offset);
|
||||||
case OP_FALSE:
|
case OP_FALSE:
|
||||||
return simpleInstruction("OP_FALSE", offset);
|
return simpleInstruction("OP_FALSE", offset);
|
||||||
|
case OP_POP:
|
||||||
|
return simpleInstruction("OP_POP", offset);
|
||||||
|
case OP_DEFINE_GLOBAL:
|
||||||
|
return constantInstruction("OP_DEFINE_GLOBAL", chunk, offset);
|
||||||
case OP_EQUAL:
|
case OP_EQUAL:
|
||||||
return simpleInstruction("OP_EQUAL", offset);
|
return simpleInstruction("OP_EQUAL", offset);
|
||||||
case OP_GREATER:
|
case OP_GREATER:
|
||||||
|
@ -60,6 +64,8 @@ int disassembleInstruction(Chunk *chunk, int offset) {
|
||||||
return simpleInstruction("OP_NOT", offset);
|
return simpleInstruction("OP_NOT", offset);
|
||||||
case OP_NEGATE:
|
case OP_NEGATE:
|
||||||
return simpleInstruction("OP_NEGATE", offset);
|
return simpleInstruction("OP_NEGATE", offset);
|
||||||
|
case OP_PRINT:
|
||||||
|
return simpleInstruction("OP_PRINT", offset);
|
||||||
case OP_RETURN:
|
case OP_RETURN:
|
||||||
return simpleInstruction("OP_RETURN", offset);
|
return simpleInstruction("OP_RETURN", offset);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -66,7 +66,7 @@ int main(int argc, const char *argv[]) {
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
repl();
|
repl();
|
||||||
} else if (argc = 2) {
|
} else if (argc == 2) {
|
||||||
runFile(argv[1]);
|
runFile(argv[1]);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Usage: clox [path]\n");
|
fprintf(stderr, "Usage: clox [path]\n");
|
||||||
|
|
|
@ -29,10 +29,13 @@ static void runtimeError(const char *format, ...) {
|
||||||
void initVM() {
|
void initVM() {
|
||||||
resetStack();
|
resetStack();
|
||||||
vm.objects = NULL;
|
vm.objects = NULL;
|
||||||
|
|
||||||
|
initTable(&vm.globals);
|
||||||
initTable(&vm.strings);
|
initTable(&vm.strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeVM() {
|
void freeVM() {
|
||||||
|
freeTable(&vm.globals);
|
||||||
freeTable(&vm.strings);
|
freeTable(&vm.strings);
|
||||||
freeObjects();
|
freeObjects();
|
||||||
}
|
}
|
||||||
|
@ -70,6 +73,7 @@ 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_STRING() AS_STRING(READ_CONSTANT())
|
||||||
#define BINARY_OP(valueType, op) \
|
#define BINARY_OP(valueType, op) \
|
||||||
do { \
|
do { \
|
||||||
if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
|
if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
|
||||||
|
@ -108,6 +112,15 @@ static InterpretResult run() {
|
||||||
case OP_FALSE:
|
case OP_FALSE:
|
||||||
push(BOOL_VAL(false));
|
push(BOOL_VAL(false));
|
||||||
break;
|
break;
|
||||||
|
case OP_POP:
|
||||||
|
pop();
|
||||||
|
break;
|
||||||
|
case OP_DEFINE_GLOBAL: {
|
||||||
|
ObjString *name = READ_STRING();
|
||||||
|
tableSet(&vm.globals, name, peek(0));
|
||||||
|
pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_EQUAL: {
|
case OP_EQUAL: {
|
||||||
Value b = pop();
|
Value b = pop();
|
||||||
Value a = pop();
|
Value a = pop();
|
||||||
|
@ -152,7 +165,15 @@ static InterpretResult run() {
|
||||||
}
|
}
|
||||||
push(NUMBER_VAL(-AS_NUMBER(pop())));
|
push(NUMBER_VAL(-AS_NUMBER(pop())));
|
||||||
break;
|
break;
|
||||||
|
case OP_PRINT: {
|
||||||
|
printValue(pop());
|
||||||
|
printf("\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
|
/* The book said to remove this, but when I do I get an infinite loop and
|
||||||
|
then a segfault because it keeps trying to add the constant 1 to the
|
||||||
|
stack. */
|
||||||
printValue(pop());
|
printValue(pop());
|
||||||
printf("\n");
|
printf("\n");
|
||||||
return INTERPRET_OK;
|
return INTERPRET_OK;
|
||||||
|
@ -162,6 +183,7 @@ static InterpretResult run() {
|
||||||
|
|
||||||
#undef READ_BYTE
|
#undef READ_BYTE
|
||||||
#undef READ_CONSTANT
|
#undef READ_CONSTANT
|
||||||
|
#undef READ_STRING
|
||||||
#undef BINARY_OP
|
#undef BINARY_OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ typedef struct {
|
||||||
uint8_t *ip;
|
uint8_t *ip;
|
||||||
Value stack[STACK_MAX];
|
Value stack[STACK_MAX];
|
||||||
Value *stackTop;
|
Value *stackTop;
|
||||||
|
Table globals;
|
||||||
Table strings;
|
Table strings;
|
||||||
Obj *objects;
|
Obj *objects;
|
||||||
} VM;
|
} VM;
|
||||||
|
|
Loading…
Reference in a new issue