From 967963c9d7522b664af2ba60647002c39ca97090 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Thu, 3 Aug 2023 15:22:06 +1000 Subject: [PATCH 01/11] Fixed a spelling mistake --- repl/lib_math.c | 4 ++-- source/toy_common.h | 2 +- test/scripts/lib/math.toy | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/repl/lib_math.c b/repl/lib_math.c index f68ce30..f0e13f7 100644 --- a/repl/lib_math.c +++ b/repl/lib_math.c @@ -1002,8 +1002,8 @@ int Toy_hookMath(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Liter // Comparison {"checkIsNaN", nativeCheckIsNaN}, - {"chechIsFinite", nativeCheckIsFinite}, - {"chechIsInfinite", nativeCheckIsInfinite}, + {"checkIsFinite", nativeCheckIsFinite}, + {"checkIsInfinite", nativeCheckIsInfinite}, {"epsilionCompare", nativeEpsilionCompare}, {NULL, NULL} diff --git a/source/toy_common.h b/source/toy_common.h index 7d8b621..2f9b239 100644 --- a/source/toy_common.h +++ b/source/toy_common.h @@ -64,7 +64,7 @@ The current patch version of Toy. This value is embedded into the bytecode. This value MUST fit into an unsigned char. !*/ -#define TOY_VERSION_PATCH 1 +#define TOY_VERSION_PATCH 2 /*! ### TOY_VERSION_BUILD diff --git a/test/scripts/lib/math.toy b/test/scripts/lib/math.toy index e8603ac..ede8884 100644 --- a/test/scripts/lib/math.toy +++ b/test/scripts/lib/math.toy @@ -144,8 +144,8 @@ import math; // test atanh { - assert chechIsInfinite(atanh(1)) == true, "atanh(1) failed"; - assert chechIsInfinite(atanh(-1)) == true, "atanh(-1) failed"; + assert checkIsInfinite(atanh(1)) == true, "atanh(1) failed"; + assert checkIsInfinite(atanh(-1)) == true, "atanh(-1) failed"; assert epsilionCompare(atanh(0), 0), "atanh(0) failed"; } @@ -159,21 +159,21 @@ import math; } -// test chechIsFinite +// test checkIsFinite { - assert chechIsFinite(NAN) == false, "chechIsFinite(NAN) failed"; - assert chechIsFinite(INFINITY) == false, "chechIsFinite(INFINITY) failed"; - assert chechIsFinite(0.0) == true, "chechIsFinite(0.0) failed"; - assert chechIsFinite(1) == true, "chechIsFinite(1) failed"; + assert checkIsFinite(NAN) == false, "checkIsFinite(NAN) failed"; + assert checkIsFinite(INFINITY) == false, "checkIsFinite(INFINITY) failed"; + assert checkIsFinite(0.0) == true, "checkIsFinite(0.0) failed"; + assert checkIsFinite(1) == true, "checkIsFinite(1) failed"; } -// test chechIsInfinite +// test checkIsInfinite { - assert chechIsInfinite(NAN) == false, "chechIsInfinite(NAN) failed"; - assert chechIsInfinite(INFINITY) == true, "chechIsInfinite(INFINITY) failed"; - assert chechIsInfinite(0.0) == false, "chechIsInfinite(0.0) failed"; - assert chechIsInfinite(1) == false, "chechIsInfinite(1) failed"; + assert checkIsInfinite(NAN) == false, "checkIsInfinite(NAN) failed"; + assert checkIsInfinite(INFINITY) == true, "checkIsInfinite(INFINITY) failed"; + assert checkIsInfinite(0.0) == false, "checkIsInfinite(0.0) failed"; + assert checkIsInfinite(1) == false, "checkIsInfinite(1) failed"; } // test epsilionCompare From 67e49b74776cb6de54114d196388e0f62da4a985 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 4 Aug 2023 14:45:07 +1000 Subject: [PATCH 02/11] Fixed the way an identifier was handled, resolved #99 --- source/toy_interpreter.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/toy_interpreter.c b/source/toy_interpreter.c index b5aa67f..4435068 100644 --- a/source/toy_interpreter.c +++ b/source/toy_interpreter.c @@ -582,7 +582,7 @@ static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) { typeIndex = (int)readByte(interpreter->bytecode, &interpreter->count); } - Toy_Literal identifier = interpreter->literalCache.literals[identifierIndex]; + Toy_Literal identifier = Toy_copyLiteral(interpreter->literalCache.literals[identifierIndex]); Toy_Literal type = Toy_copyLiteral(interpreter->literalCache.literals[typeIndex]); Toy_Literal typeIdn = type; @@ -597,6 +597,10 @@ static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) { interpreter->errorOutput("Can't redefine the variable \""); Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); + + Toy_freeLiteral(identifier); + Toy_freeLiteral(type); + return false; } @@ -623,14 +627,16 @@ static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) { Toy_printLiteralCustom(identifier, interpreter->errorOutput); interpreter->errorOutput("\"\n"); + Toy_freeLiteral(identifier); Toy_freeLiteral(type); Toy_freeLiteral(val); return false; } - Toy_freeLiteral(val); + Toy_freeLiteral(identifier); Toy_freeLiteral(type); + Toy_freeLiteral(val); return true; } From 604604e8bc8476cfbd0e4353bd97400f50971786 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 4 Aug 2023 14:46:49 +1000 Subject: [PATCH 03/11] Two opcodes weren't being used, resolved #98 --- source/toy_opcodes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index 08d774a..93e7277 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -29,8 +29,8 @@ typedef enum Toy_Opcode { TOY_OP_SCOPE_BEGIN, TOY_OP_SCOPE_END, - TOY_OP_TYPE_DECL, //declare a type to be used (as a literal) - TOY_OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal) + TOY_OP_TYPE_DECL_removed, + TOY_OP_TYPE_DECL_LONG_removed, TOY_OP_VAR_DECL, //declare a variable to be used (as a literal) TOY_OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal) From 64944c24f639212c5db81731f699601c2c0fa81d Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 4 Aug 2023 14:52:01 +1000 Subject: [PATCH 04/11] Snipped some duplicate code, resolved #97 --- source/toy_interpreter.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/source/toy_interpreter.c b/source/toy_interpreter.c index 4435068..a5f0fa5 100644 --- a/source/toy_interpreter.c +++ b/source/toy_interpreter.c @@ -1991,27 +1991,9 @@ static void readInterpreterSections(Toy_Interpreter* interpreter) { } break; - case TOY_LITERAL_TYPE: { - //what the literal is - Toy_LiteralType literalType = (Toy_LiteralType)readByte(interpreter->bytecode, &interpreter->count); - unsigned char constant = readByte(interpreter->bytecode, &interpreter->count); - - Toy_Literal typeLiteral = TOY_TO_TYPE_LITERAL(literalType, constant); - - //save the type - Toy_pushLiteralArray(&interpreter->literalCache, typeLiteral); - -#ifndef TOY_EXPORT - if (Toy_commandLine.verbose) { - printf("(type "); - Toy_printLiteral(typeLiteral); - printf(")\n"); - } -#endif - } - break; - - case TOY_LITERAL_TYPE_INTERMEDIATE: { + case TOY_LITERAL_TYPE: + case TOY_LITERAL_TYPE_INTERMEDIATE: + { //what the literal represents Toy_LiteralType literalType = (Toy_LiteralType)readByte(interpreter->bytecode, &interpreter->count); unsigned char constant = readByte(interpreter->bytecode, &interpreter->count); From d292b331842faecf1adb535c0cecc0a9448570c8 Mon Sep 17 00:00:00 2001 From: Ratstail91 Date: Fri, 4 Aug 2023 18:24:54 +1000 Subject: [PATCH 05/11] Tweaked types to stop MSVC complaining --- repl/lib_math.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/repl/lib_math.c b/repl/lib_math.c index f0e13f7..93c5ea4 100644 --- a/repl/lib_math.c +++ b/repl/lib_math.c @@ -221,7 +221,7 @@ static int nativeToRadians(Toy_Interpreter* interpreter, Toy_LiteralArray* argum // cast int to float to handle all types of numbers float degrees = TOY_IS_INTEGER(degreesLiteral)? TOY_AS_INTEGER(degreesLiteral) : TOY_AS_FLOAT(degreesLiteral); - float result = degrees * (LIB_MATH_PI / 180.0); + float result = degrees * (LIB_MATH_PI / 180.0f); //return the result Toy_Literal resultLiteral = TOY_TO_FLOAT_LITERAL(result); @@ -259,7 +259,7 @@ static int nativeToDegrees(Toy_Interpreter* interpreter, Toy_LiteralArray* argum // cast int to float to handle all types of numbers float radians = TOY_IS_INTEGER(radiansLiteral)? TOY_AS_INTEGER(radiansLiteral) : TOY_AS_FLOAT(radiansLiteral); - float result = radians * (180.0 / LIB_MATH_PI); + float result = radians * (180.0f / LIB_MATH_PI); //return the result Toy_Literal resultLiteral = TOY_TO_FLOAT_LITERAL(result); @@ -820,10 +820,10 @@ static int nativeCheckIsNaN(Toy_Interpreter* interpreter, Toy_LiteralArray* argu float x = TOY_IS_INTEGER(xLiteral)? TOY_AS_INTEGER(xLiteral) : TOY_AS_FLOAT(xLiteral); // calculate the result - float result = isnan(x); + int result = isnan(x); //return the result - Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result); + Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0); Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //cleanup @@ -859,10 +859,10 @@ static int nativeCheckIsFinite(Toy_Interpreter* interpreter, Toy_LiteralArray* a float x = TOY_IS_INTEGER(xLiteral)? TOY_AS_INTEGER(xLiteral) : TOY_AS_FLOAT(xLiteral); // calculate the result - float result = isfinite(x); + int result = isfinite(x); //return the result - Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result); + Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0); Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //cleanup @@ -898,10 +898,10 @@ static int nativeCheckIsInfinite(Toy_Interpreter* interpreter, Toy_LiteralArray* float x = TOY_IS_INTEGER(xLiteral)? TOY_AS_INTEGER(xLiteral) : TOY_AS_FLOAT(xLiteral); // calculate the result - float result = isinf(x); + int result = isinf(x); //return the result - Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result); + Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0); Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //cleanup @@ -950,10 +950,10 @@ static int nativeEpsilionCompare(Toy_Interpreter* interpreter, Toy_LiteralArray* float y = TOY_IS_INTEGER(yLiteral)? TOY_AS_INTEGER(yLiteral) : TOY_AS_FLOAT(yLiteral); // calculate the result - float result = (fabsf(x - y)) <= (LIB_MATH_EPSILON * fmaxf(1, fmaxf(fabsf(x), fabsf(y)))); + int result = (fabsf(x - y)) <= (LIB_MATH_EPSILON * fmaxf(1, fmaxf(fabsf(x), fabsf(y)))); // return the result - Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result); + Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0); Toy_pushLiteralArray(&interpreter->stack, resultLiteral); // cleanup From 3783c940646d2ec36da0dd432a1adb6b01d516e1 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 4 Aug 2023 18:43:07 +1000 Subject: [PATCH 06/11] Allow trailing commas when writing a compound --- source/toy_parser.c | 4 ++++ test/scripts/trailing-comma-bugfix.toy | 8 ++++++++ test/test_interpreter.c | 1 + 3 files changed, 13 insertions(+) create mode 100644 test/scripts/trailing-comma-bugfix.toy diff --git a/source/toy_parser.c b/source/toy_parser.c index 3bb96a5..db9b15b 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -168,6 +168,10 @@ static Toy_Opcode compound(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { consume(parser, TOY_TOKEN_COMMA, "Expected ',' in array or dictionary"); } + if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) { //allow for trailing commas + break; + } + iterations++; Toy_ASTNode* left = NULL; diff --git a/test/scripts/trailing-comma-bugfix.toy b/test/scripts/trailing-comma-bugfix.toy new file mode 100644 index 0000000..b9a0c22 --- /dev/null +++ b/test/scripts/trailing-comma-bugfix.toy @@ -0,0 +1,8 @@ +var array = [ + 1, 2, 3, + 4, 5, 6, + 7, 8, 9, //explicitly leave a trailing comma +]; + + +print "All good"; \ No newline at end of file diff --git a/test/test_interpreter.c b/test/test_interpreter.c index b67de22..6f1d8fa 100644 --- a/test/test_interpreter.c +++ b/test/test_interpreter.c @@ -140,6 +140,7 @@ int main() { "polyfill-remove.toy", "short-circuiting-support.toy", "ternary-expressions.toy", + "trailing-comma-bugfix.toy", "types.toy", NULL }; From cfec1b691198668c1252ae66465ce8e38bc113ee Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 6 Aug 2023 02:17:32 +1000 Subject: [PATCH 07/11] Added int to float coercions to function args and returns, when specified --- scripts/shadow.toy | 8 ++++++++ source/toy_interpreter.c | 14 ++++++++++++++ test/scripts/coercions.toy | 16 ++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 scripts/shadow.toy diff --git a/scripts/shadow.toy b/scripts/shadow.toy new file mode 100644 index 0000000..2b694a6 --- /dev/null +++ b/scripts/shadow.toy @@ -0,0 +1,8 @@ +//something was odd, so I broke this down for testing +import math; + +fn shadowCastPoint(x: float, y: float, depth: int) { + return sin(tan(x/y)) * depth; +} + +print shadowCastPoint(1, 1, 10); diff --git a/source/toy_interpreter.c b/source/toy_interpreter.c index a5f0fa5..1375a95 100644 --- a/source/toy_interpreter.c +++ b/source/toy_interpreter.c @@ -2310,6 +2310,13 @@ bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_Liter Toy_freeLiteral(argIdn); } + //BUGFIX: coerce ints to floats, if the function requires floats + if (TOY_IS_INTEGER(arg) && TOY_IS_TYPE(paramArray->literals[i + 1]) && TOY_AS_TYPE(paramArray->literals[i + 1]).typeOf == TOY_LITERAL_FLOAT) { + Toy_Literal f = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(arg) ); + Toy_freeLiteral(arg); + arg = f; + } + if (!Toy_setScopeVariable(inner.scope, paramArray->literals[i], arg, false)) { interpreter->errorOutput("[internal] Could not define parameter (bad type?)\n"); @@ -2404,6 +2411,13 @@ bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_Liter for (int i = 0; i < returnsFromInner.count && returnValue; i++) { Toy_Literal ret = Toy_popLiteralArray(&returnsFromInner); + //BUGFIX: coerce the returned integers to floats, if specified + if (returnArray->count > 0 && TOY_AS_TYPE(returnArray->literals[i]).typeOf == TOY_LITERAL_FLOAT && TOY_IS_INTEGER(ret)) { + Toy_Literal f = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(ret) ); + Toy_freeLiteral(ret); + ret = f; + } + //check the return types if (returnArray->count > 0 && TOY_AS_TYPE(returnArray->literals[i]).typeOf != ret.type) { interpreter->errorOutput("Bad type found in return value\n"); diff --git a/test/scripts/coercions.toy b/test/scripts/coercions.toy index 19acf6e..d0afe06 100644 --- a/test/scripts/coercions.toy +++ b/test/scripts/coercions.toy @@ -10,4 +10,20 @@ } +//test function coercion +{ + fn f(arg: float) { + assert typeof arg == float, "argument coercion failed"; + } + + f(42); + + fn g(): float { + return 42; + } + + assert typeof g() == float, "return coercion failed"; +} + + print "All good"; From f885fdaf4c27f908cd75872684a56fc20e1c1120 Mon Sep 17 00:00:00 2001 From: Ratstail91 Date: Sun, 6 Aug 2023 04:28:02 +1000 Subject: [PATCH 08/11] Short circuits are now functioning correctly, resolved #73 --- Repl.vcxproj | 2 ++ scripts/shadow.toy | 8 ----- scripts/test.toy | 13 ++++++++ source/toy_ast_node.c | 30 ++++++++++++++++++ source/toy_ast_node.h | 22 +++++++++++++ source/toy_compiler.c | 56 ++++++++++++++++++++++++++++++++-- source/toy_interpreter.c | 42 ++++++++++++++----------- source/toy_lexer.c | 6 ++-- source/toy_parser.c | 46 ++++++++++++++++++++-------- source/toy_token_types.h | 4 +-- test/scripts/short-circuit.toy | 14 +++++++++ test/test_interpreter.c | 1 + 12 files changed, 199 insertions(+), 45 deletions(-) delete mode 100644 scripts/shadow.toy create mode 100644 scripts/test.toy create mode 100644 test/scripts/short-circuit.toy diff --git a/Repl.vcxproj b/Repl.vcxproj index 2bd6283..d975652 100644 --- a/Repl.vcxproj +++ b/Repl.vcxproj @@ -136,6 +136,7 @@ + @@ -145,6 +146,7 @@ + diff --git a/scripts/shadow.toy b/scripts/shadow.toy deleted file mode 100644 index 2b694a6..0000000 --- a/scripts/shadow.toy +++ /dev/null @@ -1,8 +0,0 @@ -//something was odd, so I broke this down for testing -import math; - -fn shadowCastPoint(x: float, y: float, depth: int) { - return sin(tan(x/y)) * depth; -} - -print shadowCastPoint(1, 1, 10); diff --git a/scripts/test.toy b/scripts/test.toy new file mode 100644 index 0000000..4401bb4 --- /dev/null +++ b/scripts/test.toy @@ -0,0 +1,13 @@ +fn doA() { + print "doA()"; + return true; +} + +fn doB() { + print "doB()"; + return true; +} + +if (doA() || doB()) { + print "success"; +} \ No newline at end of file diff --git a/source/toy_ast_node.c b/source/toy_ast_node.c index 24669b1..1397846 100644 --- a/source/toy_ast_node.c +++ b/source/toy_ast_node.c @@ -124,6 +124,16 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) { //NO-OP break; + case TOY_AST_NODE_AND: + Toy_freeASTNode(node->pathAnd.left); + Toy_freeASTNode(node->pathAnd.right); + break; + + case TOY_AST_NODE_OR: + Toy_freeASTNode(node->pathOr.left); + Toy_freeASTNode(node->pathOr.right); + break; + case TOY_AST_NODE_PREFIX_INCREMENT: Toy_freeLiteral(node->prefixIncrement.identifier); break; @@ -348,6 +358,26 @@ void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) { *nodeHandle = tmp; } +void Toy_emitASTNodeAnd(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs) { + Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1); + + tmp->type = TOY_AST_NODE_AND; + tmp->binary.left = *nodeHandle; + tmp->binary.right = rhs; + + *nodeHandle = tmp; +} + +void Toy_emitASTNodeOr(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs) { + Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1); + + tmp->type = TOY_AST_NODE_OR; + tmp->binary.left = *nodeHandle; + tmp->binary.right = rhs; + + *nodeHandle = tmp; +} + void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) { Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1); diff --git a/source/toy_ast_node.h b/source/toy_ast_node.h index a9346b8..4f48c83 100644 --- a/source/toy_ast_node.h +++ b/source/toy_ast_node.h @@ -29,6 +29,8 @@ typedef enum Toy_ASTNodeType { TOY_AST_NODE_FOR, //for control flow TOY_AST_NODE_BREAK, //for control flow TOY_AST_NODE_CONTINUE, //for control flow + TOY_AST_NODE_AND, //for control flow + TOY_AST_NODE_OR, //for control flow TOY_AST_NODE_PREFIX_INCREMENT, //increment a variable TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable @@ -204,6 +206,24 @@ typedef struct Toy_NodeContinue { Toy_ASTNodeType type; } Toy_NodeContinue; +//and operator +void Toy_emitASTNodeAnd(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs); //handled node becomes lhs + +typedef struct Toy_NodeAnd { + Toy_ASTNodeType type; + Toy_ASTNode* left; + Toy_ASTNode* right; +} Toy_NodeAnd; + +//or operator +void Toy_emitASTNodeOr(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs); //handled node becomes lhs + +typedef struct Toy_NodeOr { + Toy_ASTNodeType type; + Toy_ASTNode* left; + Toy_ASTNode* right; +} Toy_NodeOr; + //pre-post increment/decrement void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier); void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier); @@ -263,6 +283,8 @@ union Toy_private_node { Toy_NodeFor pathFor; Toy_NodeBreak pathBreak; Toy_NodeContinue pathContinue; + Toy_NodeAnd pathAnd; + Toy_NodeOr pathOr; Toy_NodePrefixIncrement prefixIncrement; Toy_NodePrefixDecrement prefixDecrement; Toy_NodePostfixIncrement postfixIncrement; diff --git a/source/toy_compiler.c b/source/toy_compiler.c index baa3e2e..030ddc8 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -318,6 +318,12 @@ bool checkNodeInTree(Toy_ASTNode* tree, Toy_ASTNode* node) { case TOY_AST_NODE_FOR: return checkNodeInTree(tree->pathFor.preClause, node) || checkNodeInTree(tree->pathFor.condition, node) || checkNodeInTree(tree->pathFor.postClause, node) || checkNodeInTree(tree->pathFor.thenPath, node); + case TOY_AST_NODE_AND: + return checkNodeInTree(tree->pathAnd.left, node) || checkNodeInTree(tree->pathAnd.right, node); + + case TOY_AST_NODE_OR: + return checkNodeInTree(tree->pathOr.left, node) || checkNodeInTree(tree->pathOr.right, node); + case TOY_AST_NODE_ERROR: case TOY_AST_NODE_LITERAL: case TOY_AST_NODE_BREAK: @@ -429,8 +435,6 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode case TOY_OP_COMPARE_GREATER: case TOY_OP_COMPARE_GREATER_EQUAL: case TOY_OP_INVERT: - case TOY_OP_AND: - case TOY_OP_OR: //place the rhs result before the outer instruction compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte ret = TOY_OP_EOF; @@ -917,6 +921,54 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode } break; + case TOY_AST_NODE_AND: { + //process the lhs + Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, node->pathAnd.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode); + if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy + compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte + } + + //insert the AND opcode to signal a possible jump + compiler->bytecode[compiler->count++] = TOY_OP_AND; //1 byte + int jumpToEnd = compiler->count; + compiler->count += sizeof(unsigned short); //2 bytes + + //process the rhs + override = Toy_writeCompilerWithJumps(compiler, node->pathAnd.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode); + if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy + compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte + } + + //set the spot to jump to, to proceed + unsigned short tmpVal = compiler->count + jumpOffsets; + memcpy(compiler->bytecode + jumpToEnd, &tmpVal, sizeof(tmpVal)); + } + break; + + case TOY_AST_NODE_OR: { + //process the lhs + Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, node->pathOr.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode); + if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy + compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte + } + + //insert the AND opcode to signal a possible jump + compiler->bytecode[compiler->count++] = TOY_OP_OR; //1 byte + int jumpToEnd = compiler->count; + compiler->count += sizeof(unsigned short); //2 bytes + + //process the rhs + override = Toy_writeCompilerWithJumps(compiler, node->pathOr.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode); + if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy + compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte + } + + //set the spot to jump to, to proceed + unsigned short tmpVal = compiler->count + jumpOffsets; + memcpy(compiler->bytecode + jumpToEnd, &tmpVal, sizeof(tmpVal)); + } + break; + case TOY_AST_NODE_FN_RETURN: { //read each returned literal onto the stack, and return the number of values to return for (int i = 0; i < node->returns.returns->fnCollection.count; i++) { diff --git a/source/toy_interpreter.c b/source/toy_interpreter.c index 1375a95..db476f7 100644 --- a/source/toy_interpreter.c +++ b/source/toy_interpreter.c @@ -1048,57 +1048,63 @@ static bool execCompareLessEqual(Toy_Interpreter* interpreter, bool invert) { } static bool execAnd(Toy_Interpreter* interpreter) { - Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); - Toy_Literal rhsIdn = rhs; - if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { - Toy_freeLiteral(rhsIdn); - } - Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } - //short-circuit - broken, see issue #73 + //short-circuit - if not true if (!TOY_IS_TRUTHY(lhs)) { Toy_pushLiteralArray(&interpreter->stack, lhs); + + int target = (int)readShort(interpreter->bytecode, &interpreter->count); + + if (target + interpreter->codeStart > interpreter->length) { + interpreter->errorOutput("[internal] AND Jump out of range\n"); + return false; + } + + //actually jump + interpreter->count = target + interpreter->codeStart; } else { - Toy_pushLiteralArray(&interpreter->stack, rhs); + readShort(interpreter->bytecode, &interpreter->count); //discard } Toy_freeLiteral(lhs); - Toy_freeLiteral(rhs); return true; } static bool execOr(Toy_Interpreter* interpreter) { - Toy_Literal rhs = Toy_popLiteralArray(&interpreter->stack); Toy_Literal lhs = Toy_popLiteralArray(&interpreter->stack); - Toy_Literal rhsIdn = rhs; - if (TOY_IS_IDENTIFIER(rhs) && Toy_parseIdentifierToValue(interpreter, &rhs)) { - Toy_freeLiteral(rhsIdn); - } - Toy_Literal lhsIdn = lhs; if (TOY_IS_IDENTIFIER(lhs) && Toy_parseIdentifierToValue(interpreter, &lhs)) { Toy_freeLiteral(lhsIdn); } - //short-circuit - broken, see issue #73 + //short-circuit - if is true if (TOY_IS_TRUTHY(lhs)) { Toy_pushLiteralArray(&interpreter->stack, lhs); + + int target = (int)readShort(interpreter->bytecode, &interpreter->count); + + if (target + interpreter->codeStart > interpreter->length) { + interpreter->errorOutput("[internal] OR Jump out of range\n"); + return false; + } + + //actually jump + interpreter->count = target + interpreter->codeStart; } else { - Toy_pushLiteralArray(&interpreter->stack, rhs); + readShort(interpreter->bytecode, &interpreter->count); //discard } Toy_freeLiteral(lhs); - Toy_freeLiteral(rhs); return true; } diff --git a/source/toy_lexer.c b/source/toy_lexer.c index 80dd566..25eb78b 100644 --- a/source/toy_lexer.c +++ b/source/toy_lexer.c @@ -237,7 +237,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) { //scan for a keyword for (int i = 0; Toy_keywordTypes[i].keyword; i++) { - if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) { + if (strlen(Toy_keywordTypes[i].keyword) == (size_t)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) { Toy_Token token; token.type = Toy_keywordTypes[i].type; @@ -317,10 +317,10 @@ Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) { if (advance(lexer) != '&') { return makeErrorToken(lexer, "Unexpected '&'"); } else { - return makeToken(lexer, TOY_TOKEN_AND); + return makeToken(lexer, TOY_TOKEN_AND_AND); } - case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE); + case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR_OR : TOY_TOKEN_PIPE); case '?': return makeToken(lexer, TOY_TOKEN_QUESTION); case ':': return makeToken(lexer, TOY_TOKEN_COLON); diff --git a/source/toy_parser.c b/source/toy_parser.c index db9b15b..1b4f5af 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -339,6 +339,28 @@ static Toy_Opcode grouping(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { } } +static Toy_Opcode circuit(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { + advance(parser); + + //handle short-circuitable operators - && || + switch (parser->previous.type) { + case TOY_TOKEN_AND_AND: { + parsePrecedence(parser, nodeHandle, PREC_AND + 1); + return TOY_OP_AND; + } + + case TOY_TOKEN_OR_OR: { + parsePrecedence(parser, nodeHandle, PREC_OR + 1); + return TOY_OP_OR; + } + + default: { + error(parser, parser->previous, "Unexpected token passed to grouping precedence rule"); + return TOY_OP_EOF; + } + } +} + static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { advance(parser); @@ -432,16 +454,6 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { return TOY_OP_COMPARE_GREATER_EQUAL; } - case TOY_TOKEN_AND: { - parsePrecedence(parser, nodeHandle, PREC_AND + 1); - return TOY_OP_AND; - } - - case TOY_TOKEN_OR: { - parsePrecedence(parser, nodeHandle, PREC_OR + 1); - return TOY_OP_OR; - } - default: error(parser, parser->previous, "Unexpected token passed to binary precedence rule"); return TOY_OP_EOF; @@ -1002,8 +1014,8 @@ ParseRule parseRules[] = { //must match the token types {NULL, binary, PREC_COMPARISON},// TOKEN_GREATER, {NULL, binary, PREC_COMPARISON},// TOKEN_LESS_EQUAL, {NULL, binary, PREC_COMPARISON},// TOKEN_GREATER_EQUAL, - {NULL, binary, PREC_AND},// TOKEN_AND, - {NULL, binary, PREC_OR},// TOKEN_OR, + {NULL, circuit, PREC_AND},// TOKEN_AND, + {NULL, circuit, PREC_OR},// TOKEN_OR, //other operators {NULL, question, PREC_TERNARY}, //TOKEN_QUESTION, @@ -1285,6 +1297,16 @@ static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, Preced continue; } + if (opcode == TOY_OP_AND) { + Toy_emitASTNodeAnd(nodeHandle, rhsNode); + continue; + } + + if (opcode == TOY_OP_OR) { + Toy_emitASTNodeOr(nodeHandle, rhsNode); + continue; + } + Toy_emitASTNodeBinary(nodeHandle, rhsNode, opcode); //optimise away the constants diff --git a/source/toy_token_types.h b/source/toy_token_types.h index 2de0b3f..2f52b88 100644 --- a/source/toy_token_types.h +++ b/source/toy_token_types.h @@ -74,8 +74,8 @@ typedef enum Toy_TokenType { TOY_TOKEN_GREATER, TOY_TOKEN_LESS_EQUAL, TOY_TOKEN_GREATER_EQUAL, - TOY_TOKEN_AND, - TOY_TOKEN_OR, + TOY_TOKEN_AND_AND, + TOY_TOKEN_OR_OR, //other operators TOY_TOKEN_QUESTION, diff --git a/test/scripts/short-circuit.toy b/test/scripts/short-circuit.toy new file mode 100644 index 0000000..3c65eb2 --- /dev/null +++ b/test/scripts/short-circuit.toy @@ -0,0 +1,14 @@ +//These operators should short-circuit +assert true && false == false, "true && false == false failed"; +assert false && true == false, "false && true == false failed"; + +assert true || false == true, "true || false == true failed"; +assert false || true == true, "false || true == true failed"; + + +//make sure the right value is being returned when chained +assert "a" && "b" && "c" == "c", "chained && failed"; +assert "a" || "b" || "c" == "a", "chained || failed"; + + +print "All good"; diff --git a/test/test_interpreter.c b/test/test_interpreter.c index 6f1d8fa..59d7510 100644 --- a/test/test_interpreter.c +++ b/test/test_interpreter.c @@ -138,6 +138,7 @@ int main() { "panic-within-functions.toy", "polyfill-insert.toy", "polyfill-remove.toy", + "short-circuit.toy", "short-circuiting-support.toy", "ternary-expressions.toy", "trailing-comma-bugfix.toy", From fb4258f9df7c3b857a012de96612676e34160d97 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 6 Aug 2023 04:38:55 +1000 Subject: [PATCH 09/11] Fixed broken test --- test/scripts/short-circuiting-support.toy | 9 --------- test/test_interpreter.c | 10 +++++++--- 2 files changed, 7 insertions(+), 12 deletions(-) delete mode 100644 test/scripts/short-circuiting-support.toy diff --git a/test/scripts/short-circuiting-support.toy b/test/scripts/short-circuiting-support.toy deleted file mode 100644 index 5e5bb17..0000000 --- a/test/scripts/short-circuiting-support.toy +++ /dev/null @@ -1,9 +0,0 @@ -//explicitly support && and || short circuits - -assert 1 && 2 == 2, "&& short-circuit failed"; - -assert 1 || 2 == 1, "|| short-circuit failed"; - - -print "All good"; - diff --git a/test/test_interpreter.c b/test/test_interpreter.c index 59d7510..0f6537d 100644 --- a/test/test_interpreter.c +++ b/test/test_interpreter.c @@ -18,6 +18,7 @@ static void noPrintFn(const char* output) { //NO OP } +int failedAssertions = 0; int ignoredAssertions = 0; static void noAssertFn(const char* output) { if (strncmp(output, "!ignore", 7) == 0) { @@ -27,6 +28,7 @@ static void noAssertFn(const char* output) { fprintf(stderr, TOY_CC_ERROR "Assertion failure: "); fprintf(stderr, "%s", output); fprintf(stderr, "\n" TOY_CC_RESET); //default new line + failedAssertions++; } } @@ -139,7 +141,6 @@ int main() { "polyfill-insert.toy", "polyfill-remove.toy", "short-circuit.toy", - "short-circuiting-support.toy", "ternary-expressions.toy", "trailing-comma-bugfix.toy", "types.toy", @@ -162,7 +163,10 @@ int main() { return -1; } - printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); - return 0; + if (failedAssertions == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + + return failedAssertions; } From 401de578a51cac58be6c4024470bdad01f6fdff8 Mon Sep 17 00:00:00 2001 From: Ratstail91 Date: Sun, 6 Aug 2023 04:53:46 +1000 Subject: [PATCH 10/11] Short circuitable operators are extremely loose --- test/scripts/short-circuit.toy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/scripts/short-circuit.toy b/test/scripts/short-circuit.toy index 3c65eb2..11433f2 100644 --- a/test/scripts/short-circuit.toy +++ b/test/scripts/short-circuit.toy @@ -1,9 +1,9 @@ //These operators should short-circuit -assert true && false == false, "true && false == false failed"; -assert false && true == false, "false && true == false failed"; +assert (true && false) == false, "(true && false) == false failed"; +assert (false && true) == false, "(false && true) == false failed"; -assert true || false == true, "true || false == true failed"; -assert false || true == true, "false || true == true failed"; +assert (true || false) == true, "(true || false) == true failed"; +assert (false || true) == true, "(false || true) == true failed"; //make sure the right value is being returned when chained From 62fe86f99bb4be8cf75561787eee0d5b2e677959 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Wed, 9 Aug 2023 02:25:07 +1000 Subject: [PATCH 11/11] Fixed indexing in argument lists, resolved #102 --- scripts/test.toy | 21 +++++++++---------- source/toy_compiler.c | 2 +- .../indexing-in-argument-list-bugfix.toy | 20 ++++++++++++++++++ test/test_interpreter.c | 1 + 4 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 test/scripts/indexing-in-argument-list-bugfix.toy diff --git a/scripts/test.toy b/scripts/test.toy index 4401bb4..762cfff 100644 --- a/scripts/test.toy +++ b/scripts/test.toy @@ -1,13 +1,12 @@ -fn doA() { - print "doA()"; - return true; -} +import standard; +var array = [42]; -fn doB() { - print "doB()"; - return true; -} +var result = null; -if (doA() || doB()) { - print "success"; -} \ No newline at end of file +//problematic line +result = max(0, array[0]); + +assert result == 42, "Indexing in argument list failed"; + + +print "All good"; diff --git a/source/toy_compiler.c b/source/toy_compiler.c index 030ddc8..5c622a0 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -649,7 +649,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode for (int i = 0; i < node->fnCall.arguments->fnCollection.count; i++) { //reverse order, to count from the beginning in the interpreter //sub-calls if (node->fnCall.arguments->fnCollection.nodes[i].type != TOY_AST_NODE_LITERAL) { - Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, &node->fnCall.arguments->fnCollection.nodes[i], breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode); + Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, &node->fnCall.arguments->fnCollection.nodes[i], breakAddressesPtr, continueAddressesPtr, jumpOffsets, node); //BUGFIX: use node as rootNode, to allow indexing within argument lists if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte } diff --git a/test/scripts/indexing-in-argument-list-bugfix.toy b/test/scripts/indexing-in-argument-list-bugfix.toy new file mode 100644 index 0000000..f11d721 --- /dev/null +++ b/test/scripts/indexing-in-argument-list-bugfix.toy @@ -0,0 +1,20 @@ +fn max(lhs, rhs) { + if (lhs > rhs) { + return lhs; + } + else { + return rhs; + } +} + +var array = [42]; + +var result = null; + +//problematic line +result = max(0, array[0]); + +assert result == 42, "Indexing in argument list failed"; + + +print "All good"; diff --git a/test/test_interpreter.c b/test/test_interpreter.c index 0f6537d..2c12b59 100644 --- a/test/test_interpreter.c +++ b/test/test_interpreter.c @@ -129,6 +129,7 @@ int main() { "index-assignment-left-bugfix.toy", "index-dictionaries.toy", "index-strings.toy", + "indexing-in-argument-list-bugfix.toy", "jumps.toy", "jumps-in-functions.toy", "logicals.toy",