gll-0.2.0.3: src/GLL/Combinators/BinInterface.hs
module GLL.Combinators.BinInterface (
Parser,
parse, parseString,
char, token, Token(..),
epsilon, satisfy,
many,some,optional,
(<::=>),(<:=>),
(<$>),
(<$),
(<*>),
(*>),
(<*),
(<|>),
) where
import Prelude hiding ((<*>), (<*), (<$>), (<$), (*>))
import GLL.Combinators.Options
import GLL.Types.Abstract
import GLL.Types.Grammar hiding (epsilon)
import GLL.Parser (gllSPPF,ParseResult(..))
import qualified Data.Array as A
import qualified Data.IntMap as IM
import qualified Data.Map as M
import qualified Data.Set as S
type Visit1 = Symbol
type Visit2 = M.Map Nt [Alt] -> M.Map Nt [Alt]
type Visit3 a = PCOptions -> A.Array Int Token -> ParseContext -> SPPF
-> Int -> Int -> Int -> S.Set a
type Parser a = (Visit1, Visit2, Visit3 a)
type ParseContext = IM.IntMap (IM.IntMap Nt)
-- | Given a parser and a string of tokens, return:
-- * The grammar (GLL.Types.Abstract)
-- * a list of results, which are all semantic evaluations of 'good derivations'
-- - semantic evaluations are specified by using <$> and satisfy
-- - 'good derivations' as defined by by Tom Ridge
parse' :: PCOptions -> Parser a -> [Token] -> (Grammar, ParseResult, [a])
parse' opts (Nt start,rules,sem) str =
let cfg = Grammar start [] [ Rule x alts
| (x, alts) <- M.assocs (rules M.empty) ]
parse_r = gllSPPF cfg str
sppf = sppf_result parse_r
as = sem opts arr IM.empty sppf 0 m m
m = length str
arr = A.array (0,m) (zip [0..] str)
in (cfg,parse_r,S.toList as)
-- | The grammar of a given parser
grammar :: Parser a -> Grammar
grammar p = (\(f,_,_) -> f) (parse' defaultOptions p [])
-- | The semantic results of a parser, given a token string
parse :: Parser a -> [Token] -> [a]
parse = parseWithOptions defaultOptions
-- | The semantic results of a parser, given a token string
-- and GLL.Combinator.Options
parseWithOptions :: PCOptions -> Parser a -> [Token] -> [a]
parseWithOptions opts p str = (\(_,_,t) -> t) (parse' opts p str)
-- | Get the SPPF produced by parsing the given input with the given parser
sppf :: Parser a -> [Token] -> ParseResult
sppf p str = (\(_,s,_) -> s) (parse' defaultOptions p str)
-- | Parse a given string of characters
parseString :: Parser a -> [Char] -> [a]
parseString p = parse p . charS
-- | Parse a given string of characters and options
parseStringWithOptions :: PCOptions -> Parser a -> [Char] -> [a]
parseStringWithOptions opts p = parseWithOptions opts p . charS
infixl 3 <::=>
-- | use <::=> to enforce using parse context (to handle left-recursion)
(<::=>) :: String -> Parser a -> Parser a
x <::=> _r = let (sym,_r_rules,_r_sem) = _r
alt = Alt x [sym] -- TODO indirection (extra alt)
rules m = case M.lookup x m of
Nothing -> _r_rules (M.insert x [alt] m)
Just _ -> m
sem opts arr ctx sppf l r m
| (l,r,x) `inContext` ctx = S.empty
| otherwise = let ctx' = (l,r,x) `toContext` ctx
in _r_sem opts arr ctx' sppf l r m
in (Nt x,rules,sem)
-- | useful for non-recursive definitions (only internally)
infixl 3 <:=>
(<:=>) :: String -> Parser a -> Parser a
x <:=> _r = let (sym,_r_rules,_r_sem) = _r
alt = Alt x [sym] -- TODO indirection (extra alt)
rules m = case M.lookup x m of
Nothing -> _r_rules (M.insert x [alt] m)
Just _ -> m
in (Nt x,rules,_r_sem)
infixl 5 <$>
-- | Application of a semantic action.
(<$>) :: (Ord b, Ord a) => (a -> b) -> Parser a -> Parser b
f <$> _r = let (sym,rules,_r_sem) = _r
sem opts arr ctx sppf l r m = S.map f (_r_sem opts arr ctx sppf l r m)
in (sym,rules,sem)
infixl 6 <*>
-- | Sequence two parsers, the results of the two parsers are tupled.
(<*>) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser (a,b)
_l <*> _r = (Nt lhs_id,rules,sem)
where l_id = id_ _l
r_id = id_ _r
lhs_id = concat [l_id, "*", r_id]
-- ** one can bind this parser and recurse on it + other duplicate work
alt = Alt lhs_id [sym_ _l, sym_ _r]
rules m = case M.lookup lhs_id m of -- necessary? **
Nothing -> rules_ _r (rules_ _l (M.insert lhs_id [alt] m))
Just _ -> m
sem opts arr ctx sppf l r m =
let filter = maybe id id $ pivot_select opts in S.fromList
[ (a,b) | k <- filter ks
, a <- S.toList (sem_ _l opts arr ctx sppf l k m)
, b <- S.toList (sem_ _r opts arr ctx sppf k r m) ]
where ks = maybe [] id $ sppf `pNodeLookup` ((alt,2), l, r)
infixl 4 <|>
-- | A choice between two parsers, results of the two are concatenated
(<|>) :: (Ord a) => Parser a -> Parser a -> Parser a
_l <|> _r = (Nt lhs_id,rules,sem)
where l_id = id_ _l
r_id = id_ _r
lhs_id = concat [l_id, "|", r_id]
alts = [Alt lhs_id [sym_ _l], Alt lhs_id [sym_ _r]]
rules m = case M.lookup lhs_id m of
Nothing -> rules_ _r (rules_ _l (M.insert lhs_id alts m))
Just _ -> m
sem opts arr ctx sppf l r m =
concatChoice opts (sem_ _l opts arr ctx sppf l r m)
(sem_ _r opts arr ctx sppf l r m)
-- derived combinators
infixl 6 <*
-- | Sequencing, ignoring the result to the right
(<*) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser a
_l <* _r = (\(x,y) -> x) <$> _l <*> _r
infixl 6 *>
-- | Sequencing, ignoring the result to the left
(*>) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser b
_l *> _r = (\(x,y) -> y) <$> _l <*> _r
infixl 5 <$
-- | Ignore all results and just return the given value
(<$) :: (Ord a, Ord b) => a -> Parser b -> Parser a
f <$ _r = const f <$> _r
-- elementary parsers
raw_parser :: String -> Token -> (Token -> a) -> Parser a
raw_parser str t f = (Nt str, rules, sem)
where alt = Alt str [Term t]
rules = M.insert str [alt]
sem _ arr ctx sppf l r m
| l + 1 == r && l < m && arr A.! l == t = S.singleton (f t)
| otherwise = S.empty
-- | A parser that recognises a given character
char :: Char -> Parser Char
char c = raw_parser ([c]) (Char c) (\(Char c) -> c)
-- | A parser that recognises a given token
token :: Token -> Parser Token
token t = raw_parser (show t) t id
-- | A parser that always succeeds (and returns unit)
epsilon :: Parser ()
epsilon = (Nt x, rules, sem)
where x = "__eps"
alt = Alt x [Term Epsilon]
rules = M.insert x [alt]
sem _ arr ctx sppf l r m | l == r = S.singleton ()
| otherwise = S.empty
-- | A parser that always succeeds and returns a given value
satisfy :: (Ord a) => a -> Parser a
satisfy a = a <$ epsilon
-- helpers
sym_ :: Parser a -> Symbol
sym_ (f,_,_) = f
id_ :: Parser a -> Nt
id_ (Nt x,_,_) = x
rules_ :: Parser a -> Visit2
rules_ (_,f,_) = f
sem_ :: Parser a -> Visit3 a
sem_ (_,_,f) = f
mkNt :: String -> Char -> Nt
mkNt x c = concat ["(",x,")",[c]]
inContext :: (Int, Int, Nt) -> ParseContext -> Bool
inContext (l,r,x) = maybe False inner . IM.lookup l
where inner = maybe False ((==) x) . IM.lookup r
toContext :: (Int, Int, Nt) -> ParseContext -> ParseContext
toContext (l,r,x) = IM.insertWith IM.union l (IM.singleton r x)
concatChoice :: (Ord a) => PCOptions -> S.Set a -> S.Set a -> S.Set a
concatChoice opts ls rs = if left_biased_choice opts
then firstRes
else ls `S.union` rs
where firstRes | S.null ls = rs
| otherwise = ls
-- higher level patterns
-- | Optionally use the given parser
optional :: (Ord a) => Parser a -> Parser (Maybe a)
optional p@(Nt x,_,_) = (mkNt x '?') <:=> satisfy Nothing <|> Just <$> p
-- | Apply the given parser many times, 0 or more times (Kleene closure)
many :: (Ord a) => Parser a -> Parser [a]
many p@(Nt x,_,_) = (mkNt x '^') <::=> satisfy []
<|> uncurry (:) <$> p <*> many p
-- | Apply the given parser some times, 1 or more times (positive closure)
some :: (Ord a) => Parser a -> Parser [a]
some p@(Nt x,_,_) = let rec = (mkNt x '+') <::=> (:[]) <$> p
<|> uncurry (:) <$> p <*> rec
in rec