diff options
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/util/CommandLineParser.scala | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/util/CommandLineParser.scala b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala new file mode 100644 index 0000000000..16d79d5776 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/CommandLineParser.scala @@ -0,0 +1,145 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package util + +import scala.util.parsing.combinator._ +import scala.util.parsing.input.{ Reader } +import scala.util.parsing.input.CharArrayReader.EofCh +import scala.collection.mutable.ListBuffer + +/** A simple command line parser to replace the several different + * simple ones spread around trunk. + * + * XXX Note this has been completely obsolesced by scala.tools.cmd. + * I checked it back in as part of rolling partest back a month + * rather than go down the rabbit hole of unravelling dependencies. + */ + +trait ParserUtil extends Parsers { + class ParserPlus[+T](underlying: Parser[T]) { + def !~>[U](p: => Parser[U]): Parser[U] = (underlying ~! p) ^^ { case a~b => b } + def <~![U](p: => Parser[U]): Parser[T] = (underlying ~! p) ^^ { case a~b => a } + } + protected implicit def parser2parserPlus[T](p: Parser[T]): ParserPlus[T] = new ParserPlus(p) +} + +case class CommandLine( + args: List[String], + unaryArguments: List[String], + binaryArguments: List[String] +) { + def this(args: List[String]) = this(args, Nil, Nil) + def this(args: Array[String]) = this(args.toList, Nil, Nil) + def this(line: String) = this(CommandLineParser tokenize line, Nil, Nil) + + def withUnaryArgs(xs: List[String]) = copy(unaryArguments = xs) + def withBinaryArgs(xs: List[String]) = copy(binaryArguments = xs) + + def originalArgs = args + def assumeBinary = true + def enforceArity = true + def onlyKnownOptions = false + + val Terminator = "--" + val ValueForUnaryOption = "true" // so if --opt is given, x(--opt) = true + + def mapForUnary(opt: String) = Map(opt -> ValueForUnaryOption) + def errorFn(msg: String) = println(msg) + + /** argMap is option -> argument (or "" if it is a unary argument) + * residualArgs are what is left after removing the options and their args. + */ + lazy val (argMap, residualArgs) = { + val residualBuffer = new ListBuffer[String] + + def stripQuotes(s: String) = { + def isQuotedBy(c: Char) = s.length > 0 && s.head == c && s.last == c + if (List('"', '\'') exists isQuotedBy) s.tail.init else s + } + + def isValidOption(s: String) = !onlyKnownOptions || (unaryArguments contains s) || (binaryArguments contains s) + def isOption(s: String) = (s startsWith "-") && (isValidOption(s) || { unknownOption(s) ; false }) + def isUnary(s: String) = isOption(s) && (unaryArguments contains s) + def isBinary(s: String) = isOption(s) && !isUnary(s) && (assumeBinary || (binaryArguments contains s)) + + def unknownOption(opt: String) = + errorFn("Option '%s' not recognized.".format(opt)) + def missingArg(opt: String, what: String) = + errorFn("Option '%s' requires argument, found %s instead.".format(opt, what)) + + def loop(args: List[String]): Map[String, String] = { + def residual(xs: List[String]) = { residualBuffer ++= xs ; Map[String, String]() } + if (args.isEmpty) return Map() + val hd :: rest = args + if (rest.isEmpty) { + if (isBinary(hd) && enforceArity) + missingArg(hd, "EOF") + + if (isOption(hd)) mapForUnary(hd) else residual(args) + } + else + if (hd == Terminator) residual(rest) + else { + val hd1 :: hd2 :: rest = args + + if (hd2 == Terminator) mapForUnary(hd1) ++ residual(rest) + else if (isUnary(hd1)) mapForUnary(hd1) ++ loop(hd2 :: rest) + else if (isBinary(hd1)) { + // Disabling this check so + // --scalacopts "-verbose" works. We can't tell if it's quoted, + // the shell does us in. + // + // if (isOption(hd2) && enforceArity) + // missingArg(hd1, hd2) + + Map(hd1 -> hd2) ++ loop(rest) + } + else { residual(List(hd1)) ++ loop(hd2 :: rest) } + } + } + + (loop(args), residualBuffer map stripQuotes toList) + } + + def isSet(arg: String) = args contains arg + def get(arg: String) = argMap get arg + def getOrElse(arg: String, orElse: => String) = if (isSet(arg)) apply(arg) else orElse + def apply(arg: String) = argMap(arg) + + override def toString() = "CommandLine(\n%s)\n" format (args map (" " + _ + "\n") mkString) +} + +object CommandLineParser extends RegexParsers with ParserUtil { + override def skipWhitespace = false + + def elemExcept(xs: Elem*): Parser[Elem] = elem("elemExcept", x => x != EofCh && !(xs contains x)) + def elemOf(xs: Elem*): Parser[Elem] = elem("elemOf", xs contains _) + def escaped(ch: Char): Parser[String] = "\\" + ch + def mkQuoted(ch: Char): Parser[String] = ( + elem(ch) !~> rep(escaped(ch) | elemExcept(ch)) <~ ch ^^ (_.mkString) + | failure("Unmatched %s in input." format ch) + ) + + /** Apparently windows can't deal with the quotes sticking around. */ + lazy val squoted: Parser[String] = mkQuoted('\'') // ^^ (x => "'%s'" format x) + lazy val dquoted: Parser[String] = mkQuoted('"') // ^^ (x => "\"" + x + "\"") + lazy val token: Parser[String] = """\S+""".r + + lazy val argument: Parser[String] = squoted | dquoted | token + lazy val commandLine: Parser[List[String]] = phrase(repsep(argument, whiteSpace)) + + 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] = { + parse(commandLine, line.trim) match { + case Success(args, _) => args + case NoSuccess(msg, rest) => errorFn(msg) ; Nil + } + } + def apply(line: String) = new CommandLine(tokenize(line)) +} |