将多行解析为 Haskell 中的列表列表
Parsing multiple lines into a list of lists in Haskell
我正在尝试解析一个如下所示的文件:
a b c
f e d
我想匹配行中的每个符号并将所有内容解析为列表列表,例如:
[[A, B, C], [D, E, F]]
为此,我尝试了以下操作:
import Control.Monad
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Language
import qualified Text.ParserCombinators.Parsec.Token as P
parserP :: Parser [[MyType]]
parserP = do
x <- rowP
xs <- many (newline >> rowP)
return (x : xs)
rowP :: Parser [MyType]
rowP = manyTill cellP $ void newline <|> eof
cellP :: Parser (Cell Color)
cellP = aP <|> bP <|> ... -- rest of the parsers, they all look very similar
aP :: Parser MyType
aP = symbol "a" >> return A
bP :: Parser MyType
bP = symbol "b" >> return B
lexer = P.makeTokenParser emptyDef
symbol = P.symbol lexer
但它无法 return 多个内部列表。相反,我得到的是:
[[A, B, C, D, E, F]]
我做错了什么?我期待 manyTill 解析 cellP 直到换行符,但事实并非如此。
解析器组合器对于这么简单的东西来说太过分了。我会使用 lines :: String -> [String]
and words :: String -> [String]
来分解输入,然后将各个标记映射到 MyType
s.
toMyType :: String -> Maybe MyType
toMyType "a" = Just A
toMyType "b" = Just B
toMyType "c" = Just C
toMyType _ = Nothing
parseMyType :: String -> Maybe [[MyType]]
parseMyType = traverse (traverse toMyType) . fmap words . lines
你是对的,manyTill
一直解析到换行符。但是 manyTill
永远看不到换行符,因为 cellP
太急切了。 cellP
最终调用 P.symbol
,其文档说明
symbol :: String -> ParsecT s u m String
Lexeme parser symbol s parses string s and skips trailing white space.
那里的关键词是'white space'。事实证明,Parsec 将 whitespace 定义为任何满足 isSpace
的字符,其中包括换行符。所以 P.symbol
很高兴地使用 c
,接着是 space 和换行符,然后 manyTill
看起来并没有看到换行符,因为它是 已经消耗.
如果您想放弃 Parsec 例程,请使用 Benjamin 的解决方案。但是如果你决定坚持下去,基本的想法是你想修改语言的 whiteSpace
字段以正确定义 whitespace 不是换行符。像
lexer = let lexer0 = P.makeTokenParser emptyDef
in lexer0 { whiteSpace = void $ many (oneOf " \t") }
那是伪代码,可能不适用于您的特定情况,但想法是存在的。您想要将 whiteSpace
的定义更改为您想要定义为 whiteSpace
的任何内容,而不是系统默认定义的内容。请注意,更改此设置也会破坏您的评论语法(如果您已定义的话),因为 whiteSpace
之前已具备处理评论的能力。
简而言之,本杰明的回答可能是最好的选择。这里没有真正的理由使用 Parsec。但了解 为什么 这个特定解决方案不起作用也很有帮助:Parsec 对语言的默认定义并非旨在处理具有重要性的换行符。
我正在尝试解析一个如下所示的文件:
a b c
f e d
我想匹配行中的每个符号并将所有内容解析为列表列表,例如:
[[A, B, C], [D, E, F]]
为此,我尝试了以下操作:
import Control.Monad
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Language
import qualified Text.ParserCombinators.Parsec.Token as P
parserP :: Parser [[MyType]]
parserP = do
x <- rowP
xs <- many (newline >> rowP)
return (x : xs)
rowP :: Parser [MyType]
rowP = manyTill cellP $ void newline <|> eof
cellP :: Parser (Cell Color)
cellP = aP <|> bP <|> ... -- rest of the parsers, they all look very similar
aP :: Parser MyType
aP = symbol "a" >> return A
bP :: Parser MyType
bP = symbol "b" >> return B
lexer = P.makeTokenParser emptyDef
symbol = P.symbol lexer
但它无法 return 多个内部列表。相反,我得到的是:
[[A, B, C, D, E, F]]
我做错了什么?我期待 manyTill 解析 cellP 直到换行符,但事实并非如此。
解析器组合器对于这么简单的东西来说太过分了。我会使用 lines :: String -> [String]
and words :: String -> [String]
来分解输入,然后将各个标记映射到 MyType
s.
toMyType :: String -> Maybe MyType
toMyType "a" = Just A
toMyType "b" = Just B
toMyType "c" = Just C
toMyType _ = Nothing
parseMyType :: String -> Maybe [[MyType]]
parseMyType = traverse (traverse toMyType) . fmap words . lines
你是对的,manyTill
一直解析到换行符。但是 manyTill
永远看不到换行符,因为 cellP
太急切了。 cellP
最终调用 P.symbol
,其文档说明
symbol :: String -> ParsecT s u m String
Lexeme parser symbol s parses string s and skips trailing white space.
那里的关键词是'white space'。事实证明,Parsec 将 whitespace 定义为任何满足 isSpace
的字符,其中包括换行符。所以 P.symbol
很高兴地使用 c
,接着是 space 和换行符,然后 manyTill
看起来并没有看到换行符,因为它是 已经消耗.
如果您想放弃 Parsec 例程,请使用 Benjamin 的解决方案。但是如果你决定坚持下去,基本的想法是你想修改语言的 whiteSpace
字段以正确定义 whitespace 不是换行符。像
lexer = let lexer0 = P.makeTokenParser emptyDef
in lexer0 { whiteSpace = void $ many (oneOf " \t") }
那是伪代码,可能不适用于您的特定情况,但想法是存在的。您想要将 whiteSpace
的定义更改为您想要定义为 whiteSpace
的任何内容,而不是系统默认定义的内容。请注意,更改此设置也会破坏您的评论语法(如果您已定义的话),因为 whiteSpace
之前已具备处理评论的能力。
简而言之,本杰明的回答可能是最好的选择。这里没有真正的理由使用 Parsec。但了解 为什么 这个特定解决方案不起作用也很有帮助:Parsec 对语言的默认定义并非旨在处理具有重要性的换行符。