【问题标题】:Haskell Parsec and Unordered PropertiesHaskell Parsec 和无序属性
【发布时间】:2010-09-14 04:36:27
【问题描述】:

我正在尝试使用 Parsec 解析如下内容:

property :: CharParser SomeObject
property = do
    name
    parameters
    value
    return SomeObjectInstance { fill in records here }

我正在实现 iCalendar 规范,并且在每一个 like 上都有一个 name:parameters:value 三元组,非常类似于 XML 有一个 name:attributes:content 三元组的方式。事实上you could very easily convert an iCalendar into XML format(以为我真的看不到优势)。

我的意思是,参数根本不必按任何顺序排列,每个参数可能有不同的类型。一个参数可能是一个字符串,而另一个是另一个元素的数字 id。它们可能还没有相似之处,最后,我想将它们正确放置在我希望解析器返回的任何“SomeObjectInstance”的正确记录字段中。我该怎么做这种事情(或者你能给我举一个例子,说明有人必须解析这样的数据)?

谢谢,我知道我的问题可能有点混乱,但这反映了我对自己需要做什么的理解程度。

编辑:我试图避免给出预期的输出(因为它很大,而不是因为它是隐藏的)但这里是一个输入文件的示例(来自维基百科):

开始:VCALENDAR
版本:2.0
产品:-//hacksw/handcal//NONSGML v1.0//EN
开始:事件
UID:uid1@example.com
DTSTAMP:19970714T170000Z
组织者;CN=John Doe:MAILTO:john.doe@example.com
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
摘要:巴士底日派对
结束:VEVENT
END:VCALENDAR

如您所见,它在 VCalendar 中包含一个 VEvent,我创建了 data structures that represent them here

我正在尝试编写一个解析器,将这种类型的文件解析到我的数据结构中,但我被困在需要处理任何类型的任何顺序的属性的位上;日期、时间、整数、字符串、uid 等。我希望在不重复整个 iCalendar 规范的情况下更有意义。

【问题讨论】:

  • 我不确定你在问什么。你能举一个可能的输入和预期输出的例子吗?
  • 我提供了更多信息,我希望它现在更有意义,您已经期望输入和我试图用解析器填充的数据结构。现在我只是想弄清楚如何用 parsec 做到这一点。

标签: haskell properties icalendar parser-generator parsec


【解决方案1】:

Parsec 具有 Parsec.Perm 模块,可以精确地解析无序但线性(即在语法树中的同一级别)元素,例如 XML 文件中的属性标签。

不幸的是,Perm 模块大多没有文档记录。最好的参考是 Haddock 文档页面引用的 Parsing Permutation Phrases 论文,但即便如此,它也主要是对该技术的描述,而不是如何使用它。

【讨论】:

  • 哇,你是对的,这是很好的建议。我会尽快阅读那篇文章,如果事实证明这正是我所需要的,那么我什至可以在上面写一篇博文。
  • Parsing Permutation Phrases 的第 5 节有两个例子。原始 Parsec 手册也有一个简单的例子——它可能仍然有用,因为 Parsec 和 PPP 论文之间的组合器名称不同。 Parsec 手册可在此处获得:legacy.cs.uu.nl/daan/parsec.html
  • 对于寻找此类问题解决方案的其他人,罗伯特确实写了一篇关于该主题的博客文章:robertmassaioli.wordpress.com/2010/09/15/… 不幸的是,博客软件似乎正在剥离某些字符。我已经添加了缺失的部分并将其发布在这里:hpaste.org/46655/simple_example_of_parsec_perm 感谢您编写了一个简单的示例 Robert。
【解决方案2】:

好的,所以在BEGIN:VEVENTEND:VEVENT 之间,你有很多键值对。所以写一个规则keyValuePair返回(key, value)。现在在VEVENT 的规则中,您执行many KeyValuePair 以获取对列表。完成后,您可以使用折叠来使用给定值填充 VEVENT 记录。在你给 fold 的函数中,你使用模式匹配来找出在哪个字段中存储值。作为累加器的起始值,您使用 VEvent 记录,其中可选字段设置为Nothing。示例:

pairs <- many keyValuePairs
vevent = foldr f (VEvent {sequence = Nothing}) pairs
    where f ("SUMMARY", v) ve = ve {summary = v}
          f ("DSTART", v) ve = ve {dstart = read v}

...等等。对其他组件执行相同操作。

编辑:这是折叠的一些可运行示例代码:

data VEvent = VEvent {
        summary :: String,
        dstart :: String,
        sequenceSt :: Maybe String
        } deriving Show

vevent pairs = foldr f (VEvent {sequenceSt = Nothing}) pairs
    where f ("SUMMARY", v) ve = ve {summary = v}
          f ("DSTART", v) ve = ve {dstart = v}
          f ("SEQUENCEST", v) ve = ve {sequenceSt = Just v}

main = do print $ vevent [("SUMMARY", "lala"), ("DSTART", "lulu")]
          print $ vevent [("SUMMARY", "lala"), ("DSTART", "lulu"), ("SEQUENCEST", "lili")]

输出:

VEvent {summary = "lala", dstart = "lulu", sequenceSt = Nothing}
VEvent {summary = "lala", dstart = "lulu", sequenceSt = Just "lili"}

请注意,这将在编译时产生警告。为避免该警告,请将所有非可选字段显式初始化为 undefined

【讨论】:

  • 这与我最初的想法相似,但是您如何确定“零”是什么?您将“零”作为“VEvent {sequence = Nothing}”给出,但是如果结构像我制作的结构一样复杂,如果它有一堆必须设置的整数字段而您没有设置怎么办?不想让每个记录字段都成为可能。您可以使用多个一开始没有提供的必填字段逐个制作记录吗? (如果我需要更好地描述这个新问题,请告诉我,我会尝试提供代码示例)
  • @Robet:正如我所说,将可选的初始化为Nothing。其他的你根本不需要初始化。如果没有可选值,Vevent {} 将是一个非常好的起始值(或者您可以将每个非可选值显式设置为 undefined 以避免收到警告,但我想这会对很多字段感到厌烦)。
  • @Robert:既然你做了dstart String的类型,你需要去掉read。另外如果你想看到结果,你需要在VEvent的声明中添加deriving Show。一旦我执行了这些更改,您的馅饼就可以在 ghci 中按预期工作。
  • 哦,我刚刚再次阅读了您的评论并将所有内容设置为未定义的作品。规范说,将字段留空应该将它们设置为未定义,但它不适合我。你知道这是为什么吗?
  • @Robert:如果我刚刚编辑的示例对您不起作用,那么发生了一些奇怪的事情。如果它确实有效,那么您在测试它时一定做了一些不同的事情。
猜你喜欢
  • 2017-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
相关资源
最近更新 更多