Skip to content

Commit dd7c5ca

Browse files
committed
feat: implement inheritance, and start into accessing super
1 parent a2d6cfa commit dd7c5ca

6 files changed

Lines changed: 69 additions & 3 deletions

File tree

src/chunk.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ typedef enum
3939
OP_CLOSE_UPVALUE,
4040
OP_RETURN,
4141
OP_CLASS,
42+
OP_INHERIT,
43+
OP_GET_SUPER,
4244
OP_METHOD,
4345
} OpCode;
4446

src/compiler.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ typedef struct ClassCompiler
8787
{
8888
struct ClassCompiler* enclosing;
8989
Token name;
90+
bool has_super_class;
9091
} ClassCompiler;
9192

9293
static bool immutable_globals[UINT8_MAX];
@@ -644,6 +645,28 @@ static void variable(bool can_assign)
644645
named_variable(parser.previous, can_assign);
645646
}
646647

648+
static Token synthetic_token(const char* text)
649+
{
650+
return (Token){.start = text,
651+
.length = (int)strlen(text)};
652+
}
653+
654+
static void super_(bool can_assign)
655+
{
656+
if (current_class == NULL)
657+
error("Can't use 'super' outside of a class");
658+
else if (!current_class->has_super_class)
659+
error("Can't use 'super' in a class with no superclass");
660+
661+
consume(TOKEN_DOT, "Expect '.' after a super.");
662+
consume(TOKEN_IDENTIFIER, "Expect super class method name.");
663+
u8 name = identifier_constant(&parser.previous);
664+
665+
named_variable(synthetic_token("this"), false);
666+
named_variable(synthetic_token("super"), false);
667+
emit_bytes(OP_GET_SUPER, name);
668+
}
669+
647670
static void this_(bool can_assign)
648671
{
649672
if (current_class == NULL)
@@ -707,8 +730,8 @@ ParseRule rules[] = {
707730
[TOKEN_OR] = { NULL, or_, PREC_OR },
708731
[TOKEN_PRINT] = { NULL, NULL, PREC_NONE },
709732
[TOKEN_RETURN] = { NULL, NULL, PREC_NONE },
710-
[TOKEN_SUPER] = { NULL, NULL, PREC_NONE },
711-
[TOKEN_THIS] = { this_, NULL, PREC_NONE },
733+
[TOKEN_SUPER] = { super_, NULL, PREC_NONE },
734+
[TOKEN_THIS] = { this_, NULL, PREC_NONE },
712735
[TOKEN_TRUE] = { literal, NULL, PREC_NONE },
713736
[TOKEN_VAR] = { NULL, NULL, PREC_NONE },
714737
[TOKEN_WHILE] = { NULL, NULL, PREC_NONE },
@@ -824,9 +847,28 @@ static void class_declaration()
824847
emit_bytes(OP_CLASS, name_constant);
825848
define_variable(name_constant, true);
826849

827-
ClassCompiler class_compiler = {.name = parser.previous, .enclosing = current_class};
850+
ClassCompiler class_compiler = {.name = parser.previous,
851+
.has_super_class = false,
852+
.enclosing = current_class};
828853
current_class = &class_compiler;
829854

855+
if (match(TOKEN_COLON))
856+
{
857+
consume(TOKEN_IDENTIFIER, "Expect super class name.");
858+
variable(false);
859+
860+
if (identifiers_equal(&class_name, &parser.previous))
861+
error("A class can't inherit from itself");
862+
863+
begin_scope();
864+
add_local(synthetic_token("super"), false);
865+
define_variable(0, false);
866+
867+
named_variable(class_name, false);
868+
emit_byte(OP_INHERIT);
869+
class_compiler.has_super_class = true;
870+
}
871+
830872
named_variable(class_name, false);
831873
consume(TOKEN_LEFT_BRACE, "Expected '{' after class name");
832874
while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF))
@@ -836,6 +878,9 @@ static void class_declaration()
836878
consume(TOKEN_RIGHT_BRACE, "Expected '}' after class body");
837879
emit_byte(OP_POP);
838880

881+
if (class_compiler.has_super_class)
882+
end_scope();
883+
839884
current_class = current_class->enclosing;
840885
}
841886

src/debug.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ int disassemble_instruction(Chunk* chunk, int offset)
116116
return simple_instruction("OP_RETURN", offset);
117117
case OP_CLASS:
118118
return constant_instruction("OP_CLASS", chunk, offset);
119+
case OP_INHERIT:
120+
return simple_instruction("OP_INHERIT", offset);
119121
case OP_METHOD:
120122
return constant_instruction("OP_METHOD", chunk, offset);
121123
default:
@@ -188,6 +190,8 @@ static const char* token_type_to_string(TokenType type)
188190
return "PLUS";
189191
case TOKEN_SEMICOLON:
190192
return "SEMICOLON";
193+
case TOKEN_COLON:
194+
return "COLON";
191195
case TOKEN_SLASH:
192196
return "SLASH";
193197
case TOKEN_STAR:

src/scanner.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ Token scan_token()
252252
return make_token(TOKEN_RIGHT_BRACE);
253253
case ';':
254254
return make_token(TOKEN_SEMICOLON);
255+
case ':':
256+
return make_token(TOKEN_COLON);
255257
case ',':
256258
return make_token(TOKEN_COMMA);
257259
case '.':

src/scanner.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ typedef enum
1414
TOKEN_MINUS,
1515
TOKEN_PLUS,
1616
TOKEN_SEMICOLON,
17+
TOKEN_COLON,
1718
TOKEN_SLASH,
1819
TOKEN_STAR,
1920

src/vm.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,18 @@ static InterpretResult run()
576576
case OP_CLASS:
577577
push(OBJ_VAL(new_class(READ_STRING())));
578578
break;
579+
case OP_INHERIT:
580+
Value super_class = peek(1);
581+
582+
if (!IS_CLASS(super_class))
583+
{
584+
runtime_error("Superclass must be a class");
585+
return INTERPRET_RUNTIME_ERROR;
586+
}
587+
ObjClass* sub_class = AS_CLASS(peek(0));
588+
table_add_all(&AS_CLASS(super_class)->methods, &sub_class->methods);
589+
pop(); // sub_class
590+
break;
579591
case OP_METHOD:
580592
define_method(READ_STRING());
581593
break;

0 commit comments

Comments
 (0)