@@ -10,43 +10,25 @@ import Comments.Comment
10
10
import NameKinds .DefaultGetterName
11
11
import Annotations .Annotation
12
12
13
- /** Generate proxy classes for main functions.
14
- * A function like
15
- *
16
- * /* *
17
- * * Lorem ipsum dolor sit amet
18
- * * consectetur adipiscing elit.
19
- * *
20
- * * @param x my param x
21
- * * @param ys all my params y
22
- * */
23
- * @main(80) def f(
24
- * @main.ShortName('x') @main.Name("myX") x: S,
25
- * ys: T*
26
- * ) = ...
27
- *
28
- * would be translated to something like
29
- *
30
- * final class f {
31
- * static def main(args: Array[String]): Unit = {
32
- * val cmd = new main(80).command(args, "f", "Lorem ipsum dolor sit amet consectetur adipiscing elit.")
33
- *
34
- * val args0: () => S = cmd.argGetter[S](
35
- * new scala.annotation.MainAnnotation.ParameterInfos[S]("x", "S")
36
- * .withDocumentation("my param x")
37
- * .withAnnotations(new scala.main.ShortName('x'), new scala.main.Name("myX"))
38
- * )
39
- *
40
- * val args1: () => Seq[T] = cmd.varargGetter[T](
41
- * new scala.annotation.MainAnnotation.ParameterInfos[T]("ys", "T")
42
- * .withDocumentation("all my params y")
43
- * )
44
- *
45
- * cmd.run(f(args0.apply(), args1.apply()*))
46
- * }
47
- * }
48
- */
49
13
object MainProxies {
14
+ /** Generate proxy classes for @main functions.
15
+ * A function like
16
+ *
17
+ * @main def f(x: S, ys: T*) = ...
18
+ *
19
+ * would be translated to something like
20
+ *
21
+ * import CommandLineParser._
22
+ * class f {
23
+ * @static def main(args: Array[String]): Unit =
24
+ * try
25
+ * f(
26
+ * parseArgument[S](args, 0),
27
+ * parseRemainingArguments[T](args, 1): _*
28
+ * )
29
+ * catch case err: ParseError => showError(err)
30
+ * }
31
+ */
50
32
def mainProxiesOld (stats : List [tpd.Tree ])(using Context ): List [untpd.Tree ] = {
51
33
import tpd ._
52
34
def mainMethods (stats : List [Tree ]): List [Symbol ] = stats.flatMap {
@@ -140,6 +122,44 @@ object MainProxies {
140
122
private type DefaultValueSymbols = Map [Int , Symbol ]
141
123
private type ParameterAnnotationss = Seq [Seq [Annotation ]]
142
124
125
+ /**
126
+ * Generate proxy classes for main functions.
127
+ * A function like
128
+ *
129
+ * /* *
130
+ * * Lorem ipsum dolor sit amet
131
+ * * consectetur adipiscing elit.
132
+ * *
133
+ * * @param x my param x
134
+ * * @param ys all my params y
135
+ * */
136
+ * @main(80) def f(
137
+ * @main.ShortName('x') @main.Name("myX") x: S,
138
+ * ys: T*
139
+ * ) = ...
140
+ *
141
+ * would be translated to something like
142
+ *
143
+ * final class f {
144
+ * static def main(args: Array[String]): Unit = {
145
+ * val cmd = new main(80).command(
146
+ * args,
147
+ * "f",
148
+ * "Lorem ipsum dolor sit amet consectetur adipiscing elit.",
149
+ * new scala.annotation.MainAnnotation.ParameterInfos("x", "S")
150
+ * .withDocumentation("my param x")
151
+ * .withAnnotations(new scala.main.ShortName('x'), new scala.main.Name("myX")),
152
+ * new scala.annotation.MainAnnotation.ParameterInfos("ys", "T")
153
+ * .withDocumentation("all my params y")
154
+ * )
155
+ *
156
+ * val args0: () => S = cmd.argGetter[S]("x", None)
157
+ * val args1: () => Seq[T] = cmd.varargGetter[T]("ys")
158
+ *
159
+ * cmd.run(f(args0(), args1()*))
160
+ * }
161
+ * }
162
+ */
143
163
def mainProxies (stats : List [tpd.Tree ])(using Context ): List [untpd.Tree ] = {
144
164
import tpd ._
145
165
@@ -195,6 +215,12 @@ object MainProxies {
195
215
/** A literal value (Boolean, Int, String, etc.) */
196
216
inline def lit (any : Any ): Literal = Literal (Constant (any))
197
217
218
+ /** None */
219
+ inline def none : Tree = ref(defn.NoneModule .termRef)
220
+
221
+ /** Some(value) */
222
+ inline def some (value : Tree ): Tree = Apply (ref(defn.SomeClass .companionModule.termRef), value)
223
+
198
224
/** () => value */
199
225
def unitToValue (value : Tree ): Tree =
200
226
val anonName = nme.ANON_FUN
@@ -204,10 +230,12 @@ object MainProxies {
204
230
/**
205
231
* Creates a list of references and definitions of arguments, the first referencing the second.
206
232
* The goal is to create the
207
- * `val arg0: () => S = ...`
208
- * part of the code. The first element of a tuple is a ref to `arg0`, the second is the whole definition.
233
+ * `val args0: () => S = cmd.argGetter[S]("x", None)`
234
+ * part of the code.
235
+ * For each tuple, the first element is a ref to `args0`, the second is the whole definition, the third
236
+ * is the ParameterInfos definition associated to this argument.
209
237
*/
210
- def createArgs (mt : MethodType , cmdName : TermName ): List [(Tree , ValDef )] =
238
+ def createArgs (mt : MethodType , cmdName : TermName ): List [(Tree , ValDef , Tree )] =
211
239
mt.paramInfos.zip(mt.paramNames).zipWithIndex.map {
212
240
case ((formal, paramName), n) =>
213
241
val argName = nme.args ++ n.toString
@@ -220,11 +248,11 @@ object MainProxies {
220
248
else (argRef0, formal, defn.MainAnnotCommand_argGetter )
221
249
}
222
250
223
- // The ParameterInfos to be passed to the arg getter
251
+ // The ParameterInfos
224
252
val parameterInfos = {
225
253
val param = paramName.toString
226
254
val paramInfosTree = New (
227
- AppliedTypeTree ( TypeTree (defn.MainAnnotParameterInfos .typeRef), List ( TypeTree (formalType)) ),
255
+ TypeTree (defn.MainAnnotParameterInfos .typeRef),
228
256
// Arguments to be passed to ParameterInfos' constructor
229
257
List (List (lit(param), lit(formalType.show)))
230
258
)
@@ -234,31 +262,38 @@ object MainProxies {
234
262
* For example:
235
263
* args0paramInfos.withDocumentation("my param x")
236
264
* is represented by the pair
237
- * ( defn.MainAnnotationParameterInfos_withDocumentation, List(lit("my param x") ))
265
+ * defn.MainAnnotationParameterInfos_withDocumentation -> List(lit("my param x"))
238
266
*/
239
267
var assignations : List [(Symbol , List [Tree ])] = Nil
240
- for (dvSym <- defaultValueSymbols.get(n))
241
- assignations = (defn.MainAnnotationParameterInfos_withDefaultValue -> List (unitToValue(ref(dvSym.termRef)))) :: assignations
242
268
for (doc <- documentation.argDocs.get(param))
243
269
assignations = (defn.MainAnnotationParameterInfos_withDocumentation -> List (lit(doc))) :: assignations
244
270
245
271
val instanciatedAnnots = paramAnnotations(n).map(instanciateAnnotation).toList
246
272
if instanciatedAnnots.nonEmpty then
247
273
assignations = (defn.MainAnnotationParameterInfos_withAnnotations -> instanciatedAnnots) :: assignations
248
274
249
- if assignations.isEmpty then
250
- paramInfosTree
251
- else
252
- assignations.foldLeft[Tree ](paramInfosTree){ case (tree, (setterSym, values)) => Apply (Select (tree, setterSym.name), values) }
275
+ assignations.foldLeft[Tree ](paramInfosTree){ case (tree, (setterSym, values)) => Apply (Select (tree, setterSym.name), values) }
253
276
}
254
277
278
+ val argParams =
279
+ if formal.isRepeatedParam then
280
+ List (lit(paramName.toString))
281
+ else
282
+ val defaultValueGetterOpt = defaultValueSymbols.get(n) match {
283
+ case None =>
284
+ none
285
+ case Some (dvSym) =>
286
+ some(unitToValue(ref(dvSym.termRef)))
287
+ }
288
+ List (lit(paramName.toString), defaultValueGetterOpt)
289
+
255
290
val argDef = ValDef (
256
291
argName,
257
292
TypeTree (),
258
- Apply (TypeApply (Select (Ident (cmdName), getterSym.name), TypeTree (formalType) :: Nil ), parameterInfos ),
293
+ Apply (TypeApply (Select (Ident (cmdName), getterSym.name), TypeTree (formalType) :: Nil ), argParams ),
259
294
)
260
295
261
- (argRef, argDef)
296
+ (argRef, argDef, parameterInfos )
262
297
}
263
298
end createArgs
264
299
@@ -287,16 +322,9 @@ object MainProxies {
287
322
if (! mainFun.owner.isStaticOwner)
288
323
report.error(s " main method is not statically accessible " , pos)
289
324
else {
290
- val cmd = ValDef (
291
- cmdName,
292
- TypeTree (),
293
- Apply (
294
- Select (instanciateAnnotation(mainAnnot), defn.MainAnnot_command .name),
295
- Ident (nme.args) :: lit(mainFun.showName) :: lit(documentation.mainDoc) :: Nil
296
- )
297
- )
298
325
var args : List [ValDef ] = Nil
299
326
var mainCall : Tree = ref(mainFun.termRef)
327
+ var parameterInfoss : List [Tree ] = Nil
300
328
301
329
mainFun.info match {
302
330
case _ : ExprType =>
@@ -309,16 +337,25 @@ object MainProxies {
309
337
report.error(s " main method cannot be curried " , pos)
310
338
Nil
311
339
case _ =>
312
- val (argRefs, argVals) = createArgs(mt, cmdName).unzip
340
+ val (argRefs, argVals, paramInfoss ) = createArgs(mt, cmdName).unzip3
313
341
args = argVals
314
342
mainCall = Apply (mainCall, argRefs)
343
+ parameterInfoss = paramInfoss
315
344
}
316
345
case _ : PolyType =>
317
346
report.error(s " main method cannot have type parameters " , pos)
318
347
case _ =>
319
348
report.error(s " main can only annotate a method " , pos)
320
349
}
321
350
351
+ val cmd = ValDef (
352
+ cmdName,
353
+ TypeTree (),
354
+ Apply (
355
+ Select (instanciateAnnotation(mainAnnot), defn.MainAnnot_command .name),
356
+ Ident (nme.args) :: lit(mainFun.showName) :: lit(documentation.mainDoc) :: parameterInfoss
357
+ )
358
+ )
322
359
val run = Apply (Select (Ident (cmdName), defn.MainAnnotCommand_run .name), mainCall)
323
360
val body = Block (cmd :: args, run)
324
361
val mainArg = ValDef (nme.args, TypeTree (defn.ArrayType .appliedTo(defn.StringType )), EmptyTree )
0 commit comments