(index<- ) ./libstd/fmt/parse.rs
git branch: * master c7553ea auto merge of #13609 : richo/rust/str-type-vim, r=alexcrichton
modified: Sat Apr 19 11:22:39 2014
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Parsing of format strings
12 //!
13 //! These structures are used when parsing format strings for the compiler.
14 //! Parsing does not happen at runtime: structures of `std::fmt::rt` are
15 //! generated instead.
16
17 use prelude::*;
18
19 use char;
20 use str;
21
22 /// A piece is a portion of the format string which represents the next part
23 /// to emit. These are emitted as a stream by the `Parser` class.
24 #[deriving(Eq)]
25 pub enum Piece<'a> {
26 /// A literal string which should directly be emitted
27 String(&'a str),
28 /// A back-reference to whatever the current argument is. This is used
29 /// inside of a method call to refer back to the original argument.
30 CurrentArgument,
31 /// This describes that formatting should process the next argument (as
32 /// specified inside) for emission.
33 Argument(Argument<'a>),
34 }
35
36 /// Representation of an argument specification.
37 #[deriving(Eq)]
38 pub struct Argument<'a> {
39 /// Where to find this argument
40 pub position: Position<'a>,
41 /// How to format the argument
42 pub format: FormatSpec<'a>,
43 /// If not `None`, what method to invoke on the argument
44 pub method: Option<~Method<'a>>
45 }
46
47 /// Specification for the formatting of an argument in the format string.
48 #[deriving(Eq)]
49 pub struct FormatSpec<'a> {
50 /// Optionally specified character to fill alignment with
51 pub fill: Option<char>,
52 /// Optionally specified alignment
53 pub align: Alignment,
54 /// Packed version of various flags provided
55 pub flags: uint,
56 /// The integer precision to use
57 pub precision: Count<'a>,
58 /// The string width requested for the resulting format
59 pub width: Count<'a>,
60 /// The descriptor string representing the name of the format desired for
61 /// this argument, this can be empty or any number of characters, although
62 /// it is required to be one word.
63 pub ty: &'a str
64 }
65
66 /// Enum describing where an argument for a format can be located.
67 #[deriving(Eq)]
68 pub enum Position<'a> {
69 /// The argument will be in the next position. This is the default.
70 ArgumentNext,
71 /// The argument is located at a specific index.
72 ArgumentIs(uint),
73 /// The argument has a name.
74 ArgumentNamed(&'a str),
75 }
76
77 /// Enum of alignments which are supported.
78 #[deriving(Eq)]
79 pub enum Alignment {
80 /// The value will be aligned to the left.
81 AlignLeft,
82 /// The value will be aligned to the right.
83 AlignRight,
84 /// The value will take on a default alignment.
85 AlignUnknown,
86 }
87
88 /// Various flags which can be applied to format strings. The meaning of these
89 /// flags is defined by the formatters themselves.
90 #[deriving(Eq)]
91 pub enum Flag {
92 /// A `+` will be used to denote positive numbers.
93 FlagSignPlus,
94 /// A `-` will be used to denote negative numbers. This is the default.
95 FlagSignMinus,
96 /// An alternate form will be used for the value. In the case of numbers,
97 /// this means that the number will be prefixed with the supplied string.
98 FlagAlternate,
99 /// For numbers, this means that the number will be padded with zeroes,
100 /// and the sign (`+` or `-`) will precede them.
101 FlagSignAwareZeroPad,
102 }
103
104 /// A count is used for the precision and width parameters of an integer, and
105 /// can reference either an argument or a literal integer.
106 #[deriving(Eq)]
107 pub enum Count<'a> {
108 /// The count is specified explicitly.
109 CountIs(uint),
110 /// The count is specified by the argument with the given name.
111 CountIsName(&'a str),
112 /// The count is specified by the argument at the given index.
113 CountIsParam(uint),
114 /// The count is specified by the next parameter.
115 CountIsNextParam,
116 /// The count is implied and cannot be explicitly specified.
117 CountImplied,
118 }
119
120 /// Enum describing all of the possible methods which the formatting language
121 /// currently supports.
122 #[deriving(Eq)]
123 pub enum Method<'a> {
124 /// A plural method selects on an integer over a list of either integer or
125 /// keyword-defined clauses. The meaning of the keywords is defined by the
126 /// current locale.
127 ///
128 /// An offset is optionally present at the beginning which is used to
129 /// match against keywords, but it is not matched against the literal
130 /// integers.
131 ///
132 /// The final element of this enum is the default "other" case which is
133 /// always required to be specified.
134 Plural(Option<uint>, Vec<PluralArm<'a>>, Vec<Piece<'a>>),
135
136 /// A select method selects over a string. Each arm is a different string
137 /// which can be selected for.
138 ///
139 /// As with `Plural`, a default "other" case is required as well.
140 Select(Vec<SelectArm<'a>>, Vec<Piece<'a>>),
141 }
142
143 /// A selector for what pluralization a plural method should take
144 #[deriving(Eq, TotalEq, Hash)]
145 pub enum PluralSelector {
146 /// One of the plural keywords should be used
147 Keyword(PluralKeyword),
148 /// A literal pluralization should be used
149 Literal(uint),
150 }
151
152 /// Structure representing one "arm" of the `plural` function.
153 #[deriving(Eq)]
154 pub struct PluralArm<'a> {
155 /// A selector can either be specified by a keyword or with an integer
156 /// literal.
157 pub selector: PluralSelector,
158 /// Array of pieces which are the format of this arm
159 pub result: Vec<Piece<'a>>,
160 }
161
162 /// Enum of the 5 CLDR plural keywords. There is one more, "other", but that
163 /// is specially placed in the `Plural` variant of `Method`.
164 ///
165 /// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html
166 #[deriving(Eq, TotalEq, Hash)]
167 #[allow(missing_doc)]
168 pub enum PluralKeyword {
169 /// The plural form for zero objects.
170 Zero,
171 /// The plural form for one object.
172 One,
173 /// The plural form for two objects.
174 Two,
175 /// The plural form for few objects.
176 Few,
177 /// The plural form for many objects.
178 Many,
179 }
180
181 /// Structure representing one "arm" of the `select` function.
182 #[deriving(Eq)]
183 pub struct SelectArm<'a> {
184 /// String selector which guards this arm
185 pub selector: &'a str,
186 /// Array of pieces which are the format of this arm
187 pub result: Vec<Piece<'a>>,
188 }
189
190 /// The parser structure for interpreting the input format string. This is
191 /// modelled as an iterator over `Piece` structures to form a stream of tokens
192 /// being output.
193 ///
194 /// This is a recursive-descent parser for the sake of simplicity, and if
195 /// necessary there's probably lots of room for improvement performance-wise.
196 pub struct Parser<'a> {
197 input: &'a str,
198 cur: str::CharOffsets<'a>,
199 depth: uint,
200 /// Error messages accumulated during parsing
201 pub errors: Vec<~str>,
202 }
203
204 impl<'a> Iterator<Piece<'a>> for Parser<'a> {
205 fn next(&mut self) -> Option<Piece<'a>> {
206 match self.cur.clone().next() {
207 Some((_, '#')) => { self.cur.next(); Some(CurrentArgument) }
208 Some((_, '{')) => {
209 self.cur.next();
210 let ret = Some(Argument(self.argument()));
211 self.must_consume('}');
212 ret
213 }
214 Some((pos, '\\')) => {
215 self.cur.next();
216 self.escape(); // ensure it's a valid escape sequence
217 Some(String(self.string(pos + 1))) // skip the '\' character
218 }
219 Some((_, '}')) if self.depth == 0 => {
220 self.cur.next();
221 self.err("unmatched `}` found");
222 None
223 }
224 Some((_, '}')) | None => { None }
225 Some((pos, _)) => {
226 Some(String(self.string(pos)))
227 }
228 }
229 }
230 }
231
232 impl<'a> Parser<'a> {
233 /// Creates a new parser for the given format string
234 pub fn new<'a>(s: &'a str) -> Parser<'a> {
235 Parser {
236 input: s,
237 cur: s.char_indices(),
238 depth: 0,
239 errors: vec!(),
240 }
241 }
242
243 /// Notifies of an error. The message doesn't actually need to be of type
244 /// ~str, but I think it does when this eventually uses conditions so it
245 /// might as well start using it now.
246 fn err(&mut self, msg: &str) {
247 self.errors.push(msg.to_owned());
248 }
249
250 /// Optionally consumes the specified character. If the character is not at
251 /// the current position, then the current iterator isn't moved and false is
252 /// returned, otherwise the character is consumed and true is returned.
253 fn consume(&mut self, c: char) -> bool {
254 match self.cur.clone().next() {
255 Some((_, maybe)) if c == maybe => {
256 self.cur.next();
257 true
258 }
259 Some(..) | None => false,
260 }
261 }
262
263 /// Forces consumption of the specified character. If the character is not
264 /// found, an error is emitted.
265 fn must_consume(&mut self, c: char) {
266 self.ws();
267 match self.cur.clone().next() {
268 Some((_, maybe)) if c == maybe => {
269 self.cur.next();
270 }
271 Some((_, other)) => {
272 self.err(
273 format!("expected `{}` but found `{}`", c, other));
274 }
275 None => {
276 self.err(
277 format!("expected `{}` but string was terminated", c));
278 }
279 }
280 }
281
282 /// Attempts to consume any amount of whitespace followed by a character
283 fn wsconsume(&mut self, c: char) -> bool {
284 self.ws(); self.consume(c)
285 }
286
287 /// Consumes all whitespace characters until the first non-whitespace
288 /// character
289 fn ws(&mut self) {
290 loop {
291 match self.cur.clone().next() {
292 Some((_, c)) if char::is_whitespace(c) => { self.cur.next(); }
293 Some(..) | None => { return }
294 }
295 }
296 }
297
298 /// Consumes an escape sequence, failing if there is not a valid character
299 /// to be escaped.
300 fn escape(&mut self) -> char {
301 match self.cur.next() {
302 Some((_, c @ '#')) | Some((_, c @ '{')) |
303 Some((_, c @ '\\')) | Some((_, c @ '}')) => { c }
304 Some((_, c)) => {
305 self.err(format!("invalid escape character `{}`", c));
306 c
307 }
308 None => {
309 self.err("expected an escape sequence, but format string was \
310 terminated");
311 ' '
312 }
313 }
314 }
315
316 /// Parses all of a string which is to be considered a "raw literal" in a
317 /// format string. This is everything outside of the braces.
318 fn string(&mut self, start: uint) -> &'a str {
319 loop {
320 // we may not consume the character, so clone the iterator
321 match self.cur.clone().next() {
322 Some((pos, '\\')) | Some((pos, '#')) |
323 Some((pos, '}')) | Some((pos, '{')) => {
324 return self.input.slice(start, pos);
325 }
326 Some(..) => { self.cur.next(); }
327 None => {
328 self.cur.next();
329 return self.input.slice(start, self.input.len());
330 }
331 }
332 }
333 }
334
335 /// Parses an Argument structure, or what's contained within braces inside
336 /// the format string
337 fn argument(&mut self) -> Argument<'a> {
338 Argument {
339 position: self.position(),
340 format: self.format(),
341 method: self.method(),
342 }
343 }
344
345 /// Parses a positional argument for a format. This could either be an
346 /// integer index of an argument, a named argument, or a blank string.
347 fn position(&mut self) -> Position<'a> {
348 match self.integer() {
349 Some(i) => { ArgumentIs(i) }
350 None => {
351 match self.cur.clone().next() {
352 Some((_, c)) if char::is_alphabetic(c) => {
353 ArgumentNamed(self.word())
354 }
355 _ => ArgumentNext
356 }
357 }
358 }
359 }
360
361 /// Parses a format specifier at the current position, returning all of the
362 /// relevant information in the FormatSpec struct.
363 fn format(&mut self) -> FormatSpec<'a> {
364 let mut spec = FormatSpec {
365 fill: None,
366 align: AlignUnknown,
367 flags: 0,
368 precision: CountImplied,
369 width: CountImplied,
370 ty: self.input.slice(0, 0),
371 };
372 if !self.consume(':') { return spec }
373
374 // fill character
375 match self.cur.clone().next() {
376 Some((_, c)) => {
377 match self.cur.clone().skip(1).next() {
378 Some((_, '>')) | Some((_, '<')) => {
379 spec.fill = Some(c);
380 self.cur.next();
381 }
382 Some(..) | None => {}
383 }
384 }
385 None => {}
386 }
387 // Alignment
388 if self.consume('<') {
389 spec.align = AlignLeft;
390 } else if self.consume('>') {
391 spec.align = AlignRight;
392 }
393 // Sign flags
394 if self.consume('+') {
395 spec.flags |= 1 << (FlagSignPlus as uint);
396 } else if self.consume('-') {
397 spec.flags |= 1 << (FlagSignMinus as uint);
398 }
399 // Alternate marker
400 if self.consume('#') {
401 spec.flags |= 1 << (FlagAlternate as uint);
402 }
403 // Width and precision
404 let mut havewidth = false;
405 if self.consume('0') {
406 // small ambiguity with '0$' as a format string. In theory this is a
407 // '0' flag and then an ill-formatted format string with just a '$'
408 // and no count, but this is better if we instead interpret this as
409 // no '0' flag and '0$' as the width instead.
410 if self.consume('$') {
411 spec.width = CountIsParam(0);
412 havewidth = true;
413 } else {
414 spec.flags |= 1 << (FlagSignAwareZeroPad as uint);
415 }
416 }
417 if !havewidth {
418 spec.width = self.count();
419 }
420 if self.consume('.') {
421 if self.consume('*') {
422 spec.precision = CountIsNextParam;
423 } else {
424 spec.precision = self.count();
425 }
426 }
427 // Finally the actual format specifier
428 if self.consume('?') {
429 spec.ty = "?";
430 } else {
431 spec.ty = self.word();
432 }
433 return spec;
434 }
435
436 /// Parses a method to be applied to the previously specified argument and
437 /// its format. The two current supported methods are 'plural' and 'select'
438 fn method(&mut self) -> Option<~Method<'a>> {
439 if !self.wsconsume(',') {
440 return None;
441 }
442 self.ws();
443 match self.word() {
444 "select" => {
445 self.must_consume(',');
446 Some(self.select())
447 }
448 "plural" => {
449 self.must_consume(',');
450 Some(self.plural())
451 }
452 "" => {
453 self.err("expected method after comma");
454 return None;
455 }
456 method => {
457 self.err(format!("unknown method: `{}`", method));
458 return None;
459 }
460 }
461 }
462
463 /// Parses a 'select' statement (after the initial 'select' word)
464 fn select(&mut self) -> ~Method<'a> {
465 let mut other = None;
466 let mut arms = vec!();
467 // Consume arms one at a time
468 loop {
469 self.ws();
470 let selector = self.word();
471 if selector == "" {
472 self.err("cannot have an empty selector");
473 break
474 }
475 self.must_consume('{');
476 self.depth += 1;
477 let pieces = self.collect();
478 self.depth -= 1;
479 self.must_consume('}');
480 if selector == "other" {
481 if !other.is_none() {
482 self.err("multiple `other` statements in `select");
483 }
484 other = Some(pieces);
485 } else {
486 arms.push(SelectArm { selector: selector, result: pieces });
487 }
488 self.ws();
489 match self.cur.clone().next() {
490 Some((_, '}')) => { break }
491 Some(..) | None => {}
492 }
493 }
494 // The "other" selector must be present
495 let other = match other {
496 Some(arm) => { arm }
497 None => {
498 self.err("`select` statement must provide an `other` case");
499 vec!()
500 }
501 };
502 ~Select(arms, other)
503 }
504
505 /// Parses a 'plural' statement (after the initial 'plural' word)
506 fn plural(&mut self) -> ~Method<'a> {
507 let mut offset = None;
508 let mut other = None;
509 let mut arms = vec!();
510
511 // First, attempt to parse the 'offset:' field. We know the set of
512 // selector words which can appear in plural arms, and the only ones
513 // which start with 'o' are "other" and "offset", hence look two
514 // characters deep to see if we can consume the word "offset"
515 self.ws();
516 let mut it = self.cur.clone();
517 match it.next() {
518 Some((_, 'o')) => {
519 match it.next() {
520 Some((_, 'f')) => {
521 let word = self.word();
522 if word != "offset" {
523 self.err(format!("expected `offset`, found `{}`",
524 word));
525 } else {
526 self.must_consume(':');
527 match self.integer() {
528 Some(i) => { offset = Some(i); }
529 None => {
530 self.err("offset must be an integer");
531 }
532 }
533 }
534 }
535 Some(..) | None => {}
536 }
537 }
538 Some(..) | None => {}
539 }
540
541 // Next, generate all the arms
542 loop {
543 let mut isother = false;
544 let selector = if self.wsconsume('=') {
545 match self.integer() {
546 Some(i) => Literal(i),
547 None => {
548 self.err("plural `=` selectors must be followed by an \
549 integer");
550 Literal(0)
551 }
552 }
553 } else {
554 let word = self.word();
555 match word {
556 "other" => { isother = true; Keyword(Zero) }
557 "zero" => Keyword(Zero),
558 "one" => Keyword(One),
559 "two" => Keyword(Two),
560 "few" => Keyword(Few),
561 "many" => Keyword(Many),
562 word => {
563 self.err(format!("unexpected plural selector `{}`",
564 word));
565 if word == "" {
566 break
567 } else {
568 Keyword(Zero)
569 }
570 }
571 }
572 };
573 self.must_consume('{');
574 self.depth += 1;
575 let pieces = self.collect();
576 self.depth -= 1;
577 self.must_consume('}');
578 if isother {
579 if !other.is_none() {
580 self.err("multiple `other` statements in `select");
581 }
582 other = Some(pieces);
583 } else {
584 arms.push(PluralArm { selector: selector, result: pieces });
585 }
586 self.ws();
587 match self.cur.clone().next() {
588 Some((_, '}')) => { break }
589 Some(..) | None => {}
590 }
591 }
592
593 let other = match other {
594 Some(arm) => { arm }
595 None => {
596 self.err("`plural` statement must provide an `other` case");
597 vec!()
598 }
599 };
600 ~Plural(offset, arms, other)
601 }
602
603 /// Parses a Count parameter at the current position. This does not check
604 /// for 'CountIsNextParam' because that is only used in precision, not
605 /// width.
606 fn count(&mut self) -> Count<'a> {
607 match self.integer() {
608 Some(i) => {
609 if self.consume('$') {
610 CountIsParam(i)
611 } else {
612 CountIs(i)
613 }
614 }
615 None => {
616 let tmp = self.cur.clone();
617 match self.word() {
618 word if word.len() > 0 && self.consume('$') => {
619 CountIsName(word)
620 }
621 _ => {
622 self.cur = tmp;
623 CountImplied
624 }
625 }
626 }
627 }
628 }
629
630 /// Parses a word starting at the current position. A word is considered to
631 /// be an alphabetic character followed by any number of alphanumeric
632 /// characters.
633 fn word(&mut self) -> &'a str {
634 let start = match self.cur.clone().next() {
635 Some((pos, c)) if char::is_XID_start(c) => {
636 self.cur.next();
637 pos
638 }
639 Some(..) | None => { return self.input.slice(0, 0); }
640 };
641 let mut end;
642 loop {
643 match self.cur.clone().next() {
644 Some((_, c)) if char::is_XID_continue(c) => {
645 self.cur.next();
646 }
647 Some((pos, _)) => { end = pos; break }
648 None => { end = self.input.len(); break }
649 }
650 }
651 self.input.slice(start, end)
652 }
653
654 /// Optionally parses an integer at the current position. This doesn't deal
655 /// with overflow at all, it's just accumulating digits.
656 fn integer(&mut self) -> Option<uint> {
657 let mut cur = 0;
658 let mut found = false;
659 loop {
660 match self.cur.clone().next() {
661 Some((_, c)) => {
662 match char::to_digit(c, 10) {
663 Some(i) => {
664 cur = cur * 10 + i;
665 found = true;
666 self.cur.next();
667 }
668 None => { break }
669 }
670 }
671 None => { break }
672 }
673 }
674 if found {
675 return Some(cur);
676 } else {
677 return None;
678 }
679 }
680 }
681
682 #[cfg(test)]
683 mod tests {
684 use super::*;
685 use prelude::*;
686
687 fn same(fmt: &'static str, p: &[Piece<'static>]) {
688 let mut parser = Parser::new(fmt);
689 assert!(p == parser.collect::<Vec<Piece<'static>>>().as_slice());
690 }
691
692 fn fmtdflt() -> FormatSpec<'static> {
693 return FormatSpec {
694 fill: None,
695 align: AlignUnknown,
696 flags: 0,
697 precision: CountImplied,
698 width: CountImplied,
699 ty: "",
700 }
701 }
702
703 fn musterr(s: &str) {
704 let mut p = Parser::new(s);
705 p.next();
706 assert!(p.errors.len() != 0);
707 }
708
709 #[test]
710 fn simple() {
711 same("asdf", [String("asdf")]);
712 same("a\\{b", [String("a"), String("{b")]);
713 same("a\\#b", [String("a"), String("#b")]);
714 same("a\\}b", [String("a"), String("}b")]);
715 same("a\\}", [String("a"), String("}")]);
716 same("\\}", [String("}")]);
717 }
718
719 #[test] fn invalid01() { musterr("{") }
720 #[test] fn invalid02() { musterr("\\") }
721 #[test] fn invalid03() { musterr("\\a") }
722 #[test] fn invalid04() { musterr("{3a}") }
723 #[test] fn invalid05() { musterr("{:|}") }
724 #[test] fn invalid06() { musterr("{:>>>}") }
725
726 #[test]
727 fn format_nothing() {
728 same("{}", [Argument(Argument {
729 position: ArgumentNext,
730 format: fmtdflt(),
731 method: None,
732 })]);
733 }
734 #[test]
735 fn format_position() {
736 same("{3}", [Argument(Argument {
737 position: ArgumentIs(3),
738 format: fmtdflt(),
739 method: None,
740 })]);
741 }
742 #[test]
743 fn format_position_nothing_else() {
744 same("{3:}", [Argument(Argument {
745 position: ArgumentIs(3),
746 format: fmtdflt(),
747 method: None,
748 })]);
749 }
750 #[test]
751 fn format_type() {
752 same("{3:a}", [Argument(Argument {
753 position: ArgumentIs(3),
754 format: FormatSpec {
755 fill: None,
756 align: AlignUnknown,
757 flags: 0,
758 precision: CountImplied,
759 width: CountImplied,
760 ty: "a",
761 },
762 method: None,
763 })]);
764 }
765 #[test]
766 fn format_align_fill() {
767 same("{3:>}", [Argument(Argument {
768 position: ArgumentIs(3),
769 format: FormatSpec {
770 fill: None,
771 align: AlignRight,
772 flags: 0,
773 precision: CountImplied,
774 width: CountImplied,
775 ty: "",
776 },
777 method: None,
778 })]);
779 same("{3:0<}", [Argument(Argument {
780 position: ArgumentIs(3),
781 format: FormatSpec {
782 fill: Some('0'),
783 align: AlignLeft,
784 flags: 0,
785 precision: CountImplied,
786 width: CountImplied,
787 ty: "",
788 },
789 method: None,
790 })]);
791 same("{3:*<abcd}", [Argument(Argument {
792 position: ArgumentIs(3),
793 format: FormatSpec {
794 fill: Some('*'),
795 align: AlignLeft,
796 flags: 0,
797 precision: CountImplied,
798 width: CountImplied,
799 ty: "abcd",
800 },
801 method: None,
802 })]);
803 }
804 #[test]
805 fn format_counts() {
806 same("{:10s}", [Argument(Argument {
807 position: ArgumentNext,
808 format: FormatSpec {
809 fill: None,
810 align: AlignUnknown,
811 flags: 0,
812 precision: CountImplied,
813 width: CountIs(10),
814 ty: "s",
815 },
816 method: None,
817 })]);
818 same("{:10$.10s}", [Argument(Argument {
819 position: ArgumentNext,
820 format: FormatSpec {
821 fill: None,
822 align: AlignUnknown,
823 flags: 0,
824 precision: CountIs(10),
825 width: CountIsParam(10),
826 ty: "s",
827 },
828 method: None,
829 })]);
830 same("{:.*s}", [Argument(Argument {
831 position: ArgumentNext,
832 format: FormatSpec {
833 fill: None,
834 align: AlignUnknown,
835 flags: 0,
836 precision: CountIsNextParam,
837 width: CountImplied,
838 ty: "s",
839 },
840 method: None,
841 })]);
842 same("{:.10$s}", [Argument(Argument {
843 position: ArgumentNext,
844 format: FormatSpec {
845 fill: None,
846 align: AlignUnknown,
847 flags: 0,
848 precision: CountIsParam(10),
849 width: CountImplied,
850 ty: "s",
851 },
852 method: None,
853 })]);
854 same("{:a$.b$s}", [Argument(Argument {
855 position: ArgumentNext,
856 format: FormatSpec {
857 fill: None,
858 align: AlignUnknown,
859 flags: 0,
860 precision: CountIsName("b"),
861 width: CountIsName("a"),
862 ty: "s",
863 },
864 method: None,
865 })]);
866 }
867 #[test]
868 fn format_flags() {
869 same("{:-}", [Argument(Argument {
870 position: ArgumentNext,
871 format: FormatSpec {
872 fill: None,
873 align: AlignUnknown,
874 flags: (1 << FlagSignMinus as uint),
875 precision: CountImplied,
876 width: CountImplied,
877 ty: "",
878 },
879 method: None,
880 })]);
881 same("{:+#}", [Argument(Argument {
882 position: ArgumentNext,
883 format: FormatSpec {
884 fill: None,
885 align: AlignUnknown,
886 flags: (1 << FlagSignPlus as uint) | (1 << FlagAlternate as uint),
887 precision: CountImplied,
888 width: CountImplied,
889 ty: "",
890 },
891 method: None,
892 })]);
893 }
894 #[test]
895 fn format_mixture() {
896 same("abcd {3:a} efg", [String("abcd "), Argument(Argument {
897 position: ArgumentIs(3),
898 format: FormatSpec {
899 fill: None,
900 align: AlignUnknown,
901 flags: 0,
902 precision: CountImplied,
903 width: CountImplied,
904 ty: "a",
905 },
906 method: None,
907 }), String(" efg")]);
908 }
909
910 #[test]
911 fn select_simple() {
912 same("{, select, other { haha } }", [Argument(Argument{
913 position: ArgumentNext,
914 format: fmtdflt(),
915 method: Some(~Select(vec![], vec![String(" haha ")]))
916 })]);
917 same("{1, select, other { haha } }", [Argument(Argument{
918 position: ArgumentIs(1),
919 format: fmtdflt(),
920 method: Some(~Select(vec![], vec![String(" haha ")]))
921 })]);
922 same("{1, select, other {#} }", [Argument(Argument{
923 position: ArgumentIs(1),
924 format: fmtdflt(),
925 method: Some(~Select(vec![], vec![CurrentArgument]))
926 })]);
927 same("{1, select, other {{2, select, other {lol}}} }", [Argument(Argument{
928 position: ArgumentIs(1),
929 format: fmtdflt(),
930 method: Some(~Select(vec![], vec![Argument(Argument{
931 position: ArgumentIs(2),
932 format: fmtdflt(),
933 method: Some(~Select(vec![], vec![String("lol")]))
934 })])) // wat
935 })]);
936 }
937
938 #[test]
939 fn select_cases() {
940 same("{1, select, a{1} b{2} c{3} other{4} }", [Argument(Argument{
941 position: ArgumentIs(1),
942 format: fmtdflt(),
943 method: Some(~Select(vec![
944 SelectArm{ selector: "a", result: vec![String("1")] },
945 SelectArm{ selector: "b", result: vec![String("2")] },
946 SelectArm{ selector: "c", result: vec![String("3")] },
947 ], vec![String("4")]))
948 })]);
949 }
950
951 #[test] fn badselect01() { musterr("{select, }") }
952 #[test] fn badselect02() { musterr("{1, select}") }
953 #[test] fn badselect03() { musterr("{1, select, }") }
954 #[test] fn badselect04() { musterr("{1, select, a {}}") }
955 #[test] fn badselect05() { musterr("{1, select, other }}") }
956 #[test] fn badselect06() { musterr("{1, select, other {}") }
957 #[test] fn badselect07() { musterr("{select, other {}") }
958 #[test] fn badselect08() { musterr("{1 select, other {}") }
959 #[test] fn badselect09() { musterr("{:d select, other {}") }
960 #[test] fn badselect10() { musterr("{1:d select, other {}") }
961
962 #[test]
963 fn plural_simple() {
964 same("{, plural, other { haha } }", [Argument(Argument{
965 position: ArgumentNext,
966 format: fmtdflt(),
967 method: Some(~Plural(None, vec![], vec![String(" haha ")]))
968 })]);
969 same("{:, plural, other { haha } }", [Argument(Argument{
970 position: ArgumentNext,
971 format: fmtdflt(),
972 method: Some(~Plural(None, vec![], vec![String(" haha ")]))
973 })]);
974 same("{, plural, offset:1 =2{2} =3{3} many{yes} other{haha} }",
975 [Argument(Argument{
976 position: ArgumentNext,
977 format: fmtdflt(),
978 method: Some(~Plural(Some(1), vec![
979 PluralArm{ selector: Literal(2), result: vec![String("2")] },
980 PluralArm{ selector: Literal(3), result: vec![String("3")] },
981 PluralArm{ selector: Keyword(Many), result: vec![String("yes")] }
982 ], vec![String("haha")]))
983 })]);
984 }
985 }
libstd/fmt/parse.rs:182:16-182:16 -struct- definition:
pub struct SelectArm<'a> {
/// String selector which guards this arm
pub selector: &'a str,
references:- 9485: } else {
486: arms.push(SelectArm { selector: selector, result: pieces });
487: }
libstd/fmt/parse.rs:144:31-144:31 -enum- definition:
pub enum PluralSelector {
/// One of the plural keywords should be used
Keyword(PluralKeyword),
references:- 6143: /// A selector for what pluralization a plural method should take
145: pub enum PluralSelector {
--
156: /// literal.
157: pub selector: PluralSelector,
158: /// Array of pieces which are the format of this arm
libstd/fmt/parse.rs:195:78-195:78 -struct- definition:
/// necessary there's probably lots of room for improvement performance-wise.
pub struct Parser<'a> {
input: &'a str,
references:- 4204: impl<'a> Iterator<Piece<'a>> for Parser<'a> {
205: fn next(&mut self) -> Option<Piece<'a>> {
--
233: /// Creates a new parser for the given format string
234: pub fn new<'a>(s: &'a str) -> Parser<'a> {
235: Parser {
236: input: s,
libstd/fmt/parse.rs:106:16-106:16 -enum- definition:
pub enum Count<'a> {
/// The count is specified explicitly.
CountIs(uint),
references:- 6105: /// can reference either an argument or a literal integer.
107: pub enum Count<'a> {
--
605: /// width.
606: fn count(&mut self) -> Count<'a> {
607: match self.integer() {
libstd/fmt/parse.rs:48:16-48:16 -struct- definition:
pub struct FormatSpec<'a> {
/// Optionally specified character to fill alignment with
pub fill: Option<char>,
references:- 1047: /// Specification for the formatting of an argument in the format string.
49: pub struct FormatSpec<'a> {
--
362: /// relevant information in the FormatSpec struct.
363: fn format(&mut self) -> FormatSpec<'a> {
364: let mut spec = FormatSpec {
365: fill: None,
libstd/fmt/parse.rs:90:16-90:16 -enum- definition:
pub enum Flag {
/// A `+` will be used to denote positive numbers.
FlagSignPlus,
references:- 389: /// flags is defined by the formatters themselves.
91: pub enum Flag {
libstd/fmt/parse.rs:78:16-78:16 -enum- definition:
pub enum Alignment {
/// The value will be aligned to the left.
AlignLeft,
references:- 777: /// Enum of alignments which are supported.
79: pub enum Alignment {
libstd/fmt/mod.rs:
520: /// Boolean indication of whether the output should be left-aligned
521: pub align: parse::Alignment,
522: /// Optionally specified integer width that the output should be
--
1056: padding: uint,
1057: default: parse::Alignment,
1058: f: |&mut Formatter| -> Result) -> Result {
libstd/fmt/rt.rs:
37: pub fill: char,
38: pub align: parse::Alignment,
39: pub flags: uint,
libstd/fmt/parse.rs:67:16-67:16 -enum- definition:
pub enum Position<'a> {
/// The argument will be in the next position. This is the default.
ArgumentNext,
references:- 566: /// Enum describing where an argument for a format can be located.
68: pub enum Position<'a> {
--
346: /// integer index of an argument, a named argument, or a blank string.
347: fn position(&mut self) -> Position<'a> {
348: match self.integer() {
libstd/fmt/parse.rs:153:16-153:16 -struct- definition:
pub struct PluralArm<'a> {
/// A selector can either be specified by a keyword or with an integer
/// literal.
references:- 9152: /// Structure representing one "arm" of the `plural` function.
154: pub struct PluralArm<'a> {
--
583: } else {
584: arms.push(PluralArm { selector: selector, result: pieces });
585: }
libstd/fmt/parse.rs:122:16-122:16 -enum- definition:
pub enum Method<'a> {
/// A plural method selects on an integer over a list of either integer or
/// keyword-defined clauses. The meaning of the keywords is defined by the
references:- 7505: /// Parses a 'plural' statement (after the initial 'plural' word)
506: fn plural(&mut self) -> ~Method<'a> {
507: let mut offset = None;
libstd/fmt/parse.rs:167:22-167:22 -enum- definition:
pub enum PluralKeyword {
/// The plural form for zero objects.
Zero,
references:- 7146: /// One of the plural keywords should be used
147: Keyword(PluralKeyword),
148: /// A literal pluralization should be used
--
165: /// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html
libstd/fmt/rt.rs:
57: pub enum PluralSelector {
58: Keyword(parse::PluralKeyword),
59: Literal(uint),
libstd/fmt/parse.rs:
165: /// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html
libstd/fmt/parse.rs:24:16-24:16 -enum- definition:
pub enum Piece<'a> {
/// A literal string which should directly be emitted
String(&'a str),
references:- 9133: /// always required to be specified.
134: Plural(Option<uint>, Vec<PluralArm<'a>>, Vec<Piece<'a>>),
--
158: /// Array of pieces which are the format of this arm
159: pub result: Vec<Piece<'a>>,
160: }
--
186: /// Array of pieces which are the format of this arm
187: pub result: Vec<Piece<'a>>,
188: }
--
204: impl<'a> Iterator<Piece<'a>> for Parser<'a> {
205: fn next(&mut self) -> Option<Piece<'a>> {
206: match self.cur.clone().next() {
libstd/fmt/parse.rs:37:16-37:16 -struct- definition:
pub struct Argument<'a> {
/// Where to find this argument
pub position: Position<'a>,
references:- 1036: /// Representation of an argument specification.
38: pub struct Argument<'a> {
--
336: /// the format string
337: fn argument(&mut self) -> Argument<'a> {
338: Argument {
339: position: self.position(),