/- Copyright (c) 2018 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Sebastian Ullrich Term-Level parsers -/ prelude import init.lean.parser.level init.lean.parser.notation import init.lean.expr namespace Lean namespace Parser open Combinators Parser.HasView MonadParsec local postfix `?`:10000 := optional local postfix *:10000 := Combinators.many local postfix +:10000 := Combinators.many1 set_option class.instance_max_depth 200 @[derive Parser.HasTokens Parser.HasView] def identUnivSpec.Parser : basicParser := node! identUnivSpec [".{", levels: Level.Parser+, "}"] @[derive Parser.HasTokens Parser.HasView] def identUnivs.Parser : termParser := node! identUnivs [id: ident.Parser, univs: (monadLift identUnivSpec.Parser)?] namespace Term /-- Access leading Term -/ def getLeading : trailingTermParser := read instance : HasTokens getLeading := default _ instance : HasView Syntax getLeading := default _ @[derive Parser.HasTokens Parser.HasView] def paren.Parser : termParser := node! «paren» ["(":maxPrec, content: node! parenContent [ Term: Term.Parser, special: nodeChoice! parenSpecial { /- Do not allow trailing comma. Looks a bit weird and would clash with adding support for tuple sections (https://downloads.haskell.org/~ghc/8.2.1/docs/html/usersGuide/glasgowExts.html#tuple-sections). -/ tuple: node! tuple [", ", tail: sepBy (Term.Parser 0) (symbol ", ") false], typed: node! typed [" : ", type: Term.Parser], }?, ]?, ")" ] @[derive Parser.HasTokens Parser.HasView] def hole.Parser : termParser := node! hole [hole: symbol "_" maxPrec] @[derive Parser.HasTokens Parser.HasView] def sort.Parser : termParser := nodeChoice! sort {"Sort":maxPrec, "Type":maxPrec} @[derive HasTokens HasView] def typeSpec.Parser : termParser := node! typeSpec [" : ", type: Term.Parser 0] @[derive HasTokens HasView] def optType.Parser : termParser := typeSpec.Parser? instance optType.viewDefault : HasViewDefault optType.Parser _ none := ⟨⟩ section binder @[derive HasTokens HasView] def binderIdent.Parser : termParser := nodeChoice! binderIdent {id: ident.Parser, hole: hole.Parser} @[derive HasTokens HasView] def binderDefault.Parser : termParser := nodeChoice! binderDefault { val: node! binderDefaultVal [":=", Term: Term.Parser 0], tac: node! binderDefaultTac [".", Term: Term.Parser 0], } @[derive HasTokens HasView] def binderContent.Parser (requireType := false) : termParser := node! binderContent [ ids: binderIdent.Parser+, type: optional typeSpec.Parser requireType, default: binderDefault.Parser? ] @[derive HasTokens HasView] def simpleBinder.Parser : termParser := nodeChoice! simpleBinder { explicit: node! simpleExplicitBinder ["(", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol ")"], implicit: node! simpleImplicitBinder ["{", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol "}"], strictImplicit: node! simpleStrictImplicitBinder ["⦃", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol "⦄"], instImplicit: node! simpleInstImplicitBinder ["[", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol "]"], } def simpleBinder.View.toBinderInfo : simpleBinder.View → (BinderInfo × SyntaxIdent × Syntax) | (simpleBinder.View.explicit {id := id, type := type}) := (BinderInfo.default, id, type) | (simpleBinder.View.implicit {id := id, type := type}) := (BinderInfo.implicit, id, type) | (simpleBinder.View.strictImplicit {id := id, type := type}) := (BinderInfo.strictImplicit, id, type) | (simpleBinder.View.instImplicit {id := id, type := type}) := (BinderInfo.instImplicit, id, type) @[derive Parser.HasTokens Parser.HasView] def anonymousConstructor.Parser : termParser := node! anonymousConstructor ["⟨":maxPrec, args: sepBy (Term.Parser 0) (symbol ","), "⟩"] /- All binders must be surrounded with some kind of bracket. (e.g., '()', '{}', '[]'). We use this feature when parsing examples/definitions/theorems. The goal is to avoid counter-intuitive declarations such as: example p : False := trivial def main proof : False := trivial which would be parsed as example (p : False) : _ := trivial def main (proof : False) : _ := trivial where `_` in both cases is elaborated into `True`. This issue was raised by @gebner in the slack channel. Remark: we still want implicit delimiters for lambda/pi expressions. That is, we want to write fun x : t, s or fun x, s instead of fun (x : t), s -/ @[derive HasTokens HasView] def bracketedBinder.Parser (requireType := false) : termParser := nodeChoice! bracketedBinder { explicit: node! explicitBinder ["(", content: nodeChoice! explicitBinderContent { «notation»: command.notationLike.Parser, other: binderContent.Parser requireType }, right: symbol ")"], implicit: node! implicitBinder ["{", content: binderContent.Parser, "}"], strictImplicit: node! strictImplicitBinder ["⦃", content: binderContent.Parser, "⦄"], instImplicit: node! instImplicitBinder ["[", content: nodeLongestChoice! instImplicitBinderContent { named: node! instImplicitNamedBinder [id: ident.Parser, " : ", type: Term.Parser 0], anonymous: node! instImplicitAnonymousBinder [type: Term.Parser 0] }, "]"], anonymousConstructor: anonymousConstructor.Parser, } @[derive HasTokens HasView] def binder.Parser : termParser := nodeChoice! binder { bracketed: bracketedBinder.Parser, unbracketed: binderContent.Parser, } @[derive HasTokens HasView] def bindersExt.Parser : termParser := node! bindersExt [ leadingIds: binderIdent.Parser*, remainder: nodeChoice! bindersRemainder { type: node! bindersTypes [":", type: Term.Parser 0], -- we allow mixing like in `a (b : β) c`, but not `a : α (b : β) c : γ` mixed: nodeChoice! mixedBinder { bracketed: bracketedBinder.Parser, id: binderIdent.Parser, }+, }? ] /-- We normalize binders to simpler singleton ones during expansion. -/ @[derive HasTokens HasView] def binders.Parser : termParser := nodeChoice! binders { extended: bindersExt.Parser, -- a strict subset of `extended`, so only useful after parsing simple: simpleBinder.Parser, } /-- We normalize binders to simpler ones during expansion. These always-bracketed binders are used in declarations and cannot be reduced to nested singleton binders. -/ @[derive HasTokens HasView] def bracketedBinders.Parser : termParser := nodeChoice! bracketedBinders { extended: bracketedBinder.Parser*, -- a strict subset of `extended`, so only useful after parsing simple: simpleBinder.Parser*, } end binder @[derive Parser.HasTokens Parser.HasView] def lambda.Parser : termParser := node! lambda [ op: unicodeSymbol "λ" "fun" maxPrec, binders: binders.Parser, ",", body: Term.Parser 0 ] @[derive Parser.HasTokens Parser.HasView] def assume.Parser : termParser := node! «assume» [ "assume ":maxPrec, binders: nodeChoice! assumeBinders { anonymous: node! assumeAnonymous [": ", type: Term.Parser], binders: binders.Parser }, ", ", body: Term.Parser 0 ] @[derive Parser.HasTokens Parser.HasView] def pi.Parser : termParser := node! pi [ op: anyOf [unicodeSymbol "Π" "Pi" maxPrec, unicodeSymbol "∀" "forall" maxPrec], binders: binders.Parser, ",", range: Term.Parser 0 ] @[derive Parser.HasTokens Parser.HasView] def explicit.Parser : termParser := node! explicit [ mod: nodeChoice! explicitModifier { explicit: symbol "@" maxPrec, partialExplicit: symbol "@@" maxPrec }, id: identUnivs.Parser ] @[derive Parser.HasTokens Parser.HasView] def from.Parser : termParser := node! «from» ["from ", proof: Term.Parser] @[derive Parser.HasTokens Parser.HasView] def let.Parser : termParser := node! «let» [ "let ", lhs: nodeChoice! letLhs { id: node! letLhsId [ id: ident.Parser, -- NOTE: after expansion, binders are Empty binders: bracketedBinder.Parser*, type: optType.Parser, ], pattern: Term.Parser }, " := ", value: Term.Parser, " in ", body: Term.Parser, ] @[derive Parser.HasTokens Parser.HasView] def optIdent.Parser : termParser := (try node! optIdent [id: ident.Parser, " : "])? @[derive Parser.HasTokens Parser.HasView] def have.Parser : termParser := node! «have» [ "have ", id: optIdent.Parser, prop: Term.Parser, proof: nodeChoice! haveProof { Term: node! haveTerm [" := ", Term: Term.Parser], «from»: node! haveFrom [", ", «from»: from.Parser], }, ", ", body: Term.Parser, ] @[derive Parser.HasTokens Parser.HasView] def show.Parser : termParser := node! «show» [ "show ", prop: Term.Parser, ", ", «from»: from.Parser, ] @[derive Parser.HasTokens Parser.HasView] def match.Parser : termParser := node! «match» [ "match ", scrutinees: sepBy1 Term.Parser (symbol ", ") false, type: optType.Parser, " with ", optBar: (symbol " | ")?, equations: sepBy1 node! «matchEquation» [ lhs: sepBy1 Term.Parser (symbol ", ") false, ":=", rhs: Term.Parser] (symbol " | ") false, ] @[derive Parser.HasTokens Parser.HasView] def if.Parser : termParser := node! «if» [ "if ", id: optIdent.Parser, prop: Term.Parser, " then ", thenBranch: Term.Parser, " else ", elseBranch: Term.Parser, ] @[derive Parser.HasTokens Parser.HasView] def structInst.Parser : termParser := node! structInst [ "{":maxPrec, type: (try node! structInstType [id: ident.Parser, " . "])?, «with»: (try node! structInstWith [source: Term.Parser, " with "])?, items: sepBy nodeChoice! structInstItem { field: node! structInstField [id: ident.Parser, " := ", val: Term.Parser], source: node! structInstSource ["..", source: Term.Parser?], } (symbol ", "), "}", ] @[derive Parser.HasTokens Parser.HasView] def Subtype.Parser : termParser := node! Subtype [ "{":maxPrec, id: ident.Parser, type: optType.Parser, "//", prop: Term.Parser, "}" ] @[derive Parser.HasTokens Parser.HasView] def inaccessible.Parser : termParser := node! inaccessible [".(":maxPrec, Term: Term.Parser, ")"] @[derive Parser.HasTokens Parser.HasView] def anonymousInaccessible.Parser : termParser := node! anonymousInaccessible ["._":maxPrec] @[derive Parser.HasTokens Parser.HasView] def sorry.Parser : termParser := node! «sorry» ["sorry":maxPrec] def borrowPrec := maxPrec - 1 @[derive Parser.HasTokens Parser.HasView] def borrowed.Parser : termParser := node! borrowed ["@&":maxPrec, Term: Term.Parser borrowPrec] --- Agda's `(x : e) → f` @[derive Parser.HasTokens Parser.HasView] def depArrow.Parser : termParser := node! depArrow [binder: bracketedBinder.Parser true, op: unicodeSymbol "→" "->" 25, range: Term.Parser 24] -- TODO(Sebastian): replace with attribute @[derive HasTokens] def builtinLeadingParsers : TokenMap termParser := TokenMap.ofList [ (`ident, identUnivs.Parser), (number.name, number.Parser), (stringLit.name, stringLit.Parser), ("(", paren.Parser), ("(", depArrow.Parser), ("_", hole.Parser), ("Sort", sort.Parser), ("Type", sort.Parser), ("λ", lambda.Parser), ("fun", lambda.Parser), ("Π", pi.Parser), ("Pi", pi.Parser), ("∀", pi.Parser), ("forall", pi.Parser), ("⟨", anonymousConstructor.Parser), ("@", explicit.Parser), ("@@", explicit.Parser), ("let", let.Parser), ("have", have.Parser), ("show", show.Parser), ("assume", assume.Parser), ("match", match.Parser), ("if", if.Parser), ("{", structInst.Parser), ("{", Subtype.Parser), ("{", depArrow.Parser), ("[", depArrow.Parser), (".(", inaccessible.Parser), ("._", anonymousInaccessible.Parser), ("sorry", sorry.Parser), ("@&", borrowed.Parser) ] @[derive Parser.HasTokens Parser.HasView] def sortApp.Parser : trailingTermParser := do { l ← getLeading, guard $ l.isOfKind sort } *> node! sortApp [fn: getLeading, Arg: monadLift (Level.Parser maxPrec).run] @[derive Parser.HasTokens Parser.HasView] def app.Parser : trailingTermParser := node! app [fn: getLeading, Arg: Term.Parser maxPrec] def mkApp (fn : Syntax) (args : List Syntax) : Syntax := args.foldl (λ fn Arg, Syntax.mkNode app [fn, Arg]) fn @[derive Parser.HasTokens Parser.HasView] def arrow.Parser : trailingTermParser := node! arrow [dom: getLeading, op: unicodeSymbol "→" "->" 25, range: Term.Parser 24] @[derive Parser.HasView] def projection.Parser : trailingTermParser := try $ node! projection [ Term: getLeading, -- do not consume trailing whitespace «.»: rawStr ".", proj: nodeChoice! projectionSpec { id: Parser.ident.Parser, num: number.Parser, }, ] -- register '.' manually because of `rawStr` instance projection.tokens : HasTokens projection.Parser := /- Use maxPrec + 1 so that it bind more tightly than application: `a (b).c` should be parsed as `a ((b).c)`. -/ ⟨[{«prefix» := ".", lbp := maxPrec.succ}]⟩ @[derive HasTokens] def builtinTrailingParsers : TokenMap trailingTermParser := TokenMap.ofList [ ("→", arrow.Parser), ("->", arrow.Parser), (".", projection.Parser) ] end Term private def trailing (cfg : CommandParserConfig) : trailingTermParser := -- try local parsers first, starting with the newest one (do ps ← indexed cfg.localTrailingTermParsers, ps.foldr (<|>) (error "")) <|> -- next try all non-local parsers (do ps ← indexed cfg.trailingTermParsers, longestMatch ps) <|> -- The application parsers should only be tried as a fall-back; -- e.g. `a + b` should not be parsed as `a (+ b)`. -- TODO(Sebastian): We should be able to remove this workaround using -- the proposed more robust precedence handling anyOf [Term.sortApp.Parser, Term.app.Parser] private def leading (cfg : CommandParserConfig) : termParser := (do ps ← indexed cfg.localLeadingTermParsers, ps.foldr (<|>) (error "")) <|> (do ps ← indexed cfg.leadingTermParsers, longestMatch ps) def termParser.run (p : termParser) : commandParser := do cfg ← read, adaptReader coe $ prattParser (leading cfg) (trailing cfg) p end Parser end Lean