Skip to content

Commit

Permalink
Almost working compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
dils2k committed Feb 25, 2023
1 parent f858f5e commit 8ba86bb
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 13 deletions.
2 changes: 1 addition & 1 deletion chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ typedef enum {
OP_ADD,
OP_SUBTRACT,
OP_MULTIPLY,
OP_DEVIDE,
OP_DIVIDE,
OP_CONSTANT,
OP_NEGATE,
OP_RETURN
Expand Down
231 changes: 229 additions & 2 deletions compiler.c
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;
}
2 changes: 1 addition & 1 deletion debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ int disassembleInstruction(Chunk* chunk, int offset) {
return simpleInstruction("OP_SUBTRACT", offset);
case OP_MULTIPLY:
return simpleInstruction("OP_MULTIPLY", offset);
case OP_DEVIDE:
case OP_DIVIDE:
return simpleInstruction("OP_DEVIDE", offset);
default:
printf("Unknown opcode %d\n", instruction);
Expand Down
7 changes: 0 additions & 7 deletions scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ void initScanner(const char* source) {
scanner.line = 1;
}

typedef struct {
TokenType type;
const char* start;
int length;
int line;
} Token;

static bool isAtEnd() {
return *scanner.current == '\0';
}
Expand Down
10 changes: 9 additions & 1 deletion scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ typedef enum {
TOKEN_ERROR, TOKEN_EOF
} TokenType;

typedef struct {
TokenType type;
const char* start;
int length;
int line;
} Token;

void initScanner(const char* source);

#endif
Token scanToken();

#endif
2 changes: 1 addition & 1 deletion vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ static InterpretResult run() {
case OP_ADD: BINARY_OP(+); break;
case OP_SUBTRACT: BINARY_OP(-); break;
case OP_MULTIPLY: BINARY_OP(*); break;
case OP_DEVIDE: BINARY_OP(/); break;
case OP_DIVIDE: BINARY_OP(/); break;
case OP_NEGATE: push(-pop()); break;
case OP_RETURN: {
printValue(pop());
Expand Down

0 comments on commit 8ba86bb

Please sign in to comment.