lean4-htt/src/Init/Data/String/TakeDrop.lean
2026-02-05 09:10:32 +00:00

470 lines
18 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
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