Skip to content

Commit 9bd6708

Browse files
committed
Add MAinAnnotation Parser and Result type parameters
And remove type members Parser and Result
1 parent 1b18a68 commit 9bd6708

10 files changed

+74
-101
lines changed

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

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ object foo {
3131
),
3232
args = args
3333
)
34-
val args0 = cmd.argGetter[Int](0, None) // using cmd.Parser[Int]
35-
val args1 = cmd.varargGetter[Int] // using cmd.Parser[Int]
34+
val args0 = cmd.argGetter[Int](0, None) // using a parser of Int
35+
val args1 = cmd.varargGetter[Int] // using a parser of Int
3636
cmd.run(() => sum(args0(), args1()*))
3737
}
3838
}
@@ -47,17 +47,15 @@ Finally, the `run` method is called to run the application. It receives a by-nam
4747
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.
4848

4949
```scala
50-
class myMain extends MainAnnotation:
51-
import MainAnnotation.{ ParameterInfo, Command }
52-
53-
// Parser used to parse command line arguments
54-
type Parser[T] = util.CommandLineParser.FromString[T]
50+
// Parser used to parse command line arguments
51+
import scala.util.CommandLineParser.FromString[T]
5552

56-
// Result type of the annotated method
57-
type Result = Int
53+
// Result type of the annotated method is Int
54+
class myMain extends MainAnnotation[FromString, Int]:
55+
import MainAnnotation.{ ParameterInfo, Command }
5856

5957
/** A new command with arguments from `args` */
60-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
58+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Int] =
6159
if args.contains("--help") then
6260
println(info.documentation)
6361
// TODO: Print documentation of the parameters
@@ -75,15 +73,15 @@ class myMain extends MainAnnotation:
7573
new MyCommand(plainArgs, varargs)
7674

7775
@experimental
78-
class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[util.CommandLineParser.FromString, Int]:
76+
class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[FromString, Int]:
7977

80-
def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: Parser[T]): () => T =
78+
def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: FromString[T]): () => T =
8179
() => parser.fromString(plainArgs(idx))
8280

83-
def varargGetter[T](using parser: Parser[T]): () => Seq[T] =
81+
def varargGetter[T](using parser: FromString[T]): () => Seq[T] =
8482
() => varargs.map(arg => parser.fromString(arg))
8583

86-
def run(program: () => Result): Unit =
84+
def run(program: () => Int): Unit =
8785
println("executing program")
8886
val result = program()
8987
println("result: " + result)

library/src/scala/annotation/MainAnnotation.scala

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,14 @@ package scala.annotation
4040
* }
4141
* }
4242
* ```
43+
*
44+
* @param Parser The class used for argument string parsing and arguments into a `T`
45+
* @param Result The required result type of the main method.
46+
* If this type is Any or Unit, any type will be accepted.
4347
*/
4448
@experimental
45-
trait MainAnnotation extends StaticAnnotation:
46-
import MainAnnotation.*
47-
48-
/** The class used for argument string parsing and arguments into a `T` */
49-
type Parser[T]
50-
51-
/** The required result type of the main method.
52-
*
53-
* If this type is Any or Unit, any type will be accepted.
54-
*/
55-
type Result
49+
trait MainAnnotation[Parser[_], Result] extends StaticAnnotation:
50+
import MainAnnotation.{Command, CommandInfo}
5651

5752
/** A new command with arguments from `args`
5853
*

tests/run/main-annotation-example.scala

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import scala.annotation.*
22
import collection.mutable
3+
import scala.util.CommandLineParser.FromString
34

45
/** Sum all the numbers
56
*
@@ -20,17 +21,11 @@ object Test:
2021
end Test
2122

2223
@experimental
23-
class myMain extends MainAnnotation:
24+
class myMain extends MainAnnotation[FromString, Int]:
2425
import MainAnnotation.{ Command, CommandInfo, ParameterInfo }
2526

26-
// Parser used to parse command line arguments
27-
type Parser[T] = util.CommandLineParser.FromString[T]
28-
29-
// Result type of the annotated method
30-
type Result = Int
31-
3227
/** A new command with arguments from `args` */
33-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
28+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Int] =
3429
if args.contains("--help") then
3530
println(info.documentation)
3631
System.exit(0)
@@ -47,15 +42,15 @@ class myMain extends MainAnnotation:
4742
new MyCommand(plainArgs, varargs)
4843

