1
1
import annotation .{Annotation , StaticAnnotation }
2
2
import collection .mutable
3
3
4
- /** MainAnnotation provides the functionality for a compiler-generated wrapper class.
5
- * It links a compiler-generated main method (call it compiler-main) to a user
6
- * written main method (user-main).
7
- * The protocol of calls from compiler-main is as follows:
4
+ /** This class provides a framework for compiler-generated wrappers
5
+ * of "entry-point" methods. It routes and transforms parameters and results
6
+ * between a compiler-generated wrapper method that has calling conventions
7
+ * fixed by a framework and a user-written entry-point method that can have
8
+ * flexible argument lists. It allows the wrapper to provide help and usage
9
+ * information as well as customised error messages if the actual wrapper arguments
10
+ * do not match the expected entry-point parameters.
8
11
*
9
- * - create a `command` with the command line arguments,
10
- * - for each parameter of user-main, a call to `command.nextArgGetter`,
11
- * or `command.finalArgsGetter` if is a final varargs parameter,
12
- * - a call to `command.run` with the closure of user-main applied to all arguments.
12
+ * The protocol of calls from the wrapper method is as follows:
13
+ *
14
+ * 1. Create a `call` instance with the wrapper argument.
15
+ * 2. For each parameter of the entry-point, invoke `call.nextArgGetter`,
16
+ * or `call.finalArgsGetter` if is a final varargs parameter.
17
+ * 3. Invoke `call.run` with the closure of entry-point applied to all arguments.
13
18
*
14
19
* The wrapper class has this outline:
15
20
*
16
21
* object <wrapperClass>:
17
- * def <wrapperMethod>(args: <CommandLineArgs >) =
22
+ * @WrapperAnnotation def <wrapperMethod>(args: <WrapperArgs >) =
18
23
* ...
19
24
*
20
- * Here the `<wrapperClass>` name is the result of an inline call to `wrapperClassName`
21
- * and `<wrapperMethod>` is the result of an inline call to `wrapperMethodName`
22
- *
23
- * The result type of `<main>` is the same as the result type of `run`
24
- * in the concrete implementation of `MainAnnotation`.
25
+ * Here `<wrapperClass>` and `<wrapperMethod>` are obtained from an
26
+ * inline call to the `wrapperName` method.
25
27
*/
26
- trait MainAnnotation extends StaticAnnotation :
28
+ trait EntryPointAnnotation extends StaticAnnotation :
27
29
28
- /** The class used for argument string parsing. E.g. `scala.util.FromString`,
29
- * but could be something else
30
+ /** The class used for argument parsing. E.g. `scala.util.FromString`, if
31
+ * arguments are strings, but it could be something else.
30
32
*/
31
33
type ArgumentParser [T ]
32
34
33
35
/** The required result type of the user-defined main function */
34
- type MainResultType
36
+ type EntryPointResult
37
+
38
+ /** The type of the wrapper argument. E.g., for Java main methods: `Array[String]` */
39
+ type WrapperArgs
35
40
36
- /** The type of the command line arguments . E.g., for Java main methods: `Array[String] ` */
37
- type CommandLineArgs
41
+ /** The return type of the generated wrapper . E.g., for Java main methods: `Unit ` */
42
+ type WrapperResult
38
43
39
- /** The return type of the generated command. E.g., for Java main methods: `Unit` */
40
- type CommandResult
44
+ /** An annotation type with which the wrapper method is decorated.
45
+ * No annotation is generated if the type is left abstract.
46
+ */
47
+ type WrapperAnnotation <: Annotation
48
+
49
+ /** The fully qualified name (relative to enclosing package) to
50
+ * use for the static wrapper method.
51
+ * @param mainName the fully qualified name of the user-defined main method
52
+ */
53
+ inline def wrapperName (mainName : String ): String
41
54
42
- /** A new command with arguments from `args` */
43
- def command (args : CommandLineArgs ): Command
55
+ /** A new wrapper call with arguments from `args` */
56
+ def call (args : WrapperArgs ): Call
44
57
45
- /** A class representing a command to run */
46
- abstract class Command :
58
+ /** A class representing a wrapper call */
59
+ abstract class Call :
47
60
48
61
/** The getter for the next argument of type `T` */
49
62
def nextArgGetter [T ](argName : String , fromString : ArgumentParser [T ], defaultValue : Option [T ] = None ): () => T
50
63
51
64
/** The getter for a final varargs argument of type `T*` */
52
65
def finalArgsGetter [T ](argName : String , fromString : ArgumentParser [T ]): () => Seq [T ]
53
66
54
- /** Run `program ` if all arguments are valid,
67
+ /** Run `entryPoint ` if all arguments are valid,
55
68
* or print usage information and/or error messages.
56
- * @param program the program to run
57
- * @param mainName the fully qualified name of the user-defined main method
58
- * @param docComment the doc comment of the user-defined main method
69
+ * @param entryPointApply the applied entry-point to run
70
+ * @param entryPointName the fully qualified name of the entry-point method
71
+ * @param docComment the doc comment of the entry-point method
59
72
*/
60
- def run (program : => MainResultType , mainName : String , docComment : String ): CommandResult
61
- end Command
62
-
63
- /** The fully qualified name to use for the static wrapper method
64
- * @param mainName the fully qualified name of the user-defined main method
65
- */
66
- inline def wrapperName (mainName : String ): String
67
-
68
- /** An annotation type with which the wrapper method is decorated.
69
- * No annotation is generated if the type is left abstract.
70
- */
71
- type WrapperAnnotation <: Annotation
72
-
73
- end MainAnnotation
73
+ def run (entryPointApply : => EntryPointResult , entryPointName : String , docComment : String ): WrapperResult
74
+ end Call
75
+ end EntryPointAnnotation
74
76
75
77
// Sample main class, can be freely implemented:
76
78
77
- class main extends MainAnnotation :
79
+ class main extends EntryPointAnnotation :
78
80
79
81
type ArgumentParser [T ] = util.FromString [T ]
80
- type MainResultType = Any
81
- type CommandLineArgs = Array [String ]
82
- type CommandResult = Unit
82
+ type EntryPointResult = Unit
83
+ type WrapperArgs = Array [String ]
84
+ type WrapperResult = Unit
83
85
84
- def command (args : Array [String ]) = new Command :
86
+ def call (args : Array [String ]) = new Call :
85
87
86
88
/** A buffer of demanded argument names, plus
87
89
* "?" if it has a default
@@ -133,9 +135,9 @@ class main extends MainAnnotation:
133
135
val getters = remainingArgGetters()
134
136
() => getters.map(_())
135
137
136
- def run (f : => MainResultType , mainName : String , docComment : String ): Unit =
138
+ def run (entryPointApply : => EntryPointResult , entryPointName : String , docComment : String ): Unit =
137
139
def usage (): Unit =
138
- val cmd = mainName .dropRight(" .main" .length)
140
+ val cmd = entryPointName .dropRight(" .main" .length)
139
141
val params = argInfos.map(_ + _).mkString(" " )
140
142
println(s " Usage: java $cmd $params" )
141
143
@@ -162,11 +164,9 @@ class main extends MainAnnotation:
162
164
if errors.nonEmpty then
163
165
for msg <- errors do println(s " Error: $msg" )
164
166
usage()
165
- else f match
166
- case n : Int if n < 0 => System .exit(- n)
167
- case _ =>
167
+ else f
168
168
end run
169
- end command
169
+ end call
170
170
171
171
inline def wrapperName (mainName : String ): String =
172
172
s " ${mainName.drop(mainName.lastIndexOf('.' ) + 1 )}.main "
@@ -175,8 +175,6 @@ class main extends MainAnnotation:
175
175
176
176
end main
177
177
178
- class EntryPoint extends Annotation
179
-
180
178
// Sample main method
181
179
182
180
object myProgram :
@@ -190,11 +188,11 @@ end myProgram
190
188
// Compiler generated code:
191
189
192
190
object add extends main :
193
- @ EntryPoint def main (args : Array [String ]) =
194
- val cmd = command (args)
195
- val arg1 = cmd .nextArgGetter[Int ](" num" , summon[ArgumentParser [Int ]])
196
- val arg2 = cmd .nextArgGetter[Int ](" inc" , summon[ArgumentParser [Int ]], Some (1 ))
197
- cmd .run(myProgram.add(arg1(), arg2()), " add" , " Adds two numbers" )
191
+ def main (args : Array [String ]) =
192
+ val cll = call (args)
193
+ val arg1 = cll .nextArgGetter[Int ](" num" , summon[ArgumentParser [Int ]])
194
+ val arg2 = cll .nextArgGetter[Int ](" inc" , summon[ArgumentParser [Int ]], Some (1 ))
195
+ cll .run(myProgram.add(arg1(), arg2()), " add" , " Adds two numbers" )
198
196
end add
199
197
200
198
/** --- Some scenarios ----------------------------------------
0 commit comments