Удаление левой рекурсии в синтаксическом анализаторе базовых выражений
в качестве упражнения я реализую парсер для чрезвычайно простого языка, определенного в Haskell, используя следующий GADT (реальная грамматика для моего проекта включает в себя еще много выражений, но этого экстракта достаточно для вопроса):
data Expr a where
    I   :: Int -> Expr Int
    Add :: [Expr Int] -> Expr Int
функции синтаксического анализа следующие:
expr :: Parser (Expr Int)
expr = foldl1 mplus
    [ lit 
    , add 
    ]   
lit :: Parser (Expr Int)
lit = I . read <$> some digit
add :: Parser (Expr Int)
add = do
  i0 <- expr
  is (== '+')
  i1 <- expr
  is <- many (is (== '+') *> expr)
  pure (Add (i0:i1:is))
из-за лево-рекурсивный характер выражения грамматики, когда я пытаюсь разобрать что-то просто 1+1 С помощью expr парсер, парсер сделать застрял в бесконечной петле. 
Я видел примеры того, как разложить левую рекурсию по сети, используя преобразование из чего-то вроде:
S -> S a | b
в что-то вроде:
S -> b T
T -> a T
но я борюсь с тем, как применить это к моей парсер.
для полноты, вот код, который фактически реализует парсер:
newtype Parser a = Parser
    { runParser :: String -> [(a, String)]
    }   
instance Functor Parser where
    fmap f (Parser p) = Parser $ s ->
      fmap ((a, r) -> (f a, r)) (p s)
instance Applicative Parser where
    pure a = Parser $ s -> [(a, s)] 
    (<*>) (Parser f) (Parser p) = Parser $ s ->
      concat $ fmap ((f', r) -> fmap ((a, r') -> (f' a, r')) (p r)) (f >
instance Alternative Parser where
    empty = Parser $ s -> []
    (<|>) (Parser a) (Parser b) = Parser $ s ->
      case a s of
        (r:rs) -> (r:rs)
        []     -> case b s of
                    (r:rs) -> (r:rs)
                    []     -> []
instance Monad Parser where
    return = pure
    (>>=) (Parser a) f = Parser $ s ->
      concat $ fmap ((r, rs) -> runParser (f r) rs) (a s)
instance MonadPlus Parser where
    mzero = empty
    mplus (Parser a) (Parser b) = Parser $ s -> a s ++ b s 
char  = Parser $ case (c:cs) -> [(c, cs)]; [] -> []
is p  = char >>= c -> if p c then pure c else empty
digit = is isDigit
            1 ответов
Предположим, вы хотите проанализировать выражения без скобок, включающие литералы, сложение и умножение. Это можно сделать, сократив список по приоритетам. Вот один из способов сделать это в attoparsec, что должно быть очень похоже на то, что вы сделали бы с вашим синтаксическим анализатором. Я не эксперт по разбору, поэтому могут быть ошибки или ошибки.
import Data.Attoparsec.ByteString.Char8
import Control.Applicative
expr :: Parser (Expr Int)
expr = choice [add, mul, lit] <* skipSpace
-- choice is in Data.Attoparsec.Combinators, but is
-- actually a general Alternative operator.
add :: Parser (Expr Int)
add = Add <$> addList
addList :: Parser [Expr Int]
addList = (:) <$> addend <* skipSpace <* char '+' <*> (addList <|> ((:[]) <$> addend))
addend :: Parser (Expr Int)
addend = mul <|> multiplicand
mul :: Parser (Expr Int)
mul = Mul <$> mulList
mulList :: Parser [Expr Int]
mulList = (:) <$> multiplicand <* skipSpace <* char '*' <*> (mulList <|> ((:[]) <$> multiplicand))
multiplicand :: Parser (Expr Int)
multiplicand = lit
lit :: Parser (Expr Int)
lit = I <$> (skipSpace *> decimal)