在 Attoparsec 中解析时实现 "includes"
Implementing "includes" when parsing in Attoparsec
我写 DSL 是为了好玩。我决定使用 attoparsec 因为我很熟悉它。
我想像这样实现包含相对文件名的解析:
include /some/dir/file.ext
或网址:
include http://blah.com/my/file.ext
所以当我解析时,我希望读取引用的资源并解析整个内容,将其内容附加到 "outer" 解析状态。
问题是虽然这些语句的解析很容易,但我无法在我的 Attoparsec 解析器中 运行 IO(据我所知)。
如何使用 Attoparsec 来实现这一点?我是否使用一些字符串过滤将初始输入切碎,然后将每个 "block" 相应地解析为 parse
和 feed
?本质上是一种两遍解析方法?
Attoparsec 是纯的(Data.Attoparsec.Internal.Types.Parser
不是转换器,也不包含 IO
)所以你是对的,你不能直接从解析器中扩展包含。
将解析器分成两个阶段似乎是正确的方法:一个阶段就像 C 预处理器一样,接受一个包含 include
语句和其他内容交错的文件。 “其他东西”只需要基本上在词法上有效,而不是你的完整解析器——就像 C 预处理器只关心标记和匹配括号,而不是匹配其他括号或任何语义。然后替换包含,生成一个完全扩展的文件,您可以将其提供给现有的解析器。
如果一个包含的文件在某种意义上必须在语法上是“独立的”†,那么你可以先解析整个文件,与include
交错,然后替换他们。例如:
-- Whatever items you’re parsing.
data Item
-- A reference to an included path.
data Include = Include FilePath
parse :: Parser [Either Include Item]
-- Substitute includes; also calls ‘parse’
-- recursively until no includes remain.
substituteIncludes :: [Either Include Item] -> IO [Item]
† 比如说,如果你只是使用 attoparsec 来对不能跨越文件边界的词法标记进行词法分析,或者你正在进行完整的解析但想 禁止一个包含例如不匹配的括号。
另一种选择是通过使用不同的解析库(例如 megaparsec)将 IO
直接嵌入到您的解析器中,它提供了一个 ParsecT
转换器,您可以将 IO
环绕到直接在您的解析器中执行 IO
。我可能会为原型这样做,但尽可能将解析和扩展的关注点分开似乎更整洁。
我写 DSL 是为了好玩。我决定使用 attoparsec 因为我很熟悉它。
我想像这样实现包含相对文件名的解析:
include /some/dir/file.ext
或网址:
include http://blah.com/my/file.ext
所以当我解析时,我希望读取引用的资源并解析整个内容,将其内容附加到 "outer" 解析状态。
问题是虽然这些语句的解析很容易,但我无法在我的 Attoparsec 解析器中 运行 IO(据我所知)。
如何使用 Attoparsec 来实现这一点?我是否使用一些字符串过滤将初始输入切碎,然后将每个 "block" 相应地解析为 parse
和 feed
?本质上是一种两遍解析方法?
Attoparsec 是纯的(Data.Attoparsec.Internal.Types.Parser
不是转换器,也不包含 IO
)所以你是对的,你不能直接从解析器中扩展包含。
将解析器分成两个阶段似乎是正确的方法:一个阶段就像 C 预处理器一样,接受一个包含 include
语句和其他内容交错的文件。 “其他东西”只需要基本上在词法上有效,而不是你的完整解析器——就像 C 预处理器只关心标记和匹配括号,而不是匹配其他括号或任何语义。然后替换包含,生成一个完全扩展的文件,您可以将其提供给现有的解析器。
如果一个包含的文件在某种意义上必须在语法上是“独立的”†,那么你可以先解析整个文件,与include
交错,然后替换他们。例如:
-- Whatever items you’re parsing.
data Item
-- A reference to an included path.
data Include = Include FilePath
parse :: Parser [Either Include Item]
-- Substitute includes; also calls ‘parse’
-- recursively until no includes remain.
substituteIncludes :: [Either Include Item] -> IO [Item]
† 比如说,如果你只是使用 attoparsec 来对不能跨越文件边界的词法标记进行词法分析,或者你正在进行完整的解析但想 禁止一个包含例如不匹配的括号。
另一种选择是通过使用不同的解析库(例如 megaparsec)将 IO
直接嵌入到您的解析器中,它提供了一个 ParsecT
转换器,您可以将 IO
环绕到直接在您的解析器中执行 IO
。我可能会为原型这样做,但尽可能将解析和扩展的关注点分开似乎更整洁。