From 8277e7582cd9e34c99f6fc1e4d2987b60d70bf3f Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 10 Jan 2014 10:58:30 +0100 Subject: [PATCH] defer evaluation of logical operators --- arangod/Ahuacatl/ahuacatl-codegen.c | 22 ++++--- js/server/modules/org/arangodb/ahuacatl.js | 77 ++++++++++++++++++++++ js/server/tests/ahuacatl-logical.js | 29 ++++++-- 3 files changed, 112 insertions(+), 16 deletions(-) diff --git a/arangod/Ahuacatl/ahuacatl-codegen.c b/arangod/Ahuacatl/ahuacatl-codegen.c index 8ad46ab05d..619c59ead3 100644 --- a/arangod/Ahuacatl/ahuacatl-codegen.c +++ b/arangod/Ahuacatl/ahuacatl-codegen.c @@ -1939,28 +1939,30 @@ static void ProcessUnaryPlus (TRI_aql_codegen_js_t* const generator, //////////////////////////////////////////////////////////////////////////////// /// @brief generate code for binary and (a && b) +/// this passes two functions to the operator for lazy evaluation //////////////////////////////////////////////////////////////////////////////// static void ProcessBinaryAnd (TRI_aql_codegen_js_t* const generator, const TRI_aql_node_t* const node) { - ScopeOutput(generator, "aql.LOGICAL_AND("); + ScopeOutput(generator, "aql.LOGICAL_AND_FN(function () {\nreturn "); ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0)); - ScopeOutput(generator, ", "); + ScopeOutput(generator, "}, function () {\nreturn "); ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1)); - ScopeOutput(generator, ")"); + ScopeOutput(generator, "})"); } //////////////////////////////////////////////////////////////////////////////// /// @brief generate code for binary or (a || b) +/// this passes two functions to the operator for lazy evaluation //////////////////////////////////////////////////////////////////////////////// static void ProcessBinaryOr (TRI_aql_codegen_js_t* const generator, const TRI_aql_node_t* const node) { - ScopeOutput(generator, "aql.LOGICAL_OR("); + ScopeOutput(generator, "aql.LOGICAL_OR_FN(function () {\nreturn "); ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0)); - ScopeOutput(generator, ", "); + ScopeOutput(generator, "}, function () {\nreturn "); ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1)); - ScopeOutput(generator, ")"); + ScopeOutput(generator, "})"); } //////////////////////////////////////////////////////////////////////////////// @@ -2125,13 +2127,13 @@ static void ProcessBinaryIn (TRI_aql_codegen_js_t* const generator, static void ProcessTernary (TRI_aql_codegen_js_t* const generator, const TRI_aql_node_t* const node) { - ScopeOutput(generator, "aql.TERNARY_OPERATOR("); + ScopeOutput(generator, "aql.TERNARY_OPERATOR_FN("); ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0)); - ScopeOutput(generator, ", "); + ScopeOutput(generator, ", function () { \nreturn "); ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1)); - ScopeOutput(generator, ", "); + ScopeOutput(generator, "}, function () {\nreturn "); ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 2)); - ScopeOutput(generator, ")"); + ScopeOutput(generator, "})"); } //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/modules/org/arangodb/ahuacatl.js b/js/server/modules/org/arangodb/ahuacatl.js index 3192ea0130..8837cb4480 100644 --- a/js/server/modules/org/arangodb/ahuacatl.js +++ b/js/server/modules/org/arangodb/ahuacatl.js @@ -1064,6 +1064,26 @@ function TERNARY_OPERATOR (condition, truePart, falsePart) { return falsePart; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief execute ternary operator +/// +/// the condition operand must be a boolean value, returns either the truepart +/// or the falsepart +//////////////////////////////////////////////////////////////////////////////// + +function TERNARY_OPERATOR_FN (condition, truePart, falsePart) { + "use strict"; + + if (TYPEWEIGHT(condition) !== TYPEWEIGHT_BOOL) { + THROW(INTERNAL.errors.ERROR_QUERY_INVALID_LOGICAL_VALUE); + } + + if (condition) { + return truePart(); + } + return falsePart(); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief perform logical and /// @@ -1108,6 +1128,60 @@ function LOGICAL_OR (lhs, rhs) { return rhs; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief perform logical and +/// +/// both operands must be boolean values, returns a boolean, uses short-circuit +/// evaluation +//////////////////////////////////////////////////////////////////////////////// + +function LOGICAL_AND_FN (lhs, rhs) { + "use strict"; + + var l = lhs(); + if (TYPEWEIGHT(l) !== TYPEWEIGHT_BOOL) { + THROW(INTERNAL.errors.ERROR_QUERY_INVALID_LOGICAL_VALUE); + } + + if (! l) { + return false; + } + + var r = rhs(); + if (TYPEWEIGHT(r) !== TYPEWEIGHT_BOOL) { + THROW(INTERNAL.errors.ERROR_QUERY_INVALID_LOGICAL_VALUE); + } + + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief perform logical or +/// +/// both operands must be boolean values, returns a boolean, uses short-circuit +/// evaluation +//////////////////////////////////////////////////////////////////////////////// + +function LOGICAL_OR_FN (lhs, rhs) { + "use strict"; + + var l = lhs(); + if (TYPEWEIGHT(l) !== TYPEWEIGHT_BOOL) { + THROW(INTERNAL.errors.ERROR_QUERY_INVALID_LOGICAL_VALUE); + } + + if (l) { + return true; + } + + var r = rhs(); + if (TYPEWEIGHT(r) !== TYPEWEIGHT_BOOL) { + THROW(INTERNAL.errors.ERROR_QUERY_INVALID_LOGICAL_VALUE); + } + + return r; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief perform logical negation /// @@ -4002,8 +4076,11 @@ exports.GET_DOCUMENTS_SKIPLIST = GET_DOCUMENTS_SKIPLIST; exports.GET_DOCUMENTS_SKIPLIST_LIST = GET_DOCUMENTS_SKIPLIST_LIST; exports.COLLECTIONS = COLLECTIONS; exports.TERNARY_OPERATOR = TERNARY_OPERATOR; +exports.TERNARY_OPERATOR_FN = TERNARY_OPERATOR_FN; exports.LOGICAL_AND = LOGICAL_AND; exports.LOGICAL_OR = LOGICAL_OR; +exports.LOGICAL_AND_FN = LOGICAL_AND_FN; +exports.LOGICAL_OR_FN = LOGICAL_OR_FN; exports.LOGICAL_NOT = LOGICAL_NOT; exports.RELATIONAL_EQUAL = RELATIONAL_EQUAL; exports.RELATIONAL_UNEQUAL = RELATIONAL_UNEQUAL; diff --git a/js/server/tests/ahuacatl-logical.js b/js/server/tests/ahuacatl-logical.js index b77509f57f..cf384931c6 100644 --- a/js/server/tests/ahuacatl-logical.js +++ b/js/server/tests/ahuacatl-logical.js @@ -238,18 +238,35 @@ function ahuacatlLogicalTestSuite () { //////////////////////////////////////////////////////////////////////////////// testBinaryAndShortCircuit1 : function () { - // TODO: FIXME - // var expected = [ false ]; - // var actual = getQueryResults("RETURN false && FAIL('this will fail')"); - //assertEqual(expected, actual); + var expected = [ false ]; + var actual = getQueryResults("RETURN false && FAIL('this will fail')"); + assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// -/// @brief test binary and, short circuit evaluation +/// @brief test binary or, short circuit evaluation //////////////////////////////////////////////////////////////////////////////// testBinaryAndShortCircuit2 : function () { - assertException(function() { getQueryResults("RETURN false && FAIL('this will fail')"); }); + assertException(function() { getQueryResults("RETURN true && FAIL('this will fail')"); }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test binary or, short circuit evaluation +//////////////////////////////////////////////////////////////////////////////// + + testBinaryOrShortCircuit1 : function () { + var expected = [ true ]; + var actual = getQueryResults("RETURN true || FAIL('this will fail')"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test binary or, short circuit evaluation +//////////////////////////////////////////////////////////////////////////////// + + testBinaryOrShortCircuit2 : function () { + assertException(function() { getQueryResults("RETURN false || FAIL('this will fail')"); }); }, ////////////////////////////////////////////////////////////////////////////////