language-ocaml-0.1.31: lib/Language/OCaml/Parser/Utils/Combinators.hs
module Language.OCaml.Parser.Utils.Combinators
( chainl1
, chainl1'
, chainl1try
, chainl1try'
, leftRecursive
) where
import Control.Applicative
import Text.Megaparsec
import Language.OCaml.Parser.Utils.Types
{-
This combinator is necessary because we want to parse succesfully even when
the `op` is not part of the chain.
For instance, in OCaml, a modLongident is a sequence of `Foo.Bar.Baz` and must
be parsed as a chain. However, when presented with `Foo.Bar.baz`, we'd like the
chain to capture `Foo.Bar` and ignore the `.baz`.
-}
chainl1try' :: Parser a -> Parser (b -> a -> b) -> (a -> b) -> Parser b
chainl1try' p op bc = p >>= rest . bc
where
rest x = tryOp x <|> return x
tryOp x = try $ do
f <- op
y <- p
rest (f x y)
chainl1try :: Parser b -> Parser (b -> b -> b) -> Parser b
chainl1try p op = chainl1try' p op id
chainl1' :: (Alternative m, Monad m) => m a -> m (b -> a -> b) -> (a -> b) -> m b
chainl1' p op bc = p >>= rest . bc
where
rest x = do { f <- op ; y <- p ; rest (f x y) } <|> return x
chainl1 :: (Alternative m, Monad m) => m a -> m (a -> a -> a) -> m a
chainl1 p op = chainl1' p op id
{-
This applies the procedure of turning a left-recursive grammar into a
non-left-recursive one. Essentially, when wanting to write the grammar:
A ::=
| A B { foo A B }
| C { bar C }
One instead writes the following:
A' ::=
| C R { R (bar C) }
R ::=
| B R { \ A -> R (foo A B) }
| {-empty-} { id }
The two arguments to `leftRecursive` are the grammars for A' and R (except the
last line of R is not needed, but added by the combinator).
-}
leftRecursive :: (Monad m, Alternative m) => [m a] -> [m (a -> a)] -> m a
leftRecursive prefixes suffixes = choice $ map patchPrefix prefixes
where
patchPrefix prefixP = do
p <- prefixP
r <- rest
return $ r p
patchSuffix suffixP = do
s <- suffixP
r <- rest
return $ r . s
rest = choice $ map patchSuffix suffixes ++ [ return id ]