From f615725d72ca820e99b24d3d8bb858218ee30fe3 Mon Sep 17 00:00:00 2001 From: Mark Waddingham Date: Fri, 12 May 2017 12:47:36 +0100 Subject: [PATCH 1/5] [[ Bug 11323 ]] Add difference and symmetric difference This patch implements 'difference' and 'symmetric difference' commands for arrays. The difference command removes all keys from the destination which are present in the source, and leaves all others alone. The symmetric difference command removes all keys from the destination which are present in the source, and adds all keys from the source which are not present in the destination. --- engine/src/cmds.h | 50 +++--- engine/src/cmdsm.cpp | 98 ++++++++---- engine/src/exec-array.cpp | 132 ++++++++++++---- engine/src/exec.h | 12 +- engine/src/lextable.cpp | 2 + engine/src/newobj.cpp | 8 +- engine/src/parsedef.h | 2 + engine/src/parseerrors.h | 6 + .../core/array/setoperations.livecodescript | 147 ++++++++++++++++++ 9 files changed, 358 insertions(+), 99 deletions(-) diff --git a/engine/src/cmds.h b/engine/src/cmds.h index 13895487c15..b99cea0cb39 100644 --- a/engine/src/cmds.h +++ b/engine/src/cmds.h @@ -1756,41 +1756,35 @@ class MCSplit : public MCArrayOp class MCSetOp : public MCStatement { - MCVarref *destvar; - MCExpression *source; +public: + enum Op + { + kOpNone, + kOpUnion, + kOpUnionRecursively, + kOpIntersect, + kOpIntersectRecursively, + kOpDifference, + kOpSymmetricDifference + }; + +private: + MCAutoPointer destvar; + MCAutoPointer source; + protected: - bool intersect : 1; - // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect - bool recursive : 1; + Op op = kOpNone; + public: - MCSetOp() - { - source = NULL; - destvar = NULL; - } - virtual ~MCSetOp(); + MCSetOp(Op p_op) + : op(p_op) + { + } virtual Parse_stat parse(MCScriptPoint &); virtual void exec_ctxt(MCExecContext &ctxt); virtual void compile(MCSyntaxFactoryRef); }; -class MCArrayIntersectCmd : public MCSetOp -{ -public: - MCArrayIntersectCmd() - { - intersect = True; - } -}; - -class MCArrayUnionCmd : public MCSetOp -{ -public: - MCArrayUnionCmd() - { - intersect = False; - } -}; // MCStack manipulation comands in cmdss.cc diff --git a/engine/src/cmdsm.cpp b/engine/src/cmdsm.cpp index 3113d7b5dd5..9599d47797e 100644 --- a/engine/src/cmdsm.cpp +++ b/engine/src/cmdsm.cpp @@ -850,21 +850,24 @@ void MCArrayOp::compile(MCSyntaxFactoryRef ctxt) MCSyntaxFactoryEndStatement(ctxt); } - -MCSetOp::~MCSetOp() -{ - delete destvar; - delete source; -} - Parse_stat MCSetOp::parse(MCScriptPoint &sp) { initpoint(sp); + + if (op == kOpSymmetricDifference) + { + if (sp.skip_token(SP_COMMAND, TT_STATEMENT, S_DIFFERENCE) == PS_ERROR) + { + MCperror->add(PE_ARRAYOP_NODIFFERENCE, sp); + return PS_ERROR; + } + } + // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. Symbol_type type; if (sp.next(type) != PS_NORMAL || type != ST_ID - || sp.findvar(sp.gettoken_nameref(), &destvar) != PS_NORMAL + || sp.findvar(sp.gettoken_nameref(), &(&destvar)) != PS_NORMAL || destvar -> parsearray(sp) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_BADARRAY, sp); @@ -878,14 +881,25 @@ Parse_stat MCSetOp::parse(MCScriptPoint &sp) return PS_ERROR; } - if (sp.parseexp(True, False, &source) != PS_NORMAL) + if (sp.parseexp(True, False, &(&source)) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_BADEXP, sp); return PS_ERROR; } // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect - recursive = sp.skip_token(SP_SUGAR, TT_UNDEFINED, SG_RECURSIVELY) == PS_NORMAL; + if (sp.skip_token(SP_SUGAR, TT_UNDEFINED, SG_RECURSIVELY) == PS_NORMAL) + { + if (op == kOpIntersect) + op = kOpIntersectRecursively; + else if (op == kOpUnion) + op = kOpUnionRecursively; + else + { + MCperror->add(PE_ARRAYOP_BADRECURSIVE, sp); + return PS_ERROR; + } + } return PS_NORMAL; } @@ -894,7 +908,7 @@ void MCSetOp::exec_ctxt(MCExecContext &ctxt) { // ARRAYEVAL MCAutoValueRef t_src; - if (!ctxt . EvalExprAsValueRef(source, EE_ARRAYOP_BADEXP, &t_src)) + if (!ctxt . EvalExprAsValueRef(*source, EE_ARRAYOP_BADEXP, &t_src)) return; MCContainer t_container; @@ -909,21 +923,29 @@ void MCSetOp::exec_ctxt(MCExecContext &ctxt) return; MCAutoValueRef t_dst_value; - if (intersect) + switch(op) { - // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect - if (recursive) - MCArraysExecIntersectRecursive(ctxt, *t_dst, *t_src, &t_dst_value); - else + case kOpIntersect: MCArraysExecIntersect(ctxt, *t_dst, *t_src, &t_dst_value); - } - else - { - // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect - if (recursive) - MCArraysExecUnionRecursive(ctxt, *t_dst, *t_src, &t_dst_value); - else + break; + case kOpIntersectRecursively: + MCArraysExecIntersectRecursively(ctxt, *t_dst, *t_src, &t_dst_value); + break; + case kOpUnion: MCArraysExecUnion(ctxt, *t_dst, *t_src, &t_dst_value); + break; + case kOpUnionRecursively: + MCArraysExecUnionRecursively(ctxt, *t_dst, *t_src, &t_dst_value); + break; + case kOpDifference: + MCArraysExecDifference(ctxt, *t_dst, *t_src, &t_dst_value); + break; + case kOpSymmetricDifference: + MCArraysExecSymmetricDifference(ctxt, *t_dst, *t_src, &t_dst_value); + break; + case kOpNone: + MCUnreachable(); + break; } if (!ctxt . HasError()) @@ -938,19 +960,29 @@ void MCSetOp::compile(MCSyntaxFactoryRef ctxt) destvar -> compile(ctxt); source -> compile(ctxt); - if (intersect) + switch(op) { - if (recursive) - MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecIntersectRecursiveMethodInfo, 0, 1, 0); - else + case kOpIntersect: MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecIntersectMethodInfo, 0, 1, 0); - } - else - { - if (recursive) - MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecUnionRecursiveMethodInfo, 0, 1, 0); - else + break; + case kOpIntersectRecursively: + MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecIntersectRecursivelyMethodInfo, 0, 1, 0); + break; + case kOpUnion: MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecUnionMethodInfo, 0, 1, 0); + break; + case kOpUnionRecursively: + MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecUnionRecursivelyMethodInfo, 0, 1, 0); + break; + case kOpDifference: + MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecDifferenceMethodInfo, 0, 1, 0); + break; + case kOpSymmetricDifference: + MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecSymmetricDifferenceMethodInfo, 0, 1, 0); + break; + case kOpNone: + MCUnreachable(); + break; } MCSyntaxFactoryEndStatement(ctxt); diff --git a/engine/src/exec-array.cpp b/engine/src/exec-array.cpp index 45069f37302..d4e46845e66 100644 --- a/engine/src/exec-array.cpp +++ b/engine/src/exec-array.cpp @@ -49,9 +49,11 @@ MC_EXEC_DEFINE_EXEC_METHOD(Arrays, SplitByRow, 2) MC_EXEC_DEFINE_EXEC_METHOD(Arrays, SplitByColumn, 2) MC_EXEC_DEFINE_EXEC_METHOD(Arrays, SplitAsSet, 3) MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Union, 3) +MC_EXEC_DEFINE_EXEC_METHOD(Arrays, UnionRecursively, 3) MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Intersect, 3) -MC_EXEC_DEFINE_EXEC_METHOD(Arrays, UnionRecursive, 3) -MC_EXEC_DEFINE_EXEC_METHOD(Arrays, IntersectRecursive, 3) +MC_EXEC_DEFINE_EXEC_METHOD(Arrays, IntersectRecursively, 3) +MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Difference, 3) +MC_EXEC_DEFINE_EXEC_METHOD(Arrays, SymmetricDifference, 3) MC_EXEC_DEFINE_EVAL_METHOD(Arrays, ArrayEncode, 2) MC_EXEC_DEFINE_EVAL_METHOD(Arrays, ArrayDecode, 2) MC_EXEC_DEFINE_EVAL_METHOD(Arrays, MatrixMultiply, 3) @@ -581,8 +583,31 @@ void MCArraysExecSplitAsSet(MCExecContext& ctxt, MCStringRef p_string, MCStringR // end if // end repeat // +// if right is not an array then no-op +// else if left is not an array then left = right +// +// Semantics of 'symmetric difference tLeft with tRight' +// +// repeat for each key tKey in tRight +// if tKey is among the keys of tLeft then +// delete tLeft[tKey] +// else if tKey is not among the keys of tLeft then +// put tRight[tKey] into tLeft[tKey] +// end if +// end repeat +// +// if right is not an array then no-op +// else if left is not an array then left = right +// -void MCArraysDoUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, bool p_recursive, MCValueRef& r_result) +enum MCArrayDoUnionOp +{ + kMCArrayDoUnionOpUnion, + kMCArrayDoUnionOpUnionRecursively, + kMCArrayDoUnionOpSymmetricDifference, +}; + +static void MCArraysDoUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCArrayDoUnionOp p_op, MCValueRef& r_result) { if (!MCValueIsArray(p_src)) { @@ -617,27 +642,35 @@ void MCArraysDoUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, bo { bool t_key_exists; t_key_exists = MCArrayFetchValue(t_dst_array, ctxt . GetCaseSensitive(), t_key, t_dst_value); - - if (t_key_exists && !p_recursive) - continue; - - MCAutoValueRef t_recursive_result; + if (!t_key_exists) { - t_recursive_result = t_src_value; + if (!MCArrayStoreValue(*t_result, ctxt . GetCaseSensitive(), t_key, t_src_value)) + { + ctxt . Throw(); + return; + } } - else if (p_recursive) + else if (p_op == kMCArrayDoUnionOpUnionRecursively) { - MCArraysDoUnion(ctxt, t_dst_value, t_src_value, true, &t_recursive_result); - + MCAutoValueRef t_recursive_result; + MCArraysDoUnion(ctxt, t_dst_value, t_src_value, p_op, &t_recursive_result); if (ctxt . HasError()) return; + + if (!MCArrayStoreValue(*t_result, ctxt . GetCaseSensitive(), t_key, *t_recursive_result)) + { + ctxt . Throw(); + return; + } } - - if (!MCArrayStoreValue(*t_result, ctxt . GetCaseSensitive(), t_key, *t_recursive_result)) + else if (p_op == kMCArrayDoUnionOpSymmetricDifference) { - ctxt . Throw(); - return; + if (!MCArrayRemoveValue(*t_result, ctxt.GetCaseSensitive(), t_key)) + { + ctxt.Throw(); + return; + } } } @@ -655,8 +688,28 @@ void MCArraysDoUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, bo // end if // end repeat // +// if left is not an array then no-op +// else if right is not an array then left = empty +// +// Semantics of 'difference tLeft with tRight' +// +// repeat for each key tKey in tLeft +// if tKey is among the keys of tRight then +// delete variable tLeft[tKey] +// end if +// end repeat +// +// if left is not an array then no-op +// else if right is not an array then no-op + +enum MCArrayDoIntersectOp +{ + kMCArrayDoIntersectOpIntersect, + kMCArrayDoIntersectOpIntersectRecursively, + kMCArrayDoIntersectOpDifference, +}; -void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, bool p_recursive, MCValueRef& r_result) +static void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCArrayDoIntersectOp p_op, MCValueRef& r_result) { if (!MCValueIsArray(p_dst)) { @@ -664,10 +717,17 @@ void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src return; } - + if (!MCValueIsArray(p_src)) { - r_result = MCValueRetain(kMCEmptyString); + if (p_op != kMCArrayDoIntersectOpDifference) + { + r_result = MCValueRetain(kMCEmptyString); + } + else + { + r_result = MCValueRetain(p_dst); + } return; } @@ -691,11 +751,8 @@ void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src { bool t_key_exists; t_key_exists = MCArrayFetchValue(t_src_array, ctxt . GetCaseSensitive(), t_key, t_src_value); - - if (t_key_exists && !p_recursive) - continue; - - if (!t_key_exists) + + if (t_key_exists == (p_op == kMCArrayDoIntersectOpDifference)) { if (!MCArrayRemoveValue(*t_result, ctxt . GetCaseSensitive(), t_key)) { @@ -703,10 +760,10 @@ void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src return; } } - else if (p_recursive) + else if (p_op == kMCArrayDoIntersectOpIntersectRecursively) { MCAutoValueRef t_recursive_result; - MCArraysDoIntersect(ctxt, t_dst_value, t_src_value, true, &t_recursive_result); + MCArraysDoIntersect(ctxt, t_dst_value, t_src_value, p_op, &t_recursive_result); if (ctxt . HasError()) return; @@ -721,24 +778,35 @@ void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src void MCArraysExecUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) { - MCArraysDoUnion(ctxt, p_dst, p_src, false, r_result); + MCArraysDoUnion(ctxt, p_dst, p_src, kMCArrayDoUnionOpUnion, r_result); +} + +void MCArraysExecUnionRecursively(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) +{ + MCArraysDoUnion(ctxt, p_dst, p_src, kMCArrayDoUnionOpUnionRecursively, r_result); +} + +void MCArraysExecSymmetricDifference(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) +{ + MCArraysDoUnion(ctxt, p_dst, p_src, kMCArrayDoUnionOpSymmetricDifference, r_result); } void MCArraysExecIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) { - MCArraysDoIntersect(ctxt, p_dst, p_src, false, r_result); + MCArraysDoIntersect(ctxt, p_dst, p_src, kMCArrayDoIntersectOpIntersect, r_result); } -void MCArraysExecUnionRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) +void MCArraysExecIntersectRecursively(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) { - MCArraysDoUnion(ctxt, p_dst, p_src, true, r_result); + MCArraysDoIntersect(ctxt, p_dst, p_src, kMCArrayDoIntersectOpIntersectRecursively, r_result); } -void MCArraysExecIntersectRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) +void MCArraysExecDifference(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result) { - MCArraysDoIntersect(ctxt, p_dst, p_src, true, r_result); + MCArraysDoIntersect(ctxt, p_dst, p_src, kMCArrayDoIntersectOpDifference, r_result); } + //////////////////////////////////////////////////////////////////////////////// void MCArraysEvalArrayEncode(MCExecContext& ctxt, MCArrayRef p_array, MCStringRef p_version, MCDataRef& r_encoding) diff --git a/engine/src/exec.h b/engine/src/exec.h index 5c9dd84c340..15aa851a3f6 100644 --- a/engine/src/exec.h +++ b/engine/src/exec.h @@ -1870,9 +1870,11 @@ extern MCExecMethodInfo *kMCArraysExecSplitMethodInfo; extern MCExecMethodInfo *kMCArraysExecSplitByColumnMethodInfo; extern MCExecMethodInfo *kMCArraysExecSplitAsSetMethodInfo; extern MCExecMethodInfo *kMCArraysExecUnionMethodInfo; +extern MCExecMethodInfo *kMCArraysExecUnionRecursivelyMethodInfo; extern MCExecMethodInfo *kMCArraysExecIntersectMethodInfo; -extern MCExecMethodInfo *kMCArraysExecUnionRecursiveMethodInfo; -extern MCExecMethodInfo *kMCArraysExecIntersectRecursiveMethodInfo; +extern MCExecMethodInfo *kMCArraysExecIntersectRecursivelyMethodInfo; +extern MCExecMethodInfo *kMCArraysExecDifferenceMethodInfo; +extern MCExecMethodInfo *kMCArraysExecSymmetricDifferenceMethodInfo; extern MCExecMethodInfo *kMCArraysEvalArrayEncodeMethodInfo; extern MCExecMethodInfo *kMCArraysEvalArrayDecodeMethodInfo; extern MCExecMethodInfo *kMCArraysEvalMatrixMultiplyMethodInfo; @@ -1900,9 +1902,11 @@ void MCArraysExecSplit(MCExecContext& ctxt, MCStringRef p_string, MCStringRef p_ void MCArraysExecSplitByColumn(MCExecContext& ctxt, MCStringRef p_string, MCArrayRef& r_array); void MCArraysExecSplitAsSet(MCExecContext& ctxt, MCStringRef p_string, MCStringRef p_element_delimiter, MCArrayRef& r_array); void MCArraysExecUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); +void MCArraysExecUnionRecursively(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); void MCArraysExecIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); -void MCArraysExecUnionRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); -void MCArraysExecIntersectRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); +void MCArraysExecIntersectRecursively(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); +void MCArraysExecDifference(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); +void MCArraysExecSymmetricDifference(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result); void MCArraysEvalArrayEncode(MCExecContext& ctxt, MCArrayRef p_array, MCStringRef version, MCDataRef& r_encoding); void MCArraysEvalArrayDecode(MCExecContext& ctxt, MCDataRef p_encoding, MCArrayRef& r_array); void MCArraysEvalMatrixMultiply(MCExecContext& ctxt, MCArrayRef p_left, MCArrayRef p_right, MCArrayRef& r_result); diff --git a/engine/src/lextable.cpp b/engine/src/lextable.cpp index 5ed6a3fa641..33e316c7f54 100644 --- a/engine/src/lextable.cpp +++ b/engine/src/lextable.cpp @@ -309,6 +309,7 @@ LT command_table[] = {"define", TT_STATEMENT, S_DEFINE}, {"dehilite", TT_STATEMENT, S_UNHILITE}, {"delete", TT_STATEMENT, S_DELETE}, + {"difference", TT_STATEMENT, S_DIFFERENCE}, // MW-2008-11-05: [[ Dispatch Command ]] 'dispatch' is a statement keyword {"disable", TT_STATEMENT, S_DISABLE}, {"dispatch", TT_STATEMENT, S_DISPATCH}, @@ -410,6 +411,7 @@ LT command_table[] = {"stop", TT_STATEMENT, S_STOP}, {"subtract", TT_STATEMENT, S_SUBTRACT}, {"switch", TT_STATEMENT, S_SWITCH}, + {"symmetric", TT_STATEMENT, S_SYMMETRIC}, {"then", TT_THEN, S_UNDEFINED}, {"throw", TT_STATEMENT, S_THROW}, {"toplevel", TT_STATEMENT, S_TOP_LEVEL}, diff --git a/engine/src/newobj.cpp b/engine/src/newobj.cpp index bed3b3f443f..4257872acc6 100644 --- a/engine/src/newobj.cpp +++ b/engine/src/newobj.cpp @@ -91,6 +91,8 @@ MCStatement *MCN_new_statement(int2 which) return new MCDefine; case S_DELETE: return new MCDelete; + case S_DIFFERENCE: + return new MCSetOp(MCSetOp::kOpDifference); case S_DISABLE: return new MCDisable; // MW-2008-11-05: [[ Dispatch Command ]] Create a dispatch statement object @@ -149,7 +151,7 @@ MCStatement *MCN_new_statement(int2 which) case S_INSERT: return new MCInsert; case S_INTERSECT: - return new MCArrayIntersectCmd; + return new MCSetOp(MCSetOp::kOpIntersect); case S_KILL: return new MCKill; case S_LAUNCH: @@ -269,6 +271,8 @@ MCStatement *MCN_new_statement(int2 which) return new MCSubtract; case S_SWITCH: return new MCSwitch; + case S_SYMMETRIC: + return new MCSetOp(MCSetOp::kOpSymmetricDifference); case S_THROW: return new MCThrowKeyword; case S_TOP_LEVEL: @@ -286,7 +290,7 @@ MCStatement *MCN_new_statement(int2 which) case S_UNHILITE: return new MCUnhilite; case S_UNION: - return new MCArrayUnionCmd; + return new MCSetOp(MCSetOp::kOpUnion); case S_UNLOAD: return new MCUnload; case S_UNLOCK: diff --git a/engine/src/parsedef.h b/engine/src/parsedef.h index c933c73c752..ff136762d37 100644 --- a/engine/src/parsedef.h +++ b/engine/src/parsedef.h @@ -2016,6 +2016,7 @@ enum Statements { S_DECRYPT, S_DEFINE, S_DELETE, + S_DIFFERENCE, S_DISABLE, // MW-2008-11-05: [[ Dispatch Command ]] This is the 'dispatch' token's tag S_DISPATCH, @@ -2109,6 +2110,7 @@ enum Statements { S_START, S_STOP, S_SUBTRACT, + S_SYMMETRIC, S_SWITCH, S_THROW, S_TOP_LEVEL, diff --git a/engine/src/parseerrors.h b/engine/src/parseerrors.h index 3f0f34d6934..85a8d971127 100644 --- a/engine/src/parseerrors.h +++ b/engine/src/parseerrors.h @@ -1780,6 +1780,12 @@ enum Parse_errors // {PE-0576} messageDigest: bad parameters PE_MESSAGEDIGEST_BADPARAM, + + // {PE-0577} setop: missing 'difference' + PE_ARRAYOP_NODIFFERENCE, + + // {PE-0578} setop: 'recursive' only makes sense for union or intersect + PE_ARRAYOP_BADRECURSIVE, }; extern const char *MCparsingerrors; diff --git a/tests/lcs/core/array/setoperations.livecodescript b/tests/lcs/core/array/setoperations.livecodescript index 7f13dac96d9..985bac4da8b 100644 --- a/tests/lcs/core/array/setoperations.livecodescript +++ b/tests/lcs/core/array/setoperations.livecodescript @@ -109,6 +109,81 @@ on TestNestedArrayRecursiveUnion end TestNestedArrayRecursiveUnion +/* +Semantics of array symmetric difference: + +function ArraySymmetricDifference(pLeft, pRight) + repeat for each key tKey in pRight + if tKey is not among the keys of pLeft then + put pRight[tKey] into pLeft[tKey] + else + delete pLeft[tKey] + end if + end repeat + + return pLeft +end ArraySymmetricDifference + +*/ +on TestArraySymmetricDifference + local tLeftNonArray, tRightNonArray + + put "a" into tLeftNonArray + put "b" into tRightNonArray + symmetric difference tLeftNonArray with tRightNonArray + TestAssert "symmetric difference: left non-array, right non-array", tLeftNonArray is "a" + + local tRightArray + put "a" into tLeftNonArray + put "b" into tRightArray[1] + symmetric difference tLeftNonArray with tRightArray + TestAssert "symmetric difference: left non-array, right array", tLeftNonArray[1] is "b" + + local tLeftArray + put "a" into tLeftArray[1] + symmetric difference tLeftArray with tRightNonArray + TestAssert "symmetric difference: left array, right non-array", tLeftArray[1] is "a" + + put empty into tLeftArray + put "a" into tLeftArray[1] + put empty into tRightArray + put "b" into tRightArray[1] + symmetric difference tLeftArray with tRightArray + TestAssert "symmetric difference: left array, right array, same key", \ + tLeftArray is not an array + + put empty into tLeftArray + put "a" into tLeftArray[1] + put empty into tRightArray + put "b" into tRightArray[1] + put "c" into tRightArray[2] + symmetric difference tLeftArray with tRightArray + TestAssert "symmetric difference: left array, right array, right superset of left", \ + "1" is not among the keys of tLeftArray and tLeftArray[2] is "c" + + put empty into tLeftArray + put "a" into tLeftArray[1] + put "c" into tLeftArray[2] + put empty into tRightArray + put "b" into tRightArray[1] + symmetric difference tLeftArray with tRightArray + TestAssert "symmetric difference: left array, right array, right subset of left", \ + "1" is not among the keys of tLeftArray and tLeftArray[2] is "c" + + put empty into tLeftArray + put "a" into tLeftArray[1] + put "c" into tLeftArray[2] + put empty into tRightArray + put "b" into tRightArray[1] + put "d" into tRightArray[3] + symmetric difference tLeftArray with tRightArray + TestAssert "symmetric difference: left array, right array, neither is subset of other", \ + the number of elements in tLeftArray is 2 and \ + "1" is not among the keys of tLeftArray and \ + tLeftArray[2] is "c" and \ + tLeftArray[3] is "d" +end TestArraySymmetricDifference + /* Semantics of array intersect: @@ -209,3 +284,75 @@ on TestNestedArrayRecursiveIntersect TestAssert "recursive intersect: left value array, right value array, key present in left and right", tLeftArray4[1][2] is "b" end TestNestedArrayRecursiveIntersect + +/* +Semantics of array difference: + +function ArrayDifference(pLeft, pRight) + repeat for each key tKey in pLeft + if tKey is among the keys of pRight then + delete pLeft[tKey] + end if + end repeat + + return pLeft +end ArrayDifference + +*/ +on TestArrayDifference + local tLeftNonArray, tRightNonArray + + put "a" into tLeftNonArray + put "b" into tRightNonArray + difference tLeftNonArray with tRightNonArray + TestAssert "difference: left non-array, right non-array", tLeftNonArray is "a" + + local tRightArray + put "a" into tLeftNonArray + put "b" into tRightArray[1] + difference tLeftNonArray with tRightArray + TestAssert "difference: left non-array, right array", tLeftNonArray is "a" + + local tLeftArray + put "a" into tLeftArray[1] + difference tLeftArray with tRightNonArray + TestAssert "difference: left array, right non-array", tLeftArray[1] is "a" + + put empty into tLeftArray + put "a" into tLeftArray[1] + put empty into tRightArray + put "b" into tRightArray[1] + difference tLeftArray with tRightArray + TestAssert "difference: left array, right array, same key", \ + tLeftArray is not an array + + put empty into tLeftArray + put "a" into tLeftArray[1] + put empty into tRightArray + put "b" into tRightArray[1] + put "c" into tRightArray[2] + difference tLeftArray with tRightArray + TestAssert "difference: left array, right array, right superset of left", \ + tLeftArray is not an array + + put empty into tLeftArray + put "a" into tLeftArray[1] + put "c" into tLeftArray[2] + put empty into tRightArray + put "b" into tRightArray[1] + difference tLeftArray with tRightArray + TestAssert "difference: left array, right array, right subset of left", \ + the number of elements in tLeftArray is 1 and \ + tLeftArray[2] is "c" + + put empty into tLeftArray + put "a" into tLeftArray[1] + put "c" into tLeftArray[2] + put empty into tRightArray + put "b" into tRightArray[1] + put "d" into tRightArray[3] + difference tLeftArray with tRightArray + TestAssert "difference: left array, right array, neither is subset of other", \ + the number of elements in tLeftArray is 1 and \ + tLeftArray[2] is "c" +end TestArrayDifference From 330ef89cb99c70f7a334fa8a556890e6998604db Mon Sep 17 00:00:00 2001 From: Mark Waddingham Date: Fri, 12 May 2017 13:24:44 +0100 Subject: [PATCH 2/5] [[ Bug 11323 ]] Add 'into' form to set operation commands This patch adds an 'into' clause to the set operation commands allowing commands such as: intersect tLeft with tRight into tResult The operation of the commands is the same as the non-into form except that tLeft does not have to be a variable, and the result of the operation is placed into tResult rather than mutating tLeft. --- engine/src/cmds.h | 4 +- engine/src/cmdsm.cpp | 74 ++++++++++-- engine/src/parseerrors.h | 3 + .../core/array/setoperations.livecodescript | 111 ++++++++++++++++++ 4 files changed, 178 insertions(+), 14 deletions(-) diff --git a/engine/src/cmds.h b/engine/src/cmds.h index b99cea0cb39..c54cd2eeb30 100644 --- a/engine/src/cmds.h +++ b/engine/src/cmds.h @@ -1770,10 +1770,10 @@ class MCSetOp : public MCStatement private: MCAutoPointer destvar; + MCAutoPointer destexpr; MCAutoPointer source; - -protected: Op op = kOpNone; + bool is_into = false; public: MCSetOp(Op p_op) diff --git a/engine/src/cmdsm.cpp b/engine/src/cmdsm.cpp index 9599d47797e..c363e05b718 100644 --- a/engine/src/cmdsm.cpp +++ b/engine/src/cmdsm.cpp @@ -865,14 +865,24 @@ Parse_stat MCSetOp::parse(MCScriptPoint &sp) // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. + MCerrorlock++; Symbol_type type; + MCScriptPoint tsp(sp); if (sp.next(type) != PS_NORMAL || type != ST_ID || sp.findvar(sp.gettoken_nameref(), &(&destvar)) != PS_NORMAL || destvar -> parsearray(sp) != PS_NORMAL) { - MCperror->add(PE_ARRAYOP_BADARRAY, sp); - return PS_ERROR; - } + sp = tsp; + MCerrorlock--; + destvar.Reset(); + if (sp.parseexp(False, True, &(&destexpr)) != PS_NORMAL) + { + MCperror->add(PE_ARRAYOP_BADARRAY, sp); + return PS_ERROR; + } + } + else + MCerrorlock--; if (sp.skip_token(SP_REPEAT, TT_UNDEFINED, RF_WITH) == PS_ERROR && sp.skip_token(SP_FACTOR, TT_PREP, PT_BY) == PS_ERROR) @@ -900,6 +910,31 @@ Parse_stat MCSetOp::parse(MCScriptPoint &sp) return PS_ERROR; } } + + if (sp.skip_token(SP_FACTOR, TT_PREP, PT_INTO) == PS_NORMAL) + { + if (!destexpr) + { + destexpr.Reset(destvar.Release()); + } + + Symbol_type ttype; + if (sp.next(ttype) != PS_NORMAL || ttype != ST_ID + || sp.findvar(sp.gettoken_nameref(), &(&destvar)) != PS_NORMAL + || destvar -> parsearray(sp) != PS_NORMAL) + { + MCperror->add(PE_ARRAYOP_BADARRAY, sp); + return PS_ERROR; + } + + is_into = true; + } + + if (!destvar && is_into) + { + MCperror->add(PE_ARRAYOP_DSTNOTCONTAINER, sp); + return PS_ERROR; + } return PS_NORMAL; } @@ -911,16 +946,26 @@ void MCSetOp::exec_ctxt(MCExecContext &ctxt) if (!ctxt . EvalExprAsValueRef(*source, EE_ARRAYOP_BADEXP, &t_src)) return; + MCAutoValueRef t_dst; MCContainer t_container; - if (!destvar -> evalcontainer(ctxt, t_container)) - { - ctxt . LegacyThrow(EE_ARRAYOP_BADEXP); - return; - } + if (!is_into) + { + if (!destvar -> evalcontainer(ctxt, t_container)) + { + ctxt . LegacyThrow(EE_ARRAYOP_BADEXP); + return; + } - MCAutoValueRef t_dst; - if (!t_container.eval(ctxt, &t_dst)) - return; + if (!t_container.eval(ctxt, &t_dst)) + return; + } + else + { + if (!ctxt.EvalExprAsValueRef(*destexpr, EE_ARRAYOP_BADEXP, &t_dst)) + { + return; + } + } MCAutoValueRef t_dst_value; switch(op) @@ -949,7 +994,12 @@ void MCSetOp::exec_ctxt(MCExecContext &ctxt) } if (!ctxt . HasError()) - t_container.set(ctxt, *t_dst_value); + { + if (!is_into) + t_container.set(ctxt, *t_dst_value); + else + destvar->set(ctxt, *t_dst_value); + } } void MCSetOp::compile(MCSyntaxFactoryRef ctxt) diff --git a/engine/src/parseerrors.h b/engine/src/parseerrors.h index 85a8d971127..53038cf5967 100644 --- a/engine/src/parseerrors.h +++ b/engine/src/parseerrors.h @@ -1786,6 +1786,9 @@ enum Parse_errors // {PE-0578} setop: 'recursive' only makes sense for union or intersect PE_ARRAYOP_BADRECURSIVE, + + // {PE-0579} setop: destination is not a container (did you mean to use 'into'?) + PE_ARRAYOP_DSTNOTCONTAINER, }; extern const char *MCparsingerrors; diff --git a/tests/lcs/core/array/setoperations.livecodescript b/tests/lcs/core/array/setoperations.livecodescript index 985bac4da8b..6f1edda5917 100644 --- a/tests/lcs/core/array/setoperations.livecodescript +++ b/tests/lcs/core/array/setoperations.livecodescript @@ -62,6 +62,23 @@ on TestArrayUnion TestAssert "union: left array, right array, additional key", tArray3[1] is "b" end TestArrayUnion +on TestArrayUnionInto + local tLeft, tRight, tOut + put "a" into tLeft[1] + put "b" into tRight[2] + union tLeft with tRight into tOut + TestAssert "union into: left array unchanged", \ + the number of elements in tLeft is 1 and \ + tLeft[1] is "a" + TestAssert "union into: right array unchanged", \ + the number of elements in tRight is 1 and \ + tRight[2] is "b" + TestAssert "union into: out correct", \ + the number of elements in tOut is 2 and \ + tOut[1] is "a" and \ + tOut[2] is "b" +end TestArrayUnionInto + on TestNestedArrayNonRecursiveUnion local tLeftArray2, tRightArray2 @@ -109,6 +126,26 @@ on TestNestedArrayRecursiveUnion end TestNestedArrayRecursiveUnion +on TestArrayRecursiveUnionInto + local tLeft, tRight, tOut + put "a" into tLeft[1][1] + put "b" into tRight[1][2] + union tLeft with tRight recursively into tOut + TestAssert "union into: left array unchanged", \ + the number of elements in tLeft is 1 and \ + the number of elements in tLeft[1] is 1 and \ + tLeft[1][1] is "a" + TestAssert "union into: right array unchanged", \ + the number of elements in tRight is 1 and \ + the number of elements in tRight[1] is 1 and \ + tRight[1][2] is "b" + TestAssert "union into: out correct", \ + the number of elements in tOut is 1 and \ + the number of elements in tOut[1] is 2 and \ + tOut[1][1] is "a" and \ + tOut[1][2] is "b" +end TestArrayRecursiveUnionInto + /* Semantics of array symmetric difference: @@ -184,6 +221,23 @@ on TestArraySymmetricDifference tLeftArray[3] is "d" end TestArraySymmetricDifference +on TestArraySymmetricDifferenceInto + local tLeft, tRight, tOut + put "a" into tLeft[1] + put "b" into tRight[2] + symmetric difference tLeft with tRight into tOut + TestAssert "symmetric difference into: left array unchanged", \ + the number of elements in tLeft is 1 and \ + tLeft[1] is "a" + TestAssert "symmetric difference into: right array unchanged", \ + the number of elements in tRight is 1 and \ + tRight[2] is "b" + TestAssert "symmetric difference into: out correct", \ + the number of elements in tOut is 2 and \ + tOut[1] is "a" and \ + tOut[2] is "b" +end TestArraySymmetricDifferenceInto + /* Semantics of array intersect: @@ -235,6 +289,24 @@ on TestArrayIntersect end TestArrayIntersect +on TestArrayIntersectInto + local tLeft, tRight, tOut + put "a" into tLeft[1] + put "c" into tLeft[2] + put "b" into tRight[2] + put "d" into tRight[3] + intersect tLeft with tRight into tOut + TestAssert "intersect into: left array unchanged", \ + the number of elements in tLeft is 2 and \ + tLeft[1] is "a" and tLeft[2] is "c" + TestAssert "intersect into: right array unchanged", \ + the number of elements in tRight is 2 and \ + tRight[2] is "b" and tRight[3] is "d" + TestAssert "intersect into: out correct", \ + the number of elements in tOut is 1 and \ + tOut[2] is "c" +end TestArrayIntersectInto + on TestNestedArrayNonRecursiveIntersect local tLeftArray2, tRightArray2 @@ -285,6 +357,27 @@ on TestNestedArrayRecursiveIntersect end TestNestedArrayRecursiveIntersect +on TestArrayRecursiveIntersectInto + local tLeft, tRight, tOut + put "a" into tLeft[1][1] + put "c" into tLeft[1][2] + put "b" into tRight[1][2] + put "d" into tRight[1][3] + intersect tLeft with tRight recursively into tOut + TestAssert "intersect into: left array unchanged", \ + the number of elements in tLeft is 1 and \ + the number of elements in tLeft[1] is 2 and \ + tLeft[1][1] is "a" and tLeft[1][2] is "c" + TestAssert "intersect into: right array unchanged", \ + the number of elements in tRight is 1 and \ + the number of elements in tRight[1] is 2 and \ + tRight[1][2] is "b" and tRight[1][3] is "d" + TestAssert "intersect into: out correct", \ + the number of elements in tOut is 1 and \ + the number of elements in tOut[1] is 1 and \ + tOut[1][2] is "c" +end TestArrayRecursiveIntersectInto + /* Semantics of array difference: @@ -356,3 +449,21 @@ on TestArrayDifference the number of elements in tLeftArray is 1 and \ tLeftArray[2] is "c" end TestArrayDifference + +on TestArrayDifferenceInto + local tLeft, tRight, tOut + put "a" into tLeft[1] + put "c" into tLeft[2] + put "b" into tRight[2] + put "d" into tRight[3] + difference tLeft with tRight into tOut + TestAssert "difference into: left array unchanged", \ + the number of elements in tLeft is 2 and \ + tLeft[1] is "a" and tLeft[2] is "c" + TestAssert "difference into: right array unchanged", \ + the number of elements in tRight is 2 and \ + tRight[2] is "b" and tRIght[3] is "d" + TestAssert "difference into: out correct", \ + the number of elements in tOut is 1 and \ + tOut[1] is "a" +end TestArrayDifferenceInto From ee87efd59abf7f25c71fa0d8faa71065efa681cc Mon Sep 17 00:00:00 2001 From: Mark Waddingham Date: Fri, 12 May 2017 13:39:28 +0100 Subject: [PATCH 3/5] [[ Bug 11323 ]] Make sure MCVarref is complete in cmds.h The use of MCAutoPointer requires that MCVarref be completely defined in the cmds.h header. This patch corrects this by including variable.h in cmds.h. --- engine/src/cmds.h | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/src/cmds.h b/engine/src/cmds.h index c54cd2eeb30..150722ba2e8 100644 --- a/engine/src/cmds.h +++ b/engine/src/cmds.h @@ -23,6 +23,7 @@ along with LiveCode. If not see . */ #include "regex.h" #include "util.h" #include "uidc.h" +#include "variable.h" // general commands in cmds.cc From 6c862f43ddeb5d5dedfbbcd2ebf23f98a34f34fb Mon Sep 17 00:00:00 2001 From: Monte Goulding Date: Fri, 19 May 2017 08:36:51 +1000 Subject: [PATCH 4/5] [Bug 11323] Add docs and release note --- docs/dictionary/command/difference.lcdoc | 67 ++++++++++++++++++ docs/dictionary/command/intersect.lcdoc | 16 ++++- .../command/symmetric-difference.lcdoc | 70 +++++++++++++++++++ docs/dictionary/command/union.lcdoc | 16 ++++- docs/notes/bugfix-11323.md | 19 +++++ 5 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 docs/dictionary/command/difference.lcdoc create mode 100644 docs/dictionary/command/symmetric-difference.lcdoc create mode 100644 docs/notes/bugfix-11323.md diff --git a/docs/dictionary/command/difference.lcdoc b/docs/dictionary/command/difference.lcdoc new file mode 100644 index 00000000000..d953ef7c18e --- /dev/null +++ b/docs/dictionary/command/difference.lcdoc @@ -0,0 +1,67 @@ +Name: difference + +Type: command + +Syntax: difference with [into ] + +Summary: +Removes from an which have a +corresponding in another , and leaves all +others alone. + +Introduced: 9.0 + +OS: mac, windows, linux, ios, android + +Platforms: desktop, server, mobile + +Example: +local tLeft, tRight +put "green" into tLeft["color"] +put "left" into tLeft["align"] + +put "blue" into tRight["color"] +put "100" into tRight["width"] + +difference tLeft with tRight + +# RESULT +# the keys of tLeft = "align" +# tRight unchanged + +Parameters: +targetArray (array): +The value to modify. + + +templateArray (array): +The array to difference with. + +destinationArray (optional array): +A variable to set as the destination instead of mutating + +Description: +Use the to filter out +from an according to the contents of another . + +Each key of is checked to see whether there is a matching + in . The of + that match an of the +are removed from . + +The content of individual elements of the does not +affect the final result. Only which exist +in the , not their content, controls which + of are retained and which are +removed. + +If the into clause is used the operation of the commands is the same as +the non-into form except that does not have to be a +variable, and the result of the operation is placed into + rather than mutating . + +References: split (command), union (command), element (glossary), +array (glossary), command (glossary), key (glossary), element (keyword), +intersect (command), symmetric difference (command) + +Tags: properties diff --git a/docs/dictionary/command/intersect.lcdoc b/docs/dictionary/command/intersect.lcdoc index badda962c0d..5bc1451923e 100644 --- a/docs/dictionary/command/intersect.lcdoc +++ b/docs/dictionary/command/intersect.lcdoc @@ -2,7 +2,7 @@ Name: intersect Type: command -Syntax: intersect with [recursively] +Syntax: intersect with [recursively] [into ] Summary: Removes from an if they have no @@ -80,6 +80,8 @@ The value to modify. templateArray (array): The array to intersect with. +destinationArray (optional array): +A variable to set as the destination instead of mutating Description: Use the to filter out @@ -106,8 +108,16 @@ removed. If and have the same set of , the does not change the value of . +If the into clause is used the operation of the commands is the same as +the non-into form except that does not have to be a +variable, and the result of the operation is placed into + rather than mutating . + +Changes: +The `into` clause was added in LiveCode 9. + References: split (command), union (command), element (glossary), -array (glossary), command (glossary), key (glossary), element (keyword) +array (glossary), command (glossary), key (glossary), element (keyword), +difference (command), symmetric difference (command) Tags: properties - diff --git a/docs/dictionary/command/symmetric-difference.lcdoc b/docs/dictionary/command/symmetric-difference.lcdoc new file mode 100644 index 00000000000..67f73d34524 --- /dev/null +++ b/docs/dictionary/command/symmetric-difference.lcdoc @@ -0,0 +1,70 @@ +Name: symmetric difference + +Type: command + +Syntax: symmetric difference with [into ] + +Summary: +Removes from a target which have a +corresponding in a template , and adds + from the template that have no +which have no corresponding in the target . + +Introduced: 9.0 + +OS: mac, windows, linux, ios, android + +Platforms: desktop, server, mobile + +Example: +local tLeft, tRight +put "green" into tLeft["color"] +put "left" into tLeft["align"] + +put "blue" into tRight["color"] +put "100" into tRight["width"] + +symmetric difference tLeft with tRight + +# RESULT +# the keys of tLeft = "align" & "width" +# tRight unchanged + +Parameters: +targetArray (array): +The value to modify. + + +templateArray (array): +The array to symmetric difference with. + +destinationArray (optional array): +A variable to set as the destination instead of mutating + +Description: +Use the to set the +from an according to the contents of another . + +Each key of is checked to see whether there is a matching + in . The of + that match an of the +are removed from while the of +the that have no corresponding in +the are added to the . + +The content of individual elements of the does not +affect the final result. Only which exist +in the , not their content, controls which + of are retained and which are +removed. + +If the into clause is used the operation of the commands is the same as +the non-into form except that does not have to be a +variable, and the result of the operation is placed into + rather than mutating . + +References: split (command), union (command), element (glossary), +array (glossary), command (glossary), key (glossary), element (keyword), +intersect (command), difference (command) + +Tags: properties diff --git a/docs/dictionary/command/union.lcdoc b/docs/dictionary/command/union.lcdoc index edcfd3729e9..23ceac739a0 100644 --- a/docs/dictionary/command/union.lcdoc +++ b/docs/dictionary/command/union.lcdoc @@ -2,7 +2,7 @@ Name: union Type: command -Syntax: union with [recursively] +Syntax: union with [recursively] [into ] Summary: Combines two arrays. @@ -79,6 +79,8 @@ The value to modify. templateArray (array): The array to intersect with. +destinationArray (optional array): +A variable to set as the destination instead of mutating Description: Use the to combine two , eliminating @@ -106,9 +108,17 @@ in the , not their content, controls which but different content in each , the does not change the value of . +If the into clause is used the operation of the commands is the same as +the non-into form except that does not have to be a +variable, and the result of the operation is placed into + rather than mutating . + +Changes: +The `into` clause was added in LiveCode 9. + References: add (command), intersect (command), keys (function), element (glossary), key (glossary), command (glossary), array (glossary), -execute (glossary), element (keyword), + (operator) +execute (glossary), element (keyword), + (operator), +difference (command), symmetric difference (command) Tags: properties - diff --git a/docs/notes/bugfix-11323.md b/docs/notes/bugfix-11323.md new file mode 100644 index 00000000000..887969b3dca --- /dev/null +++ b/docs/notes/bugfix-11323.md @@ -0,0 +1,19 @@ +# New array commands `difference` and `symmetric difference` + +The `difference` command removes all keys from the destination +which are present in the source, and leaves all others alone. + +The `symmetric difference` command removes all keys from the +destination which are present in the source, and adds all keys +from the source which are not present in the destination. + +Additionally the `into` clause has been added to all array set +set operations (`union`, `intersect`, `difference`, `symmetric difference`) +allowing commands such as: + + intersect tLeft with tRight into tResult + +The operation of the commands is the same as the non-into form +except that tLeft does not have to be a variable, and the result +of the operation is placed into tResult rather than mutating +tLeft. From 66bb4acdecb4f8414869ceb6f0994025b4ca930c Mon Sep 17 00:00:00 2001 From: Mark Waddingham Date: Tue, 30 May 2017 11:23:39 +0100 Subject: [PATCH 5/5] [[ Bug 11323 ]] Tweak defintions of operations in test. --- tests/lcs/core/array/setoperations.livecodescript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lcs/core/array/setoperations.livecodescript b/tests/lcs/core/array/setoperations.livecodescript index 6f1edda5917..425ca1569d1 100644 --- a/tests/lcs/core/array/setoperations.livecodescript +++ b/tests/lcs/core/array/setoperations.livecodescript @@ -154,7 +154,7 @@ function ArraySymmetricDifference(pLeft, pRight) if tKey is not among the keys of pLeft then put pRight[tKey] into pLeft[tKey] else - delete pLeft[tKey] + delete variable pLeft[tKey] end if end repeat @@ -384,7 +384,7 @@ Semantics of array difference: function ArrayDifference(pLeft, pRight) repeat for each key tKey in pLeft if tKey is among the keys of pRight then - delete pLeft[tKey] + delete variable pLeft[tKey] end if end repeat