<commit_before>extern crate rustc_serialize;
extern crate docopt;

use std::error::Error;
use std::cmp::max;
use std::fmt::Result as FmtResult;
use std::fmt::{Display, Formatter};
use std::fs::File;
use std::io::Error as IOError;
use std::io::{BufReader, stderr, stdin};
use std::io::{Write, BufRead, Read};
use std::process::exit;
use std::str::{Utf8Error, from_utf8};
use docopt::Docopt;

const LF: char = '\n';
const NULL: char = '\0';
const VERSION: &'static str = "0.0.1";
const USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

// Macro that acts like print!, but w/ stderr
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

// Macro that acts like println!, but w/ stderr
macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

// Enum that captures all of the expected error variants we are likely
// to return.
#[derive(Debug)]
enum WCError {
    IO(IOError),
    Utf8(Utf8Error),
}

// Implement the Display trait so that we can print our errors to the
// user. Also a pre-requisite for implementing the Error trait.
impl Display for WCError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            WCError::IO(ref e) => e.fmt(f),
            WCError::Utf8(ref e) => e.fmt(f),
        }
    }
}

// Implement the Error trait so that callers can treat a WCError as
// they would any other error.
impl std::error::Error for WCError {
    fn description(&self) -> &str {
        match *self {
            WCError::IO(ref e) => e.description(),
            WCError::Utf8(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            WCError::IO(ref e) => e.cause(),
            WCError::Utf8(ref e) => e.cause()
        }
    }
}

// Make it possible to wrap an IOError as a variant of our WCError enum.
impl From<IOError> for WCError {
    fn from(e: IOError) -> WCError {
        WCError::IO(e)
    }
}

// Make it possible to wrap a Utf8Error as a variant of our WCError enum.
impl From<Utf8Error> for WCError {
    fn from(e: Utf8Error) -> WCError {
        WCError::Utf8(e)
    }
}

// Define the struct which will wrap all of our counts. Derive the
// Debug trait so we can print it out using the "{:?}" pattern for
// debugging purposes.
#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

// Implement a convenience constructor that will return a zeroed
// FileInfo struct. I had considered implementing the Zero and Add
// traits and using sum to acquire our "total" instead of a fold, but
// the behavior of max_line_length just doesn't look like add to me,
// and it may be more confusing that it's worth.
impl FileInfo {
    fn new() -> FileInfo {
        FileInfo{bytes: 0,
                 chars: 0,
                 lines: 0,
                 words: 0,
                 max_line_length: 0,
        }
    }
}

// Wrapper around Result that specializes the Error variant to be one
// of our WCErrors.
type WCResult<T> = Result<T, WCError>;

// Print the result of attempting to process a file.
fn display(args: &Args, filename: &str, result: &WCResult<FileInfo>,
           field_size: usize) -> bool {
    match *result {
        Ok(ref r) => {
            if args.flag_lines {
                print!("{:1$} ", r.lines, field_size);
            }
            if args.flag_words {
                print!("{:1$} ", r.words, field_size);
            }
            if args.flag_bytes {
                print!("{:1$} ", r.bytes, field_size);
            }
            if args.flag_chars {
                print!("{:1$} ", r.chars, field_size);
            }
            if args.flag_max_line_length {
                print!("{:1$} ", r.max_line_length, field_size);
            }
            println!("{}", filename);
            true
        },
        Err(ref e) => {
            println_stderr!("wc: {}: {}", filename, e);
            false
        },
    }
}

// Return the named file, but opened, and in a result that's usable as
// a buffered reader. In the case of a filename "-", return an
// appropriately wrapped Stdin. Doing this allows us to treat regular
// files and stdin equivalently.
fn open_file(filename: &str) -> WCResult<Box<BufRead>> {
    match filename {
        "-" => Ok(Box::new(BufReader::new(stdin()))),
        _ => Ok(Box::new(BufReader::new(try!(File::open(filename))))),
    }
}

fn process_file(filename: &str, args: &Args) -> WCResult<FileInfo> {
    let mut file = try!(open_file(filename));
    let mut info = FileInfo::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(file.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // If this if statement wasn't here, we would still need to
        // create a scope because we're going to borrow lbuf and the
        // borrow must end before we can clear it.
        if args.flag_lines || args.flag_words || args.flag_chars ||
            args.flag_max_line_length {
                // TODO: Handle files which are not utf8-encoded. Right
                // now we get an error here.
                let line = try!(from_utf8(&lbuf));

                let size = if args.flag_chars || args.flag_max_line_length {
                    line.chars().count()
                } else { 0 };

                let last = if args.flag_lines || args.flag_max_line_length {
                    line.chars().last().unwrap_or(NULL)
                } else { NULL };

                if last == LF {
                    info.lines += 1;
                }

                if args.flag_max_line_length {
                    info.max_line_length = if last == LF {
                        max(info.max_line_length, size - 1)
                    } else {
                        max(info.max_line_length, size)
                    };
                }

                info.chars += size;

                if args.flag_words {
                    let mut words: Vec<&str> = line
                        .split(|c: char| c.is_whitespace())
                        .collect();
                    words.retain(|s: &&str| s.len() > 0);
                    info.words += words.len();
                }
            }
        lbuf.clear()
    }
    Ok(info)
}

// Open the file (possibly - for stdin) and return an array of strings
// reflecting the contents of the file, split on null characters.
fn process_files0_from(filename: &str) -> WCResult<Vec<String>> {
    let mut file = try!(open_file(filename));
    let mut result = Vec::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(file.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            let line = try!(from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let mut args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        print_stderr!("wc: file operands cannot be combined with ");
        println_stderr!("--files0-from");
        println_stderr!("Try 'wc --help' for more information");
        exit(1);
    }

    // If no data is specifically requested, default to lines/words/bytes
    if !(args.flag_lines || args.flag_words || args.flag_bytes
         || args.flag_chars || args.flag_max_line_length) {
        args.flag_lines = true;
        args.flag_words = true;
        args.flag_bytes = true;
    }

    // Determine what "files" to process
    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        match process_files0_from(args.flag_files0_from.as_ref()) {
            Ok(parts) => files.extend(parts),
            Err(e) => {
                println_stderr!("wc: error reading {}: {}",
                                args.flag_files0_from, e);
                exit(1);
            },
        };
        if &args.flag_files0_from == "-" && files.contains(&"-".to_string()) {
            print_stderr!("wc: when reading file names from stdin, no file ");
            println_stderr!("name of '-' allowed");
            exit(1);
        }
    } else {
        files.extend(args.arg_FILE.clone());
    };

    // Process all of our files
    let results: Vec<WCResult<FileInfo>> = files.iter()
        .map(|f| process_file(f, &args))
        .collect();

    // Fold over the FileInfo results which are of the Ok variant to
    // compute the "total".
    let total: FileInfo = results.iter()
        .filter(|r| r.as_ref().is_ok())
        .map(|r| r.as_ref().unwrap())
        .fold(FileInfo::new(),
              |acc, item|
              FileInfo{
                  bytes: acc.bytes + item.bytes,
                  chars: acc.chars + item.chars,
                  lines: acc.lines + item.lines,
                  words: acc.words + item.words,
                  max_line_length: max(acc.max_line_length,
                                       item.max_line_length),
              });

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = if args.flag_files0_from == "-" {
        0
    } else {
        total.bytes.to_string().len()
    };

    // For determining eventual exit code
    let mut ok = true;

    // Present the results
    for data in files.iter().zip(results.iter()) {
        let (filename, result) = data;
        ok &= display(&args, filename, result, field_size)
    }
    if results.len() > 1 {
        display(&args, "total", &Ok(total), field_size);
    }
    if !ok {
        exit(1);
    }
}
<commit_msg>Pedantically sort use statements<commit_after>extern crate rustc_serialize;
extern crate docopt;

use docopt::Docopt;
use std::cmp::max;
use std::error::Error;
use std::fmt::Result as FmtResult;
use std::fmt::{Display, Formatter};
use std::fs::File;
use std::io::Error as IOError;
use std::io::{BufReader, stderr, stdin};
use std::io::{Write, BufRead, Read};
use std::process::exit;
use std::str::{Utf8Error, from_utf8};

const LF: char = '\n';
const NULL: char = '\0';
const VERSION: &'static str = "0.0.1";
const USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

// Macro that acts like print!, but w/ stderr
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

// Macro that acts like println!, but w/ stderr
macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

// Enum that captures all of the expected error variants we are likely
// to return.
#[derive(Debug)]
enum WCError {
    IO(IOError),
    Utf8(Utf8Error),
}

// Implement the Display trait so that we can print our errors to the
// user. Also a pre-requisite for implementing the Error trait.
impl Display for WCError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            WCError::IO(ref e) => e.fmt(f),
            WCError::Utf8(ref e) => e.fmt(f),
        }
    }
}

