Skip to content

Commit 9600ec3

Browse files
committed
Add CommandInfo
1 parent f8ed249 commit 9600ec3

12 files changed

+100
-77
lines changed

compiler/src/dotty/tools/dotc/ast/MainProxies.scala

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,15 @@ object MainProxies {
149149
* final class f {
150150
* static def main(args: Array[String]): Unit = {
151151
* val cmd = new myMain(80).command(
152-
* args,
153-
* "f",
154-
* "Lorem ipsum dolor sit amet consectetur adipiscing elit.",
155-
* new scala.annotation.MainAnnotation.ParameterInfo("x", "S", false, false, "my param x", Seq(new scala.main.Alias("myX")))
156-
* new scala.annotation.MainAnnotation.ParameterInfo("ys", "T", false, false, "all my params y", Seq())
152+
* info = new CommandInfo(
153+
* name = "f",
154+
* documentation = "Lorem ipsum dolor sit amet consectetur adipiscing elit.",
155+
* parameters = Seq(
156+
* new scala.annotation.MainAnnotation.ParameterInfo("x", "S", false, false, "my param x", Seq(new scala.main.Alias("myX")))
157+
* new scala.annotation.MainAnnotation.ParameterInfo("ys", "T", false, false, "all my params y", Seq())
158+
* )
159+
* )
160+
* args = args
157161
* )
158162
*
159163
* val args0: () => S = cmd.argGetter[S](0, None)
@@ -310,16 +314,22 @@ object MainProxies {
310314
end instantiateAnnotation
311315

312316
def generateMainClass(mainCall: Tree, args: List[Tree], parameterInfos: List[Tree]): TypeDef =
317+
val cmdInfo =
318+
val nameTree = Literal(Constant(mainFun.showName))
319+
val docTree = Literal(Constant(documentation.mainDoc))
320+
val paramInfos = Apply(ref(defn.SeqModule.termRef), parameterInfos)
321+
New(TypeTree(defn.MainAnnotationCommandInfo.typeRef), List(List(nameTree, docTree, paramInfos)))
322+
313323
val cmd = ValDef(
314324
nme.cmd,
315325
TypeTree(),
316326
Apply(
317327
Select(instantiateAnnotation(mainAnnot), defn.MainAnnotation_command.name),
318-
Ident(nme.args) :: Literal(Constant(mainFun.showName)) :: Literal(Constant(documentation.mainDoc)) :: parameterInfos
328+
List(cmdInfo, Ident(nme.args))
319329
)
320330
)
321331
val run = Apply(Select(Ident(nme.cmd), defn.MainAnnotationCommand_run.name), mainCall)
322-
val body = Block(cmd :: args, run)
332+
val body = Block(cmdInfo :: cmd :: args, run)
323333
val mainArg = ValDef(nme.args, TypeTree(defn.ArrayType.appliedTo(defn.StringType)), EmptyTree)
324334
.withFlags(Param)
325335
/** Replace typed `Ident`s that have been typed with a TypeSplice with the reference to the symbol.

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ class Definitions {
853853

854854
@tu lazy val MainAnnotationClass: ClassSymbol = requiredClass("scala.annotation.MainAnnotation")
855855
@tu lazy val MainAnnotation_command: Symbol = MainAnnotationClass.requiredMethod("command")
856+
@tu lazy val MainAnnotationCommandInfo: ClassSymbol = requiredClass("scala.annotation.MainAnnotation.CommandInfo")
856857
@tu lazy val MainAnnotationParameterInfo: ClassSymbol = requiredClass("scala.annotation.MainAnnotation.ParameterInfo")
857858
@tu lazy val MainAnnotationParameterInfo_withDocumentation: Symbol = MainAnnotationParameterInfo.requiredMethod("withDocumentation")
858859
@tu lazy val MainAnnotationParameterInfo_withAnnotations: Symbol = MainAnnotationParameterInfo.requiredMethod("withAnnotations")

docs/_docs/reference/experimental/main-annotation.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ When a users annotates a method with an annotation that extends `MainAnnotation`
1919
```scala
2020
object foo {
2121
def main(args: Array[String]): Unit = {
22+
2223
val cmd = new myMain().command(
23-
args = args,
24-
commandName = "sum",
25-
documentation = "Sum all the numbers",
26-
new ParameterInfo("first", "scala.Int", hasDefault=false, isVarargs=false, "Fist number to sum", Seq()),
27-
new ParameterInfo("rest", "scala.Int" , hasDefault=false, isVarargs=true, "The rest of the numbers to sum", Seq())
24+
info = new CommandInfo(
25+
name = "sum",
26+
documentation = "Sum all the numbers",
27+
parameters = Seq(
28+
new ParameterInfo("first", "scala.Int", hasDefault=false, isVarargs=false, "Fist number to sum", Seq()),
29+
new ParameterInfo("rest", "scala.Int" , hasDefault=false, isVarargs=true, "The rest of the numbers to sum", Seq())
30+
)
31+
),
32+
args = args
2833
)
2934
val args0 = cmd.argGetter[Int](0, None) // using cmd.Parser[Int]
3035
val args1 = cmd.varargGetter[Int] // using cmd.Parser[Int]
@@ -52,20 +57,20 @@ class myMain extends MainAnnotation:
5257
type Result = Int
5358

5459
/** A new command with arguments from `args` */
55-
def command(args: Array[String], commandName: String, documentation: String, parameterInfos: ParameterInfo*): Command[Parser, Result] =
60+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
5661
if args.contains("--help") then
57-
println(documentation)
62+
println(info.documentation)
5863
// TODO: Print documentation of the parameters
5964
System.exit(0)
60-
assert(parameterInfos.forall(!_.hasDefault), "Default arguments are not supported")
65+
assert(info.parameters.forall(!_.hasDefault), "Default arguments are not supported")
6166
val (plainArgs, varargs) =
62-
if parameterInfos.last.isVarargs then
63-
val numPlainArgs = parameterInfos.length - 1
67+
if info.parameters.last.isVarargs then
68+
val numPlainArgs = info.parameters.length - 1
6469
assert(numPlainArgs <= args.length, "Not enough arguments")
6570
(args.take(numPlainArgs), args.drop(numPlainArgs))
6671
else
67-
assert(parameterInfos.length <= args.length, "Not enough arguments")
68-
assert(parameterInfos.length >= args.length, "Too many arguments")
72+
assert(info.parameters.length <= args.length, "Not enough arguments")
73+
assert(info.parameters.length >= args.length, "Too many arguments")
6974
(args, Array.empty[String])
7075
new MyCommand(plainArgs, varargs)
7176

library/src/scala/annotation/MainAnnotation.scala

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ package scala.annotation
2424
* object foo {
2525
* def main(args: Array[String]): Unit = {
2626
* val cmd = new myMain().command(
27-
* args = args,
28-
* commandName = "sum",
29-
* documentation = "Sum all the numbers",
30-
* new ParameterInfo("first", "scala.Int", hasDefault=false, isVarargs=false, "Fist number to sum"),
31-
* new ParameterInfo("rest", "scala.Int" , hasDefault=false, isVarargs=true, "The rest of the numbers to sum")
27+
* info = new CommandInfo(
28+
* name = "foo.main",
29+
* documentation = "Sum all the numbers",
30+
* parameters = Seq(
31+
* new ParameterInfo("first", "scala.Int", hasDefault=false, isVarargs=false, "Fist number to sum"),
32+
* new ParameterInfo("rest", "scala.Int" , hasDefault=false, isVarargs=true, "The rest of the numbers to sum")
33+
* )
34+
* )
35+
* args = args
3236
* )
3337
* val args0 = cmd.argGetter[Int](0, None) // using cmd.Parser[Int]
3438
* val args1 = cmd.varargGetter[Int] // using cmd.Parser[Int]
@@ -52,18 +56,38 @@ trait MainAnnotation extends StaticAnnotation:
5256

5357
/** A new command with arguments from `args`
5458
*
59+
* @param info The information about the command (name, documentation and info about parameters)
5560
* @param args The command line arguments
56-
* @param commandName The name of the command (name of the annotated method)
57-
* @param documentation The documentation of the command (without the `@param` documentation)
58-
* @param parameterInfos Information about all the parameters (name, type, has default, is varargs and documentation)
5961
*/
60-
def command(args: Array[String], commandName: String, documentation: String, parameterInfos: ParameterInfo*): Command[Parser, Result]
62+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result]
6163

6264
end MainAnnotation
6365

6466
@experimental
6567
object MainAnnotation:
6668

69+
/** A class representing a command to run */
70+
trait Command[Parser[_], Result]:
71+
72+
/** The getter for the `idx`th argument of type `T`
73+
*
74+
* @param idx The index of the argument
75+
* @param defaultArgument Optional lambda to instantiate the default argument
76+
*/
77+
def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using Parser[T]): () => T
78+
79+
/** The getter for a final varargs argument of type `T*` */
80+
def varargGetter[T](using Parser[T]): () => Seq[T]
81+
82+
/** Run `program` if all arguments are valid if all arguments are valid
83+
*
84+
* @param program A function containing the call to the main method and instantiation of its arguments
85+
*/
86+
def run(program: () => Result): Unit
87+
end Command
88+
89+
final class CommandInfo(val name: String, val documentation: String, val parameters: Seq[ParameterInfo])
90+
6791
/** ParameterInfo with a name, the type of the parameter and if it has a default
6892
*
6993
* @param name The name of the parameter
@@ -79,7 +103,7 @@ object MainAnnotation:
79103
paramIsVarargs: Boolean,
80104
paramDocumentation: String,
81105
paramAnnotations: Seq[ParameterAnnotation],
82-
) {
106+
):
83107

84108
/** The name of the parameter */
85109
def name: String = paramName
@@ -100,27 +124,10 @@ object MainAnnotation:
100124
def annotations: Seq[ParameterAnnotation] = paramAnnotations
101125

102126
override def toString: String = s"$name: $typeName"
103-
}
104127

105-
/** A class representing a command to run */
106-
trait Command[Parser[_], Result]:
128+
end ParameterInfo
107129

108-
/** The getter for the `idx`th argument of type `T`
109-
*
110-
* @param idx The index of the argument
111-
* @param defaultArgument Optional lambda to instantiate the default argument
112-
*/
113-
def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using Parser[T]): () => T
114130

115-
/** The getter for a final varargs argument of type `T*` */
116-
def varargGetter[T](using Parser[T]): () => Seq[T]
117-
118-
/** Run `program` if all arguments are valid if all arguments are valid
119-
*
120-
* @param program A function containing the call to the main method and instantiation of its arguments
121-
*/
122-
def run(program: () => Result): Unit
123-
end Command
124131

125132
/** Marker trait for annotations that will be included in the ParameterInfo annotations. */
126133
trait ParameterAnnotation extends StaticAnnotation

tests/run/main-annotation-example.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ end Test
2121

2222
@experimental
2323
class myMain extends MainAnnotation:
24-
import MainAnnotation.{ ParameterInfo, Command }
24+
import MainAnnotation.{ Command, CommandInfo, ParameterInfo }
2525

2626
// Parser used to parse command line arguments
2727
type Parser[T] = util.CommandLineParser.FromString[T]
@@ -30,19 +30,19 @@ class myMain extends MainAnnotation:
3030
type Result = Int
3131

3232
/** A new command with arguments from `args` */
33-
def command(args: Array[String], commandName: String, documentation: String, parameterInfos: ParameterInfo*): Command[Parser, Result] =
33+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
3434
if args.contains("--help") then
35-
println(documentation)
35+
println(info.documentation)
3636
System.exit(0)
37-
assert(parameterInfos.forall(!_.hasDefault), "Default arguments are not supported")
37+
assert(info.parameters.forall(!_.hasDefault), "Default arguments are not supported")
3838
val (plainArgs, varargs) =
39-
if parameterInfos.last.isVarargs then
40-
val numPlainArgs = parameterInfos.length - 1
39+
if info.parameters.last.isVarargs then
40+
val numPlainArgs = info.parameters.length - 1
4141
assert(numPlainArgs <= args.length, "Not enough arguments")
4242
(args.take(numPlainArgs), args.drop(numPlainArgs))
4343
else
44-
assert(parameterInfos.length <= args.length, "Not enough arguments")
45-
assert(parameterInfos.length >= args.length, "Too many arguments")
44+
assert(info.parameters.length <= args.length, "Not enough arguments")
45+
assert(info.parameters.length >= args.length, "Too many arguments")
4646
(args, Array.empty[String])
4747
new MyCommand(plainArgs, varargs)
4848

tests/run/main-annotation-homemade-annot-1.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ class mainAwait(timeout: Int = 2) extends MainAnnotation:
3535
override type Result = Future[Any]
3636

3737
// This is a toy example, it only works with positional args
38-
override def command(args: Array[String], commandName: String, docComment: String, parameterInfos: ParameterInfo*) =
38+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
3939
new Command[Parser, Result]:
4040
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T =
4141
() => p.fromString(args(idx))
4242

4343
override def varargGetter[T](using p: Parser[T]): () => Seq[T] =
44-
() => for i <- ((parameterInfos.length-1) until args.length) yield p.fromString(args(i))
44+
() => for i <- ((info.parameters.length-1) until args.length) yield p.fromString(args(i))
4545

4646
override def run(f: () => Result): Unit = println(Await.result(f(), Duration(timeout, SECONDS)))
4747
end mainAwait

tests/run/main-annotation-homemade-annot-2.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ class myMain(runs: Int = 3)(after: String*) extends MainAnnotation:
3434
override type Parser[T] = util.CommandLineParser.FromString[T]
3535
override type Result = Any
3636

37-
override def command(args: Array[String], commandName: String, docComment: String, parameterInfos: ParameterInfo*) =
37+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
3838
new Command[Parser, Result]:
3939

4040
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T =
4141
() => p.fromString(args(idx))
4242

4343
override def varargGetter[T](using p: Parser[T]): () => Seq[T] =
44-
() => for i <- (parameterInfos.length until args.length) yield p.fromString(args(i))
44+
() => for i <- (info.parameters.length until args.length) yield p.fromString(args(i))
4545

4646
override def run(f: () => Result): Unit =
4747
for (_ <- 1 to runs)

tests/run/main-annotation-homemade-annot-3.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class mainNoArgs extends MainAnnotation:
1616
override type Parser[T] = util.CommandLineParser.FromString[T]
1717
override type Result = Any
1818

19-
override def command(args: Array[String], commandName: String, docComment: String, parameterInfos: ParameterInfo*) =
19+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
2020
new Command[Parser, Result]:
2121
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = ???
2222

tests/run/main-annotation-homemade-annot-4.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class mainManyArgs(i1: Int, s2: String, i3: Int) extends MainAnnotation:
1616
override type Parser[T] = util.CommandLineParser.FromString[T]
1717
override type Result = Any
1818

19-
override def command(args: Array[String], commandName: String, docComment: String, parameterInfos: ParameterInfo*) =
19+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
2020
new Command[Parser, Result]:
2121
override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: Parser[T]): () => T = ???
2222

tests/run/main-annotation-homemade-annot-5.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class mainManyArgs(o: Option[Int]) extends MainAnnotation:
1818
override type Parser[T] = util.CommandLineParser.FromString[T]
1919
override type Result = Any
2020

21-
override def command(args: Array[String], commandName: String, docComment: String, parameterInfos: ParameterInfo*) =
21+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
2222
new Command[Parser, Result]:
2323
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = ???
2424

tests/run/main-annotation-homemade-annot-6.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ class myMain extends MainAnnotation:
2323
override type Parser[T] = Make[T]
2424
override type Result = Any
2525

26-
override def command(args: Array[String], commandName: String, docs: String, parameterInfos: ParameterInfo*) =
26+
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
2727
def paramInfoString(paramInfo: ParameterInfo) =
2828
import paramInfo.*
2929
s" ParameterInfo(name=\"$name\", typeName=\"$typeName\", hasDefault=$hasDefault, isVarargs=$isVarargs, documentation=\"$documentation\", annotations=$annotations)"
3030
println(
3131
s"""command(
3232
| ${args.mkString("Array(", ", ", ")")},
33-
| $commandName,
34-
| "$docs",
35-
| ${parameterInfos.map(paramInfoString).mkString("Seq(\n", ",\n", "\n )*")}
33+
| ${info.name},
34+
| "${info.documentation}",
35+
| ${info.parameters.map(paramInfoString).mkString("Seq(\n", ",\n", "\n )*")}
3636
|)""".stripMargin)
3737
new Command[Parser, Result]:
3838
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T =

0 commit comments

Comments
 (0)