/- Copyright (c) 2018 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Sebastian Ullrich and Leonardo de Moura -/ prelude import Lean.ImportingFlag import Lean.Data.KVMap import Lean.Data.NameMap namespace Lean def Options := KVMap def Options.empty : Options := {} instance : Inhabited Options where default := {} instance : ToString Options := inferInstanceAs (ToString KVMap) instance : ForIn m Options (Name × DataValue) := inferInstanceAs (ForIn _ KVMap _) instance : BEq Options := inferInstanceAs (BEq KVMap) structure OptionDecl where declName : Name := by exact decl_name% defValue : DataValue group : String := "" descr : String := "" deriving Inhabited def OptionDecls := NameMap OptionDecl instance : Inhabited OptionDecls := ⟨({} : NameMap OptionDecl)⟩ private builtin_initialize optionDeclsRef : IO.Ref OptionDecls ← IO.mkRef (mkNameMap OptionDecl) @[export lean_register_option] def registerOption (name : Name) (decl : OptionDecl) : IO Unit := do unless (← initializing) do throw (IO.userError "failed to register option, options can only be registered during initialization") let decls ← optionDeclsRef.get if decls.contains name then throw $ IO.userError s!"invalid option declaration '{name}', option already exists" optionDeclsRef.set $ decls.insert name decl def getOptionDecls : IO OptionDecls := optionDeclsRef.get @[export lean_get_option_decls_array] def getOptionDeclsArray : IO (Array (Name × OptionDecl)) := do let decls ← getOptionDecls pure $ decls.fold (fun (r : Array (Name × OptionDecl)) k v => r.push (k, v)) #[] def getOptionDecl (name : Name) : IO OptionDecl := do let decls ← getOptionDecls let (some decl) ← pure (decls.find? name) | throw $ IO.userError s!"unknown option '{name}'" pure decl def getOptionDefaultValue (name : Name) : IO DataValue := do let decl ← getOptionDecl name pure decl.defValue def getOptionDescr (name : Name) : IO String := do let decl ← getOptionDecl name pure decl.descr class MonadOptions (m : Type → Type) where getOptions : m Options export MonadOptions (getOptions) instance [MonadLift m n] [MonadOptions m] : MonadOptions n where getOptions := liftM (getOptions : m _) variable [Monad m] [MonadOptions m] def getBoolOption (k : Name) (defValue := false) : m Bool := do let opts ← getOptions return opts.getBool k defValue def getNatOption (k : Name) (defValue := 0) : m Nat := do let opts ← getOptions return opts.getNat k defValue class MonadWithOptions (m : Type → Type) where withOptions (f : Options → Options) (x : m α) : m α export MonadWithOptions (withOptions) instance [MonadFunctor m n] [MonadWithOptions m] : MonadWithOptions n where withOptions f x := monadMap (m := m) (withOptions f) x /-! Remark: `_inPattern` is an internal option for communicating to the delaborator that the term being delaborated should be treated as a pattern. -/ def withInPattern [MonadWithOptions m] (x : m α) : m α := withOptions (fun o => o.setBool `_inPattern true) x def Options.getInPattern (o : Options) : Bool := o.getBool `_inPattern /-- A strongly-typed reference to an option. -/ protected structure Option (α : Type) where name : Name defValue : α deriving Inhabited namespace Option protected structure Decl (α : Type) where defValue : α group : String := "" descr : String := "" protected def get? [KVMap.Value α] (opts : Options) (opt : Lean.Option α) : Option α := opts.get? opt.name protected def get [KVMap.Value α] (opts : Options) (opt : Lean.Option α) : α := opts.get opt.name opt.defValue protected def set [KVMap.Value α] (opts : Options) (opt : Lean.Option α) (val : α) : Options := opts.set opt.name val /-- Similar to `set`, but update `opts` only if it doesn't already contains an setting for `opt.name` -/ protected def setIfNotSet [KVMap.Value α] (opts : Options) (opt : Lean.Option α) (val : α) : Options := if opts.contains opt.name then opts else opt.set opts val protected def register [KVMap.Value α] (name : Name) (decl : Lean.Option.Decl α) (ref : Name := by exact decl_name%) : IO (Lean.Option α) := do registerOption name { declName := ref defValue := KVMap.Value.toDataValue decl.defValue group := decl.group descr := decl.descr } return { name := name, defValue := decl.defValue } macro (name := registerBuiltinOption) doc?:(docComment)? "register_builtin_option" name:ident " : " type:term " := " decl:term : command => `($[$doc?]? builtin_initialize $name : Lean.Option $type ← Lean.Option.register $(quote name.getId) $decl) macro (name := registerOption) doc?:(docComment)? "register_option" name:ident " : " type:term " := " decl:term : command => `($[$doc?]? initialize $name : Lean.Option $type ← Lean.Option.register $(quote name.getId) $decl) end Option end Lean