All Downloads are FREE. Search and download functionalities are using the official Maven repository.

parsley.state.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Parsley Contributors 
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package parsley

import scala.collection.Factory

import parsley.XAssert._
import parsley.combinator.{whenS, whileS}
import parsley.syntax.zipped.Zipped2
import parsley.exceptions.UnfilledReferenceException

import parsley.internal.deepembedding.{frontend, singletons}

import Parsley.{fresh, pure, empty}


/** This module contains all the functionality and operations for using and manipulating references.
  *
  * These often have a role in performing context-sensitive parsing tasks, where a Turing-powerful
  * system is required. While `flatMap` is capable of such parsing, it is much less efficient
  * than the use of references, though slightly more flexible. In particular, the `persist` combinator
  * enabled by `StateCombinators` can serve as a drop-in replacement for `flatMap` in many scenarios.
  *
  * @since 4.5.0
  *
  * @groupprio ref 0
  * @groupname ref References
  * @groupdesc ref
  *     The `Ref` type is used to describe pieces of state that are threaded through a parser.
  *     The creation and basic combinators of references are found within `Ref` and its companion
  *     object.
  *
  * @groupprio comb 5
  * @groupname comb Reference-Based Combinators
  * @groupdesc comb
  *     Some combinators are made much more efficient in the presence of references and they can
  *     be found here.
  *
  * @groupprio ext 10
  * @groupname ext Reference Extension Combinators
  * @groupdesc ext
  *     These are implicit classes that, when in scope, enable additional combinators on
  *     parsers that interact with the reference system in some way.
  */
