Skip to content

Commit c6834c1

Browse files
authored
Fully thread-safe AST (#58)
* Added some infrastructure for tracking subroutines with labels * Added label tracking to letFinder * More work done, but now nested subroutines don't factor out * Fixed the bugs, we're passing tests now, but work is being duplicated, and that needs fixing * Fixed for 2.12.12 * Fixed explosion problem * Cleaned up the shop a bit, next step, removing the synchronized and using laziness * Removed redundant information, mutable subroutines are actually ok, because they are past letFinding * Removed superfluous synchronisation in favour of lazy vals, our work here is done * Removed superfluous import
1 parent 66834b2 commit c6834c1

File tree

7 files changed

+148
-102
lines changed

7 files changed

+148
-102
lines changed

src/main/scala-2.12/parsley/XCompat.scala

+8-8
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import scala.collection.mutable
44
import scala.language.higherKinds
55

66
private [parsley] object XCompat {
7-
def isIdentityWrap[A, B](f: A => B): Boolean = f eq $conforms[A]
7+
def isIdentityWrap[A, B](f: A => B): Boolean = f eq $conforms[A]
88

9-
def refl[A]: A =:= A = implicitly[A =:= A]
9+
def refl[A]: A =:= A = implicitly[A =:= A]
1010

11-
implicit class Subtitution[A, B](ev: A =:= B) {
12-
def substituteCo[F[_]](fa: F[A]): F[B] = fa.asInstanceOf[F[B]]
13-
}
11+
implicit class Subtitution[A, B](ev: A =:= B) {
12+
def substituteCo[F[_]](fa: F[A]): F[B] = fa.asInstanceOf[F[B]]
13+
}
1414

15-
implicit class MapValuesInPlace[K, V](m: mutable.Map[K, V]) {
16-
def mapValuesInPlace(f: (K, V) => V): mutable.Map[K, V] = m.transform(f)
17-
}
15+
implicit class MapValuesInPlace[K, V](m: mutable.Map[K, V]) {
16+
def mapValuesInPlace(f: (K, V) => V): mutable.Map[K, V] = m.transform(f)
17+
}
1818
}

src/main/scala/parsley/expr/Fixity.scala

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package parsley.expr
22

3+
import scala.language.higherKinds
4+
35
/**
46
* Denotes the fixity and associativity of an operator. Importantly, it also specifies the type of the
57
* of the operations themselves. For non-monolithic structures this is described by `fixity.GOp` and

src/main/scala/parsley/internal/deepembedding/GeneralisedEmbedding.scala

+33-41
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import scala.language.higherKinds
77

88
// Core Embedding
99
private [parsley] abstract class Singleton[A](pretty: String, instr: =>instructions.Instr) extends Parsley[A] {
10+
final override def findLetsAux[Cont[_, +_]: ContOps]
11+
(implicit seen: Set[Parsley[_]], state: LetFinderState, label: UnsafeOption[String]): Cont[Unit, Unit] = result(())
1012
final override def preprocess[Cont[_, +_]: ContOps, A_ >: A](implicit seen: Set[Parsley[_]], sub: SubMap,
1113
label: UnsafeOption[String]): Cont[Unit, Parsley[A_]] = result(this)
12-
final override def findLetsAux[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState): Cont[Unit, Unit] = result(())
1314
final override def codeGen[Cont[_, +_]: ContOps](implicit instrs: InstrBuffer, state: CodeGenState): Cont[Unit, Unit] = {
1415
result(instrs += instr)
1516
}
@@ -20,12 +21,13 @@ private [parsley] abstract class Singleton[A](pretty: String, instr: =>instructi
2021

2122
private [deepembedding] abstract class SingletonExpect[A](pretty: String, builder: UnsafeOption[String] => SingletonExpect[A], instr: instructions.Instr)
2223
extends Parsley[A] {
24+
final override def findLetsAux[Cont[_, +_]: ContOps]
25+
(implicit seen: Set[Parsley[_]], state: LetFinderState, label: UnsafeOption[String]): Cont[Unit, Unit] = result(())
2326
final override def preprocess[Cont[_, +_]: ContOps, A_ >: A](implicit seen: Set[Parsley[_]], sub: SubMap,
24-
label: UnsafeOption[String]): Cont[Unit, Parsley[A]] = {
27+
label: UnsafeOption[String]): Cont[Unit, Parsley[A]] = {
2528
if (label == null) result(this)
2629
else result(builder(label))
2730
}
28-
final override def findLetsAux[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState): Cont[Unit, Unit] = result(())
2931
final override def codeGen[Cont[_, +_]: ContOps](implicit instrs: InstrBuffer, state: CodeGenState): Cont[Unit, Unit] = {
3032
result(instrs += instr)
3133
}
@@ -34,22 +36,17 @@ private [deepembedding] abstract class SingletonExpect[A](pretty: String, builde
3436
// $COVERAGE-ON$
3537
}
3638

37-
private [deepembedding] abstract class Unary[A, B](_p: =>Parsley[A])(pretty: String => String, empty: String => Unary[A, B]) extends Parsley[B] {
39+
private [deepembedding] abstract class Unary[A, B](__p: =>Parsley[A])(pretty: String => String, empty: String => Unary[A, B]) extends Parsley[B] {
40+
private lazy val _p = __p
3841
private [deepembedding] var p: Parsley[A] = _
3942
protected val childRepeats: Int = 1
4043
protected val numInstrs: Int
41-
final override def findLetsAux[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState): Cont[Unit,Unit] = this.synchronized {
42-
processed = false
43-
p = _p
44-
p.findLets
45-
}
44+
final override def findLetsAux[Cont[_, +_]: ContOps]
45+
(implicit seen: Set[Parsley[_]], state: LetFinderState, label: UnsafeOption[String]): Cont[Unit,Unit] = _p.findLets
4646
override def preprocess[Cont[_, +_]: ContOps, B_ >: B](implicit seen: Set[Parsley[_]], sub: SubMap,
47-
label: UnsafeOption[String]): Cont[Unit, Parsley[B_]] =
48-
if (label == null && processed) result(this) else for (p <- this.p.optimised) yield {
49-
val self = /*if (label == null) this else*/ empty(label)
50-
self.ready(p)
51-
}
52-
private [deepembedding] def ready(p: Parsley[A]): this.type = this.synchronized {
47+
label: UnsafeOption[String]): Cont[Unit, Parsley[B_]] =
48+
for (p <- _p.optimised) yield empty(label).ready(p)
49+
private [deepembedding] def ready(p: Parsley[A]): this.type = {
5350
processed = true
5451
this.p = p
5552
size = p.size + numInstrs
@@ -60,26 +57,23 @@ private [deepembedding] abstract class Unary[A, B](_p: =>Parsley[A])(pretty: Str
6057
// $COVERAGE-ON$
6158
}
6259

63-
private [deepembedding] abstract class Binary[A, B, C](_left: =>Parsley[A], _right: =>Parsley[B])(pretty: (String, String) => String, empty: =>Binary[A, B, C])
64-
extends Parsley[C] {
60+
private [deepembedding] abstract class Binary[A, B, C](__left: =>Parsley[A], __right: =>Parsley[B])
61+
(pretty: (String, String) => String, empty: =>Binary[A, B, C]) extends Parsley[C] {
62+
private lazy val _left = __left
63+
private lazy val _right = __right
6564
private [deepembedding] var left: Parsley[A] = _
6665
private [deepembedding] var right: Parsley[B] = _
6766
protected val numInstrs: Int
6867
protected val leftRepeats: Int = 1
6968
protected val rightRepeats: Int = 1
70-
final override def findLetsAux[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState): Cont[Unit,Unit] = this.synchronized {
71-
processed = false
72-
left = _left
73-
right = _right
74-
left.findLets >> right.findLets
75-
}
69+
final override def findLetsAux[Cont[_, +_]: ContOps]
70+
(implicit seen: Set[Parsley[_]], state: LetFinderState, label: UnsafeOption[String]): Cont[Unit,Unit] = _left.findLets >> _right.findLets
7671
final override def preprocess[Cont[_, +_]: ContOps, C_ >: C](implicit seen: Set[Parsley[_]], sub: SubMap,
7772
label: UnsafeOption[String]): Cont[Unit, Parsley[C_]] =
78-
if (label == null && processed) result(this) else for (left <- this.left.optimised; right <- this.right.optimised) yield {
79-
val self = /*if (label == null) this else*/ empty
80-
self.ready(left, right)
73+
for (left <- _left.optimised; right <- _right.optimised) yield {
74+
empty.ready(left, right)
8175
}
82-
private [deepembedding] def ready(left: Parsley[A], right: Parsley[B]): this.type = this.synchronized {
76+
private [deepembedding] def ready(left: Parsley[A], right: Parsley[B]): this.type = {
8377
processed = true
8478
this.left = left
8579
this.right = right
@@ -93,34 +87,32 @@ private [deepembedding] abstract class Binary[A, B, C](_left: =>Parsley[A], _rig
9387
// $COVERAGE-ON$
9488
}
9589

96-
private [deepembedding] abstract class Ternary[A, B, C, D](_first: =>Parsley[A], _second: =>Parsley[B], _third: =>Parsley[C])
90+
private [deepembedding] abstract class Ternary[A, B, C, D](__first: =>Parsley[A], __second: =>Parsley[B], __third: =>Parsley[C])
9791
(pretty: (String, String, String) => String, empty: =>Ternary[A, B, C, D]) extends Parsley[D] {
92+
private lazy val _first: Parsley[A] = __first
93+
private lazy val _second: Parsley[B] = __second
94+
private lazy val _third: Parsley[C] = __third
9895
private [deepembedding] var first: Parsley[A] = _
9996
private [deepembedding] var second: Parsley[B] = _
10097
private [deepembedding] var third: Parsley[C] = _
10198
protected val numInstrs: Int
99+
final override def findLetsAux[Cont[_, +_]: ContOps]
100+
(implicit seen: Set[Parsley[_]], state: LetFinderState, label: UnsafeOption[String]): Cont[Unit, Unit] = {
101+
_first.findLets >> _second.findLets >> _third.findLets
102+
}
102103
final override def preprocess[Cont[_, +_]: ContOps, D_ >: D](implicit seen: Set[Parsley[_]], sub: SubMap,
103104
label: UnsafeOption[String]): Cont[Unit, Parsley[D_]] =
104-
if (label == null && processed) result(this) else
105-
for (first <- this.first.optimised; second <- this.second.optimised; third <- this.third.optimised) yield {
106-
val self = /*if (label == null) this else*/ empty
107-
self.ready(first, second, third)
108-
}
109-
private [deepembedding] def ready(first: Parsley[A], second: Parsley[B], third: Parsley[C]): this.type = this.synchronized {
105+
for (first <- _first.optimised; second <- _second.optimised; third <- _third.optimised) yield {
106+
empty.ready(first, second, third)
107+
}
108+
private [deepembedding] def ready(first: Parsley[A], second: Parsley[B], third: Parsley[C]): this.type = {
110109
processed = true
111110
this.first = first
112111
this.second = second
113112
this.third = third
114113
size = first.size + second.size + third.size + numInstrs
115114
this
116115
}
117-
final override def findLetsAux[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState): Cont[Unit, Unit] = this.synchronized {
118-
processed = false
119-
first = _first
120-
second = _second
121-
third = _third
122-
first.findLets >> second.findLets >> third.findLets
123-
}
124116
// $COVERAGE-OFF$
125117
final override def prettyASTAux[Cont[_, +_]: ContOps]: Cont[String, String] =
126118
for (f <- first.prettyASTAux; s <- second.prettyASTAux; t <- third.prettyASTAux) yield pretty(f, s, t)

src/main/scala/parsley/internal/deepembedding/Parsley.scala

+35-33
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,33 @@ private [parsley] abstract class Parsley[+A] private [deepembedding]
3939
}
4040

4141
// Internals
42-
final private [deepembedding] def findLets[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState): Cont[Unit, Unit] = {
43-
state.addPred(this)
42+
final private [deepembedding] def findLets[Cont[_, +_]: ContOps]
43+
(implicit seen: Set[Parsley[_]], state: LetFinderState, label: UnsafeOption[String]): Cont[Unit, Unit] = {
44+
state.addPred(this, label)
4445
if (seen(this)) result(state.addRec(this))
45-
else if (state.notProcessedBefore(this)) {
46+
else if (state.notProcessedBefore(this, label)) {
4647
this match {
4748
case self: UsesRegister => state.addReg(self.reg)
4849
case _ =>
4950
}
50-
findLetsAux(implicitly[ContOps[Cont]], seen + this, state)
51+
findLetsAux(implicitly[ContOps[Cont]], seen + this, state, label)
5152
}
5253
else result(())
5354
}
5455
final private def fix(implicit seen: Set[Parsley[_]], sub: SubMap, label: UnsafeOption[String]): Parsley[A] = {
5556
// We use the seen set here to prevent cascading sub-routines
5657
val wasSeen = seen(this)
57-
val self = sub(this)
58+
val self = sub(label, this)
5859
if (wasSeen && (self eq this)) new Rec(this, label)
5960
else if (wasSeen) this
6061
else self
6162
}
6263
final private [deepembedding] def optimised[Cont[_, +_]: ContOps, A_ >: A](implicit seen: Set[Parsley[_]],
6364
sub: SubMap,
64-
label: UnsafeOption[String] = null): Cont[Unit, Parsley[A_]] = {
65-
for (p <- this.fix.preprocess(implicitly[ContOps[Cont]], seen + this, sub, label)) yield p.optimise
65+
label: UnsafeOption[String]): Cont[Unit, Parsley[A_]] = {
66+
val fixed = this.fix
67+
if (fixed.processed) result(fixed.optimise)
68+
else for (p <- fixed.preprocess(implicitly[ContOps[Cont]], seen + this, sub, label)) yield p.optimise
6669
}
6770
final private [deepembedding] var safe = true
6871
final private var cps = false
@@ -87,20 +90,21 @@ private [parsley] abstract class Parsley[+A] private [deepembedding]
8790

8891
final private def pipeline[Cont[_, +_]: ContOps](implicit instrs: InstrBuffer, state: CodeGenState): Unit = {
8992
perform {
90-
implicit val letFinderState: LetFinderState = new LetFinderState
9193
implicit val seenSet: Set[Parsley[_]] = Set.empty
94+
implicit val label: UnsafeOption[String] = null
95+
implicit val letFinderState: LetFinderState = new LetFinderState
96+
implicit lazy val subMap: SubMap = new SubMap(letFinderState.lets)
9297
findLets >> {
93-
implicit val subMap: SubMap = new SubMap(letFinderState.lets)
9498
optimised.flatMap(p => generateCalleeSave(p.codeGen, allocateRegisters(letFinderState.usedRegs)))
9599
}
96100
}
97101
if (state.subsExist) {
98102
val end = state.freshLabel()
99103
instrs += new instructions.Jump(end)
100104
while (state.more) {
101-
val p = state.nextSub()
102-
instrs += new instructions.Label(state.getSubLabel(p))
103-
perform(p.codeGen)
105+
val sub = state.nextSub()
106+
instrs += new instructions.Label(state.getSubLabel(sub))
107+
perform(sub.p.codeGen)
104108
instrs += instructions.Return
105109
}
106110
instrs += new instructions.Label(end)
@@ -149,7 +153,7 @@ private [parsley] abstract class Parsley[+A] private [deepembedding]
149153
sub: SubMap,
150154
label: UnsafeOption[String]): Cont[Unit, Parsley[A_]]
151155
// Let-finder recursion
152-
protected def findLetsAux[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState): Cont[Unit, Unit]
156+
protected def findLetsAux[Cont[_, +_]: ContOps](implicit seen: Set[Parsley[_]], state: LetFinderState, label: UnsafeOption[String]): Cont[Unit, Unit]
153157
// Optimisation - Bottom-up
154158
protected def optimise: Parsley[A] = this
155159
// Peephole optimisation and code generation - Top-down
@@ -194,51 +198,49 @@ private [deepembedding] trait UsesRegister {
194198
// Internals
195199
private [parsley] class CodeGenState {
196200
private var current = 0
197-
private val queue = mutable.ListBuffer.empty[Parsley[_]]
198-
private val map = mutable.Map.empty[Parsley[_], Int]
201+
private val queue = mutable.ListBuffer.empty[Subroutine[_]]
202+
private val map = mutable.Map.empty[Subroutine[_], Int]
199203
def freshLabel(): Int = {
200204
val next = current
201205
current += 1
202206
next
203207
}
204208
def nlabels: Int = current
205209

206-
def getSubLabel(p: Parsley[_]): Int = map.getOrElseUpdate(p,
207-
{
208-
p +=: queue
210+
def getSubLabel(sub: Subroutine[_]): Int = map.getOrElseUpdate(sub, {
211+
sub +=: queue
209212
freshLabel()
210213
})
211214

212-
def nextSub(): Parsley[_] = queue.remove(0)
215+
def nextSub(): Subroutine[_] = queue.remove(0)
213216
def more: Boolean = queue.nonEmpty
214217
def subsExist: Boolean = map.nonEmpty
215218
}
216219

217220
private [parsley] class LetFinderState {
218221
private val _recs = mutable.Set.empty[Parsley[_]]
219-
private val _preds = mutable.Map.empty[Parsley[_], Int]
222+
private val _preds = mutable.Map.empty[(UnsafeOption[String], Parsley[_]), Int]
220223
private val _usedRegs = mutable.Set.empty[Reg[_]]
221224

222-
def addPred(p: Parsley[_]): Unit = _preds += p -> (_preds.getOrElseUpdate(p, 0) + 1)
225+
def addPred(p: Parsley[_], label: UnsafeOption[String]): Unit = _preds += (label, p) -> (_preds.getOrElseUpdate((label, p), 0) + 1)
223226
def addRec(p: Parsley[_]): Unit = _recs += p
224227
def addReg(reg: Reg[_]): Unit = _usedRegs += reg
225-
def notProcessedBefore(p: Parsley[_]): Boolean = _preds(p) == 1
226-
227-
def lets: Map[Parsley[_], Parsley[_]] = {
228-
(for ((k, v) <- _preds;
229-
if v >= 2 && !_recs.contains(k))
230-
yield k -> {
231-
val sub = Subroutine(k, null)
232-
sub.processed = false
233-
sub
234-
}).toMap
228+
def notProcessedBefore(p: Parsley[_], label: UnsafeOption[String]): Boolean = _preds((label, p)) == 1
229+
230+
def lets: Iterable[(UnsafeOption[String], Parsley[_])] = _preds.toSeq.collect {
231+
case (k@(label, p), refs) if refs >= 2 && !_recs(p) => k
235232
}
233+
236234
def recs: Set[Parsley[_]] = _recs.toSet
237235
def usedRegs: Set[Reg[_]] = _usedRegs.toSet
238236
}
239237

240-
private [parsley] class SubMap(val subMap: Map[Parsley[_], Parsley[_]]) extends AnyVal {
241-
def apply[A](p: Parsley[A]): Parsley[A] = subMap.getOrElse(p, p).asInstanceOf[Parsley[A]]
238+
private [parsley] class SubMap(subs: Iterable[(UnsafeOption[String], Parsley[_])]) {
239+
private val subMap: Map[(UnsafeOption[String], Parsley[_]), Subroutine[_]] = subs.map {
240+
case k@(label, p) => k -> new Subroutine(p, label)
241+
}.toMap
242+
243+
def apply[A](label: UnsafeOption[String], p: Parsley[A]): Parsley[A] = subMap.getOrElse((label, p), p).asInstanceOf[Parsley[A]]
242244
// $COVERAGE-OFF$
243245
override def toString: String = subMap.toString
244246
// $COVERAGE-ON$

0 commit comments

Comments
 (0)