Skip to content

Commit 1edabd0

Browse files
authored
Merge pull request livecode#5328 from peter-b/bugfix-14861
[Bug 14861] LCB: Add "reverse _" syntax for sequence types (String, List, Data)
2 parents 5f7d7a8 + de86d30 commit 1edabd0

16 files changed

Lines changed: 366 additions & 10 deletions

File tree

docs/lcb/notes/14861.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# LiveCode Builder Standard Library
2+
3+
## Sequence operations
4+
5+
* New syntax has been added for reversing the contents of sequence
6+
types (`List`, `String` and `Data`). The `reverse <Value>`
7+
statement reverses the order of the sequence.
8+
9+
# [14861] Add "reverse _" syntax for sequence types

libfoundation/include/foundation-auto.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ template<typename T> class MCAutoValueRefBase
9696
{
9797
return m_value;
9898
}
99+
100+
inline T operator -> (void) const
101+
{
102+
MCAssert(m_value != nullptr);
103+
return m_value;
104+
}
99105

100106
bool IsSet() const
101107
{

libfoundation/include/foundation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,11 @@ MC_DLLEXPORT bool MCStringMutableCopySubstringAndRelease(MCStringRef string, MCR
22112211

22122212
/////////
22132213

2214+
// Copy a string, reversing its contents
2215+
MC_DLLEXPORT bool MCStringCopyReversed(MCStringRef string, MCStringRef& r_reversed);
2216+
2217+
/////////
2218+
22142219
// Returns true if the string is mutable
22152220
MC_DLLEXPORT bool MCStringIsMutable(const MCStringRef string);
22162221

@@ -2718,6 +2723,8 @@ MC_DLLEXPORT bool MCDataRemove(MCDataRef r_data, MCRange p_range);
27182723
MC_DLLEXPORT bool MCDataReplace(MCDataRef r_data, MCRange p_range, MCDataRef p_new_data);
27192724
MC_DLLEXPORT bool MCDataReplaceBytes(MCDataRef r_data, MCRange p_range, const byte_t *p_new_data, uindex_t p_byte_count);
27202725

2726+
MC_DLLEXPORT bool MCDataReverse(MCDataRef);
2727+
27212728
MC_DLLEXPORT bool MCDataPad(MCDataRef data, byte_t byte, uindex_t count);
27222729

27232730
MC_DLLEXPORT bool MCDataContains(MCDataRef p_data, MCDataRef p_needle);
@@ -3297,6 +3304,8 @@ MC_DLLEXPORT bool MCProperListSort(MCProperListRef list, bool p_reverse, MCPrope
32973304
typedef compare_t (*MCProperListCompareElementCallback)(void *context, const MCValueRef left, const MCValueRef right);
32983305
MC_DLLEXPORT bool MCProperListStableSort(MCProperListRef list, bool p_reverse, MCProperListCompareElementCallback p_callback, void *context);
32993306

3307+
MC_DLLEXPORT bool MCProperListReverse(MCProperListRef list);
3308+
33003309
// Fetch the first element of the list. The returned value is not retained.
33013310
MC_DLLEXPORT MCValueRef MCProperListFetchHead(MCProperListRef list);
33023311
MC_DLLEXPORT // Fetch the last element of the list. The returned value is not retained.

libfoundation/src/foundation-data.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,20 @@ bool MCDataPad(MCDataRef self, byte_t p_byte, uindex_t p_count)
693693
return true;
694694
}
695695

696+
MC_DLLEXPORT_DEF
697+
bool MCDataReverse(MCDataRef self)
698+
{
699+
MCAssert(MCDataIsMutable(self));
700+
701+
// Ensure the data ref is not indirect
702+
if (__MCDataIsIndirect(self))
703+
if (!__MCDataResolveIndirect(self))
704+
return false;
705+
706+
MCInplaceReverse(self->bytes, self->byte_count);
707+
return true;
708+
}
709+
696710
MC_DLLEXPORT_DEF
697711
compare_t MCDataCompareTo(MCDataRef p_left, MCDataRef p_right)
698712
{

libfoundation/src/foundation-private.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
1919

2020

2121
#include <stdio.h>
22-
22+
#include <utility>
2323

2424
////////////////////////////////////////////////////////////////////////////////
2525

@@ -667,6 +667,27 @@ __MCAssertResolvedTypeInfo(MCTypeInfoRef x, bool (*p)(MCTypeInfoRef))
667667
#define __MCAssertIsErrorTypeInfo(x) __MCAssertResolvedTypeInfo(x, MCTypeInfoIsError)
668668
#define __MCAssertIsForeignTypeInfo(x) __MCAssertResolvedTypeInfo(x, MCTypeInfoIsForeign)
669669

670+
////////////////////////////////////////////////////////////////////////////////
671+
// ALGORITHM TEMPLATES
672+
//
673+
674+
/* Efficiently reverse the order of elements in an array, in-place.
675+
* The algorithm works from the middle of the array outwards, swapping
676+
* the elements at each end. The ElementType must be swappable,
677+
* either by being trivially copyable or by implementing a
678+
* std::swap()-compatible swap operation that can be found by ADL. */
679+
/* TODO[C++11] Obsolete this macro by using std::reverse from
680+
* <algorithm>. Would require MCSpan to support STL iteration. */
681+
template <typename ElementType, typename IndexType>
682+
inline void MCInplaceReverse(ElementType* x_elements, IndexType p_num_elements)
683+
{
684+
using std::swap;
685+
MCAssert(x_elements != nullptr || p_num_elements == 0);
686+
for (auto t_count = p_num_elements/2; t_count > 0; --t_count)
687+
swap(x_elements[t_count - 1],
688+
x_elements[p_num_elements - t_count]);
689+
}
690+
670691
////////////////////////////////////////////////////////////////////////////////
671692

672693
#endif

libfoundation/src/foundation-proper-list.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,20 @@ bool MCProperListIsHomogeneous(MCProperListRef self, MCValueTypeCode& r_type)
955955
return false;
956956
}
957957

958+
MC_DLLEXPORT_DEF
959+
bool MCProperListReverse(MCProperListRef self)
960+
{
961+
MCAssert(MCProperListIsMutable(self));
962+
963+
// Ensure the list ref is not indirect
964+
if (__MCProperListIsIndirect(self))
965+
if (!__MCProperListResolveIndirect(self))
966+
return false;
967+
968+
MCInplaceReverse(self->list, self->length);
969+
return true;
970+
}
971+
958972
////////////////////////////////////////////////////////////////////////////////
959973

960974
void __MCProperListDestroy(__MCProperList *self)

libfoundation/src/foundation-string.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,79 @@ bool MCStringMutableCopySubstringAndRelease(MCStringRef self, MCRange p_range, M
14751475

14761476
////////////////////////////////////////////////////////////////////////////////
14771477

1478+
MC_DLLEXPORT_DEF
1479+
bool MCStringCopyReversed(MCStringRef self, MCStringRef& r_new_string)
1480+
{
1481+
__MCAssertIsString(self);
1482+
1483+
if (MCStringGetLength(self) < 2)
1484+
return MCStringCopy(self, r_new_string);
1485+
1486+
/* Make a deep copy of the input string. */
1487+
/* TODO[2017-03-29] This could be optimised by _not_ copying the
1488+
* actual string buffer contents during the deep copy, but
1489+
* reversing directly from the input string's internal buffer to
1490+
* the result string's buffer. */
1491+
MCAutoStringRef t_result;
1492+
if (!MCStringMutableCopy(self, &t_result))
1493+
return false;
1494+
if (__MCStringIsIndirect(*t_result))
1495+
if (!__MCStringResolveIndirect(*t_result))
1496+
return false;
1497+
1498+
if (__MCStringIsNative(*t_result))
1499+
{
1500+
/* Native strings have one char_t per grapheme, so they can be
1501+
* reversed in-place. */
1502+
MCInplaceReverse(t_result->native_chars, t_result->char_count);
1503+
}
1504+
else if (__MCStringIsTrivial(*t_result))
1505+
{
1506+
/* Trivial strings have one unichar_t per grapheme, so they
1507+
* can be reversed in-place. */
1508+
MCInplaceReverse(t_result->chars, t_result->char_count);
1509+
}
1510+
else
1511+
{
1512+
/* These strings have some potentially-unbounded number of
1513+
* unichar_t codeunits items per grapheme. In this case, we
1514+
* reverse by iterating over the contents of the original
1515+
* string, copying the graphemes into the new string. */
1516+
MCStringRef t_original = self;
1517+
if (__MCStringIsIndirect(t_original))
1518+
{
1519+
t_original = t_original->string;
1520+
}
1521+
1522+
/* Start of the next grapheme to copy, in the input string */
1523+
uindex_t t_from = 0;
1524+
uindex_t t_length = t_original->char_count;
1525+
1526+
while (t_from < t_length)
1527+
{
1528+
/* Find the end of the current grapheme */
1529+
uindex_t t_grapheme_end =
1530+
MCStringGraphemeBreakIteratorAdvance(t_original, t_from);
1531+
1532+
if (t_grapheme_end == kMCLocaleBreakIteratorDone)
1533+
t_grapheme_end = t_length;
1534+
1535+
MCAssert(t_grapheme_end <= t_length);
1536+
1537+
MCMemoryCopy(t_result->chars + t_length - t_grapheme_end,
1538+
t_original->chars + t_from,
1539+
(t_grapheme_end - t_from) * sizeof(*t_result->chars));
1540+
1541+
t_from = t_grapheme_end;
1542+
}
1543+
}
1544+
1545+
r_new_string = t_result.Take();
1546+
return true;
1547+
}
1548+
1549+
////////////////////////////////////////////////////////////////////////////////
1550+
14781551
MC_DLLEXPORT_DEF
14791552
bool MCStringIsMutable(const MCStringRef self)
14801553
{

libscript/src/byte.lcb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,37 @@ begin
432432
MCDataExecRandomBytes(Count, output)
433433
end syntax
434434

435+
--
436+
437+
public foreign handler MCDataExecReverseBytesOf(inout Target as Data) \
438+
returns nothing binds to "<builtin>"
439+
440+
/**
441+
Summary: Reverse binary data
442+
Target: A binary data string
443+
444+
Example:
445+
variable tForward
446+
put 5 random bytes into tForward
447+
448+
variable tReversed
449+
put tForward into tReversed
450+
reverse tReversed
451+
452+
expect that the first byte of tForward is the last byte of tReversed
453+
expect that the last byte of tForward is the first byte of tReversed
454+
455+
Description:
456+
Reverses the order of bytes in the <Target>.
457+
458+
Tags: Binary
459+
*/
460+
syntax ReverseBytesOf is statement
461+
"reverse" <Target: Expression>
462+
begin
463+
MCDataExecReverseBytesOf(Target)
464+
end syntax
465+
435466
----------------------------------------------------------------
436467
-- Conversion between bytes and numbers
437468
----------------------------------------------------------------

libscript/src/char.lcb

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -535,17 +535,15 @@ Summary: Repeat over the chars of a string
535535
Iterand: A string container.
536536

537537
Example:
538-
variable tString as String
539-
put "stressed" into tString
540-
541-
variable tReversed as String
542538
variable tChar as String
543-
put "" into tReversed
544-
repeat for each char tChar in tString
545-
put tChar before tReversed
539+
variable tSpaceCount as Number
540+
repeat for each char tChar in \
541+
"the quick brown fox jumps over the lazy dog"
542+
if tChar is " " then
543+
add 1 to tSpaceCount
544+
end if
546545
end repeat
547-
548-
// tReversed is "desserts"
546+
expect that tSpaceCount is 8
549547

550548
Description:
551549
Use repeat for each to perform an operation on each char of a string. On each iteration, the <Iterand> will contain the next char of the string being iterated over.
@@ -559,6 +557,32 @@ begin
559557
MCCharRepeatForEachChar(iterator, Iterand, container)
560558
end syntax
561559

560+
--
561+
562+
public foreign handler MCStringExecReverseCharsOf(inout Target as String) \
563+
returns nothing binds to "<builtin>"
564+
565+
/**
566+
Summary: Reverse a string
567+
Target: A string
568+
569+
Example:
570+
variable tString
571+
put "abcdef" into tString
572+
reverse tString
573+
expect that tString is "fedcba"
574+
575+
Description:
576+
Reverses the order of characters in the <Target>.
577+
578+
Tags: Strings
579+
*/
580+
syntax ReverseCharsOf is statement
581+
"reverse" <Target: Expression>
582+
begin
583+
MCStringExecReverseCharsOf(Target)
584+
end syntax
585+
562586
----------------------------------------------------------------
563587

564588
/**

libscript/src/list.lcb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,4 +1098,30 @@ begin
10981098
MCListEvalConcatenate(Left, Right, output)
10991099
end syntax
11001100

1101+
----------------------------------------------------------------
1102+
1103+
public foreign handler MCListExecReverseElementsOf(inout Target as List) \
1104+
returns nothing binds to "<builtin>"
1105+
1106+
/**
1107+
Summary: Reverse a list
1108+
Target: A list
1109+
1110+
Example:
1111+
variable tList
1112+
put [1, 2, 3] into tList
1113+
reverse tList
1114+
expect that tList is [3, 2, 1]
1115+
1116+
Description:
1117+
Reverses the order of elements in the <Target>.
1118+
1119+
Tags: Lists
1120+
*/
1121+
syntax ReverseElementsOfList is statement
1122+
"reverse" <Target: Expression>
1123+
begin
1124+
MCListExecReverseElementsOf(Target)
1125+
end syntax
1126+
11011127
end module

0 commit comments

Comments
 (0)