4944
@experimental
50-
class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[util.CommandLineParser.FromString, Int]:
45+
class MyCommand(plainArgs: Seq[String], varargs: Seq[String]) extends Command[FromString, Int]:
5146

52-
def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: Parser[T]): () => T =
47+
def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using parser: FromString[T]): () => T =
5348
() => parser.fromString(plainArgs(idx))
5449

55-
def varargGetter[T](using parser: Parser[T]): () => Seq[T] =
50+
def varargGetter[T](using parser: FromString[T]): () => Seq[T] =
5651
() => varargs.map(arg => parser.fromString(arg))
5752

58-
def run(program: () => Result): Unit =
53+
def run(program: () => Int): Unit =
5954
println("executing program")
6055
val result = program()
6156
println("result: " + result)

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import scala.annotation.*
33
import scala.collection.mutable
44
import ExecutionContext.Implicits.global
55
import duration._
6+
import util.CommandLineParser.FromString
67

78
@mainAwait def get(wait: Int): Future[Int] = Future{
89
Thread.sleep(1000 * wait)
@@ -28,20 +29,17 @@ object Test:
2829
end Test
2930

3031
@experimental
31-
class mainAwait(timeout: Int = 2) extends MainAnnotation:
32+
class mainAwait(timeout: Int = 2) extends MainAnnotation[FromString, Future[Any]]:
3233
import MainAnnotation.*
3334

34-
override type Parser[T] = util.CommandLineParser.FromString[T]
35-
override type Result = Future[Any]
36-
3735
// This is a toy example, it only works with positional args
38-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
39-
new Command[Parser, Result]:
40-
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T =
36+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Future[Any]] =
37+
new Command[FromString, Future[Any]]:
38+
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T =
4139
() => p.fromString(args(idx))
4240

43-
override def varargGetter[T](using p: Parser[T]): () => Seq[T] =
41+
override def varargGetter[T](using p: FromString[T]): () => Seq[T] =
4442
() => for i <- ((info.parameters.length-1) until args.length) yield p.fromString(args(i))
4543

46-
override def run(f: () => Result): Unit = println(Await.result(f(), Duration(timeout, SECONDS)))
44+
override def run(f: () => Future[Any]): Unit = println(Await.result(f(), Duration(timeout, SECONDS)))
4745
end mainAwait

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import scala.collection.mutable
22
import scala.annotation.*
3+
import util.CommandLineParser.FromString
34

45
@myMain()("A")
56
def foo1(): Unit = println("I was run!")
@@ -28,22 +29,19 @@ end Test
2829

2930
// This is a toy example, it only works with positional args
3031
@experimental
31-
class myMain(runs: Int = 3)(after: String*) extends MainAnnotation:
32+
class myMain(runs: Int = 3)(after: String*) extends MainAnnotation[FromString, Any]:
3233
import MainAnnotation.*
3334

34-
override type Parser[T] = util.CommandLineParser.FromString[T]
35-
override type Result = Any
35+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] =
36+
new Command[FromString, Any]:
3637

37-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
38-
new Command[Parser, Result]:
39-
40-
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T =
38+
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T =
4139
() => p.fromString(args(idx))
4240

43-
override def varargGetter[T](using p: Parser[T]): () => Seq[T] =
41+
override def varargGetter[T](using p: FromString[T]): () => Seq[T] =
4442
() => for i <- (info.parameters.length until args.length) yield p.fromString(args(i))
4543

46-
override def run(f: () => Result): Unit =
44+
override def run(f: () => Any): Unit =
4745
for (_ <- 1 to runs)
4846
f()
4947
if after.length > 0 then println(after.mkString(", "))
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.annotation.*
2+
import scala.util.CommandLineParser.FromString
23

