-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
})
}
+/// An extension of `pipes::stream` that provides synchronous message sending.
+pub struct SyncChan<T> { priv duplex_stream: DuplexStream<T, ()> }
+/// An extension of `pipes::stream` that acknowledges each message received.
+pub struct SyncPort<T> { priv duplex_stream: DuplexStream<(), T> }
+
+impl<T: Send> GenericChan<T> for SyncChan<T> {
+ fn send(&self, val: T) {
+ assert!(self.try_send(val), "SyncChan.send: receiving port closed");
+ }
+}
+
+impl<T: Send> GenericSmartChan<T> for SyncChan<T> {
+ /// Sends a message, or report if the receiver has closed the connection before receiving.
+ fn try_send(&self, val: T) -> bool {
+ self.duplex_stream.try_send(val) && self.duplex_stream.try_recv().is_some()
+ }
+}
+
+impl<T: Send> GenericPort<T> for SyncPort<T> {
+ fn recv(&self) -> T {
+ self.try_recv().expect("SyncPort.recv: sending channel closed")
+ }
+
+ fn try_recv(&self) -> Option<T> {
+ do self.duplex_stream.try_recv().map_move |val| {
+ self.duplex_stream.try_send(());
+ val
+ }
+ }
+}
+
+impl<T: Send> Peekable<T> for SyncPort<T> {
+ fn peek(&self) -> bool {
+ self.duplex_stream.peek()
+ }
+}
+
+/// Creates a stream whose channel, upon sending a message, blocks until the message is received.
+pub fn rendezvous<T: Send>() -> (SyncPort<T>, SyncChan<T>) {
+ let (chan_stream, port_stream) = DuplexStream();
+ (SyncPort { duplex_stream: port_stream }, SyncChan { duplex_stream: chan_stream })
+}
+
#[cfg(test)]
mod test {
- use comm::DuplexStream;
+ use comm::{DuplexStream, rendezvous};
+ use std::rt::test::run_in_newsched_task;
+ use std::task::spawn_unlinked;
+
#[test]
pub fn DuplexStream1() {
assert!(left.recv() == 123);
assert!(right.recv() == ~"abc");
}
+
+ #[test]
+ pub fn basic_rendezvous_test() {
+ let (port, chan) = rendezvous();
+
+ do spawn {
+ chan.send("abc");
+ }
+
+ assert!(port.recv() == "abc");
+ }
+
+ #[test]
+ fn recv_a_lot() {
+ // Rendezvous streams should be able to handle any number of messages being sent
+ do run_in_newsched_task {
+ let (port, chan) = rendezvous();
+ do spawn {
+ do 1000000.times { chan.send(()) }
+ }
+ do 1000000.times { port.recv() }
+ }
+ }
+
+ #[test]
+ fn send_and_fail_and_try_recv() {
+ let (port, chan) = rendezvous();
+ do spawn_unlinked {
+ chan.duplex_stream.send(()); // Can't access this field outside this module
+ fail!()
+ }
+ port.recv()
+ }
+
+ #[test]
+ fn try_send_and_recv_then_fail_before_ack() {
+ let (port, chan) = rendezvous();
+ do spawn_unlinked {
+ port.duplex_stream.recv();
+ fail!()
+ }
+ chan.try_send(());
+ }
+
+ #[test]
+ #[should_fail]
+ fn send_and_recv_then_fail_before_ack() {
+ let (port, chan) = rendezvous();
+ do spawn_unlinked {
+ port.duplex_stream.recv();
+ fail!()
+ }
+ chan.send(());
+ }
}
Char(char),
AnyChar,
AnySequence,
- AnyWithin(~[char]),
- AnyExcept(~[char])
+ AnyWithin(~[CharSpecifier]),
+ AnyExcept(~[CharSpecifier])
+}
+
+#[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, IterBytes)]
+enum CharSpecifier {
+ SingleChar(char),
+ CharRange(char, char)
}
#[deriving(Eq)]
* This function compiles Unix shell style patterns: `?` matches any single character,
* `*` matches any (possibly empty) sequence of characters and `[...]` matches any character
* inside the brackets, unless the first character is `!` in which case it matches any
- * character except those between the `!` and the `]`.
+ * character except those between the `!` and the `]`. Character sequences can also specify
+ * ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character
+ * between 0 and 9 inclusive.
*
* The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets (e.g. `[?]`).
* When a `]` occurs immediately following `[` or `[!` then it is interpreted as
* being part of, rather then ending, the character set, so `]` and NOT `]` can be
- * matched by `[]]` and `[!]]` respectively.
+ * matched by `[]]` and `[!]]` respectively. The `-` character can be specified inside a
+ * character sequence pattern by placing it at the start or the end, e.g. `[abc-]`.
*
* When a `[` does not have a closing `]` before the end of the string then the `[` will
* be treated literally.
match chars.slice_from(i + 3).position_elem(&']') {
None => (),
Some(j) => {
- tokens.push(AnyExcept(chars.slice(i + 2, i + 3 + j).to_owned()));
+ let cs = parse_char_specifiers(chars.slice(i + 2, i + 3 + j));
+ tokens.push(AnyExcept(cs));
i += j + 4;
loop;
}
match chars.slice_from(i + 2).position_elem(&']') {
None => (),
Some(j) => {
- tokens.push(AnyWithin(chars.slice(i + 1, i + 2 + j).to_owned()));
+ let cs = parse_char_specifiers(chars.slice(i + 1, i + 2 + j));
+ tokens.push(AnyWithin(cs));
i += j + 3;
loop;
}
AnyChar => {
!require_literal(c)
}
- AnyWithin(ref chars) => {
- !require_literal(c) &&
- chars.iter()
- .rposition(|&e| chars_eq(e, c, options.case_sensitive)).is_some()
+ AnyWithin(ref specifiers) => {
+ !require_literal(c) && in_char_specifiers(*specifiers, c, options)
}
- AnyExcept(ref chars) => {
- !require_literal(c) &&
- chars.iter()
- .rposition(|&e| chars_eq(e, c, options.case_sensitive)).is_none()
+ AnyExcept(ref specifiers) => {
+ !require_literal(c) && !in_char_specifiers(*specifiers, c, options)
}
Char(c2) => {
chars_eq(c, c2, options.case_sensitive)
}
+fn parse_char_specifiers(s: &[char]) -> ~[CharSpecifier] {
+ let mut cs = ~[];
+ let mut i = 0;
+ while i < s.len() {
+ if i + 3 <= s.len() && s[i + 1] == '-' {
+ cs.push(CharRange(s[i], s[i + 2]));
+ i += 3;
+ } else {
+ cs.push(SingleChar(s[i]));
+ i += 1;
+ }
+ }
+ cs
+}
+
+fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool {
+
+ for &specifier in specifiers.iter() {
+ match specifier {
+ SingleChar(sc) => {
+ if chars_eq(c, sc, options.case_sensitive) {
+ return true;
+ }
+ }
+ CharRange(start, end) => {
+
+ // FIXME: work with non-ascii chars properly (issue #1347)
+ if !options.case_sensitive && c.is_ascii() && start.is_ascii() && end.is_ascii() {
+
+ let start = start.to_ascii().to_lower();
+ let end = end.to_ascii().to_lower();
+
+ let start_up = start.to_upper();
+ let end_up = end.to_upper();
+
+ // only allow case insensitive matching when
+ // both start and end are within a-z or A-Z
+ if start != start_up && end != end_up {
+ let start = start.to_char();
+ let end = end.to_char();
+ let c = c.to_ascii().to_lower().to_char();
+ if c >= start && c <= end {
+ return true;
+ }
+ }
+ }
+
+ if c >= start && c <= end {
+ return true;
+ }
+ }
+ }
+ }
+
+ false
+}
+
/// A helper function to determine if two chars are (possibly case-insensitively) equal.
fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) {
glob("/*/*/*/*").skip(10000).next();
}
+ #[test]
+ fn test_range_pattern() {
+
+ let pat = Pattern::new("a[0-9]b");
+ for i in range(0, 10) {
+ assert!(pat.matches(fmt!("a%db", i)));
+ }
+ assert!(!pat.matches("a_b"));
+
+ let pat = Pattern::new("a[!0-9]b");
+ for i in range(0, 10) {
+ assert!(!pat.matches(fmt!("a%db", i)));
+ }
+ assert!(pat.matches("a_b"));
+
+ let pats = ["[a-z123]", "[1a-z23]", "[123a-z]"];
+ for &p in pats.iter() {
+ let pat = Pattern::new(p);
+ for c in "abcdefghijklmnopqrstuvwxyz".iter() {
+ assert!(pat.matches(c.to_str()));
+ }
+ for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ".iter() {
+ let options = MatchOptions {case_sensitive: false, .. MatchOptions::new()};
+ assert!(pat.matches_with(c.to_str(), options));
+ }
+ assert!(pat.matches("1"));
+ assert!(pat.matches("2"));
+ assert!(pat.matches("3"));
+ }
+
+ let pats = ["[abc-]", "[-abc]", "[a-c-]"];
+ for &p in pats.iter() {
+ let pat = Pattern::new(p);
+ assert!(pat.matches("a"));
+ assert!(pat.matches("b"));
+ assert!(pat.matches("c"));
+ assert!(pat.matches("-"));
+ assert!(!pat.matches("d"));
+ }
+
+ let pat = Pattern::new("[2-1]");
+ assert!(!pat.matches("1"));
+ assert!(!pat.matches("2"));
+
+ assert!(Pattern::new("[-]").matches("-"));
+ assert!(!Pattern::new("[!-]").matches("-"));
+ }
+
#[test]
fn test_unclosed_bracket() {
// unclosed `[` should be treated literally
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// FIXME #3921. This is unsafe because linenoise uses global mutable
-// state without mutexes.
-
use std::c_str::ToCStr;
use std::libc::{c_char, c_int};
-use std::local_data;
-use std::str;
+use std::{local_data, str, rt};
+use std::unstable::finally::Finally;
#[cfg(stage0)]
pub mod rustrt {
fn linenoiseHistoryLoad(file: *c_char) -> c_int;
fn linenoiseSetCompletionCallback(callback: *u8);
fn linenoiseAddCompletion(completions: *(), line: *c_char);
+
+ fn rust_take_linenoise_lock();
+ fn rust_drop_linenoise_lock();
}
}
externfn!(fn linenoiseHistoryLoad(file: *c_char) -> c_int)
externfn!(fn linenoiseSetCompletionCallback(callback: extern "C" fn(*i8, *())))
externfn!(fn linenoiseAddCompletion(completions: *(), line: *c_char))
+
+ externfn!(fn rust_take_linenoise_lock())
+ externfn!(fn rust_drop_linenoise_lock())
+}
+
+macro_rules! locked {
+ ($expr:expr) => {
+ unsafe {
+ // FIXME #9105: can't use a static mutex in pure Rust yet.
+ rustrt::rust_take_linenoise_lock();
+ let x = $expr;
+ rustrt::rust_drop_linenoise_lock();
+ x
+ }
+ }
}
/// Add a line to history
-pub unsafe fn add_history(line: &str) -> bool {
+pub fn add_history(line: &str) -> bool {
do line.with_c_str |buf| {
- rustrt::linenoiseHistoryAdd(buf) == 1 as c_int
+ (locked!(rustrt::linenoiseHistoryAdd(buf))) == 1 as c_int
}
}
/// Set the maximum amount of lines stored
-pub unsafe fn set_history_max_len(len: int) -> bool {
- rustrt::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int
+pub fn set_history_max_len(len: int) -> bool {
+ (locked!(rustrt::linenoiseHistorySetMaxLen(len as c_int))) == 1 as c_int
}
/// Save line history to a file
-pub unsafe fn save_history(file: &str) -> bool {
+pub fn save_history(file: &str) -> bool {
do file.with_c_str |buf| {
- rustrt::linenoiseHistorySave(buf) == 1 as c_int
+ // 0 on success, -1 on failure
+ (locked!(rustrt::linenoiseHistorySave(buf))) == 0 as c_int
}
}
/// Load line history from a file
-pub unsafe fn load_history(file: &str) -> bool {
+pub fn load_history(file: &str) -> bool {
do file.with_c_str |buf| {
- rustrt::linenoiseHistoryLoad(buf) == 1 as c_int
+ // 0 on success, -1 on failure
+ (locked!(rustrt::linenoiseHistoryLoad(buf))) == 0 as c_int
}
}
/// Print out a prompt and then wait for input and return it
-pub unsafe fn read(prompt: &str) -> Option<~str> {
+pub fn read(prompt: &str) -> Option<~str> {
do prompt.with_c_str |buf| {
- let line = rustrt::linenoise(buf);
+ let line = locked!(rustrt::linenoise(buf));
if line.is_null() { None }
- else { Some(str::raw::from_c_str(line)) }
+ else {
+ unsafe {
+ do (|| {
+ Some(str::raw::from_c_str(line))
+ }).finally {
+ // linenoise's return value is from strdup, so we
+ // better not leak it.
+ rt::global_heap::exchange_free(line);
+ }
+ }
+ }
}
}
pub type CompletionCb = @fn(~str, @fn(~str));
-static complete_key: local_data::Key<@CompletionCb> = &local_data::Key;
-
-/// Bind to the main completion callback
-pub unsafe fn complete(cb: CompletionCb) {
- local_data::set(complete_key, @cb);
-
- extern fn callback(line: *c_char, completions: *()) {
- do local_data::get(complete_key) |cb| {
- let cb = **cb.unwrap();
-
- unsafe {
- do cb(str::raw::from_c_str(line)) |suggestion| {
- do suggestion.with_c_str |buf| {
- rustrt::linenoiseAddCompletion(completions, buf);
+static complete_key: local_data::Key<CompletionCb> = &local_data::Key;
+
+/// Bind to the main completion callback in the current task.
+///
+/// The completion callback should not call any `extra::rl` functions
+/// other than the closure that it receives as its second
+/// argument. Calling such a function will deadlock on the mutex used
+/// to ensure that the calls are thread-safe.
+pub fn complete(cb: CompletionCb) {
+ local_data::set(complete_key, cb);
+
+ extern fn callback(c_line: *c_char, completions: *()) {
+ do local_data::get(complete_key) |opt_cb| {
+ // only fetch completions if a completion handler has been
+ // registered in the current task.
+ match opt_cb {
+ None => {},
+ Some(cb) => {
+ let line = unsafe { str::raw::from_c_str(c_line) };
+ do (*cb)(line) |suggestion| {
+ do suggestion.with_c_str |buf| {
+ // This isn't locked, because `callback` gets
+ // called inside `rustrt::linenoise`, which
+ // *is* already inside the mutex, so
+ // re-locking would be a deadlock.
+ unsafe {
+ rustrt::linenoiseAddCompletion(completions, buf);
+ }
+ }
}
}
}
}
}
- rustrt::linenoiseSetCompletionCallback(callback);
+ locked!(rustrt::linenoiseSetCompletionCallback(callback));
}
usage_full: UsageSource<'self>,
}
-static NUM_OF_COMMANDS: uint = 7;
-
-// FIXME(#7617): should just be &'static [Command<'static>]
-// but mac os doesn't seem to like that and tries to loop
-// past the end of COMMANDS in usage thus passing garbage
-// to str::repeat and eventually malloc and crashing.
-static COMMANDS: [Command<'static>, .. NUM_OF_COMMANDS] = [
- Command{
+static COMMANDS: &'static [Command<'static>] = &'static [
+ Command {
cmd: "build",
action: CallMain("rustc", rustc::main_args),
usage_line: "compile rust source files",
usage_full: UsgCall(rustc_help),
},
- Command{
+ Command {
cmd: "run",
action: Call(cmd_run),
usage_line: "build an executable, and run it",
\n\nUsage:\trust run <filename> [<arguments>...]"
)
},
- Command{
+ Command {
cmd: "test",
action: Call(cmd_test),
usage_line: "build a test executable, and run it",
./<filestem>test~\"\n\nUsage:\trust test <filename>"
)
},
- Command{
+ Command {
cmd: "doc",
action: CallMain("rustdoc", rustdoc::main_args),
usage_line: "generate documentation from doc comments",
usage_full: UsgCall(rustdoc::config::usage),
},
- Command{
+ Command {
cmd: "pkg",
action: CallMain("rustpkg", rustpkg::main_args),
usage_line: "download, build, install rust packages",
usage_full: UsgCall(rustpkg::usage::general),
},
- Command{
+ Command {
cmd: "sketch",
action: CallMain("rusti", rusti::main_args),
usage_line: "run a rust interpreter",
usage_full: UsgStr("\nUsage:\trusti"),
},
- Command{
+ Command {
cmd: "help",
action: Call(cmd_help),
usage_line: "show detailed usage of a command",
do reader::tagged_docs(item, tag_item_field) |an_item| {
let f = item_family(an_item);
if f == PublicField || f == PrivateField || f == InheritedField {
+ // FIXME #6993: name should be of type Name, not Ident
let name = item_name(intr, an_item);
let did = item_def_id(an_item, cdata);
result.push(ty::field_ty {
- ident: name,
+ name: name.name,
id: did, vis:
struct_field_family_to_visibility(f),
});
do reader::tagged_docs(item, tag_item_unnamed_field) |an_item| {
let did = item_def_id(an_item, cdata);
result.push(ty::field_ty {
- ident: special_idents::unnamed_field,
+ name: special_idents::unnamed_field.name,
id: did,
vis: ast::inherited,
});
}
let args = class_fields.iter().map(|class_field| {
match flds.iter().find(|f|
- f.ident == class_field.ident) {
+ f.ident.name == class_field.name) {
Some(f) => f.pat,
_ => wild()
}
/// an enum to determine which variant is in use.
pub fn field_mutbl(tcx: ty::ctxt,
base_ty: ty::t,
+ // FIXME #6993: change type to Name
f_name: ast::Ident,
node_id: ast::NodeId)
-> Option<ast::Mutability> {
ty::ty_struct(did, _) => {
let r = ty::lookup_struct_fields(tcx, did);
for fld in r.iter() {
- if fld.ident == f_name {
+ if fld.name == f_name.name {
return Some(ast::MutImmutable);
}
}
ast::DefVariant(_, variant_id, _) => {
let r = ty::lookup_struct_fields(tcx, variant_id);
for fld in r.iter() {
- if fld.ident == f_name {
+ if fld.name == f_name.name {
return Some(ast::MutImmutable);
}
}
}
// Checks that a private field is in scope.
+ // FIXME #6993: change type (and name) from Ident to Name
fn check_field(&mut self, span: Span, id: ast::DefId, ident: ast::Ident) {
let fields = ty::lookup_struct_fields(self.tcx, id);
for field in fields.iter() {
- if field.ident.name != ident.name { loop; }
+ if field.name != ident.name { loop; }
if field.vis == private {
self.tcx.sess.span_err(span, fmt!("field `%s` is private",
token::ident_to_str(&ident)));
let r = ty::lookup_struct_fields(tcx, struct_id);
for field in r.iter() {
match field_pats.iter().find(|p| p.ident.name
- == field.ident.name) {
+ == field.name) {
None => reordered_patterns.push(dummy),
Some(fp) => reordered_patterns.push(fp.pat)
}
}
pub struct field_ty {
- ident: Ident,
+ name: Name,
id: DefId,
vis: ast::visibility,
}
ty_struct(def_id, ref substs) => {
let fields = struct_fields(cx, def_id, substs);
fields.len() == 1 &&
- fields[0].ident == token::special_idents::unnamed_field &&
+ fields[0].ident.name == token::special_idents::unnamed_field.name &&
type_is_immediate(cx, fields[0].mt.ty)
}
_ => false
match field.node.kind {
named_field(ident, visibility) => {
field_ty {
- ident: ident,
+ name: ident.name,
id: ast_util::local_def(field.node.id),
vis: visibility,
}
}
unnamed_field => {
field_ty {
- ident:
- syntax::parse::token::special_idents::unnamed_field,
+ name:
+ syntax::parse::token::special_idents::unnamed_field.name,
id: ast_util::local_def(field.node.id),
vis: ast::public,
}
-> ~[field] {
do lookup_struct_fields(cx, did).map |f| {
field {
- ident: f.ident,
+ // FIXME #6993: change type of field to Name and get rid of new()
+ ident: ast::Ident::new(f.name),
mt: mt {
ty: lookup_field_type(cx, did, f.id, substs),
mutbl: MutImmutable
use std::hashmap::{HashMap, HashSet};
use syntax::ast;
use syntax::ast_util;
+use syntax::parse::token;
use syntax::codemap::Span;
use syntax::print::pprust;
// Index the class fields.
let mut field_map = HashMap::new();
for (i, class_field) in class_fields.iter().enumerate() {
- field_map.insert(class_field.ident.name, i);
+ field_map.insert(class_field.name, i);
}
// Typecheck each field.
}
tcx.sess.span_err(span,
fmt!("pattern does not mention field `%s`",
- tcx.sess.str_of(field.ident)));
+ token::interner_get(field.name)));
}
}
}
fieldname: ast::Name,
substs: &ty::substs) -> Option<ty::t> {
- let o_field = items.iter().find(|f| f.ident.name == fieldname);
+ let o_field = items.iter().find(|f| f.name == fieldname);
do o_field.map() |f| {
ty::lookup_field_type(tcx, class_id, f.id, substs)
}
let mut class_field_map = HashMap::new();
let mut fields_found = 0;
for field in field_types.iter() {
- class_field_map.insert(field.ident.name, (field.id, false));
+ class_field_map.insert(field.name, (field.id, false));
}
let mut error_happened = false;
if fields_found < field_types.len() {
let mut missing_fields = ~[];
for class_field in field_types.iter() {
- let name = class_field.ident.name;
+ let name = class_field.name;
let (_, seen) = *class_field_map.get(&name);
if !seen {
missing_fields.push(
/// None if no input was read (e.g. EOF was reached).
fn get_line(use_rl: bool, prompt: &str) -> Option<~str> {
if use_rl {
- let result = unsafe { rl::read(prompt) };
+ let result = rl::read(prompt);
match result {
None => None,
Some(line) => {
- unsafe { rl::add_history(line) };
+ rl::add_history(line);
Some(line)
}
}
println("unstable. If you encounter problems, please use the");
println("compiler instead. Type :help for help.");
- unsafe {
- do rl::complete |line, suggest| {
- if line.starts_with(":") {
- suggest(~":clear");
- suggest(~":exit");
- suggest(~":help");
- suggest(~":load");
- }
+ do rl::complete |line, suggest| {
+ if line.starts_with(":") {
+ suggest(~":clear");
+ suggest(~":exit");
+ suggest(~":help");
+ suggest(~":load");
}
}
}
format!("Hello") // => ~"Hello"
format!("Hello, {:s}!", "world") // => ~"Hello, world!"
format!("The number is {:d}", 1) // => ~"The number is 1"
-format!("{}", ~[3, 4]) // => ~"~[3, 4]"
+format!("{:?}", ~[3, 4]) // => ~"~[3, 4]"
format!("{value}", value=4) // => ~"4"
format!("{} {}", 1, 2) // => ~"1 2"
~~~
priv value: &'self util::Void,
}
+impl<'self> Arguments<'self> {
+ /// When using the format_args!() macro, this function is used to generate the
+ /// Arguments structure. The compiler inserts an `unsafe` block to call this,
+ /// which is valid because the compiler performs all necessary validation to
+ /// ensure that the resulting call to format/write would be safe.
+ #[doc(hidden)] #[inline]
+ pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
+ args: &'a [Argument<'a>]) -> Arguments<'a> {
+ Arguments{ fmt: cast::transmute(fmt), args: args }
+ }
+}
+
+/// This structure represents a safely precompiled version of a format string
+/// and its arguments. This cannot be generated at runtime because it cannot
+/// safely be done so, so no constructors are given and the fields are private
+/// to prevent modification.
+///
+/// The `format_args!` macro will safely create an instance of this structure
+/// and pass it to a user-supplied function. The macro validates the format
+/// string at compile-time so usage of the `write` and `format` functions can
+/// be safely performed.
+pub struct Arguments<'self> {
+ priv fmt: &'self [rt::Piece<'self>],
+ priv args: &'self [Argument<'self>],
+}
+
/// When a format is not otherwise specified, types are formatted by ascribing
/// to this trait. There is not an explicit way of selecting this trait to be
/// used for formatting, it is only if no other format is specified.
/// and a list of arguments. The arguments will be formatted according to the
/// specified format string into the output stream provided.
///
+/// # Arguments
+///
+/// * output - the buffer to write output to
+/// * args - the precompiled arguments generated by `format_args!`
+///
+/// # Example
+///
+/// ~~~{.rust}
+/// use std::fmt;
+/// let w: &mut io::Writer = ...;
+/// format_args!(|args| { fmt::write(w, args) }, "Hello, {}!", "world");
+/// ~~~
+pub fn write(output: &mut io::Writer, args: &Arguments) {
+ unsafe { write_unsafe(output, args.fmt, args.args) }
+}
+
+/// The `write_unsafe` function takes an output stream, a precompiled format
+/// string, and a list of arguments. The arguments will be formatted according
+/// to the specified format string into the output stream provided.
+///
/// See the documentation for `format` for why this function is unsafe and care
/// should be taken if calling it manually.
///
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
-pub unsafe fn write(output: &mut io::Writer,
- fmt: &[rt::Piece], args: &[Argument]) {
+pub unsafe fn write_unsafe(output: &mut io::Writer,
+ fmt: &[rt::Piece],
+ args: &[Argument]) {
let mut formatter = Formatter {
flags: 0,
width: None,
/// The format function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string.
///
+/// # Arguments
+///
+/// * args - a structure of arguments generated via the `format_args!` macro.
+/// Because this structure can only be safely generated at
+/// compile-time, this function is safe.
+///
+/// # Example
+///
+/// ~~~{.rust}
+/// use std::fmt;
+/// let s = format_args!(fmt::format, "Hello, {}!", "world");
+/// assert_eq!(s, "Hello, world!");
+/// ~~~
+pub fn format(args: &Arguments) -> ~str {
+ unsafe { format_unsafe(args.fmt, args.args) }
+}
+
+/// The unsafe version of the formatting function.
+///
/// This is currently an unsafe function because the types of all arguments
/// aren't verified by immediate callers of this function. This currently does
/// not validate that the correct types of arguments are specified for each
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
-pub unsafe fn format(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
+pub unsafe fn format_unsafe(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
let mut output = MemWriter::new();
- write(&mut output as &mut io::Writer, fmt, args);
+ write_unsafe(&mut output as &mut io::Writer, fmt, args);
return str::from_utf8_owned(output.inner());
}
/// This is a function which calls are emitted to by the compiler itself to
/// create the Argument structures that are passed into the `format` function.
-#[doc(hidden)]
+#[doc(hidden)] #[inline]
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
t: &'a T) -> Argument<'a> {
unsafe {
/// When the compiler determines that the type of an argument *must* be a string
/// (such as for select), then it invokes this method.
-#[doc(hidden)]
+#[doc(hidden)] #[inline]
pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
argument(String::fmt, s)
}
/// When the compiler determines that the type of an argument *must* be a uint
/// (such as for plural), then it invokes this method.
-#[doc(hidden)]
+#[doc(hidden)] #[inline]
pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
argument(Unsigned::fmt, s)
}
}
}
}
-
impl<T> Pointer for *mut T {
- fn fmt(t: &*mut T, f: &mut Formatter) {
- f.flags |= 1 << (parse::FlagAlternate as uint);
- do ::uint::to_str_bytes(*t as uint, 16) |buf| {
- f.pad_integral(buf, "0x", true);
- }
- }
+ fn fmt(t: &*mut T, f: &mut Formatter) { Pointer::fmt(&(*t as *T), f) }
}
// Implementation of Default for various core types
impl<T> Default for *T {
fn fmt(me: &*T, f: &mut Formatter) { Pointer::fmt(me, f) }
}
-
impl<T> Default for *mut T {
fn fmt(me: &*mut T, f: &mut Formatter) { Pointer::fmt(me, f) }
}
/// characters.
fn word(&mut self) -> &'self str {
let start = match self.cur.clone().next() {
- Some((pos, c)) if char::is_alphabetic(c) => {
+ Some((pos, c)) if char::is_XID_start(c) => {
self.cur.next();
pos
}
let mut end;
loop {
match self.cur.clone().next() {
- Some((_, c)) if char::is_alphanumeric(c) => {
+ Some((_, c)) if char::is_XID_continue(c) => {
self.cur.next();
}
Some((pos, _)) => { end = pos; break }
}
}
- fn flush(&mut self) { fail!() }
+ fn flush(&mut self) { /* no-op */ }
}
pub struct TcpListener(~RtioTcpListenerObject);
use libc;
#[test]
- #[ignore(cfg(windows))] // FIXME #8818
+ // #[ignore(cfg(windows))] // FIXME #8818
+ #[ignore] // FIXME #9137 this library isn't thread-safe
fn test_loading_cosine() {
// The math library does not need to be loaded since it is already
// statically linked in
#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
+ #[ignore] // FIXME #9137 this library isn't thread-safe
fn test_errors_do_not_crash() {
// Open /dev/null as a library to get an error, and make sure
// that only causes an error, and not a crash.
// if it should be non-hygienic (most things are), just compare the
// 'name' fields of the idents. Or, even better, replace the idents
// with Name's.
- fail!(fmt!("not allowed to compare these idents: %?, %?", self, other));
+ fail!(fmt!("not allowed to compare these idents: %?, %?. Probably \
+ related to issue #6993", self, other));
}
}
fn ne(&self, other: &Ident) -> bool {
builtin_normal_tt_no_ctxt(ext::ifmt::expand_write));
syntax_expanders.insert(intern(&"writeln"),
builtin_normal_tt_no_ctxt(ext::ifmt::expand_writeln));
+ syntax_expanders.insert(intern(&"format_args"),
+ builtin_normal_tt_no_ctxt(ext::ifmt::expand_format_args));
syntax_expanders.insert(
intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
let p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
self.ecx.cfg(),
tts.to_owned());
- // If we want a leading expression (for ifmtf), parse it here
+ // If we want a leading expression, parse it here
let extra = if leading_expr {
let e = Some(p.parse_expr());
if !p.eat(&token::COMMA) {
~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
self.ecx.ident_of("parse"), self.ecx.ident_of(s)]
};
- let none = || {
- let p = self.ecx.path(sp, ~[self.ecx.ident_of("None")]);
- self.ecx.expr_path(p)
- };
+ let none = self.ecx.path_global(sp, ~[
+ self.ecx.ident_of("std"),
+ self.ecx.ident_of("option"),
+ self.ecx.ident_of("None")]);
+ let none = self.ecx.expr_path(none);
let some = |e: @ast::Expr| {
- self.ecx.expr_call_ident(sp, self.ecx.ident_of("Some"), ~[e])
+ let p = self.ecx.path_global(sp, ~[
+ self.ecx.ident_of("std"),
+ self.ecx.ident_of("option"),
+ self.ecx.ident_of("Some")]);
+ let p = self.ecx.expr_path(p);
+ self.ecx.expr_call(sp, p, ~[e])
};
let trans_count = |c: parse::Count| {
match c {
parse::Plural(offset, ref arms, ref default) => {
let offset = match offset {
Some(i) => { some(self.ecx.expr_uint(sp, i)) }
- None => { none() }
+ None => { none.clone() }
};
let arms = arms.iter().map(|arm| {
let p = self.ecx.path_global(sp, rtpath("PluralArm"));
// Translate the method (if any)
let method = match arg.method {
- None => { none() }
+ None => { none.clone() }
Some(ref m) => {
let m = trans_method(*m);
some(self.ecx.expr_addr_of(sp, m))
/// Actually builds the expression which the ifmt! block will be expanded
/// to
- fn to_expr(&self, extra: Option<@ast::Expr>, f: &str) -> @ast::Expr {
+ fn to_expr(&self, extra: Option<@ast::Expr>, f: Option<&str>) -> @ast::Expr {
let mut lets = ~[];
let mut locals = ~[];
let mut names = vec::from_fn(self.name_positions.len(), |_| None);
// Next, build up the static array which will become our precompiled
// format "string"
let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
+ let piece_ty = self.ecx.ty_path(self.ecx.path_all(
+ self.fmtsp,
+ true, ~[
+ self.ecx.ident_of("std"),
+ self.ecx.ident_of("fmt"),
+ self.ecx.ident_of("rt"),
+ self.ecx.ident_of("Piece"),
+ ],
+ Some(self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static"))),
+ ~[]
+ ), None);
let ty = ast::ty_fixed_length_vec(
- self.ecx.ty_mt(
- self.ecx.ty_path(self.ecx.path_all(
- self.fmtsp,
- true, ~[
- self.ecx.ident_of("std"),
- self.ecx.ident_of("fmt"),
- self.ecx.ident_of("rt"),
- self.ecx.ident_of("Piece"),
- ],
- Some(self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static"))),
- ~[]
- ), None),
- ast::MutImmutable
- ),
+ self.ecx.ty_mt(piece_ty.clone(), ast::MutImmutable),
self.ecx.expr_uint(self.fmtsp, self.pieces.len())
);
let ty = self.ecx.ty(self.fmtsp, ty);
let name = self.ecx.ident_of(fmt!("__arg%u", i));
let e = self.ecx.expr_addr_of(e.span, e);
lets.push(self.ecx.stmt_let(e.span, false, name, e));
- locals.push(self.format_arg(e.span, Left(i), name));
+ locals.push(self.format_arg(e.span, Left(i),
+ self.ecx.expr_ident(e.span, name)));
}
for (&name, &e) in self.names.iter() {
if !self.name_types.contains_key(&name) { loop }
let e = self.ecx.expr_addr_of(e.span, e);
lets.push(self.ecx.stmt_let(e.span, false, lname, e));
names[*self.name_positions.get(&name)] =
- Some(self.format_arg(e.span, Right(name), lname));
+ Some(self.format_arg(e.span, Right(name),
+ self.ecx.expr_ident(e.span, lname)));
}
let args = names.move_iter().map(|a| a.unwrap());
let mut args = locals.move_iter().chain(args);
- let mut fmt_args = match extra {
- Some(e) => ~[e], None => ~[]
- };
- fmt_args.push(self.ecx.expr_ident(self.fmtsp, static_name));
- fmt_args.push(self.ecx.expr_vec(self.fmtsp, args.collect()));
+ let result = match f {
+ // Invocation of write!()/format!(), call the function and we're
+ // done.
+ Some(f) => {
+ let mut fmt_args = match extra {
+ Some(e) => ~[e], None => ~[]
+ };
+ fmt_args.push(self.ecx.expr_ident(self.fmtsp, static_name));
+ fmt_args.push(self.ecx.expr_vec_slice(self.fmtsp,
+ args.collect()));
- // Next, build up the actual call to the {s,f}printf function.
- let result = self.ecx.expr_call_global(self.fmtsp, ~[
- self.ecx.ident_of("std"),
- self.ecx.ident_of("fmt"),
- self.ecx.ident_of(f),
- ], fmt_args);
-
- // sprintf is unsafe, but we just went through a lot of work to
- // validate that our call is save, so inject the unsafe block for the
- // user.
- let result = self.ecx.expr_block(ast::Block {
- view_items: ~[],
- stmts: ~[],
- expr: Some(result),
- id: ast::DUMMY_NODE_ID,
- rules: ast::UnsafeBlock(ast::CompilerGenerated),
- span: self.fmtsp,
- });
-
- self.ecx.expr_block(self.ecx.block(self.fmtsp, lets, Some(result)))
+ let result = self.ecx.expr_call_global(self.fmtsp, ~[
+ self.ecx.ident_of("std"),
+ self.ecx.ident_of("fmt"),
+ self.ecx.ident_of(f),
+ ], fmt_args);
+
+ // sprintf is unsafe, but we just went through a lot of work to
+ // validate that our call is save, so inject the unsafe block
+ // for the user.
+ self.ecx.expr_block(ast::Block {
+ view_items: ~[],
+ stmts: ~[],
+ expr: Some(result),
+ id: ast::DUMMY_NODE_ID,
+ rules: ast::UnsafeBlock(ast::CompilerGenerated),
+ span: self.fmtsp,
+ })
+ }
+
+ // Invocation of format_args!()
+ None => {
+ let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
+ let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
+ let result = self.ecx.expr_call_global(self.fmtsp, ~[
+ self.ecx.ident_of("std"),
+ self.ecx.ident_of("fmt"),
+ self.ecx.ident_of("Arguments"),
+ self.ecx.ident_of("new"),
+ ], ~[fmt, args]);
+
+ // We did all the work of making sure that the arguments
+ // structure is safe, so we can safely have an unsafe block.
+ let result = self.ecx.expr_block(ast::Block {
+ view_items: ~[],
+ stmts: ~[],
+ expr: Some(result),
+ id: ast::DUMMY_NODE_ID,
+ rules: ast::UnsafeBlock(ast::CompilerGenerated),
+ span: self.fmtsp,
+ });
+ let extra = extra.unwrap();
+ let resname = self.ecx.ident_of("__args");
+ lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
+ let res = self.ecx.expr_ident(self.fmtsp, resname);
+ self.ecx.expr_call(extra.span, extra, ~[
+ self.ecx.expr_addr_of(extra.span, res)])
+ }
+ };
+ self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
+ Some(result)))
}
- fn format_arg(&self, sp: Span, arg: Either<uint, @str>,
- ident: ast::Ident) -> @ast::Expr {
- let ty = match arg {
+ fn format_arg(&self, sp: Span, argno: Either<uint, @str>,
+ arg: @ast::Expr) -> @ast::Expr {
+ let ty = match argno {
Left(i) => self.arg_types[i].unwrap(),
Right(s) => *self.name_types.get(&s)
};
- let argptr = self.ecx.expr_ident(sp, ident);
let fmt_trait = match ty {
Unknown => "Default",
Known(tyname) => {
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argumentstr"),
- ], ~[argptr])
+ ], ~[arg])
}
Unsigned => {
return self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argumentuint"),
- ], ~[argptr])
+ ], ~[arg])
}
};
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argument"),
- ], ~[self.ecx.expr_path(format_fn), argptr])
+ ], ~[self.ecx.expr_path(format_fn), arg])
}
}
pub fn expand_format(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
- expand_ifmt(ecx, sp, tts, false, false, "format")
+ expand_ifmt(ecx, sp, tts, false, false, Some("format_unsafe"))
}
pub fn expand_write(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
- expand_ifmt(ecx, sp, tts, true, false, "write")
+ expand_ifmt(ecx, sp, tts, true, false, Some("write_unsafe"))
}
pub fn expand_writeln(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
- expand_ifmt(ecx, sp, tts, true, true, "write")
+ expand_ifmt(ecx, sp, tts, true, true, Some("write_unsafe"))
+}
+
+pub fn expand_format_args(ecx: @ExtCtxt, sp: Span,
+ tts: &[ast::token_tree]) -> base::MacResult {
+ expand_ifmt(ecx, sp, tts, true, false, None)
}
fn expand_ifmt(ecx: @ExtCtxt, sp: Span, tts: &[ast::token_tree],
leading_arg: bool, append_newline: bool,
- function: &str) -> base::MacResult {
+ function: Option<&str>) -> base::MacResult {
let mut cx = Context {
ecx: ecx,
args: ~[],
env_lock.unlock();
}
+static lock_and_signal linenoise_lock;
+
+extern "C" CDECL void
+rust_take_linenoise_lock() {
+ linenoise_lock.lock();
+}
+
+extern "C" CDECL void
+rust_drop_linenoise_lock() {
+ linenoise_lock.unlock();
+}
+
extern "C" CDECL unsigned int
rust_valgrind_stack_register(void *start, void *end) {
return VALGRIND_STACK_REGISTER(start, end);
rust_drop_global_args_lock
rust_take_change_dir_lock
rust_drop_change_dir_lock
+rust_take_linenoise_lock
+rust_drop_linenoise_lock
rust_get_test_int
rust_get_task
rust_uv_get_loop_from_getaddrinfo_req
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ format_args!("test"); //~ ERROR: expected token
+ format_args!("", || {}); //~ ERROR: must be a string literal
+}
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ format_args!("{}", ""); //~ ERROR: expected function
+}
#[deny(warnings)];
use std::fmt;
+use std::rt::io::Decorator;
+use std::rt::io::mem::MemWriter;
+use std::rt::io;
+use std::rt::io::Writer;
+use std::str;
struct A;
struct B;
t!(format!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3), "2 1 3 0");
t!(format!("{} {0:s}", "a"), "a a");
t!(format!("{} {0}", "a"), "a a");
+ t!(format!("{foo_bar}", foo_bar=1), "1");
// Methods should probably work
t!(format!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 0u), "c0");
let a: int = ::std::cast::transmute(3u);
format!("{}", a);
}
+
+ test_format_args();
}
// Basic test to make sure that we can invoke the `write!` macro with an
// io::Writer instance.
fn test_write() {
- use std::rt::io::Decorator;
- use std::rt::io::mem::MemWriter;
- use std::rt::io;
- use std::str;
-
let mut buf = MemWriter::new();
write!(&mut buf as &mut io::Writer, "{}", 3);
{
println!("this is a {}", "test");
println!("{foo}", foo="bar");
}
+
+// Just make sure that the macros are defined, there's not really a lot that we
+// can do with them just yet (to test the output)
+fn test_format_args() {
+ let mut buf = MemWriter::new();
+ {
+ let w = &mut buf as &mut io::Writer;
+ format_args!(|args| { fmt::write(w, args) }, "{}", 1);
+ format_args!(|args| { fmt::write(w, args) }, "test");
+ format_args!(|args| { fmt::write(w, args) }, "{test}", test=3);
+ }
+ let s = str::from_utf8_owned(buf.inner());
+ t!(s, "1test3");
+
+ let s = format_args!(fmt::format, "hello {}", "world");
+ t!(s, "hello world");
+}
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! silly_macro(
+ () => (
+ pub mod Qux {
+ pub struct Foo { x : u8 }
+ pub fn bar(_foo : Foo) {}
+ }
+ );
+)
+
+silly_macro!()
+
+fn main() {}
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-fast no compile flags for check-fast
+
+// we want this to be compiled to avoid bitrot, but the actual test
+//has to be conducted by a human, i.e. someone (you?) compiling this
+//file with a plain rustc invocation and running it and checking it
+//works.
+
+// compile-flags: --cfg robot_mode
+
+extern mod extra;
+use extra::rl;
+
+static HISTORY_FILE: &'static str = "rl-human-test-history.txt";
+
+fn main() {
+ // don't run this in robot mode, but still typecheck it.
+ if !cfg!(robot_mode) {
+ println("~~ Welcome to the rl test \"suite\". ~~");
+ println!("Operations:
+ - restrict the history to 2 lines,
+ - set the tab-completion to suggest three copies of each of the last 3 letters (or 'empty'),
+ - add 'one' and 'two' to the history,
+ - save it to `{0}`,
+ - add 'three',
+ - prompt & save input (check the history & completion work and contains only 'two', 'three'),
+ - load from `{0}`
+ - prompt & save input (history should be 'one', 'two' again),
+ - prompt once more.
+
+The bool return values of each step are printed.",
+ HISTORY_FILE);
+
+ println!("restricting history length: {}", rl::set_history_max_len(3));
+
+ do rl::complete |line, suggest| {
+ if line.is_empty() {
+ suggest(~"empty")
+ } else {
+ for c in line.rev_iter().take(3) {
+ suggest(format!("{0}{1}{1}{1}", line, c))
+ }
+ }
+ }
+
+ println!("adding 'one': {}", rl::add_history("one"));
+ println!("adding 'two': {}", rl::add_history("two"));
+
+ println!("saving history: {}", rl::save_history(HISTORY_FILE));
+
+ println!("adding 'three': {}", rl::add_history("three"));
+
+ match rl::read("> ") {
+ Some(s) => println!("saving input: {}", rl::add_history(s)),
+ None => return
+ }
+ println!("loading history: {}", rl::load_history(HISTORY_FILE));
+
+ match rl::read("> ") {
+ Some(s) => println!("saving input: {}", rl::add_history(s)),
+ None => return
+ }
+
+ rl::read("> ");
+ }
+}