-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
241 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,240 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
#include "common.h" | ||
#include "compiler.h" | ||
#include "scanner.h" | ||
|
||
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 emitReturn() { | ||
emitByte(OP_RETURN); | ||
} | ||
|
||
static void endCompiler() { | ||
emitReturn(); | ||
} | ||
|
||
static void expression(); | ||
static ParseRule* getRule(TokenType type); | ||
static void parsePrecedence(Precedence precedence); | ||
|
||
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 number() { | ||
double value = strtod(parser.previous.start, NULL); | ||
emitConstant(value); | ||
} | ||
|
||
static void parsePrecedence(Precedence precedence) { | ||
advance(); | ||
ParseFn prefixRule = getRule(parser.previous.type)->prefix; | ||
if (prefixRule == NULL) { | ||
error("Expect expression."); | ||
return; | ||
} | ||
|
||
while (precedence <= getRule(parser.current.type)->precedence) { | ||
advance(); | ||
ParseFn infixRule = getRule(parser.previous.type)->infix; | ||
infixRule(); | ||
} | ||
|
||
prefixRule(); | ||
} | ||
|
||
static void expression() { | ||
parsePrecedence(PREC_ASSIGNMENT); | ||
} | ||
|
||
static void grouping() { | ||
expression(); | ||
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression."); | ||
} | ||
|
||
static void unary() { | ||
parsePrecedence(PREC_UNARY); | ||
|
||
TokenType operatorType = parser.previous.type; | ||
|
||
expression(); | ||
|
||
switch (operatorType) { | ||
case TOKEN_MINUS: emitByte(OP_NEGATE); break; | ||
default: return; | ||
} | ||
} | ||
|
||
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 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. | ||
} | ||
} | ||
|
||
bool compile(const char* source, Chunk* chunk) { | ||
initScanner(source); | ||
compilingChunk = chunk; | ||
|
||
parser.panicMode = false; | ||
parser.hadError = false; | ||
|
||
advance(); | ||
expression(); | ||
consume(TOKEN_EOF, "Expect end of expression"); | ||
return true; | ||
} | ||
endCompiler(); | ||
return !parser.hadError; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters