关于解析:Parsec-Parser 工作正常,但能做得更好吗?

Parsec-Parser works alright, but could it be done better?

我尝试这样做:

以如下形式解析文本:

Some Text #{0,0,0} some Text #{0,0,0}#{0,0,0} more Text #{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}位应该变成与文本其余部分不同的东西。

我有这个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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"") 是丑陋的 hack 吗?

3) 通常是否有更简洁/更智能的方式来实现相同的归档?


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

2) 是的。

3) Applicatives

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

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

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