object state {
    /** This class is used to index references within the mutable state.
      *
      * @note it is undefined behaviour to use a reference in multiple different
      *       independent parsers. You should be careful to parameterise the
      *       references in shared parsers and allocate fresh ones for each "top-level"
      *       parser you will run.
      * @group ref
      *
      * @groupprio getters 0
      * @groupname getters Getters
      * @groupdesc getters
      *   These combinators allow for the retrieval of the stateful value of a reference, and
      *   injecting it into the parsing context. Does not modify the contents of the reference
      *   itself.
      *
      * @groupprio setters 5
      * @groupname setters Setters
      * @groupdesc setters
      *   These combinators directly update the value contained within a reference. This new
      *   value can be provided directly or sourced from a parser.
      *
      * @groupprio mod 10
      * @groupname mod Modification
      * @groupdesc mod
      *   These combinators modify the value stored within a reference by using a function.
      *   The function used can be provided directly or sourced from a parser.
      *
      * @groupprio local 15
      * @groupname local Local Modification
      * @groupdesc local
      *   These combinators allow for some form of local stateful modification. This means
      *   that any changes to the reference may be reverted after the execution of the parser:
      *   this may be on the parsers success, but it could also involve the parsers failure.
      */
    class Ref[A] private [parsley] {
        /** This combinator injects the value stored in this reference into a parser.
          *
          * Allows for the value stored in this reference to be purely injected into
          * the parsing context. No input is consumed in this process, and it cannot fail.
          *
          * @example Get-Get Law: {{{
          * r.get *> r.get == r.get
          * r.get <~> r.get == r.get.map(x => (x, x))
          * }}}
          *
          * @return a parser that returns the value stored in this reference.
          * @since 3.2.0
          * @group getters
          */
        def get: Parsley[A] = new Parsley(new singletons.Get(this))
        /** This combinator injects the value stored in this reference into a parser after applying a function to it.
          *
          * Allows for the value stored in this reference to be purely injected into
          * the parsing context but the function `f` is applied first. No input is
          * consumed in this process, and it cannot fail.
          *
          * @param f the function used to transform the value in this reference.
          * @tparam B the desired result type.
          * @return the value stored in this reference applied to `f`.
          * @since 3.2.0
          * @group getters
          */
        def gets[B](f: A => B): Parsley[B] = this.gets(pure(f))
        /** This combinator injects the value stored in this reference into a parser after applying a function obtained from a parser to it.
          *
          * First, `pf` is parsed, producing the function `f` on success. Then,
          * the value stored in this reference `x` is applied to the function `f`.
          * The combinator returns `f(x)`. Only `pf` is allowed to consume input.
          * If `pf` fails, the combinator fails, otherwise it will succeed.
          *
          * @param pf the parser that produces the function used to transform the value in this reference.
          * @tparam B the desired result type.
          * @return the value stored in this reference applied to a function generated from `pf`.
          * @since 3.2.0
          * @group getters
          */
        def gets[B](pf: Parsley[A => B]): Parsley[B] = pf <*> this.get
        /** This combinator stores a new value into this reference.
          *
          * Without any other effect, the value `x` will be placed into this reference.
          *
          * @example Set-Get Law: {{{
          * r.set(x) *> r.get == r.set(x).as(x)
          * }}}
          *
          * @example Set-Set Law: {{{
          * r.set(x) *> r.set(y) == r.set(y)
          * }}}
          *
          * @param x the value to place in the reference.
          * @since 4.5.0
          * @group setters
          */
        def set(x: A): Parsley[Unit] = this.set(pure(x))
        /** This combinator stores a new value into this reference.
          *
          * First, parse `p` to obtain its result `x`. Then store `x` into
          * this reference without any further effect. If `p` fails this
          * combinator fails.
          *
          * @example Get-Set Law: {{{
          * r.set(r.get) == unit
          * }}}
          *
          * @example Set-Set Law: {{{
          * // only when `q` does not inspect the value of `r`!
          * r.set(p) *> r.set(q) == p *> r.set(q)
          * }}}
          *
          * @param p the parser that produces the value to store in the reference.
          * @since 4.5.0
          * @group setters
          */
        def set(p: Parsley[A]): Parsley[Unit] = new Parsley(new frontend.Put(this, p.internal))
        /** This combinator stores a new value into this reference.
          *
          * First, parse `p` to obtain its result `x`. Then store `f(x)` into
          * this reference without any further effect. If `p` fails this
          * combinator fails.
          *
          * Equivalent to {{{
          * this.set(p.map(f))
          * }}}
          *
          * @param p the parser that produces the value to store in the reference.
          * @param f a function which adapts the result of `p` so that it can fit into this reference.
          * @since 4.5.0
          * @group setters
          */
        def sets[B](p: Parsley[B], f: B => A): Parsley[Unit] = this.set(p.map(f))
        /** This combinator modifies the value stored in this reference with a function.
          *
          * Without any other effect, get the value stored in this reference, `x`, and
          * put back `f(x)`.
          *
          * Equivalent to {{{
          * this.set(this.gets(f))
          * }}}
          *
          * @param f the function used to modify this reference's value.
          * @since 4.5.0
          * @group mod
          */
        def update(f: A => A): Parsley[Unit] = new Parsley(new singletons.Modify(this, f))
        /** This combinator modifies the value stored in this reference with a function.
          *
          * First, parse `pf` to obtain its result `f`. Then get the value stored in
          * this reference, `x`, and put back `f(x)`. If `p` fails this combinator fails.
          *
          * Equivalent to {{{
          * this.set(this.gets(pf))
          * }}}
          *
          * @param pf  the parser that produces the function used to transform the value in this reference.
          * @since 4.5.0
          * @group mod
          */
        def update(pf: Parsley[A => A]): Parsley[Unit] = this.set(this.gets(pf))
        /** This combinator changed the value stored in this reference for the duration of a given parser, resetting it afterwards.
          *
          * First get the current value in this reference `x,,old,,`, then place `x` into this reference
          * without any further effect. Then, parse `p`, producing result `y` on success. Finally,
          * put `x,,old,,` back into this reference and return `y`. If `p` fails, the whole combinator fails and
          * the state is '''not restored'''.
          *
          * @example Set-Set Law: {{{
          * r.set(x) *> r.setDuring(y)(p) == r.set(y) *> p <* r.set(x)
          * }}}
          *
          * @param x the value to place into this reference.
          * @param p the parser to execute with the adjusted state.
          * @return the parser that performs `p` with the modified state `x`.
          * @since 4.5.0
          * @group local
          */
        def setDuring[B](x: A)(p: Parsley[B]): Parsley[B] = this.setDuring(pure(x))(p)
        /** This combinator changed the value stored in this reference for the duration of a given parser, resetting it afterwards.
          *
          * First get the current value in this reference `x,,old,,`, then parse `p` to get the result `x`, placing it into this reference
          * without any further effect. Then, parse `q`, producing result `y` on success. Finally,
          * put `x,,old,,` back into this reference and return `y`. If `p` or `q` fail, the whole combinator fails and
          * the state is '''not restored'''.
          *
          * @param p the parser whose return value is placed in this reference.
          * @param q the parser to execute with the adjusted state.
          * @return the parser that performs `q` with the modified state.
          * @since 4.5.0
          * @group local
          */
        def setDuring[B](p: Parsley[A])(q: =>Parsley[B]): Parsley[B] = new Parsley(new frontend.Local(this, p.internal, q.internal))
        /** This combinator changed the value stored in this reference for the duration of a given parser, resetting it afterwards.
          *
          * First get the current value in this reference `x,,old,,`, then place `f(x,,old,,)` into this reference
          * without any further effect. Then, parse `p`, producing result `y` on success. Finally,
          * put `x,,old,,` back into this reference and return `y`. If `p` fails, the whole combinator fails and
          * the state is '''not restored'''.
          *
          * @example Set-Set Law and Set-Get Law: {{{
          * r.set(x) *> r.updateDuring(f)(p) == r.set(f(x)) *> p <* r.set(x)
          * }}}
          *
          * @param f the function used to modify the value in this reference.
          * @param p the parser to execute with the adjusted state.
          * @return the parser that performs `p` with the modified state.
          * @since 4.5.0
          * @group local
          */
        def updateDuring[B](f: A => A)(p: Parsley[B]): Parsley[B] = this.setDuring(this.gets(f))(p)
        /** This combinator rolls-back any changes to this reference made by a given parser if it fails.
          *
          * First get the current value in this reference `x,,old,,`. Then parse `p`, if it succeeds,
          * producing `y`, then `y` is returned and this reference retains its value post-`p`. Otherwise,
          * if `p` failed '''without consuming input''', `x,,old,,` is placed back into this reference
          * and this combinator fails.
          *
          * This can be used in conjunction with local to make an ''almost'' unconditional state restore: {{{
          * // `r`'s state is always rolled back after `p` unless it fails having consumed input.
          * r.rollback(r.local(x)(p))
          * }}}
          *
          * @param p the parser to perform.
          * @return the result of the parser `p`, if any.
          * @since 3.2.0
          * @group local
          */
        def rollback[B](p: Parsley[B]): Parsley[B] = this.get.persist { x =>
            p <|> (this.set(x) *> empty)
        }

