Skip to content

Commit 15d761a

Browse files
authored
Backports for 4.5 from 5.0 (j-mie6#233)
This PR backports a number of fixes from the 5.0 series. Fixes j-mie6#230, fixes j-mie6#229, fixes j-mie6#226, and fixes j-mie6#227
2 parents 03d46d5 + 4540959 commit 15d761a

File tree

8 files changed

+47
-19
lines changed

8 files changed

+47
-19
lines changed

parsley/shared/src/main/scala/parsley/internal/machine/errors/DefuncError.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,9 @@ private [parsley] final class EmptyErrorWithReason(val presentationOffset: Int,
357357
}
358358

359359
private [errors] final class TrivialMergedErrors private [errors] (val err1: TrivialDefuncError, val err2: TrivialDefuncError) extends TrivialDefuncError {
360-
override final val flags = err1.flags & err2.flags
360+
// FIXME: this is horrid, split out the flags at this point, we'll do 16-bit and 8-bit
361+
override final val flags = scala.math.max(err1.entrenchedBy, err2.entrenchedBy) |
362+
(err1.flags & err2.flags & ~DefuncError.EntrenchedMask)
361363
assume(err1.underlyingOffset == err2.underlyingOffset, "two errors only merge when they have matching offsets")
362364
override val underlyingOffset = err1.underlyingOffset
363365
assume(err1.presentationOffset == err2.presentationOffset, "two errors only merge when they have matching offsets")
@@ -369,7 +371,9 @@ private [errors] final class TrivialMergedErrors private [errors] (val err1: Tri
369371
}
370372

371373
private [errors] final class FancyMergedErrors private [errors] (val err1: FancyDefuncError, val err2: FancyDefuncError) extends FancyDefuncError {
372-
override final val flags = err1.flags & err2.flags
374+
// FIXME: this is horrid, split out the flags at this point, we'll do 16-bit and 8-bit
375+
override final val flags = scala.math.max(err1.entrenchedBy, err2.entrenchedBy) |
376+
(err1.flags & err2.flags & ~DefuncError.EntrenchedMask)
373377
assume(err1.underlyingOffset == err2.underlyingOffset, "two errors only merge when they have matching offsets")
374378
override val underlyingOffset = err1.underlyingOffset
375379
assume(err1.presentationOffset == err2.presentationOffset, "two errors only merge when they have matching offsets")

parsley/shared/src/main/scala/parsley/internal/machine/instructions/token/SymbolInstrs.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import parsley.token.errors.LabelConfig
1111
import parsley.token.predicate
1212

1313
import parsley.internal.collection.immutable.Trie
14-
import parsley.internal.errors.ExpectDesc
14+
import parsley.internal.errors.{ExpectDesc, ExpectItem}
1515
import parsley.internal.machine.Context
1616
import parsley.internal.machine.XAssert._
1717
import parsley.internal.machine.instructions.Instr
1818

1919
private [token] abstract class Specific extends Instr {
2020
protected val specific: String
2121
protected val caseSensitive: Boolean
22-
protected val expected: Iterable[ExpectDesc]
22+
protected val expected: Iterable[ExpectItem]
2323
private [this] final val strsz = specific.length
2424
private [this] final val numCodePoints = specific.codePointCount(0, strsz)
2525

@@ -65,12 +65,13 @@ private [token] abstract class Specific extends Instr {
6565
}
6666

6767
private [internal] final class SoftKeyword(protected val specific: String, letter: CharPredicate, protected val caseSensitive: Boolean,
68-
protected val expected: Iterable[ExpectDesc], expectedEnd: Iterable[ExpectDesc]) extends Specific {
68+
protected val expected: Iterable[ExpectItem], protected val reason: Option[String],
69+
expectedEnd: Iterable[ExpectDesc]) extends Specific {
6970
def this(specific: String, letter: predicate.CharPredicate, caseSensitive: Boolean, expected: LabelConfig, expectedEnd: String) = {
7071
this(if (caseSensitive) specific else specific.toLowerCase,
7172
letter.asInternalPredicate,
7273
caseSensitive,
73-
expected.asExpectDescs, Some(new ExpectDesc(expectedEnd)))
74+
expected.asExpectItems(specific), expected.asReason, Some(new ExpectDesc(expectedEnd)))
7475
}
7576

7677
protected def postprocess(ctx: Context): Unit = {
@@ -90,9 +91,10 @@ private [internal] final class SoftKeyword(protected val specific: String, lette
9091
}
9192

9293
private [internal] final class SoftOperator(protected val specific: String, letter: CharPredicate, ops: Trie[Unit],
93-
protected val expected: Iterable[ExpectDesc], expectedEnd: Iterable[ExpectDesc]) extends Specific {
94+
protected val expected: Iterable[ExpectItem], protected val reason: Option[String],
95+
expectedEnd: Iterable[ExpectDesc]) extends Specific {
9496
def this(specific: String, letter: predicate.CharPredicate, ops: Trie[Unit], expected: LabelConfig, expectedEnd: String) = {
95-
this(specific, letter.asInternalPredicate, ops, expected.asExpectDescs, Some(new ExpectDesc(expectedEnd)))
97+
this(specific, letter.asInternalPredicate, ops, expected.asExpectItems(specific), expected.asReason, Some(new ExpectDesc(expectedEnd)))
9698
}
9799
protected val caseSensitive = true
98100
private val ends = ops.suffixes(specific)

parsley/shared/src/main/scala/parsley/token/errors/ConfigImplUntyped.scala

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ private [parsley] trait LabelOps {
2727
private [parsley] final def asExpectItems(raw: Char): Iterable[ExpectItem] = asExpectItems(s"$raw")
2828
}
2929

30-
// TODO: reason extraction, maybe tie into errors?
31-
private [parsley] trait ExplainOps
32-
30+
private [parsley] sealed trait ExplainOps {
31+
private [parsley] def asReason: Option[String]
32+
}
3333

3434
// Constraining Types
3535
/** This type can be used to configure ''both'' errors that make labels and those that make reasons.
@@ -52,6 +52,10 @@ trait LabelConfig extends LabelWithExplainConfig {
5252
*/
5353
trait ExplainConfig extends LabelWithExplainConfig
5454

55+
private [parsley] sealed trait Labeller {
56+
private [parsley] def config(name: String): LabelConfig
57+
}
58+
5559
private [errors] final class Label private[errors] (val labels: Seq[String]) extends LabelConfig {
5660
require(labels.forall(_.nonEmpty), "labels cannot be empty strings")
5761
private [parsley] final override def apply[A](p: Parsley[A]) = p.labels(labels: _*)
@@ -64,16 +68,18 @@ private [errors] final class Label private[errors] (val labels: Seq[String]) ext
6468
case _ => this
6569
}
6670
private [parsley] final override def orElse(config: LabelConfig) = this
71+
private [parsley] final override def asReason: Option[String] = None
6772
}
6873
/** This object has a factory for configurations producing labels: labels may not be empty.
6974
* @since 4.1.0
7075
* @group labels
7176
*/
72-
object Label {
77+
object Label extends Labeller {
7378
def apply(label: String): LabelConfig = if (label.isEmpty) Hidden else new Label(Seq(label))
7479
def apply(label1: String, label2: String, labels: String*): LabelConfig = new Label(label1 +: label2 +: labels)
7580
// this is required internally, will go in parsley 5
7681
private [parsley] def apply(labels: String*) = new Label(labels)
82+
private [parsley] final def config(name: String) = Label(name)
7783
}
7884

7985
/** This object configures labels by stating that it must be hidden.
@@ -87,6 +93,7 @@ object Hidden extends LabelConfig {
8793
private [parsley] final override def asExpectItems(@unused raw: String) = asExpectDescs
8894
private [parsley] final override def orElse(config: LabelWithExplainConfig) = this
8995
private [parsley] final override def orElse(config: LabelConfig) = this
96+
private [parsley] final override def asReason: Option[String] = None
9097
}
9198

9299
private [errors] final class Reason private[errors] (val reason: String) extends ExplainConfig {
@@ -100,6 +107,7 @@ private [errors] final class Reason private[errors] (val reason: String) extend
100107
case lr: LabelAndReason => new LabelAndReason(lr.labels, reason)
101108
case _ => this
102109
}
110+
private [parsley] final override def asReason: Option[String] = Some(reason)
103111
}
104112
/** This object has a factory for configurations producing reasons: if the empty string is provided, this equivalent to [[NotConfigured `NotConfigured`]].
105113
* @since 4.1.0
@@ -117,6 +125,7 @@ private [errors] final class LabelAndReason private[errors] (val labels: Seq[Str
117125
private [parsley] final override def asExpectDescs(@unused otherwise: String) = asExpectDescs
118126
private [parsley] final override def asExpectItems(@unused raw: String) = asExpectDescs
119127
private [parsley] final override def orElse(config: LabelWithExplainConfig) = this
128+
private [parsley] final override def asReason: Option[String] = Some(reason)
120129
}
121130
/** This object has a factory for configurations producing labels and reasons: if the empty label is provided, this equivalent to [[Hidden `Hidden`]] with no
122131
* reason; if the empty reason is provided this is equivalent to [[Label$ `Label`]].
@@ -135,11 +144,13 @@ object LabelAndReason {
135144
* @since 4.1.0
136145
* @group labels
137146
*/
138-
object NotConfigured extends LabelConfig with ExplainConfig with LabelWithExplainConfig {
147+
object NotConfigured extends LabelConfig with ExplainConfig with LabelWithExplainConfig with Labeller {
139148
private [parsley] final override def apply[A](p: Parsley[A]) = p
140149
private [parsley] final override def asExpectDescs = None
141150
private [parsley] final override def asExpectDescs(otherwise: String) = Some(new ExpectDesc(otherwise))
142151
private [parsley] final override def asExpectItems(raw: String) = Some(new ExpectRaw(raw))
143152
private [parsley] final override def orElse(config: LabelWithExplainConfig) = config
144153
private [parsley] final override def orElse(config: LabelConfig) = config
154+
private [parsley] final override def asReason: Option[String] = None
155+
private [parsley] final def config(name: String) = this
145156
}

parsley/shared/src/main/scala/parsley/token/errors/ErrorConfig.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,11 @@ class ErrorConfig {
818818
* @group symbol
819819
*/
820820
def labelSymbolOperator(symbol: String): LabelConfig = Label(symbol)
821+
// To unify, or not to unify
822+
private [parsley] def defaultSymbolKeyword: Labeller = Label
823+
private [parsley] def defaultSymbolOperator: Labeller = Label
824+
// Other?
825+
private [parsley] def defaultSymbolPunctuation: Labeller = NotConfigured
821826
/** How the required end of a given keyword should be specified in an error.
822827
* @since 4.1.0
823828
* @note defaults to "end of symbol"

parsley/shared/src/main/scala/parsley/token/numeric/SignedInteger.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ private [token] final class SignedInteger(desc: NumericDesc, unsigned: UnsignedI
2828
override def number: Parsley[BigInt] = err.labelIntegerSignedNumber.apply(_number)
2929

3030
override protected [numeric] def bounded[T](number: Parsley[BigInt], bits: Bits, radix: Int, label: (ErrorConfig, Boolean) => LabelWithExplainConfig)
31-
(implicit ev: CanHold[bits.self,T]): Parsley[T] = label(err, false) {
31+
(implicit ev: CanHold[bits.self,T]): Parsley[T] = label(err, true) {
3232
err.filterIntegerOutOfBounds(bits.lowerSigned, bits.upperSigned, radix).collect(number) {
3333
case x if bits.lowerSigned <= x && x <= bits.upperSigned => ev.fromBigInt(x)
3434
}

parsley/shared/src/main/scala/parsley/token/symbol/ConcreteSymbol.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package parsley.token.symbol
88
import parsley.Parsley, Parsley.atomic
99
import parsley.character.{char, string}
1010
import parsley.token.descriptions.{NameDesc, SymbolDesc}
11-
import parsley.token.errors.ErrorConfig
11+
import parsley.token.errors.{ErrorConfig, NotConfigured}
1212

1313
import parsley.internal.deepembedding.singletons.token
1414

@@ -22,22 +22,26 @@ private [token] class ConcreteSymbol(nameDesc: NameDesc, symbolDesc: SymbolDesc,
2222

2323
override def apply(name: String): Parsley[Unit] = {
2424
require(name.nonEmpty, "Symbols may not be empty strings")
25+
lazy val punctuationLabel = err.labelSymbolPunctuation.get(name).map {
26+
case None => parsley.token.errors.Hidden
27+
case Some(l) => parsley.token.errors.Label(l)
28+
}.getOrElse(NotConfigured).orElse(err.defaultSymbolPunctuation.config(name))
2529
if (symbolDesc.hardKeywords(name)) softKeyword(name)
2630
else if (symbolDesc.hardOperators(name)) softOperator(name)
27-
else atomic(string(name)).void
31+
else punctuationLabel(atomic(string(name)).void)
2832
}
2933

3034
override def apply(name: Char): Parsley[Unit] = char(name).void
3135

3236
override def softKeyword(name: String): Parsley[Unit] = {
3337
require(name.nonEmpty, "Keywords may not be empty strings")
3438
new Parsley(new token.SoftKeyword(name, nameDesc.identifierLetter, symbolDesc.caseSensitive,
35-
err.labelSymbolKeyword(name), err.labelSymbolEndOfKeyword(name)))
39+
err.labelSymbolKeyword(name).orElse(err.defaultSymbolKeyword.config(name)), err.labelSymbolEndOfKeyword(name)))
3640
}
3741

3842
override def softOperator(name: String): Parsley[Unit] = {
3943
require(name.nonEmpty, "Operators may not be empty strings")
4044
new Parsley(new token.SoftOperator(name, nameDesc.operatorLetter, symbolDesc.hardOperatorsTrie,
41-
err.labelSymbolOperator(name), err.labelSymbolEndOfOperator(name)))
45+
err.labelSymbolOperator(name).orElse(err.defaultSymbolOperator.config(name)), err.labelSymbolEndOfOperator(name)))
4246
}
4347
}

parsley/shared/src/main/scala/parsley/token/text/StringCharacter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ private [token] class RawCharacter(err: ErrorConfig) extends StringCharacter {
3131

3232
private [token] class EscapableCharacter(desc: EscapeDesc, escapes: Escape, space: Parsley[_], err: ErrorConfig) extends StringCharacter {
3333
override def isRaw: Boolean = false
34-
private lazy val escapeEmpty = err.labelStringEscapeEmpty(desc.emptyEscape.fold[Parsley[Char]](empty)(char))
34+
private lazy val escapeEmpty = desc.emptyEscape.fold[Parsley[Char]](empty)(c => err.labelStringEscapeEmpty(char(c)))
3535
private lazy val escapeGap = {
3636
if (desc.gapsSupported) some(err.labelStringEscapeGap(space)) ~> err.labelStringEscapeGapEnd(desc.escBegin)
3737
else empty

parsley/shared/src/test/scala/parsley/internal/deepembedding/frontend/VisitorTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class VisitorTests extends ParsleyTest {
8383

8484
override private[parsley] def apply[A](p: Parsley[A]): Parsley[A] =
8585
dontExecute()
86+
87+
override private[parsley] def asReason: Option[String] = None
8688
}
8789

8890
private val dummyCaretWidth: CaretWidth = new FlexibleCaret(0)

0 commit comments

Comments
 (0)