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

parsley.internal.deepembedding.backend.SequenceEmbedding.scala Maven / Gradle / Ivy

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

import parsley.XAssert._

import parsley.internal.collection.mutable.DoublyLinkedList
import parsley.internal.deepembedding.ContOps, ContOps.{result, suspend, ContAdapter}
import parsley.internal.deepembedding.frontend
import parsley.internal.deepembedding.singletons._
import parsley.internal.machine.instructions

import StrictParsley.InstrBuffer

// Core Embedding
private [deepembedding] final class <*>[A, B](var left: StrictParsley[A => B], var right: StrictParsley[A]) extends StrictParsley[B] {
    def inlinable: Boolean = false
    // TODO: Refactor
    override def optimise: StrictParsley[B] = (left, right) match {
        // Fusion laws
        case (uf, Pure(x)) if (uf.isInstanceOf[Pure[_]] || uf.isInstanceOf[_ <*> _]) => uf match {
            // first position fusion
            case Pure(f) => new Pure(f(x))
            // second position fusion
            case Pure(f: Function1[t, A => B] @unchecked) <*> (uy/*: StrictParsley[t]*/) => // scalastyle:ignore disallow.space.before.token
                left = new Pure((y: t) => f(y)(x)).asInstanceOf[StrictParsley[A => B]]
                right = uy.asInstanceOf[StrictParsley[A]]
                this
            // third position fusion
            case Pure(f: Function1[t, Function1[u, A => B] @unchecked]) <*>
                 (uy/*: StrictParsley[t]*/) <*> // scalastyle:ignore disallow.space.before.token
                 (uz/*: StrictParsley[u]*/) => // scalastyle:ignore disallow.space.before.token
                left = <*>(new Pure((y: t) => (z: u) => f(y)(z)(x)), uy.asInstanceOf[StrictParsley[t]]).asInstanceOf[StrictParsley[A => B]]
                right = uz.asInstanceOf[StrictParsley[A]]
                this
            // interchange law: u <*> pure y == pure ($y) <*> u == ($y) <$> u (single instruction, so we benefit at code-gen)
            case _ =>
                left = new Pure((f: A => B) => f(x)).asInstanceOf[StrictParsley[A => B]]
                right = uf.asInstanceOf[StrictParsley[A]]
                this
        }
        // functor law: fmap f (fmap g p) == fmap (f . g) p where fmap f p = pure f <*> p from applicative
        case (Pure(f), Pure(g: Function[t, A] @unchecked) <*> (u/*: StrictParsley[t]*/)) => // scalastyle:ignore disallow.space.before.token
            left = new Pure(f.compose(g)).asInstanceOf[StrictParsley[A => B]]
            right = u.asInstanceOf[StrictParsley[A]]
            this
        // Fusion for special combinators
        case (Pure(f), ct@CharTok(c, x)) => new CharTok(c, f(x), ct.expected)
        case (Pure(f), ct@SupplementaryCharTok(c, x)) => new SupplementaryCharTok(c, f(x), ct.expected)
        case (Pure(f), ct@StringTok(c, x)) => new StringTok(c, f(x), ct.expected)
        // TODO: functor law with lift2!
        // right absorption law: mzero <*> p = mzero
        case (z: MZero, _) => z
        /* RE-ASSOCIATION LAWS */
        // re-association law 1: (q *> left) <*> right = q *> (left <*> right)
        case (q *> uf, ux) => *>(q, <*>(uf, ux).optimise)
        case (uf, seq: Seq[A] @unchecked) => seq match {
            // re-association law 2: left <*> (right <* q) = (left <*> right) <* q
            case ux <* v => <*(<*>(uf, ux).optimise, v).optimise
            // re-association law 3: p *> pure x = pure x <* p
            // consequence of re-association law 3: left <*> (q *> pure x) = (left <*> pure x) <* q
            case v *> (ux: Pure[_]) => <*(<*>(uf, ux).optimise, v).optimise
            case _ => this
        }
        // consequence of left zero law and monadic definition of <*>, preserving error properties of left
        case (u, z: MZero) => *>(u, z)
        case _ => this
    }
    override def codeGen[M[_, +_]: ContOps, R](producesResults: Boolean)(implicit instrs: InstrBuffer, state: CodeGenState): M[R, Unit] = left match {
        // pure f <*> p = f <$> p
        case Pure(f) =>
            suspend(right.codeGen[M, R](producesResults)) |> {
                if (producesResults) instrs += instructions.Lift1(f)
            }
        case _ =>
            suspend(left.codeGen[M, R](producesResults)) >>
            suspend(right.codeGen[M, R](producesResults)) |> {
                if (producesResults) instrs += instructions.Apply
            }
    }
    // $COVERAGE-OFF$
    final override def pretty: String = s"(${left.pretty} <*> ${right.pretty})"
    // $COVERAGE-ON$
}

