lean4-htt/src/Lean/Data/Options.lean
2021-12-16 15:41:29 -08:00

148 lines
5.3 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) 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
-/
import Lean.ImportingFlag
import Lean.Data.KVMap
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 _)
structure OptionDecl where
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 getOptionDefaulValue (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
def setOptionFromString (opts : Options) (entry : String) : IO Options := do
let ps := (entry.splitOn "=").map String.trim
let [key, val] ← pure ps | throw $ IO.userError "invalid configuration option entry, it must be of the form '<key> = <value>'"
let key := Name.mkSimple key
let defValue ← getOptionDefaulValue key
match defValue with
| DataValue.ofString v => pure $ opts.setString key val
| DataValue.ofBool v =>
if key == `true then pure $ opts.setBool key true
else if key == `false then pure $ opts.setBool key false
else throw $ IO.userError s!"invalid Bool option value '{val}'"
| DataValue.ofName v => pure $ opts.setName key val.toName
| DataValue.ofNat v =>
match val.toNat? with
| none => throw (IO.userError s!"invalid Nat option value '{val}'")
| some v => pure $ opts.setNat key v
| DataValue.ofInt v =>
match val.toInt? with
| none => throw (IO.userError s!"invalid Int option value '{val}'")
| some v => pure $ opts.setInt key v
| DataValue.ofSyntax _ => throw (IO.userError s!"invalid Syntax option value")
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
/-- 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 α) : IO (Lean.Option α) := do
registerOption name { defValue := KVMap.Value.toDataValue decl.defValue, group := decl.group, descr := decl.descr }
return { name := name, defValue := decl.defValue }
macro "register_builtin_option" name:ident " : " type:term " := " decl:term : command =>
`(builtin_initialize $name : Lean.Option $type ← Lean.Option.register $(quote name.getId) $decl)
macro "register_option" name:ident " : " type:term " := " decl:term : command =>
`(initialize $name : Lean.Option $type ← Lean.Option.register $(quote name.getId) $decl)
end Option
end Lean