|
|
@@ -54,11 +54,12 @@ typedef struct {
|
|
|
bool isLocal;
|
|
|
} Upvalue;
|
|
|
typedef enum {
|
|
|
+ TYPE_METHOD,
|
|
|
TYPE_FUNCTION,
|
|
|
TYPE_SCRIPT,
|
|
|
} FunctionType;
|
|
|
|
|
|
-typedef struct Compiler {
|
|
|
+typedef struct {
|
|
|
struct Compiler *enclosing;
|
|
|
ObjFunction *function;
|
|
|
FunctionType type;
|
|
|
@@ -69,8 +70,13 @@ typedef struct Compiler {
|
|
|
int scopeDepth;
|
|
|
} Compiler;
|
|
|
|
|
|
+typedef struct ClassCompiler {
|
|
|
+ struct ClassCompiler *enclosing;
|
|
|
+} ClassCompiler;
|
|
|
+
|
|
|
Parser parser;
|
|
|
Compiler *current = NULL;
|
|
|
+ClassCompiler *currentClass = NULL;
|
|
|
|
|
|
static void parsePrecedence(Precedence);
|
|
|
static uint8_t parseVariable(const char *);
|
|
|
@@ -82,6 +88,7 @@ static ParseRule *getRule(TokenType);
|
|
|
static void statement();
|
|
|
static void declaration();
|
|
|
static void markInitialized();
|
|
|
+static void namedVariable(Token name, bool canAssign);
|
|
|
|
|
|
static void initCompiler(Compiler *compiler, FunctionType type);
|
|
|
|
|
|
@@ -211,16 +218,45 @@ static void function(FunctionType type) {
|
|
|
emitByte(compiler.upvalues[i].index);
|
|
|
}
|
|
|
}
|
|
|
+static void method() {
|
|
|
+ //! To define a new method, the VM needs three things:
|
|
|
+ //!
|
|
|
+ //! 1. The name of the method.
|
|
|
+ //!
|
|
|
+ //! 2. The closure for the method body.
|
|
|
+ //!
|
|
|
+ //! 3. The class to bind the method to.
|
|
|
+ consume(TOKEN_IDENTIFIER, "Expect method name.");
|
|
|
+ uint8_t constant = identifierConstant(&parser.previous);
|
|
|
+
|
|
|
+ // Method Body ObjClosure <OP_CLOSURE>
|
|
|
+ FunctionType type = TYPE_METHOD;
|
|
|
+ function(type);
|
|
|
+
|
|
|
+ emitBytes(OP_METHOD, constant);
|
|
|
+}
|
|
|
static void classDeclaration() {
|
|
|
consume(TOKEN_IDENTIFIER, "Expect class name.");
|
|
|
+ Token className = parser.previous;
|
|
|
uint8_t nameConstant = identifierConstant(&parser.previous);
|
|
|
declareVariable();
|
|
|
|
|
|
emitBytes(OP_CLASS, nameConstant);
|
|
|
defineVariable(nameConstant);
|
|
|
|
|
|
+ ClassCompiler classCompiler;
|
|
|
+ classCompiler.enclosing = currentClass;
|
|
|
+ currentClass = &classCompiler;
|
|
|
+
|
|
|
+ namedVariable(className, false);
|
|
|
consume(TOKEN_LEFT_BRACE, "Expect '{' before class body.");
|
|
|
+ while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
|
|
|
+ method();
|
|
|
+ }
|
|
|
consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body.");
|
|
|
+ emitByte(OP_POP);
|
|
|
+
|
|
|
+ currentClass = currentClass->enclosing;
|
|
|
}
|
|
|
static void funDeclaration() {
|
|
|
uint8_t global = parseVariable("Expect function name.");
|
|
|
@@ -478,8 +514,13 @@ static void initCompiler(Compiler *compiler, FunctionType type) {
|
|
|
Local *local = ¤t->locals[current->localCount++];
|
|
|
local->depth = 0;
|
|
|
local->isCaptured = false;
|
|
|
- local->name.start = "";
|
|
|
- local->name.length = 0;
|
|
|
+ if (type != TYPE_FUNCTION) {
|
|
|
+ local->name.start = "this";
|
|
|
+ local->name.length = 4;
|
|
|
+ } else {
|
|
|
+ local->name.start = "";
|
|
|
+ local->name.length = 0;
|
|
|
+ }
|
|
|
}
|
|
|
static ObjFunction *endCompiler() {
|
|
|
emitReturn();
|
|
|
@@ -614,6 +655,13 @@ static void namedVariable(Token name, bool canAssign) {
|
|
|
static void variable(bool canAssign) {
|
|
|
namedVariable(parser.previous, canAssign);
|
|
|
}
|
|
|
+static void this_(__attribute__((unused)) bool canAssign) {
|
|
|
+ if (currentClass == NULL) {
|
|
|
+ error("Can't use 'this' outside of a class.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ variable(false);
|
|
|
+}
|
|
|
/// Unary negation: -123
|
|
|
static void unary(__attribute__((unused)) bool canAssign) {
|
|
|
TokenType operatorType = parser.previous.type;
|
|
|
@@ -754,7 +802,7 @@ ParseRule rules[] = {
|
|
|
[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_THIS] = {this_, NULL, PREC_NONE},
|
|
|
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
|
|
[TOKEN_VAR] = {NULL, NULL, PREC_NONE},
|
|
|
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|