34
@mainNoArgs def foo() = println("Hello world!")
45

@@ -10,17 +11,14 @@ object Test:
1011
end Test
1112

1213
@experimental
13-
class mainNoArgs extends MainAnnotation:
14+
class mainNoArgs extends MainAnnotation[FromString, Any]:
1415
import MainAnnotation.*
1516

16-
override type Parser[T] = util.CommandLineParser.FromString[T]
17-
override type Result = Any
17+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] =
18+
new Command[FromString, Any]:
19+
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T = ???
1820

19-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
20-
new Command[Parser, Result]:
21-
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = ???
21+
override def varargGetter[T](using p: FromString[T]): () => Seq[T] = ???
2222

23-
override def varargGetter[T](using p: Parser[T]): () => Seq[T] = ???
24-
25-
override def run(program: () => Result): Unit = program()
23+
override def run(program: () => Any): Unit = program()
2624
end command
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.annotation.*
2+
import scala.util.CommandLineParser.FromString
23

34
@mainManyArgs(1, "B", 3) def foo() = println("Hello world!")
45

@@ -10,17 +11,14 @@ object Test:
1011
end Test
1112

1213
@experimental
13-
class mainManyArgs(i1: Int, s2: String, i3: Int) extends MainAnnotation:
14+
class mainManyArgs(i1: Int, s2: String, i3: Int) extends MainAnnotation[FromString, Any]:
1415
import MainAnnotation.*
1516

16-
override type Parser[T] = util.CommandLineParser.FromString[T]
17-
override type Result = Any
17+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] =
18+
new Command[FromString, Any]:
19+
override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: FromString[T]): () => T = ???
1820

19-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
20-
new Command[Parser, Result]:
21-
override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: Parser[T]): () => T = ???
21+
override def varargGetter[T](using p: FromString[T]): () => Seq[T] = ???
2222

23-
override def varargGetter[T](using p: Parser[T]): () => Seq[T] = ???
24-
25-
override def run(program: () => Result): Unit = program()
23+
override def run(program: () => Any): Unit = program()
2624
end command
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.annotation.*
2+
import scala.util.CommandLineParser.FromString
23

34
@mainManyArgs(Some(1)) def foo() = println("Hello world!")
45
@mainManyArgs(None) def bar() = println("Hello world!")
@@ -12,17 +13,14 @@ object Test:
1213
end Test
1314

1415
@experimental
15-
class mainManyArgs(o: Option[Int]) extends MainAnnotation:
16+
class mainManyArgs(o: Option[Int]) extends MainAnnotation[FromString, Any]:
1617
import MainAnnotation.*
1718

18-
override type Parser[T] = util.CommandLineParser.FromString[T]
19-
override type Result = Any
19+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] =
20+
new Command[FromString, Any]:
21+
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: FromString[T]): () => T = ???
2022

21-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
22-
new Command[Parser, Result]:
23-
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T = ???
23+
override def varargGetter[T](using p: FromString[T]): () => Seq[T] = ???
2424

25-
override def varargGetter[T](using p: Parser[T]): () => Seq[T] = ???
26-
27-
override def run(program: () => Result): Unit = program()
25+
override def run(program: () => Any): Unit = program()
2826
end command

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@ object Test:
1717
end Test
1818

1919
@experimental
20-
class myMain extends MainAnnotation:
20+
class myMain extends MainAnnotation[Make, Any]:
2121
import MainAnnotation.*
2222