        private [this] var _v: Int = -1
        private [parsley] def addr: Int = {
            if (!allocated) throw new UnfilledReferenceException // scalastyle:ignore throw
            _v
        }
        private [parsley] def allocated: Boolean = _v != -1
        private [parsley] def allocate(v: Int): Unit = {
            assert(!allocated)
            this._v = v
        }
        private [parsley] def deallocate(): Unit = {
            assert((new Throwable).getStackTrace.exists(_.getClassName == "parsley.internal.machine.instructions.CalleeSave"))
            _v = -1
        }
        //override def toString: String = s"Reg(${if (allocated) addr else "unallocated"})"
    }

    /** This object allows for the construction of a reference via its `make` function.
      * @group ref
      */
    object Ref {
        /** This function creates a new (global) reference of a given type.
          *
          * The reference created by this function is not allocated to any specific parser until it has been
          * used by a parser. It should not be used with multiple different parsers.
          *
          * @tparam A the type to be contained in this reference during runtime
          * @return a new reference which can contain the given type.
          * @note references created in this manner ''must'' be initialised in the top-level parser and not
          *       inside a `flatMap`, as this may make them corrupt other references. They should be used with
          *       caution. It is recommended to use `makeRef` and `fillRef` where possible.
          * @since 2.2.0
          */
        def make[A]: Ref[A] = new Ref
    }