// Implement the Error trait so that callers can treat a WCError as
// they would any other error.
impl std::error::Error for WCError {
    fn description(&self) -> &str {
        match *self {
            WCError::IO(ref e) => e.description(),
            WCError::Utf8(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            WCError::IO(ref e) => e.cause(),
            WCError::Utf8(ref e) => e.cause()
        }
    }
}

// Make it possible to wrap an IOError as a variant of our WCError enum.
impl From<IOError> for WCError {
    fn from(e: IOError) -> WCError {
        WCError::IO(e)
    }
}

// Make it possible to wrap a Utf8Error as a variant of our WCError enum.
impl From<Utf8Error> for WCError {
    fn from(e: Utf8Error) -> WCError {
        WCError::Utf8(e)
    }
}

// Define the struct which will wrap all of our counts. Derive the
// Debug trait so we can print it out using the "{:?}" pattern for
// debugging purposes.
#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

// Implement a convenience constructor that will return a zeroed
// FileInfo struct. I had considered implementing the Zero and Add
// traits and using sum to acquire our "total" instead of a fold, but
// the behavior of max_line_length just doesn't look like add to me,
// and it may be more confusing that it's worth.
impl FileInfo {
    fn new() -> FileInfo {
        FileInfo{bytes: 0,
                 chars: 0,
                 lines: 0,
                 words: 0,
                 max_line_length: 0,
        }
    }
}

// Wrapper around Result that specializes the Error variant to be one
// of our WCErrors.
type WCResult<T> = Result<T, WCError>;

// Print the result of attempting to process a file.
fn display(args: &Args, filename: &str, result: &WCResult<FileInfo>,
           field_size: usize) -> bool {
    match *result {
        Ok(ref r) => {
            if args.flag_lines {
                print!("{:1$} ", r.lines, field_size);
            }
            if args.flag_words {
                print!("{:1$} ", r.words, field_size);
            }
            if args.flag_bytes {
                print!("{:1$} ", r.bytes, field_size);
            }
            if args.flag_chars {
                print!("{:1$} ", r.chars, field_size);
            }
            if args.flag_max_line_length {
                print!("{:1$} ", r.max_line_length, field_size);
            }
            println!("{}", filename);
            true
        },
        Err(ref e) => {
            println_stderr!("wc: {}: {}", filename, e);
            false
        },
    }
}

// Return the named file, but opened, and in a result that's usable as
// a buffered reader. In the case of a filename "-", return an
// appropriately wrapped Stdin. Doing this allows us to treat regular
// files and stdin equivalently.
fn open_file(filename: &str) -> WCResult<Box<BufRead>> {
    match filename {
        "-" => Ok(Box::new(BufReader::new(stdin()))),
        _ => Ok(Box::new(BufReader::new(try!(File::open(filename))))),
    }
}

fn process_file(filename: &str, args: &Args) -> WCResult<FileInfo> {
    let mut file = try!(open_file(filename));
    let mut info = FileInfo::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(file.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // If this if statement wasn't here, we would still need to
        // create a scope because we're going to borrow lbuf and the
        // borrow must end before we can clear it.
        if args.flag_lines || args.flag_words || args.flag_chars ||
            args.flag_max_line_length {
                // TODO: Handle files which are not utf8-encoded. Right
                // now we get an error here.
                let line = try!(from_utf8(&lbuf));

                let size = if args.flag_chars || args.flag_max_line_length {
                    line.chars().count()
                } else { 0 };

                let last = if args.flag_lines || args.flag_max_line_length {
                    line.chars().last().unwrap_or(NULL)
                } else { NULL };

                if last == LF {
                    info.lines += 1;
                }

                if args.flag_max_line_length {
                    info.max_line_length = if last == LF {
                        max(info.max_line_length, size - 1)
                    } else {
                        max(info.max_line_length, size)
                    };
                }

                info.chars += size;

                if args.flag_words {
                    let mut words: Vec<&str> = line
                        .split(|c: char| c.is_whitespace())
                        .collect();
                    words.retain(|s: &&str| s.len() > 0);
                    info.words += words.len();
                }
            }
        lbuf.clear()
    }
    Ok(info)
}

// Open the file (possibly - for stdin) and return an array of strings
// reflecting the contents of the file, split on null characters.
fn process_files0_from(filename: &str) -> WCResult<Vec<String>> {
    let mut file = try!(open_file(filename));
    let mut result = Vec::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(file.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            let line = try!(from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let mut args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        print_stderr!("wc: file operands cannot be combined with ");
        println_stderr!("--files0-from");
        println_stderr!("Try 'wc --help' for more information");
        exit(1);
    }

    // If no data is specifically requested, default to lines/words/bytes
    if !(args.flag_lines || args.flag_words || args.flag_bytes
         || args.flag_chars || args.flag_max_line_length) {
        args.flag_lines = true;
        args.flag_words = true;
        args.flag_bytes = true;
    }

    // Determine what "files" to process
    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        match process_files0_from(args.flag_files0_from.as_ref()) {
            Ok(parts) => files.extend(parts),
            Err(e) => {
                println_stderr!("wc: error reading {}: {}",
                                args.flag_files0_from, e);
                exit(1);
            },
        };
        if &args.flag_files0_from == "-" && files.contains(&"-".to_string()) {
            print_stderr!("wc: when reading file names from stdin, no file ");
            println_stderr!("name of '-' allowed");
            exit(1);
        }
    } else {
        files.extend(args.arg_FILE.clone());
    };

    // Process all of our files
    let results: Vec<WCResult<FileInfo>> = files.iter()
        .map(|f| process_file(f, &args))
        .collect();

    // Fold over the FileInfo results which are of the Ok variant to
    // compute the "total".
    let total: FileInfo = results.iter()
        .filter(|r| r.as_ref().is_ok())
        .map(|r| r.as_ref().unwrap())
        .fold(FileInfo::new(),
              |acc, item|
              FileInfo{
                  bytes: acc.bytes + item.bytes,
                  chars: acc.chars + item.chars,
                  lines: acc.lines + item.lines,
                  words: acc.words + item.words,
                  max_line_length: max(acc.max_line_length,
                                       item.max_line_length),
              });

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = if args.flag_files0_from == "-" {
        0
    } else {
        total.bytes.to_string().len()
    };

    // For determining eventual exit code
    let mut ok = true;

    // Present the results
    for data in files.iter().zip(results.iter()) {
        let (filename, result) = data;
        ok &= display(&args, filename, result, field_size)
    }
    if results.len() > 1 {
        display(&args, "total", &Ok(total), field_size);
    }
    if !ok {
        exit(1);
    }
}
<|endoftext|>


<commit_before>//! # Triple buffering
//!
//! In this crate, we attempt to implement a triple buffering mechanism,
//! suitable for emulating shared memory cells in a thread-safe and
//! non-blocking fashion between one single writer and one single reader.

use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

/// ## Indexing
///
/// These types are used to index into triple buffers
/// TODO: Switch to i8 / AtomicI8 once the later is stable
type TripleBufferIndex = usize;
type AtomicTripleBufferIndex = AtomicUsize;

/// ## Shared state
///
/// In a triple buffering communication protocol, the reader and writer share
/// the following storage:
///
/// - Three memory buffers suitable for storing the data at hand
/// - One index pointing to the buffer which no one is currently using
/// - One index pointing to the most recently updated buffer
///
#[derive(Debug)]
struct TripleBufferSharedState<T: Clone + PartialEq> {
    /// Three buffers of data
    buffers: [UnsafeCell<T>; 3],

    /// Index of the back-buffer (buffer which no one is currently accessing)
    back_idx: AtomicTripleBufferIndex,

    /// Index of the most recently updated buffer
    last_idx: AtomicTripleBufferIndex,
}
//
impl<T: Clone + PartialEq> Clone for TripleBufferSharedState<T> {
    fn clone(&self) -> Self {
        // The use of UnsafeCell makes buffers somwhat cumbersome to clone...
        let clone_buffer = | i: TripleBufferIndex | {
            UnsafeCell::new(
                unsafe { (*self.buffers[i].get()).clone() }
            )
        };

        // ...so better define a shortcut before we move on to actual cloning
        TripleBufferSharedState {
            buffers: [
                clone_buffer(0),
                clone_buffer(1),
                clone_buffer(2),
            ],
            back_idx: AtomicTripleBufferIndex::new(
                self.back_idx.load(Ordering::Relaxed)
            ),
            last_idx: AtomicTripleBufferIndex::new(
                self.last_idx.load(Ordering::Relaxed)
            ),
        }
    }
}
//
impl<T: Clone + PartialEq> PartialEq for TripleBufferSharedState<T> {
    fn eq(&self, other: &Self) -> bool {
        // The use of UnsafeCell makes buffers somwhat cumbersome to compare...
        let buffer_eq = | i: TripleBufferIndex | -> bool {
            unsafe { *self.buffers[i].get() == *other.buffers[i].get() }
        };

        // ...and atomics aren't much better...
        let atomics_eq = | x: &AtomicTripleBufferIndex,
                           y: &AtomicTripleBufferIndex | -> bool {
            x.load(Ordering::Relaxed) == y.load(Ordering::Relaxed)
        };

        // ...so better define some shortcuts before getting started:
        buffer_eq(0) && buffer_eq(1) && buffer_eq(2) &&
        atomics_eq(&self.back_idx, &other.back_idx) &&
        atomics_eq(&self.last_idx, &other.last_idx)
    }
}
//
unsafe impl<T: Clone + PartialEq> Sync for TripleBufferSharedState<T> {}

/// ## Producing data ("input")
///
/// The producer of data has exclusive write access to the so-called "write
/// buffer". To send an update, it writes it into the back buffer, then swaps
/// the write buffer with the back buffer, making the data readable.
///
#[derive(Debug)]
struct TripleBufferInput<T: Clone + PartialEq> {
    shared: Arc<TripleBufferSharedState<T>>,
    write_idx: TripleBufferIndex,
}
//
impl<T: Clone + PartialEq> TripleBufferInput<T> {
    /// Write a new value into the triple buffer
    fn write(&mut self, value: T) {
        // Access the shared state
        let ref shared_state = *self.shared;

        // Determine which shared buffer is the write buffer
        let active_idx = self.write_idx;

        // Move the input value into the write buffer
        let write_ptr = shared_state.buffers[active_idx].get();
        unsafe { *write_ptr = value; }

        // Swap the back buffer and the write buffer. This makes the new data
        // block available to the reader and gives us a new buffer to write to.
        self.write_idx = shared_state.back_idx.swap(
            active_idx,
            Ordering::Release  // Need this for new buffer state to be visible
        );

        // Notify the reader that we submitted an update
        shared_state.last_idx.store(
            active_idx,
            Ordering::Release  // Need this for back_idx to be visible
        );
    }
}

/// ## Consuming data ("output")
///
/// The consumer of data has exclusive read access to the so-called "read
/// buffer". To access the latest data, it swaps the read buffer with the
/// back buffer if the later contains more recent data, then reads from
/// the (possibly new) read buffer.
///
#[derive(Debug)]
struct TripleBufferOutput<T: Clone + PartialEq> {
    shared: Arc<TripleBufferSharedState<T>>,
    read_idx: TripleBufferIndex,
}
//
impl<T: Clone + PartialEq> TripleBufferOutput<T> {
    /// Access the latest fully written value from the triple buffer
    fn read(&mut self) -> &T {
        // Access the shared state
        let ref shared_state = *self.shared;

        // Check if the writer has submitted an update
        let last_idx = shared_state.last_idx.load(Ordering::Acquire);

        // If an update is pending in the back buffer, make it our read buffer
        if self.read_idx != last_idx {
            // Swap the back buffer and the read buffer. We get exclusive read
            // access to the data, and the writer gets a new back buffer.
            self.read_idx = shared_state.back_idx.swap(
                self.read_idx,
                Ordering::Acquire
            );
        }

        // Access data from the current read buffer
        let read_ptr = shared_state.buffers[self.read_idx].get();
        unsafe { &*read_ptr }
    }
}

/// ## Triple buffer
///
/// A triple buffer is created as the combination of an input and an output.
///
#[derive(Debug)]
struct TripleBuffer<T: Clone + PartialEq> {
    input: TripleBufferInput<T>,
    output: TripleBufferOutput<T>,
}
//
impl<T: Clone + PartialEq> TripleBuffer<T> {
    /// Construct a triple buffer with a certain initial value
    fn new(initial: T) -> Self {
        // Start with the shared state...
        let shared_state = Arc::new(
            TripleBufferSharedState {
                buffers: [
                    UnsafeCell::new(initial.clone()),
                    UnsafeCell::new(initial.clone()),
                    UnsafeCell::new(initial)
                ],
                back_idx: AtomicTripleBufferIndex::new(0),
                last_idx: AtomicTripleBufferIndex::new(2),
            }
        );

        // ...then construct the input and output structs
        TripleBuffer {
            input: TripleBufferInput {
                shared: shared_state.clone(),
                write_idx: 1,
            },
            output: TripleBufferOutput {
                shared: shared_state,
                read_idx: 2,
            },
        }
    }
}
//
impl<T: Clone + PartialEq> Clone for TripleBuffer<T> {
    fn clone(&self) -> Self {
        // Clone the shared state...
        let shared_state = Arc::new(
            (*self.input.shared).clone()
        );

        // ...then the input and output structs
        TripleBuffer {
            input: TripleBufferInput {
                shared: shared_state.clone(),
                write_idx: self.input.write_idx,
            },
            output: TripleBufferOutput {
                shared: shared_state,
                read_idx: self.output.read_idx,
            },
        }
    }
}
//
impl<T: Clone + PartialEq> PartialEq for TripleBuffer<T> {
    fn eq(&self, other: &Self) -> bool {
        (*self.input.shared == *other.input.shared) &&
        (self.input.write_idx == other.input.write_idx) &&
        (self.output.read_idx == other.output.read_idx)
    }
}

/// ## Testing
///
/// Unit tests and benchmarks for triple buffers
///
#[cfg(test)]
mod tests {
    /// Test that triple buffers are properly initialized
    #[test]
    fn test_init() {
        // Let's create a triple buffer
        let buf = ::TripleBuffer::new(42);

        // Access the shared state
        let ref buf_shared = *buf.input.shared;
        let back_idx = buf_shared.back_idx.load(::Ordering::Relaxed);
        let last_idx = buf_shared.last_idx.load(::Ordering::Relaxed);

        // Write-/read-/back-buffer indexes must be in range
        assert!(index_in_range(buf.input.write_idx));
        assert!(index_in_range(buf.output.read_idx));
        assert!(index_in_range(back_idx));

        // Write-/read-/back-buffer indexes must be distinct
        assert!(buf.input.write_idx != buf.output.read_idx);
        assert!(buf.input.write_idx != back_idx);
        assert!(buf.output.read_idx != back_idx);

        // Read buffer must be properly initialized
        let read_ptr = buf_shared.buffers[buf.output.read_idx].get();
        assert!(unsafe { *read_ptr } == 42);

        // Last-written index must initially point to the read buffer
        assert_eq!(last_idx, buf.output.read_idx);
    }

    /// Test that (sequentially) writing to a triple buffer works
    #[test]
    fn test_seq_write() {
        // Let's create a triple buffer
        let mut buf = ::TripleBuffer::new(false);

        // Back up the initial buffer state
        let old_buf = buf.clone();
        let ref old_shared = old_buf.input.shared;

        // Perform a write
        buf.input.write(true);

        // Check new implementation state
        {
            // Start from the old buffer state
            let mut expected_buf = old_buf.clone();
            let ref expected_shared = expected_buf.input.shared;

            // We expect the former write buffer to have received the new value
            let old_write_idx = old_buf.input.write_idx;
            let write_buf_ptr = expected_shared.buffers[old_write_idx].get();
            unsafe { *write_buf_ptr = true; }

            // We expect the former write buffer to be the new back buffer
            expected_shared.back_idx.store(
                old_write_idx,
                ::Ordering::Relaxed
            );

            // We expect the last updated index to point to former write buffer
            expected_shared.last_idx.store(
                old_write_idx,
                ::Ordering::Relaxed
            );

            // We expect the old back buffer to become the new write buffer
            let old_back_idx = old_shared.back_idx.load(::Ordering::Relaxed);
            expected_buf.input.write_idx = old_back_idx;

            // Nothing else should have changed
            assert_eq!(buf, expected_buf);
        }
    }

    /// Check that (sequentially) reading from a triple buffer works
    #[test]
    fn test_seq_read() {
        // Let's create a triple buffer and write into it
        let mut buf = ::TripleBuffer::new(1.0);
        buf.input.write(4.2);

        // Test readout from dirty (freshly written) triple buffer
        {
            // Back up the initial buffer state
            let old_buf = buf.clone();
            let ref old_shared = old_buf.input.shared;

            // Read from the buffer
            let result = *buf.output.read();

            // Output value should be correct
            assert_eq!(result, 4.2);

            // Start from the old buffer state
            let mut expected_buf = old_buf.clone();
            let ref expected_shared = expected_buf.input.shared;

            // We expect the new back index to point to the former read buffer
            expected_shared.back_idx.store(
                old_buf.output.read_idx,
                ::Ordering::Relaxed
            );

            // We expect the new read index to point to the former back buffer
            let old_back_idx = old_shared.back_idx.load(::Ordering::Relaxed);
            expected_buf.output.read_idx = old_back_idx;

            // Nothing else should have changed
            assert_eq!(buf, expected_buf);
        }

        // Test readout from clean (unchanged) triple buffer
        {
            // Back up the initial buffer state
            let old_buf = buf.clone();

            // Read from the buffer
            let result = *buf.output.read();

            // Output value should be correct
            assert_eq!(result, 4.2);

            // Buffer state should be unchanged
            assert_eq!(buf, old_buf);
        }
    }

    // Check that concurrent reads and writes work
    #[test]
    fn test_conc_access() {
        // We will stress the infrastructure by performing this many writes
        // as a reader continuously reads the latest value
        let test_write_count = 1000;

        // This is the buffer that our reader and writer will share
        let mut buf = ::TripleBuffer::new(0u64);

        // Extract the input stage so that we can send it to the writer
        let mut buf_input = buf.input;

        // The writer continuously increments the buffered value, with some
        // rate limiting to ensure that the reader can see the updates
        let writer = ::thread::spawn(move || {
            // TODO: Use a barrier here instead
            for value in 1 .. test_write_count+1 {
                buf_input.write(value);
                ::thread::yield_now();
                ::thread::sleep(::Duration::from_millis(1));
            }
        });

        // The reader continuously checks the buffered value, and should see
        // every update without any incoherent value
        let mut last_value = 0u64;
        while last_value < test_write_count {
            let new_value = *buf.output.read();
            assert!(
                (new_value >= last_value) && (new_value-last_value <= 1)
            );
            last_value = new_value;
        }

        // Wait for the writer to finish
        writer.join().unwrap();
    }

    // TODO: Benchmark the performance of concurrent reads and writes

    /// Range check for triple buffer indexes
    #[allow(unused_comparisons)]
    fn index_in_range(idx: ::TripleBufferIndex) -> bool {
        (idx >= 0) & (idx <= 2)
    }
}

fn main() {
    println!("Hello, world!");

    let mut buf = TripleBuffer::new("Hello!");
    println!("Initial value is {}", buf.output.read());

    buf.input.write("World!");
    println!("New value is {}", buf.output.read())
}
<commit_msg>Synchronized reader & writer start using a barrier<commit_after>//! # Triple buffering
//!
//! In this crate, we attempt to implement a triple buffering mechanism,
//! suitable for emulating shared memory cells in a thread-safe and
//! non-blocking fashion between one single writer and one single reader.

use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;

/// ## Indexing
///
/// These types are used to index into triple buffers
/// TODO: Switch to i8 / AtomicI8 once the later is stable
type TripleBufferIndex = usize;
type AtomicTripleBufferIndex = AtomicUsize;

/// ## Shared state
///
/// In a triple buffering communication protocol, the reader and writer share
/// the following storage:
///
/// - Three memory buffers suitable for storing the data at hand
/// - One index pointing to the buffer which no one is currently using
/// - One index pointing to the most recently updated buffer
///
#[derive(Debug)]
struct TripleBufferSharedState<T: Clone + PartialEq> {
    /// Three buffers of data
    buffers: [UnsafeCell<T>; 3],

