Skip to content

Commit fc49076

Browse files
committed
[[ Array Set Ops ]] Fix semantics of intersect and union
This patch amends the array set operations so that they have simple and well-defined semantics- Union: ``` repeat for each key tKey in tRight if tKey is not among the keys of tLeft then put tRight[tKey] into tLeft[tKey] else if tRecursive then union tLeft[tKey] with tRight[tKey] recursively end if end repeat ``` Intersect: ``` repeat for each key tKey in tLeft if tKey is not among the keys of tRight then delete variable tLeft[tKey] else if tRecursive then intersect tLeft[tKey] with tRight[tKey] recursively end if end repeat ``` This is a change from 6.7, in that the recursive behavior is now consistent with the top-level. This affects only the following two invocations of array intersect: ` intersect tLeftString with tRightString` ` intersect tLeftString with tRightArray` both of which result in tLeftString being empty in 6.7. Now these both result in tLeftString being unchanged.
1 parent 0eb549e commit fc49076

File tree

5 files changed

+381
-116
lines changed

5 files changed

+381
-116
lines changed

docs/notes/bugfix-16434.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Array intersect and union differences between 6.7 and 7

engine/src/cmdsm.cpp

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,17 +1490,9 @@ void MCSetOp::exec_ctxt(MCExecContext &ctxt)
14901490
#endif /* MCSetOp */
14911491

14921492
// ARRAYEVAL
1493-
MCAutoValueRef t_src_value;
1494-
if (!ctxt . EvalExprAsValueRef(source, EE_ARRAYOP_BADEXP, &t_src_value))
1493+
MCAutoValueRef t_src;
1494+
if (!ctxt . EvalExprAsValueRef(source, EE_ARRAYOP_BADEXP, &t_src))
14951495
return;
1496-
1497-
MCValueRef t_source;
1498-
1499-
if (!MCValueIsArray(*t_src_value))
1500-
t_source = kMCEmptyString;
1501-
else
1502-
t_source = *t_src_value;
1503-
15041496

15051497
MCAutoPointer<MCContainer> t_container;
15061498
if (!destvar -> evalcontainer(ctxt, &t_container))
@@ -1509,53 +1501,54 @@ void MCSetOp::exec_ctxt(MCExecContext &ctxt)
15091501
return;
15101502
}
15111503

1512-
MCAutoValueRef t_container_value;
1513-
if (!t_container -> eval(ctxt, &t_container_value))
1514-
return;
1515-
1516-
MCAutoArrayRef t_dst_immutable_array;
1517-
MCAutoArrayRef t_dst_array;
1518-
if (!ctxt . ConvertToArray(*t_container_value, &t_dst_immutable_array)
1519-
|| !MCArrayMutableCopy(*t_dst_immutable_array, &t_dst_array))
1520-
return;
1521-
1522-
MCAutoArrayRef t_src_array;
1523-
if (!ctxt . ConvertToArray(t_source, &t_src_array))
1504+
MCAutoValueRef t_dst;
1505+
if (!t_container -> eval(ctxt, &t_dst))
15241506
return;
15251507

1508+
MCAutoValueRef t_dst_value;
15261509
if (intersect)
15271510
{
15281511
// MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect
15291512
if (recursive)
1530-
MCArraysExecIntersectRecursive(ctxt, *t_dst_array, *t_src_array);
1513+
MCArraysExecIntersectRecursive(ctxt, *t_dst, *t_src, &t_dst_value);
15311514
else
1532-
MCArraysExecIntersect(ctxt, *t_dst_array, *t_src_array);
1515+
MCArraysExecIntersect(ctxt, *t_dst, *t_src, &t_dst_value);
15331516
}
15341517
else
15351518
{
15361519
// MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect
15371520
if (recursive)
1538-
MCArraysExecUnionRecursive(ctxt, *t_dst_array, *t_src_array);
1521+
MCArraysExecUnionRecursive(ctxt, *t_dst, *t_src, &t_dst_value);
15391522
else
1540-
MCArraysExecUnion(ctxt, *t_dst_array, *t_src_array);
1523+
MCArraysExecUnion(ctxt, *t_dst, *t_src, &t_dst_value);
15411524
}
15421525

