From f55f27726cbaf8682e0f05729240cd1e9d14a94e Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Tue, 19 May 2026 13:21:41 +1000 Subject: [PATCH] Fixed invoking functions within function arguments Somewhat similar to c9d4b9965c97755e714bbd3e7a8022b9f2e0add4 --- scripts/for-theory.toy | 31 +++++++++ scripts/hello_world.toy | 14 ++-- source/toy_ast.h | 1 + source/toy_compiler.c | 16 ++++- source/toy_parser.c | 14 ++-- source/toy_token_types.h | 64 +++++++++---------- source/toy_vm.c | 2 +- .../test_function_invocation_in_arguments.toy | 11 ++++ tests/scripts/test_keyword_pass.toy | 1 + tests/scripts/test_keyword_return.toy | 8 +++ 10 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 scripts/for-theory.toy create mode 100644 tests/scripts/test_function_invocation_in_arguments.toy create mode 100644 tests/scripts/test_keyword_pass.toy create mode 100644 tests/scripts/test_keyword_return.toy diff --git a/scripts/for-theory.toy b/scripts/for-theory.toy new file mode 100644 index 0000000..39ba6c6 --- /dev/null +++ b/scripts/for-theory.toy @@ -0,0 +1,31 @@ +//WARN: This is just a scratch pad, don't use it + +//for (var i in array) print i; +//for (var i in range(10)) print i; + + +//example of a `range` function +fn range(limit: Int) { + var counter: Int = 0; + + fn next() { + if (counter >= limit) { + return null; + } + else return counter++; + } + + return next; +} + +var next = range(10); + + +fn log(x) { + if (x == null) return; + print x; +} + +while (true) { + log(next()); +} \ No newline at end of file diff --git a/scripts/hello_world.toy b/scripts/hello_world.toy index 71aa9e7..d572290 100644 --- a/scripts/hello_world.toy +++ b/scripts/hello_world.toy @@ -1,12 +1,12 @@ -if (true) { - print "Who?"; +fn a(x) { + print x; } -else if (true) { - print "What?"; + +fn b() { + return 42; } -else { - print "IDK"; -} \ No newline at end of file + +a(b(), b()); \ No newline at end of file diff --git a/source/toy_ast.h b/source/toy_ast.h index 4c18bb9..839503d 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -76,6 +76,7 @@ typedef enum Toy_AstFlag { TOY_AST_FLAG_COLLECTION = 32, TOY_AST_FLAG_PAIR = 33, TOY_AST_FLAG_INDEX = 34, + TOY_AST_FLAG_FN_ARGUMENTS = 35, //unary flags TOY_AST_FLAG_NEGATE = 40, diff --git a/source/toy_compiler.c b/source/toy_compiler.c index 5c0621e..809431a 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -580,6 +580,18 @@ static unsigned int writeInstructionAggregate(Toy_Bytecode** mb, Toy_AstAggregat return 1; //leaves only 1 value on the stack } + else if (ast.flag == TOY_AST_FLAG_FN_ARGUMENTS) { + //BUGFIX: invoking a function as an argument gets messy, so re-count the aggregate elements and discard 'result' + int count = ast.left != NULL ? 2 : 1; + Toy_Ast* iter = ast.right; + + while (iter != NULL && iter->type == TOY_AST_AGGREGATE) { + iter = iter->aggregate.right; + count++; + } + + return count; + } else { fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST aggregate flag found\n" TOY_CC_RESET); exit(-1); @@ -776,7 +788,7 @@ static unsigned int writeInstructionReturn(Toy_Bytecode** mb, Toy_AstReturn ast) EMIT_BYTE(mb, code,0); EMIT_BYTE(mb, code,0); - return 0; + return retCount; } static unsigned int writeInstructionPrint(Toy_Bytecode** mb, Toy_AstPrint ast) { @@ -1114,7 +1126,7 @@ static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke 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 + EMIT_BYTE(mb, code, 0); //BUG: IDK how many returns return chainedInvoke ? 1 : 0; } diff --git a/source/toy_parser.c b/source/toy_parser.c index db26295..26b1bf1 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -724,7 +724,7 @@ static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast 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); + Toy_private_emitAstAggregate(bucketHandle, &args, TOY_AST_FLAG_FN_ARGUMENTS, ast); } consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' at the end of argument list"); @@ -902,10 +902,14 @@ static void makeContinueStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_ } static void makeReturnStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { - parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //expect an aggregate - Toy_private_emitAstReturn(bucketHandle, rootHandle); - - consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of return statement"); + if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) { + Toy_private_emitAstReturn(bucketHandle, rootHandle); + } + else { + parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //expect an aggregate + Toy_private_emitAstReturn(bucketHandle, rootHandle); + consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of return statement"); + } } static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { diff --git a/source/toy_token_types.h b/source/toy_token_types.h index f514f3e..e43564a 100644 --- a/source/toy_token_types.h +++ b/source/toy_token_types.h @@ -20,28 +20,28 @@ typedef enum Toy_TokenType { TOY_TOKEN_TYPE_ANY, //keywords and reserved words - TOY_TOKEN_KEYWORD_AS, + TOY_TOKEN_KEYWORD_AS, //unused TOY_TOKEN_KEYWORD_ASSERT, TOY_TOKEN_KEYWORD_BREAK, - TOY_TOKEN_KEYWORD_CLASS, + TOY_TOKEN_KEYWORD_CLASS, //unused TOY_TOKEN_KEYWORD_CONST, TOY_TOKEN_KEYWORD_CONTINUE, - TOY_TOKEN_KEYWORD_DO, + TOY_TOKEN_KEYWORD_DO, //unused TOY_TOKEN_KEYWORD_ELSE, - TOY_TOKEN_KEYWORD_EXPORT, - TOY_TOKEN_KEYWORD_FOR, - TOY_TOKEN_KEYWORD_FOREACH, - TOY_TOKEN_KEYWORD_FUNCTION, + TOY_TOKEN_KEYWORD_EXPORT, //unused + TOY_TOKEN_KEYWORD_FOR, //unused + TOY_TOKEN_KEYWORD_FOREACH, //unused + TOY_TOKEN_KEYWORD_FUNCTION, //remapped 'fn' TOY_TOKEN_KEYWORD_IF, - TOY_TOKEN_KEYWORD_IMPORT, - TOY_TOKEN_KEYWORD_IN, - TOY_TOKEN_KEYWORD_OF, + TOY_TOKEN_KEYWORD_IMPORT, //unused + TOY_TOKEN_KEYWORD_IN, //unused + TOY_TOKEN_KEYWORD_OF, //unused TOY_TOKEN_KEYWORD_PASS, TOY_TOKEN_KEYWORD_PRINT, TOY_TOKEN_KEYWORD_RETURN, TOY_TOKEN_KEYWORD_VAR, TOY_TOKEN_KEYWORD_WHILE, - TOY_TOKEN_KEYWORD_YIELD, + TOY_TOKEN_KEYWORD_YIELD, //unused //literal values TOY_TOKEN_LITERAL_TRUE, @@ -51,19 +51,19 @@ typedef enum Toy_TokenType { TOY_TOKEN_LITERAL_STRING, //math operators - TOY_TOKEN_OPERATOR_ADD, - TOY_TOKEN_OPERATOR_SUBTRACT, - TOY_TOKEN_OPERATOR_MULTIPLY, - TOY_TOKEN_OPERATOR_DIVIDE, - TOY_TOKEN_OPERATOR_MODULO, - TOY_TOKEN_OPERATOR_ADD_ASSIGN, - TOY_TOKEN_OPERATOR_SUBTRACT_ASSIGN, - TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN, - TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN, - TOY_TOKEN_OPERATOR_MODULO_ASSIGN, - TOY_TOKEN_OPERATOR_INCREMENT, - TOY_TOKEN_OPERATOR_DECREMENT, - TOY_TOKEN_OPERATOR_ASSIGN, + TOY_TOKEN_OPERATOR_ADD, // + + TOY_TOKEN_OPERATOR_SUBTRACT, // - + TOY_TOKEN_OPERATOR_MULTIPLY, // * + TOY_TOKEN_OPERATOR_DIVIDE, // / + TOY_TOKEN_OPERATOR_MODULO, // % + TOY_TOKEN_OPERATOR_ADD_ASSIGN, // += + TOY_TOKEN_OPERATOR_SUBTRACT_ASSIGN, // -= + TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN, // *= + TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN, // /= + TOY_TOKEN_OPERATOR_MODULO_ASSIGN, // %= + TOY_TOKEN_OPERATOR_INCREMENT, // ++ + TOY_TOKEN_OPERATOR_DECREMENT, // -- + TOY_TOKEN_OPERATOR_ASSIGN, // = //comparator operators TOY_TOKEN_OPERATOR_COMPARE_EQUAL, // == @@ -74,12 +74,12 @@ typedef enum Toy_TokenType { TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL, // >= //structural operators - TOY_TOKEN_OPERATOR_PAREN_LEFT, - TOY_TOKEN_OPERATOR_PAREN_RIGHT, - TOY_TOKEN_OPERATOR_BRACKET_LEFT, - TOY_TOKEN_OPERATOR_BRACKET_RIGHT, - TOY_TOKEN_OPERATOR_BRACE_LEFT, - TOY_TOKEN_OPERATOR_BRACE_RIGHT, + TOY_TOKEN_OPERATOR_PAREN_LEFT, // ( + TOY_TOKEN_OPERATOR_PAREN_RIGHT, // ) + TOY_TOKEN_OPERATOR_BRACKET_LEFT, // [ + TOY_TOKEN_OPERATOR_BRACKET_RIGHT, // ] + TOY_TOKEN_OPERATOR_BRACE_LEFT, // { + TOY_TOKEN_OPERATOR_BRACE_RIGHT, // } //other operators TOY_TOKEN_OPERATOR_AND, // && @@ -100,7 +100,7 @@ typedef enum Toy_TokenType { TOY_TOKEN_OPERATOR_PIPE, // | //meta tokens - TOY_TOKEN_ERROR, - TOY_TOKEN_EOF, + TOY_TOKEN_ERROR, //meta + TOY_TOKEN_EOF, //meta } Toy_TokenType; diff --git a/source/toy_vm.c b/source/toy_vm.c index 4820774..8795b24 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -718,7 +718,7 @@ static void processAssert(Toy_VM* vm) { //determine the args if (count == 1) { - message = TOY_VALUE_FROM_STRING(Toy_toString(&vm->memoryBucket, "assertion failed")); + message = TOY_VALUE_FROM_STRING(Toy_toString(&vm->memoryBucket, "assertion failed")); //TODO: better default error message value = Toy_popStack(&vm->stack); } else if (count == 2) { diff --git a/tests/scripts/test_function_invocation_in_arguments.toy b/tests/scripts/test_function_invocation_in_arguments.toy new file mode 100644 index 0000000..61f096b --- /dev/null +++ b/tests/scripts/test_function_invocation_in_arguments.toy @@ -0,0 +1,11 @@ + + +fn a(x) { + assert x == 42; +} + +fn b() { + return 42; +} + +a(b()); \ No newline at end of file diff --git a/tests/scripts/test_keyword_pass.toy b/tests/scripts/test_keyword_pass.toy new file mode 100644 index 0000000..e920c63 --- /dev/null +++ b/tests/scripts/test_keyword_pass.toy @@ -0,0 +1 @@ +pass; \ No newline at end of file diff --git a/tests/scripts/test_keyword_return.toy b/tests/scripts/test_keyword_return.toy new file mode 100644 index 0000000..2b1614a --- /dev/null +++ b/tests/scripts/test_keyword_return.toy @@ -0,0 +1,8 @@ + +fn empty() { //BUG: there's an extra return in the bytecode + return; +} + +fn full() { + return 42; +}