Skip to content

Commit 80785b0

Browse files
committed
Avoids closure in Stream class
s -> m (Maybe { head :: t, tail :: s, updatePos :: Position -> Position }) instead of having updatePos as a result of uncons or stripPrefix now this operations take position with input which is part of a parser state. this way we should allocation less of intermediate objects.
1 parent ea96e73 commit 80785b0

File tree

4 files changed

+89
-65
lines changed

4 files changed

+89
-65
lines changed

src/Text/Parsing/Parser.purs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ module Text.Parsing.Parser
77
, Parser
88
, runParser
99
, runParserT
10+
, unParserT
11+
, inParserT
1012
, hoistParserT
1113
, mapParserT
1214
, consume
@@ -22,14 +24,14 @@ import Control.Lazy (defer, class Lazy)
2224
import Control.Monad.Error.Class (class MonadThrow, throwError)
2325
import Control.Monad.Except (class MonadError, ExceptT(..), runExceptT, mapExceptT)
2426
import Control.Monad.Rec.Class (class MonadRec)
25-
import Control.Monad.State (runStateT, class MonadState, StateT(..), gets, evalStateT, mapStateT, modify)
27+
import Control.Monad.State (runStateT, class MonadState, StateT(..), gets, mapStateT, modify)
2628
import Control.Monad.Trans.Class (class MonadTrans, lift)
2729
import Control.MonadPlus (class Alternative, class MonadZero, class MonadPlus, class Plus)
2830
import Data.Either (Either(..))
2931
import Data.Identity (Identity)
3032
import Data.Monoid (class Monoid, mempty)
3133
import Data.Newtype (class Newtype, unwrap, over)
32-
import Data.Tuple (Tuple(..))
34+
import Data.Tuple (Tuple(..), fst)
3335
import Text.Parsing.Parser.Pos (Position, initialPos)
3436

3537
-- | A parsing error, consisting of a message and position information.
@@ -49,7 +51,9 @@ derive instance eqParseError :: Eq ParseError
4951
derive instance ordParseError :: Ord ParseError
5052

5153
-- | Contains the remaining input and current position.
52-
data ParseState s = ParseState s Position Boolean
54+
-- data ParseState s = ParseState s Position Boolean
55+
newtype ParseState s = ParseState
56+
{ input :: s, pos :: Position, consumed :: Boolean }
5357

5458
-- | The Parser monad transformer.
5559
-- |
@@ -61,8 +65,23 @@ derive instance newtypeParserT :: Newtype (ParserT s m a) _
6165

6266
-- | Apply a parser, keeping only the parsed result.
6367
runParserT :: forall m s a. Monad m => s -> ParserT s m a -> m (Either ParseError a)
64-
runParserT s p = evalStateT (runExceptT (unwrap p)) initialState where
65-
initialState = ParseState s initialPos false
68+
runParserT input p = fst <$> unParserT p initialState
69+
where
70+
initialState = ParseState { input, pos: initialPos, consumed: false }
71+
72+
-- Reveals inner function of parser
73+
unParserT :: forall m s a
74+
. Monad m
75+
=> ParserT s m a
76+
-> (ParseState s -> m (Tuple (Either ParseError a) (ParseState s)))
77+
unParserT (ParserT p) = runStateT $ runExceptT p
78+
79+
-- Takes inner function of Parser and constructs one
80+
inParserT :: forall m s a
81+
. Monad m
82+
=> (ParseState s -> m (Tuple (Either ParseError a) (ParseState s)))
83+
-> ParserT s m a
84+
inParserT = ParserT <<< ExceptT <<< StateT
6685

