<commit_before>use std::path::Path;
use std::fs;
use std::fs::File;
use std::io::BufReader;
use filetime::FileTime;
use filetime;

use std::io::Read;

use crypto::digest::Digest;
use crypto::sha2::Sha256;

pub fn store_file(store_path: &Path, source_file: &Path) -> String {
	let tmp_path = store_path.join("tmp");

	fs::copy(&source_file, &tmp_path);

	let hash = hash(&source_file);

	let final_path = store_path.join(&hash);

	fs::rename(&tmp_path, &final_path);

	hash
}

pub fn extract_file(store_path: &Path, hash: &String, data_path: &Path, filename: &String, timestamp: u64) {
	let file_in_store = store_path.join(hash);
	let file_in_wd = Path::new(filename);
	let tmp_path = data_path.join("tmp");

	println!("Extract from {} to {} ", file_in_store.to_str().unwrap(), file_in_wd.to_str().unwrap());

	fs::copy(&file_in_store, &tmp_path);
	fs::rename(&tmp_path, &file_in_wd);

	let seconds_since_1970 = FileTime::from_seconds_since_1970(timestamp, 0);
	filetime::set_file_times(&file_in_wd, seconds_since_1970, seconds_since_1970);
}

fn hash(file: &Path) -> String {
	let f = File::open(&file).unwrap();
	let mut reader = BufReader::new(f);
	let mut buffer = [0; 512];

	let mut hasher = Sha256::new();

	loop {
		let bytes_read = reader.read(&mut buffer).unwrap();
		if bytes_read == 512 {
			hasher.input(&buffer);
		} else {
			let v : Vec<u8> = buffer.iter().cloned().collect();
			let (x, _) = v.split_at(bytes_read);
			hasher.input(x);
		}
		if bytes_read == 0 { break; }
	}

	let hex = hasher.result_str();
	hex
}
<commit_msg>Remove double reading by creating a hash writer<commit_after>use std::path::Path;
use std::fs;
use std::fs::File;
use std::io::BufReader;
use std::io::BufWriter;
use filetime::FileTime;
use filetime;

use std::io;
use std::io::Result;
use std::io::Read;
use std::io::Write;

use crypto::digest::Digest;
use crypto::sha2::Sha256;

pub fn store_file(store_path: &Path, source_file: &Path) -> String {
	let tmp_path = store_path.join("tmp");

	let mut file_reader = BufReader::new(File::open(&source_file).unwrap());
	let mut file_writer = BufWriter::new(File::create(&tmp_path).unwrap());

	let mut hash_file_write = HashWriter::new(file_writer);

	io::copy(&mut file_reader, &mut hash_file_write);

	let hash = hash_file_write.get_hash();

	let final_path = store_path.join(&hash);
	fs::rename(&tmp_path, &final_path);

	hash
}

pub fn extract_file(store_path: &Path, hash: &String, data_path: &Path, filename: &String, timestamp: u64) {
	let file_in_store = store_path.join(hash);
	let file_in_wd = Path::new(filename);
	let tmp_path = data_path.join("tmp");

	println!("Extract from {} to {} ", file_in_store.to_str().unwrap(), file_in_wd.to_str().unwrap());

	fs::copy(&file_in_store, &tmp_path);
	fs::rename(&tmp_path, &file_in_wd);

	let seconds_since_1970 = FileTime::from_seconds_since_1970(timestamp, 0);
	filetime::set_file_times(&file_in_wd, seconds_since_1970, seconds_since_1970);
}

struct HashWriter<W: Write> {
	hasher: Sha256,
	writer: W
}

impl <W: Write> HashWriter<W>  {
	fn new(inner: W) -> HashWriter<W> {
		HashWriter {hasher: Sha256::new(), writer: inner}
	}
	fn get_hash(&mut self) -> String {
		self.hasher.result_str()
	}
}

impl <W: Write> Write for HashWriter<W>  {
	fn write(&mut self, buf: &[u8]) -> Result<usize> {
		self.hasher.input(&buf);
		self.writer.write(buf)
	}

	fn flush(&mut self) -> Result<()> {
		self.writer.flush()
	}
}
<|endoftext|>


<commit_before>use std::boxed::Box;
use std::collections::HashMap;

/// Create a type alias for the table containing the symbols
/// All items are acccessible via their name
/// The `T` is the value inherited from `Table`
pub type Container<'a, T: 'a> = HashMap<String, T>;

/// This is the generic container for all symbol tables
/// `T` must implement `ToJavaScript`
#[derive(Debug)]
pub struct Table<'a, T: 'a> {
    /// This is the optional parent struct.
    /// The parent will most likely have a value unless the scope is global
    parent: Option<Box<&'a Table<'a, T>>>,
    /// The container is the contents of the table
    container: Box<Container<'a, T>>,
}

impl<'a, T> Table<'a, T> {
    pub fn new(table: Option<Box<&'a Table<'a, T>>>) -> Table<'a, T> {
        Table {
            parent: table,
            container: Box::new(Container::new()),
        }
    }

    /// Insert a value into the table
    pub fn insert(&mut self, key: String, value: T) {
        self.container.insert(key, value);
    }

    /// Attempt to get a value from the table
    /// If the value can't be found within this scope. try the parent.
    /// After descending through all the scopes, return an undefiend error
    pub fn get(&self, key: &'a String) -> Option<&T> {
        // First we get from the local scope
        match self.container.get(key) {
            Some(value) => Some(value),
            None => self.parent.as_ref().and_then(|i| i.get(key)),
        }
    }
}
<commit_msg>Fix warning<commit_after>use std::boxed::Box;
use std::collections::HashMap;

/// Create a type alias for the table containing the symbols
/// All items are acccessible via their name
/// The `T` is the value inherited from `Table`
pub type Container<'a, T> = HashMap<String, T>;

/// This is the generic container for all symbol tables
/// `T` must implement `ToJavaScript`
#[derive(Debug)]
pub struct Table<'a, T: 'a> {
    /// This is the optional parent struct.
    /// The parent will most likely have a value unless the scope is global
    parent: Option<Box<&'a Table<'a, T>>>,
    /// The container is the contents of the table
    container: Box<Container<'a, T>>,
}

impl<'a, T> Table<'a, T> {
    pub fn new(table: Option<Box<&'a Table<'a, T>>>) -> Table<'a, T> {
        Table {
            parent: table,
            container: Box::new(Container::new()),
        }
    }

    /// Insert a value into the table
    pub fn insert(&mut self, key: String, value: T) {
        self.container.insert(key, value);
    }

    /// Attempt to get a value from the table
    /// If the value can't be found within this scope. try the parent.
    /// After descending through all the scopes, return an undefiend error
    pub fn get(&self, key: &'a String) -> Option<&T> {
        // First we get from the local scope
        match self.container.get(key) {
            Some(value) => Some(value),
            None => self.parent.as_ref().and_then(|i| i.get(key)),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This test is brittle!
// xfail-pretty - the pretty tests lose path information, breaking include!

pub mod m1 {
    pub mod m2 {
        pub fn where_am_i() -> ~str { (module_path!()).to_owned() }
    }
}

pub fn main() {
    assert(line!() == 21);
    assert(col!() == 11);
    assert(file!().to_owned().ends_with(~"syntax-extension-source-utils.rs"));
    assert(stringify!((2*3) + 5).to_owned() == ~"( 2 * 3 ) + 5");
    assert(include!("syntax-extension-source-utils-files/includeme.fragment").to_owned()
           == ~"victory robot 6");

    assert(
        include_str!("syntax-extension-source-utils-files/includeme.fragment").to_owned()
        .starts_with(~"/* this is for "));
    assert(
        include_bin!("syntax-extension-source-utils-files/includeme.fragment")
        [1] == (42 as u8)); // '*'
    // The Windows tests are wrapped in an extra module for some reason
    assert(m1::m2::where_am_i().ends_with(~"m1::m2"));
}
<commit_msg>Add test for using line! in a macro<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This test is brittle!
// xfail-pretty - the pretty tests lose path information, breaking include!

pub mod m1 {
    pub mod m2 {
        pub fn where_am_i() -> ~str { (module_path!()).to_owned() }
    }
}

macro_rules! indirect_line( () => ( line!() ) )

pub fn main() {
    assert(line!() == 23);
    assert(col!() == 11);
    assert(indirect_line!() == 25);
    assert(file!().to_owned().ends_with(~"syntax-extension-source-utils.rs"));
    assert(stringify!((2*3) + 5).to_owned() == ~"( 2 * 3 ) + 5");
    assert(include!("syntax-extension-source-utils-files/includeme.fragment").to_owned()
           == ~"victory robot 6");

    assert(
        include_str!("syntax-extension-source-utils-files/includeme.fragment").to_owned()
        .starts_with(~"/* this is for "));
    assert(
        include_bin!("syntax-extension-source-utils-files/includeme.fragment")
        [1] == (42 as u8)); // '*'
    // The Windows tests are wrapped in an extra module for some reason
    assert(m1::m2::where_am_i().ends_with(~"m1::m2"));
}
<|endoftext|>


<commit_before>use std::str::from_utf8;
use std::result::Result;
use nom::IResult;

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      u64,
    pub gid:      u64,
    pub size:     u64,
    pub mtime:    u64,
    pub chksum:   & 'a str,
    pub typeflag: TypeFlag,
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: u64,
    pub devminor: u64,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}

/* TODO: support vendor specific + sparse */
#[derive(Debug,PartialEq,Eq)]
pub enum TypeFlag {
    NormalFile,
    HardLink,
    SymbolicLink,
    CharacterSpecial,
    BlockSpecial,
    Directory,
    FIFO,
    ContiguousFile,
    GlobalExtendedHeaderWithMetadata,
    ExtendedHeaderWithMetadataForNext,
    VendorSpecific,
    Invalid
}

/* TODO: validation */
fn str_to_u64(s: &str, base: u64) -> u64 {
    let mut u = 0;
    let mut f = 1;

    for c in s.chars().rev().skip_while(|&c| c == '\0') {
        u += f * ((c as u64) - ('0' as u64));
        f *= base;
    }

    u
}

pub fn octal_to_u64(o: &str) -> u64 {
    str_to_u64(o, 8)
}

fn char_to_type_flag(c: char) -> TypeFlag {
    match c {
        '0' | '\0' => TypeFlag::NormalFile,
        '1' => TypeFlag::HardLink,
        '2' => TypeFlag::SymbolicLink,
        '3' => TypeFlag::CharacterSpecial,
        '4' => TypeFlag::BlockSpecial,
        '5' => TypeFlag::Directory,
        '6' => TypeFlag::FIFO,
        '7' => TypeFlag::ContiguousFile,
        'g' => TypeFlag::GlobalExtendedHeaderWithMetadata,
        'x' => TypeFlag::ExtendedHeaderWithMetadataForNext,
        'A' ... 'Z' => TypeFlag::VendorSpecific,
        _ => TypeFlag::Invalid
    }
}

macro_rules! take_str_eat_garbage (
 ( $i:expr, $size:expr ) => ( chain!( $i, s: map_res!(take_until!("\0"), from_utf8) ~ take!($size - s.len()), ||{ s } ));
);

fn parse_ustar00(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("00")                           ~
        uname:    take_str_eat_garbage!(32)  ~
        gname:    take_str_eat_garbage!(32)  ~
        devmajor: take_str_eat_garbage!(8)   ~
        devminor: take_str_eat_garbage!(8)   ~
        prefix:   take_str_eat_garbage!(155) ~
        take!(12), /* padding to 512 */
        ||{
            Some(UStarHeader {
                magic:    "ustar\0",
                version:  "00",
                uname:    uname,
                gname:    gname,
                devmajor: octal_to_u64(devmajor),
                devminor: octal_to_u64(devminor),
                prefix:   prefix
            })
        }
    )
}

fn parse_ustar(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("ustar\0") ~
        ustar: parse_ustar00,
        ||{
            ustar
        }
    )
}

fn parse_posix(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        take!(255), /* padding to 512 */
        ||{
            None
        }
    )
}

fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     take_str_eat_garbage!(100) ~
        mode:     take_str_eat_garbage!(8)   ~
        uid:      take_str_eat_garbage!(8)   ~
        gid:      take_str_eat_garbage!(8)   ~
        size:     take_str_eat_garbage!(12)  ~
        mtime:    take_str_eat_garbage!(12)  ~
        chksum:   take_str_eat_garbage!(8)   ~
        typeflag: take!(1)                   ~
        linkname: take_str_eat_garbage!(100) ~
        ustar:    alt!(parse_ustar | parse_posix),
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      octal_to_u64(uid),
                gid:      octal_to_u64(gid),
                size:     octal_to_u64(size),
                mtime:    octal_to_u64(mtime),
                chksum:   chksum,
                typeflag: char_to_type_flag(typeflag[0] as char),
                linkname: linkname,
                ustar:    ustar
            }
        }
    )
}

fn parse_contents(i: &[u8], size: u64) -> IResult<&[u8], &str> {
    let trailing = size % 512;
    let padding = match trailing {
        0 => 0,
        t => 512 - t
    };
    chain!(i,
        contents: take_str!(size as usize) ~
        take!(padding as usize),
        ||{
            contents
        }
    )
}

fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header:   parse_header ~
        contents: apply!(parse_contents, header.size),
        ||{
            TarEntry {
                header: header,
                contents: contents
            }
        }
    )
}

fn filter_entries(entries: Vec<TarEntry>) -> Result<Vec<TarEntry>, &'static str> {
    Ok(entries.into_iter().filter(|e| e.header.name != "").collect::<Vec<TarEntry>>())
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    map_res!(i, many0!(parse_entry), filter_entries)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn octal_to_u64_test() {
        match octal_to_u64("756") {
            494 => {},
            o => panic!("octal_to_u64 failed, expected 494 but got {}", o)
        }
        match octal_to_u64("") {
            0 => {},
            o => panic!("octal_to_u64 failed, expected 0 but got {}", o)
        }
    }
}
<commit_msg>tests: use assert_eq<commit_after>use std::str::from_utf8;
use std::result::Result;
use nom::IResult;

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      u64,
    pub gid:      u64,
    pub size:     u64,
    pub mtime:    u64,
    pub chksum:   & 'a str,
    pub typeflag: TypeFlag,
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: u64,
    pub devminor: u64,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}

/* TODO: support vendor specific + sparse */
#[derive(Debug,PartialEq,Eq)]
pub enum TypeFlag {
    NormalFile,
    HardLink,
    SymbolicLink,
    CharacterSpecial,
    BlockSpecial,
    Directory,
    FIFO,
    ContiguousFile,
    GlobalExtendedHeaderWithMetadata,
    ExtendedHeaderWithMetadataForNext,
    VendorSpecific,
    Invalid
}

/* TODO: validation */
fn str_to_u64(s: &str, base: u64) -> u64 {
    let mut u = 0;
    let mut f = 1;

    for c in s.chars().rev().skip_while(|&c| c == '\0') {
        u += f * ((c as u64) - ('0' as u64));
        f *= base;
    }

    u
}

pub fn octal_to_u64(o: &str) -> u64 {
    str_to_u64(o, 8)
}

fn char_to_type_flag(c: char) -> TypeFlag {
    match c {
        '0' | '\0' => TypeFlag::NormalFile,
        '1' => TypeFlag::HardLink,
        '2' => TypeFlag::SymbolicLink,
        '3' => TypeFlag::CharacterSpecial,
        '4' => TypeFlag::BlockSpecial,
        '5' => TypeFlag::Directory,
        '6' => TypeFlag::FIFO,
        '7' => TypeFlag::ContiguousFile,
        'g' => TypeFlag::GlobalExtendedHeaderWithMetadata,
        'x' => TypeFlag::ExtendedHeaderWithMetadataForNext,
        'A' ... 'Z' => TypeFlag::VendorSpecific,
        _ => TypeFlag::Invalid
    }
}

macro_rules! take_str_eat_garbage (
 ( $i:expr, $size:expr ) => ( chain!( $i, s: map_res!(take_until!("\0"), from_utf8) ~ take!($size - s.len()), ||{ s } ));
);

fn parse_ustar00(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("00")                           ~
        uname:    take_str_eat_garbage!(32)  ~
        gname:    take_str_eat_garbage!(32)  ~
        devmajor: take_str_eat_garbage!(8)   ~
        devminor: take_str_eat_garbage!(8)   ~
        prefix:   take_str_eat_garbage!(155) ~
        take!(12), /* padding to 512 */
        ||{
            Some(UStarHeader {
                magic:    "ustar\0",
                version:  "00",
                uname:    uname,
                gname:    gname,
                devmajor: octal_to_u64(devmajor),
                devminor: octal_to_u64(devminor),
                prefix:   prefix
            })
        }
    )
}

fn parse_ustar(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("ustar\0") ~
        ustar: parse_ustar00,
        ||{
            ustar
        }
    )
}

fn parse_posix(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        take!(255), /* padding to 512 */
        ||{
            None
        }
    )
}

fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     take_str_eat_garbage!(100) ~
        mode:     take_str_eat_garbage!(8)   ~
        uid:      take_str_eat_garbage!(8)   ~
        gid:      take_str_eat_garbage!(8)   ~
        size:     take_str_eat_garbage!(12)  ~
        mtime:    take_str_eat_garbage!(12)  ~
        chksum:   take_str_eat_garbage!(8)   ~
        typeflag: take!(1)                   ~
        linkname: take_str_eat_garbage!(100) ~
        ustar:    alt!(parse_ustar | parse_posix),
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      octal_to_u64(uid),
                gid:      octal_to_u64(gid),
                size:     octal_to_u64(size),
                mtime:    octal_to_u64(mtime),
                chksum:   chksum,
                typeflag: char_to_type_flag(typeflag[0] as char),
                linkname: linkname,
                ustar:    ustar
            }
        }
    )
}

fn parse_contents(i: &[u8], size: u64) -> IResult<&[u8], &str> {
    let trailing = size % 512;
    let padding = match trailing {
        0 => 0,
        t => 512 - t
    };
    chain!(i,
        contents: take_str!(size as usize) ~
        take!(padding as usize),
        ||{
            contents
        }
    )
}

fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header:   parse_header ~
        contents: apply!(parse_contents, header.size),
        ||{
            TarEntry {
                header: header,
                contents: contents
            }
        }
    )
}

fn filter_entries(entries: Vec<TarEntry>) -> Result<Vec<TarEntry>, &'static str> {
    Ok(entries.into_iter().filter(|e| e.header.name != "").collect::<Vec<TarEntry>>())
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    map_res!(i, many0!(parse_entry), filter_entries)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn octal_to_u64_test() {
        assert_eq!(octal_to_u64("756"), 494);
        assert_eq!(octal_to_u64(""), 0);
    }
}
<|endoftext|>


<commit_before>use ast;
use std::{io, mem};
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::rc::Rc;

////////////////////////////////////////////////////////////////////////////////
// Interned strings
////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Name(u32);

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct RcString(Rc<String>);

impl From<String> for RcString {
    fn from(s: String) -> Self {
        RcString(Rc::new(s))
    }
}

impl<'a> From<&'a str> for RcString {
    fn from(s: &str) -> Self {
        RcString::from(String::from(s))
    }
}

impl Borrow<str> for RcString {
    fn borrow(&self) -> &str {
        &self.0
    }
}

#[derive(Debug)]
struct StrInterner {
    map: RefCell<HashMap<RcString, Name>>,
    vec: RefCell<Vec<RcString>>,
}

impl StrInterner {
    fn new() -> Self {
        StrInterner {
            map: RefCell::new(HashMap::new()),
            vec: RefCell::new(Vec::new()),
        }
    }

