2016-10-07 2 views
1

У меня есть грамматик, которая позволяет неявное умножение, (1+2)(3+4) такое же, как (1+2)*(3+4) или (1+2)7 такое же, как (1+2)*7 Как реализовать это в Haskell? Вот то, что я до сих пор:Как разобрать выражение с неявным умножением Parsec в Haskell

import Control.Monad 
import Text.ParserCombinators.Parsec 
import Text.ParserCombinators.Parsec.Expr 
import Text.ParserCombinators.Parsec.Language 
import qualified Text.ParserCombinators.Parsec.Token as Token 

languageDef = 
    emptyDef { Token.identStart  = letter 
      , Token.identLetter  = alphaNum 
      , Token.reservedOpNames = ["+", "*"] 
      } 

lexer = Token.makeTokenParser languageDef 

reservedOp = Token.reservedOp lexer 
parens  = Token.parens  lexer 
integer = Token.integer lexer 

data Expr = Const Int 
      | Binary BinOp Expr Expr 
      deriving (Show) 

data BinOp = Add | Multiply 
      deriving (Show) 

expression = buildExpressionParser operators term 

operators = [ [Infix (reservedOp "*" >> return (Binary Multiply)) AssocLeft] 
      , [Infix (reservedOp "+" >> return (Binary Add )) AssocLeft] 
      ] 

term = liftM (Const . fromIntegral) integer 
    <|> parens expression 
    <|> (do e1 <- expression 
      e2 <- term 
      return $ Binary Multiply e1 e2) 

parseString str = 
    case parse expression "" str of 
    Left e -> error $ show e 
    Right r -> r 

, но он не работает, у меня есть ошибка при разборе, когда я пытаюсь разобрать ((1 + 5) 8) меня unexpected "8" expecting operator or ")"

+1

Не могли бы вы убедитесь, что у вас есть все импорта в вашем примере? Это немного облом, если нам нужно выяснить все, что вы импортируете ... – Alec

+1

@Alec Я добавил все недостающие части, чтобы код компилировался и работает. – Artem

+0

Вы пытались изменить определение оператора с 'reservedOp '*" 'на' reservedOp "*" <|> space'? –

ответ

1

Если у вас есть очень веские основания для оборудование за makeTokenParser, кажется немного переборщиком. Обычно это полезно, когда у вас есть язык, который очень похож на существующий язык, или у вас есть много разных уровней приоритета оператора. В вашем случае, вы можете написать expression в пару строк ...

import Text.Parsec.String (Parser) 
import Text.Parsec 
import Control.Applicative (some) 

-- ... 

expression :: Parser Expr 
expression = sum 
    where 
    product = foldl1 (Binary Multiply) <$> factor `sepBy1` optional (char '*') 
    sum  = foldl1 (Binary Add)  <$> product `sepBy1` char '+' 
    factor = int <|> between (char '(') (char ')') expression 
    int  = Const . read <$> some digit 

-- ... 

Затем на GHCi:

ghci> parseString "1+2*3" 
Binary Add (Const 1) (Binary Multiply (Const 2) (Const 3)) 
ghci> parseString "(1+2)(3+4)" 
Binary Multiply (Binary Add (Const 1) (Const 2)) (Binary Add (Const 3) (Const 4)) 
ghci> parseString "(1+2)*(3+4)" 
Binary Multiply (Binary Add (Const 1) (Const 2)) (Binary Add (Const 3) (Const 4)) 
ghci> parseString "(1+2)7" 
Binary Multiply (Binary Add (Const 1) (Const 2)) (Const 7) 
ghci> parseString "(1+2)*7" 
Binary Multiply (Binary Add (Const 1) (Const 2)) (Const 7) 
+0

Спасибо, он отлично работает, но на самом деле мне нужны другие операторы и приоритет операторов, есть также двоичные '-','/'и'^', я удалил их, чтобы сделать пример кода короче, на самом деле это причина, почему у меня есть Тип 'BinOp' – Artem

Смежные вопросы