我正在编写一个玩具解析器(用于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}}不起作用而另一个(优雅的)解决方案本身并不存在,但我真的很好奇+
是否可以实现,如果是,如何实现。
¹)欢迎任何有更好名字的人发表评论。
²)我无法测试它,所以我不确定我的代码是否正常工作。