6786
-- | The `Parser` monad is a synonym for the parser monad transformer applied to the `Identity` monad.
6887
type Parser s = ParserT s Identity
@@ -101,12 +120,12 @@ derive newtype instance monadThrowParserT :: Monad m => MonadThrow ParseError (P
101120
derive newtype instance monadErrorParserT :: Monad m => MonadError ParseError (ParserT s m)
102121

103122
instance altParserT :: Monad m => Alt (ParserT s m) where
104-
alt p1 p2 = (ParserT <<< ExceptT <<< StateT) \(s@(ParseState i p _)) -> do
105-
Tuple e s'@(ParseState i' p' c') <- runStateT (runExceptT (unwrap p1)) (ParseState i p false)
106-
case e of
107-
Left err
108-
| not c' -> runStateT (runExceptT (unwrap p2)) s
109-
_ -> pure (Tuple e s')
123+
alt p1 p2 = inParserT \(ParseState state) ->
124+
unParserT p1 (ParseState (state{consumed = false})) <#> \(Tuple e (ParseState nextState)) ->
125+
case e of
126+
Left err
127+
| not nextState.consumed -> unParserT p2 (ParseState state)
128+
_ -> pure (Tuple e (ParseState nextState))
110129

111130
instance plusParserT :: Monad m => Plus (ParserT s m) where
112131
empty = fail "No alternative"
@@ -122,12 +141,12 @@ instance monadTransParserT :: MonadTrans (ParserT s) where
122141

123142
-- | Set the consumed flag.
124143
consume :: forall s m. Monad m => ParserT s m Unit
125-
consume = modify \(ParseState input pos _) ->
126-
ParseState input pos true
144+
consume = modify \(ParseState state) ->
145+
ParseState state{consumed = true}
127146

128147
-- | Returns the current position in the stream.
129148
position :: forall s m. Monad m => ParserT s m Position
130-
position = gets \(ParseState _ pos _) -> pos
149+
position = gets \(ParseState state) -> state.pos
131150

132151
-- | Fail with a message.
133152
fail :: forall m s a. Monad m => String -> ParserT s m a

src/Text/Parsing/Parser/Combinators.purs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,14 @@
2121
module Text.Parsing.Parser.Combinators where
2222

2323
import Prelude
24-
import Control.Monad.Except (runExceptT, ExceptT(..))
25-
import Control.Monad.State (StateT(..), runStateT)
2624
import Control.Plus (empty, (<|>))
2725
import Data.Either (Either(..))
2826
import Data.Foldable (class Foldable, foldl)
2927
import Data.List (List(..), (:), many, some, singleton)
3028
import Data.Maybe (Maybe(..))
3129
import Data.Newtype (unwrap)
3230
import Data.Tuple (Tuple(..))
33-
import Text.Parsing.Parser (ParseState(..), ParserT(..), ParseError(..), fail)
31+
import Text.Parsing.Parser (ParseState(..), ParserT(..), ParseError(..), unParserT, inParserT, fail)
3432

3533
-- | Provide an error message in the case of failure.
3634
withErrorMessage :: forall m s a. Monad m => ParserT s m a -> String -> ParserT s m a
@@ -68,24 +66,28 @@ optionMaybe p = option Nothing (Just <$> p)
6866

6967
-- | In case of failure, reset the stream to the unconsumed state.
7068
try :: forall m s a. Monad m => ParserT s m a -> ParserT s m a
71-
try p = (ParserT <<< ExceptT <<< StateT) \(s@(ParseState _ _ consumed)) -> do
72-
Tuple e s'@(ParseState input position _) <- runStateT (runExceptT (unwrap p)) s
73-
case e of
74-
Left _ -> pure (Tuple e (ParseState input position consumed))
75-
_ -> pure (Tuple e s')
69+
try p = inParserT \(ParseState state) ->
70+
unParserT p (ParseState state) <#> \(Tuple e (ParseState nextState)) ->
71+
case e of
72+
Left _ -> Tuple e (ParseState nextState{consumed = state.consumed})
73+
Right _ -> Tuple e (ParseState nextState)
7674

7775
-- | Like `try`, but will reannotate the error location to the `try` point.
7876
tryRethrow :: forall m s a. Monad m => ParserT s m a -> ParserT s m a
79-
tryRethrow p = (ParserT <<< ExceptT <<< StateT) \(s@(ParseState _ position consumed)) -> do
80-
Tuple e s'@(ParseState input' position' _) <- runStateT (runExceptT (unwrap p)) s
81-
case e of
82-
Left (ParseError err _) -> pure (Tuple (Left (ParseError err position)) (ParseState input' position' consumed))
83-
_ -> pure (Tuple e s')
77+
tryRethrow p = inParserT \(ParseState state) ->
78+
unParserT p (ParseState state) <#> \(Tuple e (ParseState nextState)) ->
79+
case e of
80+
Left (ParseError err _) ->
81+
Tuple
82+
(Left (ParseError err state.pos))
83+
(ParseState nextState{consumed = state.consumed})
84+
Right _ ->
85+
Tuple e (ParseState nextState)
8486

8587
-- | Parse a phrase, without modifying the consumed state or stream position.
8688
lookAhead :: forall s a m. Monad m => ParserT s m a -> ParserT s m a
87-
lookAhead p = (ParserT <<< ExceptT <<< StateT) \s -> do
88-
Tuple e _ <- runStateT (runExceptT (unwrap p)) s
89+
lookAhead p = inParserT \s -> do
90+
Tuple e _ <- unParserT p s
8991
pure (Tuple e s)
9092

9193
-- | Parse phrases delimited by a separator.

src/Text/Parsing/Parser/Indent.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ import Control.Monad.State.Trans (get, put)
5959
import Control.Monad.Trans.Class (lift)
6060
import Data.List (List(..), many)
6161
import Data.Maybe (Maybe(..))
62-
import Text.Parsing.Parser (ParserT, ParseState(ParseState), fail)
62+
import Text.Parsing.Parser (ParserT, ParseState(..), fail)
6363
import Text.Parsing.Parser.Combinators (option, optionMaybe)
6464
import Text.Parsing.Parser.Pos (Position(..), initialPos)
6565
import Text.Parsing.Parser.Stream (prefix, oneOf)
@@ -71,7 +71,7 @@ type IndentParser s a = ParserT s (State Position) a
7171
-- | @ getPosition @ returns current position
7272
-- | should probably be added to Text.Parsing.Parser.Pos
7373
getPosition :: forall m s. (Monad m) => ParserT s m Position
74-
getPosition = gets \(ParseState _ pos _) -> pos
74+
getPosition = gets \(ParseState state) -> state.pos
7575

7676
-- | simple helper function to avoid typ-problems with MonadState instance
7777
get' :: forall s. IndentParser s Position

src/Text/Parsing/Parser/Stream.purs

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
module Text.Parsing.Parser.Stream where
44

5-
import Control.Monad.State (modify, gets)
5+
import Control.Monad.State (put, get)
66
import Control.Monad.Trans.Class (lift)
7-
import Data.Foldable (fold, elem, notElem)
7+
import Data.Foldable (foldl, elem, notElem)
88
import Data.List as L
99
import Data.List.Lazy as LazyL
1010
import Data.Maybe (Maybe(..))
11-
import Data.Monoid.Endo (Endo(..))
12-
import Data.Newtype (class Newtype, unwrap)
11+
import Data.Newtype (class Newtype)
1312
import Data.String as S
13+
import Data.Tuple (Tuple(..))
1414
import Prelude hiding (between)
1515
import Text.Parsing.Parser (ParseState(..), ParserT, fail)
1616
import Text.Parsing.Parser.Combinators (tryRethrow, (<?>))
@@ -36,60 +36,63 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where
3636
updatePos = updatePosChar
3737

3838
-- | This class exists to abstract over streams which support the string-like
39-
-- | operations which this modules needs.
39+
-- | operations with position tracking, which this modules needs.
4040
-- |
4141
-- | Instances must satisfy the following laws:
42-
-- | - `stripPrefix (Prefix a) a >>= uncons = Nothing`
42+
-- | - `stripPrefix (Prefix input) {input, position} >>= uncons = Nothing`
43+
4344
class Stream s m t | s -> t where
44-
uncons :: s -> m (Maybe { head :: t, tail :: s, updatePos :: Position -> Position })
45-
stripPrefix :: Prefix s -> s -> m (Maybe { rest :: s, updatePos :: Position -> Position })
45+
uncons :: forall r. ParserCursor s r -> m (Maybe (Tuple t (ParserCursor s r)))
46+
stripPrefix :: forall r. Prefix s -> ParserCursor s r -> m (Maybe (ParserCursor s r))
47+
48+
-- Part or ParseState which is exposed to Stream instances
49+
type ParserCursor s r = { input :: s, pos :: Position | r}
50+
4651

47-
instance stringStream :: (Applicative m) => Stream String m Char where
48-
uncons f = pure $ S.uncons f <#> \({ head, tail}) ->
49-
{ head, tail, updatePos: (_ `updatePos` head)}
50-
stripPrefix (Prefix p) s = pure $ S.stripPrefix (S.Pattern p) s <#> \rest ->
51-
{ rest, updatePos: (_ `updatePos` p)}
52+
instance stringStream :: (Applicative m) => Stream String m Char where
53+
uncons state = pure $ S.uncons state.input <#> \({ head, tail}) ->
54+
Tuple head state{input = tail, pos = updatePos state.pos head }
55+
stripPrefix (Prefix p) state = pure $ S.stripPrefix (S.Pattern p) state.input <#> \rest ->
56+
state{input = rest, pos = updatePos state.pos p}
5257

5358
instance listStream :: (Applicative m, Eq a, HasUpdatePosition a) => Stream (L.List a) m a where
54-
uncons f = pure $ L.uncons f <#> \({ head, tail}) ->
55-
{ head, tail, updatePos: (_ `updatePos` head)}
56-
stripPrefix (Prefix p) s = pure $ L.stripPrefix (L.Pattern p) s <#> \rest ->
57-
{ rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))}
59+
uncons state = pure $ L.uncons state.input <#> \({ head, tail}) ->
60+
Tuple head state{input = tail, pos = updatePos state.pos head }
61+
stripPrefix (Prefix p) state = pure $ L.stripPrefix (L.Pattern p) state.input <#> \rest ->
62+
state{input = rest, pos = foldl updatePos state.pos p}
5863

5964
instance lazyListStream :: (Applicative m, Eq a, HasUpdatePosition a) => Stream (LazyL.List a) m a where
60-
uncons f = pure $ LazyL.uncons f <#> \({ head, tail}) ->
61-
{ head, tail, updatePos: (_ `updatePos` head)}
62-
stripPrefix (Prefix p) s = pure $ LazyL.stripPrefix (LazyL.Pattern p) s <#> \rest ->
63-
{ rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))}
65+
uncons state = pure $ LazyL.uncons state.input <#> \({ head, tail}) ->
66+
Tuple head state{input = tail, pos = updatePos state.pos head }
67+
stripPrefix (Prefix p) state = pure $ LazyL.stripPrefix (LazyL.Pattern p) state.input <#> \rest ->
68+
state{input = rest, pos = foldl updatePos state.pos p}
6469

