(index<- )        ./libfmt_macros/lib.rs

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


libfmt_macros/lib.rs:72:16-72:16 -enum- definition:
pub enum Position<'a> {
    /// The argument will be in the next position. This is the default.
    ArgumentNext,
references:- 5
71: /// Enum describing where an argument for a format can be located.
73: pub enum Position<'a> {
--
351:     /// integer index of an argument, a named argument, or a blank string.
352:     fn position(&mut self) -> Position<'a> {
353:         match self.integer() {


libfmt_macros/lib.rs:53:16-53:16 -struct- definition:
pub struct FormatSpec<'a> {
    /// Optionally specified character to fill alignment with
    pub fill: Option<char>,
references:- 10
368:     fn format(&mut self) -> FormatSpec<'a> {
369:         let mut spec = FormatSpec {
370:             fill: None,


libfmt_macros/lib.rs:149:31-149:31 -enum- definition:
pub enum PluralSelector {
    /// One of the plural keywords should be used
    Keyword(PluralKeyword),
references:- 6
148: /// A selector for what pluralization a plural method should take
150: pub enum PluralSelector {
--
161:     /// literal.
162:     pub selector: PluralSelector,
163:     /// Array of pieces which are the format of this arm


libfmt_macros/lib.rs:111:16-111:16 -enum- definition:
pub enum Count<'a> {
    /// The count is specified explicitly.
    CountIs(uint),
references:- 6
610:     /// width.
611:     fn count(&mut self) -> Count<'a> {
612:         match self.integer() {


libfmt_macros/lib.rs:29:16-29:16 -enum- definition:
pub enum Piece<'a> {
    /// A literal string which should directly be emitted
    String(&'a str),
references:- 9
28: /// to emit. These are emitted as a stream by the `Parser` class.
30: pub enum Piece<'a> {
--
209: impl<'a> Iterator<Piece<'a>> for Parser<'a> {
210:     fn next(&mut self) -> Option<Piece<'a>> {
211:         match self.cur.clone().next() {


libfmt_macros/lib.rs:158:16-158:16 -struct- definition:
pub struct PluralArm<'a> {
    /// A selector can either be specified by a keyword or with an integer
    /// literal.
references:- 9
588:             } else {
589:                 arms.push(PluralArm { selector: selector, result: pieces });
590:             }


libfmt_macros/lib.rs:187:16-187:16 -struct- definition:
pub struct SelectArm<'a> {
    /// String selector which guards this arm
    pub selector: &'a str,
references:- 9
490:             } else {
491:                 arms.push(SelectArm { selector: selector, result: pieces });
492:             }


libfmt_macros/lib.rs:95:16-95:16 -enum- definition:
pub enum Flag {
    /// A `+` will be used to denote positive numbers.
    FlagSignPlus,
references:- 3
94: /// flags is defined by the formatters themselves.
96: pub enum Flag {


libfmt_macros/lib.rs:127:16-127: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:- 7
126: /// currently supports.
128: pub enum Method<'a> {
--
468:     /// Parses a 'select' statement (after the initial 'select' word)
469:     fn select(&mut self) -> Box<Method<'a>> {
470:         let mut other = None;
--
510:     /// Parses a 'plural' statement (after the initial 'plural' word)
511:     fn plural(&mut self) -> Box<Method<'a>> {
512:         let mut offset = None;


libfmt_macros/lib.rs:200:78-200:78 -struct- definition:
/// necessary there's probably lots of room for improvement performance-wise.
pub struct Parser<'a> {
    input: &'a str,
references:- 4
237: impl<'a> Parser<'a> {
238:     /// Creates a new parser for the given format string
239:     pub fn new<'a>(s: &'a str) -> Parser<'a> {
240:         Parser {
241:             input: s,


libfmt_macros/lib.rs:172:22-172:22 -enum- definition:
pub enum PluralKeyword {
    /// The plural form for zero objects.
    Zero,
references:- 7
170: /// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html


libfmt_macros/lib.rs:42:16-42:16 -struct- definition:
pub struct Argument<'a> {
    /// Where to find this argument
    pub position: Position<'a>,
references:- 10
342:     fn argument(&mut self) -> Argument<'a> {
343:         Argument {
344:             position: self.position(),


libfmt_macros/lib.rs:83:16-83:16 -enum- definition:
pub enum Alignment {
    /// The value will be aligned to the left.
    AlignLeft,
references:- 4
57:     /// Optionally specified alignment
58:     pub align: Alignment,
59:     /// Packed version of various flags provided
--
82: /// Enum of alignments which are supported.
84: pub enum Alignment {