15431526
if (!ctxt . HasError())
1544-
t_container -> set(ctxt, *t_dst_array);
1527+
t_container -> set(ctxt, *t_dst_value);
15451528
}
15461529

15471530
void MCSetOp::compile(MCSyntaxFactoryRef ctxt)
15481531
{
15491532
MCSyntaxFactoryBeginStatement(ctxt, line, pos);
15501533

15511534
// MUTABLE ARRAY
1552-
destvar -> compile_inout(ctxt);
1535+
destvar -> compile(ctxt);
15531536
source -> compile(ctxt);
15541537

1555-
if (intersect)
1556-
MCSyntaxFactoryExecMethod(ctxt, kMCArraysExecIntersectMethodInfo);
1557-
else
1558-
MCSyntaxFactoryExecMethod(ctxt, kMCArraysExecUnionMethodInfo);
1538+
if (intersect)
1539+
{
1540+
if (recursive)
1541+
MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecIntersectRecursiveMethodInfo, 0, 1, 0);
1542+
else
1543+
MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecIntersectMethodInfo, 0, 1, 0);
1544+
}
1545+
else
1546+
{
1547+
if (recursive)
1548+
MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecUnionRecursiveMethodInfo, 0, 1, 0);
1549+
else
1550+
MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecUnionMethodInfo, 0, 1, 0);
1551+
}
15591552

15601553
MCSyntaxFactoryEndStatement(ctxt);
15611554
}

engine/src/exec-array.cpp

