diff --git a/pom.xml b/pom.xml
index 0d422d4..f62c5ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
org.docopt
docopt
- 0.1-SNAPSHOT
+ 0.1c
jar
docopt parser for jvm
@@ -13,7 +13,7 @@
UTF-8
UTF-8
- 2.10.0
+ 2.11.2
@@ -46,12 +46,12 @@
org.scala-lang
scala-library
- 2.10.0
+ 2.11.2
org.scalatest
- scalatest_2.10
- 2.0.M5b
+ scalatest_2.11
+ 2.2.1-M3
test
@@ -134,7 +134,7 @@
org.scalatest
scalatest-maven-plugin
- 1.0-M2
+ 1.0
${project.build.directory}/surefire-reports
.
diff --git a/src/main/scala/org/docopt/Docopt.scala b/src/main/scala/org/docopt/Docopt.scala
index c60f4ba..2016ead 100644
--- a/src/main/scala/org/docopt/Docopt.scala
+++ b/src/main/scala/org/docopt/Docopt.scala
@@ -22,7 +22,7 @@ object Docopt {
help: Boolean = true,
version: String = "",
optionsFirst: Boolean = false): Map[String, Any] = {
- val collected = PatternParser.docopt(usage, argv.mkString(" "), help, version, optionsFirst)
+ val collected = PatternParser.docopt(usage, argv.filter(_ != ""), help, version, optionsFirst)
val tupled:Seq[(String, Any)] = collected.map(pattern => pattern match {
case o@Option(l,s,a,value:Value) => (o.name ,extractValue(value))
case Argument(name,value:Value) => (name, extractValue(value))
diff --git a/src/main/scala/org/docopt/PatternMatcher.scala b/src/main/scala/org/docopt/PatternMatcher.scala
index 7877620..163d962 100644
--- a/src/main/scala/org/docopt/PatternMatcher.scala
+++ b/src/main/scala/org/docopt/PatternMatcher.scala
@@ -49,10 +49,8 @@ object PatternMatcher {
private def collectSameName(matched: ChildPattern,
originalValue: Value,
collected: SeqPat): SeqPat = {
- // http://stackoverflow.com/questions/11394034/why-scalas-pattern-maching-does-not-work-in-for-loops-for-type-matching
- // http://www.scala-lang.org/node/2187
- val sameName = (for (a@(_a:ChildPattern) <- collected
- if a.name == matched.name) yield a).toList
+ val (psameName, nonSameName) = collected.partition { case a:ChildPattern => (a.name == matched.name) }
+ val sameName = psameName.asInstanceOf[Seq[Pattern with ChildPattern]]
def childPatternUpdateValue(child: ChildPattern, newValue: Value) = child match {
case Argument(n, _) => Argument(n, newValue)
@@ -72,7 +70,7 @@ object PatternMatcher {
collected ++ List(childPatternUpdateValue(matched, IntValue(1)))
case head :: tail => head.value match {
case IntValue(i) =>
- childPatternUpdateValue(head, IntValue(1 + i)) :: tail
+ nonSameName ++ (childPatternUpdateValue(head, IntValue(1 + i)) :: tail)
}
}
// we must update the list or start a new one.
@@ -82,9 +80,9 @@ object PatternMatcher {
collected ++ List(childPatternUpdateValue(matched, matched.value match {case StringValue(v_) => ManyStringValue(List(v_)) case x => x}))
case head :: tail => matched.value match {
case ManyStringValue(s_) =>
- childPatternUpdateValue(head, ManyStringValue(s ++ s_)) :: tail
+ nonSameName ++ (childPatternUpdateValue(head, ManyStringValue(s ++ s_)) :: tail)
case StringValue(s_) =>
- childPatternUpdateValue(head, ManyStringValue(s ++ List(s_))) :: tail
+ nonSameName ++ (childPatternUpdateValue(head, ManyStringValue(s ++ List(s_))) :: tail)
}
}
case _ => collected ++ List(childPatternUpdateValue(matched, matched.value))
diff --git a/src/main/scala/org/docopt/PatternParser.scala b/src/main/scala/org/docopt/PatternParser.scala
index 3c74064..4fe75ca 100644
--- a/src/main/scala/org/docopt/PatternParser.scala
+++ b/src/main/scala/org/docopt/PatternParser.scala
@@ -171,8 +171,9 @@ object PatternParser {
private def extractLongOptionValue(longOption: String) =
if (longOption.exists(_ == '=')) {
+ val Splitter = """^(.*?)=(.*)$""".r
try {
- val Array(long, value) = longOption.split("=")
+ val Splitter(long, value) = longOption //val Array(long, value) = longOption.split("=")
(long, Some(value))
} catch {
case _:Throwable => throw new UnparsableOptionException(longOption)
@@ -217,8 +218,9 @@ object PatternParser {
case head :: tail => head :: clarifyLongOptionAmbiguities(tail, options)
}
- def parseArgv(argv: String, options: SeqOpt, optionFirst:Boolean = false) =
- parseArgvRecursive(clarifyLongOptionAmbiguities(tokenStream(argv), options), options, optionFirst)
+ //def parseArgv(argv: String, options: SeqOpt, optionFirst:Boolean = false) : (SeqOpt, SeqPat) = parseArgv(argv.split("""\s+"""), options, optionFirst)
+ def parseArgv(argv: Array[String], options: SeqOpt, optionFirst:Boolean = false) : (SeqOpt, SeqPat) =
+ parseArgvRecursive(clarifyLongOptionAmbiguities(argv.toList, options), options, optionFirst)
private def parseArgvRecursive(tokens: Tokens, options: SeqOpt, optionFirst: Boolean, ret: List[Pattern] = Nil): (SeqOpt, SeqPat) =
tokens match {
@@ -241,7 +243,7 @@ object PatternParser {
private def tokenStream(source: String, split: Boolean = true): Tokens =
- source.split("\\s+").filter(_ != "").toList
+ source.split("""\s+""").filter(_ != "").toList
// keep only the Usage: part, remove everything after
def printableUsage(doc: String): String = {
@@ -259,7 +261,7 @@ object PatternParser {
"( " + words.tail.map(x => if (x == programName) ") | (" else x).mkString(" ") + " )"
}
- def docopt(doc: String, argv: String = "", help: Boolean = true, version: String = "", optionsFirst: Boolean = false): SeqPat = {
+ def docopt(doc: String, argv: Array[String] = Array[String](), help: Boolean = true, version: String = "", optionsFirst: Boolean = false): SeqPat = {
val usage = formalUsage(printableUsage(doc))
val options = parseOptionDescriptions(doc)
val (options_, pattern) = parsePattern(usage, options)
diff --git a/src/test/scala/org/docopt/PatternParserFunSpec.scala b/src/test/scala/org/docopt/PatternParserFunSpec.scala
index b9518f5..c9007a1 100644
--- a/src/test/scala/org/docopt/PatternParserFunSpec.scala
+++ b/src/test/scala/org/docopt/PatternParserFunSpec.scala
@@ -239,30 +239,30 @@ class PatternParserFunSpec extends FunSpec {
Option("-v", "--verbose"),
Option("-f", "--file", 1))
it("should parse correctly: %s".format("")) {
- assert (PP.parseArgv("", options) == (options, Nil))
+ assert (PP.parseArgv(Array[String](), options) == (options, Nil))
}
it("should parse correctly: %s".format("-h")) {
- assert (PP.parseArgv("-h", options) ==
+ assert (PP.parseArgv(Array("-h"), options) ==
(options, List(Option("-h","",0,BooleanValue(value = true)))))
}
it("should parse correctly: %s".format("-h --verbose")) {
- assert (PP.parseArgv("-h --verbose", options) ==
+ assert (PP.parseArgv("-h --verbose".split("""\s+"""), options) ==
(options,
List(Option("-h","",0,BooleanValue(value = true)),
Option("-v","--verbose",0,BooleanValue(value = true)))))
}
it("should parse correctly: %s".format("-h --file f.txt")) {
- assert (PP.parseArgv("-h --file f.txt", options) ==
+ assert (PP.parseArgv("-h --file f.txt".split("""\s+"""), options) ==
(options,
List(Option("-h","",0,BooleanValue(value = true)),
Option("-f","--file",1,StringValue("f.txt")))))
}
it("should parse correctly: %s".format("-h --file f.txt arg")) {
- assert (PP.parseArgv("-h --file f.txt arg", options) ==
+ assert (PP.parseArgv("-h --file f.txt arg".split("""\s+"""), options) ==
(options,
List(Option("-h","",0,BooleanValue(value = true)),
Option("-f","--file",1,StringValue("f.txt")),
@@ -270,7 +270,7 @@ class PatternParserFunSpec extends FunSpec {
}
it("should parse correctly: %s".format("-h --file f.txt arg arg2")) {
- assert (PP.parseArgv("-h --file f.txt arg arg2", options) ==
+ assert (PP.parseArgv("-h --file f.txt arg arg2".split("""\s+"""), options) ==
(options,
List(Option("-h","",0,BooleanValue(value = true)),
Option("-f","--file",1,StringValue("f.txt")),
@@ -279,7 +279,7 @@ class PatternParserFunSpec extends FunSpec {
}
it("should parse correctly: %s".format("-h arg -- -v")) {
- assert (PP.parseArgv("-h arg -- -v", options) ==
+ assert (PP.parseArgv("-h arg -- -v".split("""\s+"""), options) ==
(options,
List(Option("-h","",0,BooleanValue(value = true)),
Argument("", StringValue("arg")),
@@ -290,7 +290,7 @@ class PatternParserFunSpec extends FunSpec {
describe("long options error handling") {
it("it should intercept a non existant option") {
intercept[UnconsumedTokensException] {
- PP.docopt("Usage: prog", "--non-existant", help = false, version = "", optionsFirst = false)
+ PP.docopt("Usage: prog", Array("--non-existant"), help = false, version = "", optionsFirst = false)
}
}
@@ -300,7 +300,7 @@ class PatternParserFunSpec extends FunSpec {
--version
--verbose"""
intercept[RuntimeException] {
- PP.docopt(usage, "--ver", help = false, "", optionsFirst = false)
+ PP.docopt(usage, Array("--ver"), help = false, "", optionsFirst = false)
}
}
@@ -308,19 +308,19 @@ class PatternParserFunSpec extends FunSpec {
// since the option is defined to have an argument, the implicit ')' is
// consumed by the parseOption
intercept[MissingEnclosureException] {
- PP.docopt("Usage: prog --conflict\n\n--conflict ARG", "", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog --conflict\n\n--conflict ARG", Array(""), help = false, "", optionsFirst = false)
}
}
it("it should intercept a reversed conflicting definition") {
intercept[UnexpectedArgumentException] {
- PP.docopt("Usage: prog --long=ARG\n\n --long", "", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog --long=ARG\n\n --long", Array(""), help = false, "", optionsFirst = false)
}
}
it("it should intercept a missing argument") {
intercept[MissingArgumentException] {
- PP.docopt("Usage: prog --long ARG\n\n --long ARG", "--long", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog --long ARG\n\n --long ARG", Array("--long"), help = false, "", optionsFirst = false)
}
}
@@ -331,7 +331,7 @@ Usage: prog --derp
Options:
--derp"""
- PP.docopt(doc, "--derp=ARG", help = false, "", optionsFirst = false)
+ PP.docopt(doc, Array("--derp=ARG"), help = false, "", optionsFirst = false)
}
}
}
@@ -339,25 +339,25 @@ Options:
describe("short options error handling") {
it("it should detect conflicting definitions") {
intercept[UnparsableOptionException] {
- PP.docopt("Usage: prog -x\n\n-x this\n-x that", "", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog -x\n\n-x this\n-x that", Array(""), help = false, "", optionsFirst = false)
}
}
it("it should detect undefined options") {
intercept[UnconsumedTokensException] {
- PP.docopt("Usage: prog", "-x", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog", Array("-x"), help = false, "", optionsFirst = false)
}
}
it("it should detect conflicting definitions with arguments") {
intercept[MissingEnclosureException] {
- PP.docopt("Usage: prog -x\n\n-x ARG", "", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog -x\n\n-x ARG", Array(""), help = false, "", optionsFirst = false)
}
}
it("it should detect missing arguments") {
intercept[MissingArgumentException] {
- PP.docopt("Usage: prog -x ARG\n\n-x ARG", "-x", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog -x ARG\n\n-x ARG", Array("-x"), help = false, "", optionsFirst = false)
}
}
}
@@ -365,24 +365,24 @@ Options:
describe("[]|{}|() matching") {
it("it should detect missing ]") {
intercept[MissingEnclosureException] {
- PP.docopt("Usage: prog [a [b]", "", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog [a [b]", Array(""), help = false, "", optionsFirst = false)
}
}
it("it should detect extra )") {
intercept[UnconsumedTokensException] {
- PP.docopt("Usage: prog [a [b] ] c )", "", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog [a [b] ] c )", Array(""), help = false, "", optionsFirst = false)
}
}
}
describe("double-dash support") {
it("it should handle correctly '--'") {
- PP.docopt("Usage: prog [-o] [--] \n\n-o", "-- -o", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog [-o] [--] \n\n-o", "-- -o".split("""\s+"""), help = false, "", optionsFirst = false)
}
it("it should handle correctly '--' swapped") {
- PP.docopt("Usage: prog [-o] [--] \n\n -o", "-o 1", help = false, "", optionsFirst = false)
+ PP.docopt("Usage: prog [-o] [--] \n\n -o", "-o 1".split("""\s+"""), help = false, "", optionsFirst = false)
}
}
}