--版权所有2012谷歌公司保留所有权利。
--版权所有2013年及以后,Gergely Risko
--
--根据Apache许可证2.0版(“许可证”)许可;
--除非符合许可证,否则您不能使用此文件。
--您可以在以下地址获得许可证副本:
--
--http://www.apache.org/licenses/LICENSE-2.0
--
--除非适用法律要求或书面同意,否则软件
--根据许可证分发的是按“原样”分发的,
--无任何明示或暗示的保证或条件。
--有关管理权限的特定语言和
--许可证下的限制。
--
--作者:米哈利·巴拉兹<klao@google.com>、Gergely Risko<gergely@risko.hu>
{-#LANGUAGE视图模式#-}
{-#语言CPP#-}
{-#LANGUAGE模板Haskell#-}
{-#LANGUAGE类型同义词实例#-}
{-#LANGUAGE灵活实例#-}
{-#LANGUAGE NamedFieldPuns#-}
{-#LANGUAGE MultiWayIf#-}

-- |
--模块:HFlags
--许可证:Apache 2.0
--
--@HFlags@库支持轻松定义命令行标志,
--重新实现谷歌@gflags的创意@
-- (<http://code.google.com/p/gflags网站>).
--
--命令行标志可以在顶层的任何文件中声明,
--使用“defineFlag”。在运行时,实际值被分配给
--顶级@flagsname@常量。那些可以纯粹使用
--贯穿整个计划。
--
--在@main@函数的开头,@$'initHFlags'\“程序
--description\“必须调用@来初始化标志。所有标志
--将初始化可通过导入传递访问的
--来自@main@。这意味着,任何Haskell包都可以很容易地
--使用定义命令行标志@H标志@。此功能是
--由演示
-- <http://github.com/errge/hflags/blob/master/examples/ImportExample.hs>
--以及<http://github.com/errge/hflags/tree/master/examples/package>.
--
--一个简单的示例(更多信息请参见
-- <http://github.com/errge/hflags/tree/master/examples>目录):
--
-- > #!/usr/bin/env runhaskell
-- >
-->{-#LANGUAGE模板Haskell#-}
-- >
-->导入HFlags
-- >
-->defineFlag“name”“Indiana Jones”“问候对象”
-->defineFlag“r:repeat”(3+4::Int)“重复消息的次数。”
-- >
-->main=do s<-$initHFlags“简单程序v0.1”
-->sequence_$replicate flags_repeat问候语
-->putStrLn$“您的其他参数是:”++show s
-->putStrLn$“与:”++show HFlags.arguments相同
-->其中
-->greet=putStrLn$“你好”++flags_name++“,很高兴见到你!”
--
--在“initHFlags”时间,库还尝试从
--环境变量@HFLAGS_verbose=True@等于
--在命令行上指定@--verbose=True@。这个环境
--功能仅适用于长选项,用户必须指定
--值,即使对于@Bool@秒。
--
--/从版本0.2开始,您不能将initHFlags与程序描述放在括号中。只是/@$initHFlags@,/它更干净/


模块 HF标志 (
  --*标志的定义
  定义自定义标志,
  定义EQ标志,
  标志类型(..),
  --*在运行时初始化标志
  initH标志,
  initHFlags相关默认值,
  --*为了便于访问参数,在调用initHFlags之后
  论据,
  未定义的选项,
  --*对于调试,不应在生产代码中使用
  标志(..),
  MakeThis孤儿(..),
  全球HFlags,
  全局参数,
  全局未定义选项
  ) 哪里

--TODO(待办事项):
--短选项的重复检查:这很棘手,我们需要在HFlag_中编码信息。。。数据名称
-- ?--不,因为bools?
----如果当前值与默认值不同,help应显示当前值,以便用户可以测试命令行参数

进口 控制.例外
进口 控制.莫纳德
进口 数据.烧焦
进口 数据.功能
进口 数据.列表
进口 数据.IORef(IORef)
进口 数据.也许 吧
--这是故意懒惰的,因此依赖默认值仅为
--评估是否需要。(用户尚未指定
--选项或通过环境变量。)
进口 有资格的 数据.地图.懒惰 作为 地图
进口 数据.地图.懒惰 (地图)
进口 有资格的 数据.文本
进口 语言.哈斯克尔.真实航向
进口 系统.慰问.GetOpt(获取选项)
进口 系统.环境
进口 系统.IO(输入输出)
进口 系统.IO(输入输出).不安全的
进口 系统.出口

进口 前奏曲

--|这是一次临时黑客攻击,目的是使内部旗帜可见
--库,通过使定义标志的模块成为孤立的。对于
--用法示例,签出
-- <http://github.com/errge/hflags/blob/master/examples/package/Tup.hs>.
--已经建议对GHC 7.8进行适当修复,请参见
-- <http://ghc.haskell.org/trac/ghc/ticket/7867>.
数据 制作此孤儿 = MakeThis孤儿

--|用于存储标志的每个属性的数据类型。
数据 标志数据 = 标志数据
            { fName(姓名) :: 字符串
            , f短 :: 也许 吧 烧焦
            , fDefValue(定义值) :: 字符串
            , fArg类型 :: 字符串
            , f说明 :: 字符串
            , fModule名称 :: 字符串
            , f检查 :: IO(输入输出) () --要在@initFlags中计算的^函数@
                              --强制对参数进行语法检查。
            }

实例 显示 标志数据 哪里
  显示 fd公司 = 显示 (f名称 fd公司, f短 fd公司, fDefValue(定义值) fd公司, fArg类型 fd公司, f说明 fd公司, fModule名称 fd公司)

--|程序支持的每个标志都必须通过新的
--幻影数据类型和该数据类型的Flag实例。
--
--但库的用户不应该担心这个类或
--这些函数背后的实现细节,只需使用
--@defineFlag@Template Haskell函数用于定义新标志。
 标志  哪里
  获取标记数据 ::  -> 标志数据

--|定义标志的最灵活方式。有关示例,请参见
-- <http://github.com/errge/hflags/blob/master/examples/ComplexExample.hs>.
--对于大多数情况,“defineFlag”应该已经足够了。
--
--参数:
--
--*标志的名称(如果要为该标志使用短选项@l@,请使用@l:long@语法),
--
--*引用表达式并键入带符号的默认值,
--
--*识别参数类型的帮助字符串(例如INTLIST),
--
--*读取函数,引用表达式,
--
--*显示函数,引用表达式,
--
--*标志的帮助字符串。
定义自定义标志 :: 字符串 -> ExpQ公司 -> 字符串 -> ExpQ公司 -> ExpQ公司 -> 字符串 ->  [12月]
定义自定义标志 名称' defQ(定义Q) 参数帮助 readQ(读取Q) showQ(展示) 描述 =
   (名称, 短的) <- 如果 | 长度 名称' == 0 -> 失败 “不支持没有名称的标志。”
                         | 长度 名称' == 1 -> 返回 (名称', 只是 $  名称')
                         | 长度 名称' == 2 -> 返回 (名称', 没有什么)
                         | 名称' !! 1 == ':' -> 返回 ( 2 名称', 只是 $  名称')
                         | 否则 -> 返回 (名称', 没有什么)
     defE(定义E) <- defQ(定义Q)
     标志类型 <- 案例 defE(定义E) 属于
       SigE公司 _ 标志类型 -> 返回 $ 返回 标志类型
       _ -> 失败 defineCustomFlag的默认值必须是显式类型的表达式,如(12::Int)
     模块名 <- 功能性维修计划 位置模块 位置
      访问器名称 = mk名称 $ “旗子_” ++ 名称
     --注意:dataName的格式在这里很重要,initHFlags
     --解析名称,因此这里的生成和中的解析
     --initHFlags必须一致。
      数据名称 = mk名称 $ “HFlag_” ++ 名称
      数据ConstrName = mk名称 $ “HFlagC_” ++ 名称
     --注意:支持内部拼接[d||]会使这一切变得更好
#如果MIN_VERSION_template_haskell(2,12,0)
     12月数据 <- 数据D (cxt(文本) []) 数据名称 [] 没有什么 [正常C 数据结构名称 []]      []
#elif MIN_VERSION_模板_哈斯克尔(2,11,0)
     12月数据 <- 数据D (cxt(文本) []) 数据名称 [] 没有什么 [正常C 数据ConstrName []] (cxt(文本) [])
#其他
     12月数据 <- 数据D (cxt(文本) []) 数据名称 []         [正常C 数据ConstrName []]      []
#结尾
     实例12 <- 实例D
                    (cxt(文本) [])
                    (应用程序T (conT公司 ''标志) (conT公司 数据名称))
                      [功能D '获取标志数据 [条款 [野生植物]
                                          (正常B
                                           [| 标志数据
                                              名称
                                              短的
                                              $(应用程序E showQ(展示) defQ(定义Q))
                                              arg帮助
                                              描述
                                              模块名
                                              --seq'ng构造函数名称,因此它在生成的代码中不会被使用
                                              ($(圆锥E 数据ConstrName) `序列` 评价 $(变量E 访问器名称) >> 返回 ())
                                           |]) []]]
     标志PragmaDec <- pragInlD 访问器名称 无内联 趣味十足 所有阶段
     标志Sig <- 信号D 访问器名称 标志类型
     十二月标志 <- 功能D 访问器名称 [条款 [] (正常B $ 应用程序E readQ(读取Q) [| 查找标志 名称 模块名 |]) []]
     返回 [12月数据, 实例12, 标志PragmaDec, 标志Sig, 十二月标志]

--|这只是转发到带有@[|read|]@的“defineCustomFlag”,并且
--@[|show|]@。对于类型不是实例的标志很有用
--“FlagType”的。有关示例,请参见
-- <http://github.com/errge/hflags/blob/master/examples/ComplexExample.hs>.
--
--参数:
--
--*标志的名称(如果要为该标志使用短选项@l@,请使用@l:long@语法),
--
--*带引号的表达式和带符号的类型默认值,
--
--*识别参数类型的帮助字符串(例如INTLIST),
--
--*标志的帮助字符串。
定义EQ标志 :: 字符串 -> ExpQ公司 -> 字符串 -> 字符串 ->  [12月]
定义EQ标志 名称 defQ(定义Q) arg帮助 描述 =
 定义自定义标志 名称 defQ(定义Q) arg帮助 [| 阅读 |] [| 显示 |] 描述

--|支持简单的“defineFlag”语法的类型类。
 标志类型 t吨 哪里
  --|@defineFlag@函数定义了一个新标志。
  --
  --参数:
  --
  --*标志的名称(如果要为该标志使用短选项@l@,请使用@l:long@语法),,
  --
  --*默认值,
  --
  --*标志的帮助字符串。
  定义标志 :: 字符串 -> t吨 -> 字符串 ->  [12月]

布尔秀 :: 布尔 -> 字符串
布尔秀 真的 = “正确”
布尔秀 False(错误) = “假”

boolRead(布尔阅读) :: 字符串 -> 布尔
boolRead(布尔阅读) = boolRead'(boolRead) . 地图 到更低
  哪里
    boolRead'(boolRead) (“y”:_) = 真的
    boolRead'(boolRead) (“t”:_) = 真的
    boolRead'(boolRead) ('1':_) = 真的
    boolRead'(boolRead) (“n”:_) = False(错误)
    boolRead'(boolRead) (“f”:_) = False(错误)
    布尔读取' ('0':_) = False(错误)
    boolRead'(boolRead)  = 错误 $ “无法将字符串解析为布尔值:” ++ 

实例 标志类型 布尔 哪里
  定义标志 n个  = 定义自定义标志 n个 [|  :: 布尔 |] “BOOL”(BOOL) [| boolRead(布尔阅读) |] [| 布尔秀 |]

charShow(字符显示) :: 烧焦 -> 字符串
charShow(字符显示) x个 = x个:[]

charRead(字符读取) :: 字符串 -> 烧焦
charRead(字符读取) [x个] = x个
charRead(字符读取)  = 错误 $ “无法将字符串解析为字符:” ++ 

实例 标志类型 烧焦 哪里
  定义标志 n个  = 定义自定义标志 n个 [|  :: 烧焦 |] “查尔” [| charRead(字符读取) |] [| charShow(字符显示) |]

实例 标志类型 国际 哪里
  定义标志 n个  = 定义EQ标志 n个 [|  :: 国际 |] “内景”

实例 标志类型 整数 哪里
  定义标志 n个  = 定义EQFlag n个 [|  :: 整数 |] “整数”

实例 标志类型 字符串 哪里
  定义标志 n个  = 定义自定义标志 n个 [|  :: 字符串 |] “字符串” [| 身份证件 |] [| 身份证件 |]

实例 标志类型 双精度 哪里
  定义标志 n个  = 定义EQ标志 n个 (信号E (清淡的 (基本原理 (到理性 ))) [t吨| 双精度 |] ) “双倍”

--TODO(errge):hflags-instances cabal包,因此基本hflags
--不依赖于文本,而文本不在GHC中。
实例 标志类型 数据.文本.文本 哪里
  定义标志 n个  =
    --推迟解除数据。文本。文本到字符串提升
      = 数据.文本.打开 
    在里面 定义自定义标志 n个 [| 数据.文本.包装  :: 数据.文本.文本 |] “文本” [| 数据.文本.包装 |] [| 数据.文本.打开 |]

--|用于“initHFlags”和之间通信的全局“IORef”
--@flags_*@。这是标志名和当前值之间的映射。
{-#NOINLINE全局HFlags#-}
全球HFlags :: IORef(IORef) (也许 吧 (地图 字符串 字符串))
全球HFlags = 不安全性能IO $ 新IORef 没有什么

--|用于轻松访问参数的全局“IORef”。
{-#NOINLINE全局参数#-}
全局参数 :: IORef(IORef) (也许 吧 [字符串])
全局参数 = 不安全性能IO $ 新IORef 没有什么

--|包含命令行的非缓冲、非选项部分,
--论据。只能在调用“initHFlags”之后使用。
{-#NOINLINE参数#-}
论据 :: [字符串]
论据 = 不安全性能IO $ 
  马格斯 <- 读者参考 全局参数
  案例 马格斯 属于
    只是 参数 -> 返回 $ 参数
    没有什么 -> 错误 $ “调用initHFlags之前使用的HFlags.arguments。”

--|全局“IORef”,用于轻松访问未定义的选项,如果
--使用了@--undefok@。如果必须将这些选项传递给
--另一个库,例如@criteria或@GTK@。
{-#NOINLINE globalUndefinedOptions#-}
全局未定义选项 :: IORef(IORef) (也许 吧 [字符串])
全局未定义选项 = 不安全性能IO $ 新IORef 没有什么

--|包含命令行的非解析选项部分,如果
--@--undefok@正在使用中。当你必须通过考试时,这很有用
--其他库的这些选项,例如@criteria@或@GTK@。可以
--仅在调用“initHFlags”后使用。
{-#NOINLINE未定义选项#-}
未定义的选项 :: [字符串]
未定义的选项 = 不安全性能IO $ 
  马格斯 <- 读者参考 全局未定义选项
  案例 马格斯 属于
    只是 参数 -> 返回 $ 参数
    没有什么 -> 错误 $ “在调用initHFlags之前使用了HFlags.globalUndefOpts。”

查找标志 :: 字符串 -> 字符串 -> 字符串
查找标志 fName(姓名) fModule名称 = 不安全性能IO $ 
  旗帜 <- 读者参考 全球HFlags
  案例 旗帜 属于
    只是 标志地图 -> 案例 地图.查找 fName(姓名) 旗标图 属于
      只是  -> 返回 
      没有什么 -> 错误 $ “标志” ++ fName(姓名) ++ “运行时找不到,请查看HFlags-source/examples/package/Tup.hs.抱歉。”
    没有什么 -> 错误 $ “标志” ++ fName(姓名) ++ “(来自模块:” ++ fModule名称 ++ “)在调用initHFlags之前使用。”

--|类似Lisp的alist,键->值对。
类型 A列表 = [(字符串, 字符串)]

--|获取三个字母并返回新字母的函数。
类型 从属默认值 = A列表 -> A列表 -> A列表 -> A列表

--|初始化“globalHFlags”并返回非选项参数。
初始化标志 :: 从属默认值 -> 字符串 -> [标志数据] -> [字符串] -> IO(输入输出) [字符串]
初始化标志 从属默认值 程序描述 旗帜 参数 = 
  do帮助
   (opts选项, 非期权, 未设防, 错误)
        | doUndefok公司 = 获取选项' Permute公司 获取OptFlags 参数
        | 否则 = (\(,b条,c(c)) -> (,b条,[],c(c))) $ 获取选项 Permute公司 获取OptFlags 参数
  什么时候 ( $ 无效的 错误) $ 
    地图M_ (hPutStrLn公司 标准错误) 错误
    exit失败
   默认值 = 地图 (\标志数据 { fName(姓名), fDefValue(定义值) } -> (fName(姓名), fDefValue(定义值))) 旗帜
  环境价值 <- 获取环境
   环境默认值 = 地图 (地图Fst (来自Just . stripPrefix(条前缀) “HFLAGS_”)) $ 滤波器 ((是前缀 “HFLAGS_”) . 有限状态试验) 环境价值
   depdef(数据定义) = 从属默认值 默认值 环境默认值 opts选项
  writeIORef(写入或引用) 全球HFlags $ 只是 $ 地图.来自列表 $ 默认值 ++ depdef(数据定义) ++ 环境默认值 ++ opts选项
  writeIORef(写入或引用) 全局参数 $ 只是 非期权
  writeIORef(写入或引用) 全局未定义选项 $ 只是 未设防
  地图管理_ 强制标志 旗帜
  返回 非期权
    哪里
      地图Fst (f) (, b条) = ((f) , b条)
      helpOption(帮助选项) = 选项 “h” [“帮助”, “用法”, “版本”] (无Arg ("", "")) “显示帮助和版本信息。”
      do帮助 = 案例 获取选项 Permute公司 [helpOption(帮助选项)] 参数 属于
        ([], _, _) -> 返回 ()
        _ ->  输入StrLn $ 使用信息 (程序描述 ++ “\n”) (helpOption(帮助选项):获取OptFlags)
                退出失败

      underbook选项 = 选项 "" [“未确定”] (无Arg ("", "")) “在无法识别的命令行选项上是否失败。”
      doUndefok公司 = 案例 获取选项 Permute公司 [underbook选项] 参数 属于
        ([], _, _) -> False(错误)
        _ -> 真的

      标志获取OptArgDescr 标志数据 { fName(姓名), fArg类型 }
        | fArg类型 == “BOOL”(BOOL) = OptArg(光学参数) (\ -> (fName(姓名), 也许 吧 “正确” 身份证件 )) fArgType(参数类型)
        | 否则 = 请求参数 (\ -> (fName(姓名), )) fArg类型

      --从标志([FlagData])计算GetOpt兼容的[Option]结构
      获取OptFlags = underbook选项:
        (轻弹 地图 旗帜 $ \flagData(标记数据)@(标志数据 { fName(姓名), f短, fDefValue(定义值), f说明, fModule名称 }) ->
         选项 (可能被列入名单 f短) [fName(姓名)]
                (标志获取OptArgDescr flagData(标记数据))
                (f说明 ++ “(默认值:” ++ fDefValue(定义值) ++ “,来自模块:” ++ fModule名称 ++ ")"))

      强制标志 标志数据 { fName(姓名), fModule名称, f检查 } =
        f检查 `捕捉`
        (\电子 -> 错误 $
               “分析标志的参数时出错:” ++ fName(姓名) ++
               “,值:” ++ 查找标志 fName(姓名) 模块名称 ++
               “,错误:” ++ 显示 (电子 :: 错误呼叫))

--|从(可传递)范围内的每个模块收集所有标志数据。
--拼接后键入:@[FlagData]@。
获取标志数据 :: ExpQ公司 --[标志数据]
获取标志数据 = 
  I类 _ 实例 <- 使具体化 ''标志
  案例 被愚弄的人 实例 属于
    [] -> 返回 ()
    (被愚弄:_) -> 失败 (“标志的多重定义” ++ (信噪比 $  被愚弄) ++
                       “,模块:” ++ (显示 $ 地图 有限状态试验 被愚弄))
  列表E $ 地图 实例到标记数据 实例
    哪里
      实例到标记数据 (获取实例类型 -> (AppT公司 _ 安装)) = [| 获取标记数据 (未定义 :: $(返回 安装)) |]
      实例到标记数据 _ = 错误 “不应该发生”
      --重复检查基于生成的“data HFlag_…”
      --名称,而不是FlagData,因为我们要进行检查
      --在编译时。在TH中不可能运行getFlagData
      --在刚刚具体化的实例上。
      实例到模块名称对 (获取实例类型 -> AppT公司 _ (ConT公司 安装)) =
         (弗拉格列夫, 修改版本) = 跨度 (/= '.') $ 颠倒 $ 显示 安装
            modName(模块名称) = 颠倒 $  1 修改版本
            旗帜 =  1 $ dropWhile(删除While) (/= '_') $ 颠倒 $ 弗拉格列夫
        在里面 (modName(模式名称), 旗帜)
      实例到模块名称对 _ = 错误 “不应该发生”
      被愚弄的人 实例 = 滤波器 ((>1) . 长度) $
                        分组依据 ((==) `在` 信噪比) $
                        排序依据 (比较 `上的` 信噪比) $
                        地图 实例到模块名称对 实例

获取实例类型 :: 12月 -> 类型
#如果MIN_VERSION_template_haskell(2,11,0)
获取实例类型 (实例D _ _ 第y天 _) = 第y天
#其他
获取实例类型 (实例D   _ 第y天 _) = 第y天
#结尾
获取实例类型 _                  = 错误 “不应该发生”

--|与initHFlags相同,但可以引入
--基于用户提供的标志值的编程默认值。
--
--第二个参数必须是获得以下内容的函数
--主义者:
--
--*默认值,
--
--*来自HFLAGS_*环境变量的值,
--
--*命令行选项。
--
--必须返回包含其他默认值的列表
--将覆盖默认标志值(但不覆盖用户提供的
--值:环境或命令行)。
--
--拼接后的类型为@String->DependentDefaults->IO[String]@。
--其中:
--
--*@type AList=[(String,String)]@
--
--*@type DependentDefaults=AList->AList->AList-->AList@
initHFlags相关默认值 :: ExpQ公司 --(字符串->依赖默认值->IO[String])
initH滞后依赖默认值 = 
  [| \程序描述 depDefaults(默认值) ->
        获取参数 >>= 初始化标志 depDefaults(默认值) 程序描述 $获取标志数据 |]

--|在执行其他操作之前,必须从main调用:
--
-->main=do args<-$initHFlags“简单程序v0.1”
-- >           ...
--
--/从版本0.2开始,您不能将initHFlags与程序描述放在括号中。只是/@$initHFlags@,/它更干净/
--
--在内部,它使用Template Haskell技巧收集所有
--Flag类的实例,然后生成对
--@initFlags@以及从这些
--实例添加到列表中。
--
--拼接后的类型为@String->IO[String]@。
初始化HFlags :: ExpQ公司 --(字符串->IO[String])
初始化HFlags = 
  [| \程序描述 ->
        获取参数 >>= 初始化标志 (常数 $ 常数 $ 常数 []) 程序描述 $获取标志数据 |]