|
|
@@ -73,6 +73,7 @@ typedef struct Compiler {
|
|
|
|
|
|
typedef struct ClassCompiler {
|
|
|
struct ClassCompiler *enclosing;
|
|
|
+ bool hasSuperclass;
|
|
|
} ClassCompiler;
|
|
|
|
|
|
Parser parser;
|
|
|
@@ -90,6 +91,7 @@ static void statement();
|
|
|
static void declaration();
|
|
|
static void markInitialized();
|
|
|
static void namedVariable(Token name, bool canAssign);
|
|
|
+static void addLocal(Token name);
|
|
|
|
|
|
static void initCompiler(Compiler *compiler, FunctionType type);
|
|
|
|
|
|
@@ -98,6 +100,8 @@ static void endScope();
|
|
|
|
|
|
static void block();
|
|
|
static ObjFunction *endCompiler();
|
|
|
+static void variable(bool);
|
|
|
+static uint8_t argumentList();
|
|
|
|
|
|
static Chunk *currentChunk() {
|
|
|
return ¤t->function->chunk;
|
|
|
@@ -249,6 +253,12 @@ static void method() {
|
|
|
|
|
|
emitBytes(OP_METHOD, constant);
|
|
|
}
|
|
|
+static Token syntheticToken(const char *text) {
|
|
|
+ Token token;
|
|
|
+ token.start = text;
|
|
|
+ token.length = (int) strlen(text);
|
|
|
+ return token;
|
|
|
+}
|
|
|
static void classDeclaration() {
|
|
|
consume(TOKEN_IDENTIFIER, "Expect class name.");
|
|
|
Token className = parser.previous;
|
|
|
@@ -260,8 +270,27 @@ static void classDeclaration() {
|
|
|
|
|
|
ClassCompiler classCompiler;
|
|
|
classCompiler.enclosing = currentClass;
|
|
|
+ classCompiler.hasSuperclass = false;
|
|
|
currentClass = &classCompiler;
|
|
|
|
|
|
+ // Superclass
|
|
|
+ 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.");
|
|
|
+ }
|
|
|
+
|
|
|
+ beginScope();
|
|
|
+ addLocal(syntheticToken("super"));
|
|
|
+ defineVariable(0);
|
|
|
+
|
|
|
+ namedVariable(className, false);
|
|
|
+ emitByte(OP_INHERIT);
|
|
|
+ classCompiler.hasSuperclass = true;
|
|
|
+ }
|
|
|
+
|
|
|
namedVariable(className, false);
|
|
|
consume(TOKEN_LEFT_BRACE, "Expect '{' before class body.");
|
|
|
while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
|
|
|
@@ -270,6 +299,10 @@ static void classDeclaration() {
|
|
|
consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body.");
|
|
|
emitByte(OP_POP);
|
|
|
|
|
|
+ if (classCompiler.hasSuperclass) {
|
|
|
+ endScope();
|
|
|
+ }
|
|
|
+
|
|
|
currentClass = currentClass->enclosing;
|
|
|
}
|
|
|
static void funDeclaration() {
|
|
|
@@ -673,6 +706,28 @@ static void namedVariable(Token name, bool canAssign) {
|
|
|
static void variable(bool canAssign) {
|
|
|
namedVariable(parser.previous, canAssign);
|
|
|
}
|
|
|
+static void super_(__attribute__((unused)) bool canAssign) {
|
|
|
+ if (currentClass == NULL) {
|
|
|
+ error("Can't use 'super' outside of a class.");
|
|
|
+ } else if (!currentClass->hasSuperclass) {
|
|
|
+ error("Can't use 'super' in a class with no superclass.");
|
|
|
+ }
|
|
|
+
|
|
|
+ consume(TOKEN_DOT, "Expect '.' after 'super'.");
|
|
|
+ consume(TOKEN_IDENTIFIER, "Expect superclass method name.");
|
|
|
+ uint8_t name = identifierConstant(&parser.previous);
|
|
|
+
|
|
|
+ namedVariable(syntheticToken("this"), false);
|
|
|
+ if (match(TOKEN_LEFT_PAREN)) {
|
|
|
+ uint8_t argCount = argumentList();
|
|
|
+ namedVariable(syntheticToken("super"), false);
|
|
|
+ emitBytes(OP_SUPER_INVOKE, name);
|
|
|
+ emitByte(argCount);
|
|
|
+ } else {
|
|
|
+ namedVariable(syntheticToken("super"), false);
|
|
|
+ emitBytes(OP_GET_SUPER, name);
|
|
|
+ }
|
|
|
+}
|
|
|
static void this_(__attribute__((unused)) bool canAssign) {
|
|
|
if (currentClass == NULL) {
|
|
|
error("Can't use 'this' outside of a class.");
|
|
|
@@ -824,7 +879,7 @@ ParseRule rules[] = {
|
|
|
[TOKEN_OR] = {NULL, or_, PREC_OR},
|
|
|
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
|
|
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
|
|
- [TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
|
|
+ [TOKEN_SUPER] = {super_, NULL, PREC_NONE},
|
|
|
[TOKEN_THIS] = {this_, NULL, PREC_NONE},
|
|
|
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
|
|
[TOKEN_VAR] = {NULL, NULL, PREC_NONE},
|