{-# LANGUAGE CPP #-}
module Curry.Base.LLParseComb
(
Parser
, fullParser, prefixParser
, position, spanPosition, succeed, failure, symbol
, (<?>), (<|>), (<|?>), (<*>), (<\>), (<\\>)
, (<$>), (<$->), (<*->), (<-*>), (<**>), (<??>), (<.>)
, opt, choice, flag, optional, option, many, many1, sepBy, sepBy1
, sepBySp, sepBy1Sp
, chainr, chainr1, chainl, chainl1, between, ops
, layoutOn, layoutOff, layoutEnd
) where
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative (Applicative, (<*>), (<$>), pure)
#endif
import Control.Monad
import qualified Data.Map as Map
import Data.Maybe
import qualified Data.Set as Set
import Curry.Base.LexComb
import Curry.Base.Position
import Curry.Base.Span (span2Pos, Span, startCol, setDistance)
infixl 5 <\>, <\\>
infixl 4 <$->, <*->, <-*>, <**>, <??>, <.>
infixl 3 <|>, <|?>
infixl 2 <?>, `opt`
type ParseFun a s b = (b -> SuccessP s a) -> FailP a -> SuccessP s a
data Parser a s b = Parser
(Maybe (ParseFun a s b))
(Map.Map s (Lexer s a -> ParseFun a s b))
instance Symbol s => Functor (Parser a s) where
fmap f p = succeed f <*> p
instance Symbol s => Applicative (Parser a s) where
pure = succeed
Parser Nothing ps1 <*> p2 = Parser Nothing
(fmap (flip seqPP p2) ps1)
Parser (Just p1) ps1 <*> ~p2@(Parser e2 ps2) = Parser (fmap (seqEE p1) e2)
(Map.union (fmap (flip seqPP p2) ps1) (fmap (seqEP p1) ps2))
instance Show s => Show (Parser a s b) where
showsPrec p (Parser e ps) = showParen (p >= 10) $
showString "Parser " . shows (isJust e) .
showChar ' ' . shows (Map.keysSet ps)
fullParser :: Symbol s => Parser a s a -> Lexer s a -> FilePath -> String
-> CYM a
fullParser p lexer = parse (lexer (choose p lexer successP failP) failP)
where successP x pos s
| isEOF s = returnP x
| otherwise = failP pos (unexpected s)
prefixParser :: Symbol s => Parser a s a -> Lexer s a -> FilePath -> String
-> CYM a
prefixParser p lexer = parse (lexer (choose p lexer discardP failP) failP)
where discardP x _ _ = returnP x
choose :: Symbol s => Parser a s b -> Lexer s a -> ParseFun a s b
choose (Parser e ps) lexer success failp pos s = case Map.lookup s ps of
Just p -> p lexer success failp pos s
Nothing -> case e of
Just p -> p success failp pos s
Nothing -> failp pos (unexpected s)
unexpected :: Symbol s => s -> String
unexpected s
| isEOF s = "Unexpected end-of-file"
| otherwise = "Unexpected token " ++ show s
position :: Parser a s Position
position = Parser (Just p) Map.empty
where p success _ sp = success (span2Pos sp) sp
spanPosition :: Symbol s => Parser a s Span
spanPosition = Parser (Just p) Map.empty
where p success _ sp s = success (setDistance sp (dist (startCol sp) s)) sp s
succeed :: b -> Parser a s b
succeed x = Parser (Just p) Map.empty
where p success _ = success x
failure :: String -> Parser a s b
failure msg = Parser (Just p) Map.empty
where p _ failp pos _ = failp pos msg
symbol :: s -> Parser a s s
symbol s = Parser Nothing (Map.singleton s p)
where p lexer success failp _ s' = lexer (success s') failp
(<?>) :: Symbol s => Parser a s b -> String -> Parser a s b
p <?> msg = p <|> failure msg
(<|>) :: Symbol s => Parser a s b -> Parser a s b -> Parser a s b
Parser e1 ps1 <|> Parser e2 ps2
| isJust e1 && isJust e2 = failure "Ambiguous parser for empty word"
| not (Set.null common) = failure $ "Ambiguous parser for " ++ show common
| otherwise = Parser (e1 `mplus` e2) (Map.union ps1 ps2)
where common = Map.keysSet ps1 `Set.intersection` Map.keysSet ps2
(<|?>) :: Symbol s => Parser a s b -> Parser a s b -> Parser a s b
Parser e1 ps1 <|?> Parser e2 ps2
| isJust e1 && isJust e2 = failure "Ambiguous parser for empty word"
| otherwise = Parser (e1 `mplus` e2) (Map.union ps1' ps2)
where
ps1' = Map.fromList [ (s, maybe p (try p) (Map.lookup s ps2))
| (s, p) <- Map.toList ps1
]
try p1 p2 lexer success failp pos s =
closeP1 p2s `thenP` \p2s' ->
closeP1 p2f `thenP` \p2f' ->
parse' p1 (retry p2s') (retry p2f')
where p2s r1 = parse' p2 (select True r1) (select False r1)
p2f r1 = parse' p2 (flip (select False) r1) (select False r1)
parse' p psucc pfail =
p lexer (successK psucc) (failK pfail) pos s
successK k x pos' s' = k (pos', success x pos' s')
failK k pos' msg = k (pos', failp pos' msg)
retry k (pos',p) = closeP0 p `thenP` curry k pos'
select suc (pos1, p1) (pos2, p2) = case pos1 `compare` pos2 of
GT -> p1
EQ | suc -> failP pos1 $ "Ambiguous parse before " ++ showPosition (span2Pos pos1)
| otherwise -> p1
LT -> p2
seqEE :: ParseFun a s (b -> c) -> ParseFun a s b -> ParseFun a s c
seqEE p1 p2 success failp = p1 (\f -> p2 (success . f) failp) failp
seqEP :: ParseFun a s (b -> c) -> (Lexer s a -> ParseFun a s b)
-> Lexer s a -> ParseFun a s c
seqEP p1 p2 lexer success failp = p1 (\f -> p2 lexer (success . f) failp) failp
seqPP :: Symbol s => (Lexer s a -> ParseFun a s (b -> c)) -> Parser a s b
-> Lexer s a -> ParseFun a s c
seqPP p1 p2 lexer success failp =
p1 lexer (\f -> choose p2 lexer (success . f) failp) failp
(<\>) :: Symbol s => Parser a s b -> Parser a s c -> Parser a s b
p <\> Parser _ ps = p <\\> Map.keys ps
(<\\>) :: Symbol s => Parser a s b -> [s] -> Parser a s b
Parser e ps <\\> xs = Parser e (foldr Map.delete ps xs)
(<$->) :: Symbol s => a -> Parser b s c -> Parser b s a
f <$-> p = const f <$> p
(<*->) :: Symbol s => Parser a s b -> Parser a s c -> Parser a s b
p <*-> q = const <$> p <*> q
(<-*>) :: Symbol s => Parser a s b -> Parser a s c -> Parser a s c
p <-*> q = const id <$> p <*> q
(<**>) :: Symbol s => Parser a s b -> Parser a s (b -> c) -> Parser a s c
p <**> q = flip ($) <$> p <*> q
(<??>) :: Symbol s => Parser a s b -> Parser a s (b -> b) -> Parser a s b
p <??> q = p <**> (q `opt` id)
(<.>) :: Symbol s => Parser a s (b -> c) -> Parser a s (c -> d)
-> Parser a s (b -> d)
p1 <.> p2 = p1 <**> ((.) <$> p2)
opt :: Symbol s => Parser a s b -> b -> Parser a s b
p `opt` x = p <|> succeed x
choice :: Symbol s => [Parser a s b] -> Parser a s b
choice = foldr1 (<|>)
flag :: Symbol s => Parser a s b -> Parser a s Bool
flag p = True <$-> p `opt` False
optional :: Symbol s => Parser a s b -> Parser a s ()
optional p = const () <$> p `opt` ()
option :: Symbol s => Parser a s b -> Parser a s (Maybe b)
option p = Just <$> p `opt` Nothing
many :: Symbol s => Parser a s b -> Parser a s [b]
many p = many1 p `opt` []
many1 :: Symbol s => Parser a s b -> Parser a s [b]
many1 p = (:) <$> p <*> many p
sepBy :: Symbol s => Parser a s b -> Parser a s c -> Parser a s [b]
p `sepBy` q = p `sepBy1` q `opt` []
sepBy1 :: Symbol s => Parser a s b -> Parser a s c -> Parser a s [b]
p `sepBy1` q = (:) <$> p <*> many (q <-*> p)
sepBySp :: Symbol s => Parser a s b -> Parser a s c -> Parser a s ([b], [Span])
p `sepBySp` q = p `sepBy1Sp` q `opt` ([], [])
sepBy1Sp :: Symbol s => Parser a s b -> Parser a s c -> Parser a s ([b], [Span])
p `sepBy1Sp` q = comb <$> p <*> many ((,) <$> spanPosition <*-> q <*> p)
where comb x xs = let (ss, ys) = unzip xs
in (x:ys,ss)
chainr :: Symbol s
=> Parser a s b -> Parser a s (b -> b -> b) -> b -> Parser a s b
chainr p op x = chainr1 p op `opt` x
chainr1 :: Symbol s => Parser a s b -> Parser a s (b -> b -> b) -> Parser a s b
chainr1 p op = r where r = p <**> (flip <$> op <*> r `opt` id)
chainl :: Symbol s
=> Parser a s b -> Parser a s (b -> b -> b) -> b -> Parser a s b
chainl p op x = chainl1 p op `opt` x
chainl1 :: Symbol s => Parser a s b -> Parser a s (b -> b -> b) -> Parser a s b
chainl1 p op = foldF <$> p <*> many (flip <$> op <*> p)
where foldF x [] = x
foldF x (f:fs) = foldF (f x) fs
between :: Symbol s => Parser a s b -> Parser a s c -> Parser a s b
-> Parser a s c
between open p close = open <-*> p <*-> close
ops :: Symbol s => [(s, b)] -> Parser a s b
ops [] = failure "Curry.Base.LLParseComb.ops: empty list"
ops [(s, x)] = x <$-> symbol s
ops ((s, x) : rest) = x <$-> symbol s <|> ops rest
layoutOff :: Symbol s => Parser a s b
layoutOff = Parser (Just off) Map.empty
where off success _ pos = pushContext (-1) . success undefined pos
layoutOn :: Symbol s => Parser a s b
layoutOn = Parser (Just on) Map.empty
where on success _ pos = pushContext (column (span2Pos pos)) . success undefined pos
layoutEnd :: Symbol s => Parser a s b
layoutEnd = Parser (Just end) Map.empty
where end success _ pos = popContext . success undefined pos