lean4-htt/src/Lean/Elab/BuiltinNotation.lean
Sebastian Ullrich eb1e285e26 chore: style
2021-06-21 10:17:26 -07:00

306 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) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Init.Data.ToString
import Lean.Compiler.BorrowedAnnotation
import Lean.Meta.KAbstract
import Lean.Meta.Transform
import Lean.Elab.Term
import Lean.Elab.SyntheticMVars
namespace Lean.Elab.Term
open Meta
@[builtinTermElab anonymousCtor] def elabAnonymousCtor : TermElab := fun stx expectedType? =>
match stx with
| `(⟨$args,*⟩) => do
tryPostponeIfNoneOrMVar expectedType?
match expectedType? with
| some expectedType =>
let expectedType ← whnf expectedType
matchConstInduct expectedType.getAppFn
(fun _ => throwError "invalid constructor ⟨...⟩, expected type must be an inductive type {indentExpr expectedType}")
(fun ival us => do
match ival.ctors with
| [ctor] =>
let cinfo ← getConstInfoCtor ctor
let numExplicitFields ← forallTelescopeReducing cinfo.type fun xs _ => do
let mut n := 0
for i in [cinfo.numParams:xs.size] do
if (← getFVarLocalDecl xs[i]).binderInfo.isExplicit then
n := n + 1
return n
let args := args.getElems
if args.size < numExplicitFields then
throwError "invalid constructor ⟨...⟩, insufficient number of arguments, constructs '{ctor}' has #{numExplicitFields} explicit fields, but only #{args.size} provided"
let newStx ←
if args.size == numExplicitFields then
`($(mkCIdentFrom stx ctor) $(args)*)
else if numExplicitFields == 0 then
throwError "invalid constructor ⟨...⟩, insufficient number of arguments, constructs '{ctor}' does not have explicit fields, but #{args.size} provided"
else
let extra := args[numExplicitFields-1:args.size]
let newLast ← `(⟨$[$extra],*⟩)
let newArgs := args[0:numExplicitFields-1].toArray.push newLast
`($(mkCIdentFrom stx ctor) $(newArgs)*)
withMacroExpansion stx newStx $ elabTerm newStx expectedType?
| _ => throwError "invalid constructor ⟨...⟩, expected type must be an inductive type with only one constructor {indentExpr expectedType}")
| none => throwError "invalid constructor ⟨...⟩, expected type must be known"
| _ => throwUnsupportedSyntax
@[builtinTermElab borrowed] def elabBorrowed : TermElab := fun stx expectedType? =>
match stx with
| `(@& $e) => return markBorrowed (← elabTerm e expectedType?)
| _ => throwUnsupportedSyntax
@[builtinMacro Lean.Parser.Term.show] def expandShow : Macro := fun stx =>
match stx with
| `(show $type from $val) => let thisId := mkIdentFrom stx `this; `(let_fun $thisId : $type := $val; $thisId)
| `(show $type by%$b $tac:tacticSeq) => `(show $type from by%$b $tac:tacticSeq)
| _ => Macro.throwUnsupported
@[builtinMacro Lean.Parser.Term.have] def expandHave : Macro := fun stx =>
let thisId := mkIdentFrom stx `this
match stx with
| `(have $x $bs* $[: $type]? := $val $[;]? $body) => `(let_fun $x $bs* $[: $type]? := $val; $body)
| `(have $[: $type]? := $val $[;]? $body) => `(have $thisId:ident $[: $type]? := $val; $body)
| `(have $x $bs* $[: $type]? $alts:matchAlts $[;]? $body) => `(let_fun $x $bs* $[: $type]? $alts:matchAlts; $body)
| `(have $[: $type]? $alts:matchAlts $[;]? $body) => `(have $thisId:ident $[: $type]? $alts:matchAlts; $body)
| `(have $pattern:term $[: $type]? := $val:term $[;]? $body) => `(let_fun $pattern:term $[: $type]? := $val:term ; $body)
| _ => Macro.throwUnsupported
@[builtinMacro Lean.Parser.Term.suffices] def expandSuffices : Macro
| `(suffices $[$x :]? $type from $val $[;]? $body) => `(have $[$x]? : $type := $body; $val)
| `(suffices $[$x :]? $type by%$b $tac:tacticSeq $[;]? $body) => `(have $[$x]? : $type := $body; by%$b $tac:tacticSeq)
| _ => Macro.throwUnsupported
open Lean.Parser in
private def elabParserMacroAux (prec : Syntax) (e : Syntax) : TermElabM Syntax := do
let (some declName) ← getDeclName?
| throwError "invalid `leading_parser` macro, it must be used in definitions"
match extractMacroScopes declName with
| { name := Name.str _ s _, scopes := scps, .. } =>
let kind := quote declName
let s := quote s
-- if the parser decl is hidden by hygiene, it doesn't make sense to provide an antiquotation kind
let antiquotKind ← if scps == [] then `(some $kind) else `(none)
``(withAntiquot (mkAntiquot $s $antiquotKind) (leadingNode $kind $prec $e))
| _ => throwError "invalid `leading_parser` macro, unexpected declaration name"
@[builtinTermElab «leading_parser»] def elabLeadingParserMacro : TermElab :=
adaptExpander fun stx => match stx with
| `(leading_parser $e) => elabParserMacroAux (quote Parser.maxPrec) e
| `(leading_parser : $prec $e) => elabParserMacroAux prec e
| _ => throwUnsupportedSyntax
private def elabTParserMacroAux (prec lhsPrec : Syntax) (e : Syntax) : TermElabM Syntax := do
let declName? ← getDeclName?
match declName? with
| some declName => let kind := quote declName; ``(Lean.Parser.trailingNode $kind $prec $lhsPrec $e)
| none => throwError "invalid `trailing_parser` macro, it must be used in definitions"
@[builtinTermElab «trailing_parser»] def elabTrailingParserMacro : TermElab :=
adaptExpander fun stx => match stx with
| `(trailing_parser$[:$prec?]?$[:$lhsPrec?]? $e) =>
elabTParserMacroAux (prec?.getD <| quote Parser.maxPrec) (lhsPrec?.getD <| quote 0) e
| _ => throwUnsupportedSyntax
@[builtinTermElab panic] def elabPanic : TermElab := fun stx expectedType? => do
let arg := stx[1]
let pos ← getRefPosition
let env ← getEnv
let stxNew ← match (← getDeclName?) with
| some declName => `(panicWithPosWithDecl $(quote (toString env.mainModule)) $(quote (toString declName)) $(quote pos.line) $(quote pos.column) $arg)
| none => `(panicWithPos $(quote (toString env.mainModule)) $(quote pos.line) $(quote pos.column) $arg)
withMacroExpansion stx stxNew $ elabTerm stxNew expectedType?
@[builtinMacro Lean.Parser.Term.unreachable] def expandUnreachable : Macro := fun stx =>
`(panic! "unreachable code has been reached")
@[builtinMacro Lean.Parser.Term.assert] def expandAssert : Macro := fun stx =>
-- TODO: support for disabling runtime assertions
let cond := stx[1]
let body := stx[3]
match cond.reprint with
| some code => `(if $cond then $body else panic! ("assertion violation: " ++ $(quote code)))
| none => `(if $cond then $body else panic! ("assertion violation"))
@[builtinMacro Lean.Parser.Term.dbgTrace] def expandDbgTrace : Macro := fun stx =>
let arg := stx[1]
let body := stx[3]
if arg.getKind == interpolatedStrKind then
`(dbgTrace (s! $arg) fun _ => $body)
else
`(dbgTrace (toString $arg) fun _ => $body)
@[builtinTermElab «sorry»] def elabSorry : TermElab := fun stx expectedType? => do
logWarning "declaration uses 'sorry'"
let stxNew ← `(sorryAx _ false)
withMacroExpansion stx stxNew <| elabTerm stxNew expectedType?
/-- Return syntax `Prod.mk elems[0] (Prod.mk elems[1] ... (Prod.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
partial def mkPairs (elems : Array Syntax) : MacroM Syntax :=
let rec loop (i : Nat) (acc : Syntax) := do
if i > 0 then
let i := i - 1
let elem := elems[i]
let acc ← `(Prod.mk $elem $acc)
loop i acc
else
pure acc
loop (elems.size - 1) elems.back
private partial def hasCDot : Syntax → Bool
| Syntax.node k args =>
if k == `Lean.Parser.Term.paren then false
else if k == `Lean.Parser.Term.cdot then true
else args.any hasCDot
| _ => false
/--
Return `some` if succeeded expanding `·` notation occurring in
the given syntax. Otherwise, return `none`.
Examples:
- `· + 1` => `fun _a_1 => _a_1 + 1`
- `f · · b` => `fun _a_1 _a_2 => f _a_1 _a_2 b` -/
partial def expandCDot? (stx : Syntax) : MacroM (Option Syntax) := do
if hasCDot stx then
let (newStx, binders) ← (go stx).run #[];
`(fun $binders* => $newStx)
else
pure none
where
/--
Auxiliary function for expanding the `·` notation.
The extra state `Array Syntax` contains the new binder names.
If `stx` is a `·`, we create a fresh identifier, store in the
extra state, and return it. Otherwise, we just return `stx`. -/
go : Syntax → StateT (Array Syntax) MacroM Syntax
| stx@(Syntax.node k args) =>
if k == `Lean.Parser.Term.paren then pure stx
else if k == `Lean.Parser.Term.cdot then withFreshMacroScope do
let id ← `(a)
modify fun s => s.push id;
pure id
else do
let args ← args.mapM go
pure $ Syntax.node k args
| stx => pure stx
/--
Helper method for elaborating terms such as `(.+.)` where a constant name is expected.
This method is usually used to implement tactics that function names as arguments (e.g., `simp`).
-/
def elabCDotFunctionAlias? (stx : Syntax) : TermElabM (Option Expr) := do
let some stx ← liftMacroM <| expandCDotArg? stx | pure none
let stx ← liftMacroM <| expandMacros stx
match stx with
| `(fun $binders* => $f:ident $args*) =>
if binders == args then
try Term.resolveId? f catch _ => return none
else
return none
| `(fun $binders* => binop% $f:ident $a $b) =>
if binders == #[a, b] then
try Term.resolveId? f catch _ => return none
else
return none
| _ => return none
where
expandCDotArg? (stx : Syntax) : MacroM (Option Syntax) :=
match stx with
| `(($e)) => Term.expandCDot? e
| _ => Term.expandCDot? stx
/--
Try to expand `·` notation.
Recall that in Lean the `·` notation must be surrounded by parentheses.
We may change this is the future, but right now, here are valid examples
- `(· + 1)`
- `(f ⟨·, 1⟩ ·)`
- `(· + ·)`
- `(f · a b)` -/
@[builtinMacro Lean.Parser.Term.paren] def expandParen : Macro
| `(()) => `(Unit.unit)
| `(($e : $type)) => do
match (← expandCDot? e) with
| some e => `(($e : $type))
| none => Macro.throwUnsupported
| `(($e)) => return (← expandCDot? e).getD e
| `(($e, $es,*)) => do
let pairs ← mkPairs (#[e] ++ es)
(← expandCDot? pairs).getD pairs
| stx =>
if !stx[1][0].isMissing && stx[1][1].isMissing then
-- parsed `(` and `term`, assume it's a basic parenthesis to get any elaboration output at all
`(($(stx[1][0])))
else
throw <| Macro.Exception.error stx "unexpected parentheses notation"
@[builtinTermElab paren] def elabParen : TermElab := fun stx expectedType? => do
match stx with
| `(($e : $type)) =>
let type ← withSynthesize (mayPostpone := true) <| elabType type
let e ← elabTerm e type
ensureHasType type e
| _ => throwUnsupportedSyntax
@[builtinTermElab subst] def elabSubst : TermElab := fun stx expectedType? => do
let expectedType ← tryPostponeIfHasMVars expectedType? "invalid `▸` notation"
match stx with
| `($heq ▸ $h) => do
let mut heq ← elabTerm heq none
let heqType ← inferType heq
let heqType ← instantiateMVars heqType
match (← Meta.matchEq? heqType) with
| none => throwError "invalid `▸` notation, argument{indentExpr heq}\nhas type{indentExpr heqType}\nequality expected"
| some (α, lhs, rhs) =>
let mut lhs := lhs
let mut rhs := rhs
let mkMotive (typeWithLooseBVar : Expr) := do
withLocalDeclD (← mkFreshUserName `x) α fun x => do
mkLambdaFVars #[x] $ typeWithLooseBVar.instantiate1 x
let mut expectedAbst ← kabstract expectedType rhs
unless expectedAbst.hasLooseBVars do
expectedAbst ← kabstract expectedType lhs
unless expectedAbst.hasLooseBVars do
throwError "invalid `▸` notation, expected type{indentExpr expectedType}\ndoes contain equation left-hand-side nor right-hand-side{indentExpr heqType}"
heq ← mkEqSymm heq
(lhs, rhs) := (rhs, lhs)
let hExpectedType := expectedAbst.instantiate1 lhs
let h ← withRef h do
let h ← elabTerm h hExpectedType
try
ensureHasType hExpectedType h
catch ex =>
-- if `rhs` occurs in `hType`, we try to apply `heq` to `h` too
let hType ← inferType h
let hTypeAbst ← kabstract hType rhs
unless hTypeAbst.hasLooseBVars do
throw ex
let hTypeNew := hTypeAbst.instantiate1 lhs
unless (← isDefEq hExpectedType hTypeNew) do
throw ex
mkEqNDRec (← mkMotive hTypeAbst) h (← mkEqSymm heq)
mkEqNDRec (← mkMotive expectedAbst) h heq
| _ => throwUnsupportedSyntax
@[builtinTermElab stateRefT] def elabStateRefT : TermElab := fun stx _ => do
let σ ← elabType stx[1]
let mut mStx := stx[2]
if mStx.getKind == `Lean.Parser.Term.macroDollarArg then
mStx := mStx[1]
let m ← elabTerm mStx (← mkArrow (mkSort levelOne) (mkSort levelOne))
let ω ← mkFreshExprMVar (mkSort levelOne)
let stWorld ← mkAppM `STWorld #[ω, m]
discard <| mkInstMVar stWorld
mkAppM `StateRefT' #[ω, σ, m]
@[builtinTermElab noindex] def elabNoindex : TermElab := fun stx expectedType? => do
let e ← elabTerm stx[1] expectedType?
return DiscrTree.mkNoindexAnnotation e
end Lean.Elab.Term