/- 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.has_view monad_parsec local postfix `?`:10000 := optional local postfix *:10000 := combinators.many local postfix +:10000 := combinators.many1 set_option class.instance_max_depth 200 @[derive parser.has_tokens parser.has_view] def ident_univ_spec.parser : basic_parser := node! ident_univ_spec [".{", levels: level.parser+, "}"] @[derive parser.has_tokens parser.has_view] def ident_univs.parser : term_parser := node! ident_univs [id: ident.parser, univs: (monad_lift ident_univ_spec.parser)?] namespace term /-- Access leading term -/ def get_leading : trailing_term_parser := read instance : has_tokens get_leading := default _ instance : has_view syntax get_leading := default _ @[derive parser.has_tokens parser.has_view] def paren.parser : term_parser := node! «paren» ["(":max_prec, content: node! paren_content [ term: term.parser, special: node_choice! paren_special { /- 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/users_guide/glasgow_exts.html#tuple-sections). -/ tuple: node! tuple [", ", tail: sep_by (term.parser 0) (symbol ", ") ff], typed: node! typed [" : ", type: term.parser], }?, ]?, ")" ] @[derive parser.has_tokens parser.has_view] def hole.parser : term_parser := node! hole [hole: symbol "_" max_prec] @[derive parser.has_tokens parser.has_view] def sort.parser : term_parser := node_choice! sort {"Sort":max_prec, "Type":max_prec} @[derive has_tokens has_view] def type_spec.parser : term_parser := node! type_spec [" : ", type: term.parser 0] @[derive has_tokens has_view] def opt_type.parser : term_parser := type_spec.parser? instance opt_type.view_default : has_view_default opt_type.parser _ none := ⟨⟩ section binder @[derive has_tokens has_view] def binder_ident.parser : term_parser := node_choice! binder_ident {id: ident.parser, hole: hole.parser} @[derive has_tokens has_view] def binder_default.parser : term_parser := node_choice! binder_default { val: node! binder_default_val [":=", term: term.parser 0], tac: node! binder_default_tac [".", term: term.parser 0], } @[derive has_tokens has_view] def binder_content.parser : term_parser := node! binder_content [ ids: binder_ident.parser+, type: opt_type.parser, default: binder_default.parser? ] @[derive has_tokens has_view] def simple_binder.parser : term_parser := node_choice! simple_binder { explicit: node! simple_explicit_binder ["(", id: ident.parser, " : ", type: term.parser 0, right: symbol ")"], implicit: node! simple_implicit_binder ["{", id: ident.parser, " : ", type: term.parser 0, right: symbol "}"], strict_implicit: node! simple_strict_implicit_binder ["⦃", id: ident.parser, " : ", type: term.parser 0, right: symbol "⦄"], inst_implicit: node! simple_inst_implicit_binder ["[", id: ident.parser, " : ", type: term.parser 0, right: symbol "]"], } def simple_binder.view.to_binder_info : simple_binder.view → (binder_info × syntax_ident × syntax) | (simple_binder.view.explicit {id := id, type := type}) := (binder_info.default, id, type) | (simple_binder.view.implicit {id := id, type := type}) := (binder_info.implicit, id, type) | (simple_binder.view.strict_implicit {id := id, type := type}) := (binder_info.strict_implicit, id, type) | (simple_binder.view.inst_implicit {id := id, type := type}) := (binder_info.inst_implicit, id, type) @[derive parser.has_tokens parser.has_view] def anonymous_constructor.parser : term_parser := node! anonymous_constructor ["⟨":max_prec, args: sep_by (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 has_tokens has_view] def bracketed_binder.parser : term_parser := node_choice! bracketed_binder { explicit: node! explicit_binder ["(", content: node_choice! explicit_binder_content { «notation»: command.notation_like.parser, other: binder_content.parser }, right: symbol ")"], implicit: node! implicit_binder ["{", content: binder_content.parser, "}"], strict_implicit: node! strict_implicit_binder ["⦃", content: binder_content.parser, "⦄"], inst_implicit: node! inst_implicit_binder ["[", content: node_longest_choice! inst_implicit_binder_content { named: node! inst_implicit_named_binder [id: ident.parser, " : ", type: term.parser 0], anonymous: node! inst_implicit_anonymous_binder [type: term.parser 0] }, "]"], anonymous_constructor: anonymous_constructor.parser, } @[derive has_tokens has_view] def binder.parser : term_parser := node_choice! binder { bracketed: bracketed_binder.parser, unbracketed: binder_content.parser, } @[derive has_tokens has_view] def binders_ext.parser : term_parser := node! binders_ext [ leading_ids: binder_ident.parser*, remainder: node_choice! binders_remainder { type: node! binders_types [":", type: term.parser 0], -- we allow mixing like in `a (b : β) c`, but not `a : α (b : β) c : γ` mixed: node_choice! mixed_binder { bracketed: bracketed_binder.parser, id: binder_ident.parser, }+, }? ] /-- We normalize binders to simpler singleton ones during expansion. -/ @[derive has_tokens has_view] def binders.parser : term_parser := node_choice! binders { extended: binders_ext.parser, -- a strict subset of `extended`, so only useful after parsing simple: simple_binder.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 has_tokens has_view] def bracketed_binders.parser : term_parser := node_choice! bracketed_binders { extended: bracketed_binder.parser*, -- a strict subset of `extended`, so only useful after parsing simple: simple_binder.parser*, } end binder @[derive parser.has_tokens parser.has_view] def lambda.parser : term_parser := node! lambda [ op: unicode_symbol "λ" "fun" max_prec, binders: binders.parser, ",", body: term.parser 0 ] @[derive parser.has_tokens parser.has_view] def assume.parser : term_parser := node! «assume» [ "assume ":max_prec, binders: node_choice! assume_binders { anonymous: node! assume_anonymous [": ", type: term.parser], binders: binders.parser }, ", ", body: term.parser 0 ] @[derive parser.has_tokens parser.has_view] def pi.parser : term_parser := node! pi [ op: any_of [unicode_symbol "Π" "Pi" max_prec, unicode_symbol "∀" "forall" max_prec], binders: binders.parser, ",", range: term.parser 0 ] @[derive parser.has_tokens parser.has_view] def explicit.parser : term_parser := node! explicit [ mod: node_choice! explicit_modifier { explicit: symbol "@" max_prec, partial_explicit: symbol "@@" max_prec }, id: ident_univs.parser ] @[derive parser.has_tokens parser.has_view] def from.parser : term_parser := node! «from» ["from ", proof: term.parser] @[derive parser.has_tokens parser.has_view] def let.parser : term_parser := node! «let» [ "let ", lhs: node_choice! let_lhs { id: node! let_lhs_id [ id: ident.parser, -- NOTE: after expansion, binders are empty binders: bracketed_binder.parser*, type: opt_type.parser, ], pattern: term.parser }, " := ", value: term.parser, " in ", body: term.parser, ] @[derive parser.has_tokens parser.has_view] def opt_ident.parser : term_parser := (try node! opt_ident [id: ident.parser, " : "])? @[derive parser.has_tokens parser.has_view] def have.parser : term_parser := node! «have» [ "have ", id: opt_ident.parser, prop: term.parser, proof: node_choice! have_proof { term: node! have_term [" := ", term: term.parser], «from»: node! have_from [", ", «from»: from.parser], }, ", ", body: term.parser, ] @[derive parser.has_tokens parser.has_view] def show.parser : term_parser := node! «show» [ "show ", prop: term.parser, ", ", «from»: from.parser, ] @[derive parser.has_tokens parser.has_view] def match.parser : term_parser := node! «match» [ "match ", scrutinees: sep_by1 term.parser (symbol ", ") ff, type: opt_type.parser, " with ", opt_bar: (symbol " | ")?, equations: sep_by1 node! «match_equation» [ lhs: sep_by1 term.parser (symbol ", ") ff, ":=", rhs: term.parser] (symbol " | ") ff, ] @[derive parser.has_tokens parser.has_view] def if.parser : term_parser := node! «if» [ "if ", id: opt_ident.parser, prop: term.parser, " then ", then_branch: term.parser, " else ", else_branch: term.parser, ] @[derive parser.has_tokens parser.has_view] def struct_inst.parser : term_parser := node! struct_inst [ "{":max_prec, type: (try node! struct_inst_type [id: ident.parser, " . "])?, «with»: (try node! struct_inst_with [source: term.parser, " with "])?, items: sep_by node_choice! struct_inst_item { field: node! struct_inst_field [id: ident.parser, " := ", val: term.parser], source: node! struct_inst_source ["..", source: term.parser?], } (symbol ", "), "}", ] @[derive parser.has_tokens parser.has_view] def subtype.parser : term_parser := node! subtype [ "{":max_prec, id: ident.parser, type: opt_type.parser, "//", prop: term.parser, "}" ] @[derive parser.has_tokens parser.has_view] def inaccessible.parser : term_parser := node! inaccessible [".(":max_prec, term: term.parser, ")"] @[derive parser.has_tokens parser.has_view] def anonymous_inaccessible.parser : term_parser := node! anonymous_inaccessible ["._":max_prec] @[derive parser.has_tokens parser.has_view] def sorry.parser : term_parser := node! «sorry» ["sorry":max_prec] @[derive parser.has_tokens parser.has_view] def borrowed.parser : term_parser := node! borrowed ["@&":max_prec, term: term.parser] -- TODO(Sebastian): replace with attribute @[derive has_tokens] def builtin_leading_parsers : token_map term_parser := token_map.of_list [ (`ident, ident_univs.parser), (number.name, number.parser), (string_lit.name, string_lit.parser), ("(", paren.parser), ("_", hole.parser), ("Sort", sort.parser), ("Type", sort.parser), ("λ", lambda.parser), ("fun", lambda.parser), ("Π", pi.parser), ("Pi", pi.parser), ("∀", pi.parser), ("forall", pi.parser), ("⟨", anonymous_constructor.parser), ("@", explicit.parser), ("@@", explicit.parser), ("let", let.parser), ("have", have.parser), ("show", show.parser), ("assume", assume.parser), ("match", match.parser), ("if", if.parser), ("{", struct_inst.parser), ("{", subtype.parser), (".(", inaccessible.parser), ("._", anonymous_inaccessible.parser), ("sorry", sorry.parser), ("@&", borrowed.parser) ] @[derive parser.has_tokens parser.has_view] def sort_app.parser : trailing_term_parser := do { l ← get_leading, guard $ l.is_of_kind sort } *> node! sort_app [fn: get_leading, arg: monad_lift (level.parser max_prec).run] @[derive parser.has_tokens parser.has_view] def app.parser : trailing_term_parser := node! app [fn: get_leading, arg: term.parser max_prec] def mk_app (fn : syntax) (args : list syntax) : syntax := args.foldl (λ fn arg, syntax.mk_node app [fn, arg]) fn @[derive parser.has_tokens parser.has_view] def arrow.parser : trailing_term_parser := node! arrow [dom: get_leading, op: unicode_symbol "→" "->" 25, range: term.parser 24] @[derive parser.has_view] def projection.parser : trailing_term_parser := try $ node! projection [ term: get_leading, -- do not consume trailing whitespace «.»: raw_str ".", proj: node_choice! projection_spec { id: parser.ident.parser, num: number.parser, }, ] -- register '.' manually because of `raw_str` instance projection.tokens : has_tokens projection.parser := /- Use max_prec + 1 so that it bind more tightly than application: `a (b).c` should be parsed as `a ((b).c)`. -/ ⟨[{«prefix» := ".", lbp := max_prec.succ}]⟩ @[derive has_tokens] def builtin_trailing_parsers : token_map trailing_term_parser := token_map.of_list [ ("→", arrow.parser), ("->", arrow.parser), (".", projection.parser) ] end term private def trailing (cfg : command_parser_config) : trailing_term_parser := -- try local parsers first, starting with the newest one (do ps ← indexed cfg.local_trailing_term_parsers, ps.foldr (<|>) (error "")) <|> -- next try all non-local parsers (do ps ← indexed cfg.trailing_term_parsers, longest_match 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 any_of [term.sort_app.parser, term.app.parser] private def leading (cfg : command_parser_config) : term_parser := (do ps ← indexed cfg.local_leading_term_parsers, ps.foldr (<|>) (error "")) <|> (do ps ← indexed cfg.leading_term_parsers, longest_match ps) def term_parser.run (p : term_parser) : command_parser := do cfg ← read, adapt_reader coe $ pratt_parser (leading cfg) (trailing cfg) p end parser end lean