使用Parsec解析配置

时间:2012-02-04 22:17:00

标签: haskell parsec

这里我要记住,可能的配置是规范树,每个规范都有相应的关键字(字符串)和类型。像这样:

data Select = And | Or
data ConfigTree = Node Select [ConfigTree] | Leaf (String, *)

我不确定如何正确地写这个,因为没有“类型的类型”,但暂时没关系。

现在,给定这样一棵树,我想构建一个可以读取可能有效配置的解析器;我假设我已经有了解析关键字/类型对的子解析器。

例如,可能的配置树是:

Node And [ Leaf ("width", Double)
         , Node Or [ Leaf ("height", Double) , Leaf ("aspectratio", Double)
         ]

可以指定矩形的大小。可能的配置文件是:

aspectratio = 2
width = 10

(假设配置文件只是换行符分隔对的列表,关键字= blah,其中blah是该关键字可以处理的相应解析器;但它们可以按任何顺序排列,只需要匹配树的一个可能的“有效子集”,其中有效子集是包含顶部节点的任何子集,其包含它包含的“和”节点的所有子节点,并且恰好说出“或”节点的一个子节点包含的内容。)

我不知道如何开始构建这样的解析器。任何人都可以提供一些有关如何继续的提示,或者将上述ConfigTree数据类型完全重组为更适合解析的方法吗?

1 个答案:

答案 0 :(得分:2)

为此构建解析器的问题是您的输入格式根本不符合您的数据类型。输入格式是一个简单,易于解析的键值对列表,而您的数据类型是树。例如。要确定And节点中的所有子树是否有效,您必须知道完整的输入。

因此,不是直接在解析器中验证键值对列表,而是在之后执行。

我举了一个小例子来说明我的意思:

data Type = TDouble | TString 
data Select = And | Or 
data ConfigTree = Node Select [ConfigTree] | Leaf (String, Type)

-- matches a list of key-value pairs against a tree
match :: [(String, String)] -> ConfigTree -> Bool
match sts (Leaf (s, t)) = case filter ((== s) . fst) sts of
                            -- we don't want multiple occurences of a key
                            [(_, v)] -> if valid v t then True else False 
                            _        -> False
match sts (Node And cfgs) = and . map (match sts) $ cfgs
-- not completely what you described, because it will match 1 or more
match sts (Node Or cfgs)  = or  . map (match sts) $ cfgs

-- validates a string against a type
valid :: String -> Type -> Bool
valid s TDouble = case reads s :: [(Double, String)] of
                    [(_, "")] -> True
                    _         -> False
valid _ TString = True

-- this is what you actually parsed
config = [ ("aspectratio", "2")
         , ("width", "123")
         , ("name", "Sam")
         ]

-- the example tree
cfgTree = Node And [ Leaf ("width", TDouble)
                   , Node Or [ Leaf ("height", TDouble), Leaf ("aspectratio", TDouble)]
                   ]

我不认为这是一个特别有用的例子,因为它只是检查你的配置数据是否有效,它不会提取它们,但我希望它能证明我的意思。