Skip to content

Commit 1695915

Browse files
committed
support for empty classes
1 parent 79ca399 commit 1695915

9 files changed

Lines changed: 83 additions & 16 deletions

File tree

src/chunk.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ size_t lox::Chunk::disassemble(std::ostream& os, size_t offset,
234234
os << "POP\n";
235235
return offset + 1;
236236
}
237+
case OpCode::CLASS: {
238+
uint8_t constant_index = code[offset + 1];
239+
Value constant = constants[constant_index];
240+
os << "CLASS " << constant << "\n";
241+
return offset + 2;
242+
}
237243
case OpCode::GET_GLOBAL: {
238244
uint8_t constant_index = code[offset + 1];
239245
Value constant = constants[constant_index];

src/chunk.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ enum class OpCode {
3838
GET_UPVALUE,
3939
SET_UPVALUE,
4040
CLOSE_UPVALUE,
41+
CLASS,
4142
// more to come
4243
};
4344

src/compiler.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,11 +386,33 @@ void Parser::declaration() {
386386
var_declaration();
387387
} else if (consume_if(TokenType::FUN)) {
388388
function();
389+
} else if (consume_if(TokenType::CLASS)) {
390+
class_declaration();
389391
} else {
390392
statement();
391393
}
392394
}
393395

396+
void Parser::class_declaration() {
397+
// class token already consumed
398+
consume_or_error(TokenType::IDENTIFIER, "expected class name");
399+
std::string_view class_name = previous.lexeme;
400+
401+
// Store the class name in the constant table. The CLASS instruction will
402+
// construct the ObjClass at runtime and put it on the stack.
403+
uint8_t name_constant_index = make_constant(gc.get_string_ptr(class_name));
404+
emit(lox::OpCode::CLASS);
405+
emit(name_constant_index);
406+
407+
// This call will emit code to read from the top of the stack and create
408+
// either a local or global variable with the class.
409+
define_variable(class_name);
410+
411+
consume_or_error(TokenType::LEFT_BRACE, "expected '{' before class body");
412+
// TODO class body
413+
consume_or_error(TokenType::RIGHT_BRACE, "expected '}' after class body");
414+
}
415+
394416
void Parser::var_declaration() {
395417
// Parse identifier
396418
consume_or_error(TokenType::IDENTIFIER, "expected variable name");

src/compiler.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class Parser {
131131
void function();
132132
void declaration();
133133
void var_declaration();
134+
void class_declaration();
134135
void statement();
135136
void print_statement();
136137
void if_statement();

src/gc.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ void GC::gc() {
8888
break;
8989
case ObjType::NATIVE_FUNCTION:
9090
break;
91+
case ObjType::CLASS:
92+
break;
9193
case ObjType::FUNCTION: {
9294
ObjFunction* p = static_cast<ObjFunction*>(objptr);
9395
for (const auto& constant : p->chunk.get_constants()) {
@@ -111,6 +113,11 @@ void GC::gc() {
111113
}
112114
break;
113115
}
116+
case ObjType::INSTANCE: {
117+
// TODO
118+
throw std::runtime_error(
119+
"GC: mark_as_grey: unimplemented for ObjType::INSTANCE");
120+
}
114121
}
115122

116123
#ifdef LOX_GC_DEBUG

src/value.hpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include <functional>
66
#include <iostream>
77
#include <stddef.h>
8-
#include <stdexcept>
98
#include <stdint.h>
109
#include <string>
1110
#include <string_view>
@@ -16,7 +15,15 @@ namespace lox {
1615

1716
enum class InterpretResult { OK, COMPILE_ERROR, RUNTIME_ERROR };
1817

19-
enum class ObjType { STRING, FUNCTION, UPVALUE, CLOSURE, NATIVE_FUNCTION };
18+
enum class ObjType {
19+
STRING,
20+
FUNCTION,
21+
UPVALUE,
22+
CLOSURE,
23+
NATIVE_FUNCTION,
24+
CLASS,
25+
INSTANCE
26+
};
2027

2128
// Forward declarations
2229
class GC;
@@ -126,6 +133,29 @@ class ObjNativeFunction : public Obj {
126133
std::function<Value(size_t arg_count, const Value* args)> function;
127134
};
128135

136+
class ObjClass : public Obj {
137+
public:
138+
ObjClass(std::string_view name)
139+
: Obj(ObjType::CLASS), name(std::string(name)) {}
140+
141+
std::string to_repr() const override { return "<class " + name + ">"; }
142+
143+
private:
144+
std::string name;
145+
};
146+
147+
class ObjInstance : public Obj {
148+
public:
149+
ObjInstance(ObjClass* klass) : Obj(ObjType::INSTANCE), klass(klass) {}
150+
151+
std::string to_repr() const override {
152+
return "<instance of " + klass->to_repr() + ">";
153+
}
154+
155+
private:
156+
ObjClass* klass;
157+
};
158+
129159
bool is_truthy(const Value& value);
130160
bool is_equal(const Value& a, const Value& b);
131161
Value add(const Value& a, const Value& b, GC& gc);

src/vm.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ lox::Value VM::stack_peek() {
142142
return stack.back();
143143
}
144144

145-
std::string VM::read_global_name() {
145+
std::string VM::read_constant_string() {
146146
lox::Value var_name_value = read_constant();
147147
// This is technically unsafe since the constant could be any Value, but
148148
// by construction of the parser it should always be a string.
@@ -399,7 +399,7 @@ InterpretResult VM::run() {
399399
// have a DEFINE_GLOBAL instruction followed by the constant index.
400400
// When we get here we have already seen the DEFINE_GLOBAL
401401
// instruction, so we need to read the variable name.
402-
std::string var_name = read_global_name();
402+
std::string var_name = read_constant_string();
403403
// After this, the parser will have emitted bytecode that pushes the
404404
// value of the variable onto the stack. So we need to pop that value.
405405
// (Or, following the book, just peek it now and pop it later, after
@@ -414,8 +414,14 @@ InterpretResult VM::run() {
414414
stack_pop(); // now pop the value
415415
break;
416416
}
417+
case OpCode::CLASS: {
418+
std::string class_name = read_constant_string();
419+
auto new_class = _gc.alloc<ObjClass>(class_name);
420+
stack_push(new_class);
421+
break;
422+
}
417423
case OpCode::GET_GLOBAL: {
418-
std::string var_name = read_global_name();
424+
std::string var_name = read_constant_string();
419425
// Now that we have the name of the variable, we can look it up in our
420426
// map
421427
auto it = globals.find(var_name);
@@ -426,7 +432,7 @@ InterpretResult VM::run() {
426432
break;
427433
}
428434
case OpCode::SET_GLOBAL: {
429-
std::string var_name = read_global_name();
435+
std::string var_name = read_constant_string();
430436
auto it = globals.find(var_name);
431437
if (it == globals.end()) {
432438
error("undefined variable '" + var_name + "'");

src/vm.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class VM {
110110
// pointer.
111111
lox::Value read_constant();
112112
// Read the name of a global variable from the chunk's constant table.
113-
std::string read_global_name();
113+
std::string read_constant_string();
114114

115115
void error(const std::string& message);
116116

test.lox

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
fun bench() {
2-
fun add(a, b) { return a + b; }
3-
var result = 0;
4-
for (var i = 0; i < 10000000; i = i + 1) {
5-
result = add(result, 1);
6-
}
7-
return result;
8-
}
9-
print bench();
1+
class Cat {}
2+
3+
print Cat;

0 commit comments

Comments
 (0)