import Lean import Specimen.Utils import Batteries.Lean.Expr open Lean Elab Command Meta Term Parser /-- `mkLetBind lhs rhsTerms` constructs a monadic let-bind expression of the form `let lhs ← e0 e1 … en`, where `rhsTerms := #[e0, e1, …, en]`. - Note: `rhsTerms` cannot be empty, otherwise this function throws an exception -/ def mkLetBind (lhs : Ident) (rhsTerms : TSyntaxArray `term) : MetaM (TSyntax `doElem) := do let rhsList := rhsTerms.toList match rhsList with | f :: args => let argTerms := args.toArray `(doElem| let $lhs:term ← $f:term $argTerms* ) | [] => throwError "rhsTerms can't be empty" /-- `mkTuple components` creates an n-ary tuple from the `Name`s in the list `components` - If `components` is Empty, we produce the unit value `()` - If `components` has length 1, we just produce one single `Ident` -/ def mkTuple (components : List (Name × Option Expr)) : MetaM (TSyntax `term) := do let lctx ← getLCtx aux lctx components where aux (lctx : LocalContext) components : MetaM (TSyntax `term) := match components with | [] => `(()) | [(var, some ty)] => do let tSyn ← delabExprInLocalContext lctx ty `(($(mkIdent var) : $tSyn)) | [(var, none)] => do `($(mkIdent var)) | (var, oty) :: xs => do let tail ← aux lctx xs match oty with | some type => let tSyn ← delabExprInLocalContext lctx type `( (($(mkIdent var):term : $tSyn), $tail:term ) ) | none => `( ($(mkIdent var):term, $tail:term)) /-- Constructs a Lean monadic `do` block out of an array of `doSeq`s (expressions that appear in the `do` block) -/ def mkDoBlock (doElems : TSyntaxArray `doElem) : MetaM (TSyntax `term) := do `(do $[$doElems:doElem]*) /-- `mkIfExprWithNaryAnd predicates trueBranch elseBranch` creates a *monadic* if-expression `if (p1 && … && pn) then $trueBranch else $elseBranch`, where `predicates := #[p1, …, pn]`. Note: - `trueBranch` and `elseBranch` are `doElem`s, since the if-expr is intended to be part of a monadic `do`-block - If `predicates` is empty, the expression created is `if True then $trueBranch else $elseBranch` -/ def mkIfExprWithNaryAnd (predicates : Array Term) (trueBranch : TSyntax `doElem) (elseBranch : TSyntax `doElem) : MetaM (TSyntax `doElem) := do let condition ← match predicates.toList with | [] => `(True) | [p] => pure p | p :: ps => List.foldlM (fun acc pred => `($acc && $pred)) (init := p) ps `(doElem| if $condition then $trueBranch:doElem else $elseBranch:doElem) /-- Creates a match expression (represented as a `TSyntax term`), where the `scrutinee` is an `Ident` and the `cases` are specified as an array of `matchAlt`s -/ def mkMatchExpr (scrutinee : Ident) (cases : TSyntaxArray ``Term.matchAlt) : MetaM (TSyntax `term) := `(match $scrutinee:ident with $cases:matchAlt*) /-- Creates a match expression with simultaneous matching on multiple scrutinees. The `scrutinees` are provided as an array of `Ident`s and the `cases` are specified as an array of `matchAlt`s where each alternative should have patterns corresponding to all scrutinees -/ def mkSimultaneousMatch (scrutineeIdents : Array Ident) (cases : TSyntaxArray ``Term.matchAlt) : MetaM (TSyntax `term) := do let scrutinees ← Array.mapM (fun ident => `(matchDiscr| $ident:ident)) scrutineeIdents `(match $[$scrutinees:matchDiscr],* with $cases:matchAlt*) /-- Variant of `mkMatchExpr` where the `scrutinee` is a `TSyntax term` rather than an `Ident` -/ def mkMatchExprWithScrutineeTerm (scrutinee : TSyntax `term) (cases : TSyntaxArray ``Term.matchAlt) : MetaM (TSyntax `term) := `(match $scrutinee:term with $cases:matchAlt*) /-- Variant of `mkMatchExpr` where the `scrutinee` is a `TSyntax term`, and the resultant match expression is a `doElem` (i.e. it is part of a monadic `do`-block) -/ def mkDoElemMatchExpr (scrutinee : TSyntax `term) (cases : TSyntaxArray ``Term.matchAlt) : MetaM (TSyntax `doElem) := `(doElem| match $scrutinee:term with $cases:matchAlt*) /-- Converts a `Literal`, the datatype used to store `Nat` and `String` literals in `Lean.Expr` into the corresponding literal `TSyntax`. Note, the `Nat` literal will be wrapped in an `OfNat.ofNat` call. -/ def mkLiteral (l : Literal) : MetaM (TSyntax `term) := match l with | .natVal n => `($(Syntax.mkNatLit n)) | .strVal s => `($(Syntax.mkStrLit s))