Skip to content

Commit 7ac2fbc

Browse files
committed
fix 2759: support params like @file.txt for dotc
1 parent 1d05796 commit 7ac2fbc

File tree

2 files changed

+78
-7
lines changed

2 files changed

+78
-7
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package dotty.tools.dotc
2+
package config
3+
4+
import scala.annotation.tailrec
5+
6+
/** A simple (overly so) command line parser.
7+
* !!! This needs a thorough test suite to make sure quoting is
8+
* done correctly and portably.
9+
*/
10+
object CommandLineParser {
11+
// splits a string into a quoted prefix and the rest of the string,
12+
// taking escaping into account (using \)
13+
// `"abc"def` will match as `DoubleQuoted(abc, def)`
14+
private class QuotedExtractor(quote: Char) {
15+
def unapply(in: String): Option[(String, String)] = {
16+
val del = quote.toString
17+
if (in startsWith del) {
18+
var escaped = false
19+
val (quoted, next) = (in substring 1) span {
20+
case `quote` if !escaped => false
21+
case '\\' if !escaped => escaped = true; true
22+
case _ => escaped = false; true
23+
}
24+
// the only way to get out of the above loop is with an empty next or !escaped
25+
// require(next.isEmpty || !escaped)
26+
if (next startsWith del) Some((quoted, next substring 1))
27+
else None
28+
} else None
29+
}
30+
}
31+
private object DoubleQuoted extends QuotedExtractor('"')
32+
private object SingleQuoted extends QuotedExtractor('\'')
33+
private val Word = """(\S+)(.*)""".r
34+
35+
// parse `in` for an argument, return it and the remainder of the input (or an error message)
36+
// (argument may be in single/double quotes, taking escaping into account, quotes are stripped)
37+
private def argument(in: String): Either[String, (String, String)] = in match {
38+
case DoubleQuoted(arg, rest) => Right((arg, rest))
39+
case SingleQuoted(arg, rest) => Right((arg, rest))
40+
case Word(arg, rest) => Right((arg, rest))
41+
case _ => Left(s"Illegal argument: $in")
42+
}
43+
44+
// parse a list of whitespace-separated arguments (ignoring whitespace in quoted arguments)
45+
@tailrec private def commandLine(in: String, accum: List[String] = Nil): Either[String, (List[String], String)] = {
46+
val trimmed = in.trim
47+
if (trimmed.isEmpty) Right((accum.reverse, ""))
48+
else argument(trimmed) match {
49+
case Right((arg, next)) =>
50+
(next span Character.isWhitespace) match {
51+
case("", rest) if rest.nonEmpty => Left("Arguments should be separated by whitespace.") // TODO: can this happen?
52+
case(ws, rest) => commandLine(rest, arg :: accum)
53+
}
54+
case Left(msg) => Left(msg)
55+
}
56+
}
57+
58+
class ParseException(msg: String) extends RuntimeException(msg)
59+
60+
def tokenize(line: String): List[String] = tokenize(line, x => throw new ParseException(x))
61+
def tokenize(line: String, errorFn: String => Unit): List[String] = {
62+
commandLine(line) match {
63+
case Right((args, _)) => args
64+
case Left(msg) => errorFn(msg) ; Nil
65+
}
66+
}
67+
}

compiler/src/dotty/tools/dotc/config/CompilerCommand.scala

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package dotty.tools.dotc
22
package config
33

4-
import java.io.File
4+
import java.nio.file.{Files, Paths}
55
import Settings._
66
import core.Contexts._
77
import util.DotClass
@@ -37,14 +37,18 @@ object CompilerCommand extends DotClass {
3737
* Expands all arguments starting with @ to the contents of the
3838
* file named like each argument.
3939
*/
40-
def expandArg(arg: String): List[String] = unsupported("expandArg")/*{
40+
def expandArg(arg: String): List[String] = {
4141
def stripComment(s: String) = s takeWhile (_ != '#')
42-
val file = File(arg stripPrefix "@")
43-
if (!file.exists)
44-
throw new java.io.FileNotFoundException("argument file %s could not be found" format file.name)
42+
val path = Paths.get(arg stripPrefix "@")
43+
if (!Files.exists(path))
44+
throw new java.io.FileNotFoundException("argument file %s could not be found" format path.getFileName)
4545

46-
settings splitParams (file.lines() map stripComment mkString " ")
47-
}*/
46+
import scala.collection.JavaConversions._
47+
val lines = Files.readAllLines(path) // default to UTF-8 encoding
48+
49+
val params = lines map stripComment mkString " "
50+
CommandLineParser.tokenize(params)
51+
}
4852

4953
// expand out @filename to the contents of that filename
5054
def expandedArguments = args.toList flatMap {

0 commit comments

Comments
 (0)