private [deepembedding] final class >>=[A, B](val p: StrictParsley[A], private [>>=] val f: A => frontend.LazyParsley[B]) extends Unary[A, B] {
    override def optimise: StrictParsley[B] = p match {
        case z: MZero => z
        case _ => this
    }
    override def codeGen[M[_, +_]: ContOps, R](producesResults: Boolean)(implicit instrs: InstrBuffer, state: CodeGenState): M[R, Unit] = {
        suspend(p.codeGen[M, R](producesResults = true)) |> {
            instrs += instructions.DynCall[A] { x =>
                val p = f(x)
                // FIXME: suppress results within p, then can remove pop
                p.demandCalleeSave(state.numRegs)
                if (implicitly[ContOps[M]].isStackSafe) p.overflows()
                p.instrs
            }
            if (!producesResults) instrs += instructions.Pop
        }
    }
    // $COVERAGE-OFF$
    final override def pretty(p: String): String = s"$p.flatMap(?)"
    // $COVERAGE-ON$
}

private [deepembedding] final class Seq[A](private [backend] var before: DoublyLinkedList[StrictParsley[_]],
                                           private [backend] var res: StrictParsley[A],
                                           private [backend] var after: DoublyLinkedList[StrictParsley[_]]) extends StrictParsley[A] {
    def inlinable: Boolean = false

    private def mergeIntoRight(p: StrictParsley[A]): this.type = p match {
        case Seq(rs1, rr, rs2) =>
            before.stealAll(rs1)
            assume(!rr.isInstanceOf[Pure[_]] || rs2.isEmpty, "if rr is pure, then rs2 is empty, which retains normalisation")
            after = rs2
            res = rr
            this
        case _ => this
    }

    private def mergeFromRight(p: Seq[_], into: DoublyLinkedList[StrictParsley[_]]): this.type = {
        into.stealAll(p.before)
        Seq.whenNonPure(p.res, into.addOne(_))
        into.stealAll(p.after)
        this
    }

    private def chooseInto(p: StrictParsley[A]): DoublyLinkedList[StrictParsley[_]] = p match {
        case _: Pure[_] => before
        case _          => after
    }

    // TODO: Get behaves much like pure except for shifting positions
    // TODO: can this be optimised to reduce repeated matching?
    override def optimise: StrictParsley[A] = this match {
        // Assume that this is eliminated first, so not other before or afters
        case (_: Pure[_]) **> u => u
        case (p: MZero) **> _ => p
        case u <** (_: Pure[_]) => u
        case (p: MZero) <** _ => p
        case u@Seq(bs1, br, bs2) **> r =>
            bs2.lastOption match {
                case Some(_: MZero) => u
                case _ =>
                    before = bs1
                    Seq.whenNonPure(br, before.addOne(_))
                    before.stealAll(bs2)
                    mergeIntoRight(r)
            }
        case _ **> q => mergeIntoRight(q)
        case u@Seq(rs1, rr, rs2) <** p =>
            rs2.lastOption match {
                case Some(_: MZero) => u
                case _ =>
                    before = rs1
                    res = rr
                    assume(!rr.isInstanceOf[Pure[_]] || rs2.isEmpty, "rs2 is empty when rr is Pure")
                    assume(after.size == 1 && (after.head eq p), "after can only contain just p, which is going to get flattened, so it can be dropped")
                    after = rs2
                    val into = chooseInto(rr)
                    p match {
                        case p: Seq[_] => mergeFromRight(p, into)
                        case p =>
                            into.addOne(p)
                            this
                    }
            }
        case r <** (p: Seq[_]) =>
            assume(after.size == 1 && (after.head eq p), "after can only contain just p, which is going to get flattened, so it can be cleared")
            after.clear()
            mergeFromRight(p, chooseInto(r))
        // shift pure to the right by swapping before and after (before is empty linked list!)
        case (_: Pure[_]) <* _ =>
            assume(before.isEmpty, "empty can reuse before instead of allocating a new list because before is empty")
            val empty = before
            before = after
            after = empty
            this
        case _ => this
    }

    override def codeGen[M[_, +_]: ContOps, R](producesResults: Boolean)(implicit instrs: InstrBuffer, state: CodeGenState): M[R, Unit] = res match {
        case Pure(x) =>
            // peephole here involves CharTokFastPerform, StringTokFastPerform, and Exchange
            assume(after.isEmpty, "The pure in question is normalised to the end: if result is pure, after is empty.")
            assume(before.nonEmpty, "before cannot be empty because after is empty")
            val last = before.last
            before.initInPlace()
            suspend(Seq.codeGenMany[M, R](before.iterator)) >> {
                before.addOne(last) // undo the initInPlace, you'll thank me later
                last match {
                    case st@Satisfy(f) if producesResults => result(instrs += new instructions.SatisfyExchange(f, x, st.expected))
                    case _ =>
                        suspend(last.codeGen[M, R](producesResults = false)) |> {
                            if (producesResults) instrs += new instructions.Push(x)
                        }
                }
            }
        case _ =>
            suspend(Seq.codeGenMany[M, R](before.iterator)) >> {
                suspend(res.codeGen[M, R](producesResults)) >> {
                    suspend(Seq.codeGenMany(after.iterator))
                }
            }
    }
    // $COVERAGE-OFF$
    final override def pretty: String = (before.map(_.pretty) ++ (res.pretty :: after.map(_.pretty).toList)).mkString("seq(", ", ", ")")
    // $COVERAGE-ON$
}