    /// Index of the back-buffer (buffer which no one is currently accessing)
    back_idx: AtomicTripleBufferIndex,

    /// Index of the most recently updated buffer
    last_idx: AtomicTripleBufferIndex,
}
//
impl<T: Clone + PartialEq> Clone for TripleBufferSharedState<T> {
    fn clone(&self) -> Self {
        // The use of UnsafeCell makes buffers somwhat cumbersome to clone...
        let clone_buffer = | i: TripleBufferIndex | {
            UnsafeCell::new(
                unsafe { (*self.buffers[i].get()).clone() }
            )
        };

        // ...so better define a shortcut before we move on to actual cloning
        TripleBufferSharedState {
            buffers: [
                clone_buffer(0),
                clone_buffer(1),
                clone_buffer(2),
            ],
            back_idx: AtomicTripleBufferIndex::new(
                self.back_idx.load(Ordering::Relaxed)
            ),
            last_idx: AtomicTripleBufferIndex::new(
                self.last_idx.load(Ordering::Relaxed)
            ),
        }
    }
}
//
impl<T: Clone + PartialEq> PartialEq for TripleBufferSharedState<T> {
    fn eq(&self, other: &Self) -> bool {
        // The use of UnsafeCell makes buffers somwhat cumbersome to compare...
        let buffer_eq = | i: TripleBufferIndex | -> bool {
            unsafe { *self.buffers[i].get() == *other.buffers[i].get() }
        };

        // ...and atomics aren't much better...
        let atomics_eq = | x: &AtomicTripleBufferIndex,
                           y: &AtomicTripleBufferIndex | -> bool {
            x.load(Ordering::Relaxed) == y.load(Ordering::Relaxed)
        };

        // ...so better define some shortcuts before getting started:
        buffer_eq(0) && buffer_eq(1) && buffer_eq(2) &&
        atomics_eq(&self.back_idx, &other.back_idx) &&
        atomics_eq(&self.last_idx, &other.last_idx)
    }
}
//
unsafe impl<T: Clone + PartialEq> Sync for TripleBufferSharedState<T> {}

/// ## Producing data ("input")
///
/// The producer of data has exclusive write access to the so-called "write
/// buffer". To send an update, it writes it into the back buffer, then swaps
/// the write buffer with the back buffer, making the data readable.
///
#[derive(Debug)]
struct TripleBufferInput<T: Clone + PartialEq> {
    shared: Arc<TripleBufferSharedState<T>>,
    write_idx: TripleBufferIndex,
}
//
impl<T: Clone + PartialEq> TripleBufferInput<T> {
    /// Write a new value into the triple buffer
    fn write(&mut self, value: T) {
        // Access the shared state
        let ref shared_state = *self.shared;

        // Determine which shared buffer is the write buffer
        let active_idx = self.write_idx;

        // Move the input value into the write buffer
        let write_ptr = shared_state.buffers[active_idx].get();
        unsafe { *write_ptr = value; }

        // Swap the back buffer and the write buffer. This makes the new data
        // block available to the reader and gives us a new buffer to write to.
        self.write_idx = shared_state.back_idx.swap(
            active_idx,
            Ordering::Release  // Need this for new buffer state to be visible
        );

        // Notify the reader that we submitted an update
        shared_state.last_idx.store(
            active_idx,
            Ordering::Release  // Need this for back_idx to be visible
        );
    }
}

/// ## Consuming data ("output")
///
/// The consumer of data has exclusive read access to the so-called "read
/// buffer". To access the latest data, it swaps the read buffer with the
/// back buffer if the later contains more recent data, then reads from
/// the (possibly new) read buffer.
///
#[derive(Debug)]
struct TripleBufferOutput<T: Clone + PartialEq> {
    shared: Arc<TripleBufferSharedState<T>>,
    read_idx: TripleBufferIndex,
}
//
impl<T: Clone + PartialEq> TripleBufferOutput<T> {
    /// Access the latest fully written value from the triple buffer
    fn read(&mut self) -> &T {
        // Access the shared state
        let ref shared_state = *self.shared;

        // Check if the writer has submitted an update
        let last_idx = shared_state.last_idx.load(Ordering::Acquire);

        // If an update is pending in the back buffer, make it our read buffer
        if self.read_idx != last_idx {
            // Swap the back buffer and the read buffer. We get exclusive read
            // access to the data, and the writer gets a new back buffer.
            self.read_idx = shared_state.back_idx.swap(
                self.read_idx,
                Ordering::Acquire
            );
        }

        // Access data from the current read buffer
        let read_ptr = shared_state.buffers[self.read_idx].get();
        unsafe { &*read_ptr }
    }
}

/// ## Triple buffer
///
/// A triple buffer is created as the combination of an input and an output.
///
#[derive(Debug)]
struct TripleBuffer<T: Clone + PartialEq> {
    input: TripleBufferInput<T>,
    output: TripleBufferOutput<T>,
}
//
impl<T: Clone + PartialEq> TripleBuffer<T> {
    /// Construct a triple buffer with a certain initial value
    fn new(initial: T) -> Self {
        // Start with the shared state...
        let shared_state = Arc::new(
            TripleBufferSharedState {
                buffers: [
                    UnsafeCell::new(initial.clone()),
                    UnsafeCell::new(initial.clone()),
                    UnsafeCell::new(initial)
                ],
                back_idx: AtomicTripleBufferIndex::new(0),
                last_idx: AtomicTripleBufferIndex::new(2),
            }
        );

        // ...then construct the input and output structs
        TripleBuffer {
            input: TripleBufferInput {
                shared: shared_state.clone(),
                write_idx: 1,
            },
            output: TripleBufferOutput {
                shared: shared_state,
                read_idx: 2,
            },
        }
    }
}
//
impl<T: Clone + PartialEq> Clone for TripleBuffer<T> {
    fn clone(&self) -> Self {
        // Clone the shared state...
        let shared_state = Arc::new(
            (*self.input.shared).clone()
        );

        // ...then the input and output structs
        TripleBuffer {
            input: TripleBufferInput {
                shared: shared_state.clone(),
                write_idx: self.input.write_idx,
            },
            output: TripleBufferOutput {
                shared: shared_state,
                read_idx: self.output.read_idx,
            },
        }
    }
}
//
impl<T: Clone + PartialEq> PartialEq for TripleBuffer<T> {
    fn eq(&self, other: &Self) -> bool {
        (*self.input.shared == *other.input.shared) &&
        (self.input.write_idx == other.input.write_idx) &&
        (self.output.read_idx == other.output.read_idx)
    }
}

/// ## Testing
///
/// Unit tests and benchmarks for triple buffers
///
#[cfg(test)]
mod tests {
    /// Test that triple buffers are properly initialized
    #[test]
    fn test_init() {
        // Let's create a triple buffer
        let buf = ::TripleBuffer::new(42);

        // Access the shared state
        let ref buf_shared = *buf.input.shared;
        let back_idx = buf_shared.back_idx.load(::Ordering::Relaxed);
        let last_idx = buf_shared.last_idx.load(::Ordering::Relaxed);

        // Write-/read-/back-buffer indexes must be in range
        assert!(index_in_range(buf.input.write_idx));
        assert!(index_in_range(buf.output.read_idx));
        assert!(index_in_range(back_idx));

        // Write-/read-/back-buffer indexes must be distinct
        assert!(buf.input.write_idx != buf.output.read_idx);
        assert!(buf.input.write_idx != back_idx);
        assert!(buf.output.read_idx != back_idx);

        // Read buffer must be properly initialized
        let read_ptr = buf_shared.buffers[buf.output.read_idx].get();
        assert!(unsafe { *read_ptr } == 42);

        // Last-written index must initially point to the read buffer
        assert_eq!(last_idx, buf.output.read_idx);
    }

    /// Test that (sequentially) writing to a triple buffer works
    #[test]
    fn test_seq_write() {
        // Let's create a triple buffer
        let mut buf = ::TripleBuffer::new(false);

        // Back up the initial buffer state
        let old_buf = buf.clone();
        let ref old_shared = old_buf.input.shared;

        // Perform a write
        buf.input.write(true);

        // Check new implementation state
        {
            // Start from the old buffer state
            let mut expected_buf = old_buf.clone();
            let ref expected_shared = expected_buf.input.shared;

            // We expect the former write buffer to have received the new value
            let old_write_idx = old_buf.input.write_idx;
            let write_buf_ptr = expected_shared.buffers[old_write_idx].get();
            unsafe { *write_buf_ptr = true; }

            // We expect the former write buffer to be the new back buffer
            expected_shared.back_idx.store(
                old_write_idx,
                ::Ordering::Relaxed
            );

            // We expect the last updated index to point to former write buffer
            expected_shared.last_idx.store(
                old_write_idx,
                ::Ordering::Relaxed
            );

            // We expect the old back buffer to become the new write buffer
            let old_back_idx = old_shared.back_idx.load(::Ordering::Relaxed);
            expected_buf.input.write_idx = old_back_idx;

            // Nothing else should have changed
            assert_eq!(buf, expected_buf);
        }
    }

