From 80487ddcadda819e709beb9b996b12d322aa11a6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 6 May 2014 09:52:53 -0700 Subject: [PATCH] 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] --- mk/crates.mk | 5 +- .../fmt/parse.rs => libfmt_macros/lib.rs} | 17 +++-- src/libstd/fmt/mod.rs | 65 +++++++++++++------ src/libstd/fmt/rt.rs | 38 ++++++++++- src/libsyntax/ext/format.rs | 22 ++----- src/libsyntax/lib.rs | 1 + 6 files changed, 100 insertions(+), 48 deletions(-) rename src/{libstd/fmt/parse.rs => libfmt_macros/lib.rs} (99%) diff --git a/mk/crates.mk b/mk/crates.mk index 9da80c2bc11..b75b5ba81e2 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -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 @@ -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 \ @@ -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 diff --git a/src/libstd/fmt/parse.rs b/src/libfmt_macros/lib.rs similarity index 99% rename from src/libstd/fmt/parse.rs rename to src/libfmt_macros/lib.rs index ba126e00153..91b3fefdd02 100644 --- a/src/libstd/fmt/parse.rs +++ b/src/libfmt_macros/lib.rs @@ -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. @@ -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. @@ -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); diff --git a/src/libstd/fmt/mod.rs b/src/libstd/fmt/mod.rs index 74ab874d319..8846fa3f6f3 100644 --- a/src/libstd/fmt/mod.rs +++ b/src/libstd/fmt/mod.rs @@ -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 @@ -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, /// Optionally specified precision for numeric types @@ -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(), @@ -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 }; @@ -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(); @@ -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) }) } @@ -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()) }) } @@ -1066,13 +1091,13 @@ 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]; @@ -1080,7 +1105,7 @@ impl<'a> Formatter<'a> { 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(()) @@ -1203,7 +1228,7 @@ impl Poly for T { impl 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::(&(*self as uint), f) } } @@ -1304,7 +1329,7 @@ impl Show for ::result::Result { 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; @@ -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(()) diff --git a/src/libstd/fmt/rt.rs b/src/libstd/fmt/rt.rs index 01c2c06c3fb..33e86a4485b 100644 --- a/src/libstd/fmt/rt.rs +++ b/src/libstd/fmt/rt.rs @@ -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 @@ -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, } @@ -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, &'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>], diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index fc3136996ae..7b465a2dc35 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -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)] @@ -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, @@ -375,21 +375,11 @@ impl<'a, 'b> Context<'a, 'b> { return vec!(unnamed, allow_dead_code); } - fn parsepath(&self, s: &str) -> Vec { - 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 { 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 { - 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"), @@ -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)) } @@ -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); diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 012bc50ecab..5e1d988df5c 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -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;