使用开始和结束符号使用 Megaparsec 解析块注释
Parsing block comments with Megaparsec using symbols for start and end
我想使用 Megaparsec 在 Haskell 中解析与此类似的文本。
# START SKIP
def foo(a,b):
c = 2*a # Foo
return a + b
# END SKIP
,其中 # START SKIP
和 # END SKIP
标记要解析的文本块的开始和结束。
与 skipBlockComment 相比,我希望解析器 return 开始和结束标记之间的行。
这是我的解析器。
skip :: Parser String
skip = s >> manyTill anyChar e
where s = string "# START SKIP"
e = string "# END SKIP"
skip
解析器按预期工作。
为了在开始和结束标记内允许可变数量的白色 space,例如 # START SKIP
我尝试了以下方法:
skip' :: Parser String
skip' = s >> manyTill anyChar e
where s = symbol "#" >> symbol "START" >> symbol "SKIP"
e = symbol "#" >> symbol "END" >> symbol "SKIP"
使用skip'
解析上面的文字出现如下错误。
3:15:
unexpected 'F'
expecting "END", space, or tab
我想了解此错误的原因以及如何修复它。
正如 Alec 已经评论的那样,问题是一旦 e
遇到 '#'
,它就被视为 消耗的字符 。 parsec 及其衍生物的工作方式是,一旦您使用了任何字符,您就会致力于该解析分支——即 manyTill anyChar
替代方案不再被考虑,即使 e
最终在这里失败了。
尽管如此,您可以通过将结束分隔符包装在 try
:
中轻松请求回溯
skip' :: Parser String
skip' = s >> manyTill anyChar e
where s = symbol "#" >> symbol "START" >> symbol "SKIP"
e = try $ symbol "#" >> symbol "END" >> symbol "SKIP"
这将在使用 '#'
之前设置一个“检查点”,并且当 e
稍后失败时(在您的示例中,在 "Foo"
),它将表现得好像没有字符完全匹配。
事实上,传统的秒差距也会为 skip
提供相同的行为。只是,因为寻找一个字符串并且只有在它完全匹配 时才会成功是一项如此常见的任务,megaparsec 的 string
的实现方式类似于 try . string
,即如果失败发生在该固定字符串中,它将始终回溯。
但是,复合解析器默认情况下仍然不会回溯,就像它们在 attoparsec 中所做的那样。主要原因是,如果任何东西都可以回溯到任何一点,你就无法真正在错误消息中显示一个明确的失败点。
我想使用 Megaparsec 在 Haskell 中解析与此类似的文本。
# START SKIP
def foo(a,b):
c = 2*a # Foo
return a + b
# END SKIP
,其中 # START SKIP
和 # END SKIP
标记要解析的文本块的开始和结束。
与 skipBlockComment 相比,我希望解析器 return 开始和结束标记之间的行。
这是我的解析器。
skip :: Parser String
skip = s >> manyTill anyChar e
where s = string "# START SKIP"
e = string "# END SKIP"
skip
解析器按预期工作。
为了在开始和结束标记内允许可变数量的白色 space,例如 # START SKIP
我尝试了以下方法:
skip' :: Parser String
skip' = s >> manyTill anyChar e
where s = symbol "#" >> symbol "START" >> symbol "SKIP"
e = symbol "#" >> symbol "END" >> symbol "SKIP"
使用skip'
解析上面的文字出现如下错误。
3:15:
unexpected 'F'
expecting "END", space, or tab
我想了解此错误的原因以及如何修复它。
正如 Alec 已经评论的那样,问题是一旦 e
遇到 '#'
,它就被视为 消耗的字符 。 parsec 及其衍生物的工作方式是,一旦您使用了任何字符,您就会致力于该解析分支——即 manyTill anyChar
替代方案不再被考虑,即使 e
最终在这里失败了。
尽管如此,您可以通过将结束分隔符包装在 try
:
skip' :: Parser String
skip' = s >> manyTill anyChar e
where s = symbol "#" >> symbol "START" >> symbol "SKIP"
e = try $ symbol "#" >> symbol "END" >> symbol "SKIP"
这将在使用 '#'
之前设置一个“检查点”,并且当 e
稍后失败时(在您的示例中,在 "Foo"
),它将表现得好像没有字符完全匹配。
事实上,传统的秒差距也会为 skip
提供相同的行为。只是,因为寻找一个字符串并且只有在它完全匹配 时才会成功是一项如此常见的任务,megaparsec 的 string
的实现方式类似于 try . string
,即如果失败发生在该固定字符串中,它将始终回溯。
但是,复合解析器默认情况下仍然不会回溯,就像它们在 attoparsec 中所做的那样。主要原因是,如果任何东西都可以回溯到任何一点,你就无法真正在错误消息中显示一个明确的失败点。