pax_global_header00006660000000000000000000000064124434231610014512gustar00rootroot0000000000000052 comment=8cb6febe67081cd2a7e5ccca73ea9bd1c28d0506 scala-parser-combinators-1.0.3/000077500000000000000000000000001244342316100164065ustar00rootroot00000000000000scala-parser-combinators-1.0.3/.gitignore000066400000000000000000000012771244342316100204050ustar00rootroot00000000000000# # Are you tempted to edit this file? # # First consider if the changes make sense for all, # or if they are specific to your workflow/system. # If it is the latter, you can augment this list with # entries in .git/info/excludes # # see also test/files/.gitignore # *.jar *~ build.properties # target directories for ant build /build/ /dists/ # other /out/ /bin/ /sandbox/ # eclipse, intellij /.classpath /.project /src/intellij/*.iml /src/intellij/*.ipr /src/intellij/*.iws /.cache /.idea /.settings # bak files produced by ./cleanup-commit *.bak # Standard symbolic link to build/quick/bin qbin # Mac specific, but that is common enough a dev platform to warrant inclusion. .DS_Store target/scala-parser-combinators-1.0.3/.mailmap000066400000000000000000000065701244342316100200370ustar00rootroot00000000000000Adriaan Moors Adriaan Moors Adriaan Moors Aleksandar Prokopec Aleksandar Prokopec Aleksandar Prokopec Aleksandar Prokopec Aleksandar Prokopec Aleksandar Prokopec Aleksandar Prokopec Alex Cruise Alex Cruise Antonio Cunei Antonio Cunei Buraq Emir Caoyuan Deng Chris Hodapp Chris James Christopher Vogt Christopher Vogt Christopher Vogt Damien Obristi Daniel C. Sobral Daniel C. Sobral Daniel Lorch Erik Stenman Eugene Burmako Eugene Burmako Eugene Vigdorchik Geoff Reedy Ilya Sergei Ingo Maier Ingo Maier Josh Suereth Josh Suereth Julien Eberle Kenji Yoshida <6b656e6a69@gmail.com> Luc Bourlier Luc Bourlier Luc Bourlier Martin Odersky Martin Odersky Michael Pradel Michel Schinz Miguel Garcia Miguel Garcia Mirco Dotta Mirco Dotta Moez A. Abdel-Gawad Mohsen Lesani Nada Amin Nada Amin Nada Amin Natallie Baikevich Nikolay Mihaylov Paolo Giarrusso Pavel Pavlov Philipp Haller Philipp Haller Philippe Altherr Raphaël Noir Roland Kuhn Rüdiger Klaehn Sebastian Hack Simon Ochsenreither Stepan Koltsov Stéphane Micheloud Unknown Committer Unknown Committer Unknown Committer Viktor Klang Vincent Cremet Vojin Jovanovic Vojin Jovanovic scala-parser-combinators-1.0.3/.travis.yml000066400000000000000000000015251244342316100205220ustar00rootroot00000000000000language: scala env: global: - PUBLISH_JDK=openjdk6 # admin/build.sh only publishes when running on this jdk # Don't commit sensitive files, instead commit a version encrypted with $SECRET, # this environment variable is encrypted with this repo's private key and stored below: # (See http://docs.travis-ci.com/user/environment-variables/#Secure-Variables.) - secure: ZEAhn8ozGqcQxvJD7/G3ifou2Vl7OkNzUXM15aKy0FbqLMOzsx3hAKsWEM6e/6d/7phDkiZisers+HOlt3nLwu75M3QLGm5lo4moJJJyx17omlrBQ7+M/hu3ZxqNRCE8oNI41V3pc+ZJQsY1qA7at4NPJbnAXx9sUUO2lGmc4xI= script: - admin/build.sh scala: - 2.11.2 jdk: - openjdk6 - openjdk7 notifications: email: - adriaan.moors@typesafe.com - antoine@gourlay.fr # if we get weird timeouts, see https://github.com/spray/spray/pull/233 # 'set concurrentRestrictions in Global += Tags.limit(Tags.Test, 1)' scala-parser-combinators-1.0.3/LICENSE.md000066400000000000000000000030061244342316100200110ustar00rootroot00000000000000Copyright (c) 2002-2013 EPFL Copyright (c) 2011-2013 Typesafe, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the EPFL nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.scala-parser-combinators-1.0.3/README.md000066400000000000000000000024611244342316100176700ustar00rootroot00000000000000scala-parser-combinators [](https://travis-ci.org/scala/scala-parser-combinators) ======================== ### Scala Standard Parser Combinator Library As of Scala 2.11, this library is a separate jar that can be omitted from Scala projects that do not use Parser Combinators. ## Documentation * [Latest version](http://www.scala-lang.org/files/archive/api/2.11.2/scala-parser-combinators/) * [Previous versions](http://scala-lang.org/documentation/api.html) (included in the API docs for the Scala library until Scala 2.11) ## Adding an SBT dependency To depend on scala-parser-combinators in SBT, add something like this to your build.sbt: ``` libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.2" ``` (Assuming you're using a `scalaVersion` for which a scala-parser-combinators is published. The first 2.11 milestone for which this is true is 2.11.0-M4.) To support multiple Scala versions, see the example in https://github.com/scala/scala-module-dependency-sample. ## Contributing * See the [Scala Developer Guidelines](https://github.com/scala/scala/blob/2.12.x/CONTRIBUTING.md) for general contributing guidelines * Have a look at [existing issues](https://issues.scala-lang.org/issues/?filter=12606) scala-parser-combinators-1.0.3/admin/000077500000000000000000000000001244342316100174765ustar00rootroot00000000000000scala-parser-combinators-1.0.3/admin/build.sh000077500000000000000000000012751244342316100211410ustar00rootroot00000000000000#!/bin/bash # prep environment for publish to sonatype staging if the HEAD commit is tagged # git on travis does not fetch tags, but we have TRAVIS_TAG # headTag=$(git describe --exact-match ||:) if [ "$TRAVIS_JDK_VERSION" == "$PUBLISH_JDK" ] && [[ "$TRAVIS_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9-]+)? ]]; then echo "Going to release from tag $TRAVIS_TAG!" myVer=$(echo $TRAVIS_TAG | sed -e s/^v//) publishVersion='set every version := "'$myVer'"' extraTarget="publish-signed" cat admin/gpg.sbt >> project/plugins.sbt admin/decrypt.sh sensitive.sbt (cd admin/ && ./decrypt.sh secring.asc) fi sbt ++$TRAVIS_SCALA_VERSION "$publishVersion" clean update compile test $extraTargetscala-parser-combinators-1.0.3/admin/decrypt.sh000077500000000000000000000001151244342316100215040ustar00rootroot00000000000000#!/bin/bash openssl aes-256-cbc -pass "pass:$SECRET" -in $1.enc -out $1 -d -ascala-parser-combinators-1.0.3/admin/encrypt.sh000077500000000000000000000001121244342316100215130ustar00rootroot00000000000000#!/bin/bash openssl aes-256-cbc -pass "pass:$SECRET" -in $1 -out $1.enc -ascala-parser-combinators-1.0.3/admin/encryptAll.sh000077500000000000000000000014541244342316100221560ustar00rootroot00000000000000#!/bin/bash # Based on https://gist.github.com/kzap/5819745: echo "This will encrypt the cleartext sensitive.sbt and admin/secring.asc, while making the encrypted versions available for decryption on Travis." echo "Update your .travis.yml as directed, and delete the cleartext versions." echo "Press enter to continue." read # 1. create a secret, put it in an environment variable while encrypting files -- UNSET IT AFTER export SECRET=$(cat /dev/urandom | head -c 10000 | openssl sha1) # 2. add the "secure: ..." line under the env section -- generate it with `` (install the travis gem first) travis encrypt SECRET=$SECRET admin/encrypt.sh admin/secring.asc admin/encrypt.sh sensitive.sbt echo "Remember to rm sensitive.sbt admin/secring.asc -- once you do, they cannot be recovered (except on Travis)!"scala-parser-combinators-1.0.3/admin/gpg.sbt000066400000000000000000000012021244342316100207600ustar00rootroot00000000000000// only added when publishing: addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.3") /* There's a companion sensitive.sbt, which was created like this: 1. in an sbt shell when sbt-gpg is loaded, create pgp key in admin/: set pgpReadOnly := false pgp-cmd gen-key // use $passPhrase pgp-cmd send-key hkp://keyserver.ubuntu.com 2. create sensitive.sbt with contents: pgpPassphrase := Some($passPhrase.toArray) pgpPublicRing := file("admin/pubring.asc") pgpSecretRing := file("admin/secring.asc") credentials += Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", $sonaUser, $sonaPass) */ scala-parser-combinators-1.0.3/admin/pubring.asc000066400000000000000000000016541244342316100216420ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: BCPG v1.49 mQENBFSAu20BCACAHC5KEbSM7Dm9+ksU12Y7TIP4rLLg94e/jF29WFNnH8P4rTv/ 8WNX0OF9gNW5Ltj7IzGGkzLX3HjrEKA7SEaFyTqoCQ+FIlqNNYt8YCScMyTSfYbQ 8GOEBUEcS8HPpZoudX7T1IYIAApl46kD0H4zzPPz2QHu51uj4jyjnIGRcDmHat3q dIeBzdnGinRFY+h/b4elKI0uEAFe/WmrMb9GpUaparkbNwutwof+7gIs5N7wyamg tErARSBgs00JJtgh+lyRv7y6T9OkL8p8nylxRGjIpUM3cICBZLTg/mA1+FPvQzFm AvYQ3cLWeFLobIVjuKSxWKwybdxR6ikCZd/LABEBAAG0O3NjYWxhLXBhcnNlci1j b21iaW5hdG9ycyA8c2NhbGEtaW50ZXJuYWxzQGdvb2dsZWdyb3Vwcy5jb20+iQEc BBMBAgAGBQJUgLttAAoJEF2pklhwFz7lp+kH/21ydQFQKdIv91iUNkwdbRbFcOoY 1LTZBAg2QaWMgqo9ZqZako09IlHouzMs+8mpgu3iC0spuzt24dn0He7ruKbnUb9F AvWcyG5Kzw/zy/wvC8IZNtLqMP5RKOJmNZydoMj2DUcfPnldAwKd/UGmOyn4AEvW ND0Qi59mPcJy/mCCDyjYfu+tJQCRg2DEhKtNX06GskZTaBeuqEVt58ZdE2aAq/X0 2afs/Pv160NvbzgQ5vroamvwr2Q8E5aCiCcf7DZDyG5Kibt2Z7IvrosdAJsS75xQ Q+1w7E3af7EdVZicMjkRPEhTbrtOInCRslIfKGp221mNvl7Au/ztfGPMUKY= =/HZD -----END PGP PUBLIC KEY BLOCK----- scala-parser-combinators-1.0.3/admin/secring.asc.enc000066400000000000000000000050241244342316100223650ustar00rootroot00000000000000U2FsdGVkX1+FQtnDY5zEKGLoxEcz97shUP9GlOVy7SAuYt3TpxaZGRPhXNK/jWDZ BQ6AcQq1cgIcn9skFDvRPsA9lRotD9wCZkc5GaGmrKgtdfccHh+58s1C8ufoL6L2 ujZ4+W8KuwrYcOYevpykKcWfgmZ5RglKm5wUZaJq/khVIp4BmeeXEN7vk6oovZym ymlFWhFQJDVgNs2zS6pSJD4vnndbc801nV63KZvk86RAD1vBCUKD7OfwqYpg5iCZ dsMSkCaos+v1lBfHZDwtR0CDTrcg0gwXs3hDG5aX9PstZ/r1r8AXCjqaYtp130Ee fkpuDA4cPMC0tPAPGAhFbBuieMEaHwZevMo9VQyykYkA841U++GgXfcRjn/IacnH y1eqs+74JY1CZJzsLmDzGlAT3fHKVBjMnQS1KNZdDRfzC+2UyO/2dbkuGwSlw4aX sOKz4RFwjNGbd6FUCM90ua/nsyrBlKHfuyeyllL7Q/H10zyaPvetoCpICH3852TZ l2jnPfMrfxqpBPkDyMclRmo3uz4/zHPHwZatddNo4l7plIQHAi3cviPoZm1ncgCC t8/O2fVFx0ubfrMKc8xqMwZBllmdTW63gP61j1vv8Xb5ZBDwpJSI4ff04SygEk2s 9Qzhx5f5izCu2+jJDsLH79d+wUeNtl+MXo78zw7lHl9rLb3+Y6L9OIgX15PBIYZx mQ1oAXAhWK8I8NfpmdHkKSQMQ0Ls8stwVpJGvGUosIyU2COTuJfFWH0GQl8X0jSN eNHdpthhoYHjwwlvxeGUcUJ11zbP55zCDJLxxCDOZ8lZVf9qqIk4wpquMGXoLpyQ xIgqG/FTMlNJG5hAR3cJfFCDfjKw/Sza5Uc/BGvnRILxrqELbzgSsluL3AarpgIn TWyx1Wc8N9Z8vA9XRaDPwh5Xv9uACw6DA37XfVeBoWCht6It0w2UyefjxWMhwmgL GwS6/Ul6uf5jXGWlkuAlBt4t3XMynBzZJ5GXBLPzTDGzHGgRsi1jmtxOGoxXqv1O xIXAkOjup5hLYG0B2Zz7yCottBaD9wBULIlFA1ixmNXHwxiaU95v+3jlAaOjYT1A +QjYr6KZMM/6Jqz5gWhupRghxHx0g1m/xCEexPy4hSm4+g6TTFOx3eVj1sqUd/bV TnbPUje3lKyXBhmfDNoFOi1VSpe8/PASakqkQqysN30zyXm76rhBrX9X5+DjQb8w yeaYWZMAEripWGqGC9SyEadAV98elfJO/V/iK1AFZo62Ukl8VZ1OUz50P+8Brvvg XTG8cvZYF4XTRtGTzQbDghJeyprGB0XY5SS08tms/DiPDklR9bJWzHu3lIh2EIfT YXBWyKowZ50ih1yi4qXrABJickVLq6SKZ+Mg5X8Q/njxt+Hw9i2Cll7uoHVmGkeY Nmoy8c3Qa3XTvGnzoMWYLKNz6oTyt/EJihHXkwzZKLgL5tOXnywwpemkTX1XXbIH OBzz+m6MsWGbE1e57vCsP/c4zzEZ3dM6541WzZOzNdh61uS/XuHWXaBoyKKOH7BN rXBa5BtDmU9EBQG3eBbl1Juj41mtRtUjSXu5VSWy18mKWtbvsFUC/WTFTF/8+kzd 1Sop8Oy35lb/BHYR+B8IOM/MHIIN+xhmJHlhh3Jah6hsADyRGgocQWceSJX8OQuw JclveZ8CzSsttW7cB5hlnUp/bRrm99W9cHLda62Q3K1i3l7IyNx43IYFY+yDgnFB crkFUizItIhYdeB//9NwcIZtM6B+3uDU/zeORhZ83FZnFdcI3u6Lfy4NWRPbecAq wVkSu6h5xkc6E0mFGbB2228lpOQX6TZBQKP4XeQas1GHMuatFtQy+NCOdzYnW9eT qTyU+Oku3wAt4uMxIZubZaGAd06WUg5r7L/HmcEavdkT0DHOvzYGTK0yNrlap0yp jbCPMLXtnXT2HR1jvE483fcfBu3L7+b9R5Tz/VbqV0bEswkmjOHsz+3qUxOL3EFv 9VWrC6Q7aGm93dxAhzek1C+/UQaN9LO9jC+V9gMftJxVfwNt7syv36fV1ww/IyFX G6HBCeyQ0KBT01/UWSI9JCKL0VP44UamvRos7+8MESuTD/BDHJlEQNxL7Teyw2Tf xqUcjGzHevqjWJjdEelfBUl/sq8LcqfedDvzJo07sN3lqcZBdGiiyOOqnfUiJdU2 fuOy3PljALpjvIzgqV1Y6jDiszhl7fwRn91KkWLejZiu/vx0UcwCnWQ1aPMnOydp F12MSEqgIHpebxJFOv3tYPoUEKqGX1iATlsvTPxRD6RufGnm+pDQoKGf8qn8jfjx Q666NYVNysJtmN15PxUAjEiJuKhXuLmtwsuhxsaSxpi+M7/k9vmtkliX4+N+XK7H 7VreF04kgzZN0qdzht2AGhoehDqdotkYb5aXqTlEzuv4MF4zVOZQHDQ6A7M6n0Z4 RgQbhngngdMKHT5brKZm7Atlm1zOtAAexEA+ib6WfaJ0tRAQqJcsKhc7OnYXp8SU D+cqJqvdRIEQTNuMrwzEBiW9RldYeEsgUBYMoyX4HxQ= scala-parser-combinators-1.0.3/build.sbt000066400000000000000000000015351244342316100202230ustar00rootroot00000000000000import com.typesafe.tools.mima.plugin.{MimaPlugin, MimaKeys} scalaModuleSettings name := "scala-parser-combinators" version := "1.0.3-SNAPSHOT" scalaVersion := "2.11.2" snapshotScalaBinaryVersion := "2.11" // important!! must come here (why?) scalaModuleOsgiSettings OsgiKeys.exportPackage := Seq(s"scala.util.parsing.*;version=${version.value}") // needed to fix classloader issues (see scala-xml#20) fork in Test := true libraryDependencies += "junit" % "junit" % "4.11" % "test" libraryDependencies += "com.novocode" % "junit-interface" % "0.10" % "test" MimaPlugin.mimaDefaultSettings MimaKeys.previousArtifact := Some(organization.value % s"${name.value}_2.11" % "1.0.2") // run mima during tests test in Test := { MimaKeys.reportBinaryIssues.value (test in Test).value } scala-parser-combinators-1.0.3/project/000077500000000000000000000000001244342316100200545ustar00rootroot00000000000000scala-parser-combinators-1.0.3/project/build.properties000066400000000000000000000000231244342316100232640ustar00rootroot00000000000000sbt.version=0.13.7 scala-parser-combinators-1.0.3/project/plugins.sbt000066400000000000000000000002051244342316100222440ustar00rootroot00000000000000addSbtPlugin("org.scala-lang.modules" % "scala-module-plugin" % "1.0.2") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.6") scala-parser-combinators-1.0.3/sensitive.sbt.enc000066400000000000000000000006371244342316100217030ustar00rootroot00000000000000U2FsdGVkX19ymDGvEeR3Ld7K7e4jzUoCqTw/KfHdPdtZbPDpAc1txKP1i2y8T6hO y4QJTEhmKXsIJEnDTjyM0wEzh/yYjdE6fGNF43cW4ysSeSEBPy104gNhQXKsyohH JIb0suQ288cP8kZ9IBq/osXkWU0qe+++PJNMeUATaU+ek/z9f/YfvcWZ2jJIKvIk aRMYX/Tpkm70ap9Ko9bdDsgV0/OrPnWT7It0ITIK4P7uj+Yyl9AYBRMT1sk0vqfX oiArljvbeswaS+Ydll4u+kp/hgPMbE1IeYtmey2m9ls6FyLn+D9AfEIpUKg011K2 kVEU678T3LqTqzJvvYhRfDR+KNw/n4l1EPj/JTubMx4qZLmDkoE69o19/lNffrCj 6B1nj4/2VU79kG+XpXDXEw== scala-parser-combinators-1.0.3/src/000077500000000000000000000000001244342316100171755ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/000077500000000000000000000000001244342316100201215ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/000077500000000000000000000000001244342316100212045ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/000077500000000000000000000000001244342316100222675ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/000077500000000000000000000000001244342316100232445ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/000077500000000000000000000000001244342316100247075ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/000077500000000000000000000000001244342316100270445ustar00rootroot00000000000000ImplicitConversions.scala000066400000000000000000000042701244342316100340000ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.combinator import scala.language.implicitConversions /** This object contains implicit conversions that come in handy when using the `^^` combinator. * * Refer to [[scala.util.parsing.combinator.Parsers]] to construct an AST from the concrete syntax. * * The reason for this is that the sequential composition combinator (`~`) combines its constituents * into a ~. When several `~`s are combined, this results in nested `~`s (to the left). * The `flatten*` coercions makes it easy to apply an `n`-argument function to a nested `~` of * depth `n-1` * * The `headOptionTailToFunList` converts a function that takes a `List[A]` to a function that * accepts a `~[A, Option[List[A]]]` (this happens when parsing something of the following * shape: `p ~ opt("." ~ repsep(p, "."))` -- where `p` is a parser that yields an `A`). * * @author Martin Odersky * @author Iulian Dragos * @author Adriaan Moors */ trait ImplicitConversions { self: Parsers => implicit def flatten2[A, B, C] (f: (A, B) => C) = (p: ~[A, B]) => p match {case a ~ b => f(a, b)} implicit def flatten3[A, B, C, D] (f: (A, B, C) => D) = (p: ~[~[A, B], C]) => p match {case a ~ b ~ c => f(a, b, c)} implicit def flatten4[A, B, C, D, E] (f: (A, B, C, D) => E) = (p: ~[~[~[A, B], C], D]) => p match {case a ~ b ~ c ~ d => f(a, b, c, d)} implicit def flatten5[A, B, C, D, E, F](f: (A, B, C, D, E) => F) = (p: ~[~[~[~[A, B], C], D], E]) => p match {case a ~ b ~ c ~ d ~ e=> f(a, b, c, d, e)} implicit def headOptionTailToFunList[A, T] (f: List[A] => T)= (p: ~[A, Option[List[A]]]) => f(p._1 :: (p._2 match { case Some(xs) => xs case None => Nil})) } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/JavaTokenParsers.scala000066400000000000000000000052471244342316100333030ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.combinator import scala.annotation.migration /** `JavaTokenParsers` differs from [[scala.util.parsing.combinator.RegexParsers]] * by adding the following definitions: * * - `ident` * - `wholeNumber` * - `decimalNumber` * - `stringLiteral` * - `floatingPointNumber` */ trait JavaTokenParsers extends RegexParsers { /** Anything that is a valid Java identifier, according to * The Java Language Spec. * Generally, this means a letter, followed by zero or more letters or numbers. */ def ident: Parser[String] = """\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*""".r /** An integer, without sign or with a negative sign. */ def wholeNumber: Parser[String] = """-?\d+""".r /** Number following one of these rules: * * - An integer. For example: `13` * - An integer followed by a decimal point. For example: `3.` * - An integer followed by a decimal point and fractional part. For example: `3.14` * - A decimal point followed by a fractional part. For example: `.1` */ def decimalNumber: Parser[String] = """(\d+(\.\d*)?|\d*\.\d+)""".r /** Double quotes (`"`) enclosing a sequence of: * * - Any character except double quotes, control characters or backslash (`\`) * - A backslash followed by another backslash, a single or double quote, or one * of the letters `b`, `f`, `n`, `r` or `t` * - `\` followed by `u` followed by four hexadecimal digits */ @migration("`stringLiteral` allows escaping single and double quotes, but not forward slashes any longer.", "2.10.0") def stringLiteral: Parser[String] = ("\""+"""([^"\p{Cntrl}\\]|\\[\\'"bfnrt]|\\u[a-fA-F0-9]{4})*+"""+"\"").r /** A number following the rules of `decimalNumber`, with the following * optional additions: * * - Preceded by a negative sign * - Followed by `e` or `E` and an optionally signed integer * - Followed by `f`, `f`, `d` or `D` (after the above rule, if both are used) */ def floatingPointNumber: Parser[String] = """-?(\d+(\.\d*)?|\d*\.\d+)([eE][+-]?\d+)?[fFdD]?""".r } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/PackratParsers.scala000066400000000000000000000300051244342316100327740ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.combinator import scala.util.parsing.input.{ Reader, Position } import scala.collection.mutable import scala.language.implicitConversions /** * `PackratParsers` is a component that extends the parser combinators * provided by [[scala.util.parsing.combinator.Parsers]] with a memoization * facility (''Packrat Parsing''). * * Packrat Parsing is a technique for implementing backtracking, * recursive-descent parsers, with the advantage that it guarantees * unlimited lookahead and a linear parse time. Using this technique, * left recursive grammars can also be accepted. * * Using `PackratParsers` is very similar to using `Parsers`: * - any class/trait that extends `Parsers` (directly or through a subclass) * can mix in `PackratParsers`. * Example: `'''object''' MyGrammar '''extends''' StandardTokenParsers '''with''' PackratParsers` * - each grammar production previously declared as a `def` without formal * parameters becomes a `lazy val`, and its type is changed from * `Parser[Elem]` to `PackratParser[Elem]`. * So, for example, `'''def''' production: Parser[Int] = {...}` * becomes `'''lazy val''' production: PackratParser[Int] = {...}` * - Important: using `PackratParser`s is not an ''all or nothing'' decision. * They can be free mixed with regular `Parser`s in a single grammar. * * Cached parse results are attached to the ''input'', not the grammar. * Therefore, `PackratsParser`s require a `PackratReader` as input, which * adds memoization to an underlying `Reader`. Programmers can create * `PackratReader` objects either manually, as in * `production('''new''' PackratReader('''new''' lexical.Scanner("input")))`, * but the common way should be to rely on the combinator `phrase` to wrap * a given input with a `PackratReader` if the input is not one itself. * * @see Bryan Ford: "Packrat Parsing: Simple, Powerful, Lazy, Linear Time." ICFP'02 * @see Alessandro Warth, James R. Douglass, Todd Millstein: "Packrat Parsers Can Support Left Recursion." PEPM'08 * * @since 2.8 * @author Manohar Jonnalagedda * @author Tiark Rompf */ trait PackratParsers extends Parsers { //type Input = PackratReader[Elem] /** * A specialized `Reader` class that wraps an underlying `Reader` * and provides memoization of parse results. */ class PackratReader[+T](underlying: Reader[T]) extends Reader[T] { outer => /* * caching of intermediate parse results and information about recursion */ private[PackratParsers] val cache = mutable.HashMap.empty[(Parser[_], Position), MemoEntry[_]] private[PackratParsers] def getFromCache[T](p: Parser[T]): Option[MemoEntry[T]] = { cache.get((p, pos)).asInstanceOf[Option[MemoEntry[T]]] } private[PackratParsers] def updateCacheAndGet[T](p: Parser[T], w: MemoEntry[T]): MemoEntry[T] = { cache.put((p, pos),w) w } /* a cache for storing parser heads: allows to know which parser is involved in a recursion*/ private[PackratParsers] val recursionHeads: mutable.HashMap[Position, Head] = mutable.HashMap.empty //a stack that keeps a list of all involved rules private[PackratParsers] var lrStack: List[LR] = Nil override def source: java.lang.CharSequence = underlying.source override def offset: Int = underlying.offset def first: T = underlying.first def rest: Reader[T] = new PackratReader(underlying.rest) { override private[PackratParsers] val cache = outer.cache override private[PackratParsers] val recursionHeads = outer.recursionHeads lrStack = outer.lrStack } def pos: Position = underlying.pos def atEnd: Boolean = underlying.atEnd } /** * A parser generator delimiting whole phrases (i.e. programs). * * Overridden to make sure any input passed to the argument parser * is wrapped in a `PackratReader`. */ override def phrase[T](p: Parser[T]) = { val q = super.phrase(p) new PackratParser[T] { def apply(in: Input) = in match { case in: PackratReader[_] => q(in) case in => q(new PackratReader(in)) } } } private def getPosFromResult(r: ParseResult[_]): Position = r.next.pos // auxiliary data structures private case class MemoEntry[+T](var r: Either[LR,ParseResult[_]]){ def getResult: ParseResult[T] = r match { case Left(LR(res,_,_)) => res.asInstanceOf[ParseResult[T]] case Right(res) => res.asInstanceOf[ParseResult[T]] } } private case class LR(var seed: ParseResult[_], var rule: Parser[_], var head: Option[Head]){ def getPos: Position = getPosFromResult(seed) } private case class Head(var headParser: Parser[_], var involvedSet: List[Parser[_]], var evalSet: List[Parser[_]]){ def getHead = headParser } /** * The root class of packrat parsers. */ abstract class PackratParser[+T] extends super.Parser[T] /** * Implicitly convert a parser to a packrat parser. * The conversion is triggered by giving the appropriate target type: * {{{ * val myParser: PackratParser[MyResult] = aParser * }}} */ implicit def parser2packrat[T](p: => super.Parser[T]): PackratParser[T] = { lazy val q = p memo(super.Parser {in => q(in)}) } /* * An unspecified function that is called when a packrat reader is applied. * It verifies whether we are in the process of growing a parse or not. * In the former case, it makes sure that rules involved in the recursion are evaluated. * It also prevents non-involved rules from getting evaluated further */ private def recall(p: super.Parser[_], in: PackratReader[Elem]): Option[MemoEntry[_]] = { val cached = in.getFromCache(p) val head = in.recursionHeads.get(in.pos) head match { case None => /*no heads*/ cached case Some(h@Head(hp, involved, evalSet)) => { //heads found if(cached == None && !(hp::involved contains p)) { //Nothing in the cache, and p is not involved return Some(MemoEntry(Right(Failure("dummy ",in)))) } if(evalSet contains p){ //something in cache, and p is in the evalSet //remove the rule from the evalSet of the Head h.evalSet = h.evalSet.filterNot(_==p) val tempRes = p(in) //we know that cached has an entry here val tempEntry: MemoEntry[_] = cached.get // match {case Some(x: MemoEntry[_]) => x} //cache is modified tempEntry.r = Right(tempRes) } cached } } } /* * setting up the left-recursion. We have the LR for the rule head * we modify the involvedSets of all LRs in the stack, till we see * the current parser again */ private def setupLR(p: Parser[_], in: PackratReader[_], recDetect: LR): Unit = { if(recDetect.head == None) recDetect.head = Some(Head(p, Nil, Nil)) in.lrStack.takeWhile(_.rule != p).foreach {x => x.head = recDetect.head recDetect.head.map(h => h.involvedSet = x.rule::h.involvedSet) } } /* * growing, if needed the recursion * check whether the parser we are growing is the head of the rule. * Not => no grow */ /* * Once the result of the recall function is known, if it is nil, then we need to store a dummy failure into the cache (much like in the previous listings) and compute the future parse. If it is not, however, this means we have detected a recursion, and we use the setupLR function to update each parser involved in the recursion. */ private def lrAnswer[T](p: Parser[T], in: PackratReader[Elem], growable: LR): ParseResult[T] = growable match { //growable will always be having a head, we can't enter lrAnswer otherwise case LR(seed ,rule, Some(head)) => if(head.getHead != p) /*not head rule, so not growing*/ seed.asInstanceOf[ParseResult[T]] else { in.updateCacheAndGet(p, MemoEntry(Right[LR, ParseResult[T]](seed.asInstanceOf[ParseResult[T]]))) seed match { case f@Failure(_,_) => f case e@Error(_,_) => e case s@Success(_,_) => /*growing*/ grow(p, in, head) } } case _=> throw new Exception("lrAnswer with no head !!") } //p here should be strict (cannot be non-strict) !! //failing left-recursive grammars: This is done by simply storing a failure if nothing is found /** * Explicitly convert a given parser to a memoizing packrat parser. * In most cases, client code should avoid calling `memo` directly * and rely on implicit conversion instead. */ def memo[T](p: super.Parser[T]): PackratParser[T] = { new PackratParser[T] { def apply(in: Input) = { /* * transformed reader */ val inMem = in.asInstanceOf[PackratReader[Elem]] //look in the global cache if in a recursion val m = recall(p, inMem) m match { //nothing has been done due to recall case None => val base = LR(Failure("Base Failure",in), p, None) inMem.lrStack = base::inMem.lrStack //cache base result inMem.updateCacheAndGet(p,MemoEntry(Left(base))) //parse the input val tempRes = p(in) //the base variable has passed equality tests with the cache inMem.lrStack = inMem.lrStack.tail //check whether base has changed, if yes, we will have a head base.head match { case None => /*simple result*/ inMem.updateCacheAndGet(p,MemoEntry(Right(tempRes))) tempRes case s@Some(_) => /*non simple result*/ base.seed = tempRes //the base variable has passed equality tests with the cache val res = lrAnswer(p, inMem, base) res } case Some(mEntry) => { //entry found in cache mEntry match { case MemoEntry(Left(recDetect)) => { setupLR(p, inMem, recDetect) //all setupLR does is change the heads of the recursions, so the seed will stay the same recDetect match {case LR(seed, _, _) => seed.asInstanceOf[ParseResult[T]]} } case MemoEntry(Right(res: ParseResult[_])) => res.asInstanceOf[ParseResult[T]] } } } } } } private def grow[T](p: super.Parser[T], rest: PackratReader[Elem], head: Head): ParseResult[T] = { //store the head into the recursionHeads rest.recursionHeads.put(rest.pos, head /*match {case Head(hp,involved,_) => Head(hp,involved,involved)}*/) val oldRes: ParseResult[T] = rest.getFromCache(p).get match { case MemoEntry(Right(x)) => x.asInstanceOf[ParseResult[T]] case _ => throw new Exception("impossible match") } //resetting the evalSet of the head of the recursion at each beginning of growth head.evalSet = head.involvedSet val tempRes = p(rest); tempRes match { case s@Success(_,_) => if(getPosFromResult(oldRes) < getPosFromResult(tempRes)) { rest.updateCacheAndGet(p, MemoEntry(Right(s))) grow(p, rest, head) } else { //we're done with growing, we can remove data from recursion head rest.recursionHeads -= rest.pos rest.getFromCache(p).get match { case MemoEntry(Right(x: ParseResult[_])) => x.asInstanceOf[ParseResult[T]] case _ => throw new Exception("impossible match") } } case f => rest.recursionHeads -= rest.pos /*rest.updateCacheAndGet(p, MemoEntry(Right(f)));*/oldRes } } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/Parsers.scala000066400000000000000000001201271244342316100314730ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.combinator import scala.util.parsing.input._ import scala.collection.mutable.ListBuffer import scala.annotation.tailrec import scala.annotation.migration import scala.language.implicitConversions import scala.util.DynamicVariable // TODO: better error handling (labelling like parsec's ) /** `Parsers` is a component that ''provides'' generic parser combinators. * * There are two abstract members that must be defined in order to * produce parsers: the type `Elem` and * [[scala.util.parsing.combinator.Parsers.Parser]]. There are helper * methods that produce concrete `Parser` implementations -- see ''primitive * parser'' below. * * A `Parsers` may define multiple `Parser` instances, which are combined * to produced the desired parser. * * The type of the elements these parsers should parse must be defined * by declaring `Elem` * (each parser is polymorphic in the type of result it produces). * * There are two aspects to the result of a parser: * 1. success or failure * 1. the result. * * A [[scala.util.parsing.combinator.Parsers.Parser]] produces both kinds of information, * by returning a [[scala.util.parsing.combinator.Parsers.ParseResult]] when its `apply` * method is called on an input. * * The term ''parser combinator'' refers to the fact that these parsers * are constructed from primitive parsers and composition operators, such * as sequencing, alternation, optionality, repetition, lifting, and so on. For example, * given `p1` and `p2` of type [[scala.util.parsing.combinator.Parsers.Parser]]: * * {{{ * p1 ~ p2 // sequencing: must match p1 followed by p2 * p1 | p2 // alternation: must match either p1 or p2, with preference given to p1 * p1.? // optionality: may match p1 or not * p1.* // repetition: matches any number of repetitions of p1 * }}} * * These combinators are provided as methods on [[scala.util.parsing.combinator.Parsers.Parser]], * or as methods taking one or more `Parsers` and returning a `Parser` provided in * this class. * * A ''primitive parser'' is a parser that accepts or rejects a single * piece of input, based on a certain criterion, such as whether the * input... * - is equal to some given object (see method `accept`), * - satisfies a certain predicate (see method `acceptIf`), * - is in the domain of a given partial function (see method `acceptMatch`) * - or other conditions, by using one of the other methods available, or subclassing `Parser` * * Even more primitive parsers always produce the same result, irrespective of the input. See * methods `success`, `err` and `failure` as examples. * * @see [[scala.util.parsing.combinator.RegexParsers]] and other known subclasses for practical examples. * * @author Martin Odersky * @author Iulian Dragos * @author Adriaan Moors */ trait Parsers { /** the type of input elements the provided parsers consume (When consuming * invidual characters, a parser is typically called a ''scanner'', which * produces ''tokens'' that are consumed by what is normally called a ''parser''. * Nonetheless, the same principles apply, regardless of the input type.) */ type Elem /** The parser input is an abstract reader of input elements, i.e. the type * of input the parsers in this component expect. */ type Input = Reader[Elem] /** A base class for parser results. A result is either successful or not * (failure may be fatal, i.e., an Error, or not, i.e., a Failure). On * success, provides a result of type `T` which consists of some result * (and the rest of the input). */ sealed abstract class ParseResult[+T] { /** Functional composition of ParseResults. * * @param f the function to be lifted over this result * @return `f` applied to the result of this `ParseResult`, packaged up as a new `ParseResult` */ def map[U](f: T => U): ParseResult[U] /** Partial functional composition of ParseResults. * * @param f the partial function to be lifted over this result * @param error a function that takes the same argument as `f` and * produces an error message to explain why `f` wasn't applicable * (it is called when this is the case) * @return if `f` f is defined at the result in this `ParseResult`, `f` * applied to the result of this `ParseResult`, packaged up as * a new `ParseResult`. If `f` is not defined, `Failure`. */ def mapPartial[U](f: PartialFunction[T, U], error: T => String): ParseResult[U] def flatMapWithNext[U](f: T => Input => ParseResult[U]): ParseResult[U] def filterWithError(p: T => Boolean, error: T => String, position: Input): ParseResult[T] def append[U >: T](a: => ParseResult[U]): ParseResult[U] def isEmpty = !successful /** Returns the embedded result. */ def get: T def getOrElse[B >: T](default: => B): B = if (isEmpty) default else this.get val next: Input val successful: Boolean } /** The success case of `ParseResult`: contains the result and the remaining input. * * @param result The parser's output * @param next The parser's remaining input */ case class Success[+T](result: T, override val next: Input) extends ParseResult[T] { def map[U](f: T => U) = Success(f(result), next) def mapPartial[U](f: PartialFunction[T, U], error: T => String): ParseResult[U] = if(f.isDefinedAt(result)) Success(f(result), next) else Failure(error(result), next) def flatMapWithNext[U](f: T => Input => ParseResult[U]): ParseResult[U] = f(result)(next) def filterWithError(p: T => Boolean, error: T => String, position: Input): ParseResult[T] = if (p(result)) this else Failure(error(result), position) def append[U >: T](a: => ParseResult[U]): ParseResult[U] = this def get: T = result /** The toString method of a Success. */ override def toString = "["+next.pos+"] parsed: "+result val successful = true } private lazy val lastNoSuccessVar = new DynamicVariable[Option[NoSuccess]](None) /** A common super-class for unsuccessful parse results. */ 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 val successful = false if (lastNoSuccessVar.value forall (v => !(next.pos < v.next.pos))) lastNoSuccessVar.value = Some(this) def map[U](f: Nothing => U) = this def mapPartial[U](f: PartialFunction[Nothing, U], error: Nothing => String): ParseResult[U] = this def flatMapWithNext[U](f: Nothing => Input => ParseResult[U]): ParseResult[U] = this def filterWithError(p: Nothing => Boolean, error: Nothing => String, position: Input): ParseResult[Nothing] = this def get: Nothing = scala.sys.error("No result when parsing failed") } /** An extractor so `NoSuccess(msg, next)` can be used in matches. */ object NoSuccess { def unapply[T](x: ParseResult[T]) = x match { case Failure(msg, next) => Some((msg, next)) case Error(msg, next) => Some((msg, next)) case _ => None } } /** The failure case of `ParseResult`: contains an error-message and the remaining input. * Parsing will back-track when a failure occurs. * * @param msg An error message string describing the failure. * @param next The parser's unconsumed input at the point where the failure occurred. */ case class Failure(override val msg: String, override val next: Input) extends NoSuccess(msg, next) { /** The toString method of a Failure yields an error message. */ override def toString = "["+next.pos+"] failure: "+msg+"\n\n"+next.pos.longString def append[U >: Nothing](a: => ParseResult[U]): ParseResult[U] = { val alt = a; alt match { case Success(_, _) => alt case ns: NoSuccess => if (alt.next.pos < next.pos) this else alt }} } /** The fatal failure case of ParseResult: contains an error-message and * the remaining input. * No back-tracking is done when a parser returns an `Error`. * * @param msg An error message string describing the error. * @param next The parser's unconsumed input at the point where the error occurred. */ case class Error(override val msg: String, override val next: Input) extends NoSuccess(msg, next) { /** The toString method of an Error yields an error message. */ override def toString = "["+next.pos+"] error: "+msg+"\n\n"+next.pos.longString def append[U >: Nothing](a: => ParseResult[U]): ParseResult[U] = this } def Parser[T](f: Input => ParseResult[T]): Parser[T] = new Parser[T]{ def apply(in: Input) = f(in) } def OnceParser[T](f: Input => ParseResult[T]): Parser[T] with OnceParser[T] = new Parser[T] with OnceParser[T] { def apply(in: Input) = f(in) } /** The root class of parsers. * Parsers are functions from the Input type to ParseResult. */ abstract class Parser[+T] extends (Input => ParseResult[T]) { private var name: String = "" def named(n: String): this.type = {name=n; this} override def toString() = "Parser ("+ name +")" /** An unspecified method that defines the behaviour of this parser. */ def apply(in: Input): ParseResult[T] def flatMap[U](f: T => Parser[U]): Parser[U] = Parser{ in => this(in) flatMapWithNext(f)} def map[U](f: T => U): Parser[U] //= flatMap{x => success(f(x))} = Parser{ in => this(in) map(f)} def filter(p: T => Boolean): Parser[T] = withFilter(p) def withFilter(p: T => Boolean): Parser[T] = Parser{ in => this(in) filterWithError(p, "Input doesn't match filter: "+_, in)} // no filter yet, dealing with zero is tricky! @migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0") def append[U >: T](p0: => Parser[U]): Parser[U] = { lazy val p = p0 // lazy argument Parser{ in => this(in) append p(in)} } // the operator formerly known as +++, ++, &, but now, behold the venerable ~ // it's short, light (looks like whitespace), has few overloaded meaning (thanks to the recent change from ~ to unary_~) // and we love it! (or do we like `,` better?) /** A parser combinator for sequential composition. * * `p ~ q` succeeds if `p` succeeds and `q` succeeds on the input left over by `p`. * * @param q a parser that will be executed after `p` (this parser) * succeeds -- evaluated at most once, and only when necessary. * @return a `Parser` that -- on success -- returns a `~` (like a `Pair`, * but easier to pattern match on) that contains the result of `p` and * that of `q`. The resulting parser fails if either `p` or `q` fails. */ @migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0") def ~ [U](q: => Parser[U]): Parser[~[T, U]] = { lazy val p = q // lazy argument (for(a <- this; b <- p) yield new ~(a,b)).named("~") } /** A parser combinator for sequential composition which keeps only the right result. * * `p ~> q` succeeds if `p` succeeds and `q` succeeds on the input left over by `p`. * * @param q a parser that will be executed after `p` (this parser) * succeeds -- evaluated at most once, and only when necessary. * @return a `Parser` that -- on success -- returns the result of `q`. */ @migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0") def ~> [U](q: => Parser[U]): Parser[U] = { lazy val p = q // lazy argument (for(a <- this; b <- p) yield b).named("~>") } /** A parser combinator for sequential composition which keeps only the left result. * * `p <~ q` succeeds if `p` succeeds and `q` succeeds on the input * left over by `p`. * * @note <~ has lower operator precedence than ~ or ~>. * * @param q a parser that will be executed after `p` (this parser) succeeds -- evaluated at most once, and only when necessary * @return a `Parser` that -- on success -- returns the result of `p`. */ @migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0") def <~ [U](q: => Parser[U]): Parser[T] = { lazy val p = q // lazy argument (for(a <- this; b <- p) yield a).named("<~") } /* not really useful: V cannot be inferred because Parser is covariant in first type parameter (V is always trivially Nothing) def ~~ [U, V](q: => Parser[U])(implicit combine: (T, U) => V): Parser[V] = new Parser[V] { def apply(in: Input) = seq(Parser.this, q)((x, y) => combine(x,y))(in) } */ /** A parser combinator for non-back-tracking sequential composition. * * `p ~! q` succeeds if `p` succeeds and `q` succeeds on the input left over by `p`. * In case of failure, no back-tracking is performed (in an earlier parser produced by the `|` combinator). * * @param p a parser that will be executed after `p` (this parser) succeeds * @return a `Parser` that -- on success -- returns a `~` (like a Pair, but easier to pattern match on) * that contains the result of `p` and that of `q`. * The resulting parser fails if either `p` or `q` fails, this failure is fatal. */ def ~! [U](p: => Parser[U]): Parser[~[T, U]] = OnceParser{ (for(a <- this; b <- commit(p)) yield new ~(a,b)).named("~!") } /** A parser combinator for alternative composition. * * `p | q` succeeds if `p` succeeds or `q` succeeds. * Note that `q` is only tried if `p`s failure is non-fatal (i.e., back-tracking is allowed). * * @param q a parser that will be executed if `p` (this parser) fails (and allows back-tracking) * @return a `Parser` that returns the result of the first parser to succeed (out of `p` and `q`) * The resulting parser succeeds if (and only if) * - `p` succeeds, ''or'' * - if `p` fails allowing back-tracking and `q` succeeds. */ def | [U >: T](q: => Parser[U]): Parser[U] = append(q).named("|") // TODO /** A parser combinator for alternative with longest match composition. * * `p ||| q` succeeds if `p` succeeds or `q` succeeds. * If `p` and `q` both succeed, the parser that consumed the most characters accepts. * * @param q0 a parser that accepts if p consumes less characters. -- evaluated at most once, and only when necessary * @return a `Parser` that returns the result of the parser consuming the most characters (out of `p` and `q`). */ @migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0") def ||| [U >: T](q0: => Parser[U]): Parser[U] = new Parser[U] { lazy val q = q0 // lazy argument def apply(in: Input) = { val res1 = Parser.this(in) val res2 = q(in) (res1, res2) match { case (s1 @ Success(_, next1), s2 @ Success(_, next2)) => if (next2.pos < next1.pos) s1 else s2 case (s1 @ Success(_, _), _) => s1 case (_, s2 @ Success(_, _)) => s2 case (e1 @ Error(_, _), _) => e1 case (f1 @ Failure(_, next1), ns2 @ NoSuccess(_, next2)) => if (next2.pos < next1.pos) f1 else ns2 } } override def toString = "|||" } /** A parser combinator for function application. * * `p ^^ f` succeeds if `p` succeeds; it returns `f` applied to the result of `p`. * * @param f a function that will be applied to this parser's result (see `map` in `ParseResult`). * @return a parser that has the same behaviour as the current parser, but whose result is * transformed by `f`. */ def ^^ [U](f: T => U): Parser[U] = map(f).named(toString+"^^") /** A parser combinator that changes a successful result into the specified value. * * `p ^^^ v` succeeds if `p` succeeds; discards its result, and returns `v` instead. * * @param v The new result for the parser, evaluated at most once (if `p` succeeds), not evaluated at all if `p` fails. * @return a parser that has the same behaviour as the current parser, but whose successful result is `v` */ @migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0") def ^^^ [U](v: => U): Parser[U] = new Parser[U] { lazy val v0 = v // lazy argument def apply(in: Input) = Parser.this(in) map (x => v0) }.named(toString+"^^^") /** A parser combinator for partial function application. * * `p ^? (f, error)` succeeds if `p` succeeds AND `f` is defined at the result of `p`; * in that case, it returns `f` applied to the result of `p`. If `f` is not applicable, * error(the result of `p`) should explain why. * * @param f a partial function that will be applied to this parser's result * (see `mapPartial` in `ParseResult`). * @param error a function that takes the same argument as `f` and produces an error message * to explain why `f` wasn't applicable * @return a parser that succeeds if the current parser succeeds and `f` is applicable * to the result. If so, the result will be transformed by `f`. */ def ^? [U](f: PartialFunction[T, U], error: T => String): Parser[U] = Parser{ in => this(in).mapPartial(f, error)}.named(toString+"^?") /** A parser combinator for partial function application. * * `p ^? f` succeeds if `p` succeeds AND `f` is defined at the result of `p`; * in that case, it returns `f` applied to the result of `p`. * * @param f a partial function that will be applied to this parser's result * (see `mapPartial` in `ParseResult`). * @return a parser that succeeds if the current parser succeeds and `f` is applicable * to the result. If so, the result will be transformed by `f`. */ def ^? [U](f: PartialFunction[T, U]): Parser[U] = ^?(f, r => "Constructor function not defined at "+r) /** A parser combinator that parameterizes a subsequent parser with the * result of this one. * * Use this combinator when a parser depends on the result of a previous * parser. `p` should be a function that takes the result from the first * parser and returns the second parser. * * `p into fq` (with `fq` typically `{x => q}`) first applies `p`, and * then, if `p` successfully returned result `r`, applies `fq(r)` to the * rest of the input. * * ''From: G. Hutton. Higher-order functions for parsing. J. Funct. Program., 2(3):323--343, 1992.'' * * @example {{{ * def perlRE = "m" ~> (".".r into (separator => """[^%s]*""".format(separator).r <~ separator)) * }}} * * @param fq a function that, given the result from this parser, returns * the second parser to be applied * @return a parser that succeeds if this parser succeeds (with result `x`) * and if then `fq(x)` succeeds */ def into[U](fq: T => Parser[U]): Parser[U] = flatMap(fq) // shortcuts for combinators: /** Returns `into(fq)`. */ def >>[U](fq: T => Parser[U])=into(fq) /** Returns a parser that repeatedly parses what this parser parses. * * @return rep(this) */ def * = rep(this) /** Returns a parser that repeatedly parses what this parser parses, * interleaved with the `sep` parser. The `sep` parser specifies how * the results parsed by this parser should be combined. * * @return chainl1(this, sep) */ def *[U >: T](sep: => Parser[(U, U) => U]) = chainl1(this, sep) // TODO: improve precedence? a ~ b*(",") = a ~ (b*(",")) should be true /** Returns a parser that repeatedly (at least once) parses what this parser parses. * * @return rep1(this) */ def + = rep1(this) /** Returns a parser that optionally parses what this parser parses. * * @return opt(this) */ def ? = opt(this) /** Changes the failure message produced by a parser. * * This doesn't change the behavior of a parser on neither * success nor error, just on failure. The semantics are * slightly different than those obtained by doing `| failure(msg)`, * in that the message produced by this method will always * replace the message produced, which is not guaranteed * by that idiom. * * For example, parser `p` below will always produce the * designated failure message, while `q` will not produce * it if `sign` is parsed but `number` is not. * * {{{ * def p = sign.? ~ number withFailureMessage "Number expected!" * def q = sign.? ~ number | failure("Number expected!") * }}} * * @param msg The message that will replace the default failure message. * @return A parser with the same properties and different failure message. */ def withFailureMessage(msg: String) = Parser{ in => this(in) match { case Failure(_, next) => Failure(msg, next) case other => other } } /** Changes the error message produced by a parser. * * This doesn't change the behavior of a parser on neither * success nor failure, just on error. The semantics are * slightly different than those obtained by doing `| error(msg)`, * in that the message produced by this method will always * replace the message produced, which is not guaranteed * by that idiom. * * For example, parser `p` below will always produce the * designated error message, while `q` will not produce * it if `sign` is parsed but `number` is not. * * {{{ * def p = sign.? ~ number withErrorMessage "Number expected!" * def q = sign.? ~ number | error("Number expected!") * }}} * * @param msg The message that will replace the default error message. * @return A parser with the same properties and different error message. */ def withErrorMessage(msg: String) = Parser{ in => this(in) match { case Error(_, next) => Error(msg, next) case other => other } } } /** Wrap a parser so that its failures become errors (the `|` combinator * will give up as soon as it encounters an error, on failure it simply * tries the next alternative). */ def commit[T](p: => Parser[T]) = Parser{ in => p(in) match{ case s @ Success(_, _) => s case e @ Error(_, _) => e case f @ Failure(msg, next) => Error(msg, next) } } /** A parser matching input elements that satisfy a given predicate. * * `elem(kind, p)` succeeds if the input starts with an element `e` for which `p(e)` is true. * * @param kind The element kind, used for error messages * @param p A predicate that determines which elements match. * @return */ def elem(kind: String, p: Elem => Boolean) = acceptIf(p)(inEl => kind+" expected") /** A parser that matches only the given element `e`. * * `elem(e)` succeeds if the input starts with an element `e`. * * @param e the `Elem` that must be the next piece of input for the returned parser to succeed * @return a `Parser` that succeeds if `e` is the next available input (and returns it). */ def elem(e: Elem): Parser[Elem] = accept(e) /** A parser that matches only the given element `e`. * * The method is implicit so that elements can automatically be lifted to their parsers. * For example, when parsing `Token`s, `Identifier("new")` (which is a `Token`) can be used directly, * instead of first creating a `Parser` using `accept(Identifier("new"))`. * * @param e the `Elem` that must be the next piece of input for the returned parser to succeed * @return a `tParser` that succeeds if `e` is the next available input. */ implicit def accept(e: Elem): Parser[Elem] = acceptIf(_ == e)("`"+e+"' expected but " + _ + " found") /** A parser that matches only the given list of element `es`. * * `accept(es)` succeeds if the input subsequently provides the elements in the list `es`. * * @param es the list of expected elements * @return a Parser that recognizes a specified list of elements */ def accept[ES <% List[Elem]](es: ES): Parser[List[Elem]] = acceptSeq(es) /** The parser that matches an element in the domain of the partial function `f`. * * If `f` is defined on the first element in the input, `f` is applied * to it to produce this parser's result. * * Example: The parser `accept("name", {case Identifier(n) => Name(n)})` * accepts an `Identifier(n)` and returns a `Name(n)` * * @param expected a description of the kind of element this parser expects (for error messages) * @param f a partial function that determines when this parser is successful and what its output is * @return A parser that succeeds if `f` is applicable to the first element of the input, * applying `f` to it to produce the result. */ def accept[U](expected: String, f: PartialFunction[Elem, U]): Parser[U] = acceptMatch(expected, f) /** A parser matching input elements that satisfy a given predicate. * * `acceptIf(p)(el => "Unexpected "+el)` succeeds if the input starts with an element `e` for which `p(e)` is true. * * @param err A function from the received element into an error message. * @param p A predicate that determines which elements match. * @return A parser for elements satisfying p(e). */ def acceptIf(p: Elem => Boolean)(err: Elem => String): Parser[Elem] = Parser { in => if (in.atEnd) Failure("end of input", in) else if (p(in.first)) Success(in.first, in.rest) else Failure(err(in.first), in) } /** The parser that matches an element in the domain of the partial function `f`. * * If `f` is defined on the first element in the input, `f` is applied * to it to produce this parser's result. * * Example: The parser `acceptMatch("name", {case Identifier(n) => Name(n)})` * accepts an `Identifier(n)` and returns a `Name(n)` * * @param expected a description of the kind of element this parser expects (for error messages) * @param f a partial function that determines when this parser is successful and what its output is * @return A parser that succeeds if `f` is applicable to the first element of the input, * applying `f` to it to produce the result. */ def acceptMatch[U](expected: String, f: PartialFunction[Elem, U]): Parser[U] = Parser{ in => if (in.atEnd) Failure("end of input", in) else if (f.isDefinedAt(in.first)) Success(f(in.first), in.rest) else Failure(expected+" expected", in) } /** A parser that matches only the given [[scala.collection.Iterable]] collection of elements `es`. * * `acceptSeq(es)` succeeds if the input subsequently provides the elements in the iterable `es`. * * @param es the list of expected elements * @return a Parser that recognizes a specified list of elements */ def acceptSeq[ES <% Iterable[Elem]](es: ES): Parser[List[Elem]] = es.foldRight[Parser[List[Elem]]](success(Nil)){(x, pxs) => accept(x) ~ pxs ^^ mkList} /** A parser that always fails. * * @param msg The error message describing the failure. * @return A parser that always fails with the specified error message. */ def failure(msg: String) = Parser{ in => Failure(msg, in) } /** A parser that results in an error. * * @param msg The error message describing the failure. * @return A parser that always fails with the specified error message. */ def err(msg: String) = Parser{ in => Error(msg, in) } /** A parser that always succeeds. * * @param v The result for the parser * @return A parser that always succeeds, with the given result `v` */ def success[T](v: T) = Parser{ in => Success(v, in) } /** A helper method that turns a `Parser` into one that will * print debugging information to stdout before and after * being applied. */ def log[T](p: => Parser[T])(name: String): Parser[T] = Parser{ in => println("trying "+ name +" at "+ in) val r = p(in) println(name +" --> "+ r) r } /** A parser generator for repetitions. * * `rep(p)` repeatedly uses `p` to parse the input until `p` fails * (the result is a List of the consecutive results of `p`). * * @param p a `Parser` that is to be applied successively to the input * @return A parser that returns a list of results produced by repeatedly applying `p` to the input. */ def rep[T](p: => Parser[T]): Parser[List[T]] = rep1(p) | success(List()) /** A parser generator for interleaved repetitions. * * `repsep(p, q)` repeatedly uses `p` interleaved with `q` to parse the input, until `p` fails. * (The result is a `List` of the results of `p`.) * * Example: `repsep(term, ",")` parses a comma-separated list of term's, yielding a list of these terms. * * @param p a `Parser` that is to be applied successively to the input * @param q a `Parser` that parses the elements that separate the elements parsed by `p` * @return A parser that returns a list of results produced by repeatedly applying `p` (interleaved with `q`) to the input. * The results of `p` are collected in a list. The results of `q` are discarded. */ def repsep[T](p: => Parser[T], q: => Parser[Any]): Parser[List[T]] = rep1sep(p, q) | success(List()) /** A parser generator for non-empty repetitions. * * `rep1(p)` repeatedly uses `p` to parse the input until `p` fails -- `p` must succeed at least * once (the result is a `List` of the consecutive results of `p`) * * @param p a `Parser` that is to be applied successively to the input * @return A parser that returns a list of results produced by repeatedly applying `p` to the input * (and that only succeeds if `p` matches at least once). */ def rep1[T](p: => Parser[T]): Parser[List[T]] = rep1(p, p) /** A parser generator for non-empty repetitions. * * `rep1(f, p)` first uses `f` (which must succeed) and then repeatedly * uses `p` to parse the input until `p` fails * (the result is a `List` of the consecutive results of `f` and `p`) * * @param first a `Parser` that parses the first piece of input * @param p0 a `Parser` that is to be applied successively to the rest of the input (if any) -- evaluated at most once, and only when necessary * @return A parser that returns a list of results produced by first applying `f` and then * repeatedly `p` to the input (it only succeeds if `f` matches). */ @migration("The `p0` call-by-name arguments is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0") def rep1[T](first: => Parser[T], p0: => Parser[T]): Parser[List[T]] = Parser { in => lazy val p = p0 // lazy argument val elems = new ListBuffer[T] def continue(in: Input): ParseResult[List[T]] = { val p0 = p // avoid repeatedly re-evaluating by-name parser @tailrec def applyp(in0: Input): ParseResult[List[T]] = p0(in0) match { case Success(x, rest) => elems += x ; applyp(rest) case e @ Error(_, _) => e // still have to propagate error case _ => Success(elems.toList, in0) } applyp(in) } first(in) match { case Success(x, rest) => elems += x ; continue(rest) case ns: NoSuccess => ns } } /** A parser generator for a specified number of repetitions. * * `repN(n, p)` uses `p` exactly `n` time to parse the input * (the result is a `List` of the `n` consecutive results of `p`). * * @param p a `Parser` that is to be applied successively to the input * @param num the exact number of times `p` must succeed * @return A parser that returns a list of results produced by repeatedly applying `p` to the input * (and that only succeeds if `p` matches exactly `n` times). */ def repN[T](num: Int, p: => Parser[T]): Parser[List[T]] = if (num == 0) success(Nil) else Parser { in => val elems = new ListBuffer[T] val p0 = p // avoid repeatedly re-evaluating by-name parser @tailrec def applyp(in0: Input): ParseResult[List[T]] = if (elems.length == num) Success(elems.toList, in0) else p0(in0) match { case Success(x, rest) => elems += x ; applyp(rest) case ns: NoSuccess => ns } applyp(in) } /** A parser generator for non-empty repetitions. * * `rep1sep(p, q)` repeatedly applies `p` interleaved with `q` to parse the * input, until `p` fails. The parser `p` must succeed at least once. * * @param p a `Parser` that is to be applied successively to the input * @param q a `Parser` that parses the elements that separate the elements parsed by `p` * (interleaved with `q`) * @return A parser that returns a list of results produced by repeatedly applying `p` to the input * (and that only succeeds if `p` matches at least once). * The results of `p` are collected in a list. The results of `q` are discarded. */ def rep1sep[T](p : => Parser[T], q : => Parser[Any]): Parser[List[T]] = p ~ rep(q ~> p) ^^ {case x~y => x::y} /** A parser generator that, roughly, generalises the rep1sep generator so * that `q`, which parses the separator, produces a left-associative * function that combines the elements it separates. * * ''From: J. Fokker. Functional parsers. In J. Jeuring and E. Meijer, editors, Advanced Functional Programming, * volume 925 of Lecture Notes in Computer Science, pages 1--23. Springer, 1995.'' * * @param p a parser that parses the elements * @param q a parser that parses the token(s) separating the elements, yielding a left-associative function that * combines two elements into one */ def chainl1[T](p: => Parser[T], q: => Parser[(T, T) => T]): Parser[T] = chainl1(p, p, q) /** A parser generator that, roughly, generalises the `rep1sep` generator * so that `q`, which parses the separator, produces a left-associative * function that combines the elements it separates. * * @param first a parser that parses the first element * @param p a parser that parses the subsequent elements * @param q a parser that parses the token(s) separating the elements, * yielding a left-associative function that combines two elements * into one */ def chainl1[T, U](first: => Parser[T], p: => Parser[U], q: => Parser[(T, U) => T]): Parser[T] = first ~ rep(q ~ p) ^^ { case x ~ xs => xs.foldLeft(x: T){case (a, f ~ b) => f(a, b)} // x's type annotation is needed to deal with changed type inference due to SI-5189 } /** A parser generator that generalises the `rep1sep` generator so that `q`, * which parses the separator, produces a right-associative function that * combines the elements it separates. Additionally, the right-most (last) * element and the left-most combining function have to be supplied. * * rep1sep(p: Parser[T], q) corresponds to chainr1(p, q ^^ cons, cons, Nil) (where val cons = (x: T, y: List[T]) => x :: y) * * @param p a parser that parses the elements * @param q a parser that parses the token(s) separating the elements, yielding a right-associative function that * combines two elements into one * @param combine the "last" (left-most) combination function to be applied * @param first the "first" (right-most) element to be combined */ def chainr1[T, U](p: => Parser[T], q: => Parser[(T, U) => U], combine: (T, U) => U, first: U): Parser[U] = p ~ rep(q ~ p) ^^ { case x ~ xs => (new ~(combine, x) :: xs).foldRight(first){case (f ~ a, b) => f(a, b)} } /** A parser generator for optional sub-phrases. * * `opt(p)` is a parser that returns `Some(x)` if `p` returns `x` and `None` if `p` fails. * * @param p A `Parser` that is tried on the input * @return a `Parser` that always succeeds: either with the result provided by `p` or * with the empty result */ def opt[T](p: => Parser[T]): Parser[Option[T]] = p ^^ (x => Some(x)) | success(None) /** Wrap a parser so that its failures and errors become success and * vice versa -- it never consumes any input. */ def not[T](p: => Parser[T]): Parser[Unit] = Parser { in => p(in) match { case Success(_, _) => Failure("Expected failure", in) case _ => Success((), in) } } /** A parser generator for guard expressions. The resulting parser will * fail or succeed just like the one given as parameter but it will not * consume any input. * * @param p a `Parser` that is to be applied to the input * @return A parser that returns success if and only if `p` succeeds but * never consumes any input */ def guard[T](p: => Parser[T]): Parser[T] = Parser { in => p(in) match{ case s@ Success(s1,_) => Success(s1, in) case e => e } } /** `positioned` decorates a parser's result with the start position of the * input it consumed. * * @param p a `Parser` whose result conforms to `Positional`. * @return A parser that has the same behaviour as `p`, but which marks its * result with the start position of the input it consumed, * if it didn't already have a position. */ def positioned[T <: Positional](p: => Parser[T]): Parser[T] = Parser { in => p(in) match { case Success(t, in1) => Success(if (t.pos == NoPosition) t setPos in.pos else t, in1) case ns: NoSuccess => ns } } /** A parser generator delimiting whole phrases (i.e. programs). * * `phrase(p)` succeeds if `p` succeeds and no input is left over after `p`. * * @param p the parser that must consume all input for the resulting parser * to succeed. * @return a parser that has the same result as `p`, but that only succeeds * if `p` consumed all the input. */ def phrase[T](p: Parser[T]) = new Parser[T] { def apply(in: Input) = lastNoSuccessVar.withValue(None) { p(in) match { case s @ Success(out, in1) => if (in1.atEnd) s else lastNoSuccessVar.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1) case ns => lastNoSuccessVar.value.getOrElse(ns) } } } /** Given a concatenation with a repetition (list), move the concatenated element into the list */ def mkList[T] = (_: ~[T, List[T]]) match { case x ~ xs => x :: xs } /** A wrapper over sequence of matches. * * Given `p1: Parser[A]` and `p2: Parser[B]`, a parser composed with * `p1 ~ p2` will have type `Parser[~[A, B]]`. The successful result * of the parser can be extracted from this case class. * * It also enables pattern matching, so something like this is possible: * * {{{ * def concat(p1: Parser[String], p2: Parser[String]): Parser[String] = * p1 ~ p2 ^^ { case a ~ b => a + b } * }}} */ case class ~[+a, +b](_1: a, _2: b) { override def toString = "("+ _1 +"~"+ _2 +")" } /** A parser whose `~` combinator disallows back-tracking. */ trait OnceParser[+T] extends Parser[T] { override def ~ [U](p: => Parser[U]): Parser[~[T, U]] = OnceParser{ (for(a <- this; b <- commit(p)) yield new ~(a,b)).named("~") } } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala000066400000000000000000000147231244342316100324720ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.combinator import java.util.regex.Pattern import scala.util.matching.Regex import scala.util.parsing.input._ import scala.collection.immutable.PagedSeq import scala.language.implicitConversions /** The ''most important'' differences between `RegexParsers` and * [[scala.util.parsing.combinator.Parsers]] are: * * - `Elem` is defined to be [[scala.Char]] * - There's an implicit conversion from [[java.lang.String]] to `Parser[String]`, * so that string literals can be used as parser combinators. * - There's an implicit conversion from [[scala.util.matching.Regex]] to `Parser[String]`, * so that regex expressions can be used as parser combinators. * - The parsing methods call the method `skipWhitespace` (defaults to `true`) and, if true, * skip any whitespace before each parser is called. * - Protected val `whiteSpace` returns a regex that identifies whitespace. * * For example, this creates a very simple calculator receiving `String` input: * * {{{ * object Calculator extends RegexParsers { * def number: Parser[Double] = """\d+(\.\d*)?""".r ^^ { _.toDouble } * def factor: Parser[Double] = number | "(" ~> expr <~ ")" * def term : Parser[Double] = factor ~ rep( "*" ~ factor | "/" ~ factor) ^^ { * case number ~ list => (number /: list) { * case (x, "*" ~ y) => x * y * case (x, "/" ~ y) => x / y * } * } * def expr : Parser[Double] = term ~ rep("+" ~ log(term)("Plus term") | "-" ~ log(term)("Minus term")) ^^ { * case number ~ list => list.foldLeft(number) { // same as before, using alternate name for /: * case (x, "+" ~ y) => x + y * case (x, "-" ~ y) => x - y * } * } * * def apply(input: String): Double = parseAll(expr, input) match { * case Success(result, _) => result * case failure : NoSuccess => scala.sys.error(failure.msg) * } * } * }}} */ trait RegexParsers extends Parsers { type Elem = Char protected val whiteSpace = """\s+""".r def skipWhitespace = whiteSpace.toString.length > 0 /** Method called to handle whitespace before parsers. * * It checks `skipWhitespace` and, if true, skips anything * matching `whiteSpace` starting from the current offset. * * @param source The input being parsed. * @param offset The offset into `source` from which to match. * @return The offset to be used for the next parser. */ protected def handleWhiteSpace(source: java.lang.CharSequence, offset: Int): Int = if (skipWhitespace) (whiteSpace findPrefixMatchOf (new SubSequence(source, offset))) match { case Some(matched) => offset + matched.end case None => offset } else offset /** A parser that matches a literal string */ implicit def literal(s: String): Parser[String] = new Parser[String] { def apply(in: Input) = { val source = in.source val offset = in.offset val start = handleWhiteSpace(source, offset) var i = 0 var j = start while (i < s.length && j < source.length && s.charAt(i) == source.charAt(j)) { i += 1 j += 1 } if (i == s.length) Success(source.subSequence(start, j).toString, in.drop(j - offset)) else { val found = if (start == source.length()) "end of source" else "`"+source.charAt(start)+"'" Failure("`"+s+"' expected but "+found+" found", in.drop(start - offset)) } } } /** A parser that matches a regex string */ implicit def regex(r: Regex): Parser[String] = new Parser[String] { def apply(in: Input) = { val source = in.source val offset = in.offset val start = handleWhiteSpace(source, offset) (r findPrefixMatchOf (new SubSequence(source, start))) match { case Some(matched) => Success(source.subSequence(start, start + matched.end).toString, in.drop(start + matched.end - offset)) case None => val found = if (start == source.length()) "end of source" else "`"+source.charAt(start)+"'" Failure("string matching regex `"+r+"' expected but "+found+" found", in.drop(start - offset)) } } } /** `positioned` decorates a parser's result with the start position of the input it consumed. * If whitespace is being skipped, then it is skipped before the start position is recorded. * * @param p a `Parser` whose result conforms to `Positional`. * @return A parser that has the same behaviour as `p`, but which marks its result with the * start position of the input it consumed after whitespace has been skipped, if it * didn't already have a position. */ override def positioned[T <: Positional](p: => Parser[T]): Parser[T] = { val pp = super.positioned(p) new Parser[T] { def apply(in: Input) = { val offset = in.offset val start = handleWhiteSpace(in.source, offset) pp(in.drop (start - offset)) } } } override def phrase[T](p: Parser[T]): Parser[T] = super.phrase(p <~ opt("""\z""".r)) /** Parse some prefix of reader `in` with parser `p`. */ def parse[T](p: Parser[T], in: Reader[Char]): ParseResult[T] = p(in) /** Parse some prefix of character sequence `in` with parser `p`. */ def parse[T](p: Parser[T], in: java.lang.CharSequence): ParseResult[T] = p(new CharSequenceReader(in)) /** Parse some prefix of reader `in` with parser `p`. */ def parse[T](p: Parser[T], in: java.io.Reader): ParseResult[T] = p(new PagedSeqReader(PagedSeq.fromReader(in))) /** Parse all of reader `in` with parser `p`. */ def parseAll[T](p: Parser[T], in: Reader[Char]): ParseResult[T] = parse(phrase(p), in) /** Parse all of reader `in` with parser `p`. */ def parseAll[T](p: Parser[T], in: java.io.Reader): ParseResult[T] = parse(phrase(p), in) /** Parse all of character sequence `in` with parser `p`. */ def parseAll[T](p: Parser[T], in: java.lang.CharSequence): ParseResult[T] = parse(phrase(p), in) } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/SubSequence.scala000066400000000000000000000027701244342316100323010ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.combinator // A shallow wrapper over another CharSequence (usually a String) // // See SI-7710: in jdk7u6 String.subSequence stopped sharing the char array of the original // string and began copying it. // RegexParsers calls subSequence twice per input character: that's a lot of array copying! private[combinator] class SubSequence(s: CharSequence, start: Int, val length: Int) extends CharSequence { def this(s: CharSequence, start: Int) = this(s, start, s.length - start) def charAt(i: Int) = if (i >= 0 && i < length) s.charAt(start + i) else throw new IndexOutOfBoundsException(s"index: $i, length: $length") def subSequence(_start: Int, _end: Int) = { if (_start < 0 || _end < 0 || _end > length || _start > _end) throw new IndexOutOfBoundsException(s"start: ${_start}, end: ${_end}, length: $length") new SubSequence(s, start + _start, _end - _start) } override def toString = s.subSequence(start, start + length).toString } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/lexical/000077500000000000000000000000001244342316100304655ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/lexical/Lexical.scala000066400000000000000000000030031244342316100330470ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package lexical import token._ import input.CharArrayReader.EofCh /** This component complements the `Scanners` component with * common operations for lexical parsers. * * Refer to [[scala.util.parsing.combinator.lexical.StdLexical]] * for a concrete implementation for a simple, Scala-like language. * * @author Martin Odersky, Adriaan Moors */ abstract class Lexical extends Scanners with Tokens { /** A character-parser that matches a letter (and returns it).*/ def letter = elem("letter", _.isLetter) /** A character-parser that matches a digit (and returns it).*/ def digit = elem("digit", _.isDigit) /** A character-parser that matches any character except the ones given in `cs` (and returns it).*/ def chrExcept(cs: Char*) = elem("", ch => (cs forall (ch != _))) /** A character-parser that matches a white-space character (and returns it).*/ def whitespaceChar = elem("space char", ch => ch <= ' ' && ch != EofCh) } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/lexical/Scanners.scala000066400000000000000000000046271244342316100332570ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package lexical import input._ /** This component provides core functionality for lexical parsers. * * See its subclasses [[scala.util.parsing.combinator.lexical.Lexical]] and -- most interestingly * [[scala.util.parsing.combinator.lexical.StdLexical]], for more functionality. * * @author Martin Odersky, Adriaan Moors */ trait Scanners extends Parsers { type Elem = Char type Token /** This token is produced by a scanner `Scanner` when scanning failed. */ def errorToken(msg: String): Token /** A parser that produces a token (from a stream of characters). */ def token: Parser[Token] /** A parser for white-space -- its result will be discarded. */ def whitespace: Parser[Any] /** `Scanner` is essentially¹ a parser that produces `Token`s * from a stream of characters. The tokens it produces are typically * passed to parsers in `TokenParsers`. * * @note ¹ `Scanner` is really a `Reader` of `Token`s */ class Scanner(in: Reader[Char]) extends Reader[Token] { /** Convenience constructor (makes a character reader out of the given string) */ def this(in: String) = this(new CharArrayReader(in.toCharArray())) private val (tok, rest1, rest2) = whitespace(in) match { case Success(_, in1) => token(in1) match { case Success(tok, in2) => (tok, in1, in2) case ns: NoSuccess => (errorToken(ns.msg), ns.next, skip(ns.next)) } case ns: NoSuccess => (errorToken(ns.msg), ns.next, skip(ns.next)) } private def skip(in: Reader[Char]) = if (in.atEnd) in else in.rest override def source: java.lang.CharSequence = in.source override def offset: Int = in.offset def first = tok def rest = new Scanner(rest2) def pos = rest1.pos def atEnd = in.atEnd || (whitespace(in) match { case Success(_, in1) => in1.atEnd case _ => false }) } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/lexical/StdLexical.scala000066400000000000000000000073351244342316100335360ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package lexical import token._ import input.CharArrayReader.EofCh import scala.collection.mutable /** This component provides a standard lexical parser for a simple, * [[http://scala-lang.org Scala]]-like language. It parses keywords and * identifiers, numeric literals (integers), strings, and delimiters. * * To distinguish between identifiers and keywords, it uses a set of * reserved identifiers: every string contained in `reserved` is returned * as a keyword token. (Note that `=>` is hard-coded as a keyword.) * Additionally, the kinds of delimiters can be specified by the * `delimiters` set. * * Usually this component is used to break character-based input into * bigger tokens, which are then passed to a token-parser (see * [[scala.util.parsing.combinator.syntactical.TokenParsers]].) * * @author Martin Odersky * @author Iulian Dragos * @author Adriaan Moors */ class StdLexical extends Lexical with StdTokens { // see `token` in `Scanners` def token: Parser[Token] = ( identChar ~ rep( identChar | digit ) ^^ { case first ~ rest => processIdent(first :: rest mkString "") } | digit ~ rep( digit ) ^^ { case first ~ rest => NumericLit(first :: rest mkString "") } | '\'' ~ rep( chrExcept('\'', '\n', EofCh) ) ~ '\'' ^^ { case '\'' ~ chars ~ '\'' => StringLit(chars mkString "") } | '\"' ~ rep( chrExcept('\"', '\n', EofCh) ) ~ '\"' ^^ { case '\"' ~ chars ~ '\"' => StringLit(chars mkString "") } | EofCh ^^^ EOF | '\'' ~> failure("unclosed string literal") | '\"' ~> failure("unclosed string literal") | delim | failure("illegal character") ) /** Returns the legal identifier chars, except digits. */ def identChar = letter | elem('_') // see `whitespace in `Scanners` def whitespace: Parser[Any] = rep[Any]( whitespaceChar | '/' ~ '*' ~ comment | '/' ~ '/' ~ rep( chrExcept(EofCh, '\n') ) | '/' ~ '*' ~ failure("unclosed comment") ) protected def comment: Parser[Any] = ( rep (chrExcept (EofCh, '*')) ~ '*' ~ '/' ^^ { case _ => ' ' } | rep (chrExcept (EofCh, '*')) ~ '*' ~ comment ^^ { case _ => ' ' } ) /** The set of reserved identifiers: these will be returned as `Keyword`s. */ val reserved = new mutable.HashSet[String] /** The set of delimiters (ordering does not matter). */ val delimiters = new mutable.HashSet[String] protected def processIdent(name: String) = if (reserved contains name) Keyword(name) else Identifier(name) private lazy val _delim: Parser[Token] = { // construct parser for delimiters by |'ing together the parsers for the individual delimiters, // starting with the longest one -- otherwise a delimiter D will never be matched if there is // another delimiter that is a prefix of D def parseDelim(s: String): Parser[Token] = accept(s.toList) ^^ { x => Keyword(s) } val d = new Array[String](delimiters.size) delimiters.copyToArray(d, 0) scala.util.Sorting.quickSort(d) (d.toList map parseDelim).foldRight(failure("no matching delimiter"): Parser[Token])((x, y) => y | x) } protected def delim: Parser[Token] = _delim } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/syntactical/000077500000000000000000000000001244342316100313625ustar00rootroot00000000000000StandardTokenParsers.scala000066400000000000000000000025501244342316100364130ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/syntactical/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package syntactical import token._ import lexical.StdLexical import scala.language.implicitConversions /** This component provides primitive parsers for the standard tokens defined in `StdTokens`. * * @author Martin Odersky, Adriaan Moors */ class StandardTokenParsers extends StdTokenParsers { type Tokens = StdTokens val lexical = new StdLexical //an implicit keyword function that gives a warning when a given word is not in the reserved/delimiters list override implicit def keyword(chars : String): Parser[String] = if(lexical.reserved.contains(chars) || lexical.delimiters.contains(chars)) super.keyword(chars) else failure("You are trying to parse \""+chars+"\", but it is neither contained in the delimiters list, nor in the reserved keyword list of your lexical object") } StdTokenParsers.scala000066400000000000000000000035621244342316100354110ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/syntactical/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package syntactical import token._ import scala.collection.mutable import scala.language.implicitConversions /** This component provides primitive parsers for the standard tokens defined in `StdTokens`. * * @author Martin Odersky, Adriaan Moors */ trait StdTokenParsers extends TokenParsers { type Tokens <: StdTokens import lexical.{Keyword, NumericLit, StringLit, Identifier} protected val keywordCache = mutable.HashMap[String, Parser[String]]() /** A parser which matches a single keyword token. * * @param chars The character string making up the matched keyword. * @return a `Parser` that matches the given string */ // implicit def keyword(chars: String): Parser[String] = accept(Keyword(chars)) ^^ (_.chars) implicit def keyword(chars: String): Parser[String] = keywordCache.getOrElseUpdate(chars, accept(Keyword(chars)) ^^ (_.chars)) /** A parser which matches a numeric literal */ def numericLit: Parser[String] = elem("number", _.isInstanceOf[NumericLit]) ^^ (_.chars) /** A parser which matches a string literal */ def stringLit: Parser[String] = elem("string literal", _.isInstanceOf[StringLit]) ^^ (_.chars) /** A parser which matches an identifier */ def ident: Parser[String] = elem("identifier", _.isInstanceOf[Identifier]) ^^ (_.chars) } TokenParsers.scala000066400000000000000000000022211244342316100347250ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/syntactical/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package syntactical /** This is the core component for token-based parsers. * * @author Martin Odersky * @author Adriaan Moors */ trait TokenParsers extends Parsers { /** `Tokens` is the abstract type of the `Token`s consumed by the parsers in this component. */ type Tokens <: token.Tokens /** `lexical` is the component responsible for consuming some basic kind of * input (usually character-based) and turning it into the tokens * understood by these parsers. */ val lexical: Tokens /** The input-type for these parsers*/ type Elem = lexical.Token } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/token/000077500000000000000000000000001244342316100301645ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/token/StdTokens.scala000066400000000000000000000024761244342316100331200ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package token /** This component provides the standard `Token`s for a simple, Scala-like language. * * @author Martin Odersky * @author Adriaan Moors */ trait StdTokens extends Tokens { /** The class of keyword tokens */ case class Keyword(chars: String) extends Token { override def toString = "`"+chars+"'" } /** The class of numeric literal tokens */ case class NumericLit(chars: String) extends Token { override def toString = chars } /** The class of string literal tokens */ case class StringLit(chars: String) extends Token { override def toString = "\""+chars+"\"" } /** The class of identifier tokens */ case class Identifier(chars: String) extends Token { override def toString = "identifier "+chars } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/combinator/token/Tokens.scala000066400000000000000000000030111244342316100324270ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing package combinator package token /** This component provides the notion of `Token`, the unit of information that is passed from lexical * parsers in the `Lexical` component to the parsers in the `TokenParsers` component. * * @author Martin Odersky * @author Adriaan Moors */ trait Tokens { /** Objects of this type are produced by a lexical parser or ``scanner'', and consumed by a parser. * * @see [[scala.util.parsing.combinator.syntactical.TokenParsers]] */ abstract class Token { def chars: String } /** A class of error tokens. Error tokens are used to communicate * errors detected during lexical analysis */ case class ErrorToken(msg: String) extends Token { def chars = "*** error: "+msg } /** A class for end-of-file tokens */ case object EOF extends Token { def chars = "" } /** This token is produced by a scanner `Scanner` when scanning failed. */ def errorToken(msg: String): Token = new ErrorToken(msg) } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/000077500000000000000000000000001244342316100260465ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/CharArrayReader.scala000066400000000000000000000022601244342316100320520ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input /** An object encapsulating basic character constants. * * @author Martin Odersky * @author Adriaan Moors */ object CharArrayReader { final val EofCh = '\u001a' } /** A character array reader reads a stream of characters (keeping track of their positions) * from an array. * * @param chars an array of characters * @param index starting offset into the array; the first element returned will be `source(index)` * * @author Martin Odersky * @author Adriaan Moors */ class CharArrayReader(chars: Array[Char], index: Int) extends CharSequenceReader(chars, index) { def this(chars: Array[Char]) = this(chars, 0) } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/CharSequenceReader.scala000066400000000000000000000043531244342316100325510ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input /** An object encapsulating basic character constants. * * @author Martin Odersky, Adriaan Moors */ object CharSequenceReader { final val EofCh = '\u001a' } /** A character array reader reads a stream of characters (keeping track of their positions) * from an array. * * @param source the source sequence * @param offset starting offset. * * @author Martin Odersky */ class CharSequenceReader(override val source: java.lang.CharSequence, override val offset: Int) extends Reader[Char] { import CharSequenceReader._ /** Construct a `CharSequenceReader` with its first element at * `source(0)` and position `(1,1)`. */ def this(source: java.lang.CharSequence) = this(source, 0) /** Returns the first element of the reader, or EofCh if reader is at its end. */ def first = if (offset < source.length) source.charAt(offset) else EofCh /** Returns a CharSequenceReader consisting of all elements except the first. * * @return If `atEnd` is `true`, the result will be `this`; * otherwise, it's a `CharSequenceReader` containing the rest of input. */ def rest: CharSequenceReader = if (offset < source.length) new CharSequenceReader(source, offset + 1) else this /** The position of the first element in the reader. */ def pos: Position = new OffsetPosition(source, offset) /** true iff there are no more elements in this reader (except for trailing * EofCh's) */ def atEnd = offset >= source.length /** Returns an abstract reader consisting of all elements except the first * `n` elements. */ override def drop(n: Int): CharSequenceReader = new CharSequenceReader(source, offset + n) } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/NoPosition.scala000066400000000000000000000015011244342316100311510ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input /** Undefined position. * * @author Martin Odersky * @author Adriaan Moors */ object NoPosition extends Position { def line = 0 def column = 0 override def toString = "" override def longString = toString def lineContents = "" } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/OffsetPosition.scala000066400000000000000000000051521244342316100320310ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input import scala.collection.mutable.ArrayBuffer /** `OffsetPosition` is a standard class for positions * represented as offsets into a source ``document''. * * @param source The source document * @param offset The offset indicating the position * * @author Martin Odersky */ case class OffsetPosition(source: java.lang.CharSequence, offset: Int) extends Position { /** An index that contains all line starts, including first line, and eof. */ private lazy val index: Array[Int] = { val lineStarts = new ArrayBuffer[Int] lineStarts += 0 for (i <- 0 until source.length) if (source.charAt(i) == '\n') lineStarts += (i + 1) lineStarts += source.length lineStarts.toArray } /** The line number referred to by the position; line numbers start at 1. */ def line: Int = { var lo = 0 var hi = index.length - 1 while (lo + 1 < hi) { val mid = (hi + lo) / 2 if (offset < index(mid)) hi = mid else lo = mid } lo + 1 } /** The column number referred to by the position; column numbers start at 1. */ def column: Int = offset - index(line - 1) + 1 /** The contents of the line numbered at the current offset. * * @return the line at `offset` (not including a newline) */ def lineContents: String = source.subSequence(index(line - 1), index(line)).toString /** Returns a string representation of the `Position`, of the form `line.column`. */ override def toString = line+"."+column /** Compare this position to another, by first comparing their line numbers, * and then -- if necessary -- using the columns to break a tie. * * @param that a `Position` to compare to this `Position` * @return true if this position's line number or (in case of equal line numbers) * column is smaller than the corresponding components of `that` */ override def <(that: Position) = that match { case OffsetPosition(_, that_offset) => this.offset < that_offset case _ => this.line < that.line || this.line == that.line && this.column < that.column } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/PagedSeqReader.scala000066400000000000000000000043741244342316100316770ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input import scala.collection.immutable.PagedSeq /** An object encapsulating basic character constants. * * @author Martin Odersky * @author Adriaan Moors */ object PagedSeqReader { final val EofCh = '\u001a' } /** A character array reader reads a stream of characters (keeping track of their positions) * from an array. * * @param seq the source sequence * @param offset starting offset. * * @author Martin Odersky */ class PagedSeqReader(seq: PagedSeq[Char], override val offset: Int) extends Reader[Char] { import PagedSeqReader._ override lazy val source: java.lang.CharSequence = seq /** Construct a `PagedSeqReader` with its first element at * `source(0)` and position `(1,1)`. */ def this(seq: PagedSeq[Char]) = this(seq, 0) /** Returns the first element of the reader, or EofCh if reader is at its end */ def first = if (seq.isDefinedAt(offset)) seq(offset) else EofCh /** Returns a PagedSeqReader consisting of all elements except the first * * @return If `atEnd` is `true`, the result will be `this`; * otherwise, it's a `PagedSeqReader` containing the rest of input. */ def rest: PagedSeqReader = if (seq.isDefinedAt(offset)) new PagedSeqReader(seq, offset + 1) else this /** The position of the first element in the reader. */ def pos: Position = new OffsetPosition(source, offset) /** true iff there are no more elements in this reader (except for trailing * EofCh's). */ def atEnd = !seq.isDefinedAt(offset) /** Returns an abstract reader consisting of all elements except the first * `n` elements. */ override def drop(n: Int): PagedSeqReader = new PagedSeqReader(seq, offset + n) } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/Position.scala000066400000000000000000000045441244342316100306660ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input /** `Position` is the base trait for objects describing a position in a `document`. * * It provides functionality for: * - generating a visual representation of this position (`longString`); * - comparing two positions (`<`). * * To use this class for a concrete kind of `document`, implement the `lineContents` method. * * @author Martin Odersky * @author Adriaan Moors */ trait Position { /** The line number referred to by the position; line numbers start at 1. */ def line: Int /** The column number referred to by the position; column numbers start at 1. */ def column: Int /** The contents of the line at this position. (must not contain a new-line character). */ protected def lineContents: String /** Returns a string representation of the `Position`, of the form `line.column`. */ override def toString = ""+line+"."+column /** Returns a more ``visual'' representation of this position. * More precisely, the resulting string consists of two lines: * 1. the line in the document referred to by this position * 2. a caret indicating the column * * Example: * {{{ * List(this, is, a, line, from, the, document) * ^ * }}} */ def longString = lineContents+"\n"+lineContents.take(column-1).map{x => if (x == '\t') x else ' ' } + "^" /** Compare this position to another, by first comparing their line numbers, * and then -- if necessary -- using the columns to break a tie. * * @param `that` a `Position` to compare to this `Position` * @return true if this position's line number or (in case of equal line numbers) * column is smaller than the corresponding components of `that` */ def <(that: Position) = { this.line < that.line || this.line == that.line && this.column < that.column } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/Positional.scala000066400000000000000000000020001244342316100311640ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input /** A trait for objects that have a source position. * * @author Martin Odersky, Adriaan Moors */ trait Positional { /** The source position of this object, initially set to undefined. */ var pos: Position = NoPosition /** If current source position is undefined, update it with given position `newpos` * @return the object itself */ def setPos(newpos: Position): this.type = { if (pos eq NoPosition) pos = newpos this } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/Reader.scala000066400000000000000000000034751244342316100302660ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input /** An interface for streams of values that have positions. * * @author Martin Odersky * @author Adriaan Moors */ abstract class Reader[+T] { /** If this is a reader over character sequences, the underlying char sequence. * If not, throws a `NoSuchMethodError` exception. * * @throws [[java.lang.NoSuchMethodError]] if this not a char sequence reader. */ def source: java.lang.CharSequence = throw new NoSuchMethodError("not a char sequence reader") def offset: Int = throw new NoSuchMethodError("not a char sequence reader") /** Returns the first element of the reader */ def first: T /** Returns an abstract reader consisting of all elements except the first * * @return If `atEnd` is `true`, the result will be `this'; * otherwise, it's a `Reader` containing more elements. */ def rest: Reader[T] /** Returns an abstract reader consisting of all elements except the first `n` elements. */ def drop(n: Int): Reader[T] = { var r: Reader[T] = this var cnt = n while (cnt > 0) { r = r.rest; cnt -= 1 } r } /** The position of the first element in the reader. */ def pos: Position /** `true` iff there are no more elements in this reader. */ def atEnd: Boolean } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/input/StreamReader.scala000066400000000000000000000052541244342316100314370ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.input import java.io.BufferedReader import scala.collection.immutable.PagedSeq /** An object to create a `StreamReader` from a `java.io.Reader`. * * @author Miles Sabin */ object StreamReader { final val EofCh = '\u001a' /** Create a `StreamReader` from a `java.io.Reader`. * * @param in the `java.io.Reader` that provides the underlying * stream of characters for this Reader. */ def apply(in: java.io.Reader): StreamReader = { new StreamReader(PagedSeq.fromReader(in), 0, 1) } } /** A StreamReader reads from a character sequence, typically created as a PagedSeq * from a java.io.Reader * * NOTE: * StreamReaders do not really fulfill the new contract for readers, which * requires a `source` CharSequence representing the full input. * Instead source is treated line by line. * As a consequence, regex matching cannot extend beyond a single line * when a StreamReader are used for input. * * If you need to match regexes spanning several lines you should consider * class `PagedSeqReader` instead. * * @author Miles Sabin * @author Martin Odersky */ sealed class StreamReader private (seq: PagedSeq[Char], off: Int, lnum: Int, nextEol0: Int) extends PagedSeqReader(seq, off) { def this(seq: PagedSeq[Char], off: Int, lnum: Int) = this(seq, off, lnum, -1) import StreamReader.EofCh override def rest: StreamReader = if (!seq.isDefinedAt(off)) this else if (seq(off) == '\n') new StreamReader(seq.slice(off + 1), 0, lnum + 1, -1) else new StreamReader(seq, off + 1, lnum, nextEol0) private def nextEol = if (nextEol0 == -1) { var i = off while (seq.isDefinedAt(i) && seq(i) != '\n' && seq(i) != EofCh) i += 1 i } else nextEol0 override def drop(n: Int): StreamReader = { val eolPos = nextEol if (eolPos < off + n && seq.isDefinedAt(eolPos)) new StreamReader(seq.slice(eolPos + 1), 0, lnum + 1, -1).drop(off + n - (eolPos + 1)) else new StreamReader(seq, off + n, lnum, eolPos) } override def pos: Position = new Position { def line = lnum def column = off + 1 def lineContents = seq.slice(0, nextEol).toString } } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/json/000077500000000000000000000000001244342316100256605ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/json/JSON.scala000066400000000000000000000065131244342316100274430ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.json /** * This object provides a simple interface to the JSON parser class. * The default conversion for numerics is into a double. If you wish to * override this behavior at the global level, you can set the * `globalNumberParser` property to your own `(String => Any)` function. * If you only want to override at the per-thread level then you can set * the `perThreadNumberParser` property to your function. For example: * {{{ * val myConversionFunc = {input : String => BigDecimal(input)} * * // Global override * JSON.globalNumberParser = myConversionFunc * * // Per-thread override * JSON.perThreadNumberParser = myConversionFunc * }}} * * @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org"> */ object JSON extends Parser { /** * This method converts ''raw'' results back into the original form. */ private def unRaw (in : Any) : Any = in match { case JSONObject(obj) => obj.map({ case (k,v) => (k,unRaw(v))}).toList case JSONArray(list) => list.map(unRaw) case x => x } /** * Parse the given `JSON` string and return a list of elements. If the * string is a `JSON` object it will be a `JSONObject`. If it's a `JSON` * array it will be a `JSONArray`. * * @param input the given `JSON` string. * @return an optional `JSONType` element. */ def parseRaw(input : String) : Option[JSONType] = phrase(root)(new lexical.Scanner(input)) match { case Success(result, _) => Some(result) case _ => None } /** * Parse the given `JSON` string and return either a `List[Any]` * if the `JSON` string specifies an `Array`, or a * `Map[String,Any]` if the `JSON` string specifies an object. * * @param input the given `JSON` string. * @return an optional list or map. */ def parseFull(input: String): Option[Any] = parseRaw(input) match { case Some(data) => Some(resolveType(data)) case None => None } /** * A utility method to resolve a parsed `JSON` list into objects or * arrays. See the `parse` method for details. */ def resolveType(input: Any): Any = input match { case JSONObject(data) => data.transform { case (k,v) => resolveType(v) } case JSONArray(data) => data.map(resolveType) case x => x } /** * The global (VM) default function for converting a string to a numeric value. */ def globalNumberParser_=(f: NumericParser) { defaultNumberParser = f } def globalNumberParser : NumericParser = defaultNumberParser /** * Defines the function used to convert a numeric string literal into a * numeric format on a per-thread basis. Use `globalNumberParser` for a * global override. */ def perThreadNumberParser_=(f : NumericParser) { numberParser.set(f) } def perThreadNumberParser : NumericParser = numberParser.get() } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/json/Lexer.scala000066400000000000000000000064401244342316100277500ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.json import scala.util.parsing.combinator._ import scala.util.parsing.combinator.lexical._ import scala.util.parsing.input.CharArrayReader.EofCh /** * @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org"> */ class Lexer extends StdLexical with ImplicitConversions { override def token: Parser[Token] = //( '\"' ~ rep(charSeq | letter) ~ '\"' ^^ lift(StringLit) ( string ^^ StringLit | number ~ letter ^^ { case n ~ l => ErrorToken("Invalid number format : " + n + l) } | '-' ~> whitespace ~ number ~ letter ^^ { case ws ~ num ~ l => ErrorToken("Invalid number format : -" + num + l) } | '-' ~> whitespace ~ number ^^ { case ws ~ num => NumericLit("-" + num) } | number ^^ NumericLit | EofCh ^^^ EOF | delim | '\"' ~> failure("Unterminated string") | rep(letter) ^^ checkKeyword | failure("Illegal character") ) def checkKeyword(xs : List[Any]) = { val strRep = xs mkString "" if (reserved contains strRep) Keyword(strRep) else ErrorToken("Not a keyword: " + strRep) } /** A string is a collection of zero or more Unicode characters, wrapped in * double quotes, using backslash escapes (cf. http://www.json.org/). */ def string = '\"' ~> rep(charSeq | chrExcept('\"', '\n', EofCh)) <~ '\"' ^^ { _ mkString "" } override def whitespace = rep(whitespaceChar) def number = intPart ~ opt(fracPart) ~ opt(expPart) ^^ { case i ~ f ~ e => i + optString(".", f) + optString("", e) } def intPart = zero | intList def intList = nonzero ~ rep(digit) ^^ {case x ~ y => (x :: y) mkString ""} def fracPart = '.' ~> rep(digit) ^^ { _ mkString "" } def expPart = exponent ~ opt(sign) ~ rep1(digit) ^^ { case e ~ s ~ d => e + optString("", s) + d.mkString("") } private def optString[A](pre: String, a: Option[A]) = a match { case Some(x) => pre + x.toString case None => "" } def zero: Parser[String] = '0' ^^^ "0" def nonzero = elem("nonzero digit", d => d.isDigit && d != '0') def exponent = elem("exponent character", d => d == 'e' || d == 'E') def sign = elem("sign character", d => d == '-' || d == '+') def charSeq: Parser[String] = ('\\' ~ '\"' ^^^ "\"" |'\\' ~ '\\' ^^^ "\\" |'\\' ~ '/' ^^^ "/" |'\\' ~ 'b' ^^^ "\b" |'\\' ~ 'f' ^^^ "\f" |'\\' ~ 'n' ^^^ "\n" |'\\' ~ 'r' ^^^ "\r" |'\\' ~ 't' ^^^ "\t" |'\\' ~> 'u' ~> unicodeBlock) val hexDigits = Set[Char]() ++ "0123456789abcdefABCDEF".toArray def hexDigit = elem("hex digit", hexDigits.contains(_)) private def unicodeBlock = hexDigit ~ hexDigit ~ hexDigit ~ hexDigit ^^ { case a ~ b ~ c ~ d => new String(Array(Integer.parseInt(List(a, b, c, d) mkString "", 16)), 0, 1) } //private def lift[T](f: String => T)(xs: List[Any]): T = f(xs mkString "") } scala-parser-combinators-1.0.3/src/main/scala/scala/util/parsing/json/Parser.scala000066400000000000000000000114161244342316100301240ustar00rootroot00000000000000/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala package util.parsing.json import scala.util.parsing.combinator._ import scala.util.parsing.combinator.syntactical._ /** * A marker class for the JSON result types. * * @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org"> */ sealed abstract class JSONType { /** * This version of toString allows you to provide your own value * formatter. */ def toString (formatter : JSONFormat.ValueFormatter) : String /** * Returns a String representation of this JSON value * using the JSONFormat.defaultFormatter. */ override def toString = toString(JSONFormat.defaultFormatter) } /** * This object defines functions that are used when converting JSONType * values into String representations. Mostly this is concerned with * proper quoting of strings. * * @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org"> */ object JSONFormat { /** * This type defines a function that can be used to * format values into JSON format. */ type ValueFormatter = Any => String /** * The default formatter used by the library. You can * provide your own with the toString calls on * JSONObject and JSONArray instances. */ val defaultFormatter : ValueFormatter = (x : Any) => x match { case s : String => "\"" + quoteString(s) + "\"" case jo : JSONObject => jo.toString(defaultFormatter) case ja : JSONArray => ja.toString(defaultFormatter) case other => other.toString } /** * This function can be used to properly quote Strings * for JSON output. */ def quoteString (s : String) : String = s.map { case '"' => "\\\"" case '\\' => "\\\\" case '/' => "\\/" case '\b' => "\\b" case '\f' => "\\f" case '\n' => "\\n" case '\r' => "\\r" case '\t' => "\\t" /* We'll unicode escape any control characters. These include: * 0x0 -> 0x1f : ASCII Control (C0 Control Codes) * 0x7f : ASCII DELETE * 0x80 -> 0x9f : C1 Control Codes * * Per RFC4627, section 2.5, we're not technically required to * encode the C1 codes, but we do to be safe. */ case c if ((c >= '\u0000' && c <= '\u001f') || (c >= '\u007f' && c <= '\u009f')) => "\\u%04x".format(c.toInt) case c => c }.mkString } /** * Represents a JSON Object (map). * * @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org"> */ case class JSONObject (obj : Map[String,Any]) extends JSONType { def toString (formatter : JSONFormat.ValueFormatter) = "{" + obj.map({ case (k,v) => formatter(k.toString) + " : " + formatter(v) }).mkString(", ") + "}" } /** * Represents a JSON Array (list). * @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org"> */ case class JSONArray (list : List[Any]) extends JSONType { def toString (formatter : JSONFormat.ValueFormatter) = "[" + list.map(formatter).mkString(", ") + "]" } /** * The main JSON Parser. * * @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org"> */ class Parser extends StdTokenParsers with ImplicitConversions { // Fill in abstract defs type Tokens = Lexer val lexical = new Tokens // Configure lexical parsing lexical.reserved ++= List("true", "false", "null") lexical.delimiters ++= List("{", "}", "[", "]", ":", ",") /** Type signature for functions that can parse numeric literals */ type NumericParser = String => Any // Global default number parsing function protected var defaultNumberParser : NumericParser = {_.toDouble} // Per-thread default number parsing function protected val numberParser = new ThreadLocal[NumericParser]() { override def initialValue() = defaultNumberParser } // Define the grammar def root = jsonObj | jsonArray def jsonObj = "{" ~> repsep(objEntry, ",") <~ "}" ^^ { case vals : List[_] => JSONObject(Map(vals : _*)) } def jsonArray = "[" ~> repsep(value, ",") <~ "]" ^^ { case vals : List[_] => JSONArray(vals) } def objEntry = stringVal ~ (":" ~> value) ^^ { case x ~ y => (x, y) } def value: Parser[Any] = (jsonObj | jsonArray | number | "true" ^^^ true | "false" ^^^ false | "null" ^^^ null | stringVal) def stringVal = accept("string", { case lexical.StringLit(n) => n} ) def number = accept("number", { case lexical.NumericLit(n) => numberParser.get.apply(n)} ) } scala-parser-combinators-1.0.3/src/test/000077500000000000000000000000001244342316100201545ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/test/scala/000077500000000000000000000000001244342316100212375ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/test/scala/scala/000077500000000000000000000000001244342316100223225ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/test/scala/scala/util/000077500000000000000000000000001244342316100232775ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/000077500000000000000000000000001244342316100247425ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/000077500000000000000000000000001244342316100270775ustar00rootroot00000000000000JavaTokenParsersTest.scala000066400000000000000000000035411244342316100341120ustar00rootroot00000000000000scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinatorpackage scala.util.parsing.combinator import scala.util.parsing.input.CharArrayReader import org.junit.Test import org.junit.Assert.assertEquals class JavaTokenParsersTest { @Test def parseDecimalNumber: Unit = { object TestJavaTokenParsers extends JavaTokenParsers import TestJavaTokenParsers._ assertEquals("1.1", decimalNumber(new CharArrayReader("1.1".toCharArray)).get) assertEquals("1.", decimalNumber(new CharArrayReader("1.".toCharArray)).get) assertEquals(".1", decimalNumber(new CharArrayReader(".1".toCharArray)).get) // should fail to parse and we should get Failure as ParseResult val failure = decimalNumber(new CharArrayReader("!1".toCharArray)).asInstanceOf[Failure] assertEquals("""string matching regex `(\d+(\.\d*)?|\d*\.\d+)' expected but `!' found""", failure.msg) } @Test def parseJavaIdent: Unit = { object javaTokenParser extends JavaTokenParsers import javaTokenParser._ def parseSuccess(s: String): Unit = { val parseResult = parseAll(ident, s) parseResult match { case Success(r, _) => assertEquals(s, r) case _ => sys.error(parseResult.toString) } } def parseFailure(s: String, errorColPos: Int): Unit = { val parseResult = parseAll(ident, s) parseResult match { case Failure(_, next) => val pos = next.pos assertEquals(1, pos.line) assertEquals(errorColPos, pos.column) case _ => sys.error(parseResult.toString) } } parseSuccess("simple") parseSuccess("with123") parseSuccess("with$") parseSuccess("with\u00f8\u00df\u00f6\u00e8\u00e6") parseSuccess("with_") parseSuccess("_with") parseFailure("3start", 1) parseFailure("-start", 1) parseFailure("with-s", 5) // we♥scala parseFailure("we\u2665scala", 3) parseFailure("with space", 6) } } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/JsonTest.scala000066400000000000000000000247151244342316100316660ustar00rootroot00000000000000import scala.util.parsing.json._ import scala.collection.immutable.TreeMap import org.junit.Test import org.junit.Assert.{assertEquals, assertTrue} class JsonTest { /* This method converts parsed JSON back into real JSON notation with objects in * sorted-key order. Not required by the spec, but it allows us to do a stable * toString comparison. */ def jsonToString(in : Any) : String = in match { case l : List[_] => "[" + l.map(jsonToString).mkString(", ") + "]" case m : Map[String @unchecked,_] => "{" + m.iterator.toList .sortWith({ (x,y) => x._1 < y._1 }) .map({ case (k,v) => "\"" + k + "\": " + jsonToString(v) }) .mkString(", ") + "}" case s : String => "\"" + s + "\"" case x => x.toString } /* * This method takes input JSON values and sorts keys on objects. */ def sortJSON(in : Any) : Any = in match { case l : List[_] => l.map(sortJSON) case m : Map[String @unchecked,_] => TreeMap(m.mapValues(sortJSON).iterator.toSeq : _*) // For the object versions, sort their contents, ugly casts and all... case JSONObject(data) => JSONObject(sortJSON(data).asInstanceOf[Map[String,Any]]) case JSONArray(data) => JSONArray(sortJSON(data).asInstanceOf[List[Any]]) case x => x } // For this one, just parsing should be considered a pass def printJSON(given : String) : Unit = assertTrue("Parse failed for \"%s\"".format(given), (JSON parseRaw given).isDefined) // For this usage, do a raw parse (to JSONObject/JSONArray) def printJSON(given : String, expected : JSONType) { printJSON(given, JSON.parseRaw, expected) } // For this usage, do a raw parse (to JSONType and subclasses) def printJSONFull(given : String, expected : Any) { printJSON(given, JSON.parseFull, expected) } // For this usage, do configurable parsing so that you can do raw if desired def printJSON[T](given : String, parser : String => T, expected : Any) { parser(given) match { case None => assertTrue("Parse failed for \"%s\"".format(given), false) case Some(parsed) => if (parsed != expected) { val eStr = sortJSON(expected).toString val pStr = sortJSON(parsed).toString assertTrue(stringDiff(eStr,pStr), false) } } } def stringDiff (expected : String, actual : String): String = { if (expected != actual) { // Figure out where the Strings differ and generate a marker val mismatchPosition = expected.toList.zip(actual.toList).indexWhere({case (x,y) => x != y}) match { case -1 => Math.min(expected.length, actual.length) case x => x } val reason = (" " * mismatchPosition) + "^" "Expected: %s\nGot : %s \n %s".format(expected, actual, reason) } else { "Passed compare: " + actual } } // The library should differentiate between lower case "l" and number "1" (ticket #136) @Test def testEllVsOne: Unit = { printJSON("{\"name\" : \"value\"}", JSONObject(Map("name" -> "value"))) printJSON("{\"name\" : \"va1ue\"}", JSONObject(Map("name" -> "va1ue"))) printJSON("{\"name\" : { \"name1\" : \"va1ue1\", \"name2\" : \"va1ue2\" } }", JSONObject(Map("name" -> JSONObject(Map("name1" -> "va1ue1", "name2" -> "va1ue2"))))) } // Unicode escapes should be handled properly @Test def testEscapes: Unit = printJSON("{\"name\" : \"\\u0022\"}") // The library should return a map for JSON objects (ticket #873) @Test def testMap: Unit = printJSONFull("{\"function\" : \"add_symbol\"}", Map("function" -> "add_symbol")) // The library should recurse into arrays to find objects (ticket #2207) @Test def testObjectsInArrays: Unit = printJSON("[{\"a\" : \"team\"},{\"b\" : 52}]", JSONArray(List(JSONObject(Map("a" -> "team")), JSONObject(Map("b" -> 52.0))))) // The library should differentiate between empty maps and lists (ticket #3284) @Test def testEmptyMapsVsLists: Unit = { printJSONFull("{}", Map()) printJSONFull("[]", List()) } // Lists should be returned in the same order as specified @Test def testListOrder: Unit = printJSON("[4,1,3,2,6,5,8,7]", JSONArray(List[Double](4,1,3,2,6,5,8,7))) // Additional tests @Test def testAdditional: Unit = printJSON("{\"age\": 0}") // The library should do a proper toString representation using default and custom renderers (ticket #3605) @Test def testDefaultAndCustomRenderers: Unit = { assertEquals("{\"name\" : \"va1ue\"}", JSONObject(Map("name" -> "va1ue")).toString()) assertEquals("{\"name\" : {\"name1\" : \"va1ue1\", \"name2\" : \"va1ue2\"}}", JSONObject(Map("name" -> JSONObject(TreeMap("name1" -> "va1ue1", "name2" -> "va1ue2")))).toString()) assertEquals("[4.0, 1.0, 3.0, 2.0, 6.0, 5.0, 8.0, 7.0]", JSONArray(List[Double](4,1,3,2,6,5,8,7)).toString()) } // A test method that escapes all characters in strings def escapeEverything (in : Any) : String = in match { case s : String => "\"" + s.map(c => "\\u%04x".format(c : Int)).mkString + "\"" case jo : JSONObject => jo.toString(escapeEverything _) case ja : JSONArray => ja.toString(escapeEverything _) case other => other.toString } @Test def testEscapeEverything: Unit = assertEquals("{\"\\u006e\\u0061\\u006d\\u0065\" : \"\\u0076\\u0061\\u006c\"}", JSONObject(Map("name" -> "val")).toString(escapeEverything _)) // from http://en.wikipedia.org/wiki/JSON val sample1 = """ { "firstName": "John", "lastName": "Smith", "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": 10021 }, "phoneNumbers": [ "212 732-1234", "646 123-4567" ] }""" // Should be equivalent to: val sample1Obj = Map( "firstName" -> "John", "lastName" -> "Smith", "address" -> Map( "streetAddress" -> "21 2nd Street", "city" -> "New York", "state" -> "NY", "postalCode" -> 10021 ), "phoneNumbers"-> List( "212 732-1234", "646 123-4567" ) ) @Test def testSample1: Unit = printJSONFull(sample1, sample1Obj) // from http://www.developer.com/lang/jscript/article.php/3596836 val sample2 = """ { "fullname": "Sean Kelly", "org": "SK Consulting", "emailaddrs": [ {"type": "work", "value": "kelly@seankelly.biz"}, {"type": "home", "pref": 1, "value": "kelly@seankelly.tv"} ], "telephones": [ {"type": "work", "pref": 1, "value": "+1 214 555 1212"}, {"type": "fax", "value": "+1 214 555 1213"}, {"type": "mobile", "value": "+1 214 555 1214"} ], "addresses": [ {"type": "work", "format": "us", "value": "1234 Main StnSpringfield, TX 78080-1216"}, {"type": "home", "format": "us", "value": "5678 Main StnSpringfield, TX 78080-1316"} ], "urls": [ {"type": "work", "value": "http://seankelly.biz/"}, {"type": "home", "value": "http://seankelly.tv/"} ] }""" @Test def testSampl2: Unit = printJSON(sample2) // from http://json.org/example.html val sample3 = """ {"web-app": { "servlet": [ { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", "init-param": { "configGlossary:installationAt": "Philadelphia, PA", "configGlossary:adminEmail": "ksm@pobox.com", "configGlossary:poweredBy": "Cofax", "configGlossary:poweredByIcon": "/images/cofax.gif", "configGlossary:staticPath": "/content/static", "templateProcessorClass": "org.cofax.WysiwygTemplate", "templateLoaderClass": "org.cofax.FilesTemplateLoader", "templatePath": "templates", "templateOverridePath": "", "defaultListTemplate": "listTemplate.htm", "defaultFileTemplate": "articleTemplate.htm", "useJSP": false, "jspListTemplate": "listTemplate.jsp", "jspFileTemplate": "articleTemplate.jsp", "cachePackageTagsTrack": 200, "cachePackageTagsStore": 200, "cachePackageTagsRefresh": 60, "cacheTemplatesTrack": 100, "cacheTemplatesStore": 50, "cacheTemplatesRefresh": 15, "cachePagesTrack": 200, "cachePagesStore": 100, "cachePagesRefresh": 10, "cachePagesDirtyRead": 10, "searchEngineListTemplate": "forSearchEnginesList.htm", "searchEngineFileTemplate": "forSearchEngines.htm", "searchEngineRobotsDb": "WEB-INF/robots.db", "useDataStore": true, "dataStoreClass": "org.cofax.SqlDataStore", "redirectionClass": "org.cofax.SqlRedirection", "dataStoreName": "cofax", "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", "dataStoreUser": "sa", "dataStorePassword": "dataStoreTestQuery", "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", "dataStoreInitConns": 10, "dataStoreMaxConns": 100, "dataStoreConnUsageLimit": 100, "dataStoreLogLevel": "debug", "maxUrlLength": 500}}, { "servlet-name": "cofaxEmail", "servlet-class": "org.cofax.cds.EmailServlet", "init-param": { "mailHost": "mail1", "mailHostOverride": "mail2"}}, { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, { "servlet-name": "cofaxTools", "servlet-class": "org.cofax.cms.CofaxToolsServlet", "init-param": { "templatePath": "toolstemplates/", "log": 1, "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", "logMaxSize": "", "dataLog": 1, "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", "dataLogMaxSize": "", "removePageCache": "/content/admin/remove?cache=pages&id=", "removeTemplateCache": "/content/admin/remove?cache=templates&id=", "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", "lookInContext": 1, "adminGroupID": 4, "betaServer": true}}], "servlet-mapping": { "cofaxCDS": "/", "cofaxEmail": "/cofaxutil/aemail/*", "cofaxAdmin": "/admin/*", "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"} } }""" @Test def testSample3 = printJSON(sample3) } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/PackratParsersTest.scala000066400000000000000000000143751244342316100337030ustar00rootroot00000000000000package scala.util.parsing.combinator import org.junit.Test import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import scala.util.parsing.combinator.syntactical.StandardTokenParsers class PackratParsersTest { @Test def test1: Unit = { import grammars1._ val head = phrase(term) def extractResult(r : ParseResult[Int]): Int = r match { case Success(a,_) => a case NoSuccess(a,_) => sys.error(a) } def check(expected: Int, expr: String): Unit = { val parseResult = head(new lexical.Scanner(expr)) val result = extractResult(parseResult) assertEquals(expected, result) } check(1, "1") check(3, "1+2") check(5, "9-4") check(81, "9*9") check(4, "8/2") check(37, "4*9-0/7+9-8*1") check(9, "(1+2)*3") check(3, """/* This is a long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long comment */ 1+2""") } @Test def test2: Unit = { import grammars2._ val head = phrase(exp) def extractResult(r : ParseResult[Int]): Int = r match { case Success(a,_) => a case NoSuccess(a,_) => sys.error(a) } def check(expected: Int, expr: String): Unit = { val parseResult = head(new lexical.Scanner(expr)) val result = extractResult(parseResult) assertEquals(expected, result) } check(1, "1") check(3, "1+2") check(81, "9*9") check(43, "4*9+7") check(59, "4*9+7*2+3*3") check(188, "4*9+7*2+3*3+9*5+7*6*2") check(960, "4*(9+7)*(2+3)*3") } @Test def test3: Unit = { import grammars3._ val head = phrase(AnBnCn) def extractResult(r: ParseResult[AnBnCnResult]): AnBnCnResult = r match { case Success(a,_) => a case NoSuccess(a,_) => sys.error(a) } def threeLists(as: List[Symbol], bs: List[Symbol], cs: List[Symbol]): AnBnCnResult = { val as1 = as.map(_.name) val bs1 = bs.map(_.name) val cs1 = cs.map(_.name) new ~(new ~(as1, bs1), cs1) } def assertSuccess(expected1: List[Symbol], expected2: List[Symbol], expected3: List[Symbol], input: String): Unit = { val expected = threeLists(expected1, expected2, expected3) val parseResult = head(new lexical.Scanner(input)) val result = extractResult(parseResult) assertEquals(expected, result) } assertSuccess(List('a, 'b), List('a), List('b, 'c), "a b c") assertSuccess(List('a, 'a, 'b, 'b), List('a, 'a), List('b, 'b, 'c, 'c), "a a b b c c") assertSuccess(List('a, 'a, 'a, 'b, 'b, 'b), List('a, 'a, 'a), List('b, 'b, 'b, 'c, 'c, 'c), "a a a b b b c c c") assertSuccess(List('a, 'a, 'a, 'a, 'b, 'b, 'b, 'b), List('a, 'a, 'a, 'a), List('b, 'b, 'b, 'b, 'c, 'c, 'c, 'c), "a a a a b b b b c c c c") def assertFailure(expectedFailureMsg: String, input: String): Unit = { val packratReader = new PackratReader(new lexical.Scanner(input)) val parseResult = AnBnCn(packratReader) assertTrue(s"Not an instance of Failure: ${parseResult.toString()}", parseResult.isInstanceOf[Failure]) val failure = parseResult.asInstanceOf[Failure] assertEquals(expectedFailureMsg, failure.msg) } assertFailure("``b'' expected but `c' found", "a a a a b b b c c c c") assertFailure("end of input", "a a a a b b b b c c c") } } private object grammars1 extends StandardTokenParsers with PackratParsers { lexical.delimiters ++= List("+","-","*","/","(",")") lexical.reserved ++= List("Hello","World") /**** * term = term + fact | term - fact | fact * fact = fact * num | fact / num | num */ val term: PackratParser[Int] = (term~("+"~>fact) ^^ {case x~y => x+y} |term~("-"~>fact) ^^ {case x~y => x-y} |fact) val fact: PackratParser[Int] = (fact~("*"~>numericLit) ^^ {case x~y => x*y.toInt} |fact~("/"~>numericLit) ^^ {case x~y => x/y.toInt} |"("~>term<~")" |numericLit ^^ {_.toInt}) } private object grammars2 extends StandardTokenParsers with PackratParsers { lexical.delimiters ++= List("+","-","*","/","(",")") lexical.reserved ++= List("Hello","World") /* * exp = sum | prod | num * sum = exp ~ "+" ~ num * prod = exp ~ "*" ~ num */ val exp : PackratParser[Int] = sum | prod | numericLit ^^{_.toInt} | "("~>exp<~")" val sum : PackratParser[Int] = exp~("+"~>exp) ^^ {case x~y => x+y} val prod: PackratParser[Int] = exp~("*"~>(numericLit ^^{_.toInt} | exp)) ^^ {case x~y => x*y} } private object grammars3 extends StandardTokenParsers with PackratParsers { lexical.reserved ++= List("a","b", "c") val a: PackratParser[String] = memo("a") val b: PackratParser[String] = memo("b") val c: PackratParser[String] = memo("c") type AnBnCnResult = List[String] ~ List[String] ~ List[String] val AnBnCn: PackratParser[AnBnCnResult] = guard(repMany1(a,b) <~ not(b)) ~ rep1(a) ~ repMany1(b,c)// ^^{case x~y => x:::y} private def repMany[T](p: => Parser[T], q: => Parser[T]): Parser[List[T]] = ( p~repMany(p,q)~q ^^ {case x~xs~y => x::xs:::(y::Nil)} | success(Nil) ) def repMany1[T](p: => Parser[T], q: => Parser[T]): Parser[List[T]] = p~opt(repMany(p,q))~q ^^ {case x~Some(xs)~y => x::xs:::(y::Nil)} } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala000066400000000000000000000053121244342316100333570ustar00rootroot00000000000000package scala.util.parsing.combinator import org.junit.Test import org.junit.Assert.assertEquals class RegexParsersTest { @Test def parserNoSuccessMessage: Unit = { object parser extends RegexParsers { def sign = "-" def number = "\\d+".r type ResultType = Option[String] ~ String def p: Parser[ResultType] = sign.? ~ number withErrorMessage "Number expected!" def q: Parser[ResultType] = sign.? ~! number withErrorMessage "Number expected!" } import parser._ def extractResult(r: ParseResult[ResultType]): ResultType = r match { case Success(r, _) => r case r => sys.error(r.toString) } def result(num: Int): ResultType = { val minusSign = if (num < 0) Some("-") else None val absNumStr = Math.abs(num).toString new ~(minusSign, absNumStr) } val failure1 = parseAll(p, "-x").asInstanceOf[Failure] assertEquals("string matching regex `\\d+' expected but `x' found", failure1.msg) val failure2 = parseAll(p, "x").asInstanceOf[Failure] assertEquals("string matching regex `\\d+' expected but `x' found", failure2.msg) assertEquals(result(-5), extractResult(parseAll(p, "-5"))) assertEquals(result(5), extractResult(parseAll(p, "5"))) val error1 = parseAll(q, "-x").asInstanceOf[Error] assertEquals("Number expected!", error1.msg) val error2 = parseAll(q, "x").asInstanceOf[Error] assertEquals("Number expected!", error2.msg) assertEquals(result(-5), extractResult(parseAll(q, "-5"))) assertEquals(result(5), extractResult(parseAll(q, "5"))) } @Test def parserFilter: Unit = { object parser extends RegexParsers { val keywords = Set("if", "false") def word: Parser[String] = "\\w+".r def keyword: Parser[String] = word filter (keywords.contains) def ident: Parser[String] = word filter(!keywords.contains(_)) def test: Parser[String ~ String] = keyword ~ ident } import parser._ val failure1 = parseAll(test, "if false").asInstanceOf[Failure] assertEquals("Input doesn't match filter: false", failure1.msg) val failure2 = parseAll(test, "not true").asInstanceOf[Failure] assertEquals("Input doesn't match filter: not", failure2.msg) val success = parseAll(test, "if true").asInstanceOf[Success[String ~ String]] assertEquals(new ~("if", "true"), success.get) } @Test def parserForFilter: Unit = { object parser extends RegexParsers { def word: Parser[String] = "\\w+".r def twoWords = for { (a ~ b) <- word ~ word } yield (b, a) } import parser._ val success = parseAll(twoWords, "first second").asInstanceOf[Success[(String, String)]] assertEquals(("second", "first"), success.get) } } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/UnitTestIO.scala000066400000000000000000000021741244342316100321170ustar00rootroot00000000000000import org.junit.Test import org.junit.Assert.assertEquals class UnitTestIO { @Test def testUTF8 { def decode(ch: Int) = new String(Array(ch), 0, 1).getBytes("UTF-8") assert(new String( decode(0x004D), "utf8") == new String(Array(0x004D.asInstanceOf[Char]))) assert(new String( decode(0x0430), "utf8") == new String(Array(0x0430.asInstanceOf[Char]))) assert(new String( decode(0x4E8C), "utf8") == new String(Array(0x4E8C.asInstanceOf[Char]))) assert(new String(decode(0x10302), "utf8") == new String(Array(0xD800.asInstanceOf[Char], 0xDF02.asInstanceOf[Char]))) // a client val test = "{\"a\":\"\\u0022\"}" val expected = "a" -> "\"" val parsed = scala.util.parsing.json.JSON.parseFull(test) val result = parsed == Some(Map(expected)) assertEquals(Some(Map(expected)), parsed) } @Test def testSource { val s = "Here is a test string" val f = io.Source.fromBytes(s.getBytes("utf-8")) val b = new collection.mutable.ArrayBuffer[Char]() f.copyToBuffer(b) assertEquals(new String(b.toArray), s) } }scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/t0700.scala000066400000000000000000000014731244342316100306630ustar00rootroot00000000000000import java.io.{File,StringReader} import scala.util.parsing.combinator.Parsers import scala.util.parsing.input.{CharArrayReader, StreamReader} import org.junit.Test import org.junit.Assert.assertEquals class T0700 { class TestParsers extends Parsers { type Elem = Char def p: Parser[List[Int]] = rep(p1 | p2) def p1: Parser[Int] = 'a' ~ nl ~ 'b' ~ nl ^^^ 1 def p2: Parser[Int] = 'a' ~ nl ^^^ 2 def nl: Parser[Int] = rep(accept('\n') | accept('\r')) ^^^ 0 } @Test def test: Unit = { val tstParsers = new TestParsers val s = "a\na\na" val r1 = new CharArrayReader(s.toCharArray()) val r2 = StreamReader(new StringReader(s)) assertEquals("[3.2] parsed: List(2, 2, 2)", tstParsers.p(r1).toString) assertEquals("[3.2] parsed: List(2, 2, 2)", tstParsers.p(r2).toString) } }scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/t1100.scala000066400000000000000000000013331244342316100306510ustar00rootroot00000000000000import scala.util.parsing.combinator.Parsers import scala.util.parsing.input.CharSequenceReader import java.io.{File,StringReader} import scala.util.parsing.combinator.Parsers import scala.util.parsing.input.{CharArrayReader, StreamReader} import org.junit.Test import org.junit.Assert.assertEquals class T1100 { class TestParsers extends Parsers { type Elem = Char def p: Parser[List[Char]] = rep1(p1) def p1: Parser[Char] = accept('a') | err("errors are propagated") } val expected = """[1.4] error: errors are propagated aaab ^""" @Test def test(): Unit = { val tstParsers = new TestParsers val s = new CharSequenceReader("aaab") assertEquals(expected, tstParsers.p(s).toString) } } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/t4138.scala000066400000000000000000000006741244342316100306760ustar00rootroot00000000000000import org.junit.Test import org.junit.Assert.assertEquals class T4138 { object p extends scala.util.parsing.combinator.JavaTokenParsers @Test def test: Unit = { assertEquals("""[1.45] parsed: "lir 'de\' ' \\ \n / upa \"new\" \t parsing"""", p.parse(p.stringLiteral, """"lir 'de\' ' \\ \n / upa \"new\" \t parsing"""").toString) assertEquals("""[1.5] parsed: "s """", p.parse(p.stringLiteral, """"s " lkjse"""").toString) } } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/t4929.scala000066400000000000000000000017011244342316100306760ustar00rootroot00000000000000import scala.util.parsing.json._ import java.util.concurrent._ import collection.JavaConversions._ import org.junit.Test class t4929 { val LIMIT = 2000 val THREAD_COUNT = 20 val count = new java.util.concurrent.atomic.AtomicInteger(0) val begin = new CountDownLatch(THREAD_COUNT) val finish = new CountDownLatch(THREAD_COUNT) val errors = new ConcurrentLinkedQueue[Throwable] @Test def test: Unit = { (1 to THREAD_COUNT) foreach { i => val thread = new Thread { override def run() { begin.await(1, TimeUnit.SECONDS) try { while (count.getAndIncrement() < LIMIT && errors.isEmpty) { JSON.parseFull("""{"foo": [1,2,3,4]}""") } } catch { case t: Throwable => errors.add(t) } finish.await(10, TimeUnit.SECONDS) } } thread.setDaemon(true) thread.start() } errors foreach { throw(_) } } } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/t5514.scala000066400000000000000000000021441244342316100306670ustar00rootroot00000000000000import scala.io.Source import scala.util.parsing.combinator.Parsers import scala.util.parsing.input.Reader import scala.util.parsing.input.Position import org.junit.Test import org.junit.Assert.assertEquals class T5514 extends Parsers { var readerCount = 0 class DemoReader(n: Int) extends Reader[String] { def atEnd = n == 0 def first = if (n >= 0) "s" + n else throw new IllegalArgumentException("No more input.") def rest = new DemoReader(n - 1) def pos = new Position { def line = 0 def column = 0 def lineContents = first } readerCount += 1 } type Elem = String def startsWith(prefix: String) = acceptIf(_ startsWith prefix)("Error: " + _) @Test def test: Unit = { val resrep = startsWith("s").*(new DemoReader(10)) assertEquals("[0.0] parsed: List(s10, s9, s8, s7, s6, s5, s4, s3, s2, s1)", resrep.toString) assertEquals(11, readerCount) readerCount = 0 val resrep5 = repN(5, startsWith("s"))(new DemoReader(10)) assertEquals("[0.0] parsed: List(s10, s9, s8, s7, s6)", resrep5.toString) assertEquals(6, readerCount) } } scala-parser-combinators-1.0.3/src/test/scala/scala/util/parsing/combinator/t8879.scala000066400000000000000000000016551244342316100307160ustar00rootroot00000000000000import scala.util.parsing.input._ import scala.collection.immutable.PagedSeq import org.junit.Test import org.junit.Assert.fail class t8879 { @Test def test: Unit = { val testPagedSeq = { var nbpage = 0 def more(data: Array[Char], start: Int, len: Int): Int = { if (nbpage < 1) { var i = 0 while (i < len && nbpage < 3) { if (i % 100 != 0) { data(start + i) = 'a' } else { data(start + i) = '\n' } i += 1 } if (i == 0) -1 else { nbpage += 1 i } } else { fail("Should not read more than 1 page!") 0 } } new PagedSeq(more(_: Array[Char], _: Int, _: Int)) } val s = new StreamReader(testPagedSeq, 0, 1) // should not trigger reading of the second page s.drop(20) } }