470 lines
18 KiB
Text
470 lines
18 KiB
Text
/-
|
||
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Author: Leonardo de Moura, Mario Carneiro, Markus Himmel
|
||
-/
|
||
module
|
||
|
||
prelude
|
||
public import Init.Data.String.Substring
|
||
|
||
set_option doc.verso true
|
||
|
||
/-!
|
||
# `String.take` and variants
|
||
|
||
This file contains the implementations of `String.take` and its variants.
|
||
-/
|
||
|
||
public section
|
||
|
||
namespace String
|
||
|
||
variable {ρ : Type}
|
||
|
||
open Slice.Pattern
|
||
|
||
/--
|
||
Returns a {name}`String.Slice` obtained by removing the specified number of characters (Unicode code
|
||
points) from the start of the string.
|
||
|
||
If {name}`n` is greater than {lean}`s.length`, returns an empty slice.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".drop 4 == "green blue".toSlice`
|
||
* {lean}`"red green blue".drop 10 == "blue".toSlice`
|
||
* {lean}`"red green blue".drop 50 == "".toSlice`
|
||
* {lean}`"مرحبا بالعالم".drop 3 == "با بالعالم".toSlice`
|
||
-/
|
||
@[inline] def drop (s : String) (n : Nat) : Slice :=
|
||
s.toSlice.drop n
|
||
|
||
@[export lean_string_drop]
|
||
def Internal.dropImpl (s : String) (n : Nat) : String :=
|
||
(String.drop s n).copy
|
||
|
||
/--
|
||
Returns a {name}`String.Slice` obtained by removing the specified number of characters (Unicode code
|
||
points) from the end of the string.
|
||
|
||
If {name}`n` is greater than {lean}`s.length`, returns an empty slice.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".dropEnd 5 == "red green".toSlice`
|
||
* {lean}`"red green blue".dropEnd 11 == "red".toSlice`
|
||
* {lean}`"red green blue".dropEnd 50 == "".toSlice`
|
||
* {lean}`"مرحبا بالعالم".dropEnd 3 == "مرحبا بالع".toSlice`
|
||
-/
|
||
@[inline] def dropEnd (s : String) (n : Nat) : Slice :=
|
||
s.toSlice.dropEnd n
|
||
|
||
@[deprecated String.dropEnd (since := "2025-11-14")]
|
||
def dropRight (s : String) (n : Nat) : String :=
|
||
(s.dropEnd n).copy
|
||
|
||
@[deprecated Slice.dropEnd (since := "2025-11-20")]
|
||
def Slice.dropRight (s : Slice) (n : Nat) : Slice :=
|
||
s.dropEnd n
|
||
|
||
@[export lean_string_dropright]
|
||
def Internal.dropRightImpl (s : String) (n : Nat) : String :=
|
||
(String.dropEnd s n).copy
|
||
|
||
/--
|
||
Returns a {name}`String.Slice` that contains the first {name}`n` characters (Unicode code points) of
|
||
{name}`s`.
|
||
|
||
If {name}`n` is greater than {lean}`s.length`, returns {lean}`s.toSlice`.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".take 3 == "red".toSlice`
|
||
* {lean}`"red green blue".take 1 == "r".toSlice`
|
||
* {lean}`"red green blue".take 0 == "".toSlice`
|
||
* {lean}`"red green blue".take 100 == "red green blue".toSlice`
|
||
* {lean}`"مرحبا بالعالم".take 5 == "مرحبا".toSlice`
|
||
-/
|
||
@[inline] def take (s : String) (n : Nat) : String.Slice :=
|
||
s.toSlice.take n
|
||
|
||
/--
|
||
Returns a {name}`String.Slice` that contains the last {name}`n` characters (Unicode code points) of
|
||
{name}`s`.
|
||
|
||
If {name}`n` is greater than {lean}`s.length`, returns {lean}`s.toSlice`.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".takeEnd 4 == "blue".toSlice`
|
||
* {lean}`"red green blue".takeEnd 1 == "e".toSlice`
|
||
* {lean}`"red green blue".takeEnd 0 == "".toSlice`
|
||
* {lean}`"red green blue".takeEnd 100 == "red green blue".toSlice`
|
||
* {lean}`"مرحبا بالعالم".takeEnd 5 == "لعالم".toSlice`
|
||
-/
|
||
@[inline] def takeEnd (s : String) (n : Nat) : String.Slice :=
|
||
s.toSlice.takeEnd n
|
||
|
||
@[deprecated String.takeEnd (since := "2025-11-14")]
|
||
def takeRight (s : String) (n : Nat) : String :=
|
||
(s.takeEnd n).toString
|
||
|
||
@[deprecated Slice.takeEnd (since := "2025-11-20")]
|
||
def Slice.takeRight (s : Slice) (n : Nat) : Slice :=
|
||
s.takeEnd n
|
||
|
||
/--
|
||
Creates a string slice that contains the longest prefix of {name}`s` in which {name}`pat` matched
|
||
(potentially repeatedly).
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".takeWhile Char.isLower == "red".toSlice`
|
||
* {lean}`"red green blue".takeWhile 'r' == "r".toSlice`
|
||
* {lean}`"red red green blue".takeWhile "red " == "red red ".toSlice`
|
||
* {lean}`"red green blue".takeWhile (fun (_ : Char) => true) == "red green blue".toSlice`
|
||
-/
|
||
@[inline] def takeWhile (s : String) (pat : ρ) [ForwardPattern pat] : String.Slice :=
|
||
s.toSlice.takeWhile pat
|
||
|
||
/--
|
||
Creates a string slice by removing the longest prefix from {name}`s` in which {name}`pat` matched
|
||
(potentially repeatedly).
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".dropWhile Char.isLower == " green blue".toSlice`
|
||
* {lean}`"red green blue".dropWhile 'r' == "ed green blue".toSlice`
|
||
* {lean}`"red red green blue".dropWhile "red " == "green blue".toSlice`
|
||
* {lean}`"red green blue".dropWhile (fun (_ : Char) => true) == "".toSlice`
|
||
-/
|
||
@[inline] def dropWhile (s : String) (pat : ρ) [ForwardPattern pat] : String.Slice :=
|
||
s.toSlice.dropWhile pat
|
||
|
||
/--
|
||
Creates a string slice that contains the longest suffix of {name}`s` in which {name}`pat` matched
|
||
(potentially repeatedly).
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".takeEndWhile Char.isLower == "blue".toSlice`
|
||
* {lean}`"red green blue".takeEndWhile 'e' == "e".toSlice`
|
||
* {lean}`"red green blue".takeEndWhile (fun (_ : Char) => true) == "red green blue".toSlice`
|
||
-/
|
||
@[inline] def takeEndWhile (s : String) (pat : ρ) [BackwardPattern pat] : String.Slice :=
|
||
s.toSlice.takeEndWhile pat
|
||
|
||
@[deprecated String.takeEndWhile (since := "2025-11-17")]
|
||
def takeRightWhile (s : String) (p : Char → Bool) : String :=
|
||
(s.takeEndWhile p).toString
|
||
|
||
@[deprecated Slice.takeEndWhile (since := "2025-11-20")]
|
||
def Slice.takeRightWhile (s : Slice) (p : Char → Bool) : Slice :=
|
||
s.takeEndWhile p
|
||
|
||
/--
|
||
Creates a new string by removing the longest suffix from {name}`s` in which {name}`pat` matches
|
||
(potentially repeatedly).
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".dropEndWhile Char.isLower == "red green ".toSlice`
|
||
* {lean}`"red green blue".dropEndWhile 'e' == "red green blu".toSlice`
|
||
* {lean}`"red green blue".dropEndWhile (fun (_ : Char) => true) == "".toSlice`
|
||
-/
|
||
@[inline] def dropEndWhile (s : String) (pat : ρ) [BackwardPattern pat] : String.Slice :=
|
||
s.toSlice.dropEndWhile pat
|
||
|
||
@[deprecated String.dropEndWhile (since := "2025-11-17")]
|
||
def dropRightWhile (s : String) (p : Char → Bool) : String :=
|
||
(s.dropEndWhile p).toString
|
||
|
||
@[deprecated Slice.dropEndWhile (since := "2025-11-20")]
|
||
def Slice.dropRightWhile (s : Slice) (p : Char → Bool) : Slice :=
|
||
s.dropEndWhile p
|
||
|
||
/--
|
||
Checks whether the first string ({name}`s`) begins with the pattern ({name}`pat`).
|
||
|
||
{name (scope := "Init.Data.String.TakeDrop")}`String.isPrefixOf` is a version that takes the
|
||
potential prefix before the string.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".startsWith "red" = true`
|
||
* {lean}`"red green blue".startsWith "green" = false`
|
||
* {lean}`"red green blue".startsWith "" = true`
|
||
* {lean}`"red green blue".startsWith 'r' = true`
|
||
* {lean}`"red green blue".startsWith Char.isLower = true`
|
||
-/
|
||
@[inline] def startsWith (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
|
||
s.toSlice.startsWith pat
|
||
|
||
/--
|
||
Checks whether the second string ({name}`s`) begins with a prefix ({name}`p`).
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
{name}`String.startsWith` is a version that takes the potential prefix after the string.
|
||
|
||
Examples:
|
||
* {lean}`"red".isPrefixOf "red green blue" = true`
|
||
* {lean}`"green".isPrefixOf "red green blue" = false`
|
||
* {lean}`"".isPrefixOf "red green blue" = true`
|
||
-/
|
||
@[inline] def isPrefixOf (p : String) (s : String) : Bool :=
|
||
s.startsWith p
|
||
|
||
@[export lean_string_isprefixof]
|
||
def Internal.isPrefixOfImpl (p : String) (s : String) : Bool :=
|
||
String.isPrefixOf p s
|
||
|
||
/--
|
||
Checks whether the string ({name}`s`) ends with the pattern ({name}`pat`).
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".endsWith "blue" = true`
|
||
* {lean}`"red green blue".endsWith "green" = false`
|
||
* {lean}`"red green blue".endsWith "" = true`
|
||
* {lean}`"red green blue".endsWith 'e' = true`
|
||
* {lean}`"red green blue".endsWith Char.isLower = true`
|
||
-/
|
||
@[inline] def endsWith (s : String) (pat : ρ) [BackwardPattern pat] : Bool :=
|
||
s.toSlice.endsWith pat
|
||
|
||
/--
|
||
Removes trailing whitespace from a string by returning a slice whose end position is the last
|
||
non-whitespace character, or the start position if there is no non-whitespace character.
|
||
|
||
“Whitespace” is defined as characters for which {name}`Char.isWhitespace` returns {name}`true`.
|
||
|
||
Examples:
|
||
* {lean}`"abc".trimAsciiEnd == "abc".toSlice`
|
||
* {lean}`" abc".trimAsciiEnd == " abc".toSlice`
|
||
* {lean}`"abc \t ".trimAsciiEnd == "abc".toSlice`
|
||
* {lean}`" abc ".trimAsciiEnd == " abc".toSlice`
|
||
* {lean}`"abc\ndef\n".trimAsciiEnd == "abc\ndef".toSlice`
|
||
-/
|
||
@[inline] def trimAsciiEnd (s : String) : String.Slice :=
|
||
s.toSlice.trimAsciiEnd
|
||
|
||
@[deprecated String.trimAsciiEnd (since := "2025-11-17")]
|
||
def trimRight (s : String) : String :=
|
||
s.trimAsciiEnd.copy
|
||
|
||
@[deprecated Slice.trimAsciiEnd (since := "2025-11-20")]
|
||
def Slice.trimRight (s : Slice) : Slice :=
|
||
s.trimAsciiEnd
|
||
|
||
/--
|
||
Removes leading whitespace from a string by returning a slice whose start position is the first
|
||
non-whitespace character, or the end position if there is no non-whitespace character.
|
||
|
||
“Whitespace” is defined as characters for which {name}`Char.isWhitespace` returns {name}`true`.
|
||
|
||
Examples:
|
||
* {lean}`"abc".trimAsciiStart == "abc".toSlice`
|
||
* {lean}`" abc".trimAsciiStart == "abc".toSlice`
|
||
* {lean}`"abc \t ".trimAsciiStart == "abc \t ".toSlice`
|
||
* {lean}`" abc ".trimAsciiStart == "abc ".toSlice`
|
||
* {lean}`"abc\ndef\n".trimAsciiStart == "abc\ndef\n".toSlice`
|
||
-/
|
||
@[inline] def trimAsciiStart (s : String) : String.Slice :=
|
||
s.toSlice.trimAsciiStart
|
||
|
||
@[deprecated String.trimAsciiStart (since := "2025-11-17")]
|
||
def trimLeft (s : String) : String :=
|
||
s.trimAsciiStart.copy
|
||
|
||
@[deprecated Slice.trimAsciiStart (since := "2025-11-20")]
|
||
def Slice.trimLeft (s : Slice) : Slice :=
|
||
s.trimAsciiStart
|
||
|
||
/--
|
||
Removes leading and trailing whitespace from a string.
|
||
|
||
“Whitespace” is defined as characters for which {name}`Char.isWhitespace` returns {name}`true`.
|
||
|
||
Examples:
|
||
* {lean}`"abc".trimAscii == "abc".toSlice`
|
||
* {lean}`" abc".trimAscii == "abc".toSlice`
|
||
* {lean}`"abc \t ".trimAscii == "abc".toSlice`
|
||
* {lean}`" abc ".trimAscii == "abc".toSlice`
|
||
* {lean}`"abc\ndef\n".trimAscii == "abc\ndef".toSlice`
|
||
-/
|
||
@[inline] def trimAscii (s : String) : String.Slice :=
|
||
s.toSlice.trimAscii
|
||
|
||
@[deprecated String.trimAscii (since := "2025-11-17")]
|
||
def trim (s : String) : String :=
|
||
s.trimAscii.copy
|
||
|
||
@[deprecated Slice.trimAscii (since := "2025-11-20")]
|
||
def Slice.trim (s : Slice) : Slice :=
|
||
s.trimAscii
|
||
|
||
@[export lean_string_trim]
|
||
def Internal.trimImpl (s : String) : String :=
|
||
(String.trimAscii s).copy
|
||
|
||
/--
|
||
Repeatedly increments a position in a string, as if by {name}`String.Pos.Raw.next`, while the
|
||
predicate {name}`p` returns {lean}`true` for the character at the position. Stops incrementing at
|
||
the end of the string or when {name}`p` returns {lean}`false` for the current character.
|
||
|
||
Examples:
|
||
* {lean}`let s := " a "; ((0 : Pos.Raw).nextWhile s Char.isWhitespace).get s = 'a'`
|
||
* {lean}`let s := "a "; ((0 : Pos.Raw).nextWhile s Char.isWhitespace).get s = 'a'`
|
||
* {lean}`let s := "ba "; (Pos.Raw.nextWhile s Char.isWhitespace 0).get s = 'b'`
|
||
-/
|
||
@[inline] def Pos.Raw.nextWhile (s : String) (p : Char → Bool) (i : String.Pos.Raw) : String.Pos.Raw :=
|
||
Substring.Raw.takeWhileAux s s.rawEndPos p i
|
||
|
||
@[deprecated Pos.Raw.nextWhile (since := "2025-10-10")]
|
||
abbrev nextWhile (s : String) (p : Char → Bool) (i : String.Pos.Raw) : String.Pos.Raw :=
|
||
Pos.Raw.nextWhile s p i
|
||
|
||
@[export lean_string_nextwhile]
|
||
def Internal.nextWhileImpl (s : String) (p : Char → Bool) (i : String.Pos.Raw) : String.Pos.Raw :=
|
||
i.nextWhile s p
|
||
|
||
/--
|
||
Repeatedly increments a position in a string, as if by {name}`String.Pos.Raw.next`, while the predicate
|
||
{name}`p` returns {lean}`false` for the character at the position. Stops incrementing at the end of
|
||
the string or when {name}`p` returns {lean}`true` for the current character.
|
||
|
||
Examples:
|
||
* {lean}`let s := " a "; (Pos.Raw.nextUntil s Char.isWhitespace 0).get s = ' '`
|
||
* {lean}`let s := " a "; (Pos.Raw.nextUntil s Char.isAlpha 0).get s = 'a'`
|
||
* {lean}`let s := "a "; (Pos.Raw.nextUntil s Char.isWhitespace 0).get s = ' '`
|
||
-/
|
||
@[inline] def Pos.Raw.nextUntil (s : String) (p : Char → Bool) (i : String.Pos.Raw) : String.Pos.Raw :=
|
||
nextWhile s (fun c => !p c) i
|
||
|
||
@[deprecated Pos.Raw.nextUntil (since := "2025-10-10")]
|
||
def nextUntil (s : String) (p : Char → Bool) (i : String.Pos.Raw) : String.Pos.Raw :=
|
||
i.nextUntil s p
|
||
|
||
/--
|
||
If {name}`pat` matches a prefix of {name}`s`, returns the remainder. Returns {name}`none` otherwise.
|
||
|
||
Use {name (scope := "Init.Data.String.Slice")}`String.dropPrefix` to return the slice
|
||
unchanged when {name}`pat` does not match a prefix.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".dropPrefix? "red " == some "green blue".toSlice`
|
||
* {lean}`"red green blue".dropPrefix? "reed " == none`
|
||
* {lean}`"red green blue".dropPrefix? 'r' == some "ed green blue".toSlice`
|
||
* {lean}`"red green blue".dropPrefix? Char.isLower == some "ed green blue".toSlice`
|
||
-/
|
||
def dropPrefix? (s : String) (pat : ρ) [ForwardPattern pat] : Option String.Slice :=
|
||
s.toSlice.dropPrefix? pat
|
||
|
||
/--
|
||
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`none` otherwise.
|
||
|
||
Use {name (scope := "Init.Data.String.TakeDrop")}`String.dropSuffix` to return the slice
|
||
unchanged when {name}`pat` does not match a prefix.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".dropSuffix? " blue" == some "red green".toSlice`
|
||
* {lean}`"red green blue".dropSuffix? "bluu " == none`
|
||
* {lean}`"red green blue".dropSuffix? 'e' == some "red green blu".toSlice`
|
||
* {lean}`"red green blue".dropSuffix? Char.isLower == some "red green blu".toSlice`
|
||
-/
|
||
def dropSuffix? (s : String) (pat : ρ) [BackwardPattern pat] : Option String.Slice :=
|
||
s.toSlice.dropSuffix? pat
|
||
|
||
/--
|
||
If {name}`pat` matches a prefix of {name}`s`, returns the remainder. Returns {name}`s` unmodified
|
||
otherwise.
|
||
|
||
Use {name}`String.dropPrefix?` to return {name}`none` when {name}`pat` does not match a prefix.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".dropPrefix "red " == "green blue".toSlice`
|
||
* {lean}`"red green blue".dropPrefix "reed " == "red green blue".toSlice`
|
||
* {lean}`"red green blue".dropPrefix 'r' == "ed green blue".toSlice`
|
||
* {lean}`"red green blue".dropPrefix Char.isLower == "ed green blue".toSlice`
|
||
-/
|
||
def dropPrefix (s : String) (pat : ρ) [ForwardPattern pat] : String.Slice :=
|
||
s.toSlice.dropPrefix pat
|
||
|
||
@[deprecated String.dropPrefix (since := "2025-11-17")]
|
||
def stripPrefix (s pre : String) : String :=
|
||
(s.dropPrefix pre).toString
|
||
|
||
@[deprecated Slice.dropPrefix (since := "2025-11-20")]
|
||
def Slice.stripPrefix (s pre : Slice) : Slice :=
|
||
s.dropPrefix pre
|
||
|
||
/--
|
||
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`s` unmodified
|
||
otherwise.
|
||
|
||
Use {name}`String.dropSuffix?` to return {name}`none` when {name}`pat` does not match a prefix.
|
||
|
||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||
|
||
This function is generic over all currently supported patterns.
|
||
|
||
Examples:
|
||
* {lean}`"red green blue".dropSuffix " blue" == "red green".toSlice`
|
||
* {lean}`"red green blue".dropSuffix "bluu " == "red green blue".toSlice`
|
||
* {lean}`"red green blue".dropSuffix 'e' == "red green blu".toSlice`
|
||
* {lean}`"red green blue".dropSuffix Char.isLower == "red green blu".toSlice`
|
||
-/
|
||
def dropSuffix (s : String) (pat : ρ) [BackwardPattern pat] : String.Slice :=
|
||
s.toSlice.dropSuffix pat
|
||
|
||
@[deprecated String.dropSuffix (since := "2025-11-17")]
|
||
def stripSuffix (s : String) (suff : String) : String :=
|
||
(s.dropSuffix suff).toString
|
||
|
||
@[deprecated Slice.dropSuffix (since := "2025-11-20")]
|
||
def Slice.stripSuffix (s : Slice) (suff : Slice) : Slice :=
|
||
s.dropSuffix suff
|
||
|
||
end String
|