summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/cmd/CommandLineParser.scala
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-06-24 13:55:02 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-06-25 08:56:54 -0700
commitd863c7152cf8c363b4f9c086c62d28b37baf07ea (patch)
treeaa4199b059aa7632f268ab41fc3dac42507f8489 /src/compiler/scala/tools/cmd/CommandLineParser.scala
parent2a17db002758379fe0b9ee2a4e41ac9e3ca6c30d (diff)
downloadscala-d863c7152cf8c363b4f9c086c62d28b37baf07ea.tar.gz
scala-d863c7152cf8c363b4f9c086c62d28b37baf07ea.tar.bz2
scala-d863c7152cf8c363b4f9c086c62d28b37baf07ea.zip
Remove dependency on combinators from CommandLinerParser.
tools.cmd.CommandLineParser uses a small hand-rolled parser TODO: replace partest's usage of scala.tools.nsc.util.CommandLine by scala.tools.cmd.CommandLine
Diffstat (limited to 'src/compiler/scala/tools/cmd/CommandLineParser.scala')
-rw-r--r--src/compiler/scala/tools/cmd/CommandLineParser.scala72
1 files changed, 72 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/cmd/CommandLineParser.scala b/src/compiler/scala/tools/cmd/CommandLineParser.scala
new file mode 100644
index 0000000000..ef55178594
--- /dev/null
+++ b/src/compiler/scala/tools/cmd/CommandLineParser.scala
@@ -0,0 +1,72 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package cmd
+
+import scala.annotation.tailrec
+
+/** A simple (overly so) command line parser.
+ * !!! This needs a thorough test suite to make sure quoting is
+ * done correctly and portably.
+ */
+object CommandLineParser {
+ // splits a string into a quoted prefix and the rest of the string,
+ // taking escaping into account (using \)
+ // `"abc"def` will match as `DoubleQuoted(abc, def)`
+ private class QuotedExtractor(quote: Char) {
+ def unapply(in: String): Option[(String, String)] = {
+ val del = quote.toString
+ if (in startsWith del) {
+ var escaped = false
+ val (quoted, next) = (in substring 1) span {
+ case `quote` if !escaped => false
+ case '\\' if !escaped => escaped = true; true
+ case _ => escaped = false; true
+ }
+ // the only way to get out of the above loop is with an empty next or !escaped
+ // require(next.isEmpty || !escaped)
+ if (next startsWith del) Some((quoted, next substring 1))
+ else None
+ } else None
+ }
+ }
+ private object DoubleQuoted extends QuotedExtractor('"')
+ private object SingleQuoted extends QuotedExtractor('\'')
+ private val Word = """(\S+)(.*)""".r
+
+ // parse `in` for an argument, return it and the remainder of the input (or an error message)
+ // (argument may be in single/double quotes, taking escaping into account, quotes are stripped)
+ private def argument(in: String): Either[String, (String, String)] = in match {
+ case DoubleQuoted(arg, rest) => Right(arg, rest)
+ case SingleQuoted(arg, rest) => Right(arg, rest)
+ case Word(arg, rest) => Right(arg, rest)
+ case _ => Left("Illegal argument: "+ in)
+ }
+
+ // parse a list of whitespace-separated arguments (ignoring whitespace in quoted arguments)
+ @tailrec private def commandLine(in: String, accum: List[String] = Nil): Either[String, (List[String], String)] = {
+ val trimmed = in.trim
+ if (trimmed.isEmpty) Right(accum.reverse, "")
+ else argument(trimmed) match {
+ case Right((arg, next)) =>
+ (next span Character.isWhitespace) match {
+ case("", rest) if rest.nonEmpty => Left("Arguments should be separated by whitespace.") // TODO: can this happen?
+ case(ws, rest) => commandLine(rest, arg :: accum)
+ }
+ case Left(msg) => Left(msg)
+ }
+ }
+
+ class ParseException(msg: String) extends RuntimeException(msg)
+
+ def tokenize(line: String): List[String] = tokenize(line, x => throw new ParseException(x))
+ def tokenize(line: String, errorFn: String => Unit): List[String] = {
+ commandLine(line) match {
+ case Right((args, _)) => args
+ case Left(msg) => errorFn(msg) ; Nil
+ }
+ }
+}