Lines changed: 138 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Split, 4)
4545
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, SplitByRow, 2)
4646
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, SplitByColumn, 2)
4747
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, SplitAsSet, 3)
48-
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Union, 2)
49-
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Intersect, 2)
48+
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Union, 3)
49+
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, Intersect, 3)
50+
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, UnionRecursive, 3)
51+
MC_EXEC_DEFINE_EXEC_METHOD(Arrays, IntersectRecursive, 3)
5052
MC_EXEC_DEFINE_EVAL_METHOD(Arrays, ArrayEncode, 2)
5153
MC_EXEC_DEFINE_EVAL_METHOD(Arrays, ArrayDecode, 2)
5254
MC_EXEC_DEFINE_EVAL_METHOD(Arrays, MatrixMultiply, 3)
@@ -560,117 +562,173 @@ void MCArraysExecSplitAsSet(MCExecContext& ctxt, MCStringRef p_string, MCStringR
560562

561563
////////////////////////////////////////////////////////////////////////////////
562564

563-
void MCArraysDoUnion(MCExecContext& ctxt, MCArrayRef p_dst_array, MCArrayRef p_src_array, bool p_recursive)
565+
//
566+
// Semantics of 'union tLeft with tRight [recursively]'
567+
//
568+
// repeat for each key tKey in tRight
569+
// if tKey is not among the keys of tLeft then
570+
// put tRight[tKey] into tLeft[tKey]
571+
// else if tRecursive then
572+
// union tLeft[tKey] with tRight[tKey] recursively
573+
// end if
574+
// end repeat
575+
//
576+
577+
void MCArraysDoUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, bool p_recursive, MCValueRef& r_result)
564578
{
565-
MCNameRef t_key;
566-
MCValueRef t_src_value;
567-
MCValueRef t_dst_value;
568-
uintptr_t t_iterator;
569-
t_iterator = 0;
579+
if (!MCValueIsArray(p_src))
580+
{
581+
r_result = MCValueRetain(p_dst);
582+
return;
583+
}
584+
585+
if (!MCValueIsArray(p_dst))
586+
{
587+
r_result = MCValueRetain(p_src);
588+
return;
589+
590+
}
591+
592+
MCArrayRef t_src_array;
593+
t_src_array = (MCArrayRef)p_src;
594+
595+
MCArrayRef t_dst_array;
596+
t_dst_array = (MCArrayRef)p_dst;
570597

571-
bool t_is_array;
598+
MCAutoArrayRef t_result;
599+
if (!MCArrayMutableCopy(t_dst_array, &t_result))
600+
return;
601+
602+
MCNameRef t_key;
603+
MCValueRef t_src_value;
604+
MCValueRef t_dst_value;
605+
uintptr_t t_iterator;
606+
t_iterator = 0;
572607

573-
while(MCArrayIterate(p_src_array, t_iterator, t_key, t_src_value))
608+
while(MCArrayIterate(t_src_array, t_iterator, t_key, t_src_value))
574609
{
575-
if (MCArrayFetchValue(p_dst_array, ctxt . GetCaseSensitive(), t_key, t_dst_value))
610+
bool t_key_exists;
611+
t_key_exists = MCArrayFetchValue(t_dst_array, ctxt . GetCaseSensitive(), t_key, t_dst_value);
612+
613+
if (t_key_exists && !p_recursive)
614+
continue;
615+
616+
MCAutoValueRef t_recursive_result;
617+
if (!t_key_exists)
576618
{
577-
if (p_recursive && MCValueIsArray(t_dst_value) && MCValueIsArray(t_src_value))
578-
{
579-
// AL-2015-02-23: [[ Bug 14658 ]] Ensure recursive set operations are
580-
// performed on mutable copies of sub-arrays.
581-
MCAutoArrayRef t_dst_subarray;
582-
if (!MCArrayMutableCopy((MCArrayRef)t_dst_value, &t_dst_subarray))
583-
return;
584-
585-
MCArraysExecUnionRecursive(ctxt, *t_dst_subarray, (MCArrayRef)t_src_value);
586-
587-
if (!MCArrayStoreValue(p_dst_array, ctxt . GetCaseSensitive(), t_key, *t_dst_subarray))
588-
return;
589-
590-
if (ctxt . HasError())
591-
return;
592-
}
593-
continue;
619+
t_recursive_result = t_src_value;
620+
}
621+
else if (p_recursive)
622+
{
623+
MCArraysDoUnion(ctxt, t_dst_value, t_src_value, true, &t_recursive_result);
624+
625+
if (ctxt . HasError())
626+
return;
594627
}
595628

596-
if (!MCArrayStoreValue(p_dst_array, ctxt . GetCaseSensitive(), t_key, MCValueRetain(t_src_value)))
597-
{
598-
ctxt . Throw();
599-
return;
600-
}
629+
if (!MCArrayStoreValue(*t_result, ctxt . GetCaseSensitive(), t_key, *t_recursive_result))
630+
{
631+
ctxt . Throw();
632+
return;
633+
}
601634
}
635+
636+
r_result = MCValueRetain(*t_result);
602637
}
603638