private [backend] object Seq {
    def unapply[A](self: Seq[A]): Some[(DoublyLinkedList[StrictParsley[_]], StrictParsley[A], DoublyLinkedList[StrictParsley[_]])] = {
        Some((self.before, self.res, self.after))
    }

    private [Seq] def codeGenMany[M[_, +_]: ContOps, R](it: Iterator[StrictParsley[_]])
                                                       (implicit instrs: InstrBuffer, state: CodeGenState): M[R, Unit] = {
        if (it.hasNext) {
            suspend(it.next().codeGen[M, R](producesResults = false)) >> suspend(codeGenMany(it))
        } else result(())
    }

    private [Seq] def whenNonPure(p: StrictParsley[_], f: StrictParsley[_] => Unit): Unit = p match {
        case _: Pure[_] =>
        case p          => f(p)
    }
}

private [backend] object <*> {
    def apply[A, B](left: StrictParsley[A=>B], right: StrictParsley[A]): <*>[A, B] = new <*>[A, B](left, right)
    def unapply[A, B](self: <*>[A, B]): Some[(StrictParsley[A=>B], StrictParsley[A])] = Some((self.left, self.right))
}
private [deepembedding] object *> {
    def apply[A](left: StrictParsley[_], right: StrictParsley[A]): Seq[A] = {
        val before = DoublyLinkedList.empty[StrictParsley[_]]
        before.addOne(left)
        *>(before, right)
    }
    private [backend] def apply[A](before: DoublyLinkedList[StrictParsley[_]], res: StrictParsley[A]): Seq[A] = new Seq(before, res, DoublyLinkedList.empty)
    private [backend] def unapply[A](self: Seq[A]): Option[(DoublyLinkedList[StrictParsley[_]], StrictParsley[A])] = {
        if (self.after.isEmpty) Some((self.before, self.res))
        else None
    }
}
private [backend] object **> {
    private [backend] def unapply[A](self: Seq[A]): Option[(StrictParsley[_], StrictParsley[A])] = *>.unapply(self).collect {
        case (before, res) if self.before.size == 1 => (before.head, res)
    }
}

private [deepembedding]  object <* {
    def apply[A](left: StrictParsley[A], right: StrictParsley[_]): Seq[A] = {
        val after = DoublyLinkedList.empty[StrictParsley[_]]
        after.addOne(right)
        <*(left, after)
    }
    private [backend] def apply[A](res: StrictParsley[A], after: DoublyLinkedList[StrictParsley[_]]): Seq[A] = new Seq(DoublyLinkedList.empty, res, after)
    private [backend] def unapply[A](self: Seq[A]): Option[(StrictParsley[A], DoublyLinkedList[StrictParsley[_]])] = {
        if (self.before.isEmpty) Some((self.res, self.after))
        else None
    }
}

private [backend] object <** {
    private [backend] def unapply[A](self: Seq[A]): Option[(StrictParsley[A], StrictParsley[_])] = <*.unapply(self).collect {
        case (res, after) if after.size == 1 => (res, after.head)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy