在Parsec中定义一个新的原始组合子

时间:2016-02-17 19:22:04

标签: parsing haskell parsec parser-combinators

问题背景

我正在编写一个玩具解析器(用于Scheme),它应该能够将-+区分为标识符和数字的一部分(例如+i,{ {1}},-2.4)。我想尝试解析任何以+5+开头的数字作为数字,但有一个问题:如果解析消耗了符号,但没有符号之后的任何字符,那么我希望它如果它被包裹在-中,但是如果在符号之后消耗了任何输入,那么我希望它失败,获得一个很好的上下文错误消息和违规字符的行号/位置;将整个解析器包装在try中总是会回溯,这不是我想要的。

新组合子:try

为此,我想创建一个组合器,我称之为followedBy¹,它反映了绑定(followedBy)并采用了解析器>>=和一个函数{{1}返回一个解析器,

m

并充当k,除非followedBy :: ParsecT s u m a -> (a -> ParsecT s u m b) -> ParsecT s u m b m `followedBy` k = ... 消耗正常且>>=为空时失败,在这种情况下我希望组合器清空失败。然后可以这样使用该组合器:

m

尝试解析为数字,如果它是部分数字但是格式错误则失败,如果k后面的数字根本不是数字,则为空。

低级别实施

我已调整(char '+') `followedBy` (\sign -> parseUnsignedNumber sign) 的{​​{1}}代码来完成此操作²,但由于它使用的是Parsec未导出的符号,因此我无法编译它。有没有办法使用Parsec导出的高级构造编写相同的东西?

+的我(不可编译)实现是:

parserBind

可能的替代方案

我知道还有一个替代方案,即Text.Parsec.Prim - 解析followedBy作为标识符,沿import Text.Parsec.Prim followedBy :: ParsecT s u m a -> (a -> ParsecT s u m b) -> ParsecT s u m b -- Code adjusted from parserBind in Text.Parsec.Prim. Changed is that mcok.peerr uses eerr instead. followedBy m k = ParsecT $ \s cok cerr eok eerr -> let -- consumed-okay case for m mcok x s' err = let -- if (k x) consumes, those go straight up pcok = cok pcerr = cerr -- if (k x) doesn't consume input, but is okay, -- we still return in the consumed continuation peok x s err' = cok x s (mergeError err err') -- if (k x) doesn't consume input, but errors, -- then we return the empty error (not consuming m or k) peerr err' = eerr (mergeError err err') in unParser (k x) s pcok pcerr peok peerr -- empty-ok case for m meok x s err = let -- in these cases, (k x) can return as empty pcok = cok peok x s err' = eok x s (mergeError err err') pcerr = cerr peerr err' = eerr (mergeError err err') in unParser (k x) s pcok pcerr peok peerr -- consumed-error case for m mcerr = cerr -- empty-error case for m meerr = eerr in unParser m s mcok mcerr meok meerr 行,如果{try我将采用此路线1}}不起作用而另一个(优雅的)解决方案本身并不存在,但我真的很好奇+是否可以实现,如果是,如何实现。

¹)欢迎任何有更好名字的人发表评论。

²)我无法测试它,所以我不确定我的代码是否正常工作。

0 个答案:

没有答案