在 Attoparsec 中解析时实现“包含”

Ale*_*lex 4 parsing haskell attoparsec

我正在写一个 DSL 是为了好玩。我决定使用 attoparsec,因为我对它很熟悉。

我想用这样的相对文件名实现包含的解析:

include /some/dir/file.ext
Run Code Online (Sandbox Code Playgroud)

或网址:

include http://blah.com/my/file.ext
Run Code Online (Sandbox Code Playgroud)

因此,当我解析时,我希望读取引用的资源并解析整个内容,将其内容附加到“外部”解析状态。

问题是,虽然这些语句的解析很容易,但我无法在我的 Attoparsec 解析器中运行 IO(据我所知)。

我如何使用 Attoparsec 来实现这一目标?我吞初始输入了使用一些字符串过滤,然后分析每个“块”到parsefeed相应?本质上是两遍解析方法?

Jon*_*rdy 5

Attoparsec 是纯的(Data.Attoparsec.Internal.Types.Parser不是转换器并且不包含IO),因此您无法直接从解析器中扩展包含是对的。

将解析器分成两遍似乎是正确的方法:一个遍就像 C 预处理器一样,接受一个文件,其中的include语句与其他内容交错。“其他东西”只需要在词法上基本有效,而不是你的完整解析器——就像 C 预处理器只关心标记和匹配括号,不匹配其他括号或任何语义。然后替换包含,生成一个完全扩展的文件,您可以将其提供给现有的解析器。

如果包含的文件在某种意义上必须在语法上是“独立的” ,那么您可以先解析整个文件,与includes交错,然后替换它们。例如:

-- 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]
Run Code Online (Sandbox Code Playgroud)

比如说,如果您只是使用 attoparsec 对无论如何都不能跨越文件边界的词法标记进行词法分析,或者您正在进行完整解析但想要禁止包含例如不匹配括号的包含文件。


另一种选择是IO通过使用不同的解析库(例如 megaparsec)直接嵌入到您的解析器中,它提供了一个ParsecT转换器,您可以将其环绕IOIO直接在解析器中执行。我可能会为原型这样做,但尽可能地将解析和扩展的问题分开似乎更整洁。