diff --git a/docs/_docs/reference/experimental/main-annotation.md b/docs/_docs/reference/experimental/main-annotation.md index b3987fd62708..a7ab473cb980 100644 --- a/docs/_docs/reference/experimental/main-annotation.md +++ b/docs/_docs/reference/experimental/main-annotation.md @@ -31,8 +31,8 @@ object foo { ), args = args ) - val args0 = cmd.argGetter[Int](0, None) // using cmd.Parser[Int] - val args1 = cmd.varargGetter[Int] // using cmd.Parser[Int] + val args0 = cmd.argGetter[Int](0, None) // using a parser of Int + val args1 = cmd.varargGetter[Int] // using a parser of Int cmd.run(() => sum(args0(), args1()*)) } } @@ -47,17 +47,15 @@ Finally, the `run` method is called to run the application. It receives a by-nam Example of implementation of `myMain` that takes all arguments positionally. It used `util.CommandLineParser.FromString` and expects no default arguments. For simplicity, any errors in preprocessing or parsing results in crash. ```scala -class myMain extends MainAnnotation: - import MainAnnotation.{ ParameterInfo, Command } - - // Parser used to parse command line arguments - type Parser[T] = util.CommandLineParser.FromString[T] +// Parser used to parse command line arguments +import scala.util.CommandLineParser.FromString[T] - // Result type of the annotated method - type Result = Int +// Result type of the annotated method is Int +class myMain extends MainAnnotation[FromString, Int]: + import MainAnnotation.{ ParameterInfo, Command } /** A new command with arguments from `args` */ - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = + def command(info: CommandInfo, args: Array[String]): Command[FromString, Int] = if args.contains("--help") then println(info.documentation) // TODO: Print documentation of the parameters @@ -75,15 +73,15 @@ class myMain extends MainAnnotation: new MyCommand(plainArgs, varargs) @experimental - class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[util.CommandLineParser.FromString, Int]: + class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[FromString, Int]: - def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: Parser[T]): () => T = + def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: FromString[T]): () => T = () => parser.fromString(plainArgs(idx)) - def varargGetter[T](using parser: Parser[T]): () => Seq[T] = + def varargGetter[T](using parser: FromString[T]): () => Seq[T] = () => varargs.map(arg => parser.fromString(arg)) - def run(program: () => Result): Unit = + def run(program: () => Int): Unit = println("executing program") val result = program() println("result: " + result) diff --git a/library/src/scala/annotation/MainAnnotation.scala b/library/src/scala/annotation/MainAnnotation.scala index 998a5b882754..504e7133f560 100644 --- a/library/src/scala/annotation/MainAnnotation.scala +++ b/library/src/scala/annotation/MainAnnotation.scala @@ -40,19 +40,14 @@ package scala.annotation * } * } * ``` + * + * @param Parser The class used for argument string parsing and arguments into a `T` + * @param Result The required result type of the main method. + * If this type is Any or Unit, any type will be accepted. */ @experimental -trait MainAnnotation extends StaticAnnotation: - import MainAnnotation.* - - /** The class used for argument string parsing and arguments into a `T` */ - type Parser[T] - - /** The required result type of the main method. - * - * If this type is Any or Unit, any type will be accepted. - */ - type Result +trait MainAnnotation[Parser[_], Result] extends StaticAnnotation: + import MainAnnotation.{Command, CommandInfo} /** A new command with arguments from `args` * diff --git a/tests/run/main-annotation-example.scala b/tests/run/main-annotation-example.scala index 67d88afa3840..6e29da187b4d 100644 --- a/tests/run/main-annotation-example.scala +++ b/tests/run/main-annotation-example.scala @@ -1,5 +1,6 @@ import scala.annotation.* import collection.mutable +import scala.util.CommandLineParser.FromString /** Sum all the numbers * @@ -20,17 +21,11 @@ object Test: end Test @experimental -class myMain extends MainAnnotation: +class myMain extends MainAnnotation[FromString, Int]: import MainAnnotation.{ Command, CommandInfo, ParameterInfo } - // Parser used to parse command line arguments - type Parser[T] = util.CommandLineParser.FromString[T] - - // Result type of the annotated method - type Result = Int - /** A new command with arguments from `args` */ - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = + def command(info: CommandInfo, args: Array[String]): Command[FromString, Int] = if args.contains("--help") then println(info.documentation) System.exit(0) @@ -47,15 +42,15 @@ class myMain extends MainAnnotation: new MyCommand(plainArgs, varargs) @experimental - class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[util.CommandLineParser.FromString, Int]: + class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[FromString, Int]: - def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: Parser[T]): () => T = + def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: FromString[T]): () => T = () => parser.fromString(plainArgs(idx)) - def varargGetter[T](using parser: Parser[T]): () => Seq[T] = + def varargGetter[T](using parser: FromString[T]): () => Seq[T] = () => varargs.map(arg => parser.fromString(arg)) - def run(program: () => Result): Unit = + def run(program: () => Int): Unit = println("executing program") val result = program() println("result: " + result) diff --git a/tests/run/main-annotation-homemade-annot-1.scala b/tests/run/main-annotation-homemade-annot-1.scala index 086d8c01e3e0..177c48b00872 100644 --- a/tests/run/main-annotation-homemade-annot-1.scala +++ b/tests/run/main-annotation-homemade-annot-1.scala @@ -3,6 +3,7 @@ import scala.annotation.* import scala.collection.mutable import ExecutionContext.Implicits.global import duration._ +import util.CommandLineParser.FromString @mainAwait def get(wait: Int): Future[Int] = Future{ Thread.sleep(1000 * wait) @@ -28,20 +29,17 @@ object Test: end Test @experimental -class mainAwait(timeout: Int = 2) extends MainAnnotation: +class mainAwait(timeout: Int = 2) extends MainAnnotation[FromString, Future[Any]]: import MainAnnotation.* - override type Parser[T] = util.CommandLineParser.FromString[T] - override type Result = Future[Any] - // This is a toy example, it only works with positional args - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = - new Command[Parser, Result]: - override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = + def command(info: CommandInfo, args: Array[String]): Command[FromString, Future[Any]] = + new Command[FromString, Future[Any]]: + override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T = () => p.fromString(args(idx)) - override def varargGetter[T](using p: Parser[T]): () => Seq[T] = + override def varargGetter[T](using p: FromString[T]): () => Seq[T] = () => for i <- ((info.parameters.length-1) until args.length) yield p.fromString(args(i)) - override def run(f: () => Result): Unit = println(Await.result(f(), Duration(timeout, SECONDS))) + override def run(f: () => Future[Any]): Unit = println(Await.result(f(), Duration(timeout, SECONDS))) end mainAwait diff --git a/tests/run/main-annotation-homemade-annot-2.scala b/tests/run/main-annotation-homemade-annot-2.scala index 95778af81381..e55142622b32 100644 --- a/tests/run/main-annotation-homemade-annot-2.scala +++ b/tests/run/main-annotation-homemade-annot-2.scala @@ -1,5 +1,6 @@ import scala.collection.mutable import scala.annotation.* +import util.CommandLineParser.FromString @myMain()("A") def foo1(): Unit = println("I was run!") @@ -28,22 +29,19 @@ end Test // This is a toy example, it only works with positional args @experimental -class myMain(runs: Int = 3)(after: String*) extends MainAnnotation: +class myMain(runs: Int = 3)(after: String*) extends MainAnnotation[FromString, Any]: import MainAnnotation.* - override type Parser[T] = util.CommandLineParser.FromString[T] - override type Result = Any + def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] = + new Command[FromString, Any]: - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = - new Command[Parser, Result]: - - override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = + override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T = () => p.fromString(args(idx)) - override def varargGetter[T](using p: Parser[T]): () => Seq[T] = + override def varargGetter[T](using p: FromString[T]): () => Seq[T] = () => for i <- (info.parameters.length until args.length) yield p.fromString(args(i)) - override def run(f: () => Result): Unit = + override def run(f: () => Any): Unit = for (_ <- 1 to runs) f() if after.length > 0 then println(after.mkString(", ")) diff --git a/tests/run/main-annotation-homemade-annot-3.scala b/tests/run/main-annotation-homemade-annot-3.scala index 69d9bbdfa0c0..c5c0ef87adc2 100644 --- a/tests/run/main-annotation-homemade-annot-3.scala +++ b/tests/run/main-annotation-homemade-annot-3.scala @@ -1,4 +1,5 @@ import scala.annotation.* +import scala.util.CommandLineParser.FromString @mainNoArgs def foo() = println("Hello world!") @@ -10,17 +11,14 @@ object Test: end Test @experimental -class mainNoArgs extends MainAnnotation: +class mainNoArgs extends MainAnnotation[FromString, Any]: import MainAnnotation.* - override type Parser[T] = util.CommandLineParser.FromString[T] - override type Result = Any + def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] = + new Command[FromString, Any]: + override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T = ??? - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = - new Command[Parser, Result]: - override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = ??? + override def varargGetter[T](using p: FromString[T]): () => Seq[T] = ??? - override def varargGetter[T](using p: Parser[T]): () => Seq[T] = ??? - - override def run(program: () => Result): Unit = program() + override def run(program: () => Any): Unit = program() end command diff --git a/tests/run/main-annotation-homemade-annot-4.scala b/tests/run/main-annotation-homemade-annot-4.scala index 07a6f504401c..dad5e8dde1f4 100644 --- a/tests/run/main-annotation-homemade-annot-4.scala +++ b/tests/run/main-annotation-homemade-annot-4.scala @@ -1,4 +1,5 @@ import scala.annotation.* +import scala.util.CommandLineParser.FromString @mainManyArgs(1, "B", 3) def foo() = println("Hello world!") @@ -10,17 +11,14 @@ object Test: end Test @experimental -class mainManyArgs(i1: Int, s2: String, i3: Int) extends MainAnnotation: +class mainManyArgs(i1: Int, s2: String, i3: Int) extends MainAnnotation[FromString, Any]: import MainAnnotation.* - override type Parser[T] = util.CommandLineParser.FromString[T] - override type Result = Any + def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] = + new Command[FromString, Any]: + override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: FromString[T]): () => T = ??? - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = - new Command[Parser, Result]: - override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: Parser[T]): () => T = ??? + override def varargGetter[T](using p: FromString[T]): () => Seq[T] = ??? - override def varargGetter[T](using p: Parser[T]): () => Seq[T] = ??? - - override def run(program: () => Result): Unit = program() + override def run(program: () => Any): Unit = program() end command diff --git a/tests/run/main-annotation-homemade-annot-5.scala b/tests/run/main-annotation-homemade-annot-5.scala index 079e903e26f6..8bd16299019b 100644 --- a/tests/run/main-annotation-homemade-annot-5.scala +++ b/tests/run/main-annotation-homemade-annot-5.scala @@ -1,4 +1,5 @@ import scala.annotation.* +import scala.util.CommandLineParser.FromString @mainManyArgs(Some(1)) def foo() = println("Hello world!") @mainManyArgs(None) def bar() = println("Hello world!") @@ -12,17 +13,14 @@ object Test: end Test @experimental -class mainManyArgs(o: Option[Int]) extends MainAnnotation: +class mainManyArgs(o: Option[Int]) extends MainAnnotation[FromString, Any]: import MainAnnotation.* - override type Parser[T] = util.CommandLineParser.FromString[T] - override type Result = Any + def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] = + new Command[FromString, Any]: + override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T = ??? - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = - new Command[Parser, Result]: - override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = ??? + override def varargGetter[T](using p: FromString[T]): () => Seq[T] = ??? - override def varargGetter[T](using p: Parser[T]): () => Seq[T] = ??? - - override def run(program: () => Result): Unit = program() + override def run(program: () => Any): Unit = program() end command diff --git a/tests/run/main-annotation-homemade-annot-6.scala b/tests/run/main-annotation-homemade-annot-6.scala index b55117d39005..2bb7b16855e0 100644 --- a/tests/run/main-annotation-homemade-annot-6.scala +++ b/tests/run/main-annotation-homemade-annot-6.scala @@ -17,13 +17,10 @@ object Test: end Test @experimental -class myMain extends MainAnnotation: +class myMain extends MainAnnotation[Make, Any]: import MainAnnotation.* - override type Parser[T] = Make[T] - override type Result = Any - - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = + def command(info: CommandInfo, args: Array[String]): Command[Make, Any] = def paramInfoString(paramInfo: ParameterInfo) = import paramInfo.* s" ParameterInfo(name=\"$name\", typeName=\"$typeName\", hasDefault=$hasDefault, isVarargs=$isVarargs, documentation=\"$documentation\", annotations=$annotations)" @@ -34,16 +31,16 @@ class myMain extends MainAnnotation: | "${info.documentation}", | ${info.parameters.map(paramInfoString).mkString("Seq(\n", ",\n", "\n )*")} |)""".stripMargin) - new Command[Parser, Result]: - override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = + new Command[Make, Any]: + override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Make[T]): () => T = println(s"argGetter($idx, ${defaultArgument.map(_())})") () => p.make - override def varargGetter[T](using p: Parser[T]): () => Seq[T] = + override def varargGetter[T](using p: Make[T]): () => Seq[T] = println("varargGetter()") () => Seq(p.make, p.make) - override def run(f: () => Result): Unit = + override def run(f: () => Any): Unit = println("run()") f() println() diff --git a/tests/run/main-annotation-newMain.scala b/tests/run/main-annotation-newMain.scala index 448dc7bd1e42..38b1e8f7f805 100644 --- a/tests/run/main-annotation-newMain.scala +++ b/tests/run/main-annotation-newMain.scala @@ -1,5 +1,6 @@ import scala.annotation.* import collection.mutable +import scala.util.CommandLineParser.FromString @newMain def happyBirthday(age: Int, name: String, others: String*) = val suffix = @@ -29,15 +30,12 @@ end Test @experimental -final class newMain extends MainAnnotation: +final class newMain extends MainAnnotation[FromString, Any]: import newMain._ import MainAnnotation._ - override type Parser[T] = util.CommandLineParser.FromString[T] - override type Result = Any - - def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] = - new Command[Parser, Result]: + def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] = + new Command[FromString, Any]: private inline val argMarker = "--" private inline val shortArgMarker = "-" @@ -133,7 +131,7 @@ final class newMain extends MainAnnotation: case s => argMarker + s } - private def convert[T](argName: String, arg: String)(using p: Parser[T]): () => T = + private def convert[T](argName: String, arg: String)(using p: FromString[T]): () => T = p.fromStringOption(arg) match case Some(t) => () => t case None => error(s"invalid argument for $argName: $arg") @@ -233,7 +231,7 @@ final class newMain extends MainAnnotation: private def getInvalidNames(paramInfos: ParameterInfo): Seq[String | Char] = getAliases(paramInfos).filter(name => !nameIsValid(name) && !shortNameIsValid(name)) - override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: Parser[T]): () => T = + override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: FromString[T]): () => T = val name = info.parameters(idx).name val parameterInfo = nameToParameterInfo(name) // TODO: Decide which string is associated with this arg when constructing the command. @@ -259,7 +257,7 @@ final class newMain extends MainAnnotation: } end argGetter - override def varargGetter[T](using p: Parser[T]): () => Seq[T] = + override def varargGetter[T](using p: FromString[T]): () => Seq[T] = val name = info.parameters.last.name // TODO: Decide which strings are associated with the varargs when constructing the command. // Here we should only get the strings for this argument, apply them to the parser and handle parsing errors. @@ -269,7 +267,7 @@ final class newMain extends MainAnnotation: // First take arguments passed by name, then those passed by position () => (byNameGetters ++ positionalGetters).map(_()) - override def run(f: () => Result): Unit = + override def run(f: () => Any): Unit = // Check aliases unicity val nameAndCanonicalName = nameToParameterInfo.toList.flatMap { case (canonicalName, infos) => (canonicalName +: getAlternativeNames(infos) ++: getShortNames(infos)).map(_ -> canonicalName)