    /// Check that (sequentially) reading from a triple buffer works
    #[test]
    fn test_seq_read() {
        // Let's create a triple buffer and write into it
        let mut buf = ::TripleBuffer::new(1.0);
        buf.input.write(4.2);

        // Test readout from dirty (freshly written) triple buffer
        {
            // Back up the initial buffer state
            let old_buf = buf.clone();
            let ref old_shared = old_buf.input.shared;

            // Read from the buffer
            let result = *buf.output.read();

            // Output value should be correct
            assert_eq!(result, 4.2);

            // Start from the old buffer state
            let mut expected_buf = old_buf.clone();
            let ref expected_shared = expected_buf.input.shared;

            // We expect the new back index to point to the former read buffer
            expected_shared.back_idx.store(
                old_buf.output.read_idx,
                ::Ordering::Relaxed
            );

            // We expect the new read index to point to the former back buffer
            let old_back_idx = old_shared.back_idx.load(::Ordering::Relaxed);
            expected_buf.output.read_idx = old_back_idx;

            // Nothing else should have changed
            assert_eq!(buf, expected_buf);
        }

        // Test readout from clean (unchanged) triple buffer
        {
            // Back up the initial buffer state
            let old_buf = buf.clone();

            // Read from the buffer
            let result = *buf.output.read();

            // Output value should be correct
            assert_eq!(result, 4.2);

            // Buffer state should be unchanged
            assert_eq!(buf, old_buf);
        }
    }

    /// Check that concurrent reads and writes work
    #[test]
    fn test_conc_access() {
        // We will stress the infrastructure by performing this many writes
        // as a reader continuously reads the latest value
        let test_write_count = 1000;

        // This is the buffer that our reader and writer will share
        let mut buf = ::TripleBuffer::new(0u64);

        // Extract the input stage so that we can send it to the writer
        let mut buf_input = buf.input;

        // Setup a barrier so that the reader & writer can start synchronously
        let barrier = ::Arc::new(::Barrier::new(2));
        let w_barrier = barrier.clone();

        // The writer continuously increments the buffered value, with some
        // rate limiting to ensure that the reader can see the updates
        let writer = ::thread::spawn(move || {
            w_barrier.wait();
            for value in 1 .. test_write_count+1 {
                buf_input.write(value);
                ::thread::yield_now();
                ::thread::sleep(::Duration::from_millis(1));
            }
        });

        // The reader continuously checks the buffered value, and should see
        // every update without any incoherent value
        let mut last_value = 0u64;
        barrier.wait();
        while last_value < test_write_count {
            let new_value = *buf.output.read();
            assert!(
                (new_value >= last_value) && (new_value-last_value <= 1)
            );
            last_value = new_value;
        }

        // Wait for the writer to finish
        writer.join().unwrap();
    }

    // TODO: Benchmark the performance of concurrent reads and writes

    /// Range check for triple buffer indexes
    #[allow(unused_comparisons)]
    fn index_in_range(idx: ::TripleBufferIndex) -> bool {
        (idx >= 0) & (idx <= 2)
    }
}

fn main() {
    println!("Hello, world!");

    let mut buf = TripleBuffer::new("Hello!");
    println!("Initial value is {}", buf.output.read());

    buf.input.write("World!");
    println!("New value is {}", buf.output.read())
}
<|endoftext|>


<commit_before>#![feature(unsafe_destructor)]
#![feature(test)]
#![feature(alloc)]
#![feature(core)]
#![feature(std_misc)]
#![feature(collections)]
#![feature(old_io)]
#![feature(io)]
#![feature(hash)]
#![feature(libc)]

#![allow(dead_code)]
#![allow(missing_copy_implementations)]

extern crate core;
extern crate test;
extern crate columnar;

extern crate docopt;
use docopt::Docopt;

use test::Bencher;

use columnar::Columnar;

use progress::{Graph, Scope};
use progress::subgraph::new_graph;
use progress::subgraph::Summary::Local;
use progress::subgraph::Source::ScopeOutput;
use progress::subgraph::Target::ScopeInput;
use progress::broadcast::Progcaster;
use communication::{ThreadCommunicator, ProcessCommunicator, Communicator};

use communication::channels::Data;
use std::hash::Hash;
use core::fmt::Debug;

use example::stream::Stream;
use example::input::InputExtensionTrait;
use example::concat::ConcatExtensionTrait;
use example::feedback::FeedbackExtensionTrait;
use example::distinct::DistinctExtensionTrait;
use example::command::CommandExtensionTrait;

use example::graph_builder::GraphBoundary;
use example::barrier::BarrierScope;

use std::rc::{Rc, try_unwrap};
use std::cell::RefCell;

use std::thread;

use networking::initialize_networking;

mod progress;
mod example;
mod networking;
mod communication;

static USAGE: &'static str = "
Usage: timely queue [options] [<arguments>...]
       timely barrier [options] [<arguments>...]
       timely command [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
";

fn main() {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Hello, world!");
    println!("Starting timely with\n\tworkers:\t{}\n\tprocesses:\t{}\n\tprocessid:\t{}", workers, processes, process_id);

    // vector holding communicators to use; one per local worker.
    let communicators = if processes > 1    {
                            let addresses = range(0, processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
                            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
                        }
                        else if workers > 1 { ProcessCommunicator::new_vector(workers) }
                        else                { vec![Communicator::Thread(Box::new(ThreadCommunicator))] };

    println!("Communicator constructed: {:?}", communicators.len());

    if args.get_bool("queue") {  println!("started queue test"); _queue_multi(communicators); }
    else if args.get_bool("barrier") { println!("started barrier test"); _barrier_multi(communicators); }
    else if args.get_bool("command") { _command_multi(communicators); println!("started command test"); }
}

fn _networking(my_id: u64, threads: u64, processes: u64) {
    let addresses = range(0, processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
    let _connections = initialize_networking(addresses, my_id, threads);
}

#[bench]
fn queue_bench(bencher: &mut Bencher) { _queue(ProcessCommunicator::new_vector(1).swap_remove(0), Some(bencher)); }
fn _queue_multi(communicators: Vec<Communicator>) {
    let mut guards = Vec::new();
    for communicator in communicators.into_iter() {
        // guards.push(thread::scoped(move || _queue(communicator, None)));

        guards.push(thread::Builder::new().name(format!("worker thread {}", communicator.index()))
                                          .scoped(move || _queue(communicator, None))
                                          .unwrap());
    }
}

// #[bench]
// fn command_bench(bencher: &mut Bencher) { _command(ProcessCommunicator::new_vector(1).swap_remove(0).unwrap(), Some(bencher)); }
fn _command_multi(communicators: Vec<Communicator>) {
    let mut guards = Vec::new();
    for communicator in communicators.into_iter() {
        guards.push(thread::scoped(move || _command(communicator, None)));
    }
}

#[bench]
fn barrier_bench(bencher: &mut Bencher) { _barrier(ProcessCommunicator::new_vector(1).swap_remove(0), Some(bencher)); }
fn _barrier_multi(communicators: Vec<Communicator>) {
    let mut guards = Vec::new();
    for communicator in communicators.into_iter() {
        guards.push(thread::scoped(move || _barrier(communicator, None)));
    }
}

fn _create_subgraph<G: Graph, D: Data+Hash+Eq+Debug+Columnar>(graph: &mut G,
                                 source1: &mut Stream<G, D>,
                                 source2: &mut Stream<G, D>,
                                 progcaster: Progcaster<(G::Timestamp,u64)>)
                            -> (Stream<G, D>, Stream<G, D>) {
    // build up a subgraph using the concatenated inputs/feedbacks
    let mut subgraph = Rc::new(RefCell::new(graph.new_subgraph::<u64>(0, progcaster)));

    let (sub_egress1, sub_egress2) = {
        // create new ingress nodes, passing in a reference to the subgraph for them to use.
        let mut sub_ingress1 = subgraph.add_input(source1);
        let mut sub_ingress2 = subgraph.add_input(source2);

        // putting a distinct'ing scope into the subgraph!
        let mut distinct = sub_ingress1.distinct();

        // egress each of the streams from the subgraph.
        let sub_egress1 = subgraph.add_output_to_graph(&mut distinct, graph);
        let sub_egress2 = subgraph.add_output_to_graph(&mut sub_ingress2, graph);

        (sub_egress1, sub_egress2)
    };

    // sort of a mess, but the way to get the subgraph out of the Rc<RefCell<...>>.
    // will explode if anyone else is still sitting on a reference to subgraph.
    graph.add_scope(try_unwrap(subgraph).ok().expect("hm").into_inner());

    return (sub_egress1, sub_egress2);
}

fn _queue(allocator: Communicator, bencher: Option<&mut Bencher>) {
    let allocator = Rc::new(RefCell::new(allocator));
    // no "base scopes" yet, so the root pretends to be a subscope of some parent with a () timestamp type.
    let mut graph = new_graph(Progcaster::new(&mut (*allocator.borrow_mut())));

    // try building some input scopes
    let (mut input1, mut stream1) = graph.new_input::<u64>(allocator.clone());
    let (mut input2, mut stream2) = graph.new_input::<u64>(allocator.clone());

    // prepare some feedback edges
    let (mut feedback1, mut feedback1_output) = stream1.feedback(((), 10000), Local(1));
    let (mut feedback2, mut feedback2_output) = stream2.feedback(((), 10000), Local(1));

    // build up a subgraph using the concatenated inputs/feedbacks
    let progcaster = Progcaster::new(&mut (*allocator.borrow_mut()));
    let (mut egress1, mut egress2) = _create_subgraph(&mut graph.clone(),
                                                      &mut stream1.concat(&mut feedback1_output),
                                                      &mut stream2.concat(&mut feedback2_output),
                                                      progcaster);

    // connect feedback sources. notice that we have swapped indices ...
    feedback1.connect_input(&mut egress2);
    feedback2.connect_input(&mut egress1);

    // finalize the graph/subgraph
    graph.borrow_mut().get_internal_summary();
    graph.borrow_mut().set_external_summary(Vec::new(), &mut Vec::new());

    // do one round of push progress, pull progress ...
    graph.borrow_mut().push_external_progress(&mut Vec::new());
    graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());

    // move some data into the dataflow graph.
    input1.send_messages(&((), 0), vec![1u64]);
    input2.send_messages(&((), 0), vec![2u64]);

    // see what everyone thinks about that ...
    graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());

    input1.advance(&((), 0), &((), 1000000));
    input2.advance(&((), 0), &((), 1000000));
    input1.close_at(&((), 1000000));
    input2.close_at(&((), 1000000));

    // spin
    match bencher {
        Some(b) => b.iter(|| { graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()); }),
        None    => while graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()) { }
    }
}

