已修改以获得更完整的问题:
我想创建一个解析器(我正在使用uu-parsinglib),它获取前一个解析器的结果,如果结果包含某个构造函数,则有条件地失败:
我现在意识到这必须是一个monadic解析器。
我的语法包含非直接左递归。下面说明了问题,现实情况稍微复杂一点:
data Field =
Field_A A
Field_B B
Field_C C
Field_D String
data A =
A Prefix String
data B =
B Prefix String
data C =
C Prefix String
data Prefix =
Prefix Field
大多数时候我只对Field感兴趣,并且为了尽量减少回溯,最好关注这种情况。
我已经定义了一个帮助
的运算符(<..>) :: IsParser p => p (a -> b) -> p (b -> c) -> p (a -> c)
g <..> f = (.) <$> f <*> g
我将问题解决为:
pField :: Parser Field
pField =
( Field_D <$> pString ) <??>
pChainl' ( pReturn (helper) <*> pPrefix' ) ( pA' <<|> pB' <<|> pC' )
where pChainl' :: IsParser p => p (f -> (pre -> f) -> f) ->
p (pre -> f) ->
p (f -> f)
pChainl' op x = must_be_non_empties "pChainl'" op x (
flip f <$> pList1 (flip <$> op <*> x)
)
f x [] = x
f x (func:rest) = f (func x) rest
helper :: (Field -> Prefix) ->
Field ->
(Prefix -> Field) ->
Field
helper p i n = n $ p i
注意我已经定义了一个pChainl的变体,它允许传入初始字段,同时保持左关联。
pA' :: Parser (Prefix -> Field)
pA' = ( (flip A) <$> pString ) <..> pReturn Field_A
pB' :: Parser (Prefix -> Field)
pB' = ( (flip B) <$> pString ) <..> pReturn Field_B
pC' :: Parser (Prefix -> Field)
pC' = ( (flip C) <$> pString ) <..> pReturn Field_C
-- This consumes no input
pPrefix' :: Parser (Field -> Prefix)
pPrefix' = pReturn Prefix
问题 我想定义
pA :: Parser A
就pField而言,如果最右边的Field构造函数不是Field_A,则后置过滤器会失败。正如已经正确指出的那样,这是一个monadic解析。我找不到任何使用uu-parsinglib作为monadic解析器的令人信服的例子,那么你建议的方法是什么?
如果我正在咆哮错误的树,请告诉我。
答案 0 :(得分:1)
似乎你可以创建一个通用的条件解析器,只有在解析器返回的值通过一些测试时才会成功。这当然使用monad功能。我不确定这是否与uu-parsinglib有关。它似乎在我的测试中工作正常,但有一个例外:当条件失败并且没有其他解析器可用于消耗输入时,库会抛出异常。 (没有给出纠正步骤的内容......)
pConditional :: Parser a -> (a -> Bool) -> Parser a
pConditional p test = p >>= (\result -> case (test result) of
True -> pure result
False -> empty)
我还想知道自由使用这种条件解析器会产生的其他陷阱。 (如果有的话。)
答案 1 :(得分:0)
我想我找到了解决方案。我仍然有兴趣听取关于解析这种间接左递归的最佳方法的想法。
建议的解决方案是
pA :: Parser A
pA = do
a <- pField
case a of
(Field_A r) -> return r
otherwise -> pFail