23-
override type Parser[T] = Make[T]
24-
override type Result = Any
25-
26-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
23+
def command(info: CommandInfo, args: Array[String]): Command[Make, Any] =
2724
def paramInfoString(paramInfo: ParameterInfo) =
2825
import paramInfo.*
2926
s" ParameterInfo(name=\"$name\", typeName=\"$typeName\", hasDefault=$hasDefault, isVarargs=$isVarargs, documentation=\"$documentation\", annotations=$annotations)"
@@ -34,16 +31,16 @@ class myMain extends MainAnnotation:
3431
| "${info.documentation}",
3532
| ${info.parameters.map(paramInfoString).mkString("Seq(\n", ",\n", "\n )*")}
3633
|)""".stripMargin)
37-
new Command[Parser, Result]:
38-
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Parser[T]): () => T =
34+
new Command[Make, Any]:
35+
override def argGetter[T](idx: Int, defaultArgument: Option[() => T])(using p: Make[T]): () => T =
3936
println(s"argGetter($idx, ${defaultArgument.map(_())})")
4037
() => p.make
4138

42-
override def varargGetter[T](using p: Parser[T]): () => Seq[T] =
39+
override def varargGetter[T](using p: Make[T]): () => Seq[T] =
4340
println("varargGetter()")
4441
() => Seq(p.make, p.make)
4542

46-
override def run(f: () => Result): Unit =
43+
override def run(f: () => Any): Unit =
4744
println("run()")
4845
f()
4946
println()

tests/run/main-annotation-newMain.scala

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import scala.annotation.*
22
import collection.mutable
3+
import scala.util.CommandLineParser.FromString
34

45
@newMain def happyBirthday(age: Int, name: String, others: String*) =
56
val suffix =
@@ -29,15 +30,12 @@ end Test
2930

3031

3132
@experimental
32-
final class newMain extends MainAnnotation:
33+
final class newMain extends MainAnnotation[FromString, Any]:
3334
import newMain._
3435
import MainAnnotation._
3536

36-
override type Parser[T] = util.CommandLineParser.FromString[T]
37-
override type Result = Any
38-
39-
def command(info: CommandInfo, args: Array[String]): Command[Parser, Result] =
40-
new Command[Parser, Result]:
37+
def command(info: CommandInfo, args: Array[String]): Command[FromString, Any] =
38+
new Command[FromString, Any]:
4139

4240
private inline val argMarker = "--"
4341
private inline val shortArgMarker = "-"
@@ -133,7 +131,7 @@ final class newMain extends MainAnnotation:
133131
case s => argMarker + s
134132
}
135133

136-
private def convert[T](argName: String, arg: String)(using p: Parser[T]): () => T =
134+
private def convert[T](argName: String, arg: String)(using p: FromString[T]): () => T =
137135
p.fromStringOption(arg) match
138136
case Some(t) => () => t
139137
case None => error(s"invalid argument for $argName: $arg")
@@ -233,7 +231,7 @@ final class newMain extends MainAnnotation:
233231
private def getInvalidNames(paramInfos: ParameterInfo): Seq[String | Char] =
234232
getAliases(paramInfos).filter(name => !nameIsValid(name) && !shortNameIsValid(name))
235233

236-
override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: Parser[T]): () => T =
234+
override def argGetter[T](idx: Int, optDefaultGetter: Option[() => T])(using p: FromString[T]): () => T =
237235
val name = info.parameters(idx).name
238236
val parameterInfo = nameToParameterInfo(name)
239237
// TODO: Decide which string is associated with this arg when constructing the command.
@@ -259,7 +257,7 @@ final class newMain extends MainAnnotation:
259257
}
260258
end argGetter
261259

262-
override def varargGetter[T](using p: Parser[T]): () => Seq[T] =
260+
override def varargGetter[T](using p: FromString[T]): () => Seq[T] =
263261
val name = info.parameters.last.name
264262
// TODO: Decide which strings are associated with the varargs when constructing the command.
265263
// 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:
269267
// First take arguments passed by name, then those passed by position
270268
() => (byNameGetters ++ positionalGetters).map(_())
271269

272-
override def run(f: () => Result): Unit =
270+
override def run(f: () => Any): Unit =
273271
// Check aliases unicity
274272
val nameAndCanonicalName = nameToParameterInfo.toList.flatMap {
275273
case (canonicalName, infos) => (canonicalName +: getAlternativeNames(infos) ++: getShortNames(infos)).map(_ -> canonicalName)

0 commit comments

Comments
 (0)