lean4-htt/src/Init/Data/Repr.lean
Markus Himmel bf60550ce5
chore: rename Substring to Substring.Raw (#11154)
This PR renames `Substring`  to `Substring.Raw`.

This is to signify its status as a second-class citizen (not deprecated,
but no real plans for verification, like `String.Pos.Raw`) and to free
up the name `Substring` for a possible future type `String.Substring :
String -> Type` so that `s.Substring` is the type of substrings of `s`.

The functions `String.toSubstring` and `String.toSubstring'` will remain
for now for bootstrapping reasons.
2025-11-16 09:30:04 +00:00

467 lines
13 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
-/
module
prelude
public import Init.Data.Format.Basic
public section
open Sum Subtype Nat
open Std
/--
The standard way of turning values of some type into `Format`.
When rendered this `Format` should be as close as possible to something that can be parsed as the
input value.
-/
class Repr (α : Type u) where
/--
Turn a value of type `α` into a `Format` at a given precedence. The precedence value can be used
to avoid parentheses if they are not necessary.
-/
reprPrec : α → Nat → Format
export Repr (reprPrec)
/--
Turns `a` into a `Format` using its `Repr` instance. The precedence level is initially set to 0.
-/
abbrev repr [Repr α] (a : α) : Format :=
reprPrec a 0
/--
Turns `a` into a `String` using its `Repr` instance, rendering the `Format` at the default width of
120 columns.
The precedence level is initially set to 0.
-/
abbrev reprStr [Repr α] (a : α) : String :=
reprPrec a 0 |>.pretty
/--
Turns `a` into a `Format` using its `Repr` instance, with the precedence level set to that of
function application.
Together with `Repr.addAppParen`, this can be used to correctly parenthesize function application
syntax.
-/
abbrev reprArg [Repr α] (a : α) : Format :=
reprPrec a max_prec
/-- Auxiliary class for marking types that should be considered atomic by `Repr` methods.
We use it at `Repr (List α)` to decide whether `bracketFill` should be used or not. -/
class ReprAtom (α : Type u)
-- This instance is needed because `id` is not reducible
instance [Repr α] : Repr (id α) :=
inferInstanceAs (Repr α)
instance [Repr α] : Repr (Id α) :=
inferInstanceAs (Repr α)
/-
This instance allows us to use `Empty` as a type parameter without causing instance synthesis to fail.
-/
instance : Repr Empty where
reprPrec := nofun
protected def Bool.repr : Bool → Nat → Format
| true, _ => "true"
| false, _ => "false"
instance : Repr Bool where
reprPrec := Bool.repr
/--
Adds parentheses to `f` if the precedence `prec` from the context is at least that of function
application.
Together with `reprArg`, this can be used to correctly parenthesize function application
syntax.
-/
def Repr.addAppParen (f : Format) (prec : Nat) : Format :=
if prec >= max_prec then
Format.paren f
else
f
protected def Decidable.repr : Decidable p → Nat → Format
| .isTrue _, prec => Repr.addAppParen "isTrue _" prec
| .isFalse _, prec => Repr.addAppParen "isFalse _" prec
instance : Repr (Decidable p) where
reprPrec := Decidable.repr
instance : Repr PUnit.{u+1} where
reprPrec _ _ := "PUnit.unit"
instance [Repr α] : Repr (ULift.{v} α) where
reprPrec v prec :=
Repr.addAppParen ("ULift.up " ++ reprArg v.1) prec
instance : Repr Unit where
reprPrec _ _ := "()"
/--
Returns a representation of an optional value that should be able to be parsed as an equivalent
optional value.
This function is typically accessed through the `Repr (Option α)` instance.
-/
protected def Option.repr [Repr α] : Option α → Nat → Format
| none, _ => "none"
| some a, prec => Repr.addAppParen ("some " ++ reprArg a) prec
instance [Repr α] : Repr (Option α) where
reprPrec := Option.repr
protected def Sum.repr [Repr α] [Repr β] : Sum α β → Nat → Format
| Sum.inl a, prec => Repr.addAppParen ("Sum.inl " ++ reprArg a) prec
| Sum.inr b, prec => Repr.addAppParen ("Sum.inr " ++ reprArg b) prec
instance [Repr α] [Repr β] : Repr (Sum α β) where
reprPrec := Sum.repr
class ReprTuple (α : Type u) where
reprTuple : α → List Format → List Format
export ReprTuple (reprTuple)
instance [Repr α] : ReprTuple α where
reprTuple a xs := repr a :: xs
protected def Prod.reprTuple [Repr α] [ReprTuple β] : α × β → List Format → List Format
| (a, b), xs => reprTuple b (repr a :: xs)
instance [Repr α] [ReprTuple β] : ReprTuple (α × β) where
reprTuple := Prod.reprTuple
protected def Prod.repr [Repr α] [ReprTuple β] : α × β → Nat → Format
| (a, b), _ => Format.bracket "(" (Format.joinSep (reprTuple b [repr a]).reverse ("," ++ Format.line)) ")"
instance [Repr α] [ReprTuple β] : Repr (α × β) where
reprPrec := Prod.repr
protected def Sigma.repr {β : α → Type v} [Repr α] [(x : α) → Repr (β x)] : Sigma β → Nat → Format
| ⟨a, b⟩, _ => Format.bracket "⟨" (repr a ++ ", " ++ repr b) "⟩"
instance {β : α → Type v} [Repr α] [(x : α) → Repr (β x)] : Repr (Sigma β) where
reprPrec := Sigma.repr
instance {p : α → Prop} [Repr α] : Repr (Subtype p) where
reprPrec s prec := reprPrec s.val prec
namespace Nat
/-
We have pure functions for calculating the decimal representation of a `Nat` (`toDigits`), but also
a fast variant that handles small numbers (`USize`) via C code (`lean_string_of_usize`).
-/
/--
Returns a single digit representation of `n`, which is assumed to be in a base less than or equal to
`16`. Returns `'*'` if `n > 15`.
Examples:
* `Nat.digitChar 5 = '5'`
* `Nat.digitChar 12 = 'c'`
* `Nat.digitChar 15 = 'f'`
* `Nat.digitChar 16 = '*'`
* `Nat.digitChar 85 = '*'`
-/
def digitChar (n : Nat) : Char :=
if n = 0 then '0' else
if n = 1 then '1' else
if n = 2 then '2' else
if n = 3 then '3' else
if n = 4 then '4' else
if n = 5 then '5' else
if n = 6 then '6' else
if n = 7 then '7' else
if n = 8 then '8' else
if n = 9 then '9' else
if n = 0xa then 'a' else
if n = 0xb then 'b' else
if n = 0xc then 'c' else
if n = 0xd then 'd' else
if n = 0xe then 'e' else
if n = 0xf then 'f' else
'*'
def toDigitsCore (base : Nat) : Nat → Nat → List Char → List Char
| 0, _, ds => ds
| fuel+1, n, ds =>
let d := digitChar <| n % base;
let n' := n / base;
if n' = 0 then d::ds
else toDigitsCore base fuel n' (d::ds)
/--
Returns the decimal representation of a natural number as a list of digit characters in the given
base. If the base is greater than `16` then `'*'` is returned for digits greater than `0xf`.
Examples:
* `Nat.toDigits 10 0xff = ['2', '5', '5']`
* `Nat.toDigits 8 0xc = ['1', '4']`
* `Nat.toDigits 16 0xcafe = ['c', 'a', 'f', 'e']`
* `Nat.toDigits 80 200 = ['2', '*']`
-/
def toDigits (base : Nat) (n : Nat) : List Char :=
toDigitsCore base (n+1) n []
/--
Converts a word-sized unsigned integer into a decimal string.
This function is overridden at runtime with an efficient implementation.
Examples:
* `USize.repr 0 = "0"`
* `USize.repr 28 = "28"`
* `USize.repr 307 = "307"`
-/
@[extern "lean_string_of_usize"]
protected def _root_.USize.repr (n : @& USize) : String :=
String.ofList (toDigits 10 n.toNat)
/-- We statically allocate and memoize reprs for small natural numbers. -/
private def reprArray : Array String := Id.run do
List.range 128 |>.map (·.toUSize.repr) |> Array.mk
def reprFast (n : Nat) : String :=
if h : n < Nat.reprArray.size then Nat.reprArray.getInternal n h else
if h : n < USize.size then (USize.ofNatLT n h).repr
else String.ofList (toDigits 10 n)
/--
Converts a natural number to its decimal string representation.
-/
@[implemented_by reprFast]
protected def repr (n : Nat) : String :=
String.ofList (toDigits 10 n)
/--
Converts a natural number less than `10` to the corresponding Unicode superscript digit character.
Returns `'*'` for other numbers.
Examples:
* `Nat.superDigitChar 3 = '³'`
* `Nat.superDigitChar 7 = '⁷'`
* `Nat.superDigitChar 10 = '*'`
-/
def superDigitChar (n : Nat) : Char :=
if n = 0 then '⁰' else
if n = 1 then '¹' else
if n = 2 then '²' else
if n = 3 then '³' else
if n = 4 then '⁴' else
if n = 5 then '⁵' else
if n = 6 then '⁶' else
if n = 7 then '⁷' else
if n = 8 then '⁸' else
if n = 9 then '⁹' else
'*'
partial def toSuperDigitsAux : Nat → List Char → List Char
| n, ds =>
let d := superDigitChar <| n % 10;
let n' := n / 10;
if n' = 0 then d::ds
else toSuperDigitsAux n' (d::ds)
/--
Converts a natural number to the list of Unicode superscript digit characters that corresponds to
its decimal representation.
Examples:
* `Nat.toSuperDigits 0 = ['⁰']`
* `Nat.toSuperDigits 35 = ['³', '⁵']`
-/
def toSuperDigits (n : Nat) : List Char :=
toSuperDigitsAux n []
/--
Converts a natural number to a string that contains the its decimal representation as Unicode
superscript digit characters.
Examples:
* `Nat.toSuperscriptString 0 = "⁰"`
* `Nat.toSuperscriptString 35 = "³⁵"`
-/
def toSuperscriptString (n : Nat) : String :=
String.ofList (toSuperDigits n)
/--
Converts a natural number less than `10` to the corresponding Unicode subscript digit character.
Returns `'*'` for other numbers.
Examples:
* `Nat.subDigitChar 3 = '₃'`
* `Nat.subDigitChar 7 = '₇'`
* `Nat.subDigitChar 10 = '*'`
-/
def subDigitChar (n : Nat) : Char :=
if n = 0 then '₀' else
if n = 1 then '₁' else
if n = 2 then '₂' else
if n = 3 then '₃' else
if n = 4 then '₄' else
if n = 5 then '₅' else
if n = 6 then '₆' else
if n = 7 then '₇' else
if n = 8 then '₈' else
if n = 9 then '₉' else
'*'
partial def toSubDigitsAux : Nat → List Char → List Char
| n, ds =>
let d := subDigitChar <| n % 10;
let n' := n / 10;
if n' = 0 then d::ds
else toSubDigitsAux n' (d::ds)
/--
Converts a natural number to the list of Unicode subscript digit characters that corresponds to
its decimal representation.
Examples:
* `Nat.toSubDigits 0 = ['₀']`
* `Nat.toSubDigits 35 = ['₃', '₅']`
-/
def toSubDigits (n : Nat) : List Char :=
toSubDigitsAux n []
/--
Converts a natural number to a string that contains the its decimal representation as Unicode
subscript digit characters.
Examples:
* `Nat.toSubscriptString 0 = "₀"`
* `Nat.toSubscriptString 35 = "₃₅"`
-/
def toSubscriptString (n : Nat) : String :=
String.ofList (toSubDigits n)
end Nat
instance : Repr Nat where
reprPrec n _ := Nat.repr n
/--
Returns the decimal string representation of an integer.
-/
protected def Int.repr : Int → String
| ofNat m => Nat.repr m
| negSucc m => String.Internal.append "-" (Nat.repr (succ m))
instance : Repr Int where
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
def hexDigitRepr (n : Nat) : String :=
String.singleton <| Nat.digitChar n
def Char.quoteCore (c : Char) (inString : Bool := false) : String :=
if c = '\n' then "\\n"
else if c = '\t' then "\\t"
else if c = '\\' then "\\\\"
else if c = '\"' then "\\\""
else if !inString && c = '\'' then "\\\'"
else if c.toNat <= 31 c = '\x7f' then String.Internal.append "\\x" (smallCharToHex c)
else String.singleton c
where
smallCharToHex (c : Char) : String :=
let n := Char.toNat c;
let d2 := n / 16;
let d1 := n % 16;
String.Internal.append (hexDigitRepr d2) (hexDigitRepr d1)
/--
Quotes the character to its representation as a character literal, surrounded by single quotes and
escaped as necessary.
Examples:
* `'L'.quote = "'L'"`
* `'"'.quote = "'\\\"'"`
-/
def Char.quote (c : Char) : String :=
String.Internal.append (String.Internal.append "'" (Char.quoteCore c)) "'"
instance : Repr Char where
reprPrec c _ := c.quote
protected def Char.repr (c : Char) : String :=
c.quote
/--
Converts a string to its corresponding Lean string literal syntax. Double quotes are added to each
end, and internal characters are escaped as needed.
Examples:
* `"abc".quote = "\"abc\""`
* `"\"".quote = "\"\\\"\""`
-/
def String.quote (s : String) : String :=
if String.Internal.isEmpty s then "\"\""
else String.Internal.append (String.Internal.foldl (fun s c => String.Internal.append s (c.quoteCore (inString := true))) "\"" s) "\""
instance : Repr String where
reprPrec s _ := s.quote
instance : Repr String.Pos.Raw where
reprPrec p _ := "{ byteIdx := " ++ repr p.byteIdx ++ " }"
instance : Repr Substring.Raw where
reprPrec s _ := Format.text <| String.Internal.append (String.quote (Substring.Raw.Internal.toString s)) ".toRawSubstring"
instance (n : Nat) : Repr (Fin n) where
reprPrec f _ := repr f.val
instance : Repr UInt8 where
reprPrec n _ := repr n.toNat
instance : Repr UInt16 where
reprPrec n _ := repr n.toNat
instance : Repr UInt32 where
reprPrec n _ := repr n.toNat
instance : Repr UInt64 where
reprPrec n _ := repr n.toNat
instance : Repr USize where
reprPrec n _ := repr n.toNat
protected def List.repr [Repr α] (a : List α) (n : Nat) : Format :=
let _ : ToFormat α := ⟨repr⟩
match a, n with
| [], _ => "[]"
| as, _ => Format.bracket "[" (Format.joinSep as ("," ++ Format.line)) "]"
instance [Repr α] : Repr (List α) where
reprPrec := List.repr
protected def List.repr' [Repr α] [ReprAtom α] (a : List α) (n : Nat) : Format :=
let _ : ToFormat α := ⟨repr⟩
match a, n with
| [], _ => "[]"
| as, _ => Format.bracketFill "[" (Format.joinSep as ("," ++ Format.line)) "]"
instance [Repr α] [ReprAtom α] : Repr (List α) where
reprPrec := List.repr'
instance : ReprAtom Bool := ⟨⟩
instance : ReprAtom Nat := ⟨⟩
instance : ReprAtom Int := ⟨⟩
instance : ReprAtom Char := ⟨⟩
instance : ReprAtom String := ⟨⟩
instance : ReprAtom UInt8 := ⟨⟩
instance : ReprAtom UInt16 := ⟨⟩
instance : ReprAtom UInt32 := ⟨⟩
instance : ReprAtom UInt64 := ⟨⟩
instance : ReprAtom USize := ⟨⟩
deriving instance Repr for Lean.SourceInfo