40行Haskell代码的命令行参数解析器

40行Haskell代码的命令行解析器

第一步:定义选项结果的数据类型

为了简单起见,考虑三种情况:

  1. 独立选项:即没有任何先导符号,独立存在的选项。
  2. 开关选项:选项即自身,表示开启或者关闭某个功能。
  3. 带值选项:选项是前导符号加上后面的一个参数,表示取一个值(按字符串)

例子:

program -i input.txt -o output.txt s.log -k -v 
  • -i 为带值选项,表示输入文件名
  • -o 为带值选项,表示输出文件名
  • -k-v是开关选项
  • s.log 为独立选项

我们使用Haskell的代数数据类型(ADT)定义上述选项:

data CmdOptionResult = Standalone   {                  value :: String }
                     | OptionSwitch { name  :: String, flag  :: Bool   }
                     | OptionValue  { name  :: String, value :: String } deriving(Eq, Show, Read)

第二步:基本的解析器

  • 开关解析器
parseSwitch _ [] = (Nothing, [])
parseSwitch (sname, lname) args@(arg:as)
    = if arg == lname || arg == sname
      then (Just (OptionSwitch lname True), as)
      else (Nothing, args)
  • 带值选项解析器
parseValue _ [] = (Nothing, [])
parseValue (sname, lname) args@(arg:as)
    = if arg == lname || arg == sname
      then (Just (OptionValue lname (head as)), tail as)
      else (Nothing, args)

解析器组合子

  • 算符 <|>.
parser1 <|> parser2
    = (\as -> let (v1, s1) = parser1 as
                  (v2, s2) = parser2 as
              in  if v1 /= Nothing
                  then (v1, s1)
                  else (v2, s2))
  • 多个选项的解析器的聚合
aggregate ps = foldl1 (<|>) ps
  • 将解释器重复应用在一串参数上
repeat ps = let parser = aggregate ps
                fromJust (Just x) = x
            in  \args -> let (v, s) = parser args
                         in  if args == []
                             then []
                             else if v == Nothing --  match failed, so it is a standalone option value
                                  then (Standalone (head args)):(Util.CmdParser.repeat ps (tail args))

例子

  • 测试例子
parser_lst_example = [ parseSwitch ("-v", "--verbose")
                     , parseValue  ("-i", "--input")
                     , parseValue  ("-o", "--output")
                     , parseValue  ("-m", "--mode")
                     , parseSwitch ("-h", "--help") ]

parser_example = Util.CmdParser.repeat parser_lst_example

args_example_1 = ["-i", "input.txt", "--output", "output.txt", "-v", "-m", "1"]
args_example_2 = ["-h"]
  • 解析结果
parser_example args_example_1
[OptionValue {name = "--input", value = "input.txt"},OptionValue {name = "--output", value = "output.txt"},OptionSwitch {name = "--verbose", flag = True},OptionValue {name = "--mode", value = "1"}]
*Util.CmdParser> parser_example args_example_2
[OptionSwitch {name = "--help", flag = True}]
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明AI阅读 16,033评论 3 119
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,292评论 19 139
  • 夕阳好(236)(9) □曾晓琴 走在 群山 之间 夕阳拉长了我的影子 抒发我对君的...
    朝花夕拾123阅读 274评论 2 10