Parsec-Parser工作正常,但可以做得更好吗?

时间:2011-10-21 16:34:32

标签: parsing haskell parsec

我尝试这样做:

解析表格中的文字:

  

一些文字#{0,0,0}一些文字#{0,0,0}#{0,0,0}更多文字#{0,0,0}

进入一些数据结构列表:

  

[Inside“Some Text”,Outside(0,0,0),Inside“some text”,Outside(0,0,0),Outside(0,0,0),Inside“more text”,Outside (0,0,0)]

所以这些#{a,b,c} -bits应该变成与文本其余部分不同的东西。

我有这段代码:

module ParsecTest where

import Text.ParserCombinators.Parsec
import Monad

type Reference = (Int, Int, Int)

data Transc = Inside String | Outside Reference
              deriving (Show)

text :: Parser Transc
text =  do
         x <- manyTill anyChar ((lookAhead reference) <|> (eof >> return (Inside "")));
         return (Inside x)

transc = reference <|> text

alot :: Parser [Transc]
alot = do
        manyTill transc eof

reference :: Parser Transc
reference = try (do{ char '#';
                  char '{';
                  a <- number;
                char ',';
                b <- number;
                char ',';
                c <- number;
                char '}';
                return (Outside (a,b,c)) })

number :: Parser Int
number = do{ x <- many1 digit;
             return (read x) }

这可以按预期工作。您可以通过键入

在ghci中进行测试
  

parseTest alot“Some Text#{0,0,0} some text#{0,0,0}#{0,0,0} more text#{0,0,0}”

但我觉得这不好。

1)使用lookAhead对我的问题真的有用吗?

2)return (Inside "")是一个丑陋的黑客吗?

3)通常是否有更简洁/更聪明的方法来实现同样的目标?

1 个答案:

答案 0 :(得分:5)

1)我认为您需要lookAhead,因为您需要该解析的结果。通过使用Parser (Transc,Maybe Transc)来指示Inside以及Outside之后的可选项,可以避免运行该解析器两次。如果性能是一个问题,那么这是值得的。

2)是的。

3)Applicative s

number2 :: Parser Int
number2 = read <$> many1 digit

text2 :: Parser Transc
text2 = (Inside .) . (:) 
     <$> anyChar 
     <*> manyTill anyChar (try (lookAhead reference2) *> pure () <|> eof)


reference2 :: Parser Transc
reference2 = ((Outside .) .) . (,,) 
          <$> (string "#{" *> number2 <* char ',') 
          <*> number2 
          <*> (char ',' *> number2 <* char '}')

transc2 = reference2 <|> text2

alot2 = many transc2

您可能希望使用reference2等帮助程序重写aux x y z = Outside (x,y,z)的开头。

编辑:更改text以处理不以Outside结尾的输入。