fn _command(allocator: Communicator, bencher: Option<&mut Bencher>) {
    let allocator = Rc::new(RefCell::new(allocator));

    // no "base scopes" yet, so the root pretends to be a subscope of some parent with a () timestamp type.
    let mut graph = new_graph(Progcaster::new(&mut (*allocator.borrow_mut())));
    let mut input = graph.new_input::<u64>(allocator);
    let mut feedback = input.1.feedback(((), 1000), Local(1));
    let mut result: Stream<_, u64> = input.1.concat(&mut feedback.1)
                                            .command("./target/release/command".to_string());

    feedback.0.connect_input(&mut result);

    // start things up!
    graph.borrow_mut().get_internal_summary();
    graph.borrow_mut().set_external_summary(Vec::new(), &mut Vec::new());
    graph.borrow_mut().push_external_progress(&mut Vec::new());

    input.0.close_at(&((), 0));

    // spin
    match bencher {
        Some(b) => b.iter(|| { graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()); }),
        None    => while graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()) { },
    }
}

fn _barrier(mut allocator: Communicator, bencher: Option<&mut Bencher>) {
    let mut graph = new_graph(Progcaster::new(&mut allocator));
    graph.add_scope(BarrierScope { epoch: 0, ready: true, degree: allocator.peers(), ttl: 10000 });
    graph.connect(ScopeOutput(0, 0), ScopeInput(0, 0));

    // start things up!
    graph.borrow_mut().get_internal_summary();
    graph.borrow_mut().set_external_summary(Vec::new(), &mut Vec::new());
    graph.borrow_mut().push_external_progress(&mut Vec::new());

    // spin
    match bencher {
        Some(b) => b.iter(|| { graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()); }),
        None    => while graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()) { },
    }
}
<commit_msg>barrier iterations++<commit_after>#![feature(unsafe_destructor)]
#![feature(test)]
#![feature(alloc)]
#![feature(core)]
#![feature(std_misc)]
#![feature(collections)]
#![feature(old_io)]
#![feature(io)]
#![feature(hash)]
#![feature(libc)]

#![allow(dead_code)]
#![allow(missing_copy_implementations)]

extern crate core;
extern crate test;
extern crate columnar;

extern crate docopt;
use docopt::Docopt;

use test::Bencher;

use columnar::Columnar;

use progress::{Graph, Scope};
use progress::subgraph::new_graph;
use progress::subgraph::Summary::Local;
use progress::subgraph::Source::ScopeOutput;
use progress::subgraph::Target::ScopeInput;
use progress::broadcast::Progcaster;
use communication::{ThreadCommunicator, ProcessCommunicator, Communicator};

use communication::channels::Data;
use std::hash::Hash;
use core::fmt::Debug;

use example::stream::Stream;
use example::input::InputExtensionTrait;
use example::concat::ConcatExtensionTrait;
use example::feedback::FeedbackExtensionTrait;
use example::distinct::DistinctExtensionTrait;
use example::command::CommandExtensionTrait;

use example::graph_builder::GraphBoundary;
use example::barrier::BarrierScope;

use std::rc::{Rc, try_unwrap};
use std::cell::RefCell;

use std::thread;

use networking::initialize_networking;

mod progress;
mod example;
mod networking;
mod communication;

static USAGE: &'static str = "
Usage: timely queue [options] [<arguments>...]
       timely barrier [options] [<arguments>...]
       timely command [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
";

fn main() {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Hello, world!");
    println!("Starting timely with\n\tworkers:\t{}\n\tprocesses:\t{}\n\tprocessid:\t{}", workers, processes, process_id);

    // vector holding communicators to use; one per local worker.
    let communicators = if processes > 1    {
                            let addresses = range(0, processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
                            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
                        }
                        else if workers > 1 { ProcessCommunicator::new_vector(workers) }
                        else                { vec![Communicator::Thread(Box::new(ThreadCommunicator))] };

    println!("Communicator constructed: {:?}", communicators.len());

    if args.get_bool("queue") {  println!("started queue test"); _queue_multi(communicators); }
    else if args.get_bool("barrier") { println!("started barrier test"); _barrier_multi(communicators); }
    else if args.get_bool("command") { _command_multi(communicators); println!("started command test"); }
}

fn _networking(my_id: u64, threads: u64, processes: u64) {
    let addresses = range(0, processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
    let _connections = initialize_networking(addresses, my_id, threads);
}

#[bench]
fn queue_bench(bencher: &mut Bencher) { _queue(ProcessCommunicator::new_vector(1).swap_remove(0), Some(bencher)); }
fn _queue_multi(communicators: Vec<Communicator>) {
    let mut guards = Vec::new();
    for communicator in communicators.into_iter() {
        // guards.push(thread::scoped(move || _queue(communicator, None)));

        guards.push(thread::Builder::new().name(format!("worker thread {}", communicator.index()))
                                          .scoped(move || _queue(communicator, None))
                                          .unwrap());
    }
}

// #[bench]
// fn command_bench(bencher: &mut Bencher) { _command(ProcessCommunicator::new_vector(1).swap_remove(0).unwrap(), Some(bencher)); }
fn _command_multi(communicators: Vec<Communicator>) {
    let mut guards = Vec::new();
    for communicator in communicators.into_iter() {
        guards.push(thread::scoped(move || _command(communicator, None)));
    }
}

#[bench]
fn barrier_bench(bencher: &mut Bencher) { _barrier(ProcessCommunicator::new_vector(1).swap_remove(0), Some(bencher)); }
fn _barrier_multi(communicators: Vec<Communicator>) {
    let mut guards = Vec::new();
    for communicator in communicators.into_iter() {
        guards.push(thread::scoped(move || _barrier(communicator, None)));
    }
}

fn _create_subgraph<G: Graph, D: Data+Hash+Eq+Debug+Columnar>(graph: &mut G,
                                 source1: &mut Stream<G, D>,
                                 source2: &mut Stream<G, D>,
                                 progcaster: Progcaster<(G::Timestamp,u64)>)
                            -> (Stream<G, D>, Stream<G, D>) {
    // build up a subgraph using the concatenated inputs/feedbacks
    let mut subgraph = Rc::new(RefCell::new(graph.new_subgraph::<u64>(0, progcaster)));

    let (sub_egress1, sub_egress2) = {
        // create new ingress nodes, passing in a reference to the subgraph for them to use.
        let mut sub_ingress1 = subgraph.add_input(source1);
        let mut sub_ingress2 = subgraph.add_input(source2);

        // putting a distinct'ing scope into the subgraph!
        let mut distinct = sub_ingress1.distinct();

        // egress each of the streams from the subgraph.
        let sub_egress1 = subgraph.add_output_to_graph(&mut distinct, graph);
        let sub_egress2 = subgraph.add_output_to_graph(&mut sub_ingress2, graph);

        (sub_egress1, sub_egress2)
    };

    // sort of a mess, but the way to get the subgraph out of the Rc<RefCell<...>>.
    // will explode if anyone else is still sitting on a reference to subgraph.
    graph.add_scope(try_unwrap(subgraph).ok().expect("hm").into_inner());

    return (sub_egress1, sub_egress2);
}

fn _queue(allocator: Communicator, bencher: Option<&mut Bencher>) {
    let allocator = Rc::new(RefCell::new(allocator));
    // no "base scopes" yet, so the root pretends to be a subscope of some parent with a () timestamp type.
    let mut graph = new_graph(Progcaster::new(&mut (*allocator.borrow_mut())));

    // try building some input scopes
    let (mut input1, mut stream1) = graph.new_input::<u64>(allocator.clone());
    let (mut input2, mut stream2) = graph.new_input::<u64>(allocator.clone());

    // prepare some feedback edges
    let (mut feedback1, mut feedback1_output) = stream1.feedback(((), 10000), Local(1));
    let (mut feedback2, mut feedback2_output) = stream2.feedback(((), 10000), Local(1));

    // build up a subgraph using the concatenated inputs/feedbacks
    let progcaster = Progcaster::new(&mut (*allocator.borrow_mut()));
    let (mut egress1, mut egress2) = _create_subgraph(&mut graph.clone(),
                                                      &mut stream1.concat(&mut feedback1_output),
                                                      &mut stream2.concat(&mut feedback2_output),
                                                      progcaster);

    // connect feedback sources. notice that we have swapped indices ...
    feedback1.connect_input(&mut egress2);
    feedback2.connect_input(&mut egress1);

    // finalize the graph/subgraph
    graph.borrow_mut().get_internal_summary();
    graph.borrow_mut().set_external_summary(Vec::new(), &mut Vec::new());

    // do one round of push progress, pull progress ...
    graph.borrow_mut().push_external_progress(&mut Vec::new());
    graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());

    // move some data into the dataflow graph.
    input1.send_messages(&((), 0), vec![1u64]);
    input2.send_messages(&((), 0), vec![2u64]);

    // see what everyone thinks about that ...
    graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());

    input1.advance(&((), 0), &((), 1000000));
    input2.advance(&((), 0), &((), 1000000));
    input1.close_at(&((), 1000000));
    input2.close_at(&((), 1000000));

    // spin
    match bencher {
        Some(b) => b.iter(|| { graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()); }),
        None    => while graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()) { }
    }
}

