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.
467 lines
13 KiB
Text
467 lines
13 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
|
||
-/
|
||
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
|