2008-01-13 20:53:15 +03:00
|
|
|
|
-- |
|
2015-08-01 19:24:45 +03:00
|
|
|
|
-- Module : Text.Megaparsec.Expr
|
|
|
|
|
-- Copyright : © 2015 Megaparsec contributors
|
2015-07-30 19:20:37 +03:00
|
|
|
|
-- © 2007 Paolo Martini
|
|
|
|
|
-- © 1999–2001 Daan Leijen
|
2015-07-28 16:32:19 +03:00
|
|
|
|
-- License : BSD3
|
|
|
|
|
--
|
|
|
|
|
-- Maintainer : Mark Karpov <markkarpov@opmbx.org>
|
2015-07-30 18:45:06 +03:00
|
|
|
|
-- Stability : experimental
|
2008-01-13 20:53:15 +03:00
|
|
|
|
-- Portability : non-portable
|
2015-07-28 16:32:19 +03:00
|
|
|
|
--
|
2015-08-12 15:41:22 +03:00
|
|
|
|
-- A helper module to parse expressions. Builds a parser given a table of
|
|
|
|
|
-- operators.
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-08-01 19:24:45 +03:00
|
|
|
|
module Text.Megaparsec.Expr
|
2015-08-29 13:54:15 +03:00
|
|
|
|
( Operator (..)
|
2015-08-23 11:04:12 +03:00
|
|
|
|
, makeExprParser )
|
2015-07-28 16:32:19 +03:00
|
|
|
|
where
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-08-01 17:39:20 +03:00
|
|
|
|
import Control.Applicative ((<|>))
|
2015-07-30 18:45:06 +03:00
|
|
|
|
|
2015-08-01 19:24:45 +03:00
|
|
|
|
import Text.Megaparsec.Combinator
|
|
|
|
|
import Text.Megaparsec.Prim
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2008-01-20 09:43:50 +03:00
|
|
|
|
-- | This data type specifies operators that work on values of type @a@.
|
2015-07-30 18:45:06 +03:00
|
|
|
|
-- An operator is either binary infix or unary prefix or postfix. A binary
|
|
|
|
|
-- operator has also an associated associativity.
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
data Operator m a
|
|
|
|
|
= InfixN (m (a -> a -> a)) -- ^ non-associative infix
|
|
|
|
|
| InfixL (m (a -> a -> a)) -- ^ left-associative infix
|
|
|
|
|
| InfixR (m (a -> a -> a)) -- ^ right-associative infix
|
|
|
|
|
| Prefix (m (a -> a)) -- ^ prefix
|
|
|
|
|
| Postfix (m (a -> a)) -- ^ postfix
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-08-29 13:54:15 +03:00
|
|
|
|
-- | @makeExprParser term table@ builds an expression parser for terms
|
2015-08-23 11:04:12 +03:00
|
|
|
|
-- @term@ with operators from @table@, taking the associativity and
|
2015-08-29 13:54:15 +03:00
|
|
|
|
-- precedence specified in @table@ into account.
|
|
|
|
|
--
|
|
|
|
|
-- @table@ is a list of @[Operator s u m a]@ lists. The list is ordered in
|
|
|
|
|
-- descending precedence. All operators in one list have the same precedence
|
|
|
|
|
-- (but may have a different associativity).
|
|
|
|
|
--
|
2015-09-11 12:10:14 +03:00
|
|
|
|
-- Prefix and postfix operators of the same precedence can only occur once
|
|
|
|
|
-- (i.e. @--2@ is not allowed if @-@ is prefix negate). Prefix and postfix
|
|
|
|
|
-- operators of the same precedence associate to the left (i.e. if @++@ is
|
|
|
|
|
-- postfix increment, than @-2++@ equals @-1@, not @-3@).
|
2008-01-20 09:43:50 +03:00
|
|
|
|
--
|
2015-09-23 14:23:24 +03:00
|
|
|
|
-- @makeExprParser@ takes care of all the complexity involved in building an
|
|
|
|
|
-- expression parser. Here is an example of an expression parser that
|
|
|
|
|
-- handles prefix signs, postfix increment and basic arithmetic:
|
2008-01-20 09:43:50 +03:00
|
|
|
|
--
|
2015-08-29 13:54:15 +03:00
|
|
|
|
-- > expr = makeExprParser term table <?> "expression"
|
2008-01-20 09:43:50 +03:00
|
|
|
|
-- >
|
2015-08-29 13:54:15 +03:00
|
|
|
|
-- > term = parens expr <|> integer <?> "term"
|
2008-01-20 09:43:50 +03:00
|
|
|
|
-- >
|
2015-07-30 18:45:06 +03:00
|
|
|
|
-- > table = [ [ prefix "-" negate
|
|
|
|
|
-- > , prefix "+" id ]
|
|
|
|
|
-- > , [ postfix "++" (+1) ]
|
2015-08-29 13:54:15 +03:00
|
|
|
|
-- > , [ binary "*" (*)
|
|
|
|
|
-- > , binary "/" div ]
|
|
|
|
|
-- > , [ binary "+" (+)
|
|
|
|
|
-- > , binary "-" (-) ] ]
|
2015-07-28 16:32:19 +03:00
|
|
|
|
-- >
|
2015-08-29 13:54:15 +03:00
|
|
|
|
-- > binary name f = InfixL (reservedOp name >> return f)
|
|
|
|
|
-- > prefix name f = Prefix (reservedOp name >> return f)
|
|
|
|
|
-- > postfix name f = Postfix (reservedOp name >> return f)
|
2015-09-11 12:10:14 +03:00
|
|
|
|
--
|
|
|
|
|
-- Please note that multi-character operators should use 'try' in order to
|
|
|
|
|
-- be reported correctly in error messages.
|
2015-08-29 13:54:15 +03:00
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
makeExprParser :: MonadParsec s m t => m a -> [[Operator m a]] -> m a
|
2015-08-29 13:54:15 +03:00
|
|
|
|
makeExprParser = foldl addPrecLevel
|
2015-07-30 18:45:06 +03:00
|
|
|
|
|
2015-08-29 13:54:15 +03:00
|
|
|
|
-- | @addPrecLevel p ops@ adds ability to parse operators in table @ops@ to
|
|
|
|
|
-- parser @p@.
|
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
addPrecLevel :: MonadParsec s m t => m a -> [Operator m a] -> m a
|
2015-08-29 13:54:15 +03:00
|
|
|
|
addPrecLevel term ops =
|
|
|
|
|
term' >>= \x -> choice [ras' x, las' x, nas' x, return x] <?> "operator"
|
|
|
|
|
where (ras, las, nas, prefix, postfix) = foldr splitOp ([],[],[],[],[]) ops
|
|
|
|
|
term' = pTerm (choice prefix) term (choice postfix)
|
|
|
|
|
ras' = pInfixR (choice ras) term'
|
|
|
|
|
las' = pInfixL (choice las) term'
|
|
|
|
|
nas' = pInfixN (choice nas) term'
|
|
|
|
|
|
|
|
|
|
-- | @pTerm prefix term postfix@ parses term with @term@ surrounded by
|
|
|
|
|
-- optional prefix and postfix unary operators. Parsers @prefix@ and
|
|
|
|
|
-- @postfix@ are allowed to fail, in this case 'id' is used.
|
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
pTerm :: MonadParsec s m t => m (a -> a) -> m a -> m (a -> a) -> m a
|
2015-08-29 13:54:15 +03:00
|
|
|
|
pTerm prefix term postfix = do
|
|
|
|
|
pre <- option id (hidden prefix)
|
|
|
|
|
x <- term
|
|
|
|
|
post <- option id (hidden postfix)
|
|
|
|
|
return $ post (pre x)
|
|
|
|
|
|
|
|
|
|
-- | @pInfixN op p x@ parses non-associative infix operator @op@, then term
|
|
|
|
|
-- with parser @p@, then returns result of the operator application on @x@
|
|
|
|
|
-- and the term.
|
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
pInfixN :: MonadParsec s m t => m (a -> a -> a) -> m a -> a -> m a
|
2015-08-29 13:54:15 +03:00
|
|
|
|
pInfixN op p x = do
|
|
|
|
|
f <- op
|
|
|
|
|
y <- p
|
|
|
|
|
return $ f x y
|
|
|
|
|
|
|
|
|
|
-- | @pInfixL op p x@ parses left-associative infix operator @op@, then term
|
|
|
|
|
-- with parser @p@, then returns result of the operator application on @x@
|
|
|
|
|
-- and the term.
|
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
pInfixL :: MonadParsec s m t => m (a -> a -> a) -> m a -> a -> m a
|
2015-08-29 13:54:15 +03:00
|
|
|
|
pInfixL op p x = do
|
|
|
|
|
f <- op
|
|
|
|
|
y <- p
|
|
|
|
|
let r = f x y
|
|
|
|
|
pInfixL op p r <|> return r
|
|
|
|
|
|
|
|
|
|
-- | @pInfixR op p x@ parses right-associative infix operator @op@, then
|
|
|
|
|
-- term with parser @p@, then returns result of the operator application on
|
|
|
|
|
-- @x@ and the term.
|
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
pInfixR :: MonadParsec s m t => m (a -> a -> a) -> m a -> a -> m a
|
2015-08-29 13:54:15 +03:00
|
|
|
|
pInfixR op p x = do
|
|
|
|
|
f <- op
|
|
|
|
|
y <- p >>= \r -> pInfixR op p r <|> return r
|
|
|
|
|
return $ f x y
|
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
type Batch m a =
|
|
|
|
|
( [m (a -> a -> a)]
|
|
|
|
|
, [m (a -> a -> a)]
|
|
|
|
|
, [m (a -> a -> a)]
|
|
|
|
|
, [m (a -> a)]
|
|
|
|
|
, [m (a -> a)] )
|
2015-08-29 13:54:15 +03:00
|
|
|
|
|
|
|
|
|
-- | A helper to separate various operators (binary, unary, and according to
|
|
|
|
|
-- associativity) and return them in a tuple.
|
|
|
|
|
|
2015-09-18 12:33:44 +03:00
|
|
|
|
splitOp :: MonadParsec s m t => Operator m a -> Batch m a -> Batch m a
|
2015-08-29 13:54:15 +03:00
|
|
|
|
splitOp (InfixR op) (r, l, n, pre, post) = (op:r, l, n, pre, post)
|
|
|
|
|
splitOp (InfixL op) (r, l, n, pre, post) = (r, op:l, n, pre, post)
|
|
|
|
|
splitOp (InfixN op) (r, l, n, pre, post) = (r, l, op:n, pre, post)
|
|
|
|
|
splitOp (Prefix op) (r, l, n, pre, post) = (r, l, n, op:pre, post)
|
|
|
|
|
splitOp (Postfix op) (r, l, n, pre, post) = (r, l, n, pre, op:post)
|