604-
void MCArraysDoIntersect(MCExecContext& ctxt, MCArrayRef p_dst_array, MCArrayRef p_src_array, bool p_recursive)
639+
//
640+
// Semantics of 'intersect tLeft with tRight [recursively]'
641+
//
642+
// repeat for each key tKey in tLeft
643+
// if tKey is not among the keys of tRight then
644+
// delete variable tLeft[tKey]
645+
// else if tRecursive then
646+
// intersect tLeft[tKey] with tRight[tKey] recursively
647+
// end if
648+
// end repeat
649+
//
650+
651+
void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, bool p_recursive, MCValueRef& r_result)
605652
{
606-
MCNameRef t_key;
607-
MCValueRef t_src_value;
608-
MCValueRef t_dst_value;
609-
MCAutoArrayRef t_orig_dst_array;
610-
uintptr_t t_iterator;
611-
t_iterator = 0;
612-
613-
bool t_is_array;
653+
if (!MCValueIsArray(p_dst))
654+
{
655+
r_result = MCValueRetain(p_dst);
656+
return;
657+
658+
}
614659

615-
// TS-2015-19-18: [[ Bug 15948 ]] Take a copy of p_dst_array and iterate through
616-
// the copy so that we are not removing values from the same array. Otherwise
617-
// the array may be rehashed during the MCArrayRemoveValue() call and break the
618-
// iteration sequence.
619-
if (!MCArrayCopy(p_dst_array, &t_orig_dst_array)) {
660+
if (!MCValueIsArray(p_src))
661+
{
662+
r_result = MCValueRetain(kMCEmptyString);
620663
return;
621664
}
622665

623-
// Loop through the copy of the array, not the one that we will be removing entries
624-
// from.
625-
while(MCArrayIterate(*t_orig_dst_array, t_iterator, t_key, t_dst_value))
666+
MCArrayRef t_dst_array;
667+
t_dst_array = (MCArrayRef)p_dst;
668+
669+
MCArrayRef t_src_array;
670+
t_src_array = (MCArrayRef)p_src;
671+
672+
MCAutoArrayRef t_result;
673+
if (!MCArrayMutableCopy(t_dst_array, &t_result))
674+
return;
675+
676+
MCNameRef t_key;
677+
MCValueRef t_src_value;
678+
MCValueRef t_dst_value;
679+
uintptr_t t_iterator;
680+
t_iterator = 0;
681+
682+
while(MCArrayIterate(t_dst_array, t_iterator, t_key, t_dst_value))
626683
{
627-
if (MCArrayFetchValue(p_src_array, ctxt . GetCaseSensitive(), t_key, t_src_value))
684+
bool t_key_exists;
685+
t_key_exists = MCArrayFetchValue(t_src_array, ctxt . GetCaseSensitive(), t_key, t_src_value);
686+
687+
if (t_key_exists && !p_recursive)
688+
continue;
689+
690+
if (!t_key_exists)
628691
{
629-
if (p_recursive && MCValueIsArray(t_dst_value) && MCValueIsArray(t_src_value))
692+
if (!MCArrayRemoveValue(*t_result, ctxt . GetCaseSensitive(), t_key))
630693
{
631-
// AL-2015-02-23: [[ Bug 14658 ]] Ensure recursive set operations are
632-
// performed on mutable copies of sub-arrays.
633-
MCAutoArrayRef t_dst_subarray;
634-
if (!MCArrayMutableCopy((MCArrayRef)t_dst_value, &t_dst_subarray))
635-
return;
636-
637-
MCArraysExecIntersectRecursive(ctxt, *t_dst_subarray, (MCArrayRef)t_src_value);
638-
639-
if (!MCArrayStoreValue(p_dst_array, ctxt . GetCaseSensitive(), t_key, *t_dst_subarray))
640-
return;
641-
642-
if (ctxt . HasError())
643-
return;
694+
ctxt . Throw();
695+
return;
644696
}
645-
continue;
646697
}
647-
648-
if (!MCArrayRemoveValue(p_dst_array, ctxt . GetCaseSensitive(), t_key))
649-
{
650-
ctxt . Throw();
651-
return;
652-
}
698+
else if (p_recursive)
699+
{
700+
MCAutoValueRef t_recursive_result;
701+
MCArraysDoIntersect(ctxt, t_dst_value, t_src_value, true, &t_recursive_result);
702+
703+
if (ctxt . HasError())
704+
return;
705+
706+
if (!MCArrayStoreValue(*t_result, ctxt . GetCaseSensitive(), t_key, *t_recursive_result))
707+
return;
708+
}
653709
}
710+
711+
r_result = MCValueRetain(*t_result);
654712
}
655713

656-
void MCArraysExecUnion(MCExecContext& ctxt, MCArrayRef p_dst_array, MCArrayRef p_src_array)
714+
void MCArraysExecUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result)
657715
{
658-
MCArraysDoUnion(ctxt, p_dst_array, p_src_array, false);
716+
MCArraysDoUnion(ctxt, p_dst, p_src, false, r_result);
659717
}
660718

661-
void MCArraysExecIntersect(MCExecContext& ctxt, MCArrayRef p_dst_array, MCArrayRef p_src_array)
719+
void MCArraysExecIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result)
662720
{
663-
MCArraysDoIntersect(ctxt, p_dst_array, p_src_array, false);
721+
MCArraysDoIntersect(ctxt, p_dst, p_src, false, r_result);
664722
}
665723

