diff --git a/scripts/funky.toy b/scripts/funky.toy index 9b3768e..40a006d 100644 --- a/scripts/funky.toy +++ b/scripts/funky.toy @@ -1,5 +1,23 @@ +fn name(param1: int, param2: float, param3: string, param4) { + print param1; + print param2; + print param3; + print param4; +} +name(42, 3.14, "hello world", -1); -fn name(foobar: bool const) { - print foobar; -} \ No newline at end of file +//URGENT: return values are still needed + +fn output(arg) { + print arg; +} + +output(null); +output(true); +output(42); +output(3.1415); +output("woot!"); +output([1, 23, 3]); +output(["key":1]); +output(name); \ No newline at end of file diff --git a/source/toy_ast.c b/source/toy_ast.c index 9ed5780..ed6441a 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -213,6 +213,16 @@ void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** (*astHandle) = tmp; } +void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* args) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_FN_INVOKE; + tmp->fnInvoke.function = (*astHandle); + tmp->fnInvoke.args = args; + + (*astHandle) = tmp; +} + void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); diff --git a/source/toy_ast.h b/source/toy_ast.h index 8624b17..c9f0384 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -31,6 +31,7 @@ typedef enum Toy_AstType { TOY_AST_VAR_ACCESS, TOY_AST_FN_DECLARE, + TOY_AST_FN_INVOKE, TOY_AST_PASS, TOY_AST_ERROR, @@ -200,6 +201,12 @@ typedef struct Toy_AstFnDeclare { Toy_Ast* body; } Toy_AstFnDeclare; +typedef struct Toy_AstFnInvoke { + Toy_AstType type; + Toy_Ast* function; + Toy_Ast* args; +} Toy_AstFnInvoke; + typedef struct Toy_AstPass { Toy_AstType type; } Toy_AstPass; @@ -233,6 +240,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests Toy_AstVarAssign varAssign; Toy_AstVarAccess varAccess; Toy_AstFnDeclare fnDeclare; + Toy_AstFnInvoke fnInvoke; Toy_AstPass pass; Toy_AstError error; Toy_AstEnd end; @@ -262,8 +270,8 @@ void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body); +void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params); void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); - diff --git a/source/toy_function.h b/source/toy_function.h index 76ff756..bd1a0d4 100644 --- a/source/toy_function.h +++ b/source/toy_function.h @@ -9,12 +9,12 @@ typedef enum Toy_FunctionType { TOY_FUNCTION_NATIVE, } Toy_FunctionType; -typedef union Toy_FunctionModule { +typedef struct Toy_FunctionModule { Toy_FunctionType type; Toy_Module module; } Toy_FunctionModule; -typedef union Toy_FunctionNative { +typedef struct Toy_FunctionNative { Toy_FunctionType type; void* native; //TODO: replace with the native function pointer } Toy_FunctionNative; diff --git a/source/toy_module_compiler.c b/source/toy_module_compiler.c index 3db6220..45de6e4 100644 --- a/source/toy_module_compiler.c +++ b/source/toy_module_compiler.c @@ -215,6 +215,7 @@ static unsigned int emitParameters(Toy_ModuleCompiler* mb, Toy_Ast* ast) { //emit to the param index EMIT_INT(&mb, param, dataAddr); + EMIT_INT(&mb, param, (unsigned int)(TOY_VALUE_AS_STRING(ast->value.value)->name.varType)); //don't forget this bit //this returns the number of written parameters return 1; @@ -1041,6 +1042,32 @@ static unsigned int writeInstructionFnDeclare(Toy_ModuleCompiler** mb, Toy_AstFn return 0; } +static unsigned int writeInstructionFnInvoke(Toy_ModuleCompiler** mb, Toy_AstFnInvoke ast) { + unsigned int argCount = writeModuleCompilerCode(mb, ast.args); + + if (argCount > 255) { + fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid function invokation with %d functions arguments (maximum 255)\n" TOY_CC_RESET, (int)argCount); + (*mb)->panic = true; + return 0; + } + + unsigned int fnCount = writeModuleCompilerCode(mb, ast.function); + + if (fnCount != 1) { + fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid function invokation with %d function AST nodes (expected 1)\n" TOY_CC_RESET, (int)fnCount); + (*mb)->panic = true; + return 0; + } + + //call the function + EMIT_BYTE(mb, code, TOY_OPCODE_INVOKE); + EMIT_BYTE(mb, code, TOY_VALUE_FUNCTION); + EMIT_BYTE(mb, code, (unsigned char)argCount); + EMIT_BYTE(mb, code, 0); //IDK how many returns + + return 0; +} + static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* ast) { if (ast == NULL) { return 0; @@ -1151,6 +1178,10 @@ static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* as result += writeInstructionFnDeclare(mb, ast->fnDeclare); break; + case TOY_AST_FN_INVOKE: + result += writeInstructionFnInvoke(mb, ast->fnInvoke); + break; + case TOY_AST_PASS: //NO-OP break; diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index 11b0d76..156e1ee 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -10,6 +10,7 @@ typedef enum Toy_OpcodeType { TOY_OPCODE_ASSIGN, TOY_OPCODE_ASSIGN_COMPOUND, //assign to a compound's internals TOY_OPCODE_ACCESS, + TOY_OPCODE_INVOKE, //for calling functions TOY_OPCODE_DUPLICATE, //duplicate the top of the stack TOY_OPCODE_ELIMINATE, //remove the top of the stack diff --git a/source/toy_parser.c b/source/toy_parser.c index fb310d4..5a3133e 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -120,6 +120,7 @@ static Toy_AstFlag group(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast* static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); +static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); //precedence definitions static ParsingTuple parsingRulesetTable[] = { @@ -181,8 +182,8 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MODULO_ASSIGN, - {PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_INCREMENT, - {PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_DECREMENT, + {PREC_UNARY,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_INCREMENT, + {PREC_UNARY,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_DECREMENT, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_ASSIGN, //comparator operators @@ -194,7 +195,7 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_COMPARISON,NULL,binary},// TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL, //structural operators - {PREC_GROUP,group,NULL},// TOY_TOKEN_OPERATOR_PAREN_LEFT, + {PREC_CALL,group,invoke},// TOY_TOKEN_OPERATOR_PAREN_LEFT, {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_PAREN_RIGHT, {PREC_GROUP,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_LEFT, {PREC_NONE,compound,aggregate},// TOY_TOKEN_OPERATOR_BRACKET_RIGHT, @@ -212,7 +213,7 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_GROUP,NULL,aggregate},// TOY_TOKEN_OPERATOR_COMMA, // , {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_DOT, // . - {PREC_CALL,NULL,binary},// TOY_TOKEN_OPERATOR_CONCAT, // .. + {PREC_UNARY,NULL,binary},// TOY_TOKEN_OPERATOR_CONCAT, // .. {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_REST, // ... //unused operators @@ -557,7 +558,7 @@ static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast } case TOY_TOKEN_OPERATOR_CONCAT: { - parsePrecedence(bucketHandle, parser, rootHandle, PREC_CALL + 1); + parsePrecedence(bucketHandle, parser, rootHandle, PREC_UNARY + 1); return TOY_AST_FLAG_CONCAT; } @@ -708,6 +709,31 @@ static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, T } } +static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + //infix must advance + advance(parser); + + //read in args + Toy_Ast* args = NULL; + unsigned int paramIterations = 0; + + while (parser->current.type != TOY_TOKEN_OPERATOR_PAREN_RIGHT && (paramIterations++ == 0 || match(parser, TOY_TOKEN_OPERATOR_COMMA))) { + //get the next arg + Toy_Ast* ast = NULL; + parsePrecedence(bucketHandle, parser, &ast, PREC_GROUP); + + //add to the args aggregate (is added backwards, because weird) + Toy_private_emitAstAggregate(bucketHandle, &args, TOY_AST_FLAG_COLLECTION, ast); + } + + consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' at the end of argument list"); + + //finally, emit the call as an Ast + Toy_private_emitAstFunctionInvokation(bucketHandle, rootHandle, args); + + return TOY_AST_FLAG_NONE; +} + //grammar rules static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule) { //'step over' the token to parse @@ -741,7 +767,7 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A return; } - Toy_Ast* ptr = NULL; + Toy_Ast* ptr = (*rootHandle); //NOTE: infix functions will need to be careful not to damage the pre-existing tree, if they can avoid it Toy_AstFlag flag = infix(bucketHandle, parser, &ptr); //finished diff --git a/source/toy_scope.c b/source/toy_scope.c index 8046b32..a0a9ff2 100644 --- a/source/toy_scope.c +++ b/source/toy_scope.c @@ -139,14 +139,6 @@ void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) { return; } - //constness check - if (Toy_getNameStringVarConstant(key) && value.type == TOY_VALUE_NULL) { - char buffer[key->info.length + 256]; - sprintf(buffer, "Can't declare %s as const with value 'null'", key->name.data); - Toy_error(buffer); - return; - } - Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value); } diff --git a/source/toy_value.c b/source/toy_value.c index 81f635e..0668614 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -311,7 +311,7 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) { } case TOY_VALUE_FUNCTION: - return false; //URGENT: check this + return false; //URGENT: test this case TOY_VALUE_OPAQUE: case TOY_VALUE_ANY: @@ -432,10 +432,10 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) { switch(value.type) { case TOY_VALUE_NULL: - return Toy_createString(bucketHandle, "null"); + return Toy_createString(bucketHandle, ""); case TOY_VALUE_BOOLEAN: - return Toy_createString(bucketHandle, value.as.boolean ? "true" : "false"); + return Toy_createString(bucketHandle, value.as.boolean ? "" : ""); case TOY_VALUE_INTEGER: { char buffer[16]; @@ -622,7 +622,9 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) { return string; } - case TOY_VALUE_FUNCTION: //URGENT: check this + case TOY_VALUE_FUNCTION: + //dummy + return Toy_createString(bucketHandle, ""); case TOY_VALUE_OPAQUE: case TOY_VALUE_ANY: diff --git a/source/toy_vm.c b/source/toy_vm.c index 1e5980a..6489fbd 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -373,6 +373,79 @@ static void processAccess(Toy_VM* vm) { Toy_freeValue(name); } +static void processInvoke(Toy_VM* vm) { + Toy_ValueType valueType = READ_BYTE(vm); //unused for now + unsigned int argCount = (unsigned int)READ_BYTE(vm); + fixAlignment(vm); + + //check for invoking bad values + if (valueType != TOY_VALUE_FUNCTION) { + Toy_error("Unrecognized invoke on a non-function value"); + return; + } + + //function to call + Toy_Value value = Toy_popStack(&vm->stack); + if (TOY_VALUE_IS_FUNCTION(value) != true) { + Toy_error("Can't call a non-function value"); + return; + } + + //process based on the function type + Toy_Function* fn = TOY_VALUE_AS_FUNCTION(value); + + switch(fn->type) { + case TOY_FUNCTION_MODULE: { + Toy_Module module = fn->module.module; + + //NOTE: counts within the modules actually specify size in memory, so the argCount is multiplied by 8 for the 8 bytes used in the params table + + //check args count + if (argCount * 8 != module.paramCount) { + Toy_error("Incorrect number of parameters specified for function call"); + break; + } + + if (argCount > vm->stack->count) { + Toy_error("Incorrect number of parameters on the stack for function call"); + break; + } + + //spin up a new sub-vm + Toy_VM subVM; + Toy_initVM(&subVM); + Toy_bindVM(&subVM, &module, false); + + //inject params, backwards from the stack + for (unsigned int i = argCount; i > 0; i--) { + Toy_Value argValue = Toy_popStack(&vm->stack); + + //paramAddr is relative to the data section, and is followed by the param type + unsigned int paramAddr = ((unsigned int*)(module.code + module.paramAddr))[(i-1)*2]; + Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(module.code + module.paramAddr))[(i-1)*2 + 1]); + + //c-string of the param's name + const char* cstr = ((char*)(module.code + module.dataAddr)) + paramAddr; + + //as a name string + Toy_String* name = Toy_createNameStringLength(&subVM.literalBucket, cstr, strlen(cstr), paramType, true); + + Toy_declareScope(subVM.scope, name, argValue); + } + + //run and cleanup + Toy_runVM(&subVM); + Toy_freeVM(&subVM); + } + break; + + case TOY_FUNCTION_NATIVE: + default: + Toy_error("Can't call an unknown function type"); + break; + } +} + static void processDuplicate(Toy_VM* vm) { Toy_Value value = Toy_copyValue(Toy_peekStack(&vm->stack)); Toy_pushStack(&vm->stack, value); @@ -882,6 +955,10 @@ static void process(Toy_VM* vm) { processAccess(vm); break; + case TOY_OPCODE_INVOKE: + processInvoke(vm); + break; + case TOY_OPCODE_DUPLICATE: processDuplicate(vm); break; diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index c7df85b..9611e21 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -51,6 +51,7 @@ int test_sizeof_ast(void) { TEST_SIZEOF(Toy_AstVarAssign, 16 , 24); TEST_SIZEOF(Toy_AstVarAccess, 8 , 16); TEST_SIZEOF(Toy_AstFnDeclare, 16 , 32); + TEST_SIZEOF(Toy_AstFnInvoke, 12 , 24); TEST_SIZEOF(Toy_AstPass, 4 , 4); TEST_SIZEOF(Toy_AstError, 4 , 4); TEST_SIZEOF(Toy_AstEnd, 4 , 4); diff --git a/tests/cases/test_value.c b/tests/cases/test_value.c index 6aabda1..0b5726e 100644 --- a/tests/cases/test_value.c +++ b/tests/cases/test_value.c @@ -369,7 +369,7 @@ int test_value_stringify(void) { //check if (string->info.type != TOY_STRING_LEAF || - strcmp(string->leaf.data, "null") != 0) + strcmp(string->leaf.data, "") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: stringify 'null' failed\n" TOY_CC_RESET); Toy_freeString(string); @@ -395,7 +395,7 @@ int test_value_stringify(void) { //check if (string->info.type != TOY_STRING_LEAF || - strcmp(string->leaf.data, "true") != 0) + strcmp(string->leaf.data, "") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'true' failed\n" TOY_CC_RESET); Toy_freeString(string); @@ -421,7 +421,7 @@ int test_value_stringify(void) { //check if (string->info.type != TOY_STRING_LEAF || - strcmp(string->leaf.data, "false") != 0) + strcmp(string->leaf.data, "") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'false' failed\n" TOY_CC_RESET); Toy_freeString(string);