Skip to content

Commit 0f53203

Browse files
committed
SequencesExt!ReplaceAllSubSeqs
[Feature]
1 parent 37dfddc commit 0f53203

File tree

3 files changed

+97
-0
lines changed

3 files changed

+97
-0
lines changed

modules/SequencesExt.tla

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,4 +318,43 @@ ReplaceFirstSubSeq(r, s, t) ==
318318
THEN ReplaceSubSeqAt(IndexFirstSubSeq(s, t), r, s, t)
319319
ELSE t
320320

321+
ReplaceAllSubSeqs(r, s, t) ==
322+
(**************************************************************************)
323+
(* The sequence t with all subsequences s replaced by the sequence r *)
324+
(* Overlapping replacements are disambiguated by choosing the occurrence *)
325+
(* closer to the beginning of the sequence. *)
326+
(* *)
327+
(* Examples: *)
328+
(* *)
329+
(* ReplaceAllSubSeqs(<<>>,<<>>,<<>>) = <<>> *)
330+
(* ReplaceAllSubSeqs(<<4>>,<<>>,<<>>) = <<4>> *)
331+
(* ReplaceAllSubSeqs(<<2>>,<<3>>,<<1,3>>) = <<1,2>> *)
332+
(* ReplaceAllSubSeqs(<<2,2>>,<<1,1>>,<<1,1,1>>) = <<2,2,1>> *)
333+
(**************************************************************************)
334+
CASE s = t -> r
335+
[] r = s -> t \* TLC optimization
336+
[] s # t /\ Len(s) = 0 ->
337+
LET z == Zip([i \in 1..Len(t) |-> r], [i \in 1..Len(t) |-> <<t[i]>>])
338+
IN FlattenSeq(FlattenSeq(z))
339+
[] s # t /\ Len(s) > 0 /\ s \in SubSeqs(t) ->
340+
\* Not defined recursively to avoid infinite loops.
341+
LET match(f) == { i \in 1..Len(f) : s = f[i] }
342+
comp(p, q) == \A i \in 1..Len(p) : p[i] <= q[i]
343+
\* TODO: Replace with Seq(Seq(Range(t))) once *total* Java module
344+
\* override in place. The current override handles only the
345+
\* case where the parameters are strings (hence Range("abc")
346+
\* not a problem with TLC).
347+
R == BoundedSeq(BoundedSeq(Range(t), Len(t)), Len(t))
348+
\* A) Matches the input t.
349+
S == { f \in R : FlattenSeq(f) = t }
350+
\* B) Has the max number of matches...
351+
T == { f \in S : \A g \in S :
352+
Cardinality(match(g)) <= Cardinality(match(f)) }
353+
\* C) ...of min (leftmost) matches.
354+
u == CHOOSE f \in T :
355+
\A g \in T : comp(
356+
SetToSortSeq(match(f), <), SetToSortSeq(match(g), <))
357+
IN FlattenSeq([i \in 1..Len(u) |-> IF s = u[i] THEN r ELSE u[i]])
358+
[] OTHER -> t
359+
321360
=============================================================================

modules/tlc2/overrides/SequencesExt.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,22 @@ public static Value replaceFirstSubSeq(final Tool tool, final ExprOrOpArgNode[]
257257

258258
return null; // Non-string functions handled by pure TLA+ definition of operator.
259259
}
260+
261+
@Evaluation(definition = "ReplaceAllSubSeqs", module = "SequencesExt", warn = false, silent = true)
262+
public static Value replaceAllSubSeq(final Tool tool, final ExprOrOpArgNode[] args, final Context c,
263+
final TLCState s0, final TLCState s1, final int control, final CostModel cm) {
264+
final IValue r = tool.eval(args[0], c, s0, s1, control, cm);
265+
final IValue s = tool.eval(args[1], c, s0, s1, control, cm);
266+
final IValue t = tool.eval(args[2], c, s0, s1, control, cm);
267+
268+
if (r instanceof StringValue && s instanceof StringValue && t instanceof StringValue) {
269+
final String st = ((StringValue) t).toUnquotedString();
270+
final String ss = ((StringValue) s).toUnquotedString();
271+
final String sr = ((StringValue) r).toUnquotedString();
272+
273+
return new StringValue(st.replaceAll(Pattern.quote(ss), sr));
274+
}
275+
276+
return null; // Non-string functions handled by pure TLA+ definition of operator.
277+
}
260278
}

