如果解析器失败,请尝试从下一个特殊符号出现时进行解析



假设有一些解析器:

valid :: Parser String
valid = string "valid" <* skipWhile (/= 'n')

它可用于从多行文本中获取"valid"字符串:

> parseOnly (many $ valid <* optional endOfLine) "validnvalidnvalid"
Right ["valid","valid","valid"]

如果有一行解析器失败valid则根本不会解析进一步的文本:

> parseOnly (many $ valid <* optional endOfLine) "validninvalidnvalid"
Right ["valid"]

如何获得Rigth["valid", "valid"]?我认为try在这里可能会有所帮助,但不确定如何从下一行继续解析。

使用 parsec:

-- parser for the rest of the line
rest = manyTill anyChar (eof <|> char 'n' *> return ()) <* optional (char 'n')
-- change this to accept lines, but Just the valid ones
valid :: Parser (Maybe String)
valid = (Just <$> string "valid" <|> const Nothing <$> anyChar) <* rest
-- filter out Nothing
valids = catMaybes <$> many valid
-- Run
*Foo> runParser valids () "input" "valid1ninvvalid2nvalid3"
Right ["valid","valid"]
*Foo> runParser valids () "input" "valid1nvalid2nvalid3"
Right ["valid","valid","valid"]

在这里,我必须做一个错误的黑客:const Nothing <$> anyChar这样valid总是至少消耗一些东西,否则我不能把它交给many.但是使用Maybe您可以根据需要重写解析器(例如强制换行符)

非常相似的方法适用于attoparsec,很抱歉破坏了自己制作的乐趣。

{-# LANGUAGE OverloadedStrings #-}
import Data.Attoparsec.Text
import Control.Applicative
import Data.Maybe
import Data.Text
-- parser for the rest of the line
rest = skipWhile (/= 'n') <* optional endOfLine
-- change this to accept lines, but Just the valid ones
valid :: Parser (Maybe Text)
valid = (Just <$> string "valid" <|> const Nothing <$> anyChar) <* rest
-- filter out Nothing
valids = catMaybes <$> many valid
*Main> parseOnly valids "valid1nvalid2nvalid3"
Right ["valid","valid","valid"]
*Main> parseOnly valids "valid1ninvalid2nvalid3"
Right ["valid","valid"]

相关内容

最新更新