使用 ParsecT 进行排列解析?
Permutation parsing with `ParsecT`?
我有一个文件解析器,它使用 parsec
中的 permute
来组合各种 Parser
。看起来 permute
只允许 Identity
monad,我确信这会降低算法的复杂性。
现在我正在尝试将我所有的解析代码转换为使用 MyErrorClass m => ParsecT String () m
,其中 Monad m => MyErrorClass m
是具有特殊错误处理的 class
。具体来说,使用 permute
的代码现在需要组合需要 MyErrorClass m
的解析器,以便它们可以提供结构化错误,而不仅仅是使用 fail
。 (permute
仅在解析编程语言的模块配置文件时使用。文件格式允许嵌入某些语言结构,重用编译器的解析器。)
我只在 3 个地方使用 permute
,而且这个数字不太可能增加,所以为这 3 个中的每一个拼凑出某种一次性函数并不是不可能的. 或者,我可以删除字段排序的灵活性,但我宁愿不这样做。
在我做任何重要的事情之前,我是否缺少 parsec
中的简单解决方案?也许我可以以某种方式将 ParsecT String () m
从 parsers
填充到 permute
而不是 parsec
? (这会增加依赖性,但可能是值得的。)
编辑:
以下是有关为什么结构化错误对解析有用的一些背景知识。
在大多数情况下,我分别进行解析和验证。例如,在解析成功后检查重复的符号定义。
我经常遇到类似于 expected //, /* or argument type
的解析错误,可能需要一段时间(对我来说,语言创建者)才能理解正在调用的解析器。
MyErrorClass
允许结构化错误消息,例如
("In parsing of function declaration for " ++ show f) ??> do
-- parse the components of function f
-- any error messages here will be nested under the message above
能够像这样添加上下文至少会告诉用户解析器在失败时试图做什么。
最后,使用 MyErrorClass
也将允许 try
或 <|>
无法抑制的快速失败错误。 (在一些地方,我有使用过时语法的明确错误消息。)
正如@danidiaz 所建议的,parser-combinators
轻松解决了 文字 置换 ParsecT
.
的问题
需要注意的是,将 ParsecT
与 Monad m
一起使用 转换 错误(甚至它自己的错误)并不是特别有用,除了能够使用 <|>
和 try
.
无法忽略的快速失败错误
您不能将 ParsecT
的错误状态“降级”为 m
自己的错误类型,因为(据我所知)无法访问ParsecT
.
的当前错误状态
ParsecT
缺少与 StateT
的 mapStateT
相媲美的东西。这意味着如果不使用 runPT
(等)实际执行解析器,就无法转换存储在 m
中的错误,这是不可能进行中间解析的。
总而言之,ParsecT
不会像 StateT
那样泄露 m
,从而导致 m
的某些功能无法从 ParsecT
.
我有一个文件解析器,它使用 parsec
中的 permute
来组合各种 Parser
。看起来 permute
只允许 Identity
monad,我确信这会降低算法的复杂性。
现在我正在尝试将我所有的解析代码转换为使用 MyErrorClass m => ParsecT String () m
,其中 Monad m => MyErrorClass m
是具有特殊错误处理的 class
。具体来说,使用 permute
的代码现在需要组合需要 MyErrorClass m
的解析器,以便它们可以提供结构化错误,而不仅仅是使用 fail
。 (permute
仅在解析编程语言的模块配置文件时使用。文件格式允许嵌入某些语言结构,重用编译器的解析器。)
我只在 3 个地方使用 permute
,而且这个数字不太可能增加,所以为这 3 个中的每一个拼凑出某种一次性函数并不是不可能的. 或者,我可以删除字段排序的灵活性,但我宁愿不这样做。
在我做任何重要的事情之前,我是否缺少 parsec
中的简单解决方案?也许我可以以某种方式将 ParsecT String () m
从 parsers
填充到 permute
而不是 parsec
? (这会增加依赖性,但可能是值得的。)
编辑:
以下是有关为什么结构化错误对解析有用的一些背景知识。
在大多数情况下,我分别进行解析和验证。例如,在解析成功后检查重复的符号定义。
我经常遇到类似于 expected //, /* or argument type
的解析错误,可能需要一段时间(对我来说,语言创建者)才能理解正在调用的解析器。
MyErrorClass
允许结构化错误消息,例如
("In parsing of function declaration for " ++ show f) ??> do
-- parse the components of function f
-- any error messages here will be nested under the message above
能够像这样添加上下文至少会告诉用户解析器在失败时试图做什么。
最后,使用 MyErrorClass
也将允许 try
或 <|>
无法抑制的快速失败错误。 (在一些地方,我有使用过时语法的明确错误消息。)
正如@danidiaz 所建议的,parser-combinators
轻松解决了 文字 置换 ParsecT
.
需要注意的是,将 ParsecT
与 Monad m
一起使用 转换 错误(甚至它自己的错误)并不是特别有用,除了能够使用 <|>
和 try
.
您不能将
的当前错误状态ParsecT
的错误状态“降级”为m
自己的错误类型,因为(据我所知)无法访问ParsecT
.ParsecT
缺少与StateT
的mapStateT
相媲美的东西。这意味着如果不使用runPT
(等)实际执行解析器,就无法转换存储在m
中的错误,这是不可能进行中间解析的。
总而言之,ParsecT
不会像 StateT
那样泄露 m
,从而导致 m
的某些功能无法从 ParsecT
.