forked from ground/ground
Methods inside objects
This commit is contained in:
@@ -291,7 +291,7 @@ bool checkForErrors(GroundProgram* program) {
|
||||
CHECK_ARG_TYPE(instruction->args.length - 1, DIRREF);
|
||||
break;
|
||||
|
||||
// call &object !methodName [$arg1 $arg2 ...] &returnVal
|
||||
// callmethod &object !methodName [$arg1 $arg2 ...] &returnVal
|
||||
// Minimum 3 args: object ref, method name, return variable
|
||||
case CALLMETHOD:
|
||||
CHECK_ARGC_MIN(3);
|
||||
|
||||
@@ -1917,6 +1917,139 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CALLMETHOD: {
|
||||
if (in->args.length < 3) {
|
||||
runtimeError(TOO_FEW_ARGS, "Expecting 3 or more args", in, currentInstruction);
|
||||
}
|
||||
if (in->args.args[0].type != DIRREF) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an object for arg 1", in, currentInstruction);
|
||||
}
|
||||
if (in->args.args[1].type != FNREF) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 2", in, currentInstruction);
|
||||
}
|
||||
if (in->args.args[in->args.length - 1].type != DIRREF) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction);
|
||||
}
|
||||
|
||||
GroundVariable* objvar = findVariable(*scope->variables, in->args.args[0].value.refName);
|
||||
if (objvar == NULL) {
|
||||
runtimeError(UNKNOWN_VARIABLE, "Object not found", in, currentInstruction);
|
||||
}
|
||||
GroundValue* obj = &objvar->value;
|
||||
if (obj->type != CUSTOM) {
|
||||
runtimeError(UNKNOWN_VARIABLE, "Provided reference does not reference an object", in, currentInstruction);
|
||||
}
|
||||
|
||||
GroundObjectField* fnvar = findField(*obj->data.customVal, in->args.args[1].value.refName);
|
||||
if (fnvar == NULL) {
|
||||
runtimeError(UNKNOWN_VARIABLE, "Method not found inside specified object", in, currentInstruction);
|
||||
}
|
||||
if (fnvar->value.type != FUNCTION) {
|
||||
runtimeError(UNKNOWN_VARIABLE, "Provided reference does not reference a method", in, currentInstruction);
|
||||
}
|
||||
GroundFunction* function = fnvar->value.data.fnVal;
|
||||
if (function->argSize < in->args.length - 3) {
|
||||
runtimeError(TOO_FEW_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
|
||||
}
|
||||
if (function->argSize > in->args.length - 3) {
|
||||
runtimeError(TOO_MANY_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
|
||||
}
|
||||
if (function->isNative) {
|
||||
List argsList = createList();
|
||||
for (size_t i = 0; i < function->argSize; i++) {
|
||||
if (in->args.args[i + 2].type != VALUE) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
|
||||
}
|
||||
if (function->nativeFn) {
|
||||
if (in->args.args[i + 2].value.value.type != function->args[i].type) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
|
||||
}
|
||||
} else {
|
||||
if (!checkFnTypes(&in->args.args[i + 2].value.value, &function->args[i])) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
|
||||
}
|
||||
}
|
||||
appendToList(&argsList, in->args.args[i + 2].value.value);
|
||||
}
|
||||
|
||||
GroundScope newscope = {
|
||||
.labels = NULL,
|
||||
.variables = malloc(sizeof(GroundVariable*)),
|
||||
.isMainScope = false
|
||||
};
|
||||
|
||||
if (newscope.variables == NULL) {
|
||||
runtimeError(FIXME, "Failed to allocate memory", in, currentInstruction);
|
||||
}
|
||||
|
||||
*newscope.variables = NULL;
|
||||
|
||||
GroundObjectField* tmp;
|
||||
GroundObjectField* el;
|
||||
|
||||
HASH_ITER(hh, obj->data.customVal->fields, el, tmp) {
|
||||
addVariable(newscope.variables, el->id, el->value);
|
||||
}
|
||||
|
||||
if (function->nativeFn) {
|
||||
GroundValue returnValue = function->nativeFn(&newscope, argsList);
|
||||
if (returnValue.type == ERROR) {
|
||||
returnValue.data.errorVal.line = currentInstruction;
|
||||
returnValue.data.errorVal.hasLine = true;
|
||||
returnValue.data.errorVal.where = in;
|
||||
if (scope->isMainScope) {
|
||||
throwError(&returnValue.data.errorVal);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
if (returnValue.type != function->returnType) {
|
||||
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from native function", in, currentInstruction);
|
||||
}
|
||||
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||
|
||||
// Copy back modified variables
|
||||
HASH_ITER(hh, obj->data.customVal->fields, el, tmp) {
|
||||
el->value = findVariable(*newscope.variables, el->id)->value;
|
||||
}
|
||||
}
|
||||
free(argsList.values);
|
||||
} else {
|
||||
GroundScope newScope = copyGroundScope(&function->closure);
|
||||
for (size_t i = 0; i < function->argSize; i++) {
|
||||
if (in->args.args[i + 2].type != VALUE) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
|
||||
}
|
||||
//if (in->args.args[i + 1].value.value.type != function->args[i].type) {
|
||||
if (!checkFnTypes(&in->args.args[i + 2].value.value, &function->args[i])) {
|
||||
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
|
||||
}
|
||||
addVariable(newScope.variables, function->args[i].name, in->args.args[i + 2].value.value);
|
||||
}
|
||||
// Add the object to the scope
|
||||
addVariable(newScope.variables, "self", *obj);
|
||||
size_t currentCurrentInstruction = currentInstruction;
|
||||
currentInstruction = function->startLine;
|
||||
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
|
||||
if (returnValue.type == ERROR) {
|
||||
return returnValue;
|
||||
}
|
||||
if (returnValue.type != function->returnType) {
|
||||
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from function", in, currentInstruction);
|
||||
}
|
||||
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||
currentInstruction = currentCurrentInstruction;
|
||||
|
||||
// Copy out the object
|
||||
GroundVariable* objvar = findVariable(*newScope.variables, "self");
|
||||
if (objvar == NULL) {
|
||||
runtimeError(UNKNOWN_VARIABLE, "self not found in scope, did you drop the object?", in, currentInstruction);
|
||||
}
|
||||
addVariable(scope->variables, in->args.args[0].value.refName, objvar->value);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case USE: {
|
||||
if (in->args.length < 1) {
|
||||
|
||||
@@ -191,6 +191,7 @@ static GroundInstType getInstructionType(const char* inst) {
|
||||
if (strcmp(inst, "endfun") == 0) return ENDFUN;
|
||||
if (strcmp(inst, "pusharg") == 0) return PUSHARG;
|
||||
if (strcmp(inst, "call") == 0) return CALL;
|
||||
if (strcmp(inst, "callmethod") == 0) return CALLMETHOD;
|
||||
if (strcmp(inst, "struct") == 0) return STRUCT;
|
||||
if (strcmp(inst, "endstruct") == 0) return ENDSTRUCT;
|
||||
if (strcmp(inst, "init") == 0) return INIT;
|
||||
|
||||
@@ -533,6 +533,9 @@ void printGroundInstruction(GroundInstruction* gi) {
|
||||
case CALL:
|
||||
printf("call");
|
||||
break;
|
||||
case CALLMETHOD:
|
||||
printf("callmethod");
|
||||
break;
|
||||
case STRUCT:
|
||||
printf("struct");
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user