Skip to content

Commit fa5d08b

Browse files
authored
refactor: use String.Slice in String.take and variants (#11180)
This PR redefines `String.take` and variants to operate on `String.Slice`. While previously functions returning a substring of the input sometimes returned `String` and sometimes returned `Substring.Raw`, they now uniformly return `String.Slice`. This is a BREAKING change, because many functions now have a different return type. So for example, if `s` is a string and `f` is a function accepting a string, `f (s.drop 1)` will no longer compile because `s.drop 1` is a `String.Slice`. To fix this, insert a call to `copy` to restore the old behavior: `f (s.drop 1).copy`. Of course, in many cases, there will be more efficient options. For example, don't write `f <| s.drop 1 |>.copy |>.dropEnd 1 |>.copy`, write `f <| s.drop 1 |>.dropEnd 1 |>.copy` instead. Also, instead of `(s.drop 1).copy = "Hello"`, write `s.drop 1 == "Hello".toSlice` instead.
1 parent 03eb2f7 commit fa5d08b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+481
-341
lines changed

src/Init/Data/String/Basic.lean

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,23 +2785,6 @@ where
27852785
def substrEq (s1 : String) (pos1 : String.Pos.Raw) (s2 : String) (pos2 : String.Pos.Raw) (sz : Nat) : Bool :=
27862786
Pos.Raw.substrEq s1 pos1 s2 pos2 sz
27872787

2788-
/--
2789-
Checks whether the first string (`p`) is a prefix of the second (`s`).
2790-
2791-
`String.startsWith` is a version that takes the potential prefix after the string.
2792-
2793-
Examples:
2794-
* `"red".isPrefixOf "red green blue" = true`
2795-
* `"green".isPrefixOf "red green blue" = false`
2796-
* `"".isPrefixOf "red green blue" = true`
2797-
-/
2798-
def isPrefixOf (p : String) (s : String) : Bool :=
2799-
Pos.Raw.substrEq p 0 s 0 p.rawEndPos.byteIdx
2800-
2801-
@[export lean_string_isprefixof]
2802-
def Internal.isPrefixOfImpl (p : String) (s : String) : Bool :=
2803-
String.isPrefixOf p s
2804-
28052788
/--
28062789
Returns the position of the beginning of the line that contains the position `pos`.
28072790

src/Init/Data/String/Slice.lean

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public import Init.Data.String.Pattern
1010
public import Init.Data.Ord.Basic
1111
public import Init.Data.Iterators.Combinators.FilterMap
1212
public import Init.Data.String.ToSlice
13+
public import Init.Data.String.Termination
1314

1415
set_option doc.verso true
1516

@@ -403,19 +404,19 @@ Examples:
403404
* {lean}`"red green blue".toSlice.dropWhile (fun (_ : Char) => true) == "".toSlice`
404405
-/
405406
@[inline]
406-
partial def dropWhile [ForwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
407-
go s
407+
def dropWhile [ForwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
408+
go s.startPos
408409
where
409410
@[specialize pat]
410-
go (s : Slice) : Slice :=
411-
if let some nextS := dropPrefix? s pat then
412-
-- TODO: need lawful patterns to show this terminates
413-
if s.startInclusive.offset < nextS.startInclusive.offset then
414-
go nextS
411+
go (curr : s.Pos) : Slice :=
412+
if let some nextCurr := ForwardPattern.dropPrefix? (s.replaceStart curr) pat then
413+
if curr < Pos.ofReplaceStart nextCurr then
414+
go (Pos.ofReplaceStart nextCurr)
415415
else
416-
s
416+
s.replaceStart curr
417417
else
418-
s
418+
s.replaceStart curr
419+
termination_by curr
419420

420421
/--
421422
Removes leading whitespace from a slice by moving its start position to the first non-whitespace
@@ -463,19 +464,19 @@ Examples:
463464
* {lean}`"red green blue".toSlice.takeWhile (fun (_ : Char) => true) == "red green blue".toSlice`
464465
-/
465466
@[inline]
466-
partial def takeWhile [ForwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
467+
def takeWhile [ForwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
467468
go s.startPos
468469
where
469470
@[specialize pat]
470471
go (curr : s.Pos) : Slice :=
471472
if let some nextCurr := ForwardPattern.dropPrefix? (s.replaceStart curr) pat then
472-
if (s.replaceStart curr).startPos < nextCurr then
473-
-- TODO: need lawful patterns to show this terminates
473+
if curr < Pos.ofReplaceStart nextCurr then
474474
go (Pos.ofReplaceStart nextCurr)
475475
else
476476
s.replaceEnd curr
477477
else
478478
s.replaceEnd curr
479+
termination_by curr
479480

480481
/--
481482
Finds the position of the first match of the pattern {name}`pat` in a slice {name}`s`. If there
@@ -712,23 +713,23 @@ Examples:
712713
* {lean}`"red green blue".toSlice.dropEndWhile (fun (_ : Char) => true) == "".toSlice`
713714
-/
714715
@[inline]
715-
partial def dropEndWhile [BackwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
716-
go s
716+
def dropEndWhile [BackwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
717+
go s.endPos
717718
where
718719
@[specialize pat]
719-
go (s : Slice) : Slice :=
720-
if let some nextS := dropSuffix? s pat then
721-
-- TODO: need lawful patterns to show this terminates
722-
if nextS.endExclusive.offset < s.endExclusive.offset then
723-
go nextS
720+
go (curr : s.Pos) : Slice :=
721+
if let some nextCurr := BackwardPattern.dropSuffix? (s.replaceEnd curr) pat then
722+
if Pos.ofReplaceEnd nextCurr < curr then
723+
go (Pos.ofReplaceEnd nextCurr)
724724
else
725-
s
725+
s.replaceEnd curr
726726
else
727-
s
727+
s.replaceEnd curr
728+
termination_by curr.down
728729

729730
/--
730-
Removes trailing whitespace from a slice by moving its start position to the first non-whitespace
731-
character, or to its end position if there is no non-whitespace character.
731+
Removes trailing whitespace from a slice by moving its end position to the last non-whitespace
732+
character, or to its start position if there is no non-whitespace character.
732733
733734
“Whitespace” is defined as characters for which {name}`Char.isWhitespace` returns {name}`true`.
734735
@@ -771,19 +772,19 @@ Examples:
771772
* {lean}`"red green blue".toSlice.takeEndWhile (fun (_ : Char) => true) == "red green blue".toSlice`
772773
-/
773774
@[inline]
774-
partial def takeEndWhile [BackwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
775+
def takeEndWhile [BackwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
775776
go s.endPos
776777
where
777778
@[specialize pat]
778779
go (curr : s.Pos) : Slice :=
779780
if let some nextCurr := BackwardPattern.dropSuffix? (s.replaceEnd curr) pat then
780-
if nextCurr < (s.replaceEnd curr).endPos then
781-
-- TODO: need lawful patterns to show this terminates
781+
if Pos.ofReplaceEnd nextCurr < curr then
782782
go (Pos.ofReplaceEnd nextCurr)
783783
else
784784
s.replaceStart curr
785785
else
786786
s.replaceStart curr
787+
termination_by curr.down
787788

788789
/--
789790
Finds the position of the first match of the pattern {name}`pat` in a slice {name}`true`, starting
@@ -1338,4 +1339,36 @@ Examples:
13381339
def back (s : Slice) : Char :=
13391340
s.back?.getD default
13401341

1342+
/--
1343+
Appends the slices in a list of slices, placing the separator {name}`s` between each pair.
1344+
1345+
Examples:
1346+
* {lean}`", ".toSlice.intercalate ["red".toSlice, "green".toSlice, "blue".toSlice] = "red, green, blue"`
1347+
* {lean}`" and ".toSlice.intercalate ["tea".toSlice, "coffee".toSlice] = "tea and coffee"`
1348+
* {lean}`" | ".toSlice.intercalate ["M".toSlice, "".toSlice, "N".toSlice] = "M | | N"`
1349+
-/
1350+
def intercalate (s : Slice) : List Slice → String
1351+
| [] => ""
1352+
| a :: as => go a.copy s as
1353+
where go (acc : String) (s : Slice) : List Slice → String
1354+
| a :: as => go (acc ++ s ++ a) s as
1355+
| [] => acc
1356+
1357+
@[inherit_doc Slice.copy]
1358+
def toString (s : Slice) : String :=
1359+
s.copy
1360+
1361+
instance : ToString String.Slice where
1362+
toString := toString
1363+
1364+
/--
1365+
Converts a string to the Lean compiler's representation of names. The resulting name is
1366+
hierarchical, and the string is split at the dots ({lean}`'.'`).
1367+
1368+
{lean}`"a.b".toSlice.toName` is the name {lit}`a.b`, not {lit}`«a.b»`. For the latter, use
1369+
`Name.mkSimple`.
1370+
-/
1371+
def toName (s : Slice) : Lean.Name :=
1372+
s.toString.toName
1373+
13411374
end String.Slice

0 commit comments

Comments
 (0)