Skip to content

Commit f65fbe7

Browse files
committed
Merge branch '1.0.x' into merge-1.0.x-master
(The hostname workaround was replaced with the sudo-less variant since we can't use sudo here.) Conflicts: .travis.yml build.sbt project/plugins.sbt
2 parents 49c59e0 + b71f12e commit f65fbe7

File tree

15 files changed

+270
-37
lines changed

15 files changed

+270
-37
lines changed

Diff for: .travis.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ sudo: false
44

55
language: scala
66

7+
addons:
8+
hosts:
9+
- myshorthost
10+
hostname: myshorthost
11+
712
env:
813
global:
9-
- PUBLISH_JDK=openjdk6
1014
# PGP_PASSPHRASE
1115
- secure: "SkBtn/6OjEldoikn0MFuyeLT/pau27kwKSDYTVQeJ4BKDzdWLwLE5Q3RukLGttIfNdhOvRoocpQSW9GkZfibTHmwrRnAokucfZCqTsKbwoOp1xIoOh5GrrVrB6gcP7WBTKinqFdBgSvLOrP7GviImz4ZuB9wq1r+mToGG4pDrXc="
1216
# SONA_USER
@@ -18,7 +22,6 @@ script: admin/build.sh
1822

1923
jdk:
2024
- openjdk6
21-
- openjdk7
2225
- oraclejdk8
2326

2427
notifications:

Diff for: admin/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ To configure tag driven releases from Travis CI.
1919
Edit `.travis.yml` as prompted.
2020
4. Edit `.travis.yml` to use `./admin/build.sh` as the build script,
2121
and edit that script to use the tasks required for this project.
22-
5. Edit `.travis.yml` to select which JDK will be used for publishing.
22+
5. Edit `build.sbt` to select which JDK will be used for publishing.
2323

2424
It is important to add comments in .travis.yml to identify the name
2525
of each environment variable encoded in a `:secure` section.
@@ -30,7 +30,6 @@ form:
3030
language: scala
3131
env:
3232
global:
33-
- PUBLISH_JDK=openjdk6
3433
# PGP_PASSPHRASE
3534
- secure: "XXXXXX"
3635
# SONA_USER

Diff for: admin/build.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ set -e
77
# git on travis does not fetch tags, but we have TRAVIS_TAG
88
# headTag=$(git describe --exact-match ||:)
99

10-
if [ "$TRAVIS_JDK_VERSION" == "$PUBLISH_JDK" ] && [[ "$TRAVIS_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9-]+)? ]]; then
10+
if [[ "$TRAVIS_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9-]+)? ]]; then
1111
echo "Going to release from tag $TRAVIS_TAG!"
1212
myVer=$(echo $TRAVIS_TAG | sed -e s/^v//)
1313
publishVersion='set every version := "'$myVer'"'

Diff for: build.sbt

+25-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11
scalaVersion in ThisBuild := crossScalaVersions.value.head
22

33
crossScalaVersions in ThisBuild := {
4+
val v211 = List("2.11.8")
5+
val v212 = List("2.12.0-RC1")
6+
47
val javaVersion = System.getProperty("java.version")
5-
val isJDK6Or7 =
6-
javaVersion.startsWith("1.6.") || javaVersion.startsWith("1.7.")
7-
if (isJDK6Or7)
8-
Seq("2.11.7")
9-
else
10-
Seq("2.11.7", "2.12.0-M3")
8+
val isTravisPublishing = !util.Properties.envOrElse("TRAVIS_TAG", "").trim.isEmpty
9+
10+
if (isTravisPublishing) {
11+
if (javaVersion.startsWith("1.6.")) v211
12+
else if (javaVersion.startsWith("1.8.")) v212
13+
else Nil
14+
} else if (javaVersion.startsWith("1.6.") || javaVersion.startsWith("1.7.")) {
15+
v211
16+
} else if (javaVersion.startsWith("1.8.") || javaVersion.startsWith("9")) {
17+
v211 ++ v212
18+
} else {
19+
sys.error(s"Unsupported java version: $javaVersion.")
20+
}
1121
}
1222

1323
lazy val `scala-parser-combinators` = crossProject.in(file(".")).
1424
settings(scalaModuleSettings: _*).
25+
settings(
26+
name := "scala-parser-combinators-root"
27+
).
1528
jvmSettings(
16-
name := "scala-parser-combinators-jvm"
29+
// Mima uses the name of the jvm project in the artifactId
30+
// when resolving previous versions (so no "-jvm" project)
31+
name := "scala-parser-combinators"
1732
).
1833
jsSettings(
1934
name := "scala-parser-combinators-js"
@@ -38,10 +53,10 @@ lazy val `scala-parser-combinators` = crossProject.in(file(".")).
3853
).
3954
jsConfigure(_.enablePlugins(ScalaJSJUnitPlugin)).
4055
jvmSettings(
41-
libraryDependencies += "junit" % "junit" % "4.11" % "test",
42-
libraryDependencies += "com.novocode" % "junit-interface" % "0.10" % "test"
56+
libraryDependencies += "junit" % "junit" % "4.12" % "test",
57+
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
4358
).
44-
settings(
59+
jvmSettings(
4560
mimaPreviousVersion := None
4661
)
4762

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package scala.util.parsing.input
2+
3+
import java.lang.CharSequence
4+
import java.util.{AbstractMap, Collections}
5+
6+
private[input] trait PositionCache {
7+
private[input] lazy val indexCache: java.util.Map[CharSequence,Array[Int]] = new AbstractMap[CharSequence, Array[Int]] {
8+
9+
override def entrySet() = Collections.emptySet()
10+
11+
// the /dev/null of Maps
12+
override def put(ch: CharSequence, a: Array[Int]) = null
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package scala.util.parsing.input
2+
3+
import java.lang.{CharSequence, ThreadLocal}
4+
import java.util.WeakHashMap
5+
6+
/**
7+
* @author Tomáš Janoušek
8+
*/
9+
private[input] trait PositionCache {
10+
private lazy val indexCacheTL =
11+
// not DynamicVariable as that would share the map from parent to child :-(
12+
new ThreadLocal[java.util.Map[CharSequence, Array[Int]]] {
13+
override def initialValue = new WeakHashMap[CharSequence, Array[Int]]
14+
}
15+
16+
private[input] def indexCache = indexCacheTL.get
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import scala.util.parsing.combinator._
2+
import scala.util.DynamicVariable
3+
4+
import org.junit.Test
5+
6+
class t9010 {
7+
@Test
8+
def test: Unit = {
9+
val p = new grammar
10+
val lastNoSuccessVar = getLastNoSuccessVar(p)
11+
import p._
12+
13+
val res1 = parse(x, "x")
14+
assert(res1.successful)
15+
assert(lastNoSuccessVar.value == None)
16+
17+
val res2 = parse(x, "y")
18+
assert(!res2.successful)
19+
assert(lastNoSuccessVar.value == None)
20+
21+
val res3 = parseAll(x, "x")
22+
assert(res3.successful)
23+
assert(lastNoSuccessVar.value == None)
24+
25+
val res4 = parseAll(x, "y")
26+
assert(!res4.successful)
27+
assert(lastNoSuccessVar.value == None)
28+
}
29+
30+
private def getLastNoSuccessVar(p: Parsers): DynamicVariable[Option[_]] = {
31+
// use java reflection instead of scala (see below) because of
32+
// https://issues.scala-lang.org/browse/SI-9306
33+
val fn = "scala$util$parsing$combinator$Parsers$$lastNoSuccessVar"
34+
val f = p.getClass.getDeclaredMethod(fn)
35+
f.setAccessible(true)
36+
f.invoke(p).asInstanceOf[DynamicVariable[Option[_]]]
37+
38+
/*
39+
val ru = scala.reflect.runtime.universe
40+
val mirror = ru.runtimeMirror(getClass.getClassLoader)
41+
val lastNoSuccessVarField =
42+
ru.typeOf[Parsers].decl(ru.TermName("lastNoSuccessVar")).asTerm.accessed.asTerm
43+
mirror.reflect(p).reflectField(lastNoSuccessVarField).get.
44+
asInstanceOf[DynamicVariable[Option[_]]]
45+
*/
46+
}
47+
48+
private final class grammar extends RegexParsers {
49+
val x: Parser[String] = "x"
50+
}
51+
}

Diff for: project/build.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=0.13.8
1+
sbt.version=0.13.12

Diff for: project/plugins.sbt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
addSbtPlugin("org.scala-lang.modules" % "scala-module-plugin" % "1.0.3")
1+
addSbtPlugin("org.scala-lang.modules" % "scala-module-plugin" % "1.0.4")
22

3-
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.6")
3+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.12")

Diff for: shared/src/main/scala/scala/util/parsing/combinator/Parsers.scala

+16-11
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,19 @@ trait Parsers {
156156
val successful = true
157157
}
158158

159-
private lazy val lastNoSuccessVar = new DynamicVariable[Option[NoSuccess]](None)
159+
/* two layers of Option:
160+
* outer Option is None if lastNoSuccess tracking is disabled (outside of
161+
* phrase) and Some if tracking is enabled
162+
* inner Option is None if NoSuccess hasn't been seen yet, Some otherwise
163+
* this is necessary to avoid leaking NoSuccesses in thread locals */
164+
private lazy val lastNoSuccessVar = new DynamicVariable[Option[Option[NoSuccess]]](None)
160165

161166
/** A common super-class for unsuccessful parse results. */
162167
sealed abstract class NoSuccess(val msg: String, override val next: Input) extends ParseResult[Nothing] { // when we don't care about the difference between Failure and Error
163168
val successful = false
164169

165-
if (lastNoSuccessVar.value forall (v => !(next.pos < v.next.pos)))
166-
lastNoSuccessVar.value = Some(this)
170+
if (lastNoSuccessVar.value exists (_ forall (v => !(next.pos < v.next.pos))))
171+
lastNoSuccessVar.value = Some(Some(this))
167172

168173
def map[U](f: Nothing => U) = this
169174
def mapPartial[U](f: PartialFunction[Nothing, U], error: Nothing => String): ParseResult[U] = this
@@ -590,7 +595,7 @@ trait Parsers {
590595
* @return a `tParser` that succeeds if `e` is the next available input.
591596
*/
592597

593-
implicit def accept(e: Elem): Parser[Elem] = acceptIf(_ == e)("`"+e+"' expected but " + _ + " found")
598+
implicit def accept(e: Elem): Parser[Elem] = acceptIf(_ == e)("'"+e+"' expected but " + _ + " found")
594599

595600
/** A parser that matches only the given list of element `es`.
596601
*
@@ -908,14 +913,14 @@ trait Parsers {
908913
* if `p` consumed all the input.
909914
*/
910915
def phrase[T](p: Parser[T]) = new Parser[T] {
911-
def apply(in: Input) = lastNoSuccessVar.withValue(None) {
916+
def apply(in: Input) = lastNoSuccessVar.withValue(Some(None)) {
912917
p(in) match {
913-
case s @ Success(out, in1) =>
914-
if (in1.atEnd)
915-
s
916-
else
917-
lastNoSuccessVar.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1)
918-
case ns => lastNoSuccessVar.value.getOrElse(ns)
918+
case s @ Success(out, in1) =>
919+
if (in1.atEnd)
920+
s
921+
else
922+
lastNoSuccessVar.value flatMap (_ filterNot { _.next.pos < in1.pos }) getOrElse Failure("end of input expected", in1)
923+
case ns => lastNoSuccessVar.value.flatten.getOrElse(ns)
919924
}
920925
}
921926
}

Diff for: shared/src/main/scala/scala/util/parsing/input/OffsetPosition.scala

+25-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ package scala
1010
package util.parsing.input
1111

1212
import scala.collection.mutable.ArrayBuffer
13+
import java.lang.{CharSequence, ThreadLocal}
14+
import java.util.WeakHashMap
1315

1416
/** `OffsetPosition` is a standard class for positions
1517
* represented as offsets into a source ``document''.
@@ -19,10 +21,20 @@ import scala.collection.mutable.ArrayBuffer
1921
*
2022
* @author Martin Odersky
2123
*/
22-
case class OffsetPosition(source: java.lang.CharSequence, offset: Int) extends Position {
24+
case class OffsetPosition(source: CharSequence, offset: Int) extends Position {
2325

2426
/** An index that contains all line starts, including first line, and eof. */
2527
private lazy val index: Array[Int] = {
28+
Option(OffsetPosition.indexCache.get(source)) match {
29+
case Some(index) => index
30+
case None =>
31+
val index = genIndex
32+
OffsetPosition.indexCache.put(source, index)
33+
index
34+
}
35+
}
36+
37+
private def genIndex: Array[Int] = {
2638
val lineStarts = new ArrayBuffer[Int]
2739
lineStarts += 0
2840
for (i <- 0 until source.length)
@@ -50,8 +62,14 @@ case class OffsetPosition(source: java.lang.CharSequence, offset: Int) extends P
5062
*
5163
* @return the line at `offset` (not including a newline)
5264
*/
53-
def lineContents: String =
54-
source.subSequence(index(line - 1), index(line)).toString
65+
def lineContents: String = {
66+
val endIndex = if (source.charAt(index(line) - 1) == '\n') {
67+
index(line) - 1
68+
} else {
69+
index(line)
70+
}
71+
source.subSequence(index(line - 1), endIndex).toString
72+
}
5573

5674
/** Returns a string representation of the `Position`, of the form `line.column`. */
5775
override def toString = line+"."+column
@@ -71,3 +89,7 @@ case class OffsetPosition(source: java.lang.CharSequence, offset: Int) extends P
7189
this.line == that.line && this.column < that.column
7290
}
7391
}
92+
93+
/** An object holding the index cache.
94+
*/
95+
object OffsetPosition extends scala.runtime.AbstractFunction2[CharSequence,Int,OffsetPosition] with PositionCache

Diff for: shared/src/main/scala/scala/util/parsing/input/PagedSeqReader.scala

+8-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ object PagedSeqReader {
3030
* @author Martin Odersky
3131
*/
3232
class PagedSeqReader(seq: PagedSeq[Char],
33-
override val offset: Int) extends Reader[Char] {
33+
override val offset: Int) extends Reader[Char] { outer =>
3434
import PagedSeqReader._
3535

36-
override lazy val source: java.lang.CharSequence = seq
36+
override val source: java.lang.CharSequence = seq
3737

3838
/** Construct a `PagedSeqReader` with its first element at
3939
* `source(0)` and position `(1,1)`.
@@ -51,7 +51,9 @@ class PagedSeqReader(seq: PagedSeq[Char],
5151
* otherwise, it's a `PagedSeqReader` containing the rest of input.
5252
*/
5353
def rest: PagedSeqReader =
54-
if (seq.isDefinedAt(offset)) new PagedSeqReader(seq, offset + 1)
54+
if (seq.isDefinedAt(offset)) new PagedSeqReader(seq, offset + 1) {
55+
override val source: java.lang.CharSequence = outer.source
56+
}
5557
else this
5658

5759
/** The position of the first element in the reader.
@@ -67,5 +69,7 @@ class PagedSeqReader(seq: PagedSeq[Char],
6769
* `n` elements.
6870
*/
6971
override def drop(n: Int): PagedSeqReader =
70-
new PagedSeqReader(seq, offset + n)
72+
new PagedSeqReader(seq, offset + n) {
73+
override val source: java.lang.CharSequence = outer.source
74+
}
7175
}

Diff for: shared/src/test/scala/scala/util/parsing/combinator/PackratParsersTest.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class PackratParsersTest {
122122
val failure = parseResult.asInstanceOf[Failure]
123123
assertEquals(expectedFailureMsg, failure.msg)
124124
}
125-
assertFailure("``b'' expected but `c' found", "a a a a b b b c c c c")
125+
assertFailure("'`b'' expected but `c' found", "a a a a b b b c c c c")
126126
assertFailure("end of input", "a a a a b b b b c c c")
127127
}
128128

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package scala.util.parsing.combinator
2+
3+
import scala.util.parsing.input._
4+
import scala.collection.immutable.PagedSeq
5+
6+
import org.junit.Test
7+
import org.junit.Assert.assertTrue
8+
9+
import scala.util.parsing.combinator.syntactical.StandardTokenParsers
10+
11+
class gh45 {
12+
13+
@Test
14+
def test4: Unit = {
15+
def check(rd: Reader[Char]): Unit = {
16+
val g = new grammar
17+
val p = g.phrase(g.script)
18+
val parseResult = p(new g.lexical.Scanner(rd))
19+
assertTrue(parseResult.isInstanceOf[g.Success[_]])
20+
}
21+
22+
val str = "x once y"
23+
check(new CharSequenceReader(str))
24+
/* Note that this only tests PagedSeq.rest since neither
25+
* PackratReader nor lexical.Scanner override/use the drop method.
26+
*/
27+
check(new PagedSeqReader(PagedSeq.fromStrings(List(str))))
28+
}
29+
30+
}
31+
32+
private final class grammar extends StandardTokenParsers with PackratParsers {
33+
lexical.reserved ++= List("x", "y", "z", "once")
34+
35+
var onceCnt: Int = 0
36+
lazy val once: PackratParser[String] = memo("once") ^? {
37+
case s if onceCnt == 0 =>
38+
onceCnt += 1
39+
s
40+
}
41+
42+
lazy val script: PackratParser[Any] =
43+
( "x" ~ once ~ "z"
44+
| "x" ~ once ~ "y"
45+
)
46+
}

0 commit comments

Comments
 (0)