11
11
//! Parameterized string expansion
12
12
13
13
use core:: prelude:: * ;
14
- use core:: { char, int, vec} ;
14
+ use core:: { char, vec, util} ;
15
+ use core:: num:: strconv:: { SignNone , SignNeg , SignAll , DigAll , to_str_bytes_common} ;
15
16
use core:: iterator:: IteratorUtil ;
16
17
17
18
#[ deriving( Eq ) ]
@@ -23,13 +24,21 @@ enum States {
23
24
PushParam ,
24
25
CharConstant ,
25
26
CharClose ,
26
- IntConstant ,
27
+ IntConstant ( int ) ,
28
+ FormatPattern ( Flags , FormatState ) ,
27
29
SeekIfElse ( int ) ,
28
30
SeekIfElsePercent ( int ) ,
29
31
SeekIfEnd ( int ) ,
30
32
SeekIfEndPercent ( int )
31
33
}
32
34
35
+ #[ deriving( Eq ) ]
36
+ enum FormatState {
37
+ FormatStateFlags ,
38
+ FormatStateWidth ,
39
+ FormatStatePrecision
40
+ }
41
+
33
42
/// Types of parameters a capability can use
34
43
pub enum Param {
35
44
String ( ~str ) ,
@@ -71,8 +80,6 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
71
80
72
81
let mut stack: ~[ Param ] = ~[ ] ;
73
82
74
- let mut intstate = ~[ ] ;
75
-
76
83
// Copy parameters into a local vector for mutability
77
84
let mut mparams = [ Number ( 0 ) , ..9 ] ;
78
85
for mparams. mut_iter( ) . zip( params. iter( ) ) . advance |( dst, & src) | {
@@ -100,26 +107,11 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
100
107
_ => return Err ( ~"a non-char was used with %c")
101
108
}
102
109
} else { return Err(~" stack is empty") } ,
103
- 's' => if stack. len( ) > 0 {
104
- match stack. pop( ) {
105
- String ( s) => output. push_all( s. as_bytes( ) ) ,
106
- _ => return Err ( ~"a non-str was used with %s")
107
- }
108
- } else { return Err ( ~"stack is empty") } ,
109
- 'd' => if stack. len( ) > 0 {
110
- match stack. pop( ) {
111
- Number ( x) => {
112
- let s = x. to_str( ) ;
113
- output. push_all( s. as_bytes( ) )
114
- }
115
- _ => return Err ( ~"a non-number was used with %d")
116
- }
117
- } else { return Err ( ~"stack is empty") } ,
118
110
'p' => state = PushParam ,
119
111
'P' => state = SetVar ,
120
112
'g' => state = GetVar ,
121
113
'\'' => state = CharConstant ,
122
- '{' => state = IntConstant ,
114
+ '{' => state = IntConstant ( 0 ) ,
123
115
'l' => if stack. len( ) > 0 {
124
116
match stack. pop( ) {
125
117
String ( s) => stack. push( Number ( s. len( ) as int) ) ,
@@ -231,6 +223,30 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
231
223
( _, _) => return Err ( ~"first two params not numbers with %i")
232
224
} ,
233
225
226
+ // printf-style support for %doxXs
227
+ 'd' |'o' |'x' |'X' |'s' => if stack. len ( ) > 0 {
228
+ let flags = Flags :: new ( ) ;
229
+ let res = format ( stack. pop ( ) , FormatOp :: from_char ( cur) , flags) ;
230
+ if res. is_err ( ) { return res }
231
+ output. push_all ( res. unwrap ( ) )
232
+ } else { return Err ( ~"stack is empty") } ,
233
+ ':' |'#' |' ' |'.' |'0' ..'9' => {
234
+ let mut flags = Flags :: new ( ) ;
235
+ let mut fstate = FormatStateFlags ;
236
+ match cur {
237
+ ':' => ( ) ,
238
+ '#' => flags. alternate = true ,
239
+ ' ' => flags. space = true ,
240
+ '.' => fstate = FormatStatePrecision ,
241
+ '0' ..'9' => {
242
+ flags. width = ( cur - '0' ) as uint ;
243
+ fstate = FormatStateWidth ;
244
+ }
245
+ _ => util:: unreachable ( )
246
+ }
247
+ state = FormatPattern ( flags, fstate) ;
248
+ }
249
+
234
250
// conditionals
235
251
'?' => ( ) ,
236
252
't' => if stack. len ( ) > 0 {
@@ -288,17 +304,61 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
288
304
return Err ( ~"malformed character constant") ;
289
305
}
290
306
} ,
291
- IntConstant => {
292
- if cur == '}' {
293
- stack. push ( match int:: parse_bytes ( intstate, 10 ) {
294
- Some ( n) => Number ( n) ,
295
- None => return Err ( ~"bad int constant")
296
- } ) ;
297
- intstate. clear ( ) ;
298
- state = Nothing ;
299
- } else {
300
- intstate. push ( cur as u8 ) ;
301
- old_state = Nothing ;
307
+ IntConstant ( i) => {
308
+ match cur {
309
+ '}' => {
310
+ stack. push ( Number ( i) ) ;
311
+ state = Nothing ;
312
+ }
313
+ '0' ..'9' => {
314
+ state = IntConstant ( i* 10 + ( ( cur - '0' ) as int ) ) ;
315
+ old_state = Nothing ;
316
+ }
317
+ _ => return Err ( ~"bad int constant")
318
+ }
319
+ }
320
+ FormatPattern ( ref mut flags, ref mut fstate) => {
321
+ old_state = Nothing ;
322
+ match ( * fstate, cur) {
323
+ ( _, 'd' ) |( _, 'o' ) |( _, 'x' ) |( _, 'X' ) |( _, 's' ) => if stack. len ( ) > 0 {
324
+ let res = format ( stack. pop ( ) , FormatOp :: from_char ( cur) , * flags) ;
325
+ if res. is_err ( ) { return res }
326
+ output. push_all ( res. unwrap ( ) ) ;
327
+ old_state = state; // will cause state to go to Nothing
328
+ } else { return Err ( ~"stack is empty") } ,
329
+ ( FormatStateFlags , '#' ) => {
330
+ flags. alternate = true ;
331
+ }
332
+ ( FormatStateFlags , '-' ) => {
333
+ flags. left = true ;
334
+ }
335
+ ( FormatStateFlags , '+' ) => {
336
+ flags. sign = true ;
337
+ }
338
+ ( FormatStateFlags , ' ' ) => {
339
+ flags. space = true ;
340
+ }
341
+ ( FormatStateFlags , '0' ..'9' ) => {
342
+ flags. width = ( cur - '0' ) as uint ;
343
+ * fstate = FormatStateWidth ;
344
+ }
345
+ ( FormatStateFlags , '.' ) => {
346
+ * fstate = FormatStatePrecision ;
347
+ }
348
+ ( FormatStateWidth , '0' ..'9' ) => {
349
+ let old = flags. width ;
350
+ flags. width = flags. width * 10 + ( ( cur - '0' ) as uint ) ;
351
+ if flags. width < old { return Err ( ~"format width overflow") }
352
+ }
353
+ ( FormatStateWidth , '.' ) => {
354
+ * fstate = FormatStatePrecision ;
355
+ }
356
+ ( FormatStatePrecision , '0' ..'9' ) => {
357
+ let old = flags. precision ;
358
+ flags. precision = flags. precision * 10 + ( ( cur - '0' ) as uint ) ;
359
+ if flags. precision < old { return Err ( ~"format precision overflow") }
360
+ }
361
+ _ => return Err ( ~"invalid format specifier")
302
362
}
303
363
}
304
364
SeekIfElse ( level) => {
@@ -349,6 +409,142 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
349
409
Ok ( output)
350
410
}
351
411
412
+ #[ deriving ( Eq ) ]
413
+ priv struct Flags {
414
+ width : uint ,
415
+ precision : uint ,
416
+ alternate : bool ,
417
+ left : bool ,
418
+ sign : bool ,
419
+ space : bool
420
+ }
421
+
422
+ impl Flags {
423
+ priv fn new ( ) -> Flags {
424
+ Flags { width : 0 , precision : 0 , alternate : false ,
425
+ left : false , sign : false , space : false }
426
+ }
427
+ }
428
+
429
+ priv enum FormatOp {
430
+ FormatDigit ,
431
+ FormatOctal ,
432
+ FormatHex ,
433
+ FormatHEX ,
434
+ FormatString
435
+ }
436
+
437
+ impl FormatOp {
438
+ priv fn from_char ( c : char ) -> FormatOp {
439
+ match c {
440
+ 'd' => FormatDigit ,
441
+ 'o' => FormatOctal ,
442
+ 'x' => FormatHex ,
443
+ 'X' => FormatHEX ,
444
+ 's' => FormatString ,
445
+ _ => fail ! ( "bad FormatOp char" )
446
+ }
447
+ }
448
+ priv fn to_char ( self ) -> char {
449
+ match self {
450
+ FormatDigit => 'd' ,
451
+ FormatOctal => 'o' ,
452
+ FormatHex => 'x' ,
453
+ FormatHEX => 'X' ,
454
+ FormatString => 's'
455
+ }
456
+ }
457
+ }
458
+
459
+ priv fn format ( val : Param , op : FormatOp , flags : Flags ) -> Result < ~[ u8 ] , ~str > {
460
+ let mut s = match val {
461
+ Number ( d) => {
462
+ match op {
463
+ FormatString => {
464
+ return Err ( ~"non-number on stack with %s")
465
+ }
466
+ _ => {
467
+ let radix = match op {
468
+ FormatDigit => 10 ,
469
+ FormatOctal => 8 ,
470
+ FormatHex |FormatHEX => 16 ,
471
+ FormatString => util:: unreachable ( )
472
+ } ;
473
+ let mut ( s, _) = match op {
474
+ FormatDigit => {
475
+ let sign = if flags. sign { SignAll } else { SignNeg } ;
476
+ to_str_bytes_common ( & d, radix, false , sign, DigAll )
477
+ }
478
+ _ => to_str_bytes_common ( & ( d as uint ) , radix, false , SignNone , DigAll )
479
+ } ;
480
+ if flags. precision > s. len ( ) {
481
+ let mut s_ = vec:: with_capacity ( flags. precision ) ;
482
+ let n = flags. precision - s. len ( ) ;
483
+ s_. grow ( n, & ( '0' as u8 ) ) ;
484
+ s_. push_all_move ( s) ;
485
+ s = s_;
486
+ }
487
+ assert ! ( !s. is_empty( ) , "string conversion produced empty result" ) ;
488
+ match op {
489
+ FormatDigit => {
490
+ if flags. space && !( s[ 0 ] == '-' as u8 || s[ 0 ] == '+' as u8 ) {
491
+ s. unshift ( ' ' as u8 ) ;
492
+ }
493
+ }
494
+ FormatOctal => {
495
+ if flags. alternate && s[ 0 ] != '0' as u8 {
496
+ s. unshift ( '0' as u8 ) ;
497
+ }
498
+ }
499
+ FormatHex => {
500
+ if flags. alternate {
501
+ let s_ = util:: replace ( & mut s, ~[ '0' as u8 , 'x' as u8 ] ) ;
502
+ s. push_all_move ( s_) ;
503
+ }
504
+ }
505
+ FormatHEX => {
506
+ s = s. into_ascii ( ) . to_upper ( ) . into_bytes ( ) ;
507
+ if flags. alternate {
508
+ let s_ = util:: replace ( & mut s, ~[ '0' as u8 , 'X' as u8 ] ) ;
509
+ s. push_all_move ( s_) ;
510
+ }
511
+ }
512
+ FormatString => util:: unreachable ( )
513
+ }
514
+ s
515
+ }
516
+ }
517
+ }
518
+ String ( s) => {
519
+ match op {
520
+ FormatString => {
521
+ let mut s = s. as_bytes_with_null_consume ( ) ;
522
+ s. pop ( ) ; // remove the null
523
+ if flags. precision > 0 && flags. precision < s. len ( ) {
524
+ s. truncate ( flags. precision ) ;
525
+ }
526
+ s
527
+ }
528
+ _ => {
529
+ return Err ( fmt ! ( "non-string on stack with %%%c" , op. to_char( ) ) )
530
+ }
531
+ }
532
+ }
533
+ } ;
534
+ if flags. width > s. len ( ) {
535
+ let n = flags. width - s. len ( ) ;
536
+ if flags. left {
537
+ s. grow ( n, & ( ' ' as u8 ) ) ;
538
+ } else {
539
+ let mut s_ = vec:: with_capacity ( flags. width ) ;
540
+ s_. grow ( n, & ( ' ' as u8 ) ) ;
541
+ s_. push_all_move ( s) ;
542
+ s = s_;
543
+ }
544
+ }
545
+ Ok ( s)
546
+ }
547
+
352
548
#[ cfg( test) ]
353
549
mod test {
354
550
use super :: * ;
@@ -443,4 +639,20 @@ mod test {
443
639
assert ! ( res. is_ok( ) , res. unwrap_err( ) ) ;
444
640
assert_eq ! ( res. unwrap( ) , bytes!( "\\ E[38;5;42m" ) . to_owned( ) ) ;
445
641
}
642
+
643
+ #[ test]
644
+ fn test_format( ) {
645
+ let mut varstruct = Variables :: new( ) ;
646
+ let vars = & mut varstruct;
647
+ assert_eq!( expand( bytes!( "%p1%s%p2%2 s%p3%2 s%p4%. 2 s") ,
648
+ [ String ( ~"foo") , String ( ~"foo") , String ( ~"f") , String ( ~"foo") ] , vars) ,
649
+ Ok ( bytes!( "foofoo ffo") . to_owned( ) ) ) ;
650
+ assert_eq!( expand( bytes!( "%p1%: -4 . 2 s") , [ String ( ~"foo") ] , vars) ,
651
+ Ok ( bytes!( "fo ") . to_owned( ) ) ) ;
652
+
653
+ assert_eq ! ( expand( bytes!( "%p1%d%p1%.3d%p1%5d%p1%:+d" ) , [ Number ( 1 ) ] , vars) ,
654
+ Ok ( bytes!( "1001 1+1" ) . to_owned( ) ) ) ;
655
+ assert_eq ! ( expand( bytes!( "%p1%o%p1%#o%p2%6.4x%p2%#6.4X" ) , [ Number ( 15 ) , Number ( 27 ) ] , vars) ,
656
+ Ok ( bytes!( "17017 001b0X001B" ) . to_owned( ) ) ) ;
657
+ }
446
658
}
0 commit comments