packages feed

Earley 0.6.0 → 0.7.0

raw patch · 4 files changed

+147/−68 lines, 4 filesdep −kan-extensionsPVP ok

version bump matches the API change (PVP)

Dependencies removed: kan-extensions

API changes (from Hackage documentation)

+ Text.Earley: report :: ListLike i t => (forall s. ST s (Result s e i a)) -> Report e i
+ Text.Earley.Parser: report :: ListLike i t => (forall s. ST s (Result s e i a)) -> Report e i
- Text.Earley: [Parsed] :: a -> Int -> i -> (i -> ST s (Result s e i a)) -> Result s e i a
+ Text.Earley: [Parsed] :: (ST s [a]) -> Int -> i -> (ST s (Result s e i a)) -> Result s e i a
- Text.Earley.Parser: [Parsed] :: a -> Int -> i -> (i -> ST s (Result s e i a)) -> Result s e i a
+ Text.Earley.Parser: [Parsed] :: (ST s [a]) -> Int -> i -> (ST s (Result s e i a)) -> Result s e i a

Files

Earley.cabal view
@@ -1,5 +1,5 @@ name:                Earley-version:             0.6.0+version:             0.7.0 synopsis:            Parsing all context-free grammars using Earley's algorithm. description:         See <https://www.github.com/ollef/Earley> for more                      information and@@ -21,7 +21,7 @@ library   exposed-modules:     Text.Earley.Derived, Text.Earley.Grammar, Text.Earley.Parser Text.Earley   -- other-modules:-  build-depends:       base ==4.8.*, containers >=0.5, kan-extensions >=4.2, ListLike >=4.1+  build-depends:       base ==4.8.*, containers >=0.5, ListLike >=4.1   -- hs-source-dirs:   default-language:    Haskell2010   ghc-options:         -Wall -funbox-strict-fields
Text/Earley.hs view
@@ -6,6 +6,8 @@     symbol, namedSymbol, word   , -- * Parsing     Report(..), Result(..), parser, allParses, fullParses+    -- * Recognition+  , report   )   where import Text.Earley.Grammar
Text/Earley/Grammar.hs view
@@ -88,8 +88,9 @@   Named p m <|> q         = Named (p <|> q) m   p         <|> Named q n = Named (p <|> q) n   p         <|> q         = Plus p q-  many p       = Many p $ Pure id-  some p       = (:) <$> p <*> many p+  many Empty = pure []+  many p     = Many p $ Pure id+  some p     = (:) <$> p <*> many p  -- | A context-free grammar. --@@ -131,6 +132,6 @@ instance MonadFix (Grammar r e) where   mfix f = FixBind f return --- | Create a new non-terminal by listing its production rule.+-- | Create a new non-terminal by giving its production. rule :: Prod r e t a -> Grammar r e (Prod r e t a) rule p = RuleBind p return
Text/Earley/Parser.hs view
@@ -6,12 +6,13 @@   , parser   , allParses   , fullParses+  , report   ) where import Control.Applicative import Control.Arrow+import Control.Monad import Control.Monad.Fix import Control.Monad.ST.Lazy-import Data.Functor.Yoneda import Data.ListLike(ListLike) import qualified Data.ListLike as ListLike import Data.STRef.Lazy@@ -24,7 +25,7 @@ data Rule s r e t a = Rule   { ruleProd     :: ProdR s r e t a   , ruleNullable :: {-# UNPACK #-} !(STRef s (Maybe [a]))-  , ruleConts    :: {-# UNPACK #-} !(STRef s (Conts s r e t a r))+  , ruleConts    :: {-# UNPACK #-} !(STRef s (STRef s [Cont s r e t a r]))   }  type ProdR s r e t a = Prod (Rule s r) e t a@@ -53,6 +54,25 @@ nullableProd Empty             = return mempty nullableProd (Named p _)       = nullableProd p +-- | If we have something of type @f@, @'Args' s f a@ is what we need to do to+-- @f@ to produce @a@s.+type Args s f a = f -> ST s [a]++noArgs :: Args s a a+noArgs = return . pure++pureArg :: x -> Args s f a -> Args s (x -> f) a+pureArg x args = args . ($ x)++impureArgs :: ST s [x] -> Args s f a -> Args s (x -> f) a+impureArgs mxs args f = fmap concat . mapM (args . f) =<< mxs++mapArgs :: (a -> b) -> Args s f a -> Args s f b+mapArgs = fmap . fmap . fmap++composeArgs :: Args s a b -> Args s b c -> Args s a c+composeArgs ab bc a = fmap concat . mapM bc =<< ab a+ ------------------------------------------------------------------------------- -- * States and continuations -------------------------------------------------------------------------------@@ -61,37 +81,45 @@ -- | An Earley state with result type @a@. data State s r e t a where   State :: {-# UNPACK #-} !Pos-        -> !(ProdR s r e t b)+        -> !(ProdR s r e t f)+        -> {-# UNPACK #-} !(Args s f b)         -> {-# UNPACK #-} !(Conts s r e t b a)         -> State s r e t a-  Final :: a -> State s r e t a+  Final :: f -> Args s f a -> State s r e t a  -- | A continuation accepting an @a@ and producing a @b@. data Cont s r e t a b where   Cont      :: {-# UNPACK #-} !Pos-            -> !(ProdR s r e t (a -> b))-            -> {-# UNPACK #-} !(Conts s r e t b c)-            -> Cont s r e t a c-  FinalCont :: (a -> c) -> Cont s r e t a c+            -> {-# UNPACK #-} !(Args s a b)+            -> !(ProdR s r e t (b -> c))+            -> {-# UNPACK #-} !(Args s c d)+            -> {-# UNPACK #-} !(Conts s r e t d e')+            -> Cont s r e t a e'+  FinalCont :: Args s a c -> Cont s r e t a c -type Conts s r e t a c = STRef s [Cont s r e t a c]+data Conts s r e t a c = Conts+  { conts     :: {-# UNPACK #-} !(STRef s [Cont s r e t a c])+  , contsArgs :: {-# UNPACK #-} !(STRef s (Maybe (STRef s (ST s [a]))))+  } -contraMapCont :: (b -> a) -> Cont s r e t a c -> Cont s r e t b c-contraMapCont f (Cont pos p cs) = (Cont pos $! ((. f) <$> p)) cs-contraMapCont f (FinalCont g)   = FinalCont (g . f)+contraMapCont :: Args s b a -> Cont s r e t a c -> Cont s r e t b c+contraMapCont f (Cont pos g p args cs) = Cont pos (composeArgs f g) p args cs+contraMapCont f (FinalCont args)       = FinalCont (composeArgs f args) -contToState :: a -> Cont s r e t a c -> State s r e t c-contToState a (Cont pos p cs) = State pos (($ a) <$> p) cs-contToState a (FinalCont f)   = Final (f a)+contToState :: ST s [a] -> Cont s r e t a c -> State s r e t c+contToState r (Cont pos g p args cs) = +  let mb = fmap concat . mapM g =<< r in+  State pos p (impureArgs mb args) cs+contToState r (FinalCont args)       = Final id (impureArgs r args)  -- | Strings of non-ambiguous continuations can be optimised by removing --   indirections. simplifyCont :: Conts s r e t b a -> ST s [Cont s r e t b a]-simplifyCont cont = readSTRef cont >>= go False+simplifyCont Conts {conts = cont} = readSTRef cont >>= go False   where-    go !_ [Cont _ (Pure f) cont'] = do+    go !_ [Cont _ g (Pure f) args cont'] = do       ks' <- simplifyCont cont'-      go True $ map (contraMapCont f) ks'+      go True $ map (contraMapCont $ mapArgs f g `composeArgs` args) ks'     go True ks = do       writeSTRef cont ks       return ks@@ -114,10 +142,10 @@  -- | Given a grammar, construct an initial state. initialState :: ProdR s a e t a -> ST s (State s a e t a)-initialState r = do-  rs <- newSTRef [FinalCont id]-  return $ State (-1) r rs+initialState p = State (-1) p noArgs+              <$> (Conts <$> newSTRef [FinalCont noArgs] <*> newSTRef Nothing) + ------------------------------------------------------------------------------- -- * Parsing -------------------------------------------------------------------------------@@ -137,11 +165,13 @@ data Result s e i a   = Ended (Report e i)     -- ^ The parser ended.-  | Parsed a Int i (i -> ST s (Result s e i a))-    -- ^ The parser parsed something, namely an 'a'. The 'Int' is the position-    -- in the input where it did so, the 'i' is the rest of the input, and the-    -- function is the parser continuation. This allows incrementally feeding-    -- the parser more input (e.g. when the 'i' is empty).+  | Parsed (ST s [a]) Int i (ST s (Result s e i a))+    -- ^ The parser parsed a number of @a@s.  These are given as a computation,+    -- @'ST' s [a]@ that constructs the 'a's when run.  We can thus save some+    -- work by ignoring this computation if we do not care about the results.+    -- The 'Int' is the position in the input where these results were+    -- obtained, the @i@ the rest of the input, and the last component is the+    -- continuation.   deriving (Functor)  {-# INLINE uncons #-}@@ -156,60 +186,90 @@   | ListLike.null ts' = ts'   | otherwise         = ListLike.tail ts' -{-# SPECIALISE parse :: [State s a e t a] -> [State s a e t a] -> ST s () -> [e] -> Pos -> [t] -> ST s (Result s e [t] a) #-}+{-# SPECIALISE parse :: [State s a e t a]+                     -> [ST s [a]]+                     -> [State s a e t a]+                     -> ST s ()+                     -> [e]+                     -> Pos+                     -> [t]+                     -> ST s (Result s e [t] a) #-} -- | The internal parsing routine parse :: ListLike i t       => [State s a e t a] -- ^ States to process at this position+      -> [ST s [a]]        -- ^ Results ready to be reported (when this position has been processed)       -> [State s a e t a] -- ^ States to process at the next position       -> ST s ()           -- ^ Computation that resets the continuation refs of productions       -> [e]               -- ^ Named productions encountered at this position       -> Pos               -- ^ The current position in the input string       -> i                 -- ^ The input string       -> ST s (Result s e i a)-parse []      []    !reset names !pos   !ts = do+parse [] [] [] !reset names !pos !ts = do   reset   return $ Ended Report {position = pos, expected = names, unconsumed = ts}-parse []      !next !reset _names !pos !ts = do+parse [] [] !next !reset _ !pos !ts = do   reset-  parse next [] (return ()) [] (pos + 1) (safeTail ts)-parse (st:ss) !next !reset names !pos !ts = case st of-  Final a -> return $ Parsed a pos ts $ parse ss next reset names pos-  State spos pr scont -> case pr of+  parse next [] [] (return ()) [] (pos + 1) $ safeTail ts+parse [] !results !next !reset names !pos !ts = do+  reset+  return $ Parsed (concat <$> sequence results) pos ts+         $ parse [] [] next (return ()) names pos ts+parse (st:ss) !results !next !reset names !pos !ts = case st of+  Final f args -> parse ss (args f : results) next reset names pos ts+  State spos pr args scont -> case pr of     Terminal f p -> case uncons ts of-      Just (t, _) | f t -> parse ss (State spos (($ t) <$> p) scont : next) reset names pos ts-      _                 -> parse ss next reset names pos ts+      Just (t, _) | f t ->+        parse ss results (State spos p (pureArg t args) scont : next) reset names pos ts+      _                 -> parse ss results next reset names pos ts     NonTerminal r p -> do       rkref <- readSTRef $ ruleConts r       ks    <- readSTRef rkref-      writeSTRef rkref (Cont spos p scont : ks)-      nulls' <- nullable r-      let notExpanded = null ks-          p'          = liftYoneda p-          nulls       = fmap (\a -> State spos (lowerYoneda $ ($ a) <$> p') scont) nulls'-      if notExpanded then do-        let st' = State pos (ruleProd r) rkref-        parse (st' : nulls ++ ss)+      writeSTRef rkref (Cont spos noArgs p args scont : ks)+      nulls <- nullable r+      let nullStates = [State spos p (pureArg a args) scont | a <- nulls]+      if null ks then do -- The rule has not been expanded at this position.+        asref <- newSTRef Nothing+        let st' = State pos (ruleProd r) noArgs (Conts rkref asref)+        parse (st' : nullStates ++ ss)+              results               next               ((writeSTRef (ruleConts r) =<< newSTRef mempty) >> reset)               names               pos               ts-      else-        parse (nulls ++ ss) next reset names pos ts+      else -- The rule has already been expanded at this position.+        parse (nullStates ++ ss) results next reset names pos ts     Pure a | spos /= pos -> do-      conts <- simplifyCont scont-      parse (map (contToState a) conts ++ ss) next reset names pos ts-           | otherwise -> parse ss next reset names pos ts+      let argsRef = contsArgs scont+      masref  <- readSTRef argsRef+      case masref of+        Just asref -> do -- The continuation has already been followed at this position.+          modifySTRef asref (((++) <$> args a) <*>)+          parse ss results next reset names pos ts+        Nothing    -> do -- It hasn't.+          asref <- newSTRef (return mempty)+          modifySTRef asref (((++) <$> args a) <*>)+          writeSTRef argsRef $ Just asref+          ks  <- simplifyCont scont+          let kstates = map (contToState $ join $ readSTRef asref) ks+          parse (kstates ++ ss)+                results+                next+                (writeSTRef argsRef Nothing >> reset)+                names+                pos+                ts+           | otherwise -> parse ss results next reset names pos ts -    Plus p q    -> parse (State spos p scont : State spos q scont : ss) next reset names pos ts+    Plus p q    -> parse (State spos p args scont : State spos q args scont : ss) results next reset names pos ts     Many p q    -> do-      rkref <- newSTRef [Cont spos (Many p ((\f as a -> f (a : as)) <$> q)) scont]-      let st' = State pos p rkref-          nst = State spos (($ []) <$> q) scont-      parse (st' : nst : ss) next reset names pos ts-    Empty       -> parse ss next reset names pos ts--    Named pr' n -> parse (State spos pr' scont : ss) next reset (n : names) pos ts+      scont' <- Conts <$> newSTRef [Cont spos noArgs (Many p ((\f as a -> f (a : as)) <$> q)) args scont]+                      <*> newSTRef Nothing+      let st' = State pos p noArgs scont'+          nst = State spos q (pureArg [] args) scont+      parse (st' : nst : ss) results next reset names pos ts+    Empty       -> parse ss results next reset names pos ts+    Named pr' n -> parse (State spos pr' args scont : ss) results next reset (n : names) pos ts  {-# INLINE parser #-} -- | Create a parser from the given grammar.@@ -219,7 +279,7 @@        -> ST s (Result s e i a) parser g xs = do   s <- initialState =<< grammar g-  parse [s] [] (return ()) [] 0 xs+  parse [s] [] [] (return ()) [] 0 xs  -- | Return all parses from the result of a given parser. The result may -- contain partial parses. The 'Int's are the position at which a result was@@ -229,8 +289,10 @@   where     go :: Result s e i a -> ST s ([(a, Int)], Report e i)     go r = case r of-      Ended report     -> return ([], report)-      Parsed a pos i k -> fmap (first ((a, pos) :)) $ go =<< k i+      Ended rep         -> return ([], rep)+      Parsed mas pos _ k -> do+        as <- mas+        fmap (first (zip as (repeat pos) ++)) $ go =<< k  {-# INLINE fullParses #-} -- | Return all parses that reached the end of the input from the result of a@@ -240,7 +302,21 @@   where     go :: ListLike i t => Result s e i a -> ST s ([a], Report e i)     go r = case r of-      Ended report -> return ([], report)-      Parsed a _ i k-        | ListLike.null i -> fmap (first (a :)) $ go =<< k i-        | otherwise       -> go =<< k i+      Ended rep -> return ([], rep)+      Parsed mas _ i k+        | ListLike.null i -> do+          as <- mas+          fmap (first (as ++)) $ go =<< k+        | otherwise       -> go =<< k++{-# INLINE report #-}+-- | See e.g. how far the parser is able to parse the input string before it+-- fails.  This can be much faster than getting the parse results for highly+-- ambiguous grammars.+report :: ListLike i t => (forall s. ST s (Result s e i a)) -> Report e i+report p = runST $ p >>= go+  where+    go :: ListLike i t => Result s e i a -> ST s (Report e i)+    go r = case r of+      Ended rep      -> return rep+      Parsed _ _ _ k -> go =<< k