mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
class compilation implementation
This commit is contained in:
parent
1951b3d5f7
commit
179026294d
@ -125,6 +125,8 @@ typedef enum {
|
||||
TK_NOT, // not / !
|
||||
TK_TRUE, // true
|
||||
TK_FALSE, // false
|
||||
TK_SELF, // self
|
||||
// TODO: TK_SUPER
|
||||
|
||||
TK_DO, // do
|
||||
TK_THEN, // then
|
||||
@ -186,6 +188,7 @@ static _Keyword _keywords[] = {
|
||||
{ "not", 3, TK_NOT },
|
||||
{ "true", 4, TK_TRUE },
|
||||
{ "false", 5, TK_FALSE },
|
||||
{ "self", 4, TK_SELF },
|
||||
{ "do", 2, TK_DO },
|
||||
{ "then", 4, TK_THEN },
|
||||
{ "while", 5, TK_WHILE },
|
||||
@ -241,6 +244,14 @@ typedef enum {
|
||||
DEPTH_LOCAL, //< Local scope. Increase with inner scope.
|
||||
} Depth;
|
||||
|
||||
typedef enum {
|
||||
FUNC_MAIN, // The body function of the script.
|
||||
FUNC_TOPLEVEL,
|
||||
FUNC_LITERAL,
|
||||
FUNC_METHOD,
|
||||
FUNC_CONSTRUCTOR,
|
||||
} FuncType;
|
||||
|
||||
typedef struct {
|
||||
const char* name; //< Directly points into the source string.
|
||||
uint32_t length; //< Length of the name.
|
||||
@ -314,6 +325,9 @@ typedef struct sUpvalueInfo {
|
||||
|
||||
typedef struct sFunc {
|
||||
|
||||
// Type of the current function.
|
||||
FuncType type;
|
||||
|
||||
// Scope of the function. -2 for module body function, -1 for top level
|
||||
// function and literal functions will have the scope where it declared.
|
||||
int depth;
|
||||
@ -396,8 +410,9 @@ typedef struct sParser {
|
||||
ForwardName forwards[MAX_FORWARD_NAMES];
|
||||
int forwards_count;
|
||||
|
||||
bool repl_mode; //< True if compiling for REPL.
|
||||
bool has_errors; //< True if any syntex error occurred at.
|
||||
bool repl_mode;
|
||||
bool parsing_class;
|
||||
bool has_errors;
|
||||
bool need_more_lines; //< True if we need more lines in REPL mode.
|
||||
|
||||
} Parser;
|
||||
@ -507,6 +522,7 @@ static void parserInit(Parser* parser, PKVM* vm, Compiler* compiler,
|
||||
parser->forwards_count = 0;
|
||||
|
||||
parser->repl_mode = !!(compiler->options && compiler->options->repl_mode);
|
||||
parser->parsing_class = false;
|
||||
parser->has_errors = false;
|
||||
parser->need_more_lines = false;
|
||||
}
|
||||
@ -1444,7 +1460,7 @@ static void compilerChangeStack(Compiler* compiler, int num);
|
||||
|
||||
// Forward declaration of grammar functions.
|
||||
static void parsePrecedence(Compiler* compiler, Precedence precedence);
|
||||
static void compileFunction(Compiler* compiler, bool is_literal);
|
||||
static int compileFunction(Compiler* compiler, FuncType fn_type);
|
||||
static void compileExpression(Compiler* compiler);
|
||||
|
||||
static void exprLiteral(Compiler* compiler);
|
||||
@ -1469,6 +1485,8 @@ static void exprSubscript(Compiler* compiler);
|
||||
// true, false, null, self.
|
||||
static void exprValue(Compiler* compiler);
|
||||
|
||||
static void exprSelf(Compiler* compiler);
|
||||
|
||||
#define NO_RULE { NULL, NULL, PREC_NONE }
|
||||
#define NO_INFIX PREC_NONE
|
||||
|
||||
@ -1534,6 +1552,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
||||
/* TK_NOT */ { exprUnaryOp, NULL, PREC_UNARY },
|
||||
/* TK_TRUE */ { exprValue, NULL, NO_INFIX },
|
||||
/* TK_FALSE */ { exprValue, NULL, NO_INFIX },
|
||||
/* TK_FALSE */ { exprSelf, NULL, NO_INFIX },
|
||||
/* TK_DO */ NO_RULE,
|
||||
/* TK_THEN */ NO_RULE,
|
||||
/* TK_WHILE */ NO_RULE,
|
||||
@ -1698,7 +1717,7 @@ static void exprInterpolation(Compiler* compiler) {
|
||||
}
|
||||
|
||||
static void exprFunction(Compiler* compiler) {
|
||||
compileFunction(compiler, true);
|
||||
compileFunction(compiler, FUNC_LITERAL);
|
||||
}
|
||||
|
||||
static void exprName(Compiler* compiler) {
|
||||
@ -2035,6 +2054,25 @@ static void exprValue(Compiler* compiler) {
|
||||
}
|
||||
}
|
||||
|
||||
static void exprSelf(Compiler* compiler) {
|
||||
|
||||
if (compiler->func->type == FUNC_CONSTRUCTOR ||
|
||||
compiler->func->type == FUNC_METHOD) {
|
||||
emitOpcode(compiler, OP_PUSH_SELF);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we reach here 'self' is used in either non method or a closure
|
||||
// inside a method.
|
||||
|
||||
if (!compiler->parser.parsing_class) {
|
||||
parseError(compiler, "Invalid use of 'self'.");
|
||||
} else {
|
||||
// FIXME:
|
||||
parseError(compiler, "TODO: Closures cannot capture 'self' for now.");
|
||||
}
|
||||
}
|
||||
|
||||
static void parsePrecedence(Compiler* compiler, Precedence precedence) {
|
||||
lexToken(&(compiler->parser));
|
||||
GrammarFn prefix = getRule(compiler->parser.previous.type)->prefix;
|
||||
@ -2204,7 +2242,8 @@ static void compilerExitBlock(Compiler* compiler) {
|
||||
}
|
||||
|
||||
static void compilerPushFunc(Compiler* compiler, Func* fn,
|
||||
Function* func) {
|
||||
Function* func, FuncType type) {
|
||||
fn->type = type;
|
||||
fn->outer_func = compiler->func;
|
||||
fn->local_count = 0;
|
||||
fn->stack_size = 0;
|
||||
@ -2318,14 +2357,12 @@ static void compileStatement(Compiler* compiler);
|
||||
static void compileBlockBody(Compiler* compiler, BlockType type);
|
||||
|
||||
// Compile a class and return it's index in the module's types buffer.
|
||||
static void compileClass(Compiler* compiler) {
|
||||
static int compileClass(Compiler* compiler) {
|
||||
|
||||
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
|
||||
|
||||
TODO; //< compileClass Function is in-compilete.
|
||||
|
||||
// Consume the name of the type.
|
||||
consume(compiler, TK_NAME, "Expected a type name.");
|
||||
consume(compiler, TK_NAME, "Expected a class name.");
|
||||
const char* name = compiler->parser.previous.start;
|
||||
int name_len = compiler->parser.previous.length;
|
||||
int name_line = compiler->parser.previous.line;
|
||||
@ -2336,23 +2373,58 @@ static void compileClass(Compiler* compiler) {
|
||||
Class* cls = newClass(_vm, name, name_len,
|
||||
_vm->builtin_classes[PK_OBJECT], compiler->module,
|
||||
NULL, &cls_index);
|
||||
vmPushTempRef(_vm, &cls->_super); // cls.
|
||||
compiler->parser.parsing_class = true;
|
||||
|
||||
// Check count exceeded.
|
||||
checkMaxConstantsReached(compiler, cls_index);
|
||||
|
||||
// Compile all the methods and constructors.
|
||||
TODO;
|
||||
skipNewLines(compiler);
|
||||
while (!match(compiler, TK_END)) {
|
||||
// At the top level the stack size should be 0, before and after compiling
|
||||
// a top level statement, since there aren't any locals at the top level.
|
||||
ASSERT(compiler->parser.has_errors ||
|
||||
compiler->func->stack_size == 0, OOPS);
|
||||
|
||||
consume(compiler, TK_END, "Expected 'end' after a class declaration end.");
|
||||
consume(compiler, TK_DEF, "Expected method definition.");
|
||||
int fn_index = compileFunction(compiler, FUNC_METHOD);
|
||||
Var fn_var = compiler->module->constants.data[fn_index];
|
||||
ASSERT(IS_OBJ_TYPE(fn_var, OBJ_FUNC), OOPS);
|
||||
|
||||
// TODO: check if the constructor or method already exists and report
|
||||
// error. Make sure the error report line match the name token's line.
|
||||
|
||||
Closure* method = newClosure(_vm, (Function*)AS_OBJ(fn_var));
|
||||
if (strcmp(method->fn->name, "_init") == 0) {
|
||||
cls->ctor = method;
|
||||
|
||||
} else {
|
||||
vmPushTempRef(_vm, &method->_super); // method.
|
||||
pkClosureBufferWrite(&cls->methods, _vm, method);
|
||||
vmPopTempRef(_vm); // method.
|
||||
}
|
||||
|
||||
// At the top level the stack size should be 0, before and after compiling
|
||||
// a top level statement, since there aren't any locals at the top level.
|
||||
ASSERT(compiler->parser.has_errors ||
|
||||
compiler->func->stack_size == 0, OOPS);
|
||||
|
||||
skipNewLines(compiler);
|
||||
}
|
||||
|
||||
compiler->parser.parsing_class = false;
|
||||
vmPopTempRef(_vm); // cls.
|
||||
|
||||
return cls_index;
|
||||
}
|
||||
|
||||
// Compile a function and return it's index in the module's function buffer.
|
||||
static void compileFunction(Compiler* compiler, bool is_literal) {
|
||||
static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
||||
|
||||
const char* name;
|
||||
int name_length;
|
||||
|
||||
if (!is_literal) {
|
||||
if (fn_type != FUNC_LITERAL) {
|
||||
consume(compiler, TK_NAME, "Expected a function name.");
|
||||
name = compiler->parser.previous.start;
|
||||
name_length = compiler->parser.previous.length;
|
||||
@ -2367,7 +2439,7 @@ static void compileFunction(Compiler* compiler, bool is_literal) {
|
||||
compiler->module, false, NULL, &fn_index);
|
||||
checkMaxConstantsReached(compiler, fn_index);
|
||||
|
||||
if (!is_literal) {
|
||||
if (fn_type != FUNC_LITERAL) {
|
||||
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
|
||||
int name_line = compiler->parser.previous.line;
|
||||
int g_index = compilerAddVariable(compiler, name, name_length, name_line);
|
||||
@ -2378,8 +2450,12 @@ static void compileFunction(Compiler* compiler, bool is_literal) {
|
||||
vmPopTempRef(compiler->parser.vm); // func.
|
||||
}
|
||||
|
||||
if (fn_type == FUNC_METHOD && strncmp(name, "_init", name_length) == 0) {
|
||||
fn_type = FUNC_CONSTRUCTOR;
|
||||
}
|
||||
|
||||
Func curr_fn;
|
||||
compilerPushFunc(compiler, &curr_fn, func);
|
||||
compilerPushFunc(compiler, &curr_fn, func, fn_type);
|
||||
|
||||
int argc = 0;
|
||||
compilerEnterBlock(compiler); // Parameter depth.
|
||||
@ -2422,6 +2498,11 @@ static void compileFunction(Compiler* compiler, bool is_literal) {
|
||||
|
||||
compileBlockBody(compiler, BLOCK_FUNC);
|
||||
|
||||
if (fn_type == FUNC_CONSTRUCTOR) {
|
||||
emitOpcode(compiler, OP_PUSH_SELF);
|
||||
emitOpcode(compiler, OP_RETURN);
|
||||
}
|
||||
|
||||
consume(compiler, TK_END, "Expected 'end' after function definition end.");
|
||||
compilerExitBlock(compiler); // Parameter depth.
|
||||
emitFunctionEnd(compiler);
|
||||
@ -2439,7 +2520,7 @@ static void compileFunction(Compiler* compiler, bool is_literal) {
|
||||
// function of this function, and the bellow emit calls will write to the
|
||||
// outer function. If it's a literal function, we need to push a closure
|
||||
// of it on the stack.
|
||||
if (is_literal) {
|
||||
if (fn_type == FUNC_LITERAL) {
|
||||
emitOpcode(compiler, OP_PUSH_CLOSURE);
|
||||
emitShort(compiler, fn_index);
|
||||
|
||||
@ -2449,6 +2530,8 @@ static void compileFunction(Compiler* compiler, bool is_literal) {
|
||||
emitByte(compiler, curr_fn.upvalues[i].index);
|
||||
}
|
||||
}
|
||||
|
||||
return fn_index;
|
||||
}
|
||||
|
||||
// Finish a block body.
|
||||
@ -3013,10 +3096,22 @@ static void compileStatement(Compiler* compiler) {
|
||||
}
|
||||
|
||||
if (matchEndStatement(compiler)) {
|
||||
emitOpcode(compiler, OP_PUSH_NULL);
|
||||
|
||||
// Constructors will return self.
|
||||
if (compiler->func->type == FUNC_CONSTRUCTOR) {
|
||||
emitOpcode(compiler, OP_PUSH_SELF);
|
||||
} else {
|
||||
emitOpcode(compiler, OP_PUSH_NULL);
|
||||
}
|
||||
|
||||
emitOpcode(compiler, OP_RETURN);
|
||||
|
||||
} else {
|
||||
|
||||
if (compiler->func->type == FUNC_CONSTRUCTOR) {
|
||||
parseError(compiler, "Cannor 'return' a value from constructor.");
|
||||
}
|
||||
|
||||
compileExpression(compiler); //< Return value is at stack top.
|
||||
|
||||
// If the last expression parsed with compileExpression() is a call
|
||||
@ -3076,7 +3171,7 @@ static void compileTopLevelStatement(Compiler* compiler) {
|
||||
compileClass(compiler);
|
||||
|
||||
} else if (match(compiler, TK_DEF)) {
|
||||
compileFunction(compiler, false);
|
||||
compileFunction(compiler, FUNC_TOPLEVEL);
|
||||
|
||||
} else if (match(compiler, TK_FROM)) {
|
||||
compileFromImport(compiler);
|
||||
@ -3130,7 +3225,7 @@ PkResult compile(PKVM* vm, Module* module, const char* source,
|
||||
uint32_t globals_count = module->globals.count;
|
||||
|
||||
Func curr_fn;
|
||||
compilerPushFunc(compiler, &curr_fn, module->body->fn);
|
||||
compilerPushFunc(compiler, &curr_fn, module->body->fn, FUNC_MAIN);
|
||||
|
||||
// Lex initial tokens. current <-- next.
|
||||
lexToken(&(compiler->parser));
|
||||
|
@ -1289,8 +1289,10 @@ static void initializePrimitiveClasses(PKVM* vm) {
|
||||
Class* super = NULL;
|
||||
if (i != 0) super = vm->builtin_classes[PK_OBJECT];
|
||||
const char* name = getPkVarTypeName((PkVarType)i);
|
||||
vm->builtin_classes[i] = newClass(vm, name, (int)strlen(name),
|
||||
super, NULL, NULL, NULL);
|
||||
Class* cls = newClass(vm, name, (int)strlen(name),
|
||||
super, NULL, NULL, NULL);
|
||||
vm->builtin_classes[i] = cls;
|
||||
cls->class_of = (PkVarType)i;
|
||||
}
|
||||
|
||||
#define ADD_CTOR(type, name, ptr, arity_) \
|
||||
@ -1347,46 +1349,41 @@ Var preConstructSelf(PKVM* vm, Class* cls) {
|
||||
VM_SET_ERROR(vm, newString(vm, \
|
||||
"Class '" type_name "' cannot be instanciated."))
|
||||
|
||||
for (int i = 0; i < PK_INSTANCE; i++) {
|
||||
if (vm->builtin_classes[i] == cls) {
|
||||
switch (cls->class_of) {
|
||||
case PK_OBJECT:
|
||||
NO_INSTANCE("Object");
|
||||
return VAR_NULL;
|
||||
|
||||
switch ((PkVarType)i) {
|
||||
case PK_OBJECT:
|
||||
NO_INSTANCE("Object");
|
||||
return VAR_NULL;
|
||||
case PK_NULL:
|
||||
case PK_BOOL:
|
||||
case PK_NUMBER:
|
||||
case PK_STRING:
|
||||
case PK_LIST:
|
||||
case PK_MAP:
|
||||
case PK_RANGE:
|
||||
return VAR_NULL; // Constructor will override the null.
|
||||
|
||||
case PK_NULL:
|
||||
case PK_BOOL:
|
||||
case PK_NUMBER:
|
||||
case PK_STRING:
|
||||
case PK_LIST:
|
||||
case PK_MAP:
|
||||
case PK_RANGE:
|
||||
return VAR_NULL; // Constructor will override the null.
|
||||
case PK_MODULE:
|
||||
NO_INSTANCE("Module");
|
||||
return VAR_NULL;
|
||||
|
||||
case PK_MODULE:
|
||||
NO_INSTANCE("Module");
|
||||
return VAR_NULL;
|
||||
case PK_CLOSURE:
|
||||
NO_INSTANCE("Closure");
|
||||
return VAR_NULL;
|
||||
|
||||
case PK_CLOSURE:
|
||||
NO_INSTANCE("Closure");
|
||||
return VAR_NULL;
|
||||
case PK_FIBER:
|
||||
return VAR_NULL;
|
||||
|
||||
case PK_FIBER:
|
||||
return VAR_NULL;
|
||||
case PK_CLASS:
|
||||
NO_INSTANCE("Class");
|
||||
return VAR_NULL;
|
||||
|
||||
case PK_CLASS:
|
||||
NO_INSTANCE("Class");
|
||||
return VAR_NULL;
|
||||
|
||||
case PK_INSTANCE:
|
||||
UNREACHABLE();
|
||||
return VAR_NULL;
|
||||
}
|
||||
}
|
||||
case PK_INSTANCE:
|
||||
return VAR_OBJ(newInstance(vm, cls));
|
||||
}
|
||||
|
||||
return VAR_OBJ(newInstance(vm, cls));
|
||||
UNREACHABLE();
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
Class* getClass(PKVM* vm, Var instance) {
|
||||
|
@ -288,7 +288,7 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {
|
||||
case OBJ_INST:
|
||||
{
|
||||
Instance* inst = (Instance*)obj;
|
||||
markObject(vm, &inst->attribs._super);
|
||||
markObject(vm, &inst->attribs->_super);
|
||||
markObject(vm, &inst->cls->_super);
|
||||
vm->bytes_allocated += sizeof(Instance);
|
||||
} break;
|
||||
@ -515,6 +515,7 @@ Class* newClass(PKVM* vm, const char* name, int length,
|
||||
|
||||
pkClosureBufferInit(&cls->methods);
|
||||
|
||||
cls->class_of = PK_INSTANCE;
|
||||
cls->owner = NULL;
|
||||
cls->super_class = super;
|
||||
cls->docstring = NULL;
|
||||
@ -538,28 +539,23 @@ Class* newClass(PKVM* vm, const char* name, int length,
|
||||
|
||||
Instance* newInstance(PKVM* vm, Class* cls) {
|
||||
|
||||
#ifdef DEBUG
|
||||
bool _builtin_class = false;
|
||||
for (int i = 0; i < PK_INSTANCE; i++) {
|
||||
if (vm->builtin_classes[i] == cls) {
|
||||
_builtin_class = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(!_builtin_class, "Cannot create an instace of builtin class "
|
||||
"with newInstance() function.");
|
||||
#endif // DEBUG
|
||||
ASSERT(cls->class_of == PK_INSTANCE, "Cannot create an instace of builtin "
|
||||
"class with newInstance() function.");
|
||||
|
||||
Instance* inst = ALLOCATE(vm, Instance);
|
||||
varInitObject(&inst->_super, vm, OBJ_INST);
|
||||
vmPushTempRef(vm, &inst->_super); // inst.
|
||||
|
||||
inst->cls = cls;
|
||||
inst->attribs = newMap(vm);
|
||||
|
||||
if (cls->new_fn != NULL) {
|
||||
vmPushTempRef(vm, &inst->_super); // inst.
|
||||
inst->native = cls->new_fn();
|
||||
vmPopTempRef(vm); // inst.
|
||||
} else {
|
||||
inst->native = NULL;
|
||||
}
|
||||
|
||||
vmPopTempRef(vm); // inst.
|
||||
return inst;
|
||||
}
|
||||
|
||||
@ -1170,10 +1166,10 @@ bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) {
|
||||
ASSERT((inst != NULL) && (attrib != NULL) && (value != NULL), OOPS);
|
||||
|
||||
if (inst->native != NULL) {
|
||||
TODO;
|
||||
}
|
||||
TODO;
|
||||
|
||||
Var value_ = mapGet(&inst->attribs, VAR_OBJ(attrib));
|
||||
Var value_ = mapGet(inst->attribs, VAR_OBJ(attrib));
|
||||
if (IS_UNDEF(value_)) return false;
|
||||
|
||||
*value = value_;
|
||||
@ -1194,7 +1190,7 @@ bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mapSet(vm, &inst->attribs, VAR_OBJ(attrib), value);
|
||||
mapSet(vm, inst->attribs, VAR_OBJ(attrib), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1591,9 +1587,10 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
|
||||
{
|
||||
const Instance* inst = (const Instance*)obj;
|
||||
pkByteBufferWrite(buff, vm, '[');
|
||||
pkByteBufferWrite(buff, vm, '\'');
|
||||
pkByteBufferAddString(buff, vm, inst->cls->name->data,
|
||||
inst->cls->name->length);
|
||||
pkByteBufferWrite(buff, vm, ':');
|
||||
pkByteBufferAddString(buff, vm, "' instance at ", 14);
|
||||
|
||||
char buff_addr[STR_HEX_BUFF_SIZE];
|
||||
char* ptr = (char*)buff_addr;
|
||||
|
@ -501,6 +501,11 @@ struct Class {
|
||||
// entry in it's owner module's constant pool.
|
||||
const char* docstring;
|
||||
|
||||
// For builtin type it'll be it's enum (ex: PK_STRING, PK_NUMBER, ...) for
|
||||
// every other classes it'll be PK_INSTANCE to indicate that it's not a
|
||||
// builtin type's class.
|
||||
PkVarType class_of;
|
||||
|
||||
Closure* ctor; //< The constructor function.
|
||||
|
||||
// A buffer of methods of the class.
|
||||
@ -531,7 +536,7 @@ struct Instance {
|
||||
void* native;
|
||||
|
||||
// Dynamic attributes of an instance.
|
||||
Map attribs;
|
||||
Map* attribs;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -551,7 +551,7 @@ static inline void pushCallFrame(PKVM* vm, const Closure* closure, Var* rbp) {
|
||||
frame->closure = closure;
|
||||
frame->ip = closure->fn->fn->opcodes.data;
|
||||
|
||||
// Eat the self.
|
||||
// Capture self.
|
||||
frame->self = vm->fiber->self;
|
||||
vm->fiber->self = VAR_UNDEFINED;
|
||||
}
|
||||
@ -567,7 +567,10 @@ static inline void reuseCallFrame(PKVM* vm, const Closure* closure) {
|
||||
CallFrame* frame = fb->frames + fb->frame_count - 1;
|
||||
frame->closure = closure;
|
||||
frame->ip = closure->fn->fn->opcodes.data;
|
||||
frame->self = VAR_UNDEFINED;
|
||||
|
||||
// Capture self.
|
||||
frame->self = vm->fiber->self;
|
||||
vm->fiber->self = VAR_UNDEFINED;
|
||||
|
||||
ASSERT(*frame->rbp == VAR_NULL, OOPS);
|
||||
|
||||
|
@ -1,9 +1,33 @@
|
||||
|
||||
|
||||
## Note that classes are being implemented and temproarly
|
||||
## the classes cannot be compiled.
|
||||
|
||||
|
||||
|
||||
|
||||
class Vec2
|
||||
def _init(x, y)
|
||||
self.x = x
|
||||
self.y = y
|
||||
end
|
||||
|
||||
def add(other)
|
||||
return Vec2(self.x + other.x,
|
||||
self.y + other.y)
|
||||
end
|
||||
|
||||
## Note that operator overloading / friend functions
|
||||
## haven't implemented at this point (to_string won't actually
|
||||
## override it).
|
||||
def to_string
|
||||
return "[${self.x}, ${self.y}]"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
v1 = Vec2(1, 2); assert(v1.x == 1 and v1.y == 2)
|
||||
print("v1 = ${v1.to_string()}")
|
||||
|
||||
v2 = Vec2(3, 4); assert(v2.x == 3 and v2.y == 4)
|
||||
print("v2 = ${v2.to_string()}")
|
||||
|
||||
v3 = v1.add(v2); assert(v3.x == 4 and v3.y == 6)
|
||||
print("v3 = ${v3.to_string()}")
|
||||
|
||||
print('ALL TESTS PASSED')
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user