    fn intern(&self, string: &str) -> Name {
        let mut map = self.map.borrow_mut();
        let mut vec = self.vec.borrow_mut();

        if let Some(&idx) = map.get(string) {
            return idx;
        }

        let new_idx = Name(vec.len() as u32);
        let rc_str = RcString::from(string);
        map.insert(rc_str.clone(), new_idx);
        vec.push(rc_str);
        new_idx
    }
}

fn interner() -> Rc<StrInterner> {
    thread_local!(static KEY: Rc<StrInterner> = Rc::new(StrInterner::new()));
    KEY.with(|k| k.clone())
}

////////////////////////////////////////////////////////////////////////////////
// Lexer
////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Debug)]
pub struct Token {
    pub kind: TokenKind,
    pub span: Span,
}

impl Token {
    pub fn new(start: usize, end: usize, kind: TokenKind) -> Self {
        Token { kind: kind, span: Span::new(start, end) }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TokenKind {
    Symbol(Name),
    StrLit(String),
    ParenL,
    ParenR,
    BraceL,
    BraceR,
    BracketL,
    BracketR,
    Semicolon,
    Eof,
    Invalid,
}

/// A reference to a contiguous region of a source file.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Span {
    pub start: usize,
    pub end: usize,
}

impl Span {
    pub fn new(start: usize, end: usize) -> Self {
        Span { start: start, end: end }
    }

    pub fn point(pos: usize) -> Self {
        Span { start: pos, end: pos }
    }
}

#[derive(Clone, Debug)]
pub struct Lexer<'src> {
    source: &'src str,
    position: usize,
    token_start: usize,
    current: Option<char>,
}

impl<'src> Lexer<'src> {
    pub fn new(source: &'src str) -> Self {
        let mut lexer = Lexer { source: source, position: 0, token_start: 0, current: None };
        lexer.current = lexer.char_at(0);
        lexer
    }

    pub fn next(&mut self) -> Token {
        use self::TokenKind::*;

        self.token_start = self.position;

        let c = match self.current {
            Some(c) => c,
            None => return Token::new(self.position, self.position, TokenKind::Eof),
        };

        self.advance();

        let kind = match c {
            '"' => StrLit(self.lex_string()),
            '(' => ParenL,
            ')' => ParenR,
            '{' => BraceL,
            '}' => BraceR,
            '[' => BracketL,
            ']' => BracketR,
            ';' => Semicolon,
            _ => Invalid,
        };

        Token::new(self.token_start, self.position, kind)
    }

    /// Lex a string, assuming the initial " has already been consumed.
    fn lex_string(&mut self) -> String {
        let mut string = String::new();

        loop {
            let c = match self.current {
                Some(c) => c,
                None => {
                    errors().report(Span::point(self.position),
                                    ErrorLevel::Error,
                                    "unterminated string literal");
                    errors().report(Span::point(self.token_start),
                                    ErrorLevel::Note,
                                    "string literal began here");
                    break;
                }
            };

            let escape_start = self.position;
            self.advance();

            match c {
                '\\' => {
                    let escaped = match self.current { Some(c) => c, None => break };
                    self.advance();

                    string.push(match escaped {
                        '\\' | '"' => escaped,
                        'n' => '\n',
                        'r' => '\r',
                        't' => '\t',
                        c => {
                            errors().report(Span::new(escape_start, self.position),
                                            ErrorLevel::Error,
                                            format!("invalid character escape: {}", c));
                            c
                        }
                    });
                }

                '"' => break,
                c => string.push(c),
            }
        }

        string
    }

    // fn report_error<S: Into<String>>(&self, message: S) {
    //     errors().report(Span::new(self.token_start, self.position), message.into());
    // }

    fn peek(&self) -> Option<char> {
        self.current.and_then(|c| self.char_at(self.position + c.len_utf8()))
    }

    fn char_at(&self, pos: usize) -> Option<char> {
        self.source[pos..].chars().next()
    }

    /// Move to the next char in the source code.
    fn advance(&mut self) {
        if let Some(c) = self.current {
            self.position += c.len_utf8();
            self.current = self.char_at(self.position);
        } else {
            panic!("lexer attempted to advance past the end of the source code");
        }
    }
}

pub fn tokenize(source: &str) -> Vec<Token> {
    let mut lexer = Lexer::new(source);
    vec![]
}

////////////////////////////////////////////////////////////////////////////////
// Parser
////////////////////////////////////////////////////////////////////////////////

// #[derive(Clone, Debug)]
// struct Parser<'src> {
//     lexer: Lexer<'src>,
//     errors: Vec<Error>,
// }

    // fn parse_module(&mut self) -> ParseResult<ast::Module> {
    //     let mut fns = Vec::new();
    //     while self.token != Token::Eof {
    //         fns.push(try!(self.parse_fn_def()));
    //     }
    //     Ok(ast::Module { fns: fns })
    // }

    // fn parse_fn_def(&mut self) -> ParseResult<ast::FnDef> {
    //     try!(self.expect(&Token::KeywordFn));

    //     let name = match self.advance_and_get() {
    //         Token::Ident(name) => name,

    //         // TODO(tsion): Add an error to self.errors.
    //         _ => return Err(()),
    //     };

    //     try!(self.expect(&Token::ParenL));

    //     // TODO(tsion): Parse arguments.

    //     try!(self.expect(&Token::ParenR));
    //     try!(self.expect(&Token::BraceL));

    //     let mut body = Vec::new();

    //     while self.token != Token::BraceR {
    //         body.push(try!(self.parse_expr()));
    //     }

    //     // Skip the closing brace.
    //     self.advance();

    //     Ok(ast::FnDef {
    //         name: name,
    //         return_ty: ast::Type::Unit,
    //         args: Vec::new(),
    //         body: body,
    //     })
    // }

    // fn parse_expr(&mut self) -> ParseResult<ast::Expr> {
    //     let func = match self.advance_and_get() {
    //         Token::Ident(name) => name,

    //         // TODO(tsion): Add an error to self.errors.
    //         _ => return Err(()),
    //     };

    //     try!(self.expect(&Token::ParenL));

    //     let str = match self.advance_and_get() {
    //         Token::StrLit(str) => str,

    //         // TODO(tsion): Add an error to self.errors.
    //         _ => return Err(()),
    //     };

    //     try!(self.expect(&Token::ParenR));
    //     try!(self.expect(&Token::Semicolon));

    //     Ok(ast::Expr::FnCall {
    //         func: func,
    //         args: vec![ast::Expr::StrLit(str)],
    //     })
    // }

////////////////////////////////////////////////////////////////////////////////
// Character classes
////////////////////////////////////////////////////////////////////////////////

/// Returns `true` if the given character is whitespace.
fn is_whitespace(c: char) -> bool {
    match c {
        ' ' | '\t' | '\r' | '\n' => true,
        _ => false,
    }
}

/// Returns `true` if the given character is a digit.
fn is_digit(c: char) -> bool {
    match c {
        '0'...'9' => true,
        _ => false,
    }
}

/// Returns `true` if the given character is valid at the start of an identifier.
fn is_ident_start(c: char) -> bool {
    match c {
        'a'...'z' | 'A'...'Z' | '_' => true,
        _ => false,
    }
}

/// Returns `true` if the given character is valid after the start of an identifier.
fn is_ident_continue(c: char) -> bool {
    is_ident_start(c) || is_digit(c)
}

////////////////////////////////////////////////////////////////////////////////
// Errors
////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Error {
    pub message: String,
    pub level: ErrorLevel,
    pub span: Span,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ErrorLevel {
    Error,
    Warning,
    Note,
}

pub struct ErrorReporter {
    pub errors: RefCell<Vec<Error>>,
}

impl ErrorReporter {
    fn new() -> Self {
        ErrorReporter { errors: RefCell::new(Vec::new()) }
    }

    fn report<S: Into<String>>(&self, span: Span, level: ErrorLevel, message: S) {
        self.errors.borrow_mut().push(Error {
            message: message.into(),
            level: level,
            span: span,
        });
    }

    fn is_empty(&self) -> bool {
        self.errors.borrow().is_empty()
    }

    fn reset(&self) {
        self.errors.borrow_mut().clear();
    }
}

pub fn errors() -> Rc<ErrorReporter> {
    thread_local!(static KEY: Rc<ErrorReporter> = Rc::new(ErrorReporter::new()));
    KEY.with(|k| k.clone())
}

#[cfg(test)]
mod test {
    use super::{errors, ErrorLevel, Lexer, Span, TokenKind};
    use super::ErrorLevel::*;
    use super::TokenKind::*;

