[Chapter17&CompilingExpr] - Chapter 17 follow up
This commit is contained in:
parent
23dada12cc
commit
4c96750dbe
BIN
clox/clox
Executable file
BIN
clox/clox
Executable file
Binary file not shown.
@ -6,5 +6,5 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEBUG_TRACE_EXECUTION
|
||||
|
||||
#define DEBUG_PRINT_CODE
|
||||
#endif
|
||||
|
250
clox/compiler.c
250
clox/compiler.c
@ -1,22 +1,246 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "compiler.h"
|
||||
#include "scanner.h"
|
||||
|
||||
void compile(const char* source) {
|
||||
initScanner(source);
|
||||
int line = -1;
|
||||
for (;;) {
|
||||
Token token = scanToken();
|
||||
if (token.line != line) {
|
||||
printf("%4d ", token.line);
|
||||
line = token.line;
|
||||
} else {
|
||||
printf(" | ");
|
||||
}
|
||||
printf("%2d '%.*s'\n", token.type, token.length, token.start);
|
||||
#ifdef DEBUG_PRINT_CODE
|
||||
#include "debug.h"
|
||||
#endif
|
||||
|
||||
if (token.type == TOKEN_EOF) break;
|
||||
typedef struct {
|
||||
Token current;
|
||||
Token previous;
|
||||
bool hadError;
|
||||
bool panicMode;
|
||||
} Parser;
|
||||
|
||||
typedef enum {
|
||||
PREC_NONE,
|
||||
PREC_ASSIGNMENT, // =
|
||||
PREC_OR, // or
|
||||
PREC_AND, // and
|
||||
PREC_EQUALITY, // == !=
|
||||
PREC_COMPARISON, // < > <= >=
|
||||
PREC_TERM, // + -
|
||||
PREC_FACTOR, // * /
|
||||
PREC_UNARY, // ! -
|
||||
PREC_CALL, // . ()
|
||||
PREC_PRIMARY
|
||||
} Precedence;
|
||||
|
||||
typedef void (*ParseFn)();
|
||||
|
||||
typedef struct {
|
||||
ParseFn prefix;
|
||||
ParseFn infix;
|
||||
Precedence precedence;
|
||||
} ParseRule;
|
||||
|
||||
Parser parser;
|
||||
Chunk* compilingChunk;
|
||||
|
||||
static Chunk* currentChunk() {
|
||||
return compilingChunk;
|
||||
}
|
||||
|
||||
static void errorAt(Token* token, const char* message) {
|
||||
if (parser.panicMode) return;
|
||||
parser.panicMode = true;
|
||||
fprintf(stderr, "[line %d] Error", token->line);
|
||||
|
||||
if (token->type == TOKEN_EOF) {
|
||||
fprintf(stderr, " at end");
|
||||
} else if (token->type == TOKEN_ERROR) {
|
||||
// Nothing.
|
||||
} else {
|
||||
fprintf(stderr, " at '%.*s'", token->length, token->start);
|
||||
}
|
||||
|
||||
fprintf(stderr, ": %s\n", message);
|
||||
parser.hadError = true;
|
||||
}
|
||||
|
||||
static void error(const char* message) {
|
||||
errorAt(&parser.previous, message);
|
||||
}
|
||||
|
||||
static void errorAtCurrent(const char* message) {
|
||||
errorAt(&parser.current, message);
|
||||
}
|
||||
|
||||
static void advance() {
|
||||
parser.previous = parser.current;
|
||||
|
||||
for (;;) {
|
||||
parser.current = scanToken();
|
||||
if (parser.current.type != TOKEN_ERROR) break;
|
||||
|
||||
errorAtCurrent(parser.current.start);
|
||||
}
|
||||
}
|
||||
|
||||
static void consume(TokenType type, const char* message) {
|
||||
if (parser.current.type == type) {
|
||||
advance();
|
||||
return;
|
||||
}
|
||||
|
||||
errorAtCurrent(message);
|
||||
}
|
||||
|
||||
static void emitByte(uint8_t byte) {
|
||||
writeChunk(currentChunk(), byte, parser.previous.line);
|
||||
}
|
||||
|
||||
static void emitBytes(uint8_t byte1, uint8_t byte2) {
|
||||
emitByte(byte1);
|
||||
emitByte(byte2);
|
||||
}
|
||||
|
||||
static void endCompiler() {
|
||||
emitReturn();
|
||||
#ifdef DEBUG_PRINT_CODE
|
||||
if (!parser.hadError) {
|
||||
disassembleChunk(currentChunk(), "code");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
static void expression();
|
||||
static ParseRule* getRule(TokenType type);
|
||||
static void parsePrecedence(Precedence precedence);
|
||||
|
||||
static void binary() {
|
||||
TokenType operatorType = parser.previous.type;
|
||||
ParseRule* rule = getRule(operatorType);
|
||||
parsePrecedence((Precedence)(rule->precedence + 1));
|
||||
|
||||
switch (operatorType) {
|
||||
case TOKEN_PLUS: emitByte(OP_ADD); break;
|
||||
case TOKEN_MINUS: emitByte(OP_SUBTRACT); break;
|
||||
case TOKEN_STAR: emitByte(OP_MULTIPLY); break;
|
||||
case TOKEN_SLASH: emitByte(OP_DIVIDE); break;
|
||||
default: return; // Unreachable.
|
||||
}
|
||||
}
|
||||
|
||||
static void emitReturn() {
|
||||
emitByte(OP_RETURN);
|
||||
}
|
||||
|
||||
static uint8_t makeConstant(Value value) {
|
||||
int constant = addConstant(currentChunk(), value);
|
||||
if (constant > UINT8_MAX) {
|
||||
error("Too many constants in one chunk.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint8_t)constant;
|
||||
}
|
||||
|
||||
static void emitConstant(Value value) {
|
||||
emitBytes(OP_CONSTANT, makeConstant(value));
|
||||
}
|
||||
|
||||
static void parsePrecedence(Precedence precedence) {
|
||||
advance();
|
||||
ParseFn prefixRule = getRule(parser.previous.type)->prefix;
|
||||
if (prefixRule == NULL) {
|
||||
error("Expect expression.");
|
||||
return;
|
||||
}
|
||||
|
||||
prefixRule();
|
||||
|
||||
while (precedence <= getRule(parser.current.type)->precedence) {
|
||||
advance();
|
||||
ParseFn infixRule = getRule(parser.previous.type)->infix;
|
||||
infixRule();
|
||||
}
|
||||
}
|
||||
|
||||
ParseRule rules[] = {
|
||||
[TOKEN_LEFT_PAREN] = {grouping, NULL, PREC_NONE},
|
||||
[TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_DOT] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_MINUS] = {unary, binary, PREC_TERM},
|
||||
[TOKEN_PLUS] = {NULL, binary, PREC_TERM},
|
||||
[TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_SLASH] = {NULL, binary, PREC_FACTOR},
|
||||
[TOKEN_STAR] = {NULL, binary, PREC_FACTOR},
|
||||
[TOKEN_BANG] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_BANG_EQUAL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_EQUAL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_EQUAL_EQUAL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_GREATER] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_GREATER_EQUAL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LESS] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LESS_EQUAL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_STRING] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
|
||||
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_FALSE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_FUN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_NIL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_OR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_THIS] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_TRUE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_VAR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_EOF] = {NULL, NULL, PREC_NONE},
|
||||
};
|
||||
|
||||
static ParseRule* getRule(TokenType type) {
|
||||
return &rules[type];
|
||||
}
|
||||
|
||||
static void expression() {
|
||||
// What goes here?
|
||||
parsePrecedence(PREC_ASSIGNMENT);
|
||||
}
|
||||
static void grouping() {
|
||||
expression();
|
||||
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
|
||||
}
|
||||
|
||||
static void number() {
|
||||
double value = strtod(parser.previous.start, NULL);
|
||||
emitConstant(value);
|
||||
}
|
||||
|
||||
static void unary() {
|
||||
TokenType operatorType = parser.previous.type;
|
||||
|
||||
// Compile the operand.
|
||||
parsePrecedence(PREC_UNARY);
|
||||
// Emit the operator instruction.
|
||||
switch (operatorType) {
|
||||
case TOKEN_MINUS: emitByte(OP_NEGATE); break;
|
||||
default: return; // Unreachable.
|
||||
}
|
||||
}
|
||||
|
||||
bool compile(const char* source, Chunk* chunk) {
|
||||
initScanner(source);
|
||||
compilingChunk = chunk;
|
||||
parser.hadError = false;
|
||||
parser.panicMode = false;
|
||||
advance();
|
||||
expression();
|
||||
consume(TOKEN_EOF, "Expect end of expression.");
|
||||
endCompiler();
|
||||
return !parser.hadError;
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
#ifndef clox_compiler_h
|
||||
#define clox_compiler_h
|
||||
|
||||
void compile(const char* source);
|
||||
#include "vm.h"
|
||||
|
||||
bool compile(const char* source, Chunk* chunk);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -18,9 +18,8 @@ void initScanner(const char* source) {
|
||||
scanner.line = 1;
|
||||
}
|
||||
|
||||
static Token identifier() {
|
||||
while (isAlpha(peek()) || isDigit(peek())) advance();
|
||||
return makeToken(identifierType());
|
||||
static bool isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(char c) {
|
||||
@ -144,6 +143,10 @@ static TokenType identifierType() {
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
static Token identifier() {
|
||||
while (isAlpha(peek()) || isDigit(peek())) advance();
|
||||
return makeToken(identifierType());
|
||||
}
|
||||
static Token number() {
|
||||
while (isDigit(peek())) advance();
|
||||
|
||||
|
BIN
clox/scanner.o
BIN
clox/scanner.o
Binary file not shown.
16
clox/vm.c
16
clox/vm.c
@ -70,7 +70,19 @@ static InterpretResult run() {
|
||||
#undef READ_BYTE
|
||||
}
|
||||
InterpretResult interpret(const char* source) {
|
||||
compile(source);
|
||||
return INTERPRET_OK;
|
||||
Chunk chunk;
|
||||
initChunk(&chunk);
|
||||
|
||||
if (!compile(source, &chunk)) {
|
||||
freeChunk(&chunk);
|
||||
return INTERPRET_COMPILE_ERROR;
|
||||
}
|
||||
|
||||
vm.chunk = &chunk;
|
||||
vm.ip = vm.chunk->code;
|
||||
|
||||
InterpretResult result = run();
|
||||
|
||||
freeChunk(&chunk);
|
||||
return result;
|
||||
}
|
||||
|
BIN
clox/vm.o
BIN
clox/vm.o
Binary file not shown.
Loading…
Reference in New Issue
Block a user