diff --git a/src/compiler.c b/src/compiler.c index d6fc02d..3bc373e 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -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); diff --git a/src/interpreter.c b/src/interpreter.c index e01293b..7216d40 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -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) { diff --git a/src/parser.c b/src/parser.c index 84d84e7..276bac8 100644 --- a/src/parser.c +++ b/src/parser.c @@ -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; diff --git a/src/types.c b/src/types.c index 2926d6d..752f93f 100644 --- a/src/types.c +++ b/src/types.c @@ -533,6 +533,9 @@ void printGroundInstruction(GroundInstruction* gi) { case CALL: printf("call"); break; + case CALLMETHOD: + printf("callmethod"); + break; case STRUCT: printf("struct"); break;