使用 Parsec 解析配置

发布于 2025-01-03 02:36:26 字数 775 浏览 0 评论 0原文

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

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

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

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

Here I have in mind that a possible configuration is a tree of specifications, each specification has a corresponding keyword (the string) and type. Something like this:

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

I'm not sure how to write this properly, given that there's no "type of types", but nevermind that for the moment.

Now, given such a tree, I want to build a parser that can read a possible valid configuration; I assume I already have sub-parsers that can parse keyword/type pairs.

For instance, a possible configuration tree is:

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

which can specify the size of a rectangle. A possible configuration file would be, say:

aspectratio = 2
width = 10

(Let's assume that a configuration file is just a list of newline separated pairs, keyword = blah, where blah is something the corresponding parser for that keyword can deal with; but they can be in any order, and just have to match up with one possible "valid subset" of the tree, where a valid subset is any subset containing the top node, that contains all the children of an "and" node it contains, and say exactly one child of an "or" node it contains.)

I have no idea how to even start building such a parser. Can anyone give some tips about how to proceed, or a way to completely restructure the above ConfigTree datatype to something more amenable to parsing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ゝ偶尔ゞ 2025-01-10 02:36:26

为此构建解析器的问题是您的输入格式根本与您的数据类型不匹配。输入格式是简单、易于解析的键值对列表,而数据类型是树。例如,要确定 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)]
                   ]

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

The problem with building a parser for this, is that your input format doesn't match your data type at all. The input format is a simple, easily parsable list of key-value pairs while your data type is a tree. E.g. to determine if all the sub-trees in an And node are valid you have to know the complete input.

So, instead of doing validating the list of key-value pairs directly in the parser, just do it afterwards.

I've put together a small example to show what I mean:

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)]
                   ]

I don't think that this is a particularly useful example, because all it does is check if your config data are valid, it doesn't extract them, but I hope it demonstrates what I meant.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文