mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
inheritance and 'is' test implemented
This commit is contained in:
parent
745387e307
commit
acf38a31ca
@ -120,6 +120,7 @@ typedef enum {
|
||||
|
||||
TK_NULL, // null
|
||||
TK_IN, // in
|
||||
TK_IS, // is
|
||||
TK_AND, // and
|
||||
TK_OR, // or
|
||||
TK_NOT, // not / !
|
||||
@ -183,6 +184,7 @@ static _Keyword _keywords[] = {
|
||||
{ "end", 3, TK_END },
|
||||
{ "null", 4, TK_NULL },
|
||||
{ "in", 2, TK_IN },
|
||||
{ "is", 2, TK_IS },
|
||||
{ "and", 3, TK_AND },
|
||||
{ "or", 2, TK_OR },
|
||||
{ "not", 3, TK_NOT },
|
||||
@ -1592,6 +1594,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
||||
/* TK_END */ NO_RULE,
|
||||
/* TK_NULL */ { exprValue, NULL, NO_INFIX },
|
||||
/* TK_IN */ { NULL, exprBinaryOp, PREC_TEST },
|
||||
/* TK_IS */ { NULL, exprBinaryOp, PREC_TEST },
|
||||
/* TK_AND */ { NULL, exprAnd, PREC_LOGICAL_AND },
|
||||
/* TK_OR */ { NULL, exprOr, PREC_LOGICAL_OR },
|
||||
/* TK_NOT */ { exprUnaryOp, NULL, PREC_UNARY },
|
||||
@ -1936,6 +1939,7 @@ static void exprBinaryOp(Compiler* compiler) {
|
||||
case TK_SRIGHT: emitOpcode(compiler, OP_BIT_RSHIFT); break;
|
||||
case TK_SLEFT: emitOpcode(compiler, OP_BIT_LSHIFT); break;
|
||||
case TK_IN: emitOpcode(compiler, OP_IN); break;
|
||||
case TK_IS: emitOpcode(compiler, OP_IS); break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -2442,11 +2446,21 @@ static int compileClass(Compiler* compiler) {
|
||||
|
||||
checkMaxConstantsReached(compiler, cls_index);
|
||||
|
||||
emitOpcode(compiler, OP_PUSH_CLASS);
|
||||
if (match(compiler, TK_IS)) {
|
||||
consume(compiler, TK_NAME, "Expected a class name to inherit.");
|
||||
if (!compiler->parser.has_syntax_error) {
|
||||
exprName(compiler); // Push the super class on the stack.
|
||||
}
|
||||
} else {
|
||||
// Implicitly inherit from 'Object' class.
|
||||
emitPushValue(compiler, NAME_BUILTIN_TY, (int)PK_OBJECT);
|
||||
}
|
||||
|
||||
emitOpcode(compiler, OP_CREATE_CLASS);
|
||||
emitShort(compiler, cls_index);
|
||||
|
||||
skipNewLines(compiler);
|
||||
while (!match(compiler, TK_END)) {
|
||||
while (!compiler->parser.has_syntax_error && !match(compiler, TK_END)) {
|
||||
|
||||
if (match(compiler, TK_EOF)) {
|
||||
syntaxError(compiler, compiler->parser.previous,
|
||||
@ -2471,7 +2485,6 @@ static int compileClass(Compiler* compiler) {
|
||||
compiler->func->stack_size == 1, OOPS);
|
||||
|
||||
skipNewLines(compiler);
|
||||
if (compiler->parser.has_syntax_error) break;
|
||||
}
|
||||
|
||||
int global_index = compilerAddVariable(compiler, name, name_len, name_line);
|
||||
|
@ -1148,6 +1148,23 @@ bool varContains(PKVM* vm, Var elem, Var container) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
bool varIsType(PKVM* vm, Var inst, Var type) {
|
||||
if (!IS_OBJ_TYPE(type, OBJ_CLASS)) {
|
||||
VM_SET_ERROR(vm, newString(vm, "Right operand must be a class."));
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
Class* cls = (Class*)AS_OBJ(type);
|
||||
Class* cls_inst = getClass(vm, inst);
|
||||
|
||||
do {
|
||||
if (cls_inst == cls) return true;
|
||||
cls_inst = cls_inst->super_class;
|
||||
} while (cls_inst != NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
|
||||
#define ERR_NO_ATTRIB(vm, on, attrib) \
|
||||
|
@ -62,9 +62,13 @@ Var varBitNot(PKVM* vm, Var v); // Returns ~v.
|
||||
bool varGreater(Var v1, Var v2); // Returns v1 > v2.
|
||||
bool varLesser(Var v1, Var v2); // Returns v1 < v2.
|
||||
|
||||
// Returns [elem] in [container].
|
||||
// Returns [elem] in [container]. Sets an error if the [container] is not an
|
||||
// iterable.
|
||||
bool varContains(PKVM* vm, Var elem, Var container);
|
||||
|
||||
// Returns [inst] is [type]. Sets an error if the [type] is not a class.
|
||||
bool varIsType(PKVM* vm, Var inst, Var type);
|
||||
|
||||
// Returns the attribute named [attrib] on the variable [on].
|
||||
Var varGetAttrib(PKVM* vm, Var on, String* attrib);
|
||||
|
||||
|
@ -472,7 +472,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PUSH_CLASS:
|
||||
case OP_CREATE_CLASS:
|
||||
{
|
||||
int index = READ_SHORT();
|
||||
ASSERT_INDEX((uint32_t)index, func->owner->constants.count);
|
||||
@ -609,6 +609,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
|
||||
case OP_GTEQ:
|
||||
case OP_RANGE:
|
||||
case OP_IN:
|
||||
case OP_IS:
|
||||
case OP_REPL_PRINT:
|
||||
case OP_END:
|
||||
NO_ARGS();
|
||||
|
@ -111,9 +111,11 @@ OPCODE(STORE_UPVALUE, 1, 0)
|
||||
// params: 2 byte index.
|
||||
OPCODE(PUSH_CLOSURE, 2, 1)
|
||||
|
||||
// Push a class at the constant pool with the index of the two bytes argument.
|
||||
// Pop the stack top, which expected to be the super class of the next class
|
||||
// to be created and push that class from the constant pool with the index of
|
||||
// the two bytes argument.
|
||||
// params: 2 byte index.
|
||||
OPCODE(PUSH_CLASS, 2, 1)
|
||||
OPCODE(CREATE_CLASS, 2, 0)
|
||||
|
||||
// At the stack top, a closure and a class should be there. Add the method to
|
||||
// the class and pop it.
|
||||
@ -248,6 +250,7 @@ OPCODE(GTEQ, 0, -1)
|
||||
|
||||
OPCODE(RANGE, 0, -1) //< Pop 2 integer make range push.
|
||||
OPCODE(IN, 0, -1)
|
||||
OPCODE(IS, 0, -1)
|
||||
|
||||
// Print the repr string of the value at the stack top, used in REPL mode.
|
||||
// This will not pop the value.
|
||||
|
37
src/pk_vm.c
37
src/pk_vm.c
@ -799,12 +799,30 @@ L_vm_main_loop:
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(PUSH_CLASS):
|
||||
OPCODE(CREATE_CLASS):
|
||||
{
|
||||
Var cls = POP();
|
||||
if (!IS_OBJ_TYPE(cls, OBJ_CLASS)) {
|
||||
RUNTIME_ERROR(newString(vm, "Cannot inherit a non class object."));
|
||||
}
|
||||
|
||||
Class* base = (Class*)AS_OBJ(cls);
|
||||
|
||||
// All Builtin type class except for Object are "final" ie. cannot be
|
||||
// inherited from.
|
||||
if (base->class_of != PK_INSTANCE && base->class_of != PK_OBJECT) {
|
||||
RUNTIME_ERROR(stringFormat(vm, "$ type cannot be inherited.",
|
||||
getPkVarTypeName(base->class_of)));
|
||||
}
|
||||
|
||||
uint16_t index = READ_SHORT();
|
||||
ASSERT_INDEX(index, module->constants.count);
|
||||
ASSERT(IS_OBJ_TYPE(module->constants.data[index], OBJ_CLASS), OOPS);
|
||||
PUSH(module->constants.data[index]);
|
||||
|
||||
Class* drived = (Class*)AS_OBJ(module->constants.data[index]);
|
||||
drived->super_class = base;
|
||||
|
||||
PUSH(VAR_OBJ(drived));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -916,6 +934,10 @@ L_do_call:
|
||||
CHECK_ERROR();
|
||||
|
||||
closure = (const Closure*)(cls)->ctor;
|
||||
while (closure == NULL && cls != NULL) {
|
||||
cls = cls->super_class;
|
||||
closure = cls->ctor;
|
||||
}
|
||||
|
||||
// No constructor is defined on the class. Just return self.
|
||||
if (closure == NULL) {
|
||||
@ -1518,6 +1540,17 @@ L_do_call:
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(IS):
|
||||
{
|
||||
// Don't pop yet, we need the reference for gc.
|
||||
Var type = PEEK(-1), inst = PEEK(-2);
|
||||
bool is = varIsType(vm, inst, type);
|
||||
DROP(); DROP(); // container, elem
|
||||
PUSH(VAR_BOOL(is));
|
||||
CHECK_ERROR();
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(REPL_PRINT):
|
||||
{
|
||||
if (vm->config.stdout_write != NULL) {
|
||||
|
@ -28,6 +28,60 @@ print("v2 = ${v2.to_string()}")
|
||||
v3 = v1.add(v2); assert(v3.x == 4 and v3.y == 6)
|
||||
print("v3 = ${v3.to_string()}")
|
||||
|
||||
###############################################################################
|
||||
## INHERITANCE
|
||||
###############################################################################
|
||||
|
||||
class Shape
|
||||
def display()
|
||||
return "${self.name} shape"
|
||||
end
|
||||
end
|
||||
|
||||
class Circle is Shape
|
||||
def _init(r)
|
||||
self.r = r
|
||||
self.name = "circle"
|
||||
end
|
||||
def area()
|
||||
return 3.14 * self.r * self.r
|
||||
end
|
||||
end
|
||||
|
||||
class Rectangle is Shape
|
||||
def _init(w, h)
|
||||
self.w = w; self.h = h
|
||||
self.name = "rectangle"
|
||||
end
|
||||
def area()
|
||||
return self.w * self.h
|
||||
end
|
||||
end
|
||||
|
||||
class Square is Rectangle
|
||||
def _init(w)
|
||||
## TODO: Currently there is no way of calling super(w, h)
|
||||
## so we're setting our self here.
|
||||
self.w = w; self.h = w
|
||||
self.name = "square"
|
||||
end
|
||||
end
|
||||
|
||||
c = Circle(1)
|
||||
assert(c.display() == "circle shape")
|
||||
assert(c is Circle)
|
||||
assert(c is Shape)
|
||||
|
||||
r = Rectangle(2, 3)
|
||||
assert(r is Shape)
|
||||
assert(r.area() == 6)
|
||||
|
||||
s = Square(4)
|
||||
assert(s is Square)
|
||||
assert(s is Rectangle)
|
||||
assert(s is Shape)
|
||||
assert(s.area() == 16)
|
||||
|
||||
print('ALL TESTS PASSED')
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user