fn _command(allocator: Communicator, bencher: Option<&mut Bencher>) {
    let allocator = Rc::new(RefCell::new(allocator));

    // no "base scopes" yet, so the root pretends to be a subscope of some parent with a () timestamp type.
    let mut graph = new_graph(Progcaster::new(&mut (*allocator.borrow_mut())));
    let mut input = graph.new_input::<u64>(allocator);
    let mut feedback = input.1.feedback(((), 1000), Local(1));
    let mut result: Stream<_, u64> = input.1.concat(&mut feedback.1)
                                            .command("./target/release/command".to_string());

    feedback.0.connect_input(&mut result);

    // start things up!
    graph.borrow_mut().get_internal_summary();
    graph.borrow_mut().set_external_summary(Vec::new(), &mut Vec::new());
    graph.borrow_mut().push_external_progress(&mut Vec::new());

    input.0.close_at(&((), 0));

    // spin
    match bencher {
        Some(b) => b.iter(|| { graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()); }),
        None    => while graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()) { },
    }
}

fn _barrier(mut allocator: Communicator, bencher: Option<&mut Bencher>) {
    let mut graph = new_graph(Progcaster::new(&mut allocator));
    graph.add_scope(BarrierScope { epoch: 0, ready: true, degree: allocator.peers(), ttl: 1000000 });
    graph.connect(ScopeOutput(0, 0), ScopeInput(0, 0));

    // start things up!
    graph.borrow_mut().get_internal_summary();
    graph.borrow_mut().set_external_summary(Vec::new(), &mut Vec::new());
    graph.borrow_mut().push_external_progress(&mut Vec::new());

    // spin
    match bencher {
        Some(b) => b.iter(|| { graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()); }),
        None    => while graph.borrow_mut().pull_internal_progress(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()) { },
    }
}
<|endoftext|>


<commit_before>// Unseemly is a "core" typed language with (typed!) macros.
// You shouldn't write code in Unseemly.
// Instead, you should implement your programming language as Unseemly macros.

#![allow(dead_code,non_snake_case,unused_imports,non_upper_case_globals, unused_macros)]
// dead_code and unused_macros are hopefully temporary allowances
// non_snake_case is stylistic, unused_imports is inaccurate because of macros
// non_upper_case_globals is stylistic; I like my thread_local!s lowercase.

// unstable; only for testing
// #![feature(log_syntax,trace_macros)]
// trace_macros!(true);

// unstable; only for testing
// #[macro_use] extern crate log;
#[macro_use] extern crate lazy_static;
extern crate num;
#[macro_use] extern crate custom_derive;
#[macro_use] extern crate mac;
#[macro_use] extern crate quote;
extern crate rustyline;
extern crate regex;

use std::path::Path;
use std::fs::File;
use std::io::Read;

mod macros;

mod name; // should maybe be moved to `util`; `mbe` needs it

mod util;

mod beta;
mod read;
mod ast;

mod earley;
mod parse;

mod form;

mod ast_walk;
mod ty;
mod ty_compare;

mod runtime;

mod core_type_forms;
mod core_forms;

use runtime::reify::Reifiable;

use runtime::core_values;
use std::cell::RefCell;
use util::assoc::Assoc;
use name::{Name, n};
use ty::Ty;
use ast::Ast;
use runtime::eval::{eval, Value};
use std::io::BufRead;

thread_local! {
    pub static ty_env : RefCell<Assoc<Name, Ty>> = RefCell::new(core_values::core_types());
    pub static val_env : RefCell<Assoc<Name, Value>> = RefCell::new(core_values::core_values());
}

struct ValueCompleter {}

impl rustyline::completion::Completer for ValueCompleter {
    fn complete(&self, line: &str, pos: usize)
            -> Result<(usize, Vec<String>), rustyline::error::ReadlineError> {
        let mut break_chars = std::collections::BTreeSet::new();
        break_chars.extend(vec!['[', '(', '{', ' ', '}', ')',  ']'].iter());
        let mut res = vec![];
        let (start, word_so_far) = rustyline::completion::extract_word(line, pos, &break_chars);
        val_env.with(|vals| {
            let vals = vals.borrow();
            for k in vals.iter_keys() {
                if k.sp().starts_with(word_so_far) { res.push(k.sp()); }
            }
        });
        Ok((start, res))
    }
}

fn main() {
    let arguments : Vec<String> = std::env::args().collect();
    let prelude_filename = format!("{}/.unseemly_prelude",
                                   std::env::home_dir().unwrap().display());
    let history_filename = format!("{}/.unseemly_history",
                                   std::env::home_dir().unwrap().display());

    if arguments.len() == 1 {
        let mut rl = rustyline::Editor::<ValueCompleter>::new();
        rl.set_completer(Some(ValueCompleter{}));

        let just_type = regex::Regex::new("^:t (.*)$").unwrap();
        let assign_value = regex::Regex::new("^(\\w+)\\s*:=(.*)$").unwrap();
        let save_value = regex::Regex::new("^:s +(\\w+\\s*:=(.*))$").unwrap();

        print!("\n");
        print!("                  \x1b[1;32mUnseemly\x1b[0m\n");
        print!("    `<name> := <expr>` to bind a name for this session.\n");
        print!("    `:s <name> := <expr>` to save a binding to the prelude for the future.\n");
        print!("    `:t <expr>` to synthesize the type of <expr>.\n");
        print!("    Command history is saved over sessions.\n");
        print!("    Tab-completion works on variables, and many Bash-isms work.\n");

        if let Ok(prelude_file) = File::open(&Path::new(&prelude_filename)) {
            let prelude = std::io::BufReader::new(prelude_file);
            for line in prelude.lines() {
                let line = line.unwrap();
                if let Some(caps) = assign_value.captures(&line) {
                    if let Err(e) = assign_variable(caps.at(1).unwrap(), caps.at(2).unwrap()) {
                        print!("    Error in prelude line: {}\n    {}\n", line, e);
                    }
                }
            }
            print!("    [prelude loaded]\n");
        }

        let _ = rl.load_history(&history_filename);
        while let Ok(line) = rl.readline("\x1b[1;36m≫\x1b[0m ") {
            // TODO: count delimiters, and allow line continuation!
            rl.add_history_entry(&line);

            let result_display = if let Some(caps) = just_type.captures(&line) {
                type_unseemly_program(caps.at(1).unwrap()).map(|x| format!("{}", x))
            } else if let Some(caps) = assign_value.captures(&line) {
                assign_variable(caps.at(1).unwrap(), caps.at(2).unwrap()).map(|x| format!("{}", x))
            } else if let Some(caps) = save_value.captures(&line) {
                match eval_unseemly_program(caps.at(2).unwrap()) {
                    Ok(_) => {
                        use std::io::Write;
                        let mut prel_file = ::std::fs::OpenOptions::new().create(true).append(true)
                            .open(&prelude_filename).unwrap();
                        writeln!(prel_file, "{}", caps.at(1).unwrap()).unwrap();
                        Ok(format!("[saved to {}]", &prelude_filename))
                    }
                    Err(e) => Err(e)
                }
            } else {
                eval_unseemly_program(&line).map(|x| format!("{}", x))
            };

            match result_display {
                Ok(v) => print!("\x1b[1;32m≉\x1b[0m {}\n", v),
                Err(s) => print!("\x1b[1;31m✘\x1b[0m {}\n", s)
            }
        }
        let _ = rl.save_history(&history_filename).unwrap();
    } else {
        let ref filename = arguments[1];

        let mut raw_input = String::new();
        File::open(&Path::new(filename))
            .expect("Error opening file")
            .read_to_string(&mut raw_input)
            .expect("Error reading file");

        let result = eval_unseemly_program(&raw_input);

        print!("{:?}\n", result);
    }
}

fn assign_variable(name: &str, expr: &str) -> Result<Value, String> {
    let res = eval_unseemly_program(expr);

    if let Ok(ref v) = res {
        let ty = type_unseemly_program(expr).unwrap();
        ty_env.with(|tys| {
            val_env.with(|vals| {
                let new_tys = tys.borrow().set(n(name), ty).clone();
                let new_vals = vals.borrow().set(n(name), v.clone());
                *tys.borrow_mut() = new_tys;
                *vals.borrow_mut() = new_vals;
            })
        })
    }
    res
}

fn type_unseemly_program(program: &str) -> Result<ty::Ty, String> {
    let tokens = try!(read::read_tokens(program));

    let ast = try!(
        parse::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens)
            .map_err(|e| e.msg));

    ty_env.with(|types| {
        ty::synth_type(&ast, types.borrow().clone()).map_err(|e| format!("{:?}", e))
    })
}

fn eval_unseemly_program(program: &str) -> Result<runtime::eval::Value, String> {
    let tokens = try!(read::read_tokens(program));

    let ast : ::ast::Ast = try!(
        parse::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens)
            .map_err(|e| e.msg));

    let _type = try!(ty_env.with(|types| {
        ty::synth_type(&ast, types.borrow().clone()).map_err(|e| format!("{:?}", e))
    }));

    val_env.with(|vals| {
        runtime::eval::eval(&ast, vals.borrow().clone()).map_err(|_| "???".to_string())
    })
}

#[test]
fn end_to_end_eval() {
    assert_eq!(eval_unseemly_program("(zero? zero)"), Ok(val!(b true)));

    assert_eq!(eval_unseemly_program("(plus one one)"), Ok(val!(i 2)));

    assert_eq!(eval_unseemly_program("(.[x : int  y : int . (plus x y)]. one one)"),
               Ok(val!(i 2)));
}
<commit_msg>Add (saveable) type definition to the REPL.<commit_after>// Unseemly is a "core" typed language with (typed!) macros.
// You shouldn't write code in Unseemly.
// Instead, you should implement your programming language as Unseemly macros.

