这里我要记住,可能的配置是规范树,每个规范都有相应的关键字(字符串)和类型。像这样:
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数据类型完全重组为更适合解析的方法吗?
答案 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)]
]
我不认为这是一个特别有用的例子,因为它只是检查你的配置数据是否有效,它不会提取它们,但我希望它能证明我的意思。