diff options
Diffstat (limited to 'src/dotty/tools/dotc/config/Settings.scala')
-rw-r--r-- | src/dotty/tools/dotc/config/Settings.scala | 238 |
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 |