#![allow(dead_code,non_snake_case,unused_imports,non_upper_case_globals, unused_macros)]
// dead_code and unused_macros are hopefully temporary allowances
// non_snake_case is stylistic, unused_imports is inaccurate because of macros
// non_upper_case_globals is stylistic; I like my thread_local!s lowercase.

// unstable; only for testing
// #![feature(log_syntax,trace_macros)]
// trace_macros!(true);

// unstable; only for testing
// #[macro_use] extern crate log;
#[macro_use] extern crate lazy_static;
extern crate num;
#[macro_use] extern crate custom_derive;
#[macro_use] extern crate mac;
#[macro_use] extern crate quote;
extern crate rustyline;
extern crate regex;

use std::path::Path;
use std::fs::File;
use std::io::Read;

mod macros;

mod name; // should maybe be moved to `util`; `mbe` needs it

mod util;

mod beta;
mod read;
mod ast;

mod earley;
mod parse;

mod form;

mod ast_walk;
mod ty;
mod ty_compare;

mod runtime;

mod core_type_forms;
mod core_forms;

use runtime::reify::Reifiable;

use runtime::core_values;
use std::cell::RefCell;
use util::assoc::Assoc;
use name::{Name, n};
use ty::Ty;
use ast::Ast;
use runtime::eval::{eval, Value};
use std::io::BufRead;

thread_local! {
    pub static ty_env : RefCell<Assoc<Name, Ty>> = RefCell::new(core_values::core_types());
    pub static val_env : RefCell<Assoc<Name, Value>> = RefCell::new(core_values::core_values());
}

struct ValueCompleter {}

impl rustyline::completion::Completer for ValueCompleter {
    fn complete(&self, line: &str, pos: usize)
            -> Result<(usize, Vec<String>), rustyline::error::ReadlineError> {
        let mut break_chars = std::collections::BTreeSet::new();
        break_chars.extend(vec!['[', '(', '{', ' ', '}', ')',  ']'].iter());
        let mut res = vec![];
        let (start, word_so_far) = rustyline::completion::extract_word(line, pos, &break_chars);
        val_env.with(|vals| {
            let vals = vals.borrow();
            for k in vals.iter_keys() {
                if k.sp().starts_with(word_so_far) { res.push(k.sp()); }
            }
        });
        Ok((start, res))
    }
}

fn main() {
    let arguments : Vec<String> = std::env::args().collect();
    let prelude_filename = format!("{}/.unseemly_prelude",
                                   std::env::home_dir().unwrap().display());
    let history_filename = format!("{}/.unseemly_history",
                                   std::env::home_dir().unwrap().display());

    if arguments.len() == 1 {
        let mut rl = rustyline::Editor::<ValueCompleter>::new();
        rl.set_completer(Some(ValueCompleter{}));

        let just_type = regex::Regex::new("^:t (.*)$").unwrap();
        let assign_value = regex::Regex::new("^(\\w+)\\s*:=(.*)$").unwrap();
        let save_value = regex::Regex::new("^:s +((\\w+)\\s*:=(.*))$").unwrap();
        let assign_type = regex::Regex::new("^(\\w+)\\s*t=(.*)$").unwrap();
        let save_type = regex::Regex::new("^:s +((\\w+)\\s*t=(.*))$").unwrap();

        print!("\n");
        print!("                  \x1b[1;32mUnseemly\x1b[0m\n");
        print!("    `<name> := <expr>` to bind a name for this session.\n");
        print!("    `:t <expr>` to synthesize the type of <expr>.\n");
        print!("    `<name> t= <type>` to bind a type for this session.\n");
        print!("    `:s <name> := <expr>` to save a binding to the prelude for the future.\n");
        print!("    `:s <name> t= <expr>` to save a type binding to the prelude.\n");
        print!("    Command history is saved over sessions.\n");
        print!("    Tab-completion works on variables, and many Bash-isms work.\n");
        print!("\n");

        if let Ok(prelude_file) = File::open(&Path::new(&prelude_filename)) {
            let prelude = std::io::BufReader::new(prelude_file);
            for line in prelude.lines() {
                let line = line.unwrap();
                if let Some(caps) = assign_value.captures(&line) {
                    if let Err(e) = assign_variable(caps.at(1).unwrap(), caps.at(2).unwrap()) {
                        print!("    Error in prelude line: {}\n    {}\n", line, e);
                    }
                } else if let Some(caps) = assign_type.captures(&line) {
                    if let Err(e) = assign_t_var(caps.at(1).unwrap(), caps.at(2).unwrap()) {
                        print!("    Error in prelude line: {}\n    {}\n", line, e);
                    }
                }
            }
            print!("    [prelude loaded from {}]\n", prelude_filename);
        }

        let _ = rl.load_history(&history_filename);
        while let Ok(line) = rl.readline("\x1b[1;36m≫\x1b[0m ") {
            // TODO: count delimiters, and allow line continuation!
            rl.add_history_entry(&line);

            let result_display = if let Some(caps) = just_type.captures(&line) {
                type_unseemly_program(caps.at(1).unwrap()).map(|x| format!("{}", x))
            } else if let Some(caps) = assign_value.captures(&line) {
                assign_variable(caps.at(1).unwrap(), caps.at(2).unwrap()).map(|x| format!("{}", x))
            } else if let Some(caps) = save_value.captures(&line) {
                match assign_variable(caps.at(2).unwrap(), caps.at(3).unwrap()) {
                    Ok(_) => {
                        use std::io::Write;
                        let mut prel_file = ::std::fs::OpenOptions::new().create(true).append(true)
                            .open(&prelude_filename).unwrap();
                        writeln!(prel_file, "{}", caps.at(1).unwrap()).unwrap();
                        Ok(format!("[saved to {}]", &prelude_filename))
                    }
                    Err(e) => Err(e)
                }
            } else if let Some(caps) = assign_type.captures(&line) {
                assign_t_var(caps.at(1).unwrap(), caps.at(2).unwrap()).map(|x| format!("{}", x))
            } else if let Some(caps) = save_type.captures(&line) {
                match assign_t_var(caps.at(2).unwrap(), caps.at(3).unwrap()) {
                    Ok(_) => {
                        use std::io::Write;
                        let mut prel_file = ::std::fs::OpenOptions::new().create(true).append(true)
                            .open(&prelude_filename).unwrap();
                        writeln!(prel_file, "{}", caps.at(1).unwrap()).unwrap();
                        Ok(format!("[saved to {}]", &prelude_filename))
                    }
                    Err(e) => Err(e)
                }
            } else {
                eval_unseemly_program(&line).map(|x| format!("{}", x))
            };

            match result_display {
                Ok(v) => print!("\x1b[1;32m≉\x1b[0m {}\n", v),
                Err(s) => print!("\x1b[1;31m✘\x1b[0m {}\n", s)
            }
        }
        let _ = rl.save_history(&history_filename).unwrap();
    } else {
        let ref filename = arguments[1];

        let mut raw_input = String::new();
        File::open(&Path::new(filename))
            .expect("Error opening file")
            .read_to_string(&mut raw_input)
            .expect("Error reading file");

        let result = eval_unseemly_program(&raw_input);

        print!("{:?}\n", result);
    }
}

fn assign_variable(name: &str, expr: &str) -> Result<Value, String> {
    let res = eval_unseemly_program(expr);

    if let Ok(ref v) = res {
        let ty = type_unseemly_program(expr).unwrap();
        ty_env.with(|tys| {
            val_env.with(|vals| {
                let new_tys = tys.borrow().set(n(name), ty).clone();
                let new_vals = vals.borrow().set(n(name), v.clone());
                *tys.borrow_mut() = new_tys;
                *vals.borrow_mut() = new_vals;
            })
        })
    }
    res
}

fn assign_t_var(name: &str, expr: &str) -> Result<ty::Ty, String> {
    let tokens = try!(read::read_tokens(expr));

    let ast = try!(parse::parse(&parse::FormPat::Call(n("type")),
                                &core_forms::get_core_forms(), &tokens).map_err(|e| e.msg));

    let res = ty_env.with(|tys| {
        ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:?}", e))
    });

    if let Ok(ref t) = res {
        ty_env.with(|tys| {
            let new_tys = tys.borrow().set(n(name), t.clone());
            *tys.borrow_mut() = new_tys;
        })
    }

    res
}

fn type_unseemly_program(program: &str) -> Result<ty::Ty, String> {
    let tokens = try!(read::read_tokens(program));

    let ast = try!(
        parse::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens)
            .map_err(|e| e.msg));

    ty_env.with(|tys| {
        ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:?}", e))
    })
}

fn eval_unseemly_program(program: &str) -> Result<runtime::eval::Value, String> {
    let tokens = try!(read::read_tokens(program));

    let ast : ::ast::Ast = try!(
        parse::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens)
            .map_err(|e| e.msg));

    let _type = try!(ty_env.with(|tys| {
        ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:?}", e))
    }));

    val_env.with(|vals| {
        runtime::eval::eval(&ast, vals.borrow().clone()).map_err(|_| "???".to_string())
    })
}

#[test]
fn end_to_end_eval() {
    assert_eq!(eval_unseemly_program("(zero? zero)"), Ok(val!(b true)));

    assert_eq!(eval_unseemly_program("(plus one one)"), Ok(val!(i 2)));

    assert_eq!(eval_unseemly_program("(.[x : int  y : int . (plus x y)]. one one)"),
               Ok(val!(i 2)));
}
<|endoftext|>