1
1
my module sprintf {
2
2
my @ handlers ;
3
+ my $ assert_used_args ;
3
4
4
5
grammar Syntax {
5
6
token TOP {
@@ -18,22 +19,26 @@ my module sprintf {
18
19
}
19
20
20
21
proto token directive { <...> }
21
- token directive :sym <b > { '%' <flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ bB ] > }
22
- token directive :sym <c > { '%' <flags >* <size >? <sym > }
23
- token directive :sym <d > { '%' <flags >* <size >? $ < sym > =<[ di ] > }
24
- token directive :sym <e > { '%' <flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ eE ] > }
25
- token directive :sym <f > { '%' <flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ fF ] > }
26
- token directive :sym <g > { '%' <flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ gG ] > }
27
- token directive :sym <o > { '%' <flags >* <size >? [ '.' <precision = .size > ]? <sym > }
28
- token directive :sym <s > { '%' <flags >* <size >? <sym > }
29
- token directive :sym <u > { '%' <flags >* <size >? <sym > }
30
- token directive :sym <x > { '%' <flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ xX ] > }
22
+ token directive :sym <b > { '%' <idx > ? < flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ bB ] > }
23
+ token directive :sym <c > { '%' <idx > ? < flags >* <size >? <sym > }
24
+ token directive :sym <d > { '%' <idx > ? < flags >* <size >? [ '.' < precision = .size > ] ? $ < sym > =<[ di ] > }
25
+ token directive :sym <e > { '%' <idx > ? < flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ eE ] > }
26
+ token directive :sym <f > { '%' <idx > ? < flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ fF ] > }
27
+ token directive :sym <g > { '%' <idx > ? < flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ gG ] > }
28
+ token directive :sym <o > { '%' <idx > ? < flags >* <size >? [ '.' <precision = .size > ]? <sym > }
29
+ token directive :sym <s > { '%' <idx > ? < flags >* <size >? [ '.' < precision = .size > ] ? <sym > }
30
+ token directive :sym <u > { '%' <idx > ? < flags >* <size >? <sym > }
31
+ token directive :sym <x > { '%' <idx > ? < flags >* <size >? [ '.' <precision = .size > ]? $ < sym > =<[ xX ] > }
31
32
32
33
proto token escape { <...> }
33
34
token escape :sym <% > { '%' <flags >* <size >? <sym > }
34
35
35
36
token literal { <- [ % ] >+ }
36
37
38
+ token idx {
39
+ $ < param_index > =[\d + ] '$'
40
+ }
41
+
37
42
token flags {
38
43
| $ < space > = ' '
39
44
| $ < plus > = '+'
@@ -52,7 +57,7 @@ my module sprintf {
52
57
my @ statements ;
53
58
@ statements . push ( $ _ . ast ) for $ < statement > ;
54
59
55
- if $ * ARGS_USED < + @ * ARGS_HAVE {
60
+ if $ assert_used_args && $ * ARGS_USED < + @ * ARGS_HAVE {
56
61
nqp ::die(" Too few directives: found $ * ARGS_USED ,"
57
62
~ " fewer than the " ~ + @ * ARGS_HAVE ~ " arguments after the format string" )
58
63
}
@@ -71,8 +76,14 @@ my module sprintf {
71
76
nqp :: join (' ' , @ strings );
72
77
}
73
78
74
- sub next_argument () {
75
- @ * ARGS_HAVE [$ * ARGS_USED ++ ]
79
+ sub next_argument ($/ ) {
80
+ if $ < idx > {
81
+ $ assert_used_args := 0 ;
82
+ @ * ARGS_HAVE [$ < idx > . ast]
83
+ }
84
+ else {
85
+ @ * ARGS_HAVE [$ * ARGS_USED ++ ]
86
+ }
76
87
}
77
88
78
89
sub intify ($ number_representation ) {
@@ -132,7 +143,7 @@ my module sprintf {
132
143
}
133
144
134
145
method directive :sym <b >($/ ) {
135
- my $ int := intify(next_argument());
146
+ my $ int := intify(next_argument($/ ));
136
147
$ int := nqp ::base_I($ int , 2 );
137
148
my $ pre := ($ < sym > eq ' b' ?? ' 0b' !! ' 0B' ) if $ int && has_flag($/ , ' hash' );
138
149
if nqp :: chars ($ < precision > ) {
@@ -145,17 +156,18 @@ my module sprintf {
145
156
make $ int ;
146
157
}
147
158
method directive :sym <c >($/ ) {
148
- make nqp :: chr (next_argument())
159
+ make nqp :: chr (next_argument($/ ))
149
160
}
150
161
151
162
method directive :sym <d >($/ ) {
152
- my $ int := intify(next_argument());
163
+ my $ int := intify(next_argument($/ ));
153
164
my $ knowhow := nqp ::knowhow(). new_type(: repr(" P6bigint" ));
154
165
my $ pad := padding_char($/ );
155
166
my $ sign := nqp ::islt_I($ int , nqp ::box_i(0 , $ knowhow )) ?? ' -'
156
167
!! has_flag($/ , ' plus' )
157
168
?? ' +' !! ' ' ;
158
169
$ int := nqp ::tostr_I(nqp ::abs_I($ int , $ knowhow ));
170
+ $ int := nqp :: substr ($ int , 0 , $ < precision > . ast) if nqp :: chars ($ < precision > );
159
171
if $ pad ne ' ' && $ < size > {
160
172
$ int := $ sign ~ infix_x($ pad , $ < size > . ast - nqp :: chars ($ int ) - 1 ) ~ $ int ;
161
173
}
@@ -243,28 +255,28 @@ my module sprintf {
243
255
}
244
256
245
257
method directive :sym <e >($/ ) {
246
- my $ float := next_argument();
258
+ my $ float := next_argument($/ );
247
259
my $ precision := $ < precision > ?? $ < precision > . ast !! 6 ;
248
260
my $ pad := padding_char($/ );
249
261
my $ size := $ < size > ?? $ < size > . ast !! 0 ;
250
262
make scientific($ float , $ < sym > , $ precision , $ size , $ pad );
251
263
}
252
264
method directive :sym <f >($/ ) {
253
- my $ int := next_argument();
265
+ my $ int := next_argument($/ );
254
266
my $ precision := $ < precision > ?? $ < precision > . ast !! 6 ;
255
267
my $ pad := padding_char($/ );
256
268
my $ size := $ < size > ?? $ < size > . ast !! 0 ;
257
269
make fixed-point($ int , $ precision , $ size , $ pad );
258
270
}
259
271
method directive :sym <g >($/ ) {
260
- my $ float := next_argument();
272
+ my $ float := next_argument($/ );
261
273
my $ precision := $ < precision > ?? $ < precision > . ast !! 6 ;
262
274
my $ pad := padding_char($/ );
263
275
my $ size := $ < size > ?? $ < size > . ast !! 0 ;
264
276
make shortest($ float , $ < sym > eq ' G' ?? ' E' !! ' e' , $ precision , $ size , $ pad );
265
277
}
266
278
method directive :sym <o >($/ ) {
267
- my $ int := intify(next_argument());
279
+ my $ int := intify(next_argument($/ ));
268
280
$ int := nqp ::base_I($ int , 8 );
269
281
my $ pre := ' 0' if $ int && has_flag($/ , ' hash' );
270
282
if nqp :: chars ($ < precision > ) {
@@ -278,12 +290,14 @@ my module sprintf {
278
290
}
279
291
280
292
method directive :sym <s >($/ ) {
281
- make next_argument()
293
+ my $ string := next_argument($/ );
294
+ $ string := nqp :: substr ($ string , 0 , $ < precision > . ast) if nqp :: chars ($ < precision > );
295
+ make $ string
282
296
}
283
297
# XXX: Should we emulate an upper limit, like 2**64?
284
298
# XXX: Should we emulate p5 behaviour for negative values passed to %u ?
285
299
method directive :sym <u >($/ ) {
286
- my $ int := intify(next_argument());
300
+ my $ int := intify(next_argument($/ ));
287
301
if $ int < 0 {
288
302
my $ err := nqp ::getstderr();
289
303
nqp ::printfh($ err , " negative value '"
@@ -298,7 +312,7 @@ my module sprintf {
298
312
make nqp ::tostr_I($ int )
299
313
}
300
314
method directive :sym <x >($/ ) {
301
- my $ int := intify(next_argument());
315
+ my $ int := intify(next_argument($/ ));
302
316
$ int := nqp ::base_I($ int , 16 );
303
317
my $ pre := ' 0X' if $ int && has_flag($/ , ' hash' );
304
318
if nqp :: chars ($ < precision > ) {
@@ -319,15 +333,22 @@ my module sprintf {
319
333
make ~ $/
320
334
}
321
335
336
+ method idx ($/ ) {
337
+ my $ index := $ < param_index > - 1 ;
338
+ nqp ::die(" Parameter index starts to count at 1 but 0 was passed" ) if $ index < 0 ;
339
+ make $ index
340
+ }
341
+
322
342
method size ($/ ) {
323
- make $ < star > ?? next_argument() !! ~ $/
343
+ make $ < star > ?? next_argument({} ) !! ~ $/
324
344
}
325
345
}
326
346
327
347
my $ actions := Actions. new ();
328
348
329
349
sub sprintf ($ format , @ arguments ) {
330
350
my @ * ARGS_HAVE := @ arguments ;
351
+ $ assert_used_args := 1 ;
331
352
return Syntax. parse( $ format , : actions($ actions ) ). ast;
332
353
}
333
354
0 commit comments