6570
-- | Match end of stream.
6671
eof :: forall s t m. Stream s m t => Monad m => ParserT s m Unit
6772
eof = do
68-
input <- gets \(ParseState input _ _) -> input
69-
(lift $ uncons input) >>= case _ of
73+
ParseState state <- get
74+
(lift $ uncons state) >>= case _ of
7075
Nothing -> pure unit
7176
_ -> fail "Expected EOF"
7277

7378
-- | Match the specified prefix.
7479
prefix :: forall f c m. Stream f m c => Show f => Monad m => f -> ParserT f m f
7580
prefix p = do
76-
input <- gets \(ParseState input _ _) -> input
77-
(lift $ stripPrefix (Prefix p) input) >>= case _ of
78-
Just {rest, updatePos} -> do
79-
modify \(ParseState _ position _) ->
80-
ParseState rest (updatePos position) true
81+
ParseState state <- get
82+
(lift $ stripPrefix (Prefix p) state) >>= case _ of
83+
Nothing -> fail $ "Expected " <> show p
84+
Just state -> do
85+
put $ ParseState state{consumed = true}
8186
pure p
82-
_ -> fail ("Expected " <> show p)
8387

8488
-- | Match any token.
8589
token :: forall s t m. Stream s m t => Monad m => ParserT s m t
8690
token = do
87-
input <- gets \(ParseState input _ _) -> input
88-
(lift $ uncons input) >>= case _ of
91+
ParseState state <- get
92+
(lift $ uncons state) >>= case _ of
8993
Nothing -> fail "Unexpected EOF"
90-
Just ({ head, updatePos, tail }) -> do
91-
modify \(ParseState _ position _) ->
92-
ParseState tail (updatePos position) true
94+
Just (Tuple head nextState) -> do
95+
put $ ParseState nextState{consumed = true}
9396
pure head
9497

9598
-- | Match a token satisfying the specified predicate.

0 commit comments

Comments
 (0)