lean4-htt/library/init/lean/parser/term.lean

455 lines
14 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
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