Chapter 28.1
This commit is contained in:
parent
4e77cfa612
commit
07f691425c
7 changed files with 57 additions and 24 deletions
|
@ -37,6 +37,7 @@ typedef enum {
|
||||||
OP_CLOSE_UPVALUE,
|
OP_CLOSE_UPVALUE,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
OP_CLASS,
|
OP_CLASS,
|
||||||
|
OP_METHOD,
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -374,6 +374,15 @@ static void function(FunctionType type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void method() {
|
||||||
|
consume(TOKEN_IDENTIFIER, "Expect method name.");
|
||||||
|
uint8_t constant = identifierConstant(&parser.previous);
|
||||||
|
|
||||||
|
FunctionType type = TYPE_FUNCTION;
|
||||||
|
function(type);
|
||||||
|
emitBytes(OP_METHOD, constant);
|
||||||
|
}
|
||||||
|
|
||||||
static bool identifiersEqual(Token *a, Token *b) {
|
static bool identifiersEqual(Token *a, Token *b) {
|
||||||
if (a->length != b->length)
|
if (a->length != b->length)
|
||||||
return false;
|
return false;
|
||||||
|
@ -411,16 +420,46 @@ static void declareVariable() {
|
||||||
addLocal(*name);
|
addLocal(*name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void classDeclaration() {
|
static void classDeclaration() {
|
||||||
consume(TOKEN_IDENTIFIER, "Expect class name");
|
consume(TOKEN_IDENTIFIER, "Expect class name");
|
||||||
|
Token className = parser.previous;
|
||||||
uint8_t nameConstant = identifierConstant(&parser.previous);
|
uint8_t nameConstant = identifierConstant(&parser.previous);
|
||||||
declareVariable();
|
declareVariable();
|
||||||
|
|
||||||
emitBytes(OP_CLASS, nameConstant);
|
emitBytes(OP_CLASS, nameConstant);
|
||||||
defineVariable(nameConstant);
|
defineVariable(nameConstant);
|
||||||
|
|
||||||
|
namedVariable(className, false);
|
||||||
consume(TOKEN_LEFT_BRACE, "Expect '{' before class body.");
|
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.");
|
consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body.");
|
||||||
|
emitByte(OP_POP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void markInitialized() {
|
static void markInitialized() {
|
||||||
|
@ -643,30 +682,6 @@ static void string(bool canAssign) {
|
||||||
copyString(parser.previous.start + 1, parser.previous.length - 2)));
|
copyString(parser.previous.start + 1, parser.previous.length - 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void variable(bool canAssign) {
|
static void variable(bool canAssign) {
|
||||||
namedVariable(parser.previous, canAssign);
|
namedVariable(parser.previous, canAssign);
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,8 @@ int disassembleInstruction(Chunk *chunk, int offset) {
|
||||||
return simpleInstruction("OP_RETURN", offset);
|
return simpleInstruction("OP_RETURN", offset);
|
||||||
case OP_CLASS:
|
case OP_CLASS:
|
||||||
return constantInstruction("OP_CLASS", chunk, offset);
|
return constantInstruction("OP_CLASS", chunk, offset);
|
||||||
|
case OP_METHOD:
|
||||||
|
return constantInstruction("OP_METHOD", chunk, offset);
|
||||||
default:
|
default:
|
||||||
printf("Unknown opcode %d\n", instruction);
|
printf("Unknown opcode %d\n", instruction);
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
|
|
|
@ -79,6 +79,7 @@ static void blackenObject(Obj *object) {
|
||||||
case OBJ_CLASS: {
|
case OBJ_CLASS: {
|
||||||
ObjClass *klass = (ObjClass *)object;
|
ObjClass *klass = (ObjClass *)object;
|
||||||
markObject((Obj *)klass->name);
|
markObject((Obj *)klass->name);
|
||||||
|
markTable(&klass->methods);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OBJ_CLOSURE: {
|
case OBJ_CLOSURE: {
|
||||||
|
@ -117,6 +118,8 @@ static void freeObject(Obj *object) {
|
||||||
|
|
||||||
switch (object->type) {
|
switch (object->type) {
|
||||||
case OBJ_CLASS: {
|
case OBJ_CLASS: {
|
||||||
|
ObjClass *klass = (ObjClass *)object;
|
||||||
|
freeTable(&klass->methods);
|
||||||
FREE(ObjClass, object);
|
FREE(ObjClass, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ static Obj *allocateObject(size_t size, ObjType type) {
|
||||||
ObjClass *newClass(ObjString *name) {
|
ObjClass *newClass(ObjString *name) {
|
||||||
ObjClass *klass = ALLOCATE_OBJ(ObjClass, OBJ_CLASS);
|
ObjClass *klass = ALLOCATE_OBJ(ObjClass, OBJ_CLASS);
|
||||||
klass->name = name;
|
klass->name = name;
|
||||||
|
initTable(&klass->methods);
|
||||||
return klass;
|
return klass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Obj obj;
|
Obj obj;
|
||||||
ObjString *name;
|
ObjString *name;
|
||||||
|
Table methods;
|
||||||
} ObjClass;
|
} ObjClass;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -163,6 +163,13 @@ static void closeUpvalues(Value *last) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void defineMethod(ObjString *name) {
|
||||||
|
Value method = peek(0);
|
||||||
|
ObjClass *klass = AS_CLASS(peek(1));
|
||||||
|
tableSet(&klass->methods, name, method);
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
|
||||||
static bool isFalsey(Value value) {
|
static bool isFalsey(Value value) {
|
||||||
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
|
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
|
||||||
}
|
}
|
||||||
|
@ -422,6 +429,9 @@ static InterpretResult run() {
|
||||||
case OP_CLASS:
|
case OP_CLASS:
|
||||||
push(OBJ_VAL(newClass(READ_STRING())));
|
push(OBJ_VAL(newClass(READ_STRING())));
|
||||||
break;
|
break;
|
||||||
|
case OP_METHOD:
|
||||||
|
defineMethod(READ_STRING());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue