2021-08-02 18:09:03 -07:00
|
|
|
#include <stdio.h>
|
2021-08-16 22:17:40 -07:00
|
|
|
#include <stdlib.h>
|
2021-10-21 20:54:35 -07:00
|
|
|
#include <string.h>
|
2021-08-02 18:09:03 -07:00
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "compiler.h"
|
2022-06-04 22:00:27 -07:00
|
|
|
#include "memory.h"
|
2021-08-02 18:09:03 -07:00
|
|
|
#include "scanner.h"
|
|
|
|
|
2021-08-17 20:47:18 -07:00
|
|
|
#ifdef DEBUG_PRINT_CODE
|
|
|
|
#include "debug.h"
|
|
|
|
#endif
|
|
|
|
|
2021-08-16 22:17:40 -07:00
|
|
|
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;
|
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
typedef void (*ParseFn)(bool canAssign);
|
2021-08-17 20:41:54 -07:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
ParseFn prefix;
|
|
|
|
ParseFn infix;
|
|
|
|
Precedence precedence;
|
|
|
|
} ParseRule;
|
|
|
|
|
2021-10-21 20:00:19 -07:00
|
|
|
typedef struct {
|
|
|
|
Token name;
|
|
|
|
int depth;
|
2022-06-02 22:27:43 -07:00
|
|
|
bool isCaptured;
|
2021-10-21 20:00:19 -07:00
|
|
|
} Local;
|
|
|
|
|
2022-05-31 23:02:18 -07:00
|
|
|
typedef struct {
|
|
|
|
uint8_t index;
|
|
|
|
bool isLocal;
|
|
|
|
} Upvalue;
|
|
|
|
|
2022-08-14 16:11:39 -07:00
|
|
|
typedef enum {
|
|
|
|
TYPE_FUNCTION,
|
|
|
|
TYPE_INITIALIZER,
|
|
|
|
TYPE_METHOD,
|
|
|
|
TYPE_SCRIPT
|
|
|
|
} FunctionType;
|
2022-01-22 22:01:54 -08:00
|
|
|
|
2022-03-13 19:41:34 -07:00
|
|
|
typedef struct Compiler {
|
|
|
|
struct Compiler *enclosing;
|
2022-01-22 22:01:54 -08:00
|
|
|
ObjFunction *function;
|
|
|
|
FunctionType type;
|
|
|
|
|
2021-10-21 20:00:19 -07:00
|
|
|
Local locals[UINT8_COUNT];
|
|
|
|
int localCount;
|
2022-05-31 23:02:18 -07:00
|
|
|
Upvalue upvalues[UINT8_COUNT];
|
2021-10-21 20:00:19 -07:00
|
|
|
int scopeDepth;
|
|
|
|
} Compiler;
|
|
|
|
|
2022-08-13 14:52:57 -07:00
|
|
|
typedef struct ClassCompiler {
|
|
|
|
struct ClassCompiler *enclosing;
|
2022-08-14 22:27:35 -07:00
|
|
|
bool hasSuperclass;
|
2022-08-13 14:52:57 -07:00
|
|
|
} ClassCompiler;
|
|
|
|
|
2022-05-31 23:02:18 -07:00
|
|
|
static int resolveUpvalue(Compiler *, Token *);
|
|
|
|
|
2021-08-16 22:17:40 -07:00
|
|
|
Parser parser;
|
2021-10-21 20:00:19 -07:00
|
|
|
Compiler *current = NULL;
|
2022-08-13 14:52:57 -07:00
|
|
|
ClassCompiler *currentClass = NULL;
|
2021-08-16 22:17:40 -07:00
|
|
|
Chunk *compilingChunk;
|
|
|
|
|
2022-01-22 22:01:54 -08:00
|
|
|
static Chunk *currentChunk() { return ¤t->function->chunk; }
|
2021-08-16 22:17:40 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-08-02 18:35:10 -07:00
|
|
|
for (;;) {
|
2021-08-16 22:17:40 -07:00
|
|
|
parser.current = scanToken();
|
|
|
|
if (parser.current.type != TOKEN_ERROR)
|
2021-08-02 18:35:10 -07:00
|
|
|
break;
|
2021-08-16 22:17:40 -07:00
|
|
|
|
|
|
|
errorAtCurrent(parser.current.start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void consume(TokenType type, const char *message) {
|
|
|
|
if (parser.current.type == type) {
|
|
|
|
advance();
|
|
|
|
return;
|
2021-08-02 18:35:10 -07:00
|
|
|
}
|
2021-08-16 22:17:40 -07:00
|
|
|
|
|
|
|
errorAtCurrent(message);
|
|
|
|
}
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
static bool check(TokenType type) { return parser.current.type == type; }
|
|
|
|
|
|
|
|
static bool match(TokenType type) {
|
|
|
|
if (!check(type))
|
|
|
|
return false;
|
|
|
|
advance();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-08-16 22:17:40 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-01-12 23:09:36 -08:00
|
|
|
static void emitLoop(int loopStart) {
|
|
|
|
emitByte(OP_LOOP);
|
|
|
|
|
|
|
|
int offset = currentChunk()->count - loopStart + 2;
|
|
|
|
if (offset > UINT16_MAX)
|
|
|
|
error("Loop body too large.");
|
|
|
|
|
|
|
|
emitByte((offset >> 8) & 0xff);
|
|
|
|
emitByte(offset & 0xff);
|
|
|
|
}
|
|
|
|
|
2022-01-12 22:58:10 -08:00
|
|
|
static int emitJump(uint8_t instruction) {
|
|
|
|
emitByte(instruction);
|
|
|
|
emitByte(0xff);
|
|
|
|
emitByte(0xff);
|
|
|
|
return currentChunk()->count - 2;
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:39:11 -07:00
|
|
|
static void emitReturn() {
|
2022-08-14 16:11:39 -07:00
|
|
|
if (current->type == TYPE_INITIALIZER) {
|
|
|
|
emitBytes(OP_GET_LOCAL, 0);
|
|
|
|
} else {
|
|
|
|
emitByte(OP_NIL);
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:39:11 -07:00
|
|
|
emitByte(OP_RETURN);
|
|
|
|
}
|
2021-08-16 22:17:40 -07:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2022-01-12 22:58:10 -08:00
|
|
|
static void patchJump(int offset) {
|
|
|
|
// -2 to adjust for the bytecode for the jump offset itself...
|
|
|
|
int jump = currentChunk()->count - offset - 2;
|
|
|
|
|
|
|
|
if (jump > UINT16_MAX) {
|
|
|
|
error("Too much code to jump over.");
|
|
|
|
}
|
|
|
|
|
|
|
|
currentChunk()->code[offset] = (jump >> 8) & 0xff;
|
|
|
|
currentChunk()->code[offset + 1] = jump & 0xff;
|
|
|
|
}
|
|
|
|
|
2022-01-22 22:01:54 -08:00
|
|
|
static void initCompiler(Compiler *compiler, FunctionType type) {
|
2022-03-13 19:41:34 -07:00
|
|
|
compiler->enclosing = current;
|
2022-01-22 22:01:54 -08:00
|
|
|
compiler->function = NULL;
|
|
|
|
compiler->type = type;
|
2021-10-21 20:00:19 -07:00
|
|
|
compiler->localCount = 0;
|
|
|
|
compiler->scopeDepth = 0;
|
2022-01-22 22:01:54 -08:00
|
|
|
compiler->function = newFunction();
|
2021-10-21 20:00:19 -07:00
|
|
|
current = compiler;
|
2022-03-13 19:41:34 -07:00
|
|
|
if (type != TYPE_SCRIPT) {
|
2022-08-14 16:13:49 -07:00
|
|
|
current->function->name
|
|
|
|
= copyString(parser.previous.start, parser.previous.length);
|
2022-03-13 19:41:34 -07:00
|
|
|
}
|
2022-01-22 22:01:54 -08:00
|
|
|
|
|
|
|
Local *local = ¤t->locals[current->localCount++];
|
|
|
|
local->depth = 0;
|
2022-06-02 22:27:43 -07:00
|
|
|
local->isCaptured = false;
|
2022-08-13 14:52:57 -07:00
|
|
|
if (type != TYPE_FUNCTION) {
|
|
|
|
local->name.start = "this";
|
|
|
|
local->name.length = 4;
|
|
|
|
} else {
|
|
|
|
local->name.start = "";
|
|
|
|
local->name.length = 0;
|
|
|
|
}
|
2021-10-21 20:00:19 -07:00
|
|
|
}
|
|
|
|
|
2022-01-22 22:01:54 -08:00
|
|
|
static ObjFunction *endCompiler() {
|
2021-08-17 20:47:18 -07:00
|
|
|
emitReturn();
|
2022-01-22 22:01:54 -08:00
|
|
|
ObjFunction *function = current->function;
|
|
|
|
|
2021-08-17 20:47:18 -07:00
|
|
|
#ifdef DEBUG_PRINT_CODE
|
|
|
|
if (!parser.hadError) {
|
2022-01-22 22:01:54 -08:00
|
|
|
disassembleChunk(currentChunk(), function->name != NULL
|
|
|
|
? function->name->chars
|
|
|
|
: "<script>");
|
2021-08-17 20:47:18 -07:00
|
|
|
}
|
|
|
|
#endif
|
2022-01-22 22:01:54 -08:00
|
|
|
|
2022-03-13 19:41:34 -07:00
|
|
|
current = current->enclosing;
|
2022-01-22 22:01:54 -08:00
|
|
|
return function;
|
2021-08-17 20:47:18 -07:00
|
|
|
}
|
2021-08-16 22:17:40 -07:00
|
|
|
|
2021-10-21 20:18:48 -07:00
|
|
|
static void beginScope() { current->scopeDepth++; }
|
|
|
|
|
2021-10-21 20:54:35 -07:00
|
|
|
static void endScope() {
|
|
|
|
current->scopeDepth--;
|
|
|
|
|
2022-08-14 16:13:49 -07:00
|
|
|
while (current->localCount > 0
|
|
|
|
&& current->locals[current->localCount - 1].depth
|
|
|
|
> current->scopeDepth) {
|
2022-06-02 22:27:43 -07:00
|
|
|
if (current->locals[current->localCount - 1].isCaptured) {
|
|
|
|
emitByte(OP_CLOSE_UPVALUE);
|
|
|
|
} else {
|
|
|
|
emitByte(OP_POP);
|
|
|
|
}
|
2021-10-21 20:54:35 -07:00
|
|
|
current->localCount--;
|
|
|
|
}
|
|
|
|
}
|
2021-10-21 20:18:48 -07:00
|
|
|
|
2021-08-17 20:41:54 -07:00
|
|
|
static void expression();
|
2021-09-20 21:47:41 -07:00
|
|
|
static void statement();
|
|
|
|
static void declaration();
|
2021-08-17 20:41:54 -07:00
|
|
|
static ParseRule *getRule(TokenType type);
|
|
|
|
static void parsePrecedence(Precedence precedence);
|
2021-09-20 21:47:41 -07:00
|
|
|
static uint8_t parseVariable(const char *name);
|
|
|
|
static void defineVariable(uint8_t global);
|
2021-10-11 00:13:15 -07:00
|
|
|
static uint8_t identifierConstant(Token *name);
|
2021-10-25 21:49:54 -07:00
|
|
|
static int resolveLocal(Compiler *compiler, Token *name);
|
2021-08-17 20:41:54 -07:00
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
static void binary(bool canAssign) {
|
2021-08-17 20:41:54 -07:00
|
|
|
TokenType operatorType = parser.previous.type;
|
|
|
|
ParseRule *rule = getRule(operatorType);
|
|
|
|
parsePrecedence((Precedence)(rule->precedence + 1));
|
|
|
|
|
|
|
|
switch (operatorType) {
|
2021-09-06 02:03:31 -07:00
|
|
|
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;
|
2021-08-17 20:41:54 -07:00
|
|
|
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.
|
|
|
|
}
|
2021-08-16 22:17:40 -07:00
|
|
|
}
|
2021-08-17 20:41:54 -07:00
|
|
|
|
2022-03-15 00:39:11 -07:00
|
|
|
static uint8_t argumentList() {
|
|
|
|
uint8_t argCount = 0;
|
|
|
|
if (!check(TOKEN_RIGHT_PAREN)) {
|
|
|
|
do {
|
|
|
|
expression();
|
|
|
|
if (argCount == 255) {
|
|
|
|
error("Can't have more than 255 arguments.");
|
|
|
|
}
|
|
|
|
argCount++;
|
|
|
|
} while (match(TOKEN_COMMA));
|
|
|
|
}
|
|
|
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after arguments.");
|
|
|
|
return argCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void call(bool canAssign) {
|
|
|
|
uint8_t argCount = argumentList();
|
|
|
|
emitBytes(OP_CALL, argCount);
|
|
|
|
}
|
|
|
|
|
2022-08-13 00:04:31 -07:00
|
|
|
static void dot(bool canAssign) {
|
|
|
|
consume(TOKEN_IDENTIFIER, "Expect property name after '.'.");
|
|
|
|
uint8_t name = identifierConstant(&parser.previous);
|
|
|
|
|
|
|
|
if (canAssign && match(TOKEN_EQUAL)) {
|
|
|
|
expression();
|
|
|
|
emitBytes(OP_SET_PROPERTY, name);
|
2022-08-14 19:56:32 -07:00
|
|
|
} else if (match(TOKEN_LEFT_PAREN)) {
|
|
|
|
uint8_t argCount = argumentList();
|
|
|
|
emitBytes(OP_INVOKE, name);
|
|
|
|
emitByte(argCount);
|
2022-08-13 00:04:31 -07:00
|
|
|
} else {
|
|
|
|
emitBytes(OP_GET_PROPERTY, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
static void literal(bool canAssign) {
|
2021-09-06 02:03:31 -07:00
|
|
|
switch (parser.previous.type) {
|
|
|
|
case TOKEN_FALSE:
|
|
|
|
emitByte(OP_FALSE);
|
|
|
|
break;
|
|
|
|
case TOKEN_NIL:
|
|
|
|
emitByte(OP_NIL);
|
|
|
|
break;
|
|
|
|
case TOKEN_TRUE:
|
|
|
|
emitByte(OP_TRUE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return; /* Unreachable */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-16 22:17:40 -07:00
|
|
|
static void expression() { parsePrecedence(PREC_ASSIGNMENT); }
|
|
|
|
|
2021-10-21 20:18:48 -07:00
|
|
|
static void block() {
|
|
|
|
while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
|
|
|
|
declaration();
|
|
|
|
}
|
|
|
|
|
|
|
|
consume(TOKEN_RIGHT_BRACE, "Expect '}' after block.");
|
|
|
|
}
|
|
|
|
|
2022-03-13 19:41:34 -07:00
|
|
|
static void function(FunctionType type) {
|
|
|
|
Compiler compiler;
|
|
|
|
initCompiler(&compiler, type);
|
|
|
|
beginScope();
|
|
|
|
|
|
|
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after function name.");
|
|
|
|
if (!check(TOKEN_RIGHT_PAREN)) {
|
|
|
|
do {
|
|
|
|
current->function->arity++;
|
|
|
|
if (current->function->arity > 255) {
|
|
|
|
errorAtCurrent("Can't have more than 250 parameters.");
|
|
|
|
}
|
|
|
|
uint8_t constant = parseVariable("Expect parameter name.");
|
|
|
|
defineVariable(constant);
|
|
|
|
} while (match(TOKEN_COMMA));
|
|
|
|
}
|
|
|
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
|
|
|
|
consume(TOKEN_LEFT_BRACE, "Expect '{' before function body.");
|
|
|
|
block();
|
|
|
|
|
|
|
|
ObjFunction *function = endCompiler();
|
2022-03-23 17:00:22 -07:00
|
|
|
emitBytes(OP_CLOSURE, makeConstant(OBJ_VAL(function)));
|
2022-05-31 23:02:18 -07:00
|
|
|
|
|
|
|
for (int i = 0; i < function->upvalueCount; i++) {
|
|
|
|
emitByte(compiler.upvalues[i].isLocal ? 1 : 0);
|
|
|
|
emitByte(compiler.upvalues[i].index);
|
|
|
|
}
|
2022-03-13 19:41:34 -07:00
|
|
|
}
|
|
|
|
|
2022-08-13 12:20:00 -07:00
|
|
|
static void method() {
|
|
|
|
consume(TOKEN_IDENTIFIER, "Expect method name.");
|
|
|
|
uint8_t constant = identifierConstant(&parser.previous);
|
|
|
|
|
2022-08-13 14:52:57 -07:00
|
|
|
FunctionType type = TYPE_METHOD;
|
2022-08-14 16:13:49 -07:00
|
|
|
if (parser.previous.length == 4
|
|
|
|
&& memcmp(parser.previous.start, "init", 4) == 0) {
|
2022-08-14 16:11:39 -07:00
|
|
|
type = TYPE_INITIALIZER;
|
|
|
|
}
|
|
|
|
|
2022-08-13 12:20:00 -07:00
|
|
|
function(type);
|
|
|
|
emitBytes(OP_METHOD, constant);
|
|
|
|
}
|
|
|
|
|
2022-08-12 21:17:45 -07:00
|
|
|
static bool identifiersEqual(Token *a, Token *b) {
|
|
|
|
if (a->length != b->length)
|
|
|
|
return false;
|
|
|
|
return memcmp(a->start, b->start, a->length) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addLocal(Token name) {
|
|
|
|
if (current->localCount == UINT8_COUNT) {
|
|
|
|
error("Too many local variables in function.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Local *local = ¤t->locals[current->localCount++];
|
|
|
|
local->name = name;
|
|
|
|
local->depth = -1;
|
|
|
|
local->isCaptured = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void declareVariable() {
|
|
|
|
if (current->scopeDepth == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Token *name = &parser.previous;
|
|
|
|
for (int i = current->localCount - 1; i >= 0; i--) {
|
|
|
|
Local *local = ¤t->locals[i];
|
|
|
|
if (local->depth != -1 && local->depth < current->scopeDepth) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (identifiersEqual(name, &local->name)) {
|
|
|
|
error("Already a variable with this name in this scope.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addLocal(*name);
|
|
|
|
}
|
|
|
|
|
2022-08-13 12:20:00 -07:00
|
|
|
static void namedVariable(Token name, bool canAssign) {
|
|
|
|
uint8_t getOp, setOp;
|
|
|
|
int arg = resolveLocal(current, &name);
|
|
|
|
|
|
|
|
if (arg != -1) {
|
|
|
|
getOp = OP_GET_LOCAL;
|
|
|
|
setOp = OP_SET_LOCAL;
|
|
|
|
} else if ((arg = resolveUpvalue(current, &name)) != -1) {
|
|
|
|
getOp = OP_GET_UPVALUE;
|
|
|
|
setOp = OP_SET_UPVALUE;
|
|
|
|
} else {
|
|
|
|
arg = identifierConstant(&name);
|
|
|
|
getOp = OP_GET_GLOBAL;
|
|
|
|
setOp = OP_SET_GLOBAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canAssign && match(TOKEN_EQUAL)) {
|
|
|
|
expression();
|
|
|
|
emitBytes(setOp, arg);
|
|
|
|
} else {
|
|
|
|
emitBytes(getOp, arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-14 20:14:43 -07:00
|
|
|
static void variable(bool canAssign) {
|
|
|
|
namedVariable(parser.previous, canAssign);
|
|
|
|
}
|
|
|
|
|
2022-08-14 22:27:35 -07:00
|
|
|
static Token syntheticToken(const char *text) {
|
|
|
|
Token token;
|
|
|
|
token.start = text;
|
|
|
|
token.length = (int)strlen(text);
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
2022-08-12 21:17:45 -07:00
|
|
|
static void classDeclaration() {
|
|
|
|
consume(TOKEN_IDENTIFIER, "Expect class name");
|
2022-08-13 12:20:00 -07:00
|
|
|
Token className = parser.previous;
|
2022-08-12 21:17:45 -07:00
|
|
|
uint8_t nameConstant = identifierConstant(&parser.previous);
|
|
|
|
declareVariable();
|
|
|
|
|
|
|
|
emitBytes(OP_CLASS, nameConstant);
|
|
|
|
defineVariable(nameConstant);
|
|
|
|
|
2022-08-13 14:52:57 -07:00
|
|
|
ClassCompiler classCompiler;
|
2022-08-14 22:27:35 -07:00
|
|
|
classCompiler.hasSuperclass = false;
|
2022-08-13 14:52:57 -07:00
|
|
|
classCompiler.enclosing = currentClass;
|
|
|
|
currentClass = &classCompiler;
|
|
|
|
|
2022-08-14 20:14:43 -07:00
|
|
|
if (match(TOKEN_LESS)) {
|
|
|
|
consume(TOKEN_IDENTIFIER, "Expect superclass name.");
|
|
|
|
variable(false);
|
|
|
|
|
|
|
|
if (identifiersEqual(&className, &parser.previous)) {
|
|
|
|
error("A class can't inherit from itself.");
|
|
|
|
}
|
|
|
|
|
2022-08-14 22:27:35 -07:00
|
|
|
beginScope();
|
|
|
|
addLocal(syntheticToken("super"));
|
|
|
|
defineVariable(0);
|
|
|
|
|
2022-08-14 20:14:43 -07:00
|
|
|
namedVariable(className, false);
|
|
|
|
emitByte(OP_INHERIT);
|
2022-08-14 22:27:35 -07:00
|
|
|
classCompiler.hasSuperclass = true;
|
2022-08-14 20:14:43 -07:00
|
|
|
}
|
|
|
|
|
2022-08-13 12:20:00 -07:00
|
|
|
namedVariable(className, false);
|
2022-08-12 21:17:45 -07:00
|
|
|
consume(TOKEN_LEFT_BRACE, "Expect '{' before class body.");
|
2022-08-13 12:20:00 -07:00
|
|
|
while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
|
|
|
|
method();
|
|
|
|
}
|
2022-08-12 21:17:45 -07:00
|
|
|
consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body.");
|
2022-08-13 12:20:00 -07:00
|
|
|
emitByte(OP_POP);
|
2022-08-13 14:52:57 -07:00
|
|
|
|
2022-08-14 22:27:35 -07:00
|
|
|
if (classCompiler.hasSuperclass) {
|
|
|
|
endScope();
|
|
|
|
}
|
|
|
|
|
2022-08-13 14:52:57 -07:00
|
|
|
currentClass = currentClass->enclosing;
|
2022-08-12 21:17:45 -07:00
|
|
|
}
|
|
|
|
|
2022-03-13 19:41:34 -07:00
|
|
|
static void markInitialized() {
|
|
|
|
if (current->scopeDepth == 0)
|
|
|
|
return;
|
|
|
|
current->locals[current->localCount - 1].depth = current->scopeDepth;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void funDeclaration() {
|
|
|
|
uint8_t global = parseVariable("Expect function name.");
|
|
|
|
markInitialized();
|
|
|
|
function(TYPE_FUNCTION);
|
|
|
|
defineVariable(global);
|
|
|
|
}
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-01-21 21:24:26 -08:00
|
|
|
static void forStatement() {
|
|
|
|
beginScope();
|
|
|
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'for'.");
|
|
|
|
if (match(TOKEN_SEMICOLON)) {
|
|
|
|
// No initializer
|
|
|
|
} else if (match(TOKEN_VAR)) {
|
|
|
|
varDeclaration();
|
|
|
|
} else {
|
|
|
|
expressionStatement();
|
|
|
|
}
|
|
|
|
|
|
|
|
int loopStart = currentChunk()->count;
|
|
|
|
int exitJump = -1;
|
|
|
|
if (!match(TOKEN_SEMICOLON)) {
|
|
|
|
expression();
|
|
|
|
consume(TOKEN_SEMICOLON, "Expect ';' after loop condition.");
|
|
|
|
|
|
|
|
// Jump out of the loop if the condition is false.
|
|
|
|
exitJump = emitJump(OP_JUMP_IF_FALSE);
|
|
|
|
emitByte(OP_POP); /* Condition. */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match(TOKEN_RIGHT_PAREN)) {
|
|
|
|
int bodyJump = emitJump(OP_JUMP);
|
|
|
|
int incrementStart = currentChunk()->count;
|
|
|
|
expression();
|
|
|
|
emitByte(OP_POP);
|
|
|
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after for clauses.");
|
|
|
|
|
|
|
|
emitLoop(loopStart);
|
|
|
|
loopStart = incrementStart;
|
|
|
|
patchJump(bodyJump);
|
|
|
|
}
|
|
|
|
|
|
|
|
statement();
|
|
|
|
emitLoop(loopStart);
|
|
|
|
|
|
|
|
if (exitJump != -1) {
|
|
|
|
patchJump(exitJump);
|
|
|
|
emitByte(OP_POP); /* Condition. */
|
|
|
|
}
|
|
|
|
|
|
|
|
endScope();
|
|
|
|
}
|
|
|
|
|
2022-01-12 22:58:10 -08:00
|
|
|
static void ifStatement() {
|
|
|
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'.");
|
|
|
|
expression();
|
|
|
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
|
|
|
|
|
|
|
|
int thenJump = emitJump(OP_JUMP_IF_FALSE);
|
|
|
|
emitByte(OP_POP);
|
|
|
|
statement();
|
|
|
|
|
|
|
|
int elseJump = emitJump(OP_JUMP);
|
|
|
|
|
|
|
|
patchJump(thenJump);
|
|
|
|
emitByte(OP_POP);
|
|
|
|
|
|
|
|
if (match(TOKEN_ELSE))
|
|
|
|
statement();
|
|
|
|
patchJump(elseJump);
|
|
|
|
}
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
static void printStatement() {
|
|
|
|
expression();
|
|
|
|
consume(TOKEN_SEMICOLON, "Expect ';' after value.");
|
|
|
|
emitByte(OP_PRINT);
|
|
|
|
}
|
|
|
|
|
2022-03-22 16:45:48 -07:00
|
|
|
static void returnStatement() {
|
|
|
|
if (current->type == TYPE_SCRIPT) {
|
|
|
|
error("Can't return from top-level code.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(TOKEN_SEMICOLON)) {
|
|
|
|
emitReturn();
|
|
|
|
} else {
|
2022-08-14 16:11:39 -07:00
|
|
|
if (current->type == TYPE_INITIALIZER) {
|
|
|
|
error("Can't return a value from an initializer.");
|
|
|
|
}
|
|
|
|
|
2022-03-22 16:45:48 -07:00
|
|
|
expression();
|
|
|
|
consume(TOKEN_SEMICOLON, "Expect ';' after return value.");
|
|
|
|
emitByte(OP_RETURN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-12 23:09:36 -08:00
|
|
|
static void whileStatement() {
|
|
|
|
int loopStart = currentChunk()->count;
|
|
|
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
|
|
|
|
expression();
|
|
|
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
|
|
|
|
|
|
|
|
int exitJump = emitJump(OP_JUMP_IF_FALSE);
|
|
|
|
emitByte(OP_POP);
|
|
|
|
statement();
|
|
|
|
emitLoop(loopStart);
|
|
|
|
|
|
|
|
patchJump(exitJump);
|
|
|
|
emitByte(OP_POP);
|
|
|
|
}
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
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() {
|
2022-08-12 21:17:45 -07:00
|
|
|
if (match(TOKEN_CLASS)) {
|
|
|
|
classDeclaration();
|
|
|
|
} else if (match(TOKEN_FUN)) {
|
2022-03-13 19:41:34 -07:00
|
|
|
funDeclaration();
|
|
|
|
} else if (match(TOKEN_VAR)) {
|
2021-09-20 21:47:41 -07:00
|
|
|
varDeclaration();
|
|
|
|
} else {
|
|
|
|
statement();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parser.panicMode)
|
|
|
|
synchronize();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void statement() {
|
|
|
|
if (match(TOKEN_PRINT)) {
|
|
|
|
printStatement();
|
2022-01-21 21:24:26 -08:00
|
|
|
} else if (match(TOKEN_FOR)) {
|
|
|
|
forStatement();
|
2022-01-12 22:58:10 -08:00
|
|
|
} else if (match(TOKEN_IF)) {
|
|
|
|
ifStatement();
|
2022-03-22 16:45:48 -07:00
|
|
|
} else if (match(TOKEN_RETURN)) {
|
|
|
|
returnStatement();
|
2022-01-12 23:09:36 -08:00
|
|
|
} else if (match(TOKEN_WHILE)) {
|
|
|
|
whileStatement();
|
2021-10-21 20:18:48 -07:00
|
|
|
} else if (match(TOKEN_LEFT_BRACE)) {
|
|
|
|
beginScope();
|
|
|
|
block();
|
|
|
|
endScope();
|
2021-09-20 21:47:41 -07:00
|
|
|
} else {
|
|
|
|
expressionStatement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
static void number(bool canAssign) {
|
2021-08-16 22:17:40 -07:00
|
|
|
double value = strtod(parser.previous.start, NULL);
|
2021-08-25 01:08:14 -07:00
|
|
|
emitConstant(NUMBER_VAL(value));
|
2021-08-16 22:17:40 -07:00
|
|
|
}
|
|
|
|
|
2022-01-12 22:58:10 -08:00
|
|
|
static void and_(bool canAssign) {
|
|
|
|
int endJump = emitJump(OP_JUMP_IF_FALSE);
|
|
|
|
|
|
|
|
emitByte(OP_POP);
|
|
|
|
parsePrecedence(PREC_AND);
|
|
|
|
|
|
|
|
patchJump(endJump);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void or_(bool canAssign) {
|
|
|
|
int elseJump = emitJump(OP_JUMP_IF_FALSE);
|
|
|
|
int endJump = emitJump(OP_JUMP);
|
|
|
|
|
|
|
|
patchJump(elseJump);
|
|
|
|
emitByte(OP_POP);
|
|
|
|
|
|
|
|
parsePrecedence(PREC_OR);
|
|
|
|
patchJump(endJump);
|
|
|
|
}
|
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
static void string(bool canAssign) {
|
2021-09-07 22:34:21 -07:00
|
|
|
emitConstant(OBJ_VAL(
|
|
|
|
copyString(parser.previous.start + 1, parser.previous.length - 2)));
|
|
|
|
}
|
|
|
|
|
2022-08-13 14:52:57 -07:00
|
|
|
static void this_(bool canAssign) {
|
|
|
|
if (currentClass == NULL) {
|
|
|
|
error("Can't use 'this' outside of a class.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
variable(false);
|
|
|
|
}
|
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
static void unary(bool canAssign) {
|
2021-08-16 22:17:40 -07:00
|
|
|
TokenType operatorType = parser.previous.type;
|
|
|
|
|
|
|
|
// Compile the operand.
|
|
|
|
parsePrecedence(PREC_UNARY);
|
|
|
|
|
|
|
|
// Emit the operator instruction.
|
|
|
|
switch (operatorType) {
|
2021-09-06 02:03:31 -07:00
|
|
|
case TOKEN_BANG:
|
|
|
|
emitByte(OP_NOT);
|
|
|
|
break;
|
2021-08-16 22:17:40 -07:00
|
|
|
case TOKEN_MINUS:
|
|
|
|
emitByte(OP_NEGATE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return; // Unreachable.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
static void grouping(bool canAssign) {
|
2021-08-16 22:17:40 -07:00
|
|
|
expression();
|
|
|
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
|
|
|
|
}
|
|
|
|
|
2021-08-17 20:41:54 -07:00
|
|
|
ParseRule rules[] = {
|
2022-03-15 00:39:11 -07:00
|
|
|
[TOKEN_LEFT_PAREN] = {grouping, call, PREC_CALL},
|
2021-08-17 20:41:54 -07:00
|
|
|
[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},
|
2022-08-13 00:04:31 -07:00
|
|
|
[TOKEN_DOT] = {NULL, dot, PREC_CALL},
|
2021-08-17 20:41:54 -07:00
|
|
|
[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},
|
2021-09-06 02:03:31 -07:00
|
|
|
[TOKEN_BANG] = {unary, NULL, PREC_NONE},
|
|
|
|
[TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY},
|
2021-08-17 20:41:54 -07:00
|
|
|
[TOKEN_EQUAL] = {NULL, NULL, PREC_NONE},
|
2021-09-06 02:03:31 -07:00
|
|
|
[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},
|
2021-10-11 00:13:15 -07:00
|
|
|
[TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE},
|
2021-09-07 22:34:21 -07:00
|
|
|
[TOKEN_STRING] = {string, NULL, PREC_NONE},
|
2021-08-17 20:41:54 -07:00
|
|
|
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
|
2022-01-12 22:58:10 -08:00
|
|
|
[TOKEN_AND] = {NULL, and_, PREC_AND},
|
2021-08-17 20:41:54 -07:00
|
|
|
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
|
|
|
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
2021-09-06 02:03:31 -07:00
|
|
|
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
|
2021-08-17 20:41:54 -07:00
|
|
|
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
|
|
|
|
[TOKEN_FUN] = {NULL, NULL, PREC_NONE},
|
|
|
|
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
2021-09-06 02:03:31 -07:00
|
|
|
[TOKEN_NIL] = {literal, NULL, PREC_NONE},
|
2022-01-12 22:58:10 -08:00
|
|
|
[TOKEN_OR] = {NULL, or_, PREC_OR},
|
2021-08-17 20:41:54 -07:00
|
|
|
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
|
|
|
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
|
|
|
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
2022-08-13 14:52:57 -07:00
|
|
|
[TOKEN_THIS] = {this_, NULL, PREC_NONE},
|
2021-09-06 02:03:31 -07:00
|
|
|
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
2021-08-17 20:41:54 -07:00
|
|
|
[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 void parsePrecedence(Precedence precedence) {
|
|
|
|
advance();
|
|
|
|
ParseFn prefixRule = getRule(parser.previous.type)->prefix;
|
|
|
|
if (prefixRule == NULL) {
|
|
|
|
error("Expect expression.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-21 19:51:01 -07:00
|
|
|
bool canAssign = precedence <= PREC_ASSIGNMENT;
|
|
|
|
prefixRule(canAssign);
|
2021-08-17 20:41:54 -07:00
|
|
|
|
|
|
|
while (precedence <= getRule(parser.current.type)->precedence) {
|
|
|
|
advance();
|
|
|
|
ParseFn infixRule = getRule(parser.previous.type)->infix;
|
2021-10-21 19:51:01 -07:00
|
|
|
infixRule(canAssign);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canAssign && match(TOKEN_EQUAL)) {
|
|
|
|
error("Invalid assignment target.");
|
2021-08-17 20:41:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
static uint8_t identifierConstant(Token *name) {
|
|
|
|
return makeConstant(OBJ_VAL(copyString(name->start, name->length)));
|
|
|
|
}
|
|
|
|
|
2021-10-25 21:49:54 -07:00
|
|
|
static int resolveLocal(Compiler *compiler, Token *name) {
|
|
|
|
for (int i = compiler->localCount - 1; i >= 0; i--) {
|
|
|
|
Local *local = &compiler->locals[i];
|
|
|
|
if (identifiersEqual(name, &local->name)) {
|
|
|
|
if (local->depth == -1) {
|
|
|
|
error("Can't read local variable in its own initializer.");
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-05-31 23:02:18 -07:00
|
|
|
static int addUpvalue(Compiler *compiler, uint8_t index, bool isLocal) {
|
|
|
|
int upvalueCount = compiler->function->upvalueCount;
|
|
|
|
|
|
|
|
for (int i = 0; i < upvalueCount; i++) {
|
|
|
|
Upvalue *upvalue = &compiler->upvalues[i];
|
|
|
|
if (upvalue->index == index && upvalue->isLocal == isLocal) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (upvalueCount == UINT8_COUNT) {
|
|
|
|
error("Too many closure variables in function.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
compiler->upvalues[upvalueCount].isLocal = isLocal;
|
|
|
|
compiler->upvalues[upvalueCount].index = index;
|
|
|
|
return compiler->function->upvalueCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int resolveUpvalue(Compiler *compiler, Token *name) {
|
|
|
|
if (compiler->enclosing == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
int local = resolveLocal(compiler->enclosing, name);
|
|
|
|
if (local != -1) {
|
2022-06-02 22:27:43 -07:00
|
|
|
compiler->enclosing->locals[local].isCaptured = true;
|
2022-05-31 23:02:18 -07:00
|
|
|
return addUpvalue(compiler, (uint8_t)local, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
int upvalue = resolveUpvalue(compiler->enclosing, name);
|
|
|
|
if (upvalue != -1) {
|
|
|
|
return addUpvalue(compiler, (uint8_t)upvalue, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
static uint8_t parseVariable(const char *errorMessage) {
|
|
|
|
consume(TOKEN_IDENTIFIER, errorMessage);
|
2021-10-21 20:54:35 -07:00
|
|
|
|
|
|
|
declareVariable();
|
|
|
|
if (current->scopeDepth > 0)
|
|
|
|
return 0;
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
return identifierConstant(&parser.previous);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void defineVariable(uint8_t global) {
|
2021-10-21 20:54:35 -07:00
|
|
|
if (current->scopeDepth > 0) {
|
2021-10-25 21:49:54 -07:00
|
|
|
markInitialized();
|
2021-10-21 20:54:35 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-20 21:47:41 -07:00
|
|
|
emitBytes(OP_DEFINE_GLOBAL, global);
|
|
|
|
}
|
|
|
|
|
2021-08-17 20:41:54 -07:00
|
|
|
static ParseRule *getRule(TokenType type) { return &rules[type]; }
|
|
|
|
|
2022-01-22 22:01:54 -08:00
|
|
|
ObjFunction *compile(const char *source) {
|
2021-08-16 22:17:40 -07:00
|
|
|
initScanner(source);
|
2021-10-21 20:00:19 -07:00
|
|
|
Compiler compiler;
|
2022-01-22 22:01:54 -08:00
|
|
|
initCompiler(&compiler, TYPE_SCRIPT);
|
2021-08-16 22:17:40 -07:00
|
|
|
|
|
|
|
parser.hadError = false;
|
|
|
|
parser.panicMode = false;
|
|
|
|
|
|
|
|
advance();
|
2021-09-20 21:47:41 -07:00
|
|
|
|
|
|
|
while (!match(TOKEN_EOF)) {
|
|
|
|
declaration();
|
|
|
|
}
|
|
|
|
|
2022-01-22 22:01:54 -08:00
|
|
|
ObjFunction *function = endCompiler();
|
|
|
|
return parser.hadError ? NULL : function;
|
2021-08-02 18:35:10 -07:00
|
|
|
}
|
2022-06-04 22:00:27 -07:00
|
|
|
|
|
|
|
void markCompilerRoots() {
|
|
|
|
Compiler *compiler = current;
|
|
|
|
while (compiler != NULL) {
|
|
|
|
markObject((Obj *)compiler->function);
|
|
|
|
compiler = compiler->enclosing;
|
|
|
|
}
|
|
|
|
}
|