aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/config/Settings.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-02-03 11:59:38 +0100
committerMartin Odersky <odersky@gmail.com>2013-02-03 11:59:38 +0100
commit8ac1b32ad3b190de82f5c4f1d1666f093ad8c20f (patch)
tree024b841641152deb5743d678885afae68ba8647b /src/dotty/tools/dotc/config/Settings.scala
parentabc8f5e1c382b1ead761976227878c4c38ebfbf5 (diff)
downloaddotty-8ac1b32ad3b190de82f5c4f1d1666f093ad8c20f.tar.gz
dotty-8ac1b32ad3b190de82f5c4f1d1666f093ad8c20f.tar.bz2
dotty-8ac1b32ad3b190de82f5c4f1d1666f093ad8c20f.zip
Integration of settings, platform, pathresolver, etc.
Diffstat (limited to 'src/dotty/tools/dotc/config/Settings.scala')
-rw-r--r--src/dotty/tools/dotc/config/Settings.scala238
1 files changed, 226 insertions, 12 deletions
diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala
index 83c7a747f..eff9c245f 100644
--- a/src/dotty/tools/dotc/config/Settings.scala
+++ b/src/dotty/tools/dotc/config/Settings.scala
@@ -1,26 +1,240 @@
package dotty.tools.dotc
package config
+import collection.mutable.{ ArrayBuffer }
+import scala.util.{ Try, Success, Failure }
+import scala.reflect.internal.util.StringOps
+import reflect.ClassTag
+import core.Contexts._
-class Settings {
+object Settings {
- protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".")
+ val BooleanTag = ClassTag.Boolean
+ val IntTag = ClassTag.Int
+ val StringTag = ClassTag(classOf[String])
+ val ListTag = ClassTag(classOf[List[_]])
- protected implicit def mkSetting[T](x: T): Setting[T] = new Setting(x)
+ class SettingsState(initialValues: Seq[Any]) {
+ private var values = ArrayBuffer(initialValues: _*)
+ private var _wasRead: Boolean = false
- var default: Settings = this
+ def value(idx: Int) = {
+ _wasRead = true
+ values
+ }
- var classpath: Setting[String] = defaultClasspath
- var debug: Setting[Boolean] = false
- var verbose: Setting[Boolean] = false
+ def update(idx: Int, x: Any): SettingsState =
+ if (_wasRead)
+ new SettingsState(values).update(idx, x)
+ else {
+ values(idx) = x
+ this
+ }
+ }
- var XmaxClassfileName: Setting[Int] = 255
+ case class ArgsSummary(
+ sstate: SettingsState,
+ arguments: List[String],
+ errors: List[String]) {
- var YtermConflict: Setting[String] = "error"
+ def fail(msg: String) =
+ ArgsSummary(sstate, arguments, errors :+ msg)
+ }
- def processArguments(arguments: List[String], processAll: Boolean): (Boolean, List[String]) = ???
+ case class Setting[T: ClassTag] private[Settings] (
+ name: String,
+ description: String,
+ default: T,
+ helpArg: String = "",
+ choices: Seq[T] = Nil,
+ prefix: String = "",
+ aliases: List[String] = Nil,
+ depends: List[(Setting[_], Any)] = Nil)(private[Settings] val idx: Int) {
+ def withAbbreviation(abbrv: String): Setting[T] =
+ copy(aliases = aliases :+ abbrv)(idx)
-}
+ def dependsOn[U](setting: Setting[U], value: U): Setting[T] =
+ copy(depends = depends :+ (setting, value))(idx)
-case class Setting[T](value: T) \ No newline at end of file
+ def valueIn(state: SettingsState): T =
+ state.value(idx).asInstanceOf[T]
+
+ def updateIn(state: SettingsState, x: Any): SettingsState = x match {
+ case _: T => state.update(idx, x)
+ case _ => throw new ClassCastException("illegal argument")
+ }
+
+ def isDefaultIn(state: SettingsState) = valueIn(state) == default
+
+ def legalChoices: String =
+ if (choices.isEmpty) ""
+ else choices match {
+ case r: Range => r.head + ".." + r.last
+ case xs: List[_] => xs.mkString(", ")
+ }
+
+ def isLegal(arg: Any): Boolean =
+ if (choices.isEmpty)
+ arg match {
+ case _: T => true
+ case _ => false
+ }
+ else choices match {
+ case r: Range =>
+ arg match {
+ case x: Int => r.head <= x && x <= r.last
+ case _ => false
+ }
+ case xs: List[_] =>
+ xs contains arg
+ }
+
+ def tryToSet(state: ArgsSummary): ArgsSummary = {
+ val ArgsSummary(sstate, arg :: args, errors) = state
+ def update(value: Any, args: List[String]) =
+ ArgsSummary(updateIn(sstate, value), args, errors)
+ def fail(msg: String, args: List[String]) =
+ ArgsSummary(sstate, args, errors :+ msg)
+ def missingArg =
+ fail(s"missing argument for option $name", args)
+ def doSet(argRest: String) = (implicitly[ClassTag[T]], args) match {
+ case (BooleanTag, _) =>
+ update(true, args)
+ case (ListTag, _) =>
+ if (argRest.isEmpty) missingArg
+ else update((argRest split ",").toList, args)
+ case (StringTag, arg2 :: args2) =>
+ if (choices.nonEmpty && !(choices contains arg2))
+ fail(s"$arg2 is not a valid choice for $name", args2)
+ else
+ update(arg2, args2)
+ case (IntTag, arg2 :: args2) =>
+ try {
+ val x = arg.toInt
+ choices match {
+ case r: Range if x < r.head || r.last < x =>
+ fail(s"$arg2 is out of legal range $legalChoices for $name", args2)
+ case _ =>
+ update(x, args2)
+ }
+ } catch {
+ case _: NumberFormatException =>
+ fail(s"$arg2 is not an integer argument for $name", args2)
+ }
+ case (_, Nil) =>
+ missingArg
+ }
+
+ if (prefix != "" && arg.startsWith(prefix))
+ doSet(arg drop prefix.length)
+ else if (prefix == "" && name == arg.takeWhile(_ != ':'))
+ doSet(arg.dropWhile(_ != ':').drop(1))
+ else
+ state
+ }
+ }
+
+ object Setting {
+ implicit class SettingDecorator[T](val setting: Setting[T]) extends AnyVal {
+ def value(implicit ctx: Context): T = setting.valueIn(ctx.sstate)
+ def update(x: T)(implicit ctx: Context): SettingsState = setting.updateIn(ctx.sstate, x)
+ def isDefault(implicit ctx: Context): Boolean = setting.isDefaultIn(ctx.sstate)
+ }
+ }
+
+ class SettingGroup {
+
+ val _allSettings = new ArrayBuffer[Setting[_]]
+ def allSettings: Seq[Setting[_]] = _allSettings
+
+ def defaultState = new SettingsState(allSettings map (_.default))
+
+ def userSetSettings(state: SettingsState) =
+ allSettings filterNot (_.isDefaultIn(state))
+
+ def toConciseString(state: SettingsState) =
+ userSetSettings(state).mkString("(", " ", ")")
+
+ private def checkDependencies(state: ArgsSummary): ArgsSummary =
+ (state /: userSetSettings(state.sstate))(checkDependenciesOfSetting)
+
+ private def checkDependenciesOfSetting(state: ArgsSummary, setting: Setting[_]) =
+ (state /: setting.depends) { (s, dep) =>
+ val (depSetting, reqValue) = dep
+ if (depSetting.valueIn(state.sstate) == reqValue) s
+ else s.fail(s"incomplete option ${setting.name} (requires ${depSetting.name})")
+ }
+
+ /** Iterates over the arguments applying them to settings where applicable.
+ * Then verifies setting dependencies are met.
+ *
+ * This temporarily takes a boolean indicating whether to keep
+ * processing if an argument is seen which is not a command line option.
+ * This is an expedience for the moment so that you can say
+ *
+ * scalac -d /tmp foo.scala -optimise
+ *
+ * while also allowing
+ *
+ * scala Program opt opt
+ *
+ * to get their arguments.
+ */
+ protected def processArguments(state: ArgsSummary, processAll: Boolean, skipped: List[String]): ArgsSummary = {
+ def stateWithArgs(args: List[String]) = ArgsSummary(state.sstate, args, state.errors)
+ state.arguments match {
+ case Nil =>
+ checkDependencies(stateWithArgs(skipped))
+ case "--" :: args =>
+ checkDependencies(stateWithArgs(skipped ++ args))
+ case x :: _ if x startsWith "-" =>
+ def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
+ case setting :: settings1 =>
+ val state1 = setting.tryToSet(state)
+ if (state1 ne state) processArguments(state1, processAll, skipped)
+ else loop(settings1)
+ case Nil =>
+ state.fail(s"bad option: '$x'")
+ }
+ loop(allSettings.toList)
+ case arg :: args =>
+ if (processAll) processArguments(stateWithArgs(args), processAll, skipped :+ arg)
+ else state
+ }
+ }
+
+ def processArguments(arguments: List[String], processAll: Boolean)(implicit ctx: Context): ArgsSummary =
+ processArguments(ArgsSummary(ctx.sstate, arguments, Nil), processAll, Nil)
+
+ def publish[T](settingf: Int => Setting[T]): Setting[T] = {
+ val setting = settingf(_allSettings.length)
+ _allSettings += setting
+ setting
+ }
+
+ def BooleanSetting(name: String, descr: String): Setting[Boolean] =
+ publish(Setting(name, descr, false))
+
+ def StringSetting(name: String, helpArg: String, descr: String, default: String): Setting[String] =
+ publish(Setting(name, descr, default, helpArg))
+
+ def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): Setting[String] =
+ publish(Setting(name, descr, default, helpArg, choices))
+
+ def IntSetting(name: String, descr: String, default: Int, range: Seq[Int]): Setting[Int] =
+ publish(Setting(name, descr, default, choices = range))
+
+ def MultiStringSetting(name: String, helpArg: String, descr: String): Setting[List[String]] =
+ publish(Setting(name, descr, Nil, helpArg))
+
+ def PathSetting(name: String, descr: String, default: String): Setting[String] =
+ publish(Setting(name, descr, default))
+
+ def PhasesSetting(name: String, descr: String, default: String = ""): Setting[List[String]] =
+ publish(Setting(name, descr, if (default.isEmpty) Nil else List(default)))
+
+ def PrefixSetting(name: String, pre: String, descr: String): Setting[List[String]] =
+ publish(Setting(name, descr, Nil, prefix = pre))
+ }
+} \ No newline at end of file