inheritance and 'is' test implemented

This commit is contained in:
Thakee Nathees 2022-04-26 21:57:35 +05:30
parent 745387e307
commit acf38a31ca
7 changed files with 134 additions and 9 deletions

View File

@ -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);

View File

@ -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) \

View File

@ -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);

View File

@ -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();

View File

@ -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.

View File

@ -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) {

View File

@ -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')