{-# OPTIONS_GHC -O2 -fglasgow-exts -funbox-strict-fields #-} module Text.Parser.PArrow.Prim (runParser, Res(..), PErr) where import Text.Parser.PArrow.CharSet import Text.Parser.PArrow.MD import Data.ByteString.Base (ByteString(..)) import Data.Seq ((|>)) import qualified Data.Seq as Seq import qualified Data.ByteString.Char8 as Str import Data.IntMap (IntMap, singleton, empty, unionWith) import Data.Generics newtype ParseState = MkParseState { psInput :: ByteString } deriving (Show, Eq, Ord, Typeable, Data) data Res r = POk !ParseState r | PErr !PErr deriving (Show) type PSF r = ParseState -> Res r type PErr = IntMap Label optM :: MD i o -> PSF o optM = matcher err :: ParseState -> MD i o -> Res o' err i p = PErr (singleton (psIndex i) (label p)) psIndex :: ParseState -> Int psIndex MkParseState{ psInput = (PS _ s _) } = s psEmptyMatch :: ParseState -> ByteString psEmptyMatch MkParseState{ psInput = (PS p s _) } = PS p s 0 matcher :: forall i o. MD i o -> PSF o matcher p@(MNot x) i = case optM x i of POk _ _ -> err i p PErr{} -> POk i undefined matcher (MChoice l) i = let mm acc [] = PErr acc mm acc (c:cs) = case optM c i of POk s t -> POk s t PErr err -> mm (unionWith mappend acc err) cs in mm empty l matcher p@(MEqual x) i@MkParseState{psInput = s} = if Str.isPrefixOf x s then let (pre, post) = Str.splitAt (Str.length x) s in POk i{psInput = post} pre else err i p matcher p@(MDyn _ f) i@MkParseState{psInput = s} = case f s of Just (ok, post) -> let pre = Str.take (Str.length s - Str.length post) s in POk i{psInput = post} (pre, ok) _ -> err i p matcher (MSeq a b) i = case optM a i of POk s t -> case b of (MPure _ f) -> POk s (f t) _ -> optM b s PErr e -> PErr e matcher (MEmpty) i = POk i (error "result for empty") matcher (MPure _ _) i = POk i (error "result for pure") matcher p@(MCSet _) i@MkParseState{psInput = s} | Str.null s = err i p matcher (MCSet CS_Any) i@MkParseState{psInput = s} = POk i{psInput = Str.tail s} (Str.take 1 s) matcher p@(MCSet cs) i@MkParseState{psInput = s} = if cs `containsChar` Str.head s then POk i{psInput=Str.tail s} (Str.take 1 s) else err i p matcher (MParWire _ _) _ = error "matcher on ParWire" matcher (MJoin a b) i = case optM a i of POk s t -> case optM b s of POk s' t' -> POk s' (t,t') PErr e -> maybe (PErr e) (\a' -> optM (MJoin a' b) i) (backtrack a i) PErr e -> PErr e matcher (MGreedy min max x) i = let p = optM x sm st acc len | QuantInt len >= max = POk st (Right acc) sm st acc len = case p st of POk st' r -> sm st' (acc |> r) (succ len) PErr e | len < min -> PErr e _ -> POk st (if Seq.null acc then (Left (psEmptyMatch i)) else (Right acc)) in sm i Seq.empty 0 matcher (MLazy 0 _ _) i = POk i (Left (psEmptyMatch i)) matcher (MLazy min max x) i = let p = optM x sm st acc len | len >= min = POk st (Right acc) sm st acc len | QuantInt len >= max = POk st (Right acc) sm st acc len = case p st of POk st' r -> sm st' (acc |> r) (succ len) PErr e | len < min -> PErr e _ -> POk st (if Seq.null acc then (Left (psEmptyMatch i)) else (Right acc)) in sm i Seq.empty 0 backtrack :: MD i o -> ParseState -> Maybe (MD i o) backtrack a@(MGreedy min _ x) i = let POk _ r = optM a i in either (const Nothing) (backup . Seq.length) r where backup n | n <= min = Nothing | otherwise = Just (MGreedy min (QuantInt (pred n)) x) backtrack a@(MLazy _ max x) i = let POk _ r = optM a i in either (const $ backup 0) (backup . Seq.length) r where backup n | QuantInt n >= max = Nothing | otherwise = Just (MLazy (succ n) max x) backtrack (MJoin a b) i = do b' <- backtrack b i return (MJoin a b') backtrack (MSeq a b@(MPure{})) i = do a' <- backtrack a i return (MSeq a' b) backtrack _ _ = Nothing -- | Run a parser producing either a list of error messages or output. runParser :: Show o => MD i o -> ByteString -> Res o runParser md input = optM md (MkParseState{psInput = input})