lean4-htt/tests/playground/parser.lean
2019-03-28 10:32:39 -07:00

254 lines
9.4 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.

namespace Lean
namespace Parser
abbrev Pos := String.Pos
/-
σ is the non-backtrackable State
δ is the backtrackable State
μ is the extra error Message data
-/
inductive Result (σ δ μ α : Type)
| ok {} (a : α) (i : Pos) (st : σ) (bst : δ) (eps : Bool) : Result
| error {} (msg : String) (i : Pos) (st : σ) (extra : Option μ) (eps : Bool) : Result
inductive Result.IsOk {σ δ μ α : Type} : Result σ δ μ α → Prop
| mk (a : α) (i : Pos) (st : σ) (bst : δ) (eps : Bool) : Result.IsOk (Result.ok a i st bst eps)
theorem errorIsNotOk {σ δ μ α : Type} {msg : String} {i : Pos} {st : σ} {extra : Option μ} {eps : Bool}
(h : Result.IsOk (@Result.error σ δ μ α msg i st extra eps)) : False :=
match h with end
@[inline] def unreachableError {σ δ μ α β : Type} {msg : String} {i : Pos} {st : σ} {extra : Option μ} {eps : Bool}
(h : Result.IsOk (@Result.error σ δ μ α msg i st extra eps)) : β :=
False.elim (errorIsNotOk h)
def input (σ δ μ : Type) : Type := { r : Result σ δ μ Unit // r.IsOk }
@[inline] def mkInput {σ δ μ : Type} (i : Pos) (st : σ) (bst : δ) (eps := true) : input σ δ μ :=
⟨Result.ok () i st bst eps, Result.IsOk.mk _ _ _ _ _⟩
def ParserM (σ δ μ α : Type) :=
String → input σ δ μ → Result σ δ μ α
variables {σ δ μ α β : Type}
namespace ParserM
protected def default : ParserM σ δ μ α :=
λ _ inp,
match inp with
| ⟨Result.ok _ i st bst _, h⟩ := Result.error "" i st none true
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
instance : Inhabited (ParserM σ δ μ α) :=
⟨ParserM.default⟩
@[inline] def run (p : ParserM σ δ μ α) (st : σ) (bst : δ) (s : String) : Result σ δ μ α :=
p s (mkInput 0 st bst)
@[inline] def pure (a : α) : ParserM σ δ μ α :=
λ _ inp,
match inp with
| ⟨Result.ok _ it st bst _, h⟩ := Result.ok a it st bst true
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[inline] def bind (x : ParserM σ δ μ α) (f : α → ParserM σ δ μ β) : ParserM σ δ μ β :=
λ str inp,
match x str inp with
| Result.ok a i st bst e₁ :=
(match f a str (mkInput i st bst) with
| Result.ok b i st bst e₂ := Result.ok b i st bst (strictAnd e₁ e₂)
| Result.error msg i st ext e₂ := Result.error msg i st ext (strictAnd e₁ e₂))
| Result.error msg i st etx e := Result.error msg i st etx e
instance isMonad : Monad (ParserM σ δ μ) :=
{pure := @ParserM.pure _ _ _, bind := @ParserM.bind _ _ _}
def mkError (r : input σ δ μ) (msg : String) (eps := true) : Result σ δ μ α :=
match r with
| ⟨Result.ok _ i c s _, _⟩ := Result.error msg i c none eps
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[inline] def orelse (p q : ParserM σ δ μ α) : ParserM σ δ μ α :=
λ str inp,
match inp with
| ⟨Result.ok _ i₁ _ bst₁ _, _⟩ :=
(match p str inp with
| Result.error _ _ st₂ _ tt := q str (mkInput i₁ st₂ bst₁)
| other := other)
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[inline] def failure : ParserM σ δ μ α :=
λ _ inp, mkError inp "failure"
instance : Alternative (ParserM σ δ μ) :=
{ orelse := @ParserM.orelse _ _ _,
failure := @ParserM.failure _ _ _,
.. ParserM.isMonad }
def setSilentError : Result σ δ μ α → Result σ δ μ α
| (Result.error msg i st ext _) := Result.error msg i st ext true
| other := other
@[inline] def currPos : input σ δ μ → Pos
| ⟨Result.ok _ i _ _ _, _⟩ := i
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[inline] def try {α : Type} (p : ParserM σ δ μ α) : ParserM σ δ μ α :=
λ str inp, setSilentError (p str inp)
@[inline] def lookahead (p : ParserM σ δ μ α) : ParserM σ δ μ α :=
λ str inp,
match inp with
| ⟨Result.ok _ i _ bst _, _⟩ :=
(match p str inp with
| Result.ok a _ st _ _ := Result.ok a i st bst true
| other := other)
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[specialize] def satisfy (p : Char → Bool) : ParserM σ δ μ Char :=
λ str inp,
match inp with
| ⟨Result.ok _ i st bst _, _⟩ :=
if str.atEnd i then mkError inp "end of input"
else let c := str.get i in
if p c then Result.ok c (str.next i) st bst false
else mkError inp "unexpected character"
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[specialize] partial def takeUntilAux (p : Char → Bool) : ParserM σ δ μ Unit
| str inp :=
match inp with
| ⟨Result.ok _ i st bst _, _⟩ :=
if str.atEnd i then inp.val
else let c := str.get i in
if p c then inp.val
else takeUntilAux str (mkInput (str.next i) st bst false)
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[inline] def takeUntil (p : Char → Bool) : ParserM σ δ μ Unit :=
λ str inp, takeUntilAux p str inp
partial def strAux (s : String) (errorMsg : String) : Pos → ParserM σ δ μ Unit
| j str inp :=
if s.atEnd j then inp.val
else match inp with
| ⟨Result.ok _ i st bst _, _⟩ :=
if str.atEnd i then mkError inp errorMsg
else if s.get j == str.get i then strAux (s.next j) str (mkInput (str.next i) st bst false)
else mkError inp errorMsg
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
@[inline] def str (s : String) : ParserM σ δ μ Unit :=
strAux s ("expected '" ++ repr s ++ "'") 0
@[specialize] partial def manyLoop (a : α) (p : ParserM σ δ μ α) : Bool → ParserM σ δ μ α
| fst str inp :=
match inp with
| ⟨Result.ok _ i₀ _ bst₀ _, _⟩ :=
(match p str inp with
| Result.ok _ i st bst _ := manyLoop false str (mkInput i st bst)
| Result.error _ _ st _ _ := Result.ok a i₀ st bst₀ fst)
| ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h
-- Auxiliary Function used to lift manyAux
@[inline] def manyAux (a : α) (p : ParserM σ δ μ α) : ParserM σ δ μ α :=
manyLoop a p true
@[inline] def many (p : ParserM σ δ μ Unit) : ParserM σ δ μ Unit :=
manyAux () p
@[inline] def many1 (p : ParserM σ δ μ Unit) : ParserM σ δ μ Unit :=
p *> many p
end ParserM
class monadParser (σ : outParam Type) (δ : outParam Type) (μ : outParam Type) (m : Type → Type) :=
(lift {} {α : Type} : ParserM σ δ μ α → m α)
(map {} {α : Type} : (Π β, ParserM σ δ μ β → ParserM σ δ μ β) → m α → m α)
instance monadParserBase : monadParser σ δ μ (ParserM σ δ μ) :=
{ lift := λ α, id,
map := λ α f x, f α x }
instance monadParserTrans {m n : Type → Type} [HasMonadLift m n] [MonadFunctor m m n n] [monadParser σ δ μ m] : monadParser σ δ μ n :=
{ lift := λ α p, monadLift (monadParser.lift p : m α),
map := λ α f x, monadMap (λ β x, (monadParser.map @f x : m β)) x }
class monadParserAux (σ : outParam Type) (δ : outParam Type) (μ : outParam Type) (m : Type → Type) :=
(map {} {α : Type} : (ParserM σ δ μ α → ParserM σ δ μ α) → m α → m α)
instance monadParserAuxBase : monadParserAux σ δ μ (ParserM σ δ μ) :=
{ map := λ α, id }
instance monadParserAuxReader {m : Type → Type} {ρ : Type} [Monad m] [monadParserAux σ δ μ m] : monadParserAux σ δ μ (ReaderT ρ m) :=
{ map := λ α f x r, (monadParserAux.map f : m α → m α) (x r) }
section
variables {m : Type → Type} [monadParser σ δ μ m]
@[inline] def satisfy (p : Char → Bool) : m Char := monadParser.lift (ParserM.satisfy p)
def ch (c : Char) : m Char := satisfy (= c)
def alpha : m Char := satisfy Char.isAlpha
def digit : m Char := satisfy Char.isDigit
def upper : m Char := satisfy Char.isUpper
def lower : m Char := satisfy Char.isLower
def any : m Char := satisfy (λ _, True)
@[inline] def takeUntil (p : Char → Bool) : m Unit := monadParser.lift (ParserM.takeUntil p)
@[inline] def str (s : String) : m Unit := monadParser.lift (ParserM.str s)
@[inline] def lookahead (p : m α) : m α :=
monadParser.map (λ β p, ParserM.lookahead p) p
@[inline] def takeWhile (p : Char → Bool) : m Unit := takeUntil (λ c, !p c)
@[inline] def whitespace : m Unit := takeWhile Char.isWhitespace
end
section
variables {m : Type → Type} [monadParserAux σ δ μ m]
@[inline] def many (p : m Unit) : m Unit := monadParserAux.map ParserM.many p
@[inline] def many1 (p : m Unit) : m Unit := monadParserAux.map ParserM.many1 p
end
end Parser
end Lean
abbrev Parser := ReaderT Nat (Lean.Parser.ParserM Unit Unit Unit) Unit
open Lean.Parser
-- setOption pp.implicit True
-- setOption Trace.Compiler.boxed True
def testP : Parser :=
many1 (str "++" <|> str "**" <|> (str "--" *> takeUntil (= '\n') *> any *> pure ()))
def testParser (x : Parser) (input : String) : String :=
match (x 0).run () () input with
| Lean.Parser.Result.ok _ i _ _ _ := "Ok at " ++ toString i
| Result.error msg i _ _ _ := "Error at " ++ toString i ++ ": " ++ msg
@[noinline] def test (s : String) : IO Unit :=
IO.println (testParser testP s)
def mkBigString : Nat → String → String
| 0 s := s
| (n+1) s := mkBigString n (s ++ "-- new comment\n")
def prof {α : Type} (msg : String) (p : IO α) : IO α :=
let msg₁ := "Time for '" ++ msg ++ "':" in
let msg₂ := "Memory usage for '" ++ msg ++ "':" in
allocprof msg₂ (timeit msg₁ p)
def main (xs : List String) : IO Unit :=
let s₁ := mkBigString xs.head.toNat "" in
let s₂ := s₁ ++ "bad" ++ mkBigString 20 "" in
prof "Parser 1" (test s₁) *>
prof "Parser 2" (test s₂)