繁体   English   中英

使用Parsec解析正则表达式

[英]Using Parsec to parse regular expressions

我正在尝试通过实现一个小的正则表达式解析器来学习Parsec。 在BNF中,我的语法看起来像:

EXP  : EXP *
     | LIT EXP
     | LIT

我试图在Haskell中实现这个:

expr = try star
       <|> try litE
       <|> lit

litE  = do c <- noneOf "*"
           rest <- expr
           return (c : rest)

lit   = do c <- noneOf "*"
           return [c]

star = do content <- expr
          char '*'
          return (content ++ "*")

这里有一些无限循环(例如expr - > star - > expr,不消耗任何标记),这使得解析器永远循环。 我不确定如何修复它,因为star本质是它最终会消耗它的强制令牌。

有什么想法吗?

你应该使用Parsec.Expr.buildExprParser ; 它非常适合这个目的。 您只需描述您的运算符,它们的优先级和关联性,以及如何解析原子,组合器为您构建解析器!

您可能还希望添加使用parens对术语进行分组的功能,以便您可以将*应用于多个文字。

这是我的尝试(我投入了|+?以获得良好的衡量标准:

import Control.Applicative
import Control.Monad
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr

data Term = Literal Char
          | Sequence [Term]
          | Repeat (Int, Maybe Int) Term
          | Choice [Term]
  deriving ( Show )

term :: Parser Term
term = buildExpressionParser ops atom where

  ops = [ [ Postfix (Repeat (0, Nothing) <$ char '*')
          , Postfix (Repeat (1, Nothing) <$ char '+')
          , Postfix (Repeat (0, Just 1)  <$ char '?')
          ]
        , [ Infix (return sequence) AssocRight
          ]
        , [ Infix (choice <$ char '|') AssocRight
          ]
        ]

  atom = msum [ Literal <$> lit
              , parens term
              ]

  lit = noneOf "*+?|()"
  sequence a b = Sequence $ (seqTerms a) ++ (seqTerms b)
  choice a b = Choice $ (choiceTerms a) ++ (choiceTerms b)
  parens = between (char '(') (char ')')

  seqTerms (Sequence ts) = ts
  seqTerms t = [t]

  choiceTerms (Choice ts) = ts
  choiceTerms t = [t]

main = parseTest term "he(llo)*|wor+ld?"

你的语法是左递归的,这在try不好,因为Parsec会反复回溯。 有几种方法可以解决这个问题。 可能最简单的只是在另一个规则中使*可选:

lit :: Parser (Char, Maybe Char)
lit = do
  c <- noneOf "*"
  s <- optionMaybe $ char '*'
  return (c, s)

当然,无论如何,你最终可能会以数据类型包装东西,并且有很多方法可以解决它。 这是我的头脑中的一个:

import Control.Applicative ((<$>))

data Term = Literal Char
          | Sequence [Term]
          | Star Term

expr :: Parser Term
expr = Sequence <$> many term

term :: Parser Term
term = do
  c <- lit
  s <- optionMaybe $ char '*' -- Easily extended for +, ?, etc.
  return $ if isNothing s
    then Literal c
    else Star $ Literal c

也许更有经验的Haskeller会提供更好的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM