Skip to content

Commit

Permalink
std: Extract format string parsing out of libstd
Browse files Browse the repository at this point in the history
This code does not belong in libstd, and rather belongs in a dedicated crate. In
the future, the syntax::ext::format module should move to the fmt_macros crate
(hence the name of the crate), but for now the fmt_macros crate will only
contain the format string parser.

The entire fmt_macros crate is marked #[experimental] because it is not meant
for general consumption, only the format!() interface is officially supported,
not the internals.

This is a breaking change for anyone using the internals of std::fmt::parse.
Some of the flags have moved to std::fmt::rt, while the actual parsing support
has all moved to the fmt_macros library.

[breaking-change]
  • Loading branch information
alexcrichton committed May 8, 2014
1 parent 87115fd commit 80487dd
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 48 deletions.
5 changes: 3 additions & 2 deletions mk/crates.mk
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
TARGET_CRATES := libc std green rustuv native flate arena glob term semver \
uuid serialize sync getopts collections num test time rand \
workcache url log regex graphviz core
HOST_CRATES := syntax rustc rustdoc fourcc hexfloat regex_macros
HOST_CRATES := syntax rustc rustdoc fourcc hexfloat regex_macros fmt_macros
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
TOOLS := compiletest rustdoc rustc

Expand All @@ -61,7 +61,7 @@ DEPS_std := core libc native:rustrt native:compiler-rt native:backtrace
DEPS_green := std rand native:context_switch
DEPS_rustuv := std native:uv native:uv_support
DEPS_native := std
DEPS_syntax := std term serialize collections log
DEPS_syntax := std term serialize collections log fmt_macros
DEPS_rustc := syntax native:rustllvm flate arena serialize sync getopts \
collections time log
DEPS_rustdoc := rustc native:hoedown serialize sync getopts collections \
Expand All @@ -88,6 +88,7 @@ DEPS_workcache := std serialize collections log
DEPS_log := std sync
DEPS_regex := std collections
DEPS_regex_macros = syntax std regex
DEPS_fmt_macros = std

TOOL_DEPS_compiletest := test green rustuv getopts
TOOL_DEPS_rustdoc := rustdoc native
Expand Down
17 changes: 10 additions & 7 deletions src/libstd/fmt/parse.rs → src/libfmt_macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Parsing of format strings
//! Macro support for format strings
//!
//! These structures are used when parsing format strings for the compiler.
//! Parsing does not happen at runtime: structures of `std::fmt::rt` are
//! generated instead.

use prelude::*;
#![crate_id = "fmt_macros#0.11-pre"]
#![license = "MIT/ASL2"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![feature(macro_rules, globs)]
#![experimental]

use char;
use owned::Box;
use str;
use std::char;
use std::str;

