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} @[inline] def parserM.run (p : parserM σ δ μ α) (st : σ) (bst : δ) (s : String) : Result σ δ μ α := p s (mkInput 0 st bst) @[inline] def parserM.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 @[inlineIfReduce] def strictOr (b₁ b₂ : Bool) := b₁ || b₂ @[inlineIfReduce] def strictAnd (b₁ b₂ : Bool) := b₁ && b₂ @[inline] def parserM.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 parserMIsMonad : 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 parserM.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 parserM.failure : parserM σ δ μ α := λ _ inp, mkError inp "failure" instance : Alternative (parserM σ δ μ) := { orelse := @parserM.orelse _ _ _, failure := @parserM.failure _ _ _, .. Parser.parserMIsMonad } 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 namespace Prim @[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] def takeUntilAux (p : Char → Bool) : Nat → parserM σ δ μ Unit | 0 str inp := inp.val | (n+1) 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 n 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.length str inp def strAux (inS : String) (s : String) (errorMsg : String) : Nat → input σ δ μ → Pos → Result σ δ μ Unit | 0 inp j := mkError inp errorMsg | (n+1) inp j := if s.atEnd j then inp.val else match inp with | ⟨Result.ok _ i st bst e, _⟩ := if inS.atEnd i then mkError inp errorMsg else if inS.get i = s.get j then strAux n (mkInput (inS.next i) st bst false) (s.next j) else mkError inp errorMsg | ⟨Result.error _ _ _ _ _, h⟩ := unreachableError h @[inline] def str (s : String) : parserM σ δ μ Unit := λ inStr inp, strAux inStr s ("expected '" ++ repr s ++ "'") inStr.length inp 0 @[specialize] def manyLoop (a : α) (p : parserM σ δ μ α) : Nat → Bool → parserM σ δ μ α | 0 fst := pure a | (k+1) fst := λ str inp, match inp with | ⟨Result.ok _ i₀ _ bst₀ _, _⟩ := (match p str inp with | Result.ok _ i st bst _ := manyLoop k 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 σ δ μ α := λ str inp, manyLoop a p str.length true str inp @[inline] def many (p : parserM σ δ μ Unit) : parserM σ δ μ Unit := manyAux () p @[inline] def many1 (p : parserM σ δ μ Unit) : parserM σ δ μ Unit := p *> many p end Prim 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 (Prim.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 (Prim.takeUntil p) @[inline] def str (s : String) : m Unit := monadParser.lift (Prim.str s) @[inline] def lookahead (p : m α) : m α := monadParser.map (λ β p, Prim.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 Prim.many p @[inline] def many1 (p : m Unit) : m Unit := monadParserAux.map Prim.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₂)