lean4-htt/library/init/lean/format.lean

206 lines
7.2 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) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
prelude
import init.lean.options init.data.array
universes u v
namespace Lean
inductive Format
| nil : Format
| line : Format
| text : String → Format
| nest : Nat → Format → Format
| compose : Bool → Format → Format → Format
| choice : Format → Format → Format
namespace Format
@[export lean.format_append_core]
protected def append (a b : Format) : Format :=
compose false a b
instance : HasAppend Format := ⟨Format.append⟩
instance : HasCoe String Format := ⟨text⟩
instance : Inhabited Format := ⟨nil⟩
def join (xs : List Format) : Format :=
xs.foldl HasAppend.append ""
def isNil : Format → Bool
| nil => true
| _ => false
def flatten : Format → Format
| nil => nil
| line => text " "
| f@(text _) => f
| nest _ f => flatten f
| choice f _ => flatten f
| f@(compose true _ _) => f
| f@(compose false f₁ f₂) => compose true (flatten f₁) (flatten f₂)
@[export lean.format_group_core]
def group : Format → Format
| nil => nil
| f@(text _) => f
| f@(compose true _ _) => f
| f => choice (flatten f) f
structure SpaceResult :=
(found := false)
(exceeded := false)
(space := 0)
@[inline] private def merge (w : Nat) (r₁ : SpaceResult) (r₂ : Thunk SpaceResult) : SpaceResult :=
if r₁.exceeded || r₁.found then r₁
else
let y := r₂.get;
if y.exceeded || y.found then y
else
let newSpace := r₁.space + y.space;
{ space := newSpace, exceeded := newSpace > w }
def spaceUptoLine : Format → Nat → SpaceResult
| nil, w => {}
| line, w => { found := true }
| text s, w => { space := s.length, exceeded := s.length > w }
| compose _ f₁ f₂, w => merge w (spaceUptoLine f₁ w) (spaceUptoLine f₂ w)
| nest _ f, w => spaceUptoLine f w
| choice f₁ f₂, w => spaceUptoLine f₂ w
def spaceUptoLine' : List (Nat × Format) → Nat → SpaceResult
| [], w => {}
| p::ps, w => merge w (spaceUptoLine p.2 w) (spaceUptoLine' ps w)
partial def be : Nat → Nat → String → List (Nat × Format) → String
| w, k, out, [] => out
| w, k, out, (i, nil)::z => be w k out z
| w, k, out, (i, (compose _ f₁ f₂))::z => be w k out ((i, f₁)::(i, f₂)::z)
| w, k, out, (i, (nest n f))::z => be w k out ((i+n, f)::z)
| w, k, out, (i, text s)::z => be w (k + s.length) (out ++ s) z
| w, k, out, (i, line)::z => be w i ((out ++ "\n").pushn ' ' i) z
| w, k, out, (i, choice f₁ f₂)::z =>
let r := merge w (spaceUptoLine f₁ w) (spaceUptoLine' z w);
if r.exceeded then be w k out ((i, f₂)::z) else be w k out ((i, f₁)::z)
@[inline] def bracket (l : String) (f : Format) (r : String) : Format :=
group (nest l.length $ l ++ f ++ r)
@[inline] def paren (f : Format) : Format :=
bracket "(" f ")"
@[inline] def sbracket (f : Format) : Format :=
bracket "[" f "]"
def defIndent := 4
def defUnicode := true
def defWidth := 120
def getWidth (o : Options) : Nat := o.get `format.width defWidth
def getIndent (o : Options) : Nat := o.get `format.indent defIndent
def getUnicode (o : Options) : Bool := o.get `format.unicode defUnicode
@[init] def indentOption : IO Unit :=
registerOption `format.indent { defValue := defIndent, group := "format", descr := "indentation" }
@[init] def unicodeOption : IO Unit :=
registerOption `format.unicode { defValue := defUnicode, group := "format", descr := "unicode characters" }
@[init] def widthOption : IO Unit :=
registerOption `format.width { defValue := defWidth, group := "format", descr := "line width" }
@[export lean.format_pretty_core]
def prettyAux (f : Format) (w : Nat := defWidth) : String :=
be w 0 "" [(0, f)]
def pretty (f : Format) (o : Options := {}) : String :=
prettyAux f (getWidth o)
end Format
open Lean.Format
class HasFormat (α : Type u) :=
(format : α → Format)
export Lean.HasFormat (format)
def fmt {α : Type u} [HasFormat α] : α → Format :=
format
instance toStringToFormat {α : Type u} [HasToString α] : HasFormat α :=
⟨text ∘ toString⟩
-- note: must take precendence over the above instance to avoid premature formatting
instance formatHasFormat : HasFormat Format :=
⟨id⟩
instance stringHasFormat : HasFormat String := ⟨Format.text⟩
def Format.joinSep {α : Type u} [HasFormat α] : List α → Format → Format
| [], sep => nil
| [a], sep => format a
| a::as, sep => format a ++ sep ++ Format.joinSep as sep
def Format.prefixJoin {α : Type u} [HasFormat α] (pre : Format) : List α → Format
| [] => nil
| a::as => pre ++ format a ++ Format.prefixJoin as
def Format.joinSuffix {α : Type u} [HasFormat α] : List α → Format → Format
| [], suffix => nil
| a::as, suffix => format a ++ suffix ++ Format.joinSuffix as suffix
def List.format {α : Type u} [HasFormat α] : List α → Format
| [] => "[]"
| xs => sbracket $ Format.joinSep xs ("," ++ line)
instance listHasFormat {α : Type u} [HasFormat α] : HasFormat (List α) :=
⟨List.format⟩
instance prodHasFormat {α : Type u} {β : Type v} [HasFormat α] [HasFormat β] : HasFormat (Prod α β) :=
⟨fun ⟨a, b⟩ => paren $ format a ++ "," ++ line ++ format b⟩
def Format.joinArraySep {α : Type u} [HasFormat α] (a : Array α) (sep : Format) : Format :=
a.iterate nil (fun i a r => if i.val > 0 then r ++ sep ++ format a else r ++ format a)
instance natHasFormat : HasFormat Nat := ⟨fun n => toString n⟩
instance uint16HasFormat : HasFormat UInt16 := ⟨fun n => toString n⟩
instance uint32HasFormat : HasFormat UInt32 := ⟨fun n => toString n⟩
instance uint64HasFormat : HasFormat UInt64 := ⟨fun n => toString n⟩
instance usizeHasFormat : HasFormat USize := ⟨fun n => toString n⟩
instance nameHasFormat : HasFormat Name := ⟨fun n => n.toString⟩
protected def Format.repr : Format → Format
| nil => "Format.nil"
| line => "Format.line"
| text s => paren $ "Format.text" ++ line ++ repr s
| nest n f => paren $ "Format.nest" ++ line ++ repr n ++ line ++ Format.repr f
| compose b f₁ f₂ => paren $ "Format.compose " ++ repr b ++ line ++ Format.repr f₁ ++ line ++ Format.repr f₂
| choice f₁ f₂ => paren $ "Format.choice" ++ line ++ Format.repr f₁ ++ line ++ Format.repr f₂
instance formatHasToString : HasToString Format := ⟨Format.pretty⟩
instance : HasRepr Format := ⟨Format.pretty ∘ Format.repr⟩
def formatDataValue : DataValue → Format
| DataValue.ofString v => format (repr v)
| DataValue.ofBool v => format v
| DataValue.ofName v => "`" ++ format v
| DataValue.ofNat v => format v
| DataValue.ofInt v => format v
instance dataValueHasFormat : HasFormat DataValue := ⟨formatDataValue⟩
def formatEntry : Name × DataValue → Format
| (n, v) => format n ++ " := " ++ format v
instance entryHasFormat : HasFormat (Name × DataValue) := ⟨formatEntry⟩
def formatKVMap (m : KVMap) : Format :=
sbracket (Format.joinSep m.entries ", ")
instance kvMapHasFormat : HasFormat KVMap := ⟨formatKVMap⟩
end Lean