std: Extract format string parsing out of libstd

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]
This commit is contained in:
Alex Crichton 2014-05-06 09:52:53 -07:00
parent 87115fd001
commit 80487ddcad
6 changed files with 100 additions and 48 deletions

View File

@ -52,7 +52,7 @@
TARGET_CRATES := libc std green rustuv native flate arena glob term semver \ TARGET_CRATES := libc std green rustuv native flate arena glob term semver \
uuid serialize sync getopts collections num test time rand \ uuid serialize sync getopts collections num test time rand \
workcache url log regex graphviz core 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) CRATES := $(TARGET_CRATES) $(HOST_CRATES)
TOOLS := compiletest rustdoc rustc TOOLS := compiletest rustdoc rustc
@ -61,7 +61,7 @@ DEPS_std := core libc native:rustrt native:compiler-rt native:backtrace
DEPS_green := std rand native:context_switch DEPS_green := std rand native:context_switch
DEPS_rustuv := std native:uv native:uv_support DEPS_rustuv := std native:uv native:uv_support
DEPS_native := std 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 \ DEPS_rustc := syntax native:rustllvm flate arena serialize sync getopts \
collections time log collections time log
DEPS_rustdoc := rustc native:hoedown serialize sync getopts collections \ DEPS_rustdoc := rustc native:hoedown serialize sync getopts collections \
@ -88,6 +88,7 @@ DEPS_workcache := std serialize collections log
DEPS_log := std sync DEPS_log := std sync
DEPS_regex := std collections DEPS_regex := std collections
DEPS_regex_macros = syntax std regex DEPS_regex_macros = syntax std regex
DEPS_fmt_macros = std
TOOL_DEPS_compiletest := test green rustuv getopts TOOL_DEPS_compiletest := test green rustuv getopts
TOOL_DEPS_rustdoc := rustdoc native TOOL_DEPS_rustdoc := rustdoc native

View File

@ -8,17 +8,21 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // 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. //! These structures are used when parsing format strings for the compiler.
//! Parsing does not happen at runtime: structures of `std::fmt::rt` are //! Parsing does not happen at runtime: structures of `std::fmt::rt` are
//! generated instead. //! 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 std::char;
use owned::Box; use std::str;
use str;
/// A piece is a portion of the format string which represents the next part /// 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. /// to emit. These are emitted as a stream by the `Parser` class.
@ -164,7 +168,7 @@ pub struct PluralArm<'a> {
/// is specially placed in the `Plural` variant of `Method`. /// is specially placed in the `Plural` variant of `Method`.
/// ///
/// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html /// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html
#[deriving(Eq, TotalEq, Hash)] #[deriving(Eq, TotalEq, Hash, Show)]
#[allow(missing_doc)] #[allow(missing_doc)]
pub enum PluralKeyword { pub enum PluralKeyword {
/// The plural form for zero objects. /// The plural form for zero objects.
@ -683,7 +687,6 @@ impl<'a> Parser<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use prelude::*;
fn same(fmt: &'static str, p: &[Piece<'static>]) { fn same(fmt: &'static str, p: &[Piece<'static>]) {
let mut parser = Parser::new(fmt); let mut parser = Parser::new(fmt);

View File

@ -510,9 +510,34 @@ pub use self::num::Radix;
pub use self::num::RadixFmt; pub use self::num::RadixFmt;
mod num; mod num;
pub mod parse;
pub mod rt; 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<()>; pub type Result = io::IoResult<()>;
/// A struct to represent both where to emit formatting strings to and how they /// A struct to represent both where to emit formatting strings to and how they
@ -524,7 +549,7 @@ pub struct Formatter<'a> {
/// Character used as 'fill' whenever there is alignment /// Character used as 'fill' whenever there is alignment
pub fill: char, pub fill: char,
/// Boolean indication of whether the output should be left-aligned /// 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 /// Optionally specified integer width that the output should be
pub width: Option<uint>, pub width: Option<uint>,
/// Optionally specified precision for numeric types /// Optionally specified precision for numeric types
@ -757,7 +782,7 @@ pub unsafe fn write_unsafe(output: &mut io::Writer,
width: None, width: None,
precision: None, precision: None,
buf: output, buf: output,
align: parse::AlignUnknown, align: rt::AlignUnknown,
fill: ' ', fill: ' ',
args: args, args: args,
curarg: args.iter(), curarg: args.iter(),
@ -890,15 +915,15 @@ impl<'a> Formatter<'a> {
let value = value - match offset { Some(i) => i, None => 0 }; let value = value - match offset { Some(i) => i, None => 0 };
for s in selectors.iter() { for s in selectors.iter() {
let run = match s.selector { let run = match s.selector {
rt::Keyword(parse::Zero) => value == 0, rt::Keyword(rt::Zero) => value == 0,
rt::Keyword(parse::One) => value == 1, rt::Keyword(rt::One) => value == 1,
rt::Keyword(parse::Two) => value == 2, rt::Keyword(rt::Two) => value == 2,
// FIXME: Few/Many should have a user-specified boundary // FIXME: Few/Many should have a user-specified boundary
// One possible option would be in the function // One possible option would be in the function
// pointer of the 'arg: Argument' struct. // pointer of the 'arg: Argument' struct.
rt::Keyword(parse::Few) => value < 8, rt::Keyword(rt::Few) => value < 8,
rt::Keyword(parse::Many) => value >= 8, rt::Keyword(rt::Many) => value >= 8,
rt::Literal(..) => false rt::Literal(..) => false
}; };
@ -960,7 +985,7 @@ impl<'a> Formatter<'a> {
/// This function will correctly account for the flags provided as well as /// This function will correctly account for the flags provided as well as
/// the minimum width. It will not take precision into account. /// the minimum width. It will not take precision into account.
pub fn pad_integral(&mut self, is_positive: bool, prefix: &str, buf: &[u8]) -> Result { 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(); let mut width = buf.len();
@ -1000,11 +1025,11 @@ impl<'a> Formatter<'a> {
Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => { Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => {
self.fill = '0'; self.fill = '0';
try!(write_prefix(self)); 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 // Otherwise, the sign and prefix goes after the padding
Some(min) => { 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) try!(write_prefix(f)); f.buf.write(buf)
}) })
} }
@ -1055,7 +1080,7 @@ impl<'a> Formatter<'a> {
// If we're under both the maximum and the minimum width, then fill // If we're under both the maximum and the minimum width, then fill
// up the minimum width with the specified string + some alignment. // up the minimum width with the specified string + some alignment.
Some(width) => { 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()) me.buf.write(s.as_bytes())
}) })
} }
@ -1066,13 +1091,13 @@ impl<'a> Formatter<'a> {
/// afterwards depending on whether right or left alingment is requested. /// afterwards depending on whether right or left alingment is requested.
fn with_padding(&mut self, fn with_padding(&mut self,
padding: uint, padding: uint,
default: parse::Alignment, default: rt::Alignment,
f: |&mut Formatter| -> Result) -> Result { f: |&mut Formatter| -> Result) -> Result {
let align = match self.align { let align = match self.align {
parse::AlignUnknown => default, rt::AlignUnknown => default,
parse::AlignLeft | parse::AlignRight => self.align rt::AlignLeft | rt::AlignRight => self.align
}; };
if align == parse::AlignLeft { if align == rt::AlignLeft {
try!(f(self)); try!(f(self));
} }
let mut fill = [0u8, ..4]; let mut fill = [0u8, ..4];
@ -1080,7 +1105,7 @@ impl<'a> Formatter<'a> {
for _ in range(0, padding) { for _ in range(0, padding) {
try!(self.buf.write(fill.slice_to(len))); try!(self.buf.write(fill.slice_to(len)));
} }
if align == parse::AlignRight { if align == rt::AlignRight {
try!(f(self)); try!(f(self));
} }
Ok(()) Ok(())
@ -1203,7 +1228,7 @@ impl<T> Poly for T {
impl<T> Pointer for *T { impl<T> Pointer for *T {
fn fmt(&self, f: &mut Formatter) -> Result { 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) secret_lower_hex::<uint>(&(*self as uint), f)
} }
} }
@ -1304,7 +1329,7 @@ impl<T: Show, U: Show> Show for ::result::Result<T, U> {
impl<'a, T: Show> Show for &'a [T] { impl<'a, T: Show> Show for &'a [T] {
fn fmt(&self, f: &mut Formatter) -> Result { 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, "[")); try!(write!(f.buf, "["));
} }
let mut is_first = true; let mut is_first = true;
@ -1316,7 +1341,7 @@ impl<'a, T: Show> Show for &'a [T] {
} }
try!(write!(f.buf, "{}", *x)) 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, "]")); try!(write!(f.buf, "]"));
} }
Ok(()) Ok(())

View File

@ -17,9 +17,17 @@
#![allow(missing_doc)] #![allow(missing_doc)]
#![doc(hidden)] #![doc(hidden)]
use fmt::parse;
use option::Option; 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> { pub enum Piece<'a> {
String(&'a str), String(&'a str),
// FIXME(#8259): this shouldn't require the unit-value here // FIXME(#8259): this shouldn't require the unit-value here
@ -35,12 +43,20 @@ pub struct Argument<'a> {
pub struct FormatSpec { pub struct FormatSpec {
pub fill: char, pub fill: char,
pub align: parse::Alignment, pub align: Alignment,
pub flags: uint, pub flags: uint,
pub precision: Count, pub precision: Count,
pub width: Count, pub width: Count,
} }
#[cfg(not(stage0))]
#[deriving(Eq)]
pub enum Alignment {
AlignLeft,
AlignRight,
AlignUnknown,
}
pub enum Count { pub enum Count {
CountIs(uint), CountIsParam(uint), CountIsNextParam, CountImplied, CountIs(uint), CountIsParam(uint), CountIsNextParam, CountImplied,
} }
@ -49,16 +65,32 @@ pub enum Position {
ArgumentNext, ArgumentIs(uint) ArgumentNext, ArgumentIs(uint)
} }
#[cfg(not(stage0))]
pub enum Flag {
FlagSignPlus,
FlagSignMinus,
FlagAlternate,
FlagSignAwareZeroPad,
}
pub enum Method<'a> { pub enum Method<'a> {
Plural(Option<uint>, &'a [PluralArm<'a>], &'a [Piece<'a>]), Plural(Option<uint>, &'a [PluralArm<'a>], &'a [Piece<'a>]),
Select(&'a [SelectArm<'a>], &'a [Piece<'a>]), Select(&'a [SelectArm<'a>], &'a [Piece<'a>]),
} }
pub enum PluralSelector { pub enum PluralSelector {
Keyword(parse::PluralKeyword), Keyword(PluralKeyword),
Literal(uint), Literal(uint),
} }
pub enum PluralKeyword {
Zero,
One,
Two,
Few,
Many,
}
pub struct PluralArm<'a> { pub struct PluralArm<'a> {
pub selector: PluralSelector, pub selector: PluralSelector,
pub result: &'a [Piece<'a>], pub result: &'a [Piece<'a>],

View File

@ -18,7 +18,7 @@ use parse::token::InternedString;
use parse::token; use parse::token;
use rsparse = parse; use rsparse = parse;
use std::fmt::parse; use parse = fmt_macros;
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
#[deriving(Eq)] #[deriving(Eq)]
@ -232,7 +232,7 @@ impl<'a, 'b> Context<'a, 'b> {
parse::Keyword(name) => { parse::Keyword(name) => {
self.ecx.span_err(self.fmtsp, self.ecx.span_err(self.fmtsp,
format!("duplicate selector \ format!("duplicate selector \
`{:?}`", name)); `{}`", name));
} }
parse::Literal(idx) => { parse::Literal(idx) => {
self.ecx.span_err(self.fmtsp, self.ecx.span_err(self.fmtsp,
@ -375,21 +375,11 @@ impl<'a, 'b> Context<'a, 'b> {
return vec!(unnamed, allow_dead_code); 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> { fn rtpath(&self, s: &str) -> Vec<ast::Ident> {
vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
self.ecx.ident_of("rt"), self.ecx.ident_of(s)) 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 { fn none(&self) -> @ast::Expr {
let none = self.ecx.path_global(self.fmtsp, vec!( let none = self.ecx.path_global(self.fmtsp, vec!(
self.ecx.ident_of("std"), self.ecx.ident_of("std"),
@ -475,7 +465,7 @@ impl<'a, 'b> Context<'a, 'b> {
}).collect(); }).collect();
let (lr, selarg) = match arm.selector { let (lr, selarg) = match arm.selector {
parse::Keyword(t) => { parse::Keyword(t) => {
let p = self.ctpath(format!("{:?}", t)); let p = self.rtpath(t.to_str());
let p = self.ecx.path_global(sp, p); let p = self.ecx.path_global(sp, p);
(self.rtpath("Keyword"), self.ecx.expr_path(p)) (self.rtpath("Keyword"), self.ecx.expr_path(p))
} }
@ -564,13 +554,13 @@ impl<'a, 'b> Context<'a, 'b> {
let fill = self.ecx.expr_lit(sp, ast::LitChar(fill)); let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
let align = match arg.format.align { let align = match arg.format.align {
parse::AlignLeft => { parse::AlignLeft => {
self.ecx.path_global(sp, self.parsepath("AlignLeft")) self.ecx.path_global(sp, self.rtpath("AlignLeft"))
} }
parse::AlignRight => { parse::AlignRight => {
self.ecx.path_global(sp, self.parsepath("AlignRight")) self.ecx.path_global(sp, self.rtpath("AlignRight"))
} }
parse::AlignUnknown => { 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); let align = self.ecx.expr_path(align);

View File

@ -35,6 +35,7 @@ extern crate term;
extern crate collections; extern crate collections;
#[phase(syntax, link)] #[phase(syntax, link)]
extern crate log; extern crate log;
extern crate fmt_macros;
pub mod util { pub mod util {
pub mod interner; pub mod interner;