lean4-htt/tmp/new-frontend/parser/combinators.lean
Leonardo de Moura 55626ba60d chore(library/init/lean): disable new frontend for now
We are going to start making drastic changes in the parser,
elaborator, attributes, etc. Examples:
- No View objects. I am going to implement match_syntax.
- No RecT in the parser. I am going to implement parser extensions
using an approach similar to the one I used to implement environment
extensions.
- No Parsec. I will use an approach similar to the one used in the
experiment https://github.com/leanprover/lean4/tree/master/tests/playground/parser

It is easier to perform these changes with the new frontend disabled.
I will slowly re-active it as I apply the changes.

cc @kha
2019-06-05 15:26:43 -07:00

247 lines
10 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: Sebastian Ullrich
Syntax-tree creating Parser Combinators
-/
prelude
import init.lean.parser.basic
import init.data.list.instances
namespace Lean
namespace Parser
namespace Combinators
open HasTokens HasView MonadParsec
variables {α : Type} {m : Type → Type}
local notation `Parser` := m Syntax
variables [Monad m] [MonadExcept (Parsec.Message Syntax) m] [MonadParsec Syntax m] [Alternative m]
def node (k : SyntaxNodeKind) (rs : List Parser) : Parser :=
do args ← rs.mfoldl (λ (args : List Syntax) r, do
-- on error, append partial Syntax tree to previous successful parses and rethrow
a ← catch r $ λ msg, match args with
-- do not wrap an error in the first argument to uphold the invariant documented at `SyntaxNode`
| [] := throw msg
| _ :=
let args := msg.custom.get :: args in
throw {msg with custom := Syntax.mkNode k args.reverse},
pure (a::args)
) [],
pure $ Syntax.mkNode k args.reverse
@[reducible] def seq : List Parser → Parser := node noKind
instance node.tokens (k) (rs : List Parser) [Parser.HasTokens rs] : Parser.HasTokens (node k rs) :=
⟨tokens rs⟩
instance node.view (k) (rs : List Parser) [i : HasView α k] : Parser.HasView α (node k rs) :=
{ view := i.view, review := i.review }
-- Each Parser Combinator comes equipped with `HasView` and `HasTokens` instances
private def many1Aux (p : Parser) : List Syntax → Nat → Parser
| as 0 := error "unreachable"
| as (n+1) := do
a ← catch p (λ msg, throw {msg with custom :=
-- append `Syntax.missing` to make clear that List is incomplete
Syntax.list (Syntax.missing::msg.custom.get::as).reverse}),
many1Aux (a::as) n <|> pure (Syntax.list (a::as).reverse)
def many1 (r : Parser) : Parser :=
do rem ← remaining, many1Aux r [] (rem+1)
instance many1.tokens (r : Parser) [Parser.HasTokens r] : Parser.HasTokens (many1 r) :=
⟨tokens r⟩
instance many1.view (r : Parser) [Parser.HasView α r] : Parser.HasView (List α) (many1 r) :=
{ view := λ stx, match stx.asNode with
| some n := n.args.map (HasView.view r)
| _ := [HasView.view r Syntax.missing],
review := λ as, Syntax.list $ as.map (review r) }
def many (r : Parser) : Parser :=
many1 r <|> pure (Syntax.list [])
instance many.tokens (r : Parser) [Parser.HasTokens r] : Parser.HasTokens (many r) :=
⟨tokens r⟩
instance many.view (r : Parser) [HasView α r] : Parser.HasView (List α) (many r) :=
/- Remark: `many1.view` can handle empty list. -/
{..many1.view r}
private def sepByAux (p : m Syntax) (sep : Parser) (allowTrailingSep : Bool) : Bool → List Syntax → Nat → Parser
| pOpt as 0 := error "unreachable"
| pOpt as (n+1) := do
let p := if pOpt then some <$> p <|> pure none else some <$> p,
some a ← catch p (λ msg, throw {msg with custom :=
-- append `Syntax.missing` to make clear that List is incomplete
Syntax.list (Syntax.missing::msg.custom.get::as).reverse})
| pure (Syntax.list as.reverse),
-- I don't want to think about what the output on a failed separator parse should look like
let sep := try sep,
some s ← some <$> sep <|> pure none
| pure (Syntax.list (a::as).reverse),
sepByAux allowTrailingSep (s::a::as) n
def sepBy (p sep : Parser) (allowTrailingSep := true) : Parser :=
do rem ← remaining, sepByAux p sep allowTrailingSep true [] (rem+1)
def sepBy1 (p sep : Parser) (allowTrailingSep := true) : Parser :=
do rem ← remaining, sepByAux p sep allowTrailingSep false [] (rem+1)
instance sepBy.tokens (p sep : Parser) (a) [Parser.HasTokens p] [Parser.HasTokens sep] :
Parser.HasTokens (sepBy p sep a) :=
⟨tokens p ++ tokens sep⟩
structure SepBy.Elem.View (α β : Type) :=
(item : α)
(separator : Option β := none)
instance SepBy.Elem.View.itemCoe {α β : Type} : HasCoeT α (SepBy.Elem.View α β) :=
⟨λ a, ⟨a, none⟩⟩
private def sepBy.viewAux {α β} (p sep : Parser) [Parser.HasView α p] [Parser.HasView β sep] :
List Syntax → List (SepBy.Elem.View α β)
| [] := []
| [stx] := [⟨HasView.view p stx, none⟩]
| (stx1::stx2::stxs) := ⟨HasView.view p stx1, some $ HasView.view sep stx2⟩::sepBy.viewAux stxs
instance sepBy.view {α β} (p sep : Parser) (a) [Parser.HasView α p] [Parser.HasView β sep] :
Parser.HasView (List (SepBy.Elem.View α β)) (sepBy p sep a) :=
{ view := λ stx, match stx.asNode with
| some n := sepBy.viewAux p sep n.args
| _ := [⟨view p Syntax.missing, none⟩],
review := λ as, Syntax.list $ as.bind (λ a, match a with
| ⟨v, some vsep⟩ := [review p v, review sep vsep]
| ⟨v, none⟩ := [review p v]) }
instance sepBy1.tokens (p sep : Parser) (a) [Parser.HasTokens p] [Parser.HasTokens sep] :
Parser.HasTokens (sepBy1 p sep a) :=
⟨tokens (sepBy p sep a)⟩
instance sepBy1.View {α β} (p sep : Parser) (a) [Parser.HasView α p] [Parser.HasView β sep] :
Parser.HasView (List (SepBy.Elem.View α β)) (sepBy1 p sep a) :=
{..sepBy.view p sep a}
/-- Optionally parse `r`. `require` can be used to conditionally override the
behavior without changing the structure of the syntax tree. -/
def optional (r : Parser) (require := false) : Parser :=
if require then r else
do r ← optional $
-- on error, wrap in "some"
catch r (λ msg, throw {msg with custom := Syntax.list [msg.custom.get]}),
pure $ match r with
| some r := Syntax.list [r]
| none := Syntax.list []
instance optional.tokens (r : Parser) [Parser.HasTokens r] (req) : Parser.HasTokens (optional r req) :=
⟨tokens r⟩
instance optional.view (r : Parser) [Parser.HasView α r] (req) : Parser.HasView (Option α) (optional r req) :=
{ view := λ stx, match stx.asNode with
| some {args := [], ..} := none
| some {args := [stx], ..} := some $ HasView.view r stx
| _ := some $ view r Syntax.missing,
review := λ a, match a with
| some a := Syntax.list [review r a]
| none := Syntax.list [] }
instance optional.viewDefault (r : Parser) [Parser.HasView α r] (req) : Parser.HasViewDefault (optional r req) (Option α) none := ⟨⟩
/-- Parse a List `[p1, ..., pn]` of parsers as `p1 <|> ... <|> pn`.
Note that there is NO explicit encoding of which Parser was chosen;
parsers should instead produce distinct Node names for disambiguation. -/
def anyOf (rs : List Parser) : Parser :=
match rs with
| [] := error "anyOf"
| (r::rs) := rs.foldl (<|>) r
instance anyOf.tokens (rs : List Parser) [Parser.HasTokens rs] : Parser.HasTokens (anyOf rs) :=
⟨tokens rs⟩
instance anyOf.view (rs : List Parser) : Parser.HasView Syntax (anyOf rs) := default _
/-- Parse a List `[p1, ..., pn]` of parsers with `MonadParsec.longestMatch`.
If the Result is ambiguous, wrap it in a `choice` Node.
Note that there is NO explicit encoding of which Parser was chosen;
parsers should instead produce distinct Node names for disambiguation. -/
def longestMatch (rs : List Parser) : Parser :=
do stxs ← MonadParsec.longestMatch rs,
match stxs with
| [stx] := pure stx
| _ := pure $ Syntax.mkNode choice stxs
instance longestMatch.tokens (rs : List Parser) [Parser.HasTokens rs] : Parser.HasTokens (longestMatch rs) :=
⟨tokens rs⟩
instance longestMatch.view (rs : List Parser) : Parser.HasView Syntax (longestMatch rs) := default _
def choiceAux : List Parser → Nat → Parser
| [] _ := error "choice: Empty List"
| (r::rs) i :=
do { stx ← r,
pure $ Syntax.mkNode ⟨Name.mkNumeral Name.anonymous i⟩ [stx] }
<|> choiceAux rs (i+1)
/-- Parse a List `[p1, ..., pn]` of parsers as `p1 <|> ... <|> pn`.
The Result will be wrapped in a Node with the index of the successful
Parser as the Name.
Remark: Does not have a `HasView` instance because we only use it in `nodeChoice!` macros
that define their own views. -/
def choice (rs : List Parser) : Parser :=
choiceAux rs 0
instance choice.tokens (rs : List Parser) [Parser.HasTokens rs] : Parser.HasTokens (choice rs) :=
⟨tokens rs⟩
/-- Like `choice`, but using `longestMatch`. Does not create choice nodes, prefers the first successful Parser. -/
def longestChoice (rs : List Parser) : Parser :=
do stx::stxs ← MonadParsec.longestMatch $ rs.enum.map $ λ ⟨i, r⟩, do {
stx ← r,
pure $ Syntax.mkNode ⟨Name.mkNumeral Name.anonymous i⟩ [stx]
} | error "unreachable",
pure stx
instance longestChoice.tokens (rs : List Parser) [Parser.HasTokens rs] : Parser.HasTokens (longestChoice rs) :=
⟨tokens rs⟩
instance try.tokens (r : Parser) [Parser.HasTokens r] : Parser.HasTokens (try r) :=
⟨tokens r⟩
instance try.view (r : Parser) [i : Parser.HasView α r] : Parser.HasView α (try r) :=
{..i}
instance label.tokens (r : Parser) (l) [Parser.HasTokens r] : Parser.HasTokens (label r l) :=
⟨tokens r⟩
instance label.view (r : Parser) (l) [i : Parser.HasView α r] : Parser.HasView α (label r l) :=
{..i}
instance recurse.tokens (α δ m a) [MonadRec α δ m] : Parser.HasTokens (recurse a : m δ) :=
default _ -- recursive use should not contribute any new tokens
instance recurse.view (α δ m a) [MonadRec α δ m] : Parser.HasView Syntax (recurse a : m δ) := default _
instance monadLift.tokens {m' : Type → Type} [HasMonadLiftT m m'] (r : m Syntax) [Parser.HasTokens r] :
Parser.HasTokens (monadLift r : m' Syntax) :=
⟨tokens r⟩
instance monadLift.view {m' : Type → Type} [HasMonadLiftT m m'] (r : m Syntax) [i : Parser.HasView α r] :
Parser.HasView α (monadLift r : m' Syntax) :=
{..i}
instance seqLeft.tokens {α : Type} (x : m α) (p : m Syntax) [Parser.HasTokens p] : Parser.HasTokens (p <* x) :=
⟨tokens p⟩
instance seqLeft.view {α β : Type} (x : m α) (p : m Syntax) [i : Parser.HasView β p] : Parser.HasView β (p <* x) :=
{..i}
instance seqRight.tokens {α : Type} (x : m α) (p : m Syntax) [Parser.HasTokens p] : Parser.HasTokens (x *> p) :=
⟨tokens p⟩
instance seqRight.view {α β : Type} (x : m α) (p : m Syntax) [i : Parser.HasView β p] : Parser.HasView β (x *> p) :=
{..i}
instance coe.tokens {β} (r : Parser) [Parser.HasTokens r] [HasCoeT Parser β]: Parser.HasTokens (coe r : β) :=
⟨tokens r⟩
instance coe.view {β} (r : Parser) [i : Parser.HasView α r] [HasCoeT Parser β] : Parser.HasView α (coe r : β) :=
{..i}
instance coe.viewDefault {β} (d : α) (r : Parser) [HasView α r] [Parser.HasViewDefault r α d] [HasCoeT Parser β] : Parser.HasViewDefault (coe r : β) α d := ⟨⟩
end Combinators
end Parser
end Lean