tests/SequencesExtTests.tla

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,44 @@ ASSUME ReplaceFirstSubSeq("ddd", "abab", "abab") = "ddd"
227227
ASSUME ReplaceFirstSubSeq("\\\\", "\\", "Properly escape the \\quotes") = "Properly escape the \\\\quotes"
228228
ASSUME ReplaceFirstSubSeq("replaces", "%pattern%", "This %pattern% the pattern") = "This replaces the pattern"
229229

230+
-----------------------------------------------------------------------------
231+
232+
ASSUME AssertEq(ReplaceAllSubSeqs(<<4>>,<<1>>,<<>>), <<>>)
233+
ASSUME AssertEq(ReplaceAllSubSeqs(<<4>>,<<2>>,<<1>>), <<1>>)
234+
ASSUME AssertEq(ReplaceAllSubSeqs(<<4>>,<<2>>,<<1,1>>), <<1,1>>)
235+
236+
ASSUME AssertEq(ReplaceAllSubSeqs(<<>>,<<>>,<<>>), <<>>)
237+
ASSUME AssertEq(ReplaceAllSubSeqs(<<4>>,<<>>,<<>>), <<4>>)
238+
ASSUME AssertEq(ReplaceAllSubSeqs(<<4,4>>,<<>>,<<>>), <<4,4>>)
239+
240+
ASSUME AssertEq(ReplaceAllSubSeqs(<<1>>, <<1>>, <<1>>), <<1>>)
241+
ASSUME AssertEq(ReplaceAllSubSeqs(<<2>>, <<3>>, <<1,3>>), <<1,2>>)
242+
ASSUME AssertEq(ReplaceAllSubSeqs(<<1,1>>, <<1>>, <<1,1>>), <<1,1,1,1>>)
243+
ASSUME AssertEq(ReplaceAllSubSeqs(<<1,1>>, <<1,1>>, <<1,1>>), <<1,1>>)
244+
ASSUME AssertEq(ReplaceAllSubSeqs(<<2,2>>, <<1,1>>, <<1,1,1>>), <<2,2,1>>)
245+
ASSUME AssertEq(ReplaceAllSubSeqs(<<>>, <<1,1>>, <<1,1,1>>), <<1>>)
246+
ASSUME AssertEq(ReplaceAllSubSeqs(<<2,2>>, <<1,1,1>>, <<1,1,1>>), <<2,2>>)
247+
ASSUME AssertEq(ReplaceAllSubSeqs(<<>>, <<1,1,1>>, <<1,1,1>>), <<>>)
248+
ASSUME AssertEq(ReplaceAllSubSeqs(<<2,2>>, <<1,1,1>>, <<1,1,1,1>>), <<2,2,1>>)
249+
250+
ASSUME AssertEq(ReplaceAllSubSeqs(<<1,1>>, <<>>, <<2,2>>), <<1,1,2,1,1,2>>)
251+
252+
ASSUME ReplaceAllSubSeqs("dd", "d", "dd") = "dddd"
253+
254+
ASSUME ReplaceAllSubSeqs("ddd", "a", "") = ""
255+
ASSUME ReplaceAllSubSeqs("ddd", "", "") = "ddd"
256+
ASSUME ReplaceAllSubSeqs("ddd", "ab", "abab") = "dddddd"
257+
ASSUME ReplaceAllSubSeqs("ddd", "abab", "abab") = "ddd"
258+
259+
ASSUME ReplaceAllSubSeqs("ddd", "aa", "aaa") = "ddda"
260+
ASSUME ReplaceAllSubSeqs("a", "a", "a") = "a"
261+
ASSUME ReplaceAllSubSeqs("aa", "aa", "aa") = "aa"
262+
ASSUME ReplaceAllSubSeqs("aa", "aa", "aaaa") = "aaaa"
263+
ASSUME ReplaceAllSubSeqs("aa", "aa", "bbbb") = "bbbb"
264+
265+
ASSUME ReplaceAllSubSeqs("ddd", "", "abab") = "dddadddbdddadddbddd"
266+
267+
ASSUME AssertEq(ReplaceAllSubSeqs("replaces", "%pattern%", "This %pattern% the pattern %pattern% multipe times"), "This replaces the pattern replaces multipe times")
268+
ASSUME AssertEq(ReplaceAllSubSeqs("\\\\", "\\", "Properly escape the \\quotes"), "Properly escape the \\\\quotes")
269+
230270
=============================================================================

0 commit comments

Comments
 (0)