/// A piece is a portion of the format string which represents the next part
/// to emit. These are emitted as a stream by the `Parser` class.
Expand Down Expand Up @@ -164,7 +168,7 @@ pub struct PluralArm<'a> {
/// is specially placed in the `Plural` variant of `Method`.
///
/// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html
#[deriving(Eq, TotalEq, Hash)]
#[deriving(Eq, TotalEq, Hash, Show)]
#[allow(missing_doc)]
pub enum PluralKeyword {
/// The plural form for zero objects.
Expand Down Expand Up @@ -683,7 +687,6 @@ impl<'a> Parser<'a> {
#[cfg(test)]
mod tests {
use super::*;
use prelude::*;

fn same(fmt: &'static str, p: &[Piece<'static>]) {
let mut parser = Parser::new(fmt);
Expand Down
65 changes: 45 additions & 20 deletions src/libstd/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,9 +510,34 @@ pub use self::num::Radix;
pub use self::num::RadixFmt;

mod num;
pub mod parse;
pub mod rt;

#[cfg(stage0)]
#[allow(missing_doc)]
pub mod parse {
#[deriving(Eq)]
pub enum Alignment {
AlignLeft,
AlignRight,
AlignUnknown,
}

pub enum PluralKeyword {
Zero,
One,
Two,
Few,
Many,
}

pub enum Flag {
FlagSignPlus,
FlagSignMinus,
FlagAlternate,
FlagSignAwareZeroPad,
}
}

pub type Result = io::IoResult<()>;

/// A struct to represent both where to emit formatting strings to and how they
Expand All @@ -524,7 +549,7 @@ pub struct Formatter<'a> {
/// Character used as 'fill' whenever there is alignment
pub fill: char,
/// Boolean indication of whether the output should be left-aligned
pub align: parse::Alignment,
pub align: rt::Alignment,
/// Optionally specified integer width that the output should be
pub width: Option<uint>,
/// Optionally specified precision for numeric types
Expand Down Expand Up @@ -757,7 +782,7 @@ pub unsafe fn write_unsafe(output: &mut io::Writer,
width: None,
precision: None,
buf: output,
align: parse::AlignUnknown,
align: rt::AlignUnknown,
fill: ' ',
args: args,
curarg: args.iter(),
Expand Down Expand Up @@ -890,15 +915,15 @@ impl<'a> Formatter<'a> {
let value = value - match offset { Some(i) => i, None => 0 };
for s in selectors.iter() {
let run = match s.selector {
rt::Keyword(parse::Zero) => value == 0,
rt::Keyword(parse::One) => value == 1,
rt::Keyword(parse::Two) => value == 2,
rt::Keyword(rt::Zero) => value == 0,
rt::Keyword(rt::One) => value == 1,
rt::Keyword(rt::Two) => value == 2,

// FIXME: Few/Many should have a user-specified boundary
// One possible option would be in the function
// pointer of the 'arg: Argument' struct.
rt::Keyword(parse::Few) => value < 8,
rt::Keyword(parse::Many) => value >= 8,
rt::Keyword(rt::Few) => value < 8,
rt::Keyword(rt::Many) => value >= 8,

rt::Literal(..) => false
};
Expand Down Expand Up @@ -960,7 +985,7 @@ impl<'a> Formatter<'a> {
/// This function will correctly account for the flags provided as well as
/// the minimum width. It will not take precision into account.
pub fn pad_integral(&mut self, is_positive: bool, prefix: &str, buf: &[u8]) -> Result {
use fmt::parse::{FlagAlternate, FlagSignPlus, FlagSignAwareZeroPad};
use fmt::rt::{FlagAlternate, FlagSignPlus, FlagSignAwareZeroPad};

let mut width = buf.len();

Expand Down Expand Up @@ -1000,11 +1025,11 @@ impl<'a> Formatter<'a> {
Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => {
self.fill = '0';
try!(write_prefix(self));
self.with_padding(min - width, parse::AlignRight, |f| f.buf.write(buf))
self.with_padding(min - width, rt::AlignRight, |f| f.buf.write(buf))
}
// Otherwise, the sign and prefix goes after the padding
Some(min) => {
self.with_padding(min - width, parse::AlignRight, |f| {
self.with_padding(min - width, rt::AlignRight, |f| {
try!(write_prefix(f)); f.buf.write(buf)
})
}
Expand Down Expand Up @@ -1055,7 +1080,7 @@ impl<'a> Formatter<'a> {
// If we're under both the maximum and the minimum width, then fill
// up the minimum width with the specified string + some alignment.
Some(width) => {
self.with_padding(width - s.len(), parse::AlignLeft, |me| {
self.with_padding(width - s.len(), rt::AlignLeft, |me| {
me.buf.write(s.as_bytes())
})
}
Expand All @@ -1066,21 +1091,21 @@ impl<'a> Formatter<'a> {
/// afterwards depending on whether right or left alingment is requested.
fn with_padding(&mut self,
padding: uint,
default: parse::Alignment,
default: rt::Alignment,
f: |&mut Formatter| -> Result) -> Result {
let align = match self.align {
parse::AlignUnknown => default,
parse::AlignLeft | parse::AlignRight => self.align
rt::AlignUnknown => default,
rt::AlignLeft | rt::AlignRight => self.align
};
if align == parse::AlignLeft {
if align == rt::AlignLeft {
try!(f(self));
}
let mut fill = [0u8, ..4];
let len = self.fill.encode_utf8(fill);
for _ in range(0, padding) {
try!(self.buf.write(fill.slice_to(len)));
}
if align == parse::AlignRight {
if align == rt::AlignRight {
try!(f(self));
}
Ok(())
Expand Down Expand Up @@ -1203,7 +1228,7 @@ impl<T> Poly for T {

impl<T> Pointer for *T {
fn fmt(&self, f: &mut Formatter) -> Result {
f.flags |= 1 << (parse::FlagAlternate as uint);
f.flags |= 1 << (rt::FlagAlternate as uint);
secret_lower_hex::<uint>(&(*self as uint), f)
}
}
Expand Down Expand Up @@ -1304,7 +1329,7 @@ impl<T: Show, U: Show> Show for ::result::Result<T, U> {

impl<'a, T: Show> Show for &'a [T] {
fn fmt(&self, f: &mut Formatter) -> Result {
if f.flags & (1 << (parse::FlagAlternate as uint)) == 0 {
if f.flags & (1 << (rt::FlagAlternate as uint)) == 0 {
try!(write!(f.buf, "["));
}
let mut is_first = true;
Expand All @@ -1316,7 +1341,7 @@ impl<'a, T: Show> Show for &'a [T] {
}
try!(write!(f.buf, "{}", *x))
}
if f.flags & (1 << (parse::FlagAlternate as uint)) == 0 {
if f.flags & (1 << (rt::FlagAlternate as uint)) == 0 {
try!(write!(f.buf, "]"));
}
Ok(())
Expand Down
38 changes: 35 additions & 3 deletions src/libstd/fmt/rt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@
#![allow(missing_doc)]
#![doc(hidden)]

use fmt::parse;
use option::Option;

#[cfg(stage0)]
pub use fmt::parse::{Alignment, AlignLeft, AlignRight, AlignUnknown};
#[cfg(stage0)]
pub use fmt::parse::{PluralKeyword, Zero, One, Two, Few, Many};
#[cfg(stage0)]
pub use fmt::parse::{Flag, FlagSignPlus, FlagSignMinus, FlagSignAwareZeroPad};
#[cfg(stage0)]
pub use fmt::parse::{FlagAlternate};

pub enum Piece<'a> {
String(&'a str),
// FIXME(#8259): this shouldn't require the unit-value here
Expand All @@ -35,12 +43,20 @@ pub struct Argument<'a> {

pub struct FormatSpec {
pub fill: char,
pub align: parse::Alignment,
pub align: Alignment,
pub flags: uint,
pub precision: Count,
pub width: Count,
}

#[cfg(not(stage0))]
#[deriving(Eq)]
pub enum Alignment {
AlignLeft,
AlignRight,
AlignUnknown,
}

pub enum Count {
CountIs(uint), CountIsParam(uint), CountIsNextParam, CountImplied,
}
Expand All @@ -49,16 +65,32 @@ pub enum Position {
ArgumentNext, ArgumentIs(uint)
}

#[cfg(not(stage0))]
pub enum Flag {
FlagSignPlus,
FlagSignMinus,
FlagAlternate,
FlagSignAwareZeroPad,
}

pub enum Method<'a> {
Plural(Option<uint>, &'a [PluralArm<'a>], &'a [Piece<'a>]),
Select(&'a [SelectArm<'a>], &'a [Piece<'a>]),
}

pub enum PluralSelector {
Keyword(parse::PluralKeyword),
Keyword(PluralKeyword),
Literal(uint),
}

pub enum PluralKeyword {
Zero,
One,
Two,
Few,
Many,
}

pub struct PluralArm<'a> {
pub selector: PluralSelector,
pub result: &'a [Piece<'a>],
Expand Down
22 changes: 6 additions & 16 deletions src/libsyntax/ext/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use parse::token::InternedString;
use parse::token;
use rsparse = parse;

use std::fmt::parse;
use parse = fmt_macros;
use collections::{HashMap, HashSet};

#[deriving(Eq)]
Expand Down Expand Up @@ -232,7 +232,7 @@ impl<'a, 'b> Context<'a, 'b> {
parse::Keyword(name) => {
self.ecx.span_err(self.fmtsp,
format!("duplicate selector \
`{:?}`", name));
`{}`", name));
}
parse::Literal(idx) => {
self.ecx.span_err(self.fmtsp,
Expand Down Expand Up @@ -375,21 +375,11 @@ impl<'a, 'b> Context<'a, 'b> {
return vec!(unnamed, allow_dead_code);
}

fn parsepath(&self, s: &str) -> Vec<ast::Ident> {
vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
self.ecx.ident_of("parse"), self.ecx.ident_of(s))
}

fn rtpath(&self, s: &str) -> Vec<ast::Ident> {
vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
self.ecx.ident_of("rt"), self.ecx.ident_of(s))
}

fn ctpath(&self, s: &str) -> Vec<ast::Ident> {
vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
self.ecx.ident_of("parse"), self.ecx.ident_of(s))
}

fn none(&self) -> @ast::Expr {
let none = self.ecx.path_global(self.fmtsp, vec!(
self.ecx.ident_of("std"),
Expand Down Expand Up @@ -475,7 +465,7 @@ impl<'a, 'b> Context<'a, 'b> {
}).collect();
let (lr, selarg) = match arm.selector {
parse::Keyword(t) => {
let p = self.ctpath(format!("{:?}", t));
let p = self.rtpath(t.to_str());
let p = self.ecx.path_global(sp, p);
(self.rtpath("Keyword"), self.ecx.expr_path(p))
}
Expand Down Expand Up @@ -564,13 +554,13 @@ impl<'a, 'b> Context<'a, 'b> {
let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
let align = match arg.format.align {
parse::AlignLeft => {
self.ecx.path_global(sp, self.parsepath("AlignLeft"))
self.ecx.path_global(sp, self.rtpath("AlignLeft"))
}
parse::AlignRight => {
self.ecx.path_global(sp, self.parsepath("AlignRight"))
self.ecx.path_global(sp, self.rtpath("AlignRight"))
}
parse::AlignUnknown => {
self.ecx.path_global(sp, self.parsepath("AlignUnknown"))
self.ecx.path_global(sp, self.rtpath("AlignUnknown"))
}
};
let align = self.ecx.expr_path(align);
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extern crate term;
extern crate collections;
#[phase(syntax, link)]
extern crate log;
extern crate fmt_macros;

pub mod util {
pub mod interner;
Expand Down

5 comments on commit 80487dd

@bors
Copy link
Contributor

@bors bors commented on 80487dd May 8, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from brson
at alexcrichton@80487dd

@bors
Copy link
Contributor

@bors bors commented on 80487dd May 8, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging alexcrichton/rust/libfmt = 80487dd into auto

@bors
Copy link
Contributor

@bors bors commented on 80487dd May 8, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alexcrichton/rust/libfmt = 80487dd merged ok, testing candidate = d8781b3

@bors
Copy link
Contributor

@bors bors commented on 80487dd May 8, 2014

@bors
Copy link
Contributor

@bors bors commented on 80487dd May 8, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = d8781b3

Please sign in to comment.