    /** This class, when in scope, enables the use of combinators directly on parsers
      * that interact with the reference system to store and persist results so they
      * can be used multiple times.
      *
      * @constructor This constructor should not be called manually, it is designed to be used via Scala's implicit resolution.
      * @param p the value that this class is enabling methods on.
      * @param con a conversion that allows values convertible to parsers to be used.
      * @group ext
      */
    implicit final class StateCombinators[P, A](p: P)(implicit con: P => Parsley[A]) {
        /** This combinator fills a fresh reference with the result of this parser, this
          * reference is provided to the given function, which continues the parse.
          *
          * This allows for a more controlled way of creating references during a parse,
          * without explicitly creating them with `Ref.make[A]` and using `put`. These
          * references are intended to be fresh every time they are "created", in other
          * words, a recursive call with a `fillReg` call inside will modify a different
          * reference each time.
          *
          * @example {{{
          * // this is an efficient implementation for persist.
          * def persist[B](f: Parsley[A] => Parsley[B]): Parsley[B] = this.fillRef(reg => f(ref.get))
          * }}}
          *
          * @param body a function to generate a parser that can interact with the freshly created reference.
          * @since 4.0.0
          */
        def fillRef[B](body: Ref[A] => Parsley[B]): Parsley[B] = {
            val ref = Ref.make[A]
            new Parsley(new frontend.NewReg(ref, con(p).internal, body(ref).internal))
        }
        /** This combinator allows for the result of this parser to be used multiple times within a function,
          * without needing to reparse or recompute.
          *
          * Similar to `flatMap`, except it is much cheaper to do, at the cost of the restriction that the argument is `Parsley[A]` and not just `A`.
          *
          * @example {{{
          * // this is a reasonable implementation, though direct use of `branch` may be more efficent.
          * def filter(pred: A => Boolean): Parsley[A] = {
          *     this.persist(px => ifS(px.map(pred), px, empty))
          * }
          * }}}
          *
          * @param f a function to generate a new parser that can observe the result of this parser many times without reparsing.
          * @since 3.2.0
          */
        def persist[B](f: Parsley[A] => Parsley[B]): Parsley[B] = this.fillRef(ref => f(ref.get))
    }

    /** This class, when in scope, enables a method to create and fill a reference with a
      * given value.
      *
      * @constructor This constructor should not be called manually, it is designed to be used via Scala's implicit resolution.
      * @param x the value to initialise a reference with.
      * @group ext
      */
    implicit final class RefMaker[A](val x: A) extends AnyVal {
        /** This combinator fills a fresh reference with the this value.
          *
          * This allows for a more controlled way of creating references during a parse,
          * without explicitly creating them with `Ref.make[A]` and using `set`. These
          * references are intended to be fresh every time they are "created", in other
          * words, a recursive call with a `makeRef` call inside will modify a different
          * reference.
          *
          * @param body a function to generate a parser that can interact with the freshly created reference.
          * @see [[parsley.state.StateCombinators.fillRef `fillRef`]] for a version that uses the result of a parser to fill the reference instead.
          * @since 4.0.0
          */
        def makeRef[B](body: Ref[A] => Parsley[B]): Parsley[B] = pure(x).fillRef(body)
    }

