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",