{-| Module : Parsley.Register Description : The register combinators License : BSD-3-Clause Maintainer : Jamie Willis Stability : stable This module exposes combinators designed to work with /registers/. These are small pieces of state that are carried through the parsing process. They can be used, for example, to perform indentation sensitive parsing. In fact, they are a flexible replacement for the monadic combinators, in conjunction with the "Parsley.Selective" combinators. In particular, the `bind` combinator implements a limited form of the @(>>=)@ operation, where the structure of the resulting parser will still be statically determinable. Registers paired with "Parsley.Selective" combinators are Turing-Compete. @since 0.1.0.0 -} module Parsley.Register ( Reg, newRegister, get, put, newRegister_, put_, gets, gets_, modify, modify_, move, swap, local, local_, localModify, localModify_, bind, rollback, for ) where import Prelude hiding (pure, (<*>), (*>), (<*)) import Parsley.Alternative (empty, (<|>)) import Parsley.Applicative (pure, (<*>), (*>), (<*)) import Parsley.Internal (Parser, Reg) import Parsley.ParserOps (ParserOps) import Parsley.Selective (when, while) import qualified Parsley.Internal as Internal (newRegister, get, put) {-| Creates a new register initialised with the value obtained from parsing the first argument. This register is provided to the second argument, a function that generates a parser depending on operations derived from the register. This parser is then performed. Note: The rank-2 type here serves a similar purpose to that in the @ST@ monad. It prevents the register from leaking outside of the scope of the function, safely encapsulating the stateful effect of the register. @since 0.1.0.0 -} newRegister :: Parser a -- ^ Parser with which to initialise the register -> (forall r. Reg r a -> Parser b) -- ^ Used to generate the second parser to execute -> Parser b newRegister = Internal.newRegister {-| Fetches a value from a register and returns it as its result. @since 0.1.0.0 -} get :: Reg r a -> Parser a get = Internal.get {-| Puts the result of the given parser into the given register. The old value in the register will be lost. @since 0.1.0.0 -} put :: Reg r a -> Parser a -> Parser () put = Internal.put {-| Like `newRegister`, except the initial value of the register is seeded from a pure value as opposed to the result of a parser. @since 0.1.0.0 -} newRegister_ :: ParserOps rep => rep a -> (forall r. Reg r a -> Parser b) -> Parser b newRegister_ x = newRegister (pure x) {-| Like `put`, except the new value of the register is a pure value as opposed to the result of a parser. @since 0.1.0.0 -} put_ :: ParserOps rep => Reg r a -> rep a -> Parser () put_ r = put r . pure {-| @gets reg p@ first parses @p@ to get as a result, function @f@. Then, taking into account any changes made during @p@, the value is fetched from @reg@ and applied to @f@. @since 0.1.0.0 -} gets :: Reg r a -> Parser (a -> b) -> Parser b gets r p = p <*> get r {-| Like `gets`, except the adapter function is a pure argument as opposed to the result of a parser. @since 0.1.0.0 -} gets_ :: ParserOps rep => Reg r a -> rep (a -> b) -> Parser b gets_ r = gets r . pure {-| @modify reg p@ first parses @p@ to collect the function @f@, then taking into account any changes made during @f@, the value in @reg@ is modified using the function @f@ and put back into it. @since 0.1.0.0 -} modify :: Reg r a -> Parser (a -> a) -> Parser () modify r p = put r (gets r p) {-| Like `modify`, except the modification function is a pure argument as opposed to the result of a parser. @since 0.1.0.0 -} modify_ :: ParserOps rep => Reg r a -> rep (a -> a) -> Parser () modify_ r = modify r . pure {-| @move dst src@ takes the value stored in @src@ and additionally stores it into @dst@. @since 0.1.0.0 -} move :: Reg r1 a -> Reg r2 a -> Parser () move dst src = put dst (get src) {-| This combinator uses registers to emulate a restricted form of @(`>>=`)@: in a traditional monadic setting, this would be considered to be the implementation: > bind p f = p >>= f . pure Essentially, the result of @p@ is available to be summoned purely as many times as needed. However, it cannot be used to dynamically create structure: the selective combinators can be used to provide that functionality partially. @since 0.1.0.0 -} bind :: Parser a -> (Parser a -> Parser b) -> Parser b bind p f = newRegister p (f . get) {-| @local reg p q@ first parses @p@ and stores its value in @reg@ for the /duration/ of parsing @q@. If @q@ succeeds, @reg@ will be restored to its original state /before/ @p@ was parsed. @since 0.1.0.0 -} local :: Reg r a -> Parser a -> Parser b -> Parser b local r p q = bind (get r) $ \x -> put r p *> q <* put r x {-| @local_ reg x p@ stores @x@ in @reg@ for the /duration/ of parsing @p@. If @p@ succeeds, @reg@ will be restored to its original state. @since 1.0.2.0 -} local_ :: ParserOps rep => Reg r a -> rep a -> Parser b -> Parser b local_ r = local r . pure {-| @localModify reg p q@ first parses @p@ and @reg@ with its returned function for the /duration/ of parsing @q@. If @q@ succeeds, @reg@ will be restored to its original state /before/ @p@ was parsed. @since 1.0.2.0 -} localModify :: Reg r a -> Parser (a -> a) -> Parser b -> Parser b localModify r p q = bind (get r) $ \x -> modify r p *> q <* put r x {-| @localModify_ reg x p@ modifes @reg@ using @f@ for the /duration/ of parsing @p@. If @p@ succeeds, @reg@ will be restored to its original state. @since 1.0.2.0 -} localModify_ :: ParserOps rep => Reg r a -> rep (a -> a) -> Parser b -> Parser b localModify_ r = localModify r . pure {-| This combinator will swap the values contained in two registers. @since 0.1.0.0 -} swap :: Reg r1 a -> Reg r2 a -> Parser () swap r1 r2 = bind (get r1) $ \x -> move r1 r2 *> put r2 x {-| @rollback reg p@ will perform @p@ and if it fails without consuming input, @reg@ will be restored to its original state from /before/ @p@ was parsed, and the combinator will fail. If @p@ succeeds the state in @reg@ will not be restored to an old version. @since 0.1.0.0 -} rollback :: Reg r a -> Parser b -> Parser b rollback r p = bind (get r) $ \x -> p <|> put r x *> empty {-| This combinator is like a traditional imperative-style @for@-loop. Given @for init cond step body@, @init@ is first parsed to initialise a register called @i@; the parser @cond@ is then performed to check that the value in @i@ adheres to the predicate it returns; if so, then the @body@ is parsed, @step@ modifies the state in @i@, and then the process repeats from @cond@ again. When @cond@ returns @False@ for the predicate applied to @i@'s state, the loop terminates gracefully. If any component of this parser fails the loop will fail. @since 0.1.0.0 -} for :: Parser a -> Parser (a -> Bool) -> Parser (a -> a) -> Parser () -> Parser () for init cond step body = newRegister init $ \i -> let cond' :: Parser Bool cond' = gets i cond in when cond' (while (body *> modify i step *> cond'))