    /** This combinator allows for the repeated execution of a parser `body` in a stateful loop, `body` will have access to the current value of the state.
      *
      * `forP_(init, cond, step)(body)` behaves much like a traditional for loop using `init`, `cond`, `step` and `body` as parsers
      * which control the loop itself. First, a reference `r` is created and initialised with `init`. Then `cond` is parsed, producing
      * the function `pred`. If `r.gets(pred)` returns true, then `body` is parsed, then `r` is modified with the result of parsing `step`.
      * This repeats until `r.gets(pred)` returns false. This is useful for performing certain context sensitive tasks.
      *
      * @example the classic context sensitive grammar of `a^n^b^n^c^n^` can be matched using `forP_`:
      * {{{
      * val r = Ref.make[Int]
      *
      * r.set(0) *>
      * many('a' *> r.update(_+1)) *>
      * forP_[Int](r.get, pure(_ != 0), pure(_ - 1)){_ => 'b'} *>
      * forP_[Int](r.get, pure(_ != 0), pure(_ - 1)){_ => 'c'}
      * }}}
      *
      * @param init the initial value of the induction variable.
      * @param cond the condition by which the loop terminates.
      * @param step the change in induction variable on each iteration.
      * @param body the body of the loop performed each iteration, which has access to the current value of the state.
      * @return a parser that initialises some state with `init` and then parses body until `cond` is true, modifying the state each iteration with `step`.
      * @see [[parsley.state.forYieldP_ `forYieldP_`]] for a version that returns the results of each `body` parse.
      * @group comb
      */
    def forP_[A](init: Parsley[A], cond: =>Parsley[A => Boolean], step: =>Parsley[A => A])(body: Parsley[A] => Parsley[_]): Parsley[Unit] = {
        init.fillRef { ref =>
          lazy val _cond = ref.gets(cond)
          lazy val _step = ref.update(step)
          whenS(_cond) {
            whileS(body(ref.get) *> _step *> _cond)
          }
        }
    }

    /** This combinator allows for the repeated execution of a parser `body` in a stateful loop, `body` will have access to the current value of the state.
      *
      * `forP_(init, cond, step)(body)` behaves much like a traditional for comprehension using `init`, `cond`, `step` and `body` as parsers
      * which control the loop itself. First, a reference `r` is created and initialised with `init`. Then `cond` is parsed, producing
      * the function `pred`. If `r.gets(pred)` returns true, then `body` is parsed, then `r` is modified with the result of parsing `step`.
      * This repeats until `r.gets(pred)` returns false. This is useful for performing certain context sensitive tasks. Unlike `forP_` the
      * results of the body invokations are returned in a list.
      *
      * @example the classic context sensitive grammar of `a^n^b^n^c^n^` can be matched using `forP_`:
      * {{{
      * val r = Ref.make[Int]
      *
      * r.set(0) *>
      * many('a' *> r.update(_+1)) *>
      * forYieldP_[Int, Char](r.get, pure(_ != 0), pure(_ - 1)){_ => 'b'} *>
      * forYieldP_[Int, Char](r.get, pure(_ != 0), pure(_ - 1)){_ => 'c'}
      * }}}
      *
      * This will return a list `n` `'c'` characters.
      *
      * @param init the initial value of the induction variable.
      * @param cond the condition by which the loop terminates.
      * @param step the change in induction variable on each iteration.
      * @param body the body of the loop performed each iteration, which has access to the current value of the state.
      * @return a parser that initialises some state with `init` and then parses body until `cond` is true, modifying the state each iteration with `step`.
      * @see [[parsley.state.forP_ `forP_`]] for a version that ignores the results of the body.
      * @group comb
      */
    def forYieldP_[A, B](init: Parsley[A], cond: =>Parsley[A => Boolean], step: =>Parsley[A => A])(body: Parsley[A] => Parsley[B]): Parsley[List[B]] = {
        forYieldPGen_(init, cond, step)(body, List)
    }
    private [parsley] def forYieldPGen_[A, B, C](init: Parsley[A], cond: =>Parsley[A => Boolean], step: =>Parsley[A => A])
                                                (body: Parsley[A] => Parsley[B], factory: Factory[B, C]): Parsley[C] = {
        fresh(factory.newBuilder).persist { acc =>
            forP_(init, cond, step) { x =>
                (acc, body(x)).zipped(_ += _).impure // we don't want this optimised out, it's a mutable operation in a resultless context
            } ~> acc.map(_.result())
        }
    }

