diff --git a/src/Init/Data/String/Basic.lean b/src/Init/Data/String/Basic.lean index 2c52fc86ae..3723c9401a 100644 --- a/src/Init/Data/String/Basic.lean +++ b/src/Init/Data/String/Basic.lean @@ -2429,37 +2429,6 @@ def Pos.Raw.prev : (@& String) → (@& Pos.Raw) → Pos.Raw def prev : (@& String) → (@& Pos.Raw) → Pos.Raw | s, p => Pos.Raw.utf8PrevAux s.toList 0 p -/-- -Returns the first character in `s`. If `s = ""`, returns `(default : Char)`. - -Examples: -* `"abc".front = 'a'` -* `"".front = (default : Char)` --/ -@[inline, expose] def front (s : String) : Char := - Pos.Raw.get s 0 - -@[export lean_string_front] -def Internal.frontImpl (s : String) : Char := - String.front s - -theorem front_eq_get {s : String} : s.front = (0 : Pos.Raw).get s := rfl - -/-- -Returns the last character in `s`. If `s = ""`, returns `(default : Char)`. - -Examples: -* `"abc".back = 'c'` -* `"".back = (default : Char)` --/ -@[inline, expose] def back (s : String) : Char := - (s.rawEndPos.prev s).get s - -theorem back_eq_get_prev_rawEndPos {s : String} : s.back = (s.rawEndPos.prev s).get s := rfl - -@[deprecated back_eq_get_prev_rawEndPos (since := "2025-10-20")] -theorem back_eq_get_prev_endPos {s : String} : s.back = (s.rawEndPos.prev s).get s := rfl - /-- Returns `true` if a specified byte position is greater than or equal to the position which points to the end of a string. Otherwise, returns `false`. diff --git a/src/Init/Data/String/Search.lean b/src/Init/Data/String/Search.lean index 0562461e82..32e3c7150d 100644 --- a/src/Init/Data/String/Search.lean +++ b/src/Init/Data/String/Search.lean @@ -112,9 +112,25 @@ Examples: * {lean}`("baaab".split "aa").toList == ["b".toSlice, "ab".toSlice]` -/ @[inline] -def split (s : String) (pat : ρ) [ToForwardSearcher pat σ] := +def split (s : String) (pat : ρ) [ToForwardSearcher pat σ] := (s.toSlice.split pat : Std.Iter String.Slice) +/-- +Splits a string at each subslice that matches the pattern {name}`pat`. Unlike {name}`split` the +matched subslices are included at the end of each subslice. + +This function is generic over all currently supported patterns. + +Examples: + * {lean}`("coffee tea water".splitInclusive Char.isWhitespace).toList == ["coffee ".toSlice, "tea ".toSlice, "water".toSlice]` + * {lean}`("coffee tea water".splitInclusive ' ').toList == ["coffee ".toSlice, "tea ".toSlice, "water".toSlice]` + * {lean}`("coffee tea water".splitInclusive " tea ").toList == ["coffee tea ".toSlice, "water".toSlice]` + * {lean}`("baaab".splitInclusive "aa").toList == ["baa".toSlice, "ab".toSlice]` +-/ +@[inline] +def splitInclusive (s : String) (pat : ρ) [ToForwardSearcher pat σ] := + (s.toSlice.splitInclusive pat : Std.Iter String.Slice) + @[deprecated String.Slice.foldl (since := "2025-11-20")] def foldlAux {α : Type u} (f : α → Char → α) (s : String) (stopPos : Pos.Raw) (i : Pos.Raw) (a : α) : α := s.slice! (s.pos! i) (s.pos! stopPos) |>.foldl f a @@ -200,6 +216,147 @@ Examples: @[inline] def toNat! (s : String) : Nat := s.toSlice.toNat! +/-- +Returns the first character in {name}`s`. If {name}`s` is empty, returns {name}`none`. + +Examples: +* {lean}`"abc".front? = some 'a'` +* {lean}`"".front? = none` +-/ +@[inline] +def front? (s : String) : Option Char := + s.toSlice.front? + +/-- +Returns the first character in {name}`s`. If {lean}`s = ""`, returns {lean}`(default : Char)`. + +Examples: +* {lean}`"abc".front = 'a'` +* {lean}`"".front = (default : Char)` +-/ +@[inline, expose] def front (s : String) : Char := + s.toSlice.front + +@[export lean_string_front] +def Internal.frontImpl (s : String) : Char := + String.front s + +/-- +Returns the last character in {name}`s`. If {name}`s` is empty, returns {name}`none`. + +Examples: +* {lean}`"abc".back? = some 'c'` +* {lean}`"".back? = none` +-/ +@[inline] +def back? (s : String) : Option Char := + s.toSlice.back? + + +/-- +Returns the last character in {name}`s`. If {lean}`s = ""`, returns {lean}`(default : Char)`. + +Examples: +* {lean}`"abc".back = 'c'` +* {lean}`"".back = (default : Char)` +-/ +@[inline, expose] def back (s : String) : Char := + s.toSlice.back + +theorem Slice.Pos.ofSlice_ne_endValidPos {s : String} {p : s.toSlice.Pos} + (h : p ≠ s.toSlice.endPos) : p.ofSlice ≠ s.endValidPos := by + rwa [ne_eq, ← ValidPos.toSlice_inj, toSlice_ofSlice, ← endPos_toSlice] + +@[inline] +def Internal.toSliceWithProof {s : String} : + { p : s.toSlice.Pos // p ≠ s.toSlice.endPos } → { p : s.ValidPos // p ≠ s.endValidPos } := + fun ⟨p, h⟩ => ⟨p.ofSlice, Slice.Pos.ofSlice_ne_endValidPos h⟩ + +/-- +Creates an iterator over all valid positions within {name}`s`. + +Examples + * {lean}`("abc".positions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['a', 'b', 'c']` + * {lean}`("abc".positions.map (·.val.offset.byteIdx) |>.toList) = [0, 1, 2]` + * {lean}`("ab∀c".positions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['a', 'b', '∀', 'c']` + * {lean}`("ab∀c".positions.map (·.val.offset.byteIdx) |>.toList) = [0, 1, 2, 5]` +-/ +@[inline] +def positions (s : String) := + (s.toSlice.positions.map Internal.toSliceWithProof : Std.Iter { p : s.ValidPos // p ≠ s.endValidPos }) + +/-- +Creates an iterator over all characters (Unicode code points) in {name}`s`. + +Examples: + * {lean}`"abc".chars.toList = ['a', 'b', 'c']` + * {lean}`"ab∀c".chars.toList = ['a', 'b', '∀', 'c']` +-/ +@[inline] +def chars (s : String) := + (s.toSlice.chars : Std.Iter Char) + +/-- +Creates an iterator over all valid positions within {name}`s`, starting from the last valid +position and iterating towards the first one. + +Examples + * {lean}`("abc".revPositions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['c', 'b', 'a']` + * {lean}`("abc".revPositions.map (·.val.offset.byteIdx) |>.toList) = [2, 1, 0]` + * {lean}`("ab∀c".revPositions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['c', '∀', 'b', 'a']` + * {lean}`("ab∀c".toSlice.revPositions.map (·.val.offset.byteIdx) |>.toList) = [5, 2, 1, 0]` +-/ +@[inline] +def revPositions (s : String) := + (s.toSlice.revPositions.map Internal.toSliceWithProof : Std.Iter { p : s.ValidPos // p ≠ s.endValidPos }) + +/-- +Creates an iterator over all characters (Unicode code points) in {name}`s`, starting from the end +of the slice and iterating towards the start. + +Example: + * {lean}`"abc".revChars.toList = ['c', 'b', 'a']` + * {lean}`"ab∀c".revChars.toList = ['c', '∀', 'b', 'a']` +-/ +@[inline] +def revChars (s : String) := + (s.toSlice.revChars : Std.Iter Char) + +/-- +Creates an iterator over all bytes in {name}`s`. + +Examples: + * {lean}`"abc".byteIterator.toList = [97, 98, 99]` + * {lean}`"ab∀c".byteIterator.toList = [97, 98, 226, 136, 128, 99]` +-/ +@[inline] +def byteIterator (s : String) := + (s.toSlice.bytes : Std.Iter UInt8) + +/-- +Creates an iterator over all bytes in {name}`s`, starting from the last one and iterating towards +the first one. + +Examples: + * {lean}`"abc".revBytes.toList = [99, 98, 97]` + * {lean}`"ab∀c".revBytes.toList = [99, 128, 136, 226, 98, 97]` +-/ +@[inline] +def revBytes (s : String) := + (s.toSlice.revBytes : Std.Iter UInt8) + +/-- +Creates an iterator over all lines in {name}`s` with the line ending characters `\r\n` or `\n` being +stripped. + +Examples: + * {lean}`"foo\r\nbar\n\nbaz\n".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz".toSlice]` + * {lean}`"foo\r\nbar\n\nbaz".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz".toSlice]` + * {lean}`"foo\r\nbar\n\nbaz\r".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz\r".toSlice]` +-/ +def lines (s : String) := + s.toSlice.lines + end end String diff --git a/src/Init/Data/String/Slice.lean b/src/Init/Data/String/Slice.lean index d9a79a69ee..c615b22f8f 100644 --- a/src/Init/Data/String/Slice.lean +++ b/src/Init/Data/String/Slice.lean @@ -1303,13 +1303,13 @@ def toNat! (s : Slice) : Nat := panic! "Nat expected" /-- -Returns the first character in {name}`s`. If {name}`s` is empty, {name}`none`. +Returns the first character in {name}`s`. If {name}`s` is empty, returns {name}`none`. Examples: * {lean}`"abc".toSlice.front? = some 'a'` * {lean}`"".toSlice.front? = none` -/ -@[inline] +@[inline, expose] def front? (s : Slice) : Option Char := s.startPos.get? @@ -1320,7 +1320,7 @@ Examples: * {lean}`"abc".toSlice.front = 'a'` * {lean}`"".toSlice.front = (default : Char)` -/ -@[inline] +@[inline, expose] def front (s : Slice) : Char := s.front?.getD default @@ -1331,7 +1331,7 @@ Examples: * {lean}`"abc".toSlice.back? = some 'c'` * {lean}`"".toSlice.back? = none` -/ -@[inline] +@[inline, expose] def back? (s : Slice) : Option Char := s.endPos.prev? |>.bind (·.get?) @@ -1342,7 +1342,7 @@ Examples: * {lean}`"abc".toSlice.back = 'c'` * {lean}`"".toSlice.back = (default : Char)` -/ -@[inline] +@[inline, expose] def back (s : Slice) : Char := s.back?.getD default diff --git a/src/Init/Data/ToString/Name.lean b/src/Init/Data/ToString/Name.lean index 1c95c39766..3495478f2a 100644 --- a/src/Init/Data/ToString/Name.lean +++ b/src/Init/Data/ToString/Name.lean @@ -8,6 +8,7 @@ module prelude public import Init.Data.String.Substring import Init.Data.String.TakeDrop +import Init.Data.String.Search /-! Here we give the. implementation of `Name.toString`. There is also a private implementation in diff --git a/src/Init/System/IO.lean b/src/Init/System/IO.lean index 03945e42db..016512c23e 100644 --- a/src/Init/System/IO.lean +++ b/src/Init/System/IO.lean @@ -10,6 +10,7 @@ public import Init.System.IOError public import Init.System.FilePath public import Init.Data.Ord.UInt import Init.Data.String.TakeDrop +import Init.Data.String.Search public section diff --git a/src/Lean/Data/Lsp/LanguageFeatures.lean b/src/Lean/Data/Lsp/LanguageFeatures.lean index b2b6f9d8ca..562d97109b 100644 --- a/src/Lean/Data/Lsp/LanguageFeatures.lean +++ b/src/Lean/Data/Lsp/LanguageFeatures.lean @@ -10,6 +10,7 @@ prelude public import Lean.Data.Lsp.Basic meta import Lean.Data.Json public import Lean.Expr +public import Init.Data.String.Search public section diff --git a/src/Lean/Data/Name.lean b/src/Lean/Data/Name.lean index e033385f40..3c961a1b23 100644 --- a/src/Lean/Data/Name.lean +++ b/src/Lean/Data/Name.lean @@ -10,6 +10,7 @@ public import Init.Data.Ord.Basic import Init.Data.String.TakeDrop import Init.Data.Ord.String import Init.Data.Ord.UInt +import Init.Data.String.Search public section namespace Lean