@@ -8,7 +8,9 @@ module StandardLibrary.String {
88 import opened UInt
99 import opened Sequence
1010 import opened MemoryMath
11- export provides Int2String, Base10Int2String, HasSubString, Wrappers, UInt, HasSubStringPos, SearchAndReplace, SearchAndReplacePos, SearchAndReplaceAll
11+ export provides Int2String, Base10Int2String, HasSubString, Wrappers, UInt,
12+ HasSubStringPos, SearchAndReplace, SearchAndReplacePos, SearchAndReplaceAll,
13+ SearchAndReplacePosWhole, SearchAndReplaceAllWhole, AlphaNumeric, AlphaNumericUnder
1214
1315 const Base10: seq < char > := ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
1416
@@ -61,18 +63,8 @@ module StandardLibrary.String {
6163 returns (o : seq < T> )
6264 requires 0 < |old_str|
6365 {
64- var old_pos := HasSubString (source, old_str);
65- if old_pos. None? {
66- return source;
67- } else {
68- SequenceIsSafeBecauseItIsInMemory (source);
69- SequenceIsSafeBecauseItIsInMemory (old_str);
70- ValueIsSafeBecauseItIsInMemory (old_pos.value);
71- var pos : uint64 := old_pos. value as uint64;
72- var source_len : uint64 := |source| as uint64;
73- var old_str_len : uint64 := |old_str| as uint64;
74- return source[.. pos] + new_str + source[pos+ old_str_len.. ];
75- }
66+ var x := SearchAndReplacePos (source, old_str, new_str, 0);
67+ return x. 0;
7668 }
7769
7870 // Replace first occurrence of old_str after pos in source with new_str and return the result.
@@ -101,6 +93,81 @@ module StandardLibrary.String {
10193 }
10294 }
10395
96+ predicate method BadStart< T (==)> (source : seq < T> , pos : uint64, chars : seq < T> )
97+ requires pos as nat <= |source|
98+ {
99+ if pos == 0 then
100+ false
101+ else
102+ source[pos- 1] in chars
103+ }
104+
105+ predicate method BadEnd< T (==)> (source : seq < T> , pos : uint64, match_len : uint64, chars : seq < T> ) {
106+ SequenceIsSafeBecauseItIsInMemory (source);
107+ var source_len : uint64 := |source| as uint64;
108+ if Add (pos, match_len) >= source_len then
109+ false
110+ else
111+ source[pos+ match_len] in chars
112+
113+ }
114+ predicate method BadMatch< T (==)> (source : seq < T> , pos : uint64, match_len : uint64, chars : seq < T> )
115+ requires pos as nat <= |source|
116+ {
117+ BadStart (source, pos, chars) || BadEnd (source, pos, match_len, chars)
118+ }
119+
120+ const AlphaNumeric : string := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
121+ const AlphaNumericUnder : string := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
122+
123+ // Replace first occurrence of old_str after pos in source with new_str and return the result.
124+ // If old_str does not exist in source, the source is returned unchanged
125+ // second value in output is None if old_str not found, otherwise it points just after the replaced value
126+ // a match only counts if the character before or after is not in chars
127+ method SearchAndReplacePosWhole< T (==)> (source: seq < T> , old_str: seq < T> , new_str: seq < T> , xpos : uint64, chars : seq < T> )
128+ returns (o : (seq < T> , Option< uint64> ))
129+ requires xpos as nat <= |source|
130+ requires 0 < |old_str|
131+ ensures o. 1. Some? ==> |o. 0| - o. 1. value as nat < |source| - xpos as nat
132+ ensures o. 1. Some? ==> o. 1. value as nat <= |o. 0|
133+ {
134+ SequenceIsSafeBecauseItIsInMemory (source);
135+ SequenceIsSafeBecauseItIsInMemory (old_str);
136+ var old_str_len : uint64 := |old_str| as uint64;
137+ var pos : uint64 := xpos;
138+
139+ while pos < |source| as uint64 {
140+ var old_pos := HasSubStringPos (source, old_str, pos);
141+ if old_pos. None? {
142+ return (source, None);
143+ } else if BadMatch (source, old_pos.value, old_str_len, chars) {
144+ pos := old_pos. value + 1;
145+ } else {
146+ SequenceIsSafeBecauseItIsInMemory (source);
147+ SequenceIsSafeBecauseItIsInMemory (new_str);
148+ var source_len : uint64 := |source| as uint64;
149+ var new_str_len : uint64 := |new_str| as uint64;
150+ o := (source[.. old_pos. value] + new_str + source[old_pos. value+ old_str_len.. ], Some (Add(old_pos.value, new_str_len)));
151+ assert |o. 0| - o. 1. value as nat < |source| - pos as nat ;
152+ return o;
153+ }
154+ }
155+ return (source, None); // not really needed, but Dafny is dumb
156+ }
157+
158+ // Replace first occurrence of old_str after pos in source with new_str and return the result.
159+ // If old_str does not exist in source, the source is returned unchanged
160+ // second value in output is None if old_str not found, otherwise it points just after the replaced value
161+ // a match only counts if the character before or after is not in chars
162+ method SearchAndReplaceWhole< T (==)> (source: seq < T> , old_str: seq < T> , new_str: seq < T> , chars : seq < T> )
163+ returns (o : (seq < T> , Option< uint64> ))
164+ requires 0 < |old_str|
165+ ensures o. 1. Some? ==> |o. 0| - o. 1. value as nat < |source| as nat
166+ ensures o. 1. Some? ==> o. 1. value as nat <= |o. 0|
167+ {
168+ o := SearchAndReplacePosWhole (source, old_str, new_str, 0, chars);
169+ }
170+
104171 // Replace all occurrences of old_str in source with new_str and return the result.
105172 // If old_str does not exist in source, the source is returned unchanged
106173 // safe to use if new_str contains old_str
@@ -125,6 +192,32 @@ module StandardLibrary.String {
125192 }
126193 }
127194
195+ // Replace all occurrences of old_str in source with new_str and return the result.
196+ // If old_str does not exist in source, the source is returned unchanged
197+ // safe to use if new_str contains old_str
198+ // a match only counts if the character before or after is not in chars
199+ method SearchAndReplaceAllWhole< T (==)> (source_in: seq < T> , old_str: seq < T> , new_str: seq < T> , chars : seq < T> )
200+ returns (o : seq < T> )
201+ requires 0 < |old_str|
202+ {
203+ var pos : uint64 := 0;
204+ var source := source_in;
205+ while true
206+ invariant pos as nat <= |source|
207+ decreases |source| - pos as nat
208+ {
209+ var res := SearchAndReplacePosWhole (source, old_str, new_str, pos, chars);
210+ if res. 1. None? {
211+ SequenceIsSafeBecauseItIsInMemory (source);
212+ pos := |source| as uint64; // unneeded, but Dafny is dumb
213+ return res. 0;
214+ }
215+ source := res. 0;
216+ pos := res. 1. value;
217+ }
218+ }
219+
220+
128221 /* Returns the index of a substring or None, if the substring is not in the string */
129222 method HasSubString< T (==)> (haystack: seq < T> , needle: seq < T> )
130223 returns (o: Option< nat > )
0 commit comments