我之前已经问过这个问题(here)但事实证明所提供的解决方案并未处理所有测试用例。另外,我需要' Text'解析器而不是' String',所以我需要parsec3。
好的,解析器应该允许引号之间的每种类型的char,甚至是引号。引用文本的末尾标有'字符,后跟|,输入的空格或结尾。
所以,
' AA'''' |
应该返回一个字符串
AA'''
这就是我所拥有的:
import Text.Parsec
import Text.Parsec.Text
quotedLabel :: Parser Text
quotedLabel = do -- reads the first quote.
spaces
string "'"
lab <- liftM pack $ endBy1 anyChar endOfQuote
return lab
endOfQuote = do
string "'"
try(eof) <|> try( oneOf "| ")
现在,问题在于eof
的类型与oneOf "| "
的类型不同,因此编译会失败。
我该如何解决这个问题?有没有更好的方法来实现我想要做的事情?
答案 0 :(得分:2)
要更改任何仿函数计算的结果,您可以使用:
fmap (const x) functor_comp
e.g:
getLine :: IO String
fmap (const ()) getLine :: IO ()
eof :: Parser ()
oneOf "| " :: Parser Char
fmap (const ()) (oneOf "| ") :: Parser ()
另一种选择是使用Control.Applicative
中的运算符:
getLine *> return 3 :: IO Integer
执行getLine
,丢弃结果并返回3.
在您的情况下,您可以使用:
try(eof) <|> try( oneOf "| " *> return ())
答案 1 :(得分:2)
<强>空白强>
首先评论处理空白区域......
通常的做法是编写解析器以便它们 消耗令牌后面的空格 或句法单位。定义组合器是很常见的:
lexeme p = p <* spaces
轻松将解析器 p 转换为丢弃空格的解析器 遵循 p 分析的任何内容。例如,如果你有
number = many1 digit
只要你想吃掉它就可以使用lexeme number
数字后面的空格。
有关处理空白和其他建议的更多方法 解析语言时,请参阅this Megaparsec tutorial。
标签表达
根据your previous SO question显示您想要的内容 解析表单的表达式:
label1 | label2 | ... | labeln
其中每个标签可以是简单标签或带引号的标签。
解析此模式的惯用方法是使用sepBy
,如下所示:
labels :: Parser String
labels = sepBy1 (try quotedLabel <|> simpleLabel) (char '|')
我们定义了simpleLabel和quotedLabel 它们中可能出现什么字符。对于simpleLabel有效 字符是非|和非空间:
simpleLabel :: Parser String
simpleLabel = many (noneOf "| ")
quotedLabel是单引号,后跟运行 有效的quotedLabel字符后跟一个结尾 单引号:
sq = char '\''
quotedLabel :: Parser String
quotedLabel = do
char sq
chs <- many validChar
char sq
return chs
validChar是非单引号或单引号 引号后面没有eof或竖条:
validChar = noneOf [sq] <|> try validQuote
validQuote = do
char sq
notFollowedBy eof
notFollowedBy (char '|')
return sq
如果仅出现单引号,则第一个notFollowedBy
将失败
在输入结束之前。如果第二个notFollowedBy
将失败
下一个字符是垂直条。因此两者的顺序
只有在跟随非垂直条形字符时才会成功
单引号。在这种情况下,应该解释单引号
作为字符串的一部分而不是终止单引号。
不幸的是,这并不是很有效,因为
notFollowedBy
的当前实施
将永远成功使用一个不消耗任何东西的解析器
输入 - 即像eof
。 (有关详细信息,请参阅this issue。)
要解决此问题,我们可以使用此替代方案 实现:
notFollowedBy' :: (Stream s m t, Show a) => ParsecT s u m a -> ParsecT s u m ()
notFollowedBy' p = try $ join $
do {a <- try p; return (unexpected (show a));}
<|> return (return ())
这是完整的解决方案,包含一些测试。添加一些lexeme
调用你可以让这个解析器占用你决定的任何空白区域
它并不重要。
import Text.Parsec hiding (labels)
import Text.Parsec.String
import Control.Monad
notFollowedBy' :: (Stream s m t, Show a) => ParsecT s u m a -> ParsecT s u m ()
notFollowedBy' p = try $ join $
do {a <- try p; return (unexpected (show a));}
<|> return (return ())
sq = '\''
validChar = do
noneOf "'" <|> try validQuote
validQuote = do
char sq
notFollowedBy' eof
notFollowedBy (char '|')
return sq
quotedLabel :: Parser String
quotedLabel = do
char sq
str <- many validChar
char sq
return str
plainLabel :: Parser String
plainLabel = many (noneOf "| ")
labels :: Parser [String]
labels = sepBy1 (try quotedLabel <|> try plainLabel) (char '|')
test input expected = do
case parse (labels <* eof) "" input of
Left e -> putStrLn $ "error: " ++ show e
Right v -> if v == expected
then putStrLn $ "OK - got: " ++ show v
else putStrLn $ "NOT OK - got: " ++ show v ++ " expected: " ++ show expected
test1 = test "a|b|c" ["a","b","c"]
test2 = test "a|'b b'|c" ["a", "b b", "c"]
test3 = test "'abc''|def" ["abc'", "def" ]
test4 = test "'abc'" ["abc"]
test5 = test "x|'abc'" ["x","abc"]