    #[test]
    fn lex_empty_string() {
        lexer_test(r#""""#, StrLit(String::from("")), &[]);
    }

    #[test]
    fn lex_unterminated_empty_string() {
        lexer_test(r#"""#, StrLit(String::from("")), &[
            (1, 1, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_unterminated_escape_empty_string() {
        lexer_test(r#""\"#, StrLit(String::from("")), &[
            (2, 2, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_unterminated_escape_string() {
        lexer_test(r#""foo\"#, StrLit(String::from("foo")), &[
            (5, 5, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_string() {
        lexer_test(r#""foobar""#, StrLit(String::from("foobar")), &[]);
    }

    #[test]
    fn lex_unterminated_string() {
        lexer_test(r#""foobar"#, StrLit(String::from("foobar")), &[
            (7, 7, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_string_with_just_newline() {
        lexer_test(r#""\n""#, StrLit(String::from("\n")), &[]);
    }

    #[test]
    fn lex_unterminated_string_with_just_newline() {
        lexer_test(r#""\n"#, StrLit(String::from("\n")), &[
            (3, 3, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_string_with_newline() {
        lexer_test(r#""foo\nbar""#, StrLit(String::from("foo\nbar")), &[]);
    }

    #[test]
    fn lex_unterminated_string_with_newline() {
        lexer_test(r#""foo\nbar"#, StrLit(String::from("foo\nbar")), &[
            (9, 9, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_invalid_escape_string() {
        lexer_test(r#""foo\cbar""#, StrLit(String::from("foocbar")), &[
            (4, 6, Error, "invalid character escape: c"),
        ]);
    }

    #[test]
    fn lex_unterminated_invalid_escape_string() {
        lexer_test(r#""foo\cbar"#, StrLit(String::from("foocbar")), &[
            (4, 6, Error, "invalid character escape: c"),
            (9, 9, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    fn lexer_test(source: &str,
                  expected: TokenKind,
                  expected_errors: &[(usize, usize, ErrorLevel, &str)]) {
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.next().kind, expected);
        assert_errors(expected_errors);
    }

    fn assert_errors(expected_errors: &[(usize, usize, ErrorLevel, &str)]) {
        let reporter = errors();

        {
            let errors = reporter.errors.borrow();

            for (i, &(start, end, level, message)) in expected_errors.iter().enumerate() {
                assert_eq!(errors[i], super::Error {
                    message: String::from(message),
                    level: level,
                    span: Span::new(start, end),
                });
            }

            // Checking that there are no unexpected errors this way will pretty-print the
            // unexpected errors if they exist.
            assert_eq!(&errors[expected_errors.len()..], &[]);
        }

        reporter.reset();
    }
}
<commit_msg>Fix missing errors for character escape before end of file.<commit_after>use ast;
use std::{io, mem};
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::rc::Rc;

////////////////////////////////////////////////////////////////////////////////
// Interned strings
////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Name(u32);

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct RcString(Rc<String>);

impl From<String> for RcString {
    fn from(s: String) -> Self {
        RcString(Rc::new(s))
    }
}

impl<'a> From<&'a str> for RcString {
    fn from(s: &str) -> Self {
        RcString::from(String::from(s))
    }
}

impl Borrow<str> for RcString {
    fn borrow(&self) -> &str {
        &self.0
    }
}

#[derive(Debug)]
struct StrInterner {
    map: RefCell<HashMap<RcString, Name>>,
    vec: RefCell<Vec<RcString>>,
}

impl StrInterner {
    fn new() -> Self {
        StrInterner {
            map: RefCell::new(HashMap::new()),
            vec: RefCell::new(Vec::new()),
        }
    }

    fn intern(&self, string: &str) -> Name {
        let mut map = self.map.borrow_mut();
        let mut vec = self.vec.borrow_mut();

        if let Some(&idx) = map.get(string) {
            return idx;
        }

        let new_idx = Name(vec.len() as u32);
        let rc_str = RcString::from(string);
        map.insert(rc_str.clone(), new_idx);
        vec.push(rc_str);
        new_idx
    }
}

fn interner() -> Rc<StrInterner> {
    thread_local!(static KEY: Rc<StrInterner> = Rc::new(StrInterner::new()));
    KEY.with(|k| k.clone())
}

////////////////////////////////////////////////////////////////////////////////
// Lexer
////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Debug)]
pub struct Token {
    pub kind: TokenKind,
    pub span: Span,
}

impl Token {
    pub fn new(start: usize, end: usize, kind: TokenKind) -> Self {
        Token { kind: kind, span: Span::new(start, end) }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TokenKind {
    Symbol(Name),
    StrLit(String),
    ParenL,
    ParenR,
    BraceL,
    BraceR,
    BracketL,
    BracketR,
    Semicolon,
    Eof,
    Invalid,
}

/// A reference to a contiguous region of a source file.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Span {
    pub start: usize,
    pub end: usize,
}

impl Span {
    pub fn new(start: usize, end: usize) -> Self {
        Span { start: start, end: end }
    }

    pub fn point(pos: usize) -> Self {
        Span { start: pos, end: pos }
    }
}

#[derive(Clone, Debug)]
pub struct Lexer<'src> {
    source: &'src str,
    position: usize,
    token_start: usize,
    current: Option<char>,
}

impl<'src> Lexer<'src> {
    pub fn new(source: &'src str) -> Self {
        let mut lexer = Lexer { source: source, position: 0, token_start: 0, current: None };
        lexer.current = lexer.char_at(0);
        lexer
    }

    pub fn next(&mut self) -> Token {
        use self::TokenKind::*;

        self.token_start = self.position;

        let c = match self.current {
            Some(c) => c,
            None => return Token::new(self.position, self.position, TokenKind::Eof),
        };

        self.advance();

        let kind = match c {
            '"' => StrLit(self.lex_string()),
            '(' => ParenL,
            ')' => ParenR,
            '{' => BraceL,
            '}' => BraceR,
            '[' => BracketL,
            ']' => BracketR,
            ';' => Semicolon,
            _ => Invalid,
        };

        Token::new(self.token_start, self.position, kind)
    }

    /// Lex a string, assuming the initial " has already been consumed.
    fn lex_string(&mut self) -> String {
        let mut string = String::new();

        loop {
            let c = match self.current {
                Some(c) => c,
                None => {
                    errors().report(Span::point(self.position),
                                    ErrorLevel::Error,
                                    "unterminated string literal");
                    errors().report(Span::point(self.token_start),
                                    ErrorLevel::Note,
                                    "string literal began here");
                    break;
                }
            };

            let escape_start = self.position;
            self.advance();

            match c {
                '\\' => {
                    let escaped = match self.current { Some(c) => c, None => continue };
                    self.advance();

                    string.push(match escaped {
                        '\\' | '"' => escaped,
                        'n' => '\n',
                        'r' => '\r',
                        't' => '\t',
                        c => {
                            errors().report(Span::new(escape_start, self.position),
                                            ErrorLevel::Error,
                                            format!("invalid character escape: {}", c));
                            c
                        }
                    });
                }

                '"' => break,
                c => string.push(c),
            }
        }

        string
    }

    // fn report_error<S: Into<String>>(&self, message: S) {
    //     errors().report(Span::new(self.token_start, self.position), message.into());
    // }

    fn peek(&self) -> Option<char> {
        self.current.and_then(|c| self.char_at(self.position + c.len_utf8()))
    }

    fn char_at(&self, pos: usize) -> Option<char> {
        self.source[pos..].chars().next()
    }

    /// Move to the next char in the source code.
    fn advance(&mut self) {
        if let Some(c) = self.current {
            self.position += c.len_utf8();
            self.current = self.char_at(self.position);
        } else {
            panic!("lexer attempted to advance past the end of the source code");
        }
    }
}

pub fn tokenize(source: &str) -> Vec<Token> {
    let mut lexer = Lexer::new(source);
    vec![]
}

////////////////////////////////////////////////////////////////////////////////
// Parser
////////////////////////////////////////////////////////////////////////////////

// #[derive(Clone, Debug)]
// struct Parser<'src> {
//     lexer: Lexer<'src>,
//     errors: Vec<Error>,
// }

    // fn parse_module(&mut self) -> ParseResult<ast::Module> {
    //     let mut fns = Vec::new();
    //     while self.token != Token::Eof {
    //         fns.push(try!(self.parse_fn_def()));
    //     }
    //     Ok(ast::Module { fns: fns })
    // }

    // fn parse_fn_def(&mut self) -> ParseResult<ast::FnDef> {
    //     try!(self.expect(&Token::KeywordFn));

    //     let name = match self.advance_and_get() {
    //         Token::Ident(name) => name,

    //         // TODO(tsion): Add an error to self.errors.
    //         _ => return Err(()),
    //     };

    //     try!(self.expect(&Token::ParenL));

    //     // TODO(tsion): Parse arguments.

    //     try!(self.expect(&Token::ParenR));
    //     try!(self.expect(&Token::BraceL));

    //     let mut body = Vec::new();

    //     while self.token != Token::BraceR {
    //         body.push(try!(self.parse_expr()));
    //     }

    //     // Skip the closing brace.
    //     self.advance();

    //     Ok(ast::FnDef {
    //         name: name,
    //         return_ty: ast::Type::Unit,
    //         args: Vec::new(),
    //         body: body,
    //     })
    // }

    // fn parse_expr(&mut self) -> ParseResult<ast::Expr> {
    //     let func = match self.advance_and_get() {
    //         Token::Ident(name) => name,

    //         // TODO(tsion): Add an error to self.errors.
    //         _ => return Err(()),
    //     };

    //     try!(self.expect(&Token::ParenL));

    //     let str = match self.advance_and_get() {
    //         Token::StrLit(str) => str,

    //         // TODO(tsion): Add an error to self.errors.
    //         _ => return Err(()),
    //     };

    //     try!(self.expect(&Token::ParenR));
    //     try!(self.expect(&Token::Semicolon));

    //     Ok(ast::Expr::FnCall {
    //         func: func,
    //         args: vec![ast::Expr::StrLit(str)],
    //     })
    // }

////////////////////////////////////////////////////////////////////////////////
// Character classes
////////////////////////////////////////////////////////////////////////////////

/// Returns `true` if the given character is whitespace.
fn is_whitespace(c: char) -> bool {
    match c {
        ' ' | '\t' | '\r' | '\n' => true,
        _ => false,
    }
}

/// Returns `true` if the given character is a digit.
fn is_digit(c: char) -> bool {
    match c {
        '0'...'9' => true,
        _ => false,
    }
}

/// Returns `true` if the given character is valid at the start of an identifier.
fn is_ident_start(c: char) -> bool {
    match c {
        'a'...'z' | 'A'...'Z' | '_' => true,
        _ => false,
    }
}

/// Returns `true` if the given character is valid after the start of an identifier.
fn is_ident_continue(c: char) -> bool {
    is_ident_start(c) || is_digit(c)
}

////////////////////////////////////////////////////////////////////////////////
// Errors
////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Error {
    pub message: String,
    pub level: ErrorLevel,
    pub span: Span,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ErrorLevel {
    Error,
    Warning,
    Note,
}

pub struct ErrorReporter {
    pub errors: RefCell<Vec<Error>>,
}

impl ErrorReporter {
    fn new() -> Self {
        ErrorReporter { errors: RefCell::new(Vec::new()) }
    }

    fn report<S: Into<String>>(&self, span: Span, level: ErrorLevel, message: S) {
        self.errors.borrow_mut().push(Error {
            message: message.into(),
            level: level,
            span: span,
        });
    }

    fn is_empty(&self) -> bool {
        self.errors.borrow().is_empty()
    }

    fn reset(&self) {
        self.errors.borrow_mut().clear();
    }
}

pub fn errors() -> Rc<ErrorReporter> {
    thread_local!(static KEY: Rc<ErrorReporter> = Rc::new(ErrorReporter::new()));
    KEY.with(|k| k.clone())
}

#[cfg(test)]
mod test {
    use super::{errors, ErrorLevel, Lexer, Span, TokenKind};
    use super::ErrorLevel::*;
    use super::TokenKind::*;

    #[test]
    fn lex_empty_string() {
        lexer_test(r#""""#, StrLit(String::from("")), &[]);
    }

    #[test]
    fn lex_unterminated_empty_string() {
        lexer_test(r#"""#, StrLit(String::from("")), &[
            (1, 1, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_unterminated_escape_empty_string() {
        lexer_test(r#""\"#, StrLit(String::from("")), &[
            (2, 2, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_unterminated_escape_string() {
        lexer_test(r#""foo\"#, StrLit(String::from("foo")), &[
            (5, 5, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_string() {
        lexer_test(r#""foobar""#, StrLit(String::from("foobar")), &[]);
    }

    #[test]
    fn lex_unterminated_string() {
        lexer_test(r#""foobar"#, StrLit(String::from("foobar")), &[
            (7, 7, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_string_with_just_newline() {
        lexer_test(r#""\n""#, StrLit(String::from("\n")), &[]);
    }

    #[test]
    fn lex_unterminated_string_with_just_newline() {
        lexer_test(r#""\n"#, StrLit(String::from("\n")), &[
            (3, 3, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_string_with_newline() {
        lexer_test(r#""foo\nbar""#, StrLit(String::from("foo\nbar")), &[]);
    }

    #[test]
    fn lex_unterminated_string_with_newline() {
        lexer_test(r#""foo\nbar"#, StrLit(String::from("foo\nbar")), &[
            (9, 9, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    #[test]
    fn lex_invalid_escape_string() {
        lexer_test(r#""foo\cbar""#, StrLit(String::from("foocbar")), &[
            (4, 6, Error, "invalid character escape: c"),
        ]);
    }

    #[test]
    fn lex_unterminated_invalid_escape_string() {
        lexer_test(r#""foo\cbar"#, StrLit(String::from("foocbar")), &[
            (4, 6, Error, "invalid character escape: c"),
            (9, 9, Error, "unterminated string literal"),
            (0, 0, Note, "string literal began here"),
        ]);
    }

    fn lexer_test(source: &str,
                  expected: TokenKind,
                  expected_errors: &[(usize, usize, ErrorLevel, &str)]) {
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.next().kind, expected);
        assert_errors(expected_errors);
    }

    fn assert_errors(expected_errors: &[(usize, usize, ErrorLevel, &str)]) {
        let reporter = errors();

        {
            let errors = reporter.errors.borrow();

            for (i, &(start, end, level, message)) in expected_errors.iter().enumerate() {
                assert_eq!(errors[i], super::Error {
                    message: String::from(message),
                    level: level,
                    span: Span::new(start, end),
                });
            }

            // Checking that there are no unexpected errors this way will pretty-print the
            // unexpected errors if they exist.
            assert_eq!(&errors[expected_errors.len()..], &[]);
        }

        reporter.reset();
    }
}
<|endoftext|>


<commit_before>use std::result:: { Result };
use lcov_parser:: { LCOVParser, LCOVRecord, RecordParseError };
use report:: { Report, FileResult };
use coverage:: { Coverage };

pub struct ReportParser {
    parser: LCOVParser,
    files: Vec<FileResult>,
    counter: Option<HitCounter>
}

impl ReportParser {
    pub fn new(report: &str) -> Self {
        let parser = LCOVParser::new(report);
        ReportParser { parser: parser, files: vec!(), counter: None }
    }
    pub fn parse(&mut self) -> Result<Report, RecordParseError> {
        let records = try!(self.parser.parse());

        for record in records.iter() {
            self.on_parsed(record);
        }

        let report = Report::new(self.files.clone());
        Ok(report)
    }

    pub fn on_parsed(&mut self, record: &LCOVRecord) {
        match record {
            &LCOVRecord::SourceFile(ref name) => self.on_source_file(name.clone()),
            &LCOVRecord::Data(_, excution_count, _) => self.on_data(excution_count),
            &LCOVRecord::EndOfRecord => self.on_end_of_record(),
            _ => {}
        }
    }

    pub fn on_source_file(&mut self, name: String) {
        println!("{}", name);
        self.counter = Some(HitCounter::new(name));
    }

    pub fn on_data(&mut self, excution_count: u32) {
        println!("{}", excution_count);
        match self.counter.as_mut() {
            Some(counter) => counter.proceed(excution_count),
            None => {}
        }
    }

    pub fn on_end_of_record(&mut self) {
        println!("{}", "excution_count");
        match self.counter.as_mut() {
            Some(counter) => self.files.push(counter.to_file_result()),
            None => {}
        }
    }
}

pub struct HitCounter {
    name: String,
    found: u32,
    hit: u32
}

impl HitCounter {
    pub fn new(name: String) -> Self {
        HitCounter {
            name: name,
            found: 0,
            hit: 0
        }
    }
    pub fn name(&self) -> &String {
        &self.name
    }
    pub fn proceed(&mut self, excution_count: u32) {
        self.found += 1;
        if excution_count <= 0 {
            return;
        }
        self.hit += 1;
    }
    pub fn to_file_result(&self) -> FileResult {
        let value = f64::from(self.found) / f64::from(self.hit);
        FileResult::new(self.name.clone(), Coverage::new(value))
    }
}

#[cfg(test)]
mod tests {
    use std::fs:: { File };
    use parser:: { ReportParser };
    use std::io:: { Read };

    #[test]
    fn test_parse_report() {
        let mut buffer = String::new();
        let mut f = File::open("tests/fixtures/report.lcov").unwrap();
        let _ = f.read_to_string(&mut buffer);

        let mut parser = ReportParser::new(buffer.as_str());
        let report = parser.parse().unwrap();
        assert_eq!(report.files().len(), 2);
    }
}
<commit_msg>counter to processor<commit_after>use std::result:: { Result };
use lcov_parser:: { LCOVParser, LCOVRecord, RecordParseError };
use report:: { Report, FileResult };
use coverage:: { Coverage };

pub struct ReportParser {
    parser: LCOVParser,
    files: Vec<FileResult>,
    processor: Option<FileProcessor>
}

impl ReportParser {
    pub fn new(report: &str) -> Self {
        let parser = LCOVParser::new(report);
        ReportParser { parser: parser, files: vec!(), processor: None }
    }
    pub fn parse(&mut self) -> Result<Report, RecordParseError> {
        let records = try!(self.parser.parse());

        for record in records.iter() {
            self.on_parsed(record);
        }

        let report = Report::new(self.files.clone());
        Ok(report)
    }

    pub fn on_parsed(&mut self, record: &LCOVRecord) {
        match record {
            &LCOVRecord::SourceFile(ref name) => self.on_source_file(name.clone()),
            &LCOVRecord::Data(_, excution_count, _) => self.on_data(excution_count),
            &LCOVRecord::EndOfRecord => self.on_end_of_record(),
            _ => {}
        }
    }

    pub fn on_source_file(&mut self, name: String) {
        println!("{}", name);
        self.processor = Some(FileProcessor::new(name));
    }

    pub fn on_data(&mut self, excution_count: u32) {
        println!("{}", excution_count);
        match self.processor.as_mut() {
            Some(processor) => processor.proceed(excution_count),
            None => {}
        }
    }

    pub fn on_end_of_record(&mut self) {
        println!("{}", "excution_count");
        match self.processor.as_mut() {
            Some(processor) => self.files.push(processor.to_file_result()),
            None => {}
        }
    }
}

pub struct FileProcessor {
    name: String,
    found: u32,
    hit: u32
}

impl FileProcessor {
    pub fn new(name: String) -> Self {
        FileProcessor {
            name: name,
            found: 0,
            hit: 0
        }
    }
    pub fn name(&self) -> &String {
        &self.name
    }
    pub fn proceed(&mut self, excution_count: u32) {
        self.found += 1;
        if excution_count <= 0 {
            return;
        }
        self.hit += 1;
    }
    pub fn to_file_result(&self) -> FileResult {
        let value = f64::from(self.hit) / f64::from(self.found);
        FileResult::new(self.name.clone(), Coverage::new(value))
    }
}

#[cfg(test)]
mod tests {
    use std::fs:: { File };
    use parser:: { ReportParser };
    use coverage:: { Coverage };
    use std::io:: { Read };

    #[test]
    fn test_parse_report() {
        let mut buffer = String::new();
        let mut f = File::open("tests/fixtures/report.lcov").unwrap();
        let _ = f.read_to_string(&mut buffer);

        let mut parser = ReportParser::new(buffer.as_str());
        let report = parser.parse().unwrap();
        let files = report.files();

        assert_eq!(files.len(), 2);

        let first = files.get(0).unwrap();
        assert_eq!(first.coverage(), &Coverage::new(0.75));
    }
}
<|endoftext|>


<commit_before>#![feature(globs)]
#![crate_type = "bin"]
extern crate rust_js;
use rust_js::lexer::Lexer;
use rust_js::parser::{Parser, VerboseResult};
use rust_js::exec::{Executor, Interpreter};
use std::io;

fn main() {
	let mut engine : ~Interpreter = Executor::new();
	print!("> ");
	for line in io::stdin().lines() {
		match line {
			Ok(line) => {
				let mut lexer = Lexer::new();
				lexer.lex_str(line.unwrap()).v_unwrap();
				let mut parser = Parser::new(lexer.tokens);
				let result_e = parser.parse_all().v_unwrap();
				let result = engine.run(result_e);
				match result {
					Ok(v) => print!("{}", v.borrow()),
					Err(v) => print!("Error: {}", v.borrow())
				}
				print!("\n> ");
			},
			Err(err) => {
				fail!("{}", err);
			}
		}
	}
}<commit_msg>Fix attempt to unwrap a string<commit_after>#![feature(globs)]
#![crate_type = "bin"]
extern crate rust_js;
use rust_js::lexer::Lexer;
use rust_js::parser::{Parser, VerboseResult};
use rust_js::exec::{Executor, Interpreter};
use std::io;

fn main() {
	let mut engine : ~Interpreter = Executor::new();
	print!("> ");
	for line in io::stdin().lines() {
		match line {
			Ok(line) => {
				let mut lexer = Lexer::new();
				lexer.lex_str(line).v_unwrap();
				let mut parser = Parser::new(lexer.tokens);
				let result_e = parser.parse_all().v_unwrap();
				let result = engine.run(result_e);
				match result {
					Ok(v) => print!("{}", v.borrow()),
					Err(v) => print!("Error: {}", v.borrow())
				}
				print!("\n> ");
			},
			Err(err) => {
				fail!("{}", err);
			}
		}
	}
}<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This test case tests the incremental compilation hash (ICH) implementation
// for match expressions.

// The general pattern followed here is: Change one thing between rev1 and rev2
// and make sure that the hash has changed, then change nothing between rev2 and
// rev3 and make sure that the hash has not changed.

// must-compile-successfully
// revisions: cfail1 cfail2 cfail3
// compile-flags: -Z query-dep-graph

#![allow(warnings)]
#![feature(rustc_attrs)]
#![crate_type="rlib"]

// Add Arm ---------------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        2 => 2,
        _ => 100,
    }
}

// Change Order Of Arms --------------------------------------------------------
#[cfg(cfail1)]
pub fn change_order_of_arms(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_order_of_arms(x: u32) -> u32 {
    match x {
        1 => 1,
        0 => 0,
        _ => 100,
    }
}

// Add Guard Clause ------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 if y => 1,
        _ => 100,
    }
}

// Change Guard Clause ------------------------------------------------------------
#[cfg(cfail1)]
pub fn change_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 if y => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 if !y => 1,
        _ => 100,
    }
}

// Add @-Binding ---------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => x,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        x @ _ => x,
    }
}

// Change Name of @-Binding ----------------------------------------------------
#[cfg(cfail1)]
pub fn change_name_of_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        x @ _ => 7,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_name_of_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        y @ _ => 7,
    }
}

// Change Simple Binding To Pattern --------------------------------------------
#[cfg(cfail1)]
pub fn change_simple_name_to_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (0, 0) => 0,
        a      => 1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_simple_name_to_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (0, 0) => 0,
        (x, y) => 1
    }
}

// Change Name In Pattern ------------------------------------------------------
#[cfg(cfail1)]
pub fn change_name_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (a, 0) => 0,
        (a, 1) => a,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_name_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (b, 0) => 0,
        (a, 1) => a,
        _ => 100,
    }
}

// Change Mutability Of Binding In Pattern -------------------------------------
#[cfg(cfail1)]
pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (a, 0) => 0,
        _      => 1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (mut a, 0) => 0,
        _      => 1
    }
}

// Add `ref` To Binding In Pattern -------------------------------------
#[cfg(cfail1)]
pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (a, 0) => 0,
        _      => 1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (ref a, 0) => 0,
        _      => 1,
    }
}

// Add `&` To Binding In Pattern -------------------------------------
#[cfg(cfail1)]
pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 {
    match (&x, x & 1) {
        (a, 0) => 0,
        _      => 1
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 {
    match (&x, x & 1) {
        (&a, 0) => 0,
        _      => 1,
    }
}

// Change RHS Of Arm -----------------------------------------------------------
#[cfg(cfail1)]
pub fn change_rhs_of_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 2,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_rhs_of_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 3,
        _ => 2,
    }
}

// Add Alternative To Arm ------------------------------------------------------
#[cfg(cfail1)]
pub fn add_alternative_to_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 2,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_alternative_to_arm(x: u32) -> u32 {
    match x {
        0 | 7 => 0,
        1 => 3,
        _ => 2,
    }
}
<commit_msg>update match-expressions.rs<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This test case tests the incremental compilation hash (ICH) implementation
// for match expressions.

// The general pattern followed here is: Change one thing between rev1 and rev2
// and make sure that the hash has changed, then change nothing between rev2 and
// rev3 and make sure that the hash has not changed.

// must-compile-successfully
// revisions: cfail1 cfail2 cfail3
// compile-flags: -Z query-dep-graph

#![allow(warnings)]
#![feature(rustc_attrs)]
#![crate_type="rlib"]

// Add Arm ---------------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        2 => 2,
        _ => 100,
    }
}

// Change Order Of Arms --------------------------------------------------------
#[cfg(cfail1)]
pub fn change_order_of_arms(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_order_of_arms(x: u32) -> u32 {
    match x {
        1 => 1,
        0 => 0,
        _ => 100,
    }
}

// Add Guard Clause ------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 if y => 1,
        _ => 100,
    }
}

// Change Guard Clause ------------------------------------------------------------
#[cfg(cfail1)]
pub fn change_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 if y => 1,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_guard_clause(x: u32, y: bool) -> u32 {
    match x {
        0 => 0,
        1 if !y => 1,
        _ => 100,
    }
}

// Add @-Binding ---------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => x,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        x @ _ => x,
    }
}

// Change Name of @-Binding ----------------------------------------------------
#[cfg(cfail1)]
pub fn change_name_of_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        x @ _ => 7,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_name_of_at_binding(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        y @ _ => 7,
    }
}

// Change Simple Binding To Pattern --------------------------------------------
#[cfg(cfail1)]
pub fn change_simple_name_to_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (0, 0) => 0,
        a => 1,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_simple_name_to_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (0, 0) => 0,
        (x, y) => 1,
    }
}

// Change Name In Pattern ------------------------------------------------------
#[cfg(cfail1)]
pub fn change_name_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (a, 0) => 0,
        (a, 1) => a,
        _ => 100,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_name_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (b, 0) => 0,
        (a, 1) => a,
        _ => 100,
    }
}

// Change Mutability Of Binding In Pattern -------------------------------------
#[cfg(cfail1)]
pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (a, 0) => 0,
        _ => 1,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (mut a, 0) => 0,
        _ => 1,
    }
}

// Add `ref` To Binding In Pattern -------------------------------------
#[cfg(cfail1)]
pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (a, 0) => 0,
        _ => 1,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 {
    match (x, x & 1) {
        (ref a, 0) => 0,
        _ => 1,
    }
}

// Add `&` To Binding In Pattern -------------------------------------
#[cfg(cfail1)]
pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 {
    match (&x, x & 1) {
        (a, 0) => 0,
        _ => 1,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 {
    match (&x, x & 1) {
        (&a, 0) => 0,
        _ => 1,
    }
}

// Change RHS Of Arm -----------------------------------------------------------
#[cfg(cfail1)]
pub fn change_rhs_of_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 2,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn change_rhs_of_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 3,
        _ => 2,
    }
}

// Add Alternative To Arm ------------------------------------------------------
#[cfg(cfail1)]
pub fn add_alternative_to_arm(x: u32) -> u32 {
    match x {
        0 => 0,
        1 => 1,
        _ => 2,
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,MirValidated,MirOptimized,TypeckTables")]
#[rustc_clean(cfg="cfail3")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_alternative_to_arm(x: u32) -> u32 {
    match x {
        0 | 7 => 0,
        1 => 3,
        _ => 2,
    }
}
<|endoftext|>


<commit_before>use rustc::middle::{const_eval, def_id, ty};
use rustc_mir::mir_map::MirMap;
use rustc_mir::repr::{self as mir, Mir};
use syntax::ast::Attribute;
use syntax::attr::AttrMetaMethods;

use std::iter;

const TRACE_EXECUTION: bool = false;

#[derive(Clone, Debug, PartialEq)]
enum Value {
    Uninit,
    Bool(bool),
    Int(i64), // FIXME: Should be bit-width aware.
    Func(def_id::DefId),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Pointer {
    Stack(usize),
    // TODO(tsion): Heap
}

/// A stack frame:
///
/// ```text
/// +-----------------------+
/// | Arg(0)                |
/// | Arg(1)                | arguments
/// | ...                   |
/// | Arg(num_args - 1)     |
/// + - - - - - - - - - - - +
/// | Var(0)                |
/// | Var(1)                | variables
/// | ...                   |
/// | Var(num_vars - 1)     |
/// + - - - - - - - - - - - +
/// | Temp(0)               |
/// | Temp(1)               | temporaries
/// | ...                   |
/// | Temp(num_temps - 1)   |
/// + - - - - - - - - - - - +
/// | Aggregates            | aggregates
/// +-----------------------+
/// ```
#[derive(Debug)]
struct Frame {
    return_ptr: Pointer,
    offset: usize,
    num_args: usize,
    num_vars: usize,
    num_temps: usize,
    // aggregates
}

impl Frame {
    fn size(&self) -> usize {
        self.num_args + self.num_vars + self.num_temps
    }

    fn arg_offset(&self, i: usize) -> usize {
        self.offset + i
    }

    fn var_offset(&self, i: usize) -> usize {
        self.offset + self.num_args + i
    }

    fn temp_offset(&self, i: usize) -> usize {
        self.offset + self.num_args + self.num_vars + i
    }
}

struct Interpreter<'a, 'tcx: 'a> {
    tcx: &'a ty::ctxt<'tcx>,
    mir_map: &'a MirMap<'tcx>,
    value_stack: Vec<Value>,
    call_stack: Vec<Frame>,
}

impl<'a, 'tcx> Interpreter<'a, 'tcx> {
    fn new(tcx: &'a ty::ctxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
        Interpreter {
            tcx: tcx,
            mir_map: mir_map,
            value_stack: vec![Value::Uninit], // Allocate a spot for the top-level return value.
            call_stack: Vec::new(),
        }
    }

    fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) {
        let frame = Frame {
            return_ptr: return_ptr,
            offset: self.value_stack.len(),
            num_args: mir.arg_decls.len(),
            num_vars: mir.var_decls.len(),
            num_temps: mir.temp_decls.len(),
        };

        self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size()));

        for (i, arg) in args.iter().enumerate() {
            self.value_stack[frame.arg_offset(i)] = arg.clone();
        }

        self.call_stack.push(frame);

    }

    fn pop_stack_frame(&mut self) {
        let frame = self.call_stack.pop().expect("tried to pop stack frame, but there were none");
        self.value_stack.truncate(frame.offset);
    }

    fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) {
        self.push_stack_frame(mir, args, return_ptr);
        let mut block = mir::START_BLOCK;

        loop {
            let block_data = mir.basic_block_data(block);

            for stmt in &block_data.statements {
                if TRACE_EXECUTION { println!("{:?}", stmt); }

                match stmt.kind {
                    mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
                        let ptr = self.eval_lvalue(lvalue);
                        let value = self.eval_rvalue(rvalue);
                        self.write_pointer(ptr, value);
                    }

                    mir::StatementKind::Drop(_kind, ref _lv) => {
                        // TODO
                    },
                }
            }

            if TRACE_EXECUTION { println!("{:?}", block_data.terminator); }

            match block_data.terminator {
                mir::Terminator::Return => break,
                mir::Terminator::Goto { target } => block = target,

                mir::Terminator::Call { ref data, targets } => {
                    let mir::CallData { ref destination, ref func, ref args } = *data;

                    let ptr = self.eval_lvalue(destination);
                    let func_val = self.eval_operand(func);

                    if let Value::Func(def_id) = func_val {
                        let node_id = self.tcx.map.as_local_node_id(def_id).unwrap();
                        let mir = &self.mir_map[&node_id];
                        let arg_vals: Vec<Value> =
                            args.iter().map(|arg| self.eval_operand(arg)).collect();

                        self.call(mir, &arg_vals, ptr);
                        block = targets[0];
                    } else {
                        panic!("tried to call a non-function value: {:?}", func_val);
                    }
                }

                mir::Terminator::If { ref cond, targets } => {
                    match self.eval_operand(cond) {
                        Value::Bool(true) => block = targets[0],
                        Value::Bool(false) => block = targets[1],
                        cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val),
                    }
                }

                mir::Terminator::SwitchInt { ref discr, ref values, ref targets, .. } => {
                    let discr_val = self.read_lvalue(discr);

                    let index = values.iter().position(|v| discr_val == self.eval_constant(v))
                        .expect("discriminant matched no values");

                    block = targets[index];
                }

                // mir::Terminator::Diverge => unimplemented!(),
                // mir::Terminator::Panic { target } => unimplemented!(),
                // mir::Terminator::Switch { ref discr, adt_def, ref targets } => unimplemented!(),
                _ => unimplemented!(),
            }
        }

        self.pop_stack_frame();
    }

    fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> Pointer {
        let frame = self.call_stack.last().expect("missing call frame");

        match *lvalue {
            mir::Lvalue::ReturnPointer => frame.return_ptr,
            mir::Lvalue::Arg(i)  => Pointer::Stack(frame.arg_offset(i as usize)),
            mir::Lvalue::Var(i)  => Pointer::Stack(frame.var_offset(i as usize)),
            mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)),
            _ => unimplemented!(),
        }
    }

    fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Value, right: Value) -> Value {
        match (left, right) {
            (Value::Int(l), Value::Int(r)) => {
                match bin_op {
                    mir::BinOp::Add    => Value::Int(l + r),
                    mir::BinOp::Sub    => Value::Int(l - r),
                    mir::BinOp::Mul    => Value::Int(l * r),
                    mir::BinOp::Div    => Value::Int(l / r),
                    mir::BinOp::Rem    => Value::Int(l % r),
                    mir::BinOp::BitXor => Value::Int(l ^ r),
                    mir::BinOp::BitAnd => Value::Int(l & r),
                    mir::BinOp::BitOr  => Value::Int(l | r),
                    mir::BinOp::Shl    => Value::Int(l << r),
                    mir::BinOp::Shr    => Value::Int(l >> r),
                    mir::BinOp::Eq     => Value::Bool(l == r),
                    mir::BinOp::Lt     => Value::Bool(l < r),
                    mir::BinOp::Le     => Value::Bool(l <= r),
                    mir::BinOp::Ne     => Value::Bool(l != r),
                    mir::BinOp::Ge     => Value::Bool(l >= r),
                    mir::BinOp::Gt     => Value::Bool(l > r),
                }
            }

            _ => unimplemented!(),
        }
    }

    fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value {
        match *rvalue {
            mir::Rvalue::Use(ref operand) => self.eval_operand(operand),

            mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => {
                let left_val = self.eval_operand(left);
                let right_val = self.eval_operand(right);
                self.eval_binary_op(bin_op, left_val, right_val)
            }

            mir::Rvalue::UnaryOp(un_op, ref operand) => {
                match (un_op, self.eval_operand(operand)) {
                    (mir::UnOp::Not, Value::Int(n)) => Value::Int(!n),
                    (mir::UnOp::Neg, Value::Int(n)) => Value::Int(-n),
                    _ => unimplemented!(),
                }
            }

            // mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs),
            //                        ref operands) => {
            //     let num_fields = adt_def.variants[variant].fields.len();
            //     debug_assert_eq!(num_fields, operands.len());

            //     let data = operands.iter().map(|op| self.eval_operand(op)).collect();
            //     Value::Adt(variant, data)
            // }

            _ => unimplemented!(),
        }
    }

    fn eval_operand(&mut self, op: &mir::Operand) -> Value {
        match *op {
            mir::Operand::Consume(ref lvalue) => self.read_lvalue(lvalue),

            mir::Operand::Constant(ref constant) => {
                match constant.literal {
                    mir::Literal::Value { ref value } => self.eval_constant(value),

                    mir::Literal::Item { def_id, substs: _ } => {
                        Value::Func(def_id)
                    }
                }
            }
        }
    }

    fn eval_constant(&self, const_val: &const_eval::ConstVal) -> Value {
        match *const_val {
            const_eval::ConstVal::Float(_f)         => unimplemented!(),
            const_eval::ConstVal::Int(i)            => Value::Int(i),
            const_eval::ConstVal::Uint(_u)          => unimplemented!(),
            const_eval::ConstVal::Str(ref _s)       => unimplemented!(),
            const_eval::ConstVal::ByteStr(ref _bs)  => unimplemented!(),
            const_eval::ConstVal::Bool(b)           => Value::Bool(b),
            const_eval::ConstVal::Struct(_node_id)  => unimplemented!(),
            const_eval::ConstVal::Tuple(_node_id)   => unimplemented!(),
            const_eval::ConstVal::Function(_def_id) => unimplemented!(),
        }
    }

    fn read_lvalue(&self, lvalue: &mir::Lvalue) -> Value {
        self.read_pointer(self.eval_lvalue(lvalue))
    }

    fn read_pointer(&self, p: Pointer) -> Value {
        match p {
            Pointer::Stack(offset) => self.value_stack[offset].clone(),
        }
    }

    fn write_pointer(&mut self, p: Pointer, val: Value) {
        match p {
            Pointer::Stack(offset) => self.value_stack[offset] = val,
        }
    }
}

pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>) {
    for (&id, mir) in mir_map {
        for attr in tcx.map.attrs(id) {
            if attr.check_name("miri_run") {
                let item = tcx.map.expect_item(id);

                println!("Interpreting: {}", item.name);

                let mut interpreter = Interpreter::new(tcx, mir_map);
                let return_ptr = Pointer::Stack(0);
                interpreter.call(mir, &[], return_ptr);

                let val_str = format!("{:?}", interpreter.read_pointer(return_ptr));
                if !check_expected(&val_str, attr) {
                    println!("=> {}\n", val_str);
                }
            }
        }
    }
}

fn check_expected(actual: &str, attr: &Attribute) -> bool {
    if let Some(meta_items) = attr.meta_item_list() {
        for meta_item in meta_items {
            if meta_item.check_name("expected") {
                let expected = meta_item.value_str().unwrap();

                if actual == &expected[..] {
                    println!("Test passed!\n");
                } else {
                    println!("Actual value:\t{}\nExpected value:\t{}\n", actual, expected);
                }

                return true;
            }
        }
    }

    false
}
<commit_msg>Support ADT aggregate rvalues and allocation.<commit_after>use rustc::middle::{const_eval, def_id, ty};
use rustc_mir::mir_map::MirMap;
use rustc_mir::repr::{self as mir, Mir};
use syntax::ast::Attribute;
use syntax::attr::AttrMetaMethods;

use std::iter;

const TRACE_EXECUTION: bool = false;

#[derive(Clone, Debug, PartialEq)]
enum Value {
    Uninit,
    Bool(bool),
    Int(i64), // FIXME: Should be bit-width aware.
    Adt { variant: usize, data: Pointer },
    Func(def_id::DefId),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Pointer {
    Stack(usize),
    // TODO(tsion): Heap
}

impl Pointer {
    fn offset(self, i: usize) -> Self {
        match self {
            Pointer::Stack(p) => Pointer::Stack(p + i),
        }
    }
}

/// A stack frame:
///
/// ```text
/// +-----------------------+
/// | Arg(0)                |
/// | Arg(1)                | arguments
/// | ...                   |
/// | Arg(num_args - 1)     |
/// + - - - - - - - - - - - +
/// | Var(0)                |
/// | Var(1)                | variables
/// | ...                   |
/// | Var(num_vars - 1)     |
/// + - - - - - - - - - - - +
/// | Temp(0)               |
/// | Temp(1)               | temporaries
/// | ...                   |
/// | Temp(num_temps - 1)   |
/// + - - - - - - - - - - - +
/// | Aggregates            | aggregates
/// +-----------------------+
/// ```
#[derive(Debug)]
struct Frame {
    return_ptr: Pointer,
    offset: usize,
    num_args: usize,
    num_vars: usize,
    num_temps: usize,
    num_aggregate_fields: usize,
}

impl Frame {
    fn size(&self) -> usize {
        self.num_args + self.num_vars + self.num_temps + self.num_aggregate_fields
    }

    fn arg_offset(&self, i: usize) -> usize {
        self.offset + i
    }

    fn var_offset(&self, i: usize) -> usize {
        self.offset + self.num_args + i
    }

    fn temp_offset(&self, i: usize) -> usize {
        self.offset + self.num_args + self.num_vars + i
    }
}

struct Interpreter<'a, 'tcx: 'a> {
    tcx: &'a ty::ctxt<'tcx>,
    mir_map: &'a MirMap<'tcx>,
    value_stack: Vec<Value>,
    call_stack: Vec<Frame>,
}

impl<'a, 'tcx> Interpreter<'a, 'tcx> {
    fn new(tcx: &'a ty::ctxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
        Interpreter {
            tcx: tcx,
            mir_map: mir_map,
            value_stack: vec![Value::Uninit], // Allocate a spot for the top-level return value.
            call_stack: Vec::new(),
        }
    }

    fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) {
        let frame = Frame {
            return_ptr: return_ptr,
            offset: self.value_stack.len(),
            num_args: mir.arg_decls.len(),
            num_vars: mir.var_decls.len(),
            num_temps: mir.temp_decls.len(),
            num_aggregate_fields: 0,
        };

        self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size()));

        for (i, arg) in args.iter().enumerate() {
            self.value_stack[frame.arg_offset(i)] = arg.clone();
        }

        self.call_stack.push(frame);

    }

    fn pop_stack_frame(&mut self) {
        let frame = self.call_stack.pop().expect("tried to pop stack frame, but there were none");
        self.value_stack.truncate(frame.offset);
    }

    fn allocate_aggregate(&mut self, size: usize) -> Pointer {
        let frame = self.call_stack.last_mut().expect("missing call frame");
        let ptr = Pointer::Stack(self.value_stack.len());
        self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size()));
        ptr
    }

    fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) {
        self.push_stack_frame(mir, args, return_ptr);
        let mut block = mir::START_BLOCK;

        loop {
            let block_data = mir.basic_block_data(block);

            for stmt in &block_data.statements {
                if TRACE_EXECUTION { println!("{:?}", stmt); }

                match stmt.kind {
                    mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
                        let ptr = self.eval_lvalue(lvalue);
                        let value = self.eval_rvalue(rvalue);
                        self.write_pointer(ptr, value);
                    }

                    mir::StatementKind::Drop(_kind, ref _lv) => {
                        // TODO
                    },
                }
            }

            if TRACE_EXECUTION { println!("{:?}", block_data.terminator); }

            match block_data.terminator {
                mir::Terminator::Return => break,
                mir::Terminator::Goto { target } => block = target,

                mir::Terminator::Call { ref data, targets } => {
                    let mir::CallData { ref destination, ref func, ref args } = *data;

                    let ptr = self.eval_lvalue(destination);
                    let func_val = self.eval_operand(func);

                    if let Value::Func(def_id) = func_val {
                        let node_id = self.tcx.map.as_local_node_id(def_id).unwrap();
                        let mir = &self.mir_map[&node_id];
                        let arg_vals: Vec<Value> =
                            args.iter().map(|arg| self.eval_operand(arg)).collect();

                        self.call(mir, &arg_vals, ptr);
                        block = targets[0];
                    } else {
                        panic!("tried to call a non-function value: {:?}", func_val);
                    }
                }

                mir::Terminator::If { ref cond, targets } => {
                    match self.eval_operand(cond) {
                        Value::Bool(true) => block = targets[0],
                        Value::Bool(false) => block = targets[1],
                        cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val),
                    }
                }

                mir::Terminator::SwitchInt { ref discr, ref values, ref targets, .. } => {
                    let discr_val = self.read_lvalue(discr);

                    let index = values.iter().position(|v| discr_val == self.eval_constant(v))
                        .expect("discriminant matched no values");

                    block = targets[index];
                }

                // mir::Terminator::Diverge => unimplemented!(),
                // mir::Terminator::Panic { target } => unimplemented!(),
                // mir::Terminator::Switch { ref discr, adt_def, ref targets } => unimplemented!(),
                _ => unimplemented!(),
            }
        }

        self.pop_stack_frame();
    }

    fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> Pointer {
        let frame = self.call_stack.last().expect("missing call frame");

        match *lvalue {
            mir::Lvalue::ReturnPointer => frame.return_ptr,
            mir::Lvalue::Arg(i)  => Pointer::Stack(frame.arg_offset(i as usize)),
            mir::Lvalue::Var(i)  => Pointer::Stack(frame.var_offset(i as usize)),
            mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)),
            _ => unimplemented!(),
        }
    }

    fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Value, right: Value) -> Value {
        match (left, right) {
            (Value::Int(l), Value::Int(r)) => {
                match bin_op {
                    mir::BinOp::Add    => Value::Int(l + r),
                    mir::BinOp::Sub    => Value::Int(l - r),
                    mir::BinOp::Mul    => Value::Int(l * r),
                    mir::BinOp::Div    => Value::Int(l / r),
                    mir::BinOp::Rem    => Value::Int(l % r),
                    mir::BinOp::BitXor => Value::Int(l ^ r),
                    mir::BinOp::BitAnd => Value::Int(l & r),
                    mir::BinOp::BitOr  => Value::Int(l | r),
                    mir::BinOp::Shl    => Value::Int(l << r),
                    mir::BinOp::Shr    => Value::Int(l >> r),
                    mir::BinOp::Eq     => Value::Bool(l == r),
                    mir::BinOp::Lt     => Value::Bool(l < r),
                    mir::BinOp::Le     => Value::Bool(l <= r),
                    mir::BinOp::Ne     => Value::Bool(l != r),
                    mir::BinOp::Ge     => Value::Bool(l >= r),
                    mir::BinOp::Gt     => Value::Bool(l > r),
                }
            }

            _ => unimplemented!(),
        }
    }

    fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value {
        match *rvalue {
            mir::Rvalue::Use(ref operand) => self.eval_operand(operand),

            mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => {
                let left_val = self.eval_operand(left);
                let right_val = self.eval_operand(right);
                self.eval_binary_op(bin_op, left_val, right_val)
            }

            mir::Rvalue::UnaryOp(un_op, ref operand) => {
                match (un_op, self.eval_operand(operand)) {
                    (mir::UnOp::Not, Value::Int(n)) => Value::Int(!n),
                    (mir::UnOp::Neg, Value::Int(n)) => Value::Int(-n),
                    _ => unimplemented!(),
                }
            }

            mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs),
                                   ref operands) => {
                let max_fields = adt_def.variants
                    .iter()
                    .map(|v| v.fields.len())
                    .max()
                    .unwrap_or(0);

                let ptr = self.allocate_aggregate(max_fields);

                for (i, operand) in operands.iter().enumerate() {
                    let val = self.eval_operand(operand);
                    self.write_pointer(ptr.offset(i), val);
                }

                Value::Adt { variant: variant, data: ptr }
            }

            _ => unimplemented!(),
        }
    }

    fn eval_operand(&mut self, op: &mir::Operand) -> Value {
        match *op {
            mir::Operand::Consume(ref lvalue) => self.read_lvalue(lvalue),

            mir::Operand::Constant(ref constant) => {
                match constant.literal {
                    mir::Literal::Value { ref value } => self.eval_constant(value),

                    mir::Literal::Item { def_id, substs: _ } => {
                        Value::Func(def_id)
                    }
                }
            }
        }
    }

    fn eval_constant(&self, const_val: &const_eval::ConstVal) -> Value {
        match *const_val {
            const_eval::ConstVal::Float(_f)         => unimplemented!(),
            const_eval::ConstVal::Int(i)            => Value::Int(i),
            const_eval::ConstVal::Uint(_u)          => unimplemented!(),
            const_eval::ConstVal::Str(ref _s)       => unimplemented!(),
            const_eval::ConstVal::ByteStr(ref _bs)  => unimplemented!(),
            const_eval::ConstVal::Bool(b)           => Value::Bool(b),
            const_eval::ConstVal::Struct(_node_id)  => unimplemented!(),
            const_eval::ConstVal::Tuple(_node_id)   => unimplemented!(),
            const_eval::ConstVal::Function(_def_id) => unimplemented!(),
        }
    }

    fn read_lvalue(&self, lvalue: &mir::Lvalue) -> Value {
        self.read_pointer(self.eval_lvalue(lvalue))
    }

    fn read_pointer(&self, p: Pointer) -> Value {
        match p {
            Pointer::Stack(offset) => self.value_stack[offset].clone(),
        }
    }

    fn write_pointer(&mut self, p: Pointer, val: Value) {
        match p {
            Pointer::Stack(offset) => self.value_stack[offset] = val,
        }
    }
}

pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>) {
    for (&id, mir) in mir_map {
        for attr in tcx.map.attrs(id) {
            if attr.check_name("miri_run") {
                let item = tcx.map.expect_item(id);

                println!("Interpreting: {}", item.name);

                let mut interpreter = Interpreter::new(tcx, mir_map);
                let return_ptr = Pointer::Stack(0);
                interpreter.call(mir, &[], return_ptr);

                let val_str = format!("{:?}", interpreter.read_pointer(return_ptr));
                if !check_expected(&val_str, attr) {
                    println!("=> {}\n", val_str);
                }
            }
        }
    }
}

fn check_expected(actual: &str, attr: &Attribute) -> bool {
    if let Some(meta_items) = attr.meta_item_list() {
        for meta_item in meta_items {
            if meta_item.check_name("expected") {
                let expected = meta_item.value_str().unwrap();

                if actual == &expected[..] {
                    println!("Test passed!\n");
                } else {
                    println!("Actual value:\t{}\nExpected value:\t{}\n", actual, expected);
                }

                return true;
            }
        }
    }

    false
}
<|endoftext|>