module Foundation.Parser
( Parser
, parse
, parseFeed
, parseOnly
,
Result(..)
, ParseError(..)
, reportError
,
ParserSource(..)
,
element
, anyElement
, elements
, string
, satisfy
, satisfy_
, take
, takeWhile
, takeAll
, skip
, skipWhile
, skipAll
, (<|>)
, many
, some
, optional
, repeat, Condition(..), And(..)
) where
import Control.Applicative (Alternative, empty, (<|>), many, some, optional)
import Control.Monad (MonadPlus, mzero, mplus)
import Basement.Compat.Base
import Basement.Types.OffsetSize
import Foundation.Numerical
import Foundation.Collection hiding (take, takeWhile)
import qualified Foundation.Collection as C
import Foundation.String
data ParseError input
= NotEnough (CountOf (Element input))
| NotEnoughParseOnly
| ExpectedElement (Element input) (Element input)
| Expected (Chunk input) (Chunk input)
| Satisfy (Maybe String)
deriving (Typeable)
instance Typeable input => Exception (ParseError input)
instance Show (ParseError input) where
show (NotEnough (CountOf sz)) = "NotEnough: missing " <> show sz <> " element(s)"
show NotEnoughParseOnly = "NotEnough, parse only"
show (ExpectedElement _ _) = "Expected _ but received _"
show (Expected _ _) = "Expected _ but received _"
show (Satisfy Nothing) = "Satisfy"
show (Satisfy (Just s)) = "Satisfy: " <> toList s
data Result input result
= ParseFailed (ParseError input)
| ParseOk (Chunk input) result
| ParseMore (Chunk input -> Result input result)
instance Show k => Show (Result input k) where
show (ParseFailed err) = "Parser failed: " <> show err
show (ParseOk _ k) = "Parser succeed: " <> show k
show (ParseMore _) = "Parser incomplete: need more"
instance Functor (Result input) where
fmap f r = case r of
ParseFailed err -> ParseFailed err
ParseOk rest a -> ParseOk rest (f a)
ParseMore more -> ParseMore (fmap f . more)
class (Sequential input, IndexedCollection input) => ParserSource input where
type Chunk input
nullChunk :: input -> Chunk input -> Bool
appendChunk :: input -> Chunk input -> input
subChunk :: input -> Offset (Element input) -> CountOf (Element input) -> Chunk input
spanChunk :: input -> Offset (Element input) -> (Element input -> Bool) -> (Chunk input, Offset (Element input))
endOfParserSource :: ParserSource input => input -> Offset (Element input) -> Bool
endOfParserSource l off = off .==# length l
data NoMore = More | NoMore
deriving (Show, Eq)
type Failure input result = input -> Offset (Element input) -> NoMore -> ParseError input -> Result input result
type Success input result' result = input -> Offset (Element input) -> NoMore -> result' -> Result input result
newtype Parser input result = Parser
{ runParser :: forall result'
. input -> Offset (Element input) -> NoMore
-> Failure input result'
-> Success input result result'
-> Result input result'
}
instance Functor (Parser input) where
fmap f fa = Parser $ \buf off nm err ok ->
runParser fa buf off nm err $ \buf' off' nm' a -> ok buf' off' nm' (f a)
instance ParserSource input => Applicative (Parser input) where
pure a = Parser $ \buf off nm _ ok -> ok buf off nm a
fab <*> fa = Parser $ \buf0 off0 nm0 err ok ->
runParser fab buf0 off0 nm0 err $ \buf1 off1 nm1 ab ->
runParser_ fa buf1 off1 nm1 err $ \buf2 off2 nm2 -> ok buf2 off2 nm2 . ab
instance ParserSource input => Monad (Parser input) where
return = pure
m >>= k = Parser $ \buf off nm err ok ->
runParser m buf off nm err $ \buf' off' nm' a ->
runParser_ (k a) buf' off' nm' err ok
instance ParserSource input => MonadPlus (Parser input) where
mzero = error "Foundation.Parser.Internal.MonadPlus.mzero"
mplus f g = Parser $ \buf off nm err ok ->
runParser f buf off nm (\buf' _ nm' _ -> runParser g buf' off nm' err ok) ok
instance ParserSource input => Alternative (Parser input) where
empty = error "Foundation.Parser.Internal.Alternative.empty"
(<|>) = mplus
runParser_ :: ParserSource input
=> Parser input result
-> input
-> Offset (Element input)
-> NoMore
-> Failure input result'
-> Success input result result'
-> Result input result'
runParser_ parser buf off NoMore err ok = runParser parser buf off NoMore err ok
runParser_ parser buf off nm err ok
| endOfParserSource buf off = ParseMore $ \chunk ->
if nullChunk buf chunk
then runParser parser buf off NoMore err ok
else runParser parser (appendChunk buf chunk) off nm err ok
| otherwise = runParser parser buf off nm err ok
parseFeed :: (ParserSource input, Monad m)
=> m (Chunk input)
-> Parser input a
-> input
-> m (Result input a)
parseFeed feeder p initial = loop $ parse p initial
where loop (ParseMore k) = feeder >>= (loop . k)
loop r = return r
parse :: ParserSource input
=> Parser input a -> input -> Result input a
parse p s = runParser p s 0 More failure success
failure :: input -> Offset (Element input) -> NoMore -> ParseError input -> Result input r
failure _ _ _ = ParseFailed
success :: ParserSource input => input -> Offset (Element input) -> NoMore -> r -> Result input r
success buf off _ = ParseOk rest
where
!rest = subChunk buf off (length buf `sizeSub` offsetAsSize off)
parseOnly :: (ParserSource input, Monoid (Chunk input))
=> Parser input a
-> input
-> Either (ParseError input) a
parseOnly p i = case runParser p i 0 NoMore failure success of
ParseFailed err -> Left err
ParseOk _ r -> Right r
ParseMore _ -> Left NotEnoughParseOnly
instance ParserSource String where
type Chunk String = String
nullChunk _ = null
appendChunk = mappend
subChunk c off sz = C.take sz $ C.drop (offsetAsSize off) c
spanChunk buf off predicate =
let c = C.drop (offsetAsSize off) buf
(t, _) = C.span predicate c
in (t, off `offsetPlusE` length t)
instance ParserSource [a] where
type Chunk [a] = [a]
nullChunk _ = null
appendChunk = mappend
subChunk c off sz = C.take sz $ C.drop (offsetAsSize off) c
spanChunk buf off predicate =
let c = C.drop (offsetAsSize off) buf
(t, _) = C.span predicate c
in (t, off `offsetPlusE` length t)
reportError :: ParseError input -> Parser input a
reportError pe = Parser $ \buf off nm err _ -> err buf off nm pe
anyElement :: ParserSource input => Parser input (Element input)
anyElement = Parser $ \buf off nm err ok ->
case buf ! off of
Nothing -> err buf off nm $ NotEnough 1
Just x -> ok buf (succ off) nm x
element :: ( ParserSource input
, Eq (Element input)
, Element input ~ Element (Chunk input)
)
=> Element input
-> Parser input ()
element expectedElement = Parser $ \buf off nm err ok ->
case buf ! off of
Nothing -> err buf off nm $ NotEnough 1
Just x | expectedElement == x -> ok buf (succ off) nm ()
| otherwise -> err buf off nm $ ExpectedElement expectedElement x
elements :: ( ParserSource input, Sequential (Chunk input)
, Element (Chunk input) ~ Element input
, Eq (Chunk input)
)
=> Chunk input -> Parser input ()
elements = consumeEq
where
consumeEq :: ( ParserSource input
, Sequential (Chunk input)
, Element (Chunk input) ~ Element input
, Eq (Chunk input)
)
=> Chunk input -> Parser input ()
consumeEq expected = Parser $ \buf off nm err ok ->
if endOfParserSource buf off
then
err buf off nm $ NotEnough lenE
else
let !lenI = sizeAsOffset (length buf) off
in if lenI >= lenE
then
let a = subChunk buf off lenE
in if a == expected
then ok buf (off + sizeAsOffset lenE) nm ()
else err buf off nm $ Expected expected a
else
let a = subChunk buf off lenI
(e', r) = splitAt lenI expected
in if a == e'
then runParser_ (consumeEq r) buf (off + sizeAsOffset lenI) nm err ok
else err buf off nm $ Expected e' a
where
!lenE = length expected
satisfy :: ParserSource input => Maybe String -> (Element input -> Bool) -> Parser input (Element input)
satisfy desc predicate = Parser $ \buf off nm err ok ->
case buf ! off of
Nothing -> err buf off nm $ NotEnough 1
Just x | predicate x -> ok buf (succ off) nm x
| otherwise -> err buf off nm $ Satisfy desc
satisfy_ :: ParserSource input => (Element input -> Bool) -> Parser input (Element input)
satisfy_ = satisfy Nothing
take :: ( ParserSource input
, Sequential (Chunk input)
, Element input ~ Element (Chunk input)
)
=> CountOf (Element (Chunk input))
-> Parser input (Chunk input)
take n = Parser $ \buf off nm err ok ->
let lenI = sizeAsOffset (length buf) off
in if endOfParserSource buf off && n > 0
then err buf off nm $ NotEnough n
else case n lenI of
Just s | s > 0 -> let h = subChunk buf off lenI
in runParser_ (take s) buf (sizeAsOffset lenI) nm err $
\buf' off' nm' t -> ok buf' off' nm' (h <> t)
_ -> ok buf (off + sizeAsOffset n) nm (subChunk buf off n)
takeWhile :: ( ParserSource input, Sequential (Chunk input)
)
=> (Element input -> Bool)
-> Parser input (Chunk input)
takeWhile predicate = Parser $ \buf off nm err ok ->
if endOfParserSource buf off
then ok buf off nm mempty
else let (b1, off') = spanChunk buf off predicate
in if endOfParserSource buf off'
then runParser_ (takeWhile predicate) buf off' nm err
$ \buf' off'' nm' b1T -> ok buf' off'' nm' (b1 <> b1T)
else ok buf off' nm b1
takeAll :: (ParserSource input, Sequential (Chunk input)) => Parser input (Chunk input)
takeAll = getAll >> returnBuffer
where
returnBuffer :: ParserSource input => Parser input (Chunk input)
returnBuffer = Parser $ \buf off nm _ ok ->
let !lenI = length buf
!off' = sizeAsOffset lenI
!sz = off' off
in ok buf off' nm (subChunk buf off sz)
getAll :: (ParserSource input, Sequential (Chunk input)) => Parser input ()
getAll = Parser $ \buf off nm err ok ->
case nm of
NoMore -> ok buf off nm ()
More -> ParseMore $ \nextChunk ->
if nullChunk buf nextChunk
then ok buf off NoMore ()
else runParser getAll (appendChunk buf nextChunk) off nm err ok
skip :: ParserSource input => CountOf (Element input) -> Parser input ()
skip n = Parser $ \buf off nm err ok ->
let lenI = sizeAsOffset (length buf) off
in if endOfParserSource buf off && n > 0
then err buf off nm $ NotEnough n
else case n lenI of
Just s | s > 0 -> runParser_ (skip s) buf (sizeAsOffset lenI) nm err ok
_ -> ok buf (off + sizeAsOffset n) nm ()
skipWhile :: ( ParserSource input, Sequential (Chunk input)
)
=> (Element input -> Bool)
-> Parser input ()
skipWhile predicate = Parser $ \buf off nm err ok ->
if endOfParserSource buf off
then ok buf off nm ()
else let (_, off') = spanChunk buf off predicate
in if endOfParserSource buf off'
then runParser_ (skipWhile predicate) buf off' nm err ok
else ok buf off' nm ()
skipAll :: (ParserSource input, Collection (Chunk input)) => Parser input ()
skipAll = flushAll
where
flushAll :: (ParserSource input, Collection (Chunk input)) => Parser input ()
flushAll = Parser $ \buf off nm err ok ->
let !off' = sizeAsOffset $ length buf in
case nm of
NoMore -> ok buf off' NoMore ()
More -> ParseMore $ \nextChunk ->
if null nextChunk
then ok buf off' NoMore ()
else runParser flushAll buf off nm err ok
string :: String -> Parser String ()
string = elements
data Condition = Between !And | Exactly !Word
deriving (Show, Eq, Typeable)
data And = And !Word !Word
deriving (Eq, Typeable)
instance Show And where
show (And a b) = show a <> " and " <> show b
repeat :: ParserSource input
=> Condition -> Parser input a -> Parser input [a]
repeat (Exactly n) = repeatE n
repeat (Between a) = repeatA a
repeatE :: (ParserSource input)
=> Word -> Parser input a -> Parser input [a]
repeatE 0 _ = return []
repeatE n p = (:) <$> p <*> repeatE (n1) p
repeatA :: (ParserSource input)
=> And -> Parser input a -> Parser input [a]
repeatA (And 0 0) _ = return []
repeatA (And 0 n) p = ((:) <$> p <*> repeatA (And 0 (n1)) p) <|> return []
repeatA (And l u) p = (:) <$> p <*> repeatA (And (l1) (u1)) p