kuroko/compiler.c

410 lines
10 KiB
C
Raw Normal View History

#include <stdio.h>
#include <stdlib.h>
#include "kuroko.h"
#include "compiler.h"
#include "scanner.h"
#include "object.h"
typedef struct {
KrkToken current;
KrkToken previous;
int hadError;
int panicMode;
} Parser;
typedef enum {
PREC_NONE,
PREC_ASSIGNMENT, /* = */
PREC_OR, /* or */
PREC_AND, /* and */
PREC_EQUALITY, /* == != in */
PREC_COMPARISON, /* < > <= >= */
PREC_TERM, /* + - */
PREC_FACTOR, /* * */
PREC_UNARY, /* ! - not */
PREC_CALL, /* . () */
PREC_PRIMARY
} Precedence;
2020-12-26 10:53:15 +03:00
typedef void (*ParseFn)(int);
typedef struct {
ParseFn prefix;
ParseFn infix;
Precedence precedence;
} ParseRule;
Parser parser;
KrkChunk * compilingChunk;
static KrkChunk * currentChunk() {
return compilingChunk;
}
static void parsePrecedence(Precedence precedence);
2020-12-26 10:53:15 +03:00
static size_t parseVariable(const char * errorMessage);
static void defineVariable(size_t global);
static size_t identifierConstant(KrkToken * name);
static ParseRule * getRule(KrkTokenType type);
2020-12-26 10:53:15 +03:00
static void expression();
static void statement();
static void declaration();
static void errorAt(KrkToken * token, const char * message) {
if (parser.panicMode) return;
parser.panicMode = 1;
fprintf(stderr, "[line %d] Error", (int)token->line);
if (token->type == TOKEN_EOF) {
fprintf(stderr, " at end");
} else if (token->type != TOKEN_ERROR) {
fprintf(stderr, " at '%.*s'", (int)token->length, token->start);
}
fprintf(stderr, ": %s\n", message);
parser.hadError = 1;
}
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 = krk_scanToken();
if (parser.current.type == TOKEN_RETRY) continue;
if (parser.current.type != TOKEN_ERROR) break;
errorAtCurrent(parser.current.start);
}
}
static void consume(KrkTokenType type, const char * message) {
if (parser.current.type == type) {
advance();
return;
}
errorAtCurrent(message);
}
2020-12-26 10:53:15 +03:00
static int check(KrkTokenType type) {
return parser.current.type == type;
}
static int match(KrkTokenType type) {
if (!check(type)) return 0;
advance();
return 1;
}
static void emitByte(uint8_t byte) {
krk_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();
}
2020-12-26 10:53:15 +03:00
static void endOfLine() {
if (!(match(TOKEN_EOL) || match(TOKEN_EOF))) {
errorAtCurrent("Expected end of line.");
}
}
static void emitConstant(KrkValue value) {
krk_writeConstant(currentChunk(), value, parser.previous.line);
}
2020-12-26 10:53:15 +03:00
static void number(int canAssign) {
const char * start= parser.previous.start;
int base = 10;
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X')) {
base = 10;
start += 2;
} else if (start[0] == '0' && (start[1] == 'b' || start[1] == 'B')) {
base = 2;
start += 2;
} else if (start[0] == '0') {
base = 8;
start += 1;
}
if (base == 10) {
/* See if it's a float */
for (size_t j = 0; j < parser.previous.length; ++j) {
if (parser.previous.start[j] == '.') {
double value = strtod(start, NULL);
emitConstant(FLOATING_VAL(value));
return;
}
}
}
int value = strtol(start, NULL, base);
emitConstant(INTEGER_VAL(value));
}
2020-12-26 10:53:15 +03:00
static void binary(int canAssign) {
KrkTokenType operatorType = parser.previous.type;
ParseRule * rule = getRule(operatorType);
parsePrecedence((Precedence)(rule->precedence + 1));
switch (operatorType) {
case TOKEN_BANG_EQUAL: emitBytes(OP_EQUAL, OP_NOT); break;
case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); break;
case TOKEN_GREATER: emitByte(OP_GREATER); break;
case TOKEN_GREATER_EQUAL: emitBytes(OP_LESS, OP_NOT); break;
case TOKEN_LESS: emitByte(OP_LESS); break;
case TOKEN_LESS_EQUAL: emitBytes(OP_GREATER, OP_NOT); break;
case TOKEN_PLUS: emitByte(OP_ADD); break;
case TOKEN_MINUS: emitByte(OP_SUBTRACT); break;
case TOKEN_ASTERISK: emitByte(OP_MULTIPLY); break;
case TOKEN_SOLIDUS: emitByte(OP_DIVIDE); break;
default: return;
}
}
2020-12-26 10:53:15 +03:00
static void literal(int canAssign) {
switch (parser.previous.type) {
case TOKEN_FALSE: emitByte(OP_FALSE); break;
case TOKEN_NONE: emitByte(OP_NONE); break;
case TOKEN_TRUE: emitByte(OP_TRUE); break;
default: return;
}
}
static void expression() {
parsePrecedence(PREC_ASSIGNMENT);
}
2020-12-26 10:53:15 +03:00
static void varDeclaration() {
ssize_t global = parseVariable("Expected variable name.");
if (match(TOKEN_EQUAL)) {
expression();
} else {
emitByte(OP_NONE);
}
endOfLine();
defineVariable(global);
}
static void printStatement() {
expression();
endOfLine();
emitByte(OP_PRINT);
}
static void synchronize() {
parser.panicMode = 0;
while (parser.current.type != TOKEN_EOF) {
if (parser.previous.type == TOKEN_EOL) return;
switch (parser.current.type) {
case TOKEN_CLASS:
case TOKEN_DEF:
case TOKEN_LET:
case TOKEN_FOR:
case TOKEN_IF:
case TOKEN_WHILE:
case TOKEN_PRINT:
case TOKEN_RETURN:
return;
default: break;
}
advance();
}
}
static void declaration() {
if (match(TOKEN_LET)) {
varDeclaration();
} else {
statement();
}
if (parser.panicMode) synchronize();
}
static void expressionStatement() {
expression();
endOfLine();
emitByte(OP_POP);
}
static void statement() {
if (match(TOKEN_PRINT)) {
printStatement();
} else {
expressionStatement();
}
}
static void grouping(int canAssign) {
expression();
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
}
2020-12-26 10:53:15 +03:00
static void unary(int canAssign) {
KrkTokenType operatorType = parser.previous.type;
parsePrecedence(PREC_UNARY);
switch (operatorType) {
case TOKEN_MINUS: emitByte(OP_NEGATE); break;
/* These are equivalent */
case TOKEN_BANG:
case TOKEN_NOT:
emitByte(OP_NOT);
break;
default: return;
}
}
2020-12-26 10:53:15 +03:00
static void string(int canAssign) {
emitConstant(OBJECT_VAL(copyString(parser.previous.start + 1, parser.previous.length - 2)));
}
2020-12-26 10:53:15 +03:00
#define EMIT_CONSTANT_OP(opc, arg) do { if (arg < 256) { emitBytes(opc, arg); } \
else { emitBytes(opc ## _LONG, arg >> 16); emitBytes(arg >> 8, arg); } } while (0)
static void namedVariable(KrkToken name, int canAssign) {
size_t arg = identifierConstant(&name);
if (canAssign && match(TOKEN_EQUAL)) {
expression();
EMIT_CONSTANT_OP(OP_SET_GLOBAL, arg);
} else {
EMIT_CONSTANT_OP(OP_GET_GLOBAL, arg);
}
}
static void variable(int canAssign) {
namedVariable(parser.previous, canAssign);
}
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_LEFT_SQUARE] = {NULL, NULL, PREC_NONE},
[TOKEN_RIGHT_SQUARE] = {NULL, NULL, PREC_NONE},
[TOKEN_COLON] = {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_SOLIDUS] = {NULL, binary, PREC_FACTOR},
[TOKEN_ASTERISK] = {NULL, binary, PREC_FACTOR},
[TOKEN_BANG] = {unary, NULL, PREC_NONE},
[TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY},
[TOKEN_EQUAL] = {NULL, NULL, PREC_NONE},
[TOKEN_EQUAL_EQUAL] = {NULL, binary, PREC_EQUALITY},
[TOKEN_GREATER] = {NULL, binary, PREC_COMPARISON},
[TOKEN_GREATER_EQUAL] = {NULL, binary, PREC_COMPARISON},
[TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
[TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
2020-12-26 10:53:15 +03:00
[TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE},
[TOKEN_STRING] = {string, NULL, PREC_NONE},
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
[TOKEN_CODEPOINT] = {NULL, NULL, PREC_NONE}, /* should be equivalent to number */
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
[TOKEN_DEF] = {NULL, NULL, PREC_NONE},
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
[TOKEN_IN] = {NULL, NULL, PREC_NONE},
[TOKEN_LET] = {NULL, NULL, PREC_NONE},
[TOKEN_NONE] = {literal, NULL, PREC_NONE},
[TOKEN_NOT] = {unary, NULL, PREC_NONE},
[TOKEN_OR] = {NULL, NULL, PREC_NONE},
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
[TOKEN_SELF] = {NULL, NULL, PREC_NONE},
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
/* This is going to get interesting */
[TOKEN_INDENTATION] = {NULL, NULL, PREC_NONE},
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
[TOKEN_EOF] = {NULL, NULL, PREC_NONE},
};
static void parsePrecedence(Precedence precedence) {
advance();
ParseFn prefixRule = getRule(parser.previous.type)->prefix;
if (prefixRule == NULL) {
error("expect expression");
return;
}
2020-12-26 10:53:15 +03:00
int canAssign = precedence <= PREC_ASSIGNMENT;
prefixRule(canAssign);
while (precedence <= getRule(parser.current.type)->precedence) {
advance();
ParseFn infixRule = getRule(parser.previous.type)->infix;
2020-12-26 10:53:15 +03:00
infixRule(canAssign);
}
if (canAssign && match(TOKEN_EQUAL)) {
error("invalid assignment target");
}
}
2020-12-26 10:53:15 +03:00
static size_t identifierConstant(KrkToken * name) {
return krk_addConstant(currentChunk(), OBJECT_VAL(copyString(name->start, name->length)));
}
static size_t parseVariable(const char * errorMessage) {
consume(TOKEN_IDENTIFIER, errorMessage);
return identifierConstant(&parser.previous);
}
static void defineVariable(size_t global) {
EMIT_CONSTANT_OP(OP_DEFINE_GLOBAL, global);
}
static ParseRule * getRule(KrkTokenType type) {
return &rules[type];
}
int krk_compile(const char * src, KrkChunk * chunk) {
krk_initScanner(src);
compilingChunk = chunk;
parser.hadError = 0;
parser.panicMode = 0;
advance();
2020-12-26 10:53:15 +03:00
while (!match(TOKEN_EOF)) {
declaration();
}
endCompiler();
return !parser.hadError;
}