666-
void MCArraysExecUnionRecursive(MCExecContext& ctxt, MCArrayRef p_dst_array, MCArrayRef p_src_array)
724+
void MCArraysExecUnionRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result)
667725
{
668-
MCArraysDoUnion(ctxt, p_dst_array, p_src_array, true);
726+
MCArraysDoUnion(ctxt, p_dst, p_src, true, r_result);
669727
}
670728

671-
void MCArraysExecIntersectRecursive(MCExecContext& ctxt, MCArrayRef p_dst_array, MCArrayRef p_src_array)
729+
void MCArraysExecIntersectRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result)
672730
{
673-
MCArraysDoIntersect(ctxt, p_dst_array, p_src_array, true);
731+
MCArraysDoIntersect(ctxt, p_dst, p_src, true, r_result);
674732
}
675733

676734
////////////////////////////////////////////////////////////////////////////////

engine/src/exec.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,8 @@ extern MCExecMethodInfo *kMCArraysExecSplitByColumnMethodInfo;
18651865
extern MCExecMethodInfo *kMCArraysExecSplitAsSetMethodInfo;
18661866
extern MCExecMethodInfo *kMCArraysExecUnionMethodInfo;
18671867
extern MCExecMethodInfo *kMCArraysExecIntersectMethodInfo;
1868+
extern MCExecMethodInfo *kMCArraysExecUnionRecursiveMethodInfo;
1869+
extern MCExecMethodInfo *kMCArraysExecIntersectRecursiveMethodInfo;
18681870
extern MCExecMethodInfo *kMCArraysEvalArrayEncodeMethodInfo;
18691871
extern MCExecMethodInfo *kMCArraysEvalArrayDecodeMethodInfo;
18701872
extern MCExecMethodInfo *kMCArraysEvalMatrixMultiplyMethodInfo;
@@ -1885,10 +1887,10 @@ void MCArraysExecCombineAsSet(MCExecContext& ctxt, MCArrayRef p_array, MCStringR
18851887
void MCArraysExecSplit(MCExecContext& ctxt, MCStringRef p_string, MCStringRef p_element_delimiter, MCStringRef p_key_delimiter, MCArrayRef& r_array);
18861888
void MCArraysExecSplitByColumn(MCExecContext& ctxt, MCStringRef p_string, MCArrayRef& r_array);
18871889
void MCArraysExecSplitAsSet(MCExecContext& ctxt, MCStringRef p_string, MCStringRef p_element_delimiter, MCArrayRef& r_array);
1888-
void MCArraysExecUnion(MCExecContext& ctxt, MCArrayRef x_dst_array, MCArrayRef p_src_array);
1889-
void MCArraysExecIntersect(MCExecContext& ctxt, MCArrayRef x_dst_array, MCArrayRef p_src_array);
1890-
void MCArraysExecUnionRecursive(MCExecContext& ctxt, MCArrayRef x_dst_array, MCArrayRef p_src_array);
1891-
void MCArraysExecIntersectRecursive(MCExecContext& ctxt, MCArrayRef x_dst_array, MCArrayRef p_src_array);
1890+
void MCArraysExecUnion(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result);
1891+
void MCArraysExecIntersect(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result);
1892+
void MCArraysExecUnionRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result);
1893+
void MCArraysExecIntersectRecursive(MCExecContext& ctxt, MCValueRef p_dst, MCValueRef p_src, MCValueRef& r_result);
18921894
void MCArraysEvalArrayEncode(MCExecContext& ctxt, MCArrayRef p_array, MCStringRef version, MCDataRef& r_encoding);
18931895
void MCArraysEvalArrayDecode(MCExecContext& ctxt, MCDataRef p_encoding, MCArrayRef& r_array);
18941896
void MCArraysEvalMatrixMultiply(MCExecContext& ctxt, MCArrayRef p_left, MCArrayRef p_right, MCArrayRef& r_result);

0 commit comments

Comments
 (0)