    /** This combinator allows for the repeated execution of a parser in a stateful loop.
      *
      * `forP(init, cond, step)(body)` behaves much like a traditional for loop using `init`, `cond`, `step` and `body` as parsers
      * which control the loop itself. First, a reference `r` is created and initialised with `init`. Then `cond` is parsed, producing
      * the function `pred`. If `r.gets(pred)` returns true, then `body` is parsed, then `r` is modified with the result of parsing `step`.
      * This repeats until `r.gets(pred)` returns false. This is useful for performing certain context sensitive tasks.
      *
      * @example the classic context sensitive grammar of `a^n^b^n^c^n^` can be matched using `forP`:
      * {{{
      * val r = Ref.make[Int]
      *
      * r.set(0) *>
      * many('a' *> r.update(_+1)) *>
      * forP[Int](r.get, pure(_ != 0), pure(_ - 1)){'b'} *>
      * forP[Int](r.get, pure(_ != 0), pure(_ - 1)){'c'}
      * }}}
      *
      * @param init the initial value of the induction variable.
      * @param cond the condition by which the loop terminates.
      * @param step the change in induction variable on each iteration.
      * @param body the body of the loop performed each iteration.
      * @return a parser that initialises some state with `init` and then parses body until `cond` is true, modifying the state each iteration with `step`.
      * @see [[parsley.state.forYieldP `forYieldP`]] for a version that returns the results of each `body` parse.
      * @group comb
      */
    def forP[A](init: Parsley[A], cond: =>Parsley[A => Boolean], step: =>Parsley[A => A])(body: =>Parsley[_]): Parsley[Unit] = {
        lazy val _body = body
        forP_(init, cond, step) { _ =>
            _body
        }
    }

    /** This combinator allows for the repeated execution of a parser in a stateful loop.
      *
      * `forYieldP(init, cond, step)(body)` behaves much like a traditional for comprehension using `init`, `cond`, `step` and `body` as parsers
      * which control the loop itself. First, a reference `r` is created and initialised with `init`. Then `cond` is parsed, producing
      * the function `pred`. If `r.gets(pred)` returns true, then `body` is parsed, then `r` is modified with the result of parsing `step`.
      * This repeats until `r.gets(pred)` returns false. This is useful for performing certain context sensitive tasks. Unlike `forP` the
      * results of the body invokations are returned in a list.
      *
      * @example the classic context sensitive grammar of `a^n^b^n^c^n^` can be matched using `forP`:
      * {{{
      * val r = Ref.make[Int]
      *
      * r.set(0) *>
      * many('a' *> r.update(_+1)) *>
      * forYieldP[Int, Char](r.get, pure(_ != 0), pure(_ - 1)){'b'} *>
      * forYieldP[Int, Char](r.get, pure(_ != 0), pure(_ - 1)){'c'}
      * }}}
      *
      * This will return a list `n` `'c'` characters.
      *
      * @param init the initial value of the induction variable.
      * @param cond the condition by which the loop terminates.
      * @param step the change in induction variable on each iteration.
      * @param body the body of the loop performed each iteration.
      * @return a parser that initialises some state with `init` and then parses body until `cond` is true, modifying the state each iteration with `step`.
      *         The results of the iterations are returned in a list.
      * @see [[parsley.state.forP `forP`]] for a version that ignores the results.
      * @group comb
      */
    def forYieldP[A, B](init: Parsley[A], cond: =>Parsley[A => Boolean], step: =>Parsley[A => A])(body: =>Parsley[B]): Parsley[List[B]] = {
        forYieldPGen(init, cond, step)(body, List)
    }
    private [parsley] def forYieldPGen[A, B, C](init: Parsley[A], cond: =>Parsley[A => Boolean], step: =>Parsley[A => A])
                                               (body: =>Parsley[B], factory: Factory[B, C]): Parsley[C] = {
        forYieldPGen_(init, cond, step)(_ => body, factory)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy