import Lean import Plausible.Gen open Lean Meta Std -- Create idents for commonly-called functions & commonly-referenced types namespace Idents -- Idents for commonly-called functions def generatorCombinatorsThunkGenFn : Ident := mkIdent $ Name.mkStr2 "GeneratorCombinators" "thunkGen" def frequencyFn : Ident := mkIdent $ Name.mkStr2 "GeneratorCombinators" "frequency" def oneOfWithDefaultGenCombinatorFn : Ident := mkIdent $ Name.mkStr2 "GeneratorCombinators" "oneOfWithDefault" def oneOfWithDefaultEnumCombinatorFn : Ident := mkIdent $ Name.mkStr2 "EnumeratorCombinators" "oneOfWithDefault" def interpSampleFn : Ident := mkIdent $ Name.mkStr3 "Plausible" "SampleableExt" "interpSample" /-- Ident for the inner `aux_arb` function that appears in derived generators -/ def auxArbFn : Ident := mkIdent $ Name.mkStr1 "aux_arb" /-- Ident for the inner `aux_enum` function that appears in derived enumerators -/ def auxEnumFn : Ident := mkIdent $ Name.mkStr1 "aux_enum" /-- Ident for the inner `aux_dec` function that appears in derived checkers -/ def auxDecFn : Ident := mkIdent $ Name.mkStr1 "aux_dec" /-- Ident for the `DecOpt.andOptList` checker combinator (see `DecOpt.lean`) -/ def andOptListFn : Ident := mkIdent $ Name.mkStr2 "DecOpt" "andOptList" /-- Ident for the `DecOpt.negOpt` checker combinator (see `DecOpt.lean`) -/ def negOptFn : Ident := mkIdent $ Name.mkStr2 "DecOpt" "negOpt" /-- Ident for `GeneratorCombinators.backtrack`. -/ def genBacktrackFn : Ident := mkIdent $ Name.mkStr2 "GeneratorCombinators" "backtrack" /-- Ident for the `DecOpt.checkerBacktrack` checker combinator (see `DecOpt.lean`) -/ def checkerBacktrackFn : Ident := mkIdent $ Name.mkStr2 "DecOpt" "checkerBacktrack" /-- Ident for the `EnumeratorCombinators.enumerate` combinator (see `EnumeratorCombinators.lean`) -/ def enumerateFn : Ident := mkIdent $ Name.mkStr2 "EnumeratorCombinators" "enumerate" /-- Ident for the `EnumeratorCombinators.enumerating` combinator (see `EnumeratorCombinators.lean`) -/ def enumeratingFn : Ident := mkIdent $ Name.mkStr2 "EnumeratorCombinators" "enumerating" /-- Ident for the `EnumeratorCombinators.enumeratingOpt` combinator (see `EnumeratorCombinators.lean`) -/ def enumeratingOptFn : Ident := mkIdent $ Name.mkStr2 "EnumeratorCombinators" "enumeratingOpt" /-- Ident for `pure`. -/ def pureFn : Ident := mkIdent $ Name.mkStr1 "pure" /-- Idents for boolean literals. -/ def trueIdent : Ident := mkIdent ``true def falseIdent : Ident := mkIdent ``false -- Idents for fuel and size arguments to generators def fuelIdent : Ident := mkIdent $ Name.mkStr1 "fuel" def initSizeIdent : Ident := mkIdent $ Name.mkStr1 "initSize" def sizeIdent : Ident := mkIdent $ Name.mkStr1 "size" /-- `Ident` representing `MonadExcept.throw`-/ def failFn : Ident := mkIdent ``MonadExcept.throw def genericFailure : Ident := mkIdent ``Plausible.Gen.genericFailure def outOfFuelError : Ident := mkIdent `Gen.outOfFuelError -- Idents for typeclasses def arbitrarySizedSuchThatTypeclass : Ident := mkIdent $ Name.mkStr1 "ArbitrarySizedSuchThat" def enumSizedSuchThatTypeclass : Ident := mkIdent $ Name.mkStr1 "EnumSizedSuchThat" def arbitrarySizedTypeclass : Ident := mkIdent $ Name.mkStr1 "ArbitrarySized" def enumSizedTypeclass : Ident := mkIdent $ Name.mkStr1 "EnumSized" def decOptTypeclass : Ident := mkIdent $ Name.mkStr1 "DecOpt" -- Idents for typeclass functions def arbitraryFn : Ident := mkIdent $ Name.mkStr2 "Arbitrary" "arbitrary" def enumFn : Ident := mkIdent $ Name.mkStr2 "Enum" "enum" def enumSTFn : Ident := mkIdent $ Name.mkStr2 "EnumSuchThat" "enumST" def arbitrarySizedFn : Ident := mkIdent $ Name.mkStr2 "ArbitrarySized" "arbitrarySized" def unqualifiedArbitrarySizedFn : Ident := mkIdent $ Name.mkStr1 "arbitrarySized" def unqualifiedEnumSizedFn : Ident := mkIdent $ Name.mkStr1 "enumSized" def arbitrarySTFn : Ident := mkIdent $ Name.mkStr2 "ArbitrarySuchThat" "arbitraryST" def unqualifiedArbitrarySizedSTFn : Ident := mkIdent $ Name.mkStr1 "arbitrarySizedST" def unqualifiedEnumSizedSTFn : Ident := mkIdent $ Name.mkStr1 "enumSizedST" def unqualifiedDecOptFn : Ident := mkIdent $ Name.mkStr1 "decOpt" def decOptFn : Ident := mkIdent $ Name.mkStr2 "DecOpt" "decOpt" -- Idents for commonly-used types / constructors / type constructors def boolIdent : Ident := mkIdent ``Bool def natIdent : Ident := mkIdent ``Nat def zeroIdent : Ident := mkIdent ``Nat.zero def succIdent : Ident := mkIdent ``Nat.succ def exceptTTypeConstructor : Ident := mkIdent ``ExceptT def exceptTypeConstructor : Ident := mkIdent ``Except def genErrorType : Ident := mkIdent ``Plausible.GenError def genTypeConstructor : Ident := mkIdent ``Plausible.Gen def enumTypeConstructor : Ident := mkIdent $ Name.mkStr1 "Enumerator" /-- Produces a fresh user-facing & *accessible* identifier with respect to the local context - Note: prefer using this function over `Core.mkFreshUserName`, which is meant to create fresh names that are *inaccessible* to the user (i.e. `mkFreshUserName` will add daggers (`†`) to the name to make them inaccessible). - This function ensures that the identifier is fresh by adding suffixes containing underscores/numbers when necessary (in lieu of adding daggers). -/ def mkFreshAccessibleIdent (localCtx : LocalContext) (name : Name) : Ident := mkIdent $ LocalContext.getUnusedName localCtx name /-- `genFreshName existingNames namePrefix` produces a fresh name with the prefix `namePrefix` that is guaranteed to be not within `existingNames`. - Note: the body of this function operates in the identity monad since we want local mutable state and access to the syntactic sugar provided by `while` loops -/ def genFreshName (existingNames : Array Name) (namePrefix : Name) : Name := Id.run do let mut count := 0 let mut freshName := Name.appendAfter namePrefix s!"_{count}" while (existingNames.contains freshName) do count := count + 1 freshName := Name.appendAfter namePrefix s!"_{count}" return freshName /-- `genFreshNames existingNames namePrefixes` produces an array of fresh names, all of them guaranteed to be not in `existingNames`, where the `i`-th fresh name produced has prefix `namePrefixes[i]`. This function is implemented using a fold: when producing the `i`-th fresh name, we ensure that it does not clash with `existingNames` *and* the previous `i-1` fresh names produced. -/ def genFreshNames (existingNames : Array Name) (namePrefixes : Array Name) : Array Name := Array.foldl (fun acc name => Array.push acc (genFreshName (acc ++ existingNames) name)) #[] namePrefixes /-- `lookupNameInNameMap nameMap existingNames name` returns the `Ident` for the freshened name associated with the key `name` in `nameMap`. If `name` doesn't appear as a key in `nameMap`, a fresh name that is guaranteed not to clash with `existingNames` is produced. -/ def lookupFreshenedNameInNameMap (nameMap : HashMap Name Name) (existingNames : Array Name) (name : Name) : Ident := match nameMap[name]? with | some newName => Lean.mkIdent newName | none => Lean.mkIdent (genFreshName existingNames name) /-- Extracts the name of a parameter from a corresponding `Term`. If this is not possible, a fresh user-facing name is produced. -/ def extractParamName (arg : Term) : MetaM Name := match arg with | `($name:ident) => return name.getId | _ => return (genFreshName #[] `param) end Idents