<commit_before>use std::io::prelude::*;
use std::io;
use std::path::Path;

use analysis::types::IsIncomplete;
use env::Env;
use file_saver::save_to_file;
use library::{Type, MAIN_NAMESPACE};
use nameutil::crate_name;

pub fn generate(env :&Env) {
    let tests = env.config.target_path.join("tests");
    let abi_c = tests.join("abi.c");
    let abi_rs = tests.join("abi.rs");

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let ctypes = ns.types
        .iter()
        .filter_map(|t| t.as_ref())
        .filter(|t| !t.is_incomplete(&env.library))
        .filter_map(|t| match *t {
            Type::Alias(_) |
            Type::Class(_) |
            Type::Record(_) |
            Type::Union(_) |
            Type::Enumeration(_) |
            Type::Bitfield(_) |
            Type::Interface(_)
            => {
                let full_name = format!("{}.{}", &ns.name, t.get_name());
                if !env.type_status_sys(&full_name).ignored() {
                    t.get_glib_name()
                } else {
                    None
                }
            },
            _ => None,
        })
        .filter(|s| !is_name_made_up(s))
        .collect::<Vec<_>>();

    if ctypes.is_empty() {
        return;
    }

    save_to_file(&abi_c, env.config.make_backup, |w| {
        generate_abi_c(env, &abi_c, w)
    });
    save_to_file(&abi_rs, env.config.make_backup, |w| {
        generate_abi_rs(env, &abi_rs, w, &ctypes)
    });
}

/// Checks if type name is unlikely to correspond to a real C type name.
fn is_name_made_up(name: &str) -> bool {
    // Unnamed types are assigned name during parsing, those names contain an underscore.
    name.contains('_')
}

fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> {
    info!("Generating file {:?}", path);

    writeln!(w, "/* For %z support in printf when using MinGW. */")?;
    writeln!(w, "#define _POSIX_C_SOURCE 200809L")?;
    writeln!(w, "#include <stdalign.h>")?;
    writeln!(w, "#include <stdio.h>")?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    for include in ns.c_includes.iter() {
        writeln!(w, "#include <{}>", include)?;
    }

    writeln!(w, "{}", r##"
int main() {
  printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE));
}"##)

}

fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[&str]) -> io::Result<()> {
    info!("Generating file {:?}", path);

    let name = format!("{}_sys", crate_name(&env.config.library_name));
    writeln!(w, "extern crate {};", &name)?;
    writeln!(w, "extern crate shell_words;")?;
    writeln!(w, "{}", r##"
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::mem::{align_of, size_of};
use std::process::Command;
use std::str;"##)?;
    writeln!(w, "use {}::*;", &name)?;
    writeln!(w, "{}", r##"

#[derive(Clone, Debug)]
struct Compiler {
    pub args: Vec<String>,
}

impl Compiler {
    pub fn new() -> Result<Compiler, Box<Error>> {
        let mut args = get_var("CC", "cc")?;
        args.extend(get_var("CFLAGS", "")?);
        args.extend(get_var("CPPFLAGS", "")?);
        Ok(Compiler { args })
    }

    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) {
        let arg = match val.into() {
            None => format!("-D{}", var),
            Some(val) => format!("-D{}={}", var, val),
        };
        self.args.push(arg);
    }

    pub fn to_command(&self) -> Command {
        let mut cmd = Command::new(&self.args[0]);
        cmd.args(&self.args[1..]);
        cmd
    }
}

fn get_var(name: &str, default: &str) -> Result<Vec<String>, Box<Error>> {
    match env::var(name) {
        Ok(value) => Ok(shell_words::split(&value)?),
        Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?),
        Err(err) => Err(format!("{} {}", name, err).into()),
    }
}

fn pkg_config_cflags(package: &str) -> Result<Vec<String>, Box<Error>> {
    let mut cmd = Command::new("pkg-config");
    cmd.arg("--cflags");
    cmd.arg(package);
    let out = cmd.output()?;
    if !out.status.success() {
        return Err(format!("command {:?} returned {}",
                           &cmd, out.status).into());
    }
    let stdout = str::from_utf8(&out.stdout)?;
    Ok(shell_words::split(stdout)?)
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct ABI {
    size: usize,
    alignment: usize,
}

impl ABI {
    pub fn from_type<T: Sized>() -> ABI {
        ABI {
            size: size_of::<T>(),
            alignment: align_of::<T>(),
        }
    }
}

#[test]
fn test_cross_validate_abi_with_c() {
    // Configure compiler instance."##)?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let package_name = ns.package_name.as_ref()
        .expect("Missing package name");

    writeln!(w, "\tlet package_name = \"{}\";", package_name)?;
    writeln!(w, "{}", r##"    let mut cc = Compiler::new()
        .expect("compiler from environment");
    let cflags = pkg_config_cflags(package_name)
        .expect("cflags from pkg-config");
    cc.args.extend(cflags);
    cc.args.push("-Wno-deprecated-declarations".to_owned());

    // Sanity check that compilation works.
    assert_eq!(ABI {size: 1, alignment: 1},
               get_c_abi(&cc, "char").expect("C ABI for char"));

    let rust = get_rust_abi();
    let mut failed = false;
    for (name, rust_abi) in &rust {
        match get_c_abi(&cc, name) {
            Err(e) => {
                failed = true;
                eprintln!("{}", e);
                continue;
            },
            Ok(ref c_abi) => {
                if rust_abi != c_abi {
                    failed = true;
                    eprintln!("ABI mismatch for {}\nRust: {:?}\nC:    {:?}",
                              name, rust_abi, c_abi);
                }
            }
        };
    }
    assert!(!failed);
}

fn get_c_abi(cc: &Compiler, name: &str) -> Result<ABI, Box<Error>> {
    let mut cc = cc.clone();
    cc.define("ABI_TEST_TYPE", name);
    cc.args.push("tests/abi.c".to_owned());
    cc.args.push("-oabi".to_owned());

    let mut cc_cmd = cc.to_command();
    let status = cc_cmd.spawn()?.wait()?;
    if !status.success() {
        return Err(format!("compilation command {:?} failed, {}",
                           &cc_cmd, status).into());
    }

    let mut abi_cmd = Command::new("./abi");
    let output = abi_cmd.output()?;
    if !output.status.success() {
        return Err(format!("command {:?} failed, {}",
                           &abi_cmd, output.status).into());
    }

    let stdout = str::from_utf8(&output.stdout)?;
    let mut words = stdout.split_whitespace();
    let size = words.next().unwrap().parse().unwrap();
    let alignment = words.next().unwrap().parse().unwrap();
    Ok(ABI {size, alignment})
}

fn get_rust_abi() -> BTreeMap<String, ABI> {
    let mut abi = BTreeMap::new();"##)?;

    for ctype in ctypes {
        writeln!(w, r##"    abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##,
                 ctype=ctype)?;
    }

    writeln!(w, "{}", r##"    abi
}

"##)

}

<commit_msg>Generate "DO NOT EDIT" header for ABI tests.<commit_after>use std::io::prelude::*;
use std::io;
use std::path::Path;

use analysis::types::IsIncomplete;
use env::Env;
use file_saver::save_to_file;
use library::{Type, MAIN_NAMESPACE};
use nameutil::crate_name;
use codegen::general;

pub fn generate(env :&Env) {
    let tests = env.config.target_path.join("tests");
    let abi_c = tests.join("abi.c");
    let abi_rs = tests.join("abi.rs");

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let ctypes = ns.types
        .iter()
        .filter_map(|t| t.as_ref())
        .filter(|t| !t.is_incomplete(&env.library))
        .filter_map(|t| match *t {
            Type::Alias(_) |
            Type::Class(_) |
            Type::Record(_) |
            Type::Union(_) |
            Type::Enumeration(_) |
            Type::Bitfield(_) |
            Type::Interface(_)
            => {
                let full_name = format!("{}.{}", &ns.name, t.get_name());
                if !env.type_status_sys(&full_name).ignored() {
                    t.get_glib_name()
                } else {
                    None
                }
            },
            _ => None,
        })
        .filter(|s| !is_name_made_up(s))
        .collect::<Vec<_>>();

    if ctypes.is_empty() {
        return;
    }

    save_to_file(&abi_c, env.config.make_backup, |w| {
        generate_abi_c(env, &abi_c, w)
    });
    save_to_file(&abi_rs, env.config.make_backup, |w| {
        generate_abi_rs(env, &abi_rs, w, &ctypes)
    });
}

/// Checks if type name is unlikely to correspond to a real C type name.
fn is_name_made_up(name: &str) -> bool {
    // Unnamed types are assigned name during parsing, those names contain an underscore.
    name.contains('_')
}

fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    writeln!(w, "/* For %z support in printf when using MinGW. */")?;
    writeln!(w, "#define _POSIX_C_SOURCE 200809L")?;
    writeln!(w, "#include <stdalign.h>")?;
    writeln!(w, "#include <stdio.h>")?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    for include in ns.c_includes.iter() {
        writeln!(w, "#include <{}>", include)?;
    }

    writeln!(w, "{}", r##"
int main() {
  printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE));
}"##)

}

fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[&str]) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    let name = format!("{}_sys", crate_name(&env.config.library_name));
    writeln!(w, "extern crate {};", &name)?;
    writeln!(w, "extern crate shell_words;")?;
    writeln!(w, "{}", r##"
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::mem::{align_of, size_of};
use std::process::Command;
use std::str;"##)?;
    writeln!(w, "use {}::*;", &name)?;
    writeln!(w, "{}", r##"

#[derive(Clone, Debug)]
struct Compiler {
    pub args: Vec<String>,
}

impl Compiler {
    pub fn new() -> Result<Compiler, Box<Error>> {
        let mut args = get_var("CC", "cc")?;
        args.extend(get_var("CFLAGS", "")?);
        args.extend(get_var("CPPFLAGS", "")?);
        Ok(Compiler { args })
    }

    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) {
        let arg = match val.into() {
            None => format!("-D{}", var),
            Some(val) => format!("-D{}={}", var, val),
        };
        self.args.push(arg);
    }

    pub fn to_command(&self) -> Command {
        let mut cmd = Command::new(&self.args[0]);
        cmd.args(&self.args[1..]);
        cmd
    }
}

fn get_var(name: &str, default: &str) -> Result<Vec<String>, Box<Error>> {
    match env::var(name) {
        Ok(value) => Ok(shell_words::split(&value)?),
        Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?),
        Err(err) => Err(format!("{} {}", name, err).into()),
    }
}

fn pkg_config_cflags(package: &str) -> Result<Vec<String>, Box<Error>> {
    let mut cmd = Command::new("pkg-config");
    cmd.arg("--cflags");
    cmd.arg(package);
    let out = cmd.output()?;
    if !out.status.success() {
        return Err(format!("command {:?} returned {}",
                           &cmd, out.status).into());
    }
    let stdout = str::from_utf8(&out.stdout)?;
    Ok(shell_words::split(stdout)?)
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct ABI {
    size: usize,
    alignment: usize,
}

impl ABI {
    pub fn from_type<T: Sized>() -> ABI {
        ABI {
            size: size_of::<T>(),
            alignment: align_of::<T>(),
        }
    }
}

#[test]
fn test_cross_validate_abi_with_c() {
    // Configure compiler instance."##)?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let package_name = ns.package_name.as_ref()
        .expect("Missing package name");

    writeln!(w, "\tlet package_name = \"{}\";", package_name)?;
    writeln!(w, "{}", r##"    let mut cc = Compiler::new()
        .expect("compiler from environment");
    let cflags = pkg_config_cflags(package_name)
        .expect("cflags from pkg-config");
    cc.args.extend(cflags);
    cc.args.push("-Wno-deprecated-declarations".to_owned());

    // Sanity check that compilation works.
    assert_eq!(ABI {size: 1, alignment: 1},
               get_c_abi(&cc, "char").expect("C ABI for char"));

    let rust = get_rust_abi();
    let mut failed = false;
    for (name, rust_abi) in &rust {
        match get_c_abi(&cc, name) {
            Err(e) => {
                failed = true;
                eprintln!("{}", e);
                continue;
            },
            Ok(ref c_abi) => {
                if rust_abi != c_abi {
                    failed = true;
                    eprintln!("ABI mismatch for {}\nRust: {:?}\nC:    {:?}",
                              name, rust_abi, c_abi);
                }
            }
        };
    }
    assert!(!failed);
}

fn get_c_abi(cc: &Compiler, name: &str) -> Result<ABI, Box<Error>> {
    let mut cc = cc.clone();
    cc.define("ABI_TEST_TYPE", name);
    cc.args.push("tests/abi.c".to_owned());
    cc.args.push("-oabi".to_owned());

    let mut cc_cmd = cc.to_command();
    let status = cc_cmd.spawn()?.wait()?;
    if !status.success() {
        return Err(format!("compilation command {:?} failed, {}",
                           &cc_cmd, status).into());
    }

    let mut abi_cmd = Command::new("./abi");
    let output = abi_cmd.output()?;
    if !output.status.success() {
        return Err(format!("command {:?} failed, {}",
                           &abi_cmd, output.status).into());
    }

    let stdout = str::from_utf8(&output.stdout)?;
    let mut words = stdout.split_whitespace();
    let size = words.next().unwrap().parse().unwrap();
    let alignment = words.next().unwrap().parse().unwrap();
    Ok(ABI {size, alignment})
}

fn get_rust_abi() -> BTreeMap<String, ABI> {
    let mut abi = BTreeMap::new();"##)?;

    for ctype in ctypes {
        writeln!(w, r##"    abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##,
                 ctype=ctype)?;
    }

    writeln!(w, "{}", r##"    abi
}

"##)

}

<|endoftext|>


<commit_before>use super::Object;
#[cfg(feature = "chrono_time")]
use chrono::prelude::*;
#[cfg(not(feature = "chrono_time"))]
use time::strptime;
use time::{strftime, Tm};

#[cfg(feature = "chrono_time")]
impl From<DateTime<Local>> for Object {
	fn from(date: DateTime<Local>) -> Self {
		let mut timezone_str = date.format("D:%Y%m%d%H%M%S%:z'").to_string().into_bytes();
		convert_utc_offset(&mut timezone_str);
		Object::string_literal(timezone_str)
	}
}

// Find the last `:` and turn it into an `'` to account for PDF weirdness
fn convert_utc_offset(bytes: &mut [u8]) {
	let mut index = bytes.len();
	while let Some(last) = bytes[..index].last_mut() {
		if *last == b':' {
			*last = b'\'';
			break;
		}
		index -= 1;
	}
}

#[cfg(feature = "chrono_time")]
impl From<DateTime<Utc>> for Object {
	fn from(date: DateTime<Utc>) -> Self {
		Object::string_literal(date.format("D:%Y%m%d%H%M%SZ").to_string())
	}
}

impl From<Tm> for Object {
	fn from(date: Tm) -> Self {
		// can only fail if the TIME_FMT_ENCODE_STR would be invalid
		Object::string_literal(if date.tm_utcoff != 0 {
			// D:%Y%m%d%H%M%S:%z'
			//
			// UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive).
			let timezone = strftime("%z", &date).unwrap();
			let timezone_str_start = strftime("%Y%m%d%H%M%S", &date).unwrap();
			let mut timezone_str = format!("D:{}{}:{}'", timezone_str_start, &timezone[..3], &timezone[3..]).into_bytes();
			convert_utc_offset(&mut timezone_str);
			timezone_str
		} else {
			format!("D:{}", strftime("%Y%m%d%H%M%SZ", &date).unwrap()).into_bytes()
		})
	}
}

impl Object {
	// Parses the `D`, `:` and `\` out of a `Object::String` to parse the date time
	fn datetime_string(&self) -> Option<String> {
		if let Object::String(ref bytes, _) = self {
			String::from_utf8(bytes.iter().filter(|b| ![b'D', b':', b'\''].contains(b)).cloned().collect()).ok()
		} else {
			None
		}
	}

	#[cfg(feature = "chrono_time")]
	pub fn as_datetime(&self) -> Option<DateTime<Local>> {
	#[cfg(feature = "chrono_time")]
		const TIME_FMT_DECODE_STR: &str = "%Y%m%d%H%M%S%#z";
		let text = self.datetime_string()?;
		DateTime::parse_from_str(&text, TIME_FMT_DECODE_STR).map(|date| date.with_timezone(&Local)).ok()
	}

	/// WARNING: `tm_wday` (weekday), `tm_yday` (day index in year), `tm_isdst`
	/// (daylight saving time) and `tm_nsec` (nanoseconds of the date from 1970)
	/// are set to 0 since they aren't available in the PDF time format. They could,
	/// however, be calculated manually
	#[cfg(not(feature = "chrono_time"))]
	pub fn as_datetime(&self) -> Option<Tm> {
		const TIME_FMT_DECODE_STR: &str = "%Y%m%d%H%M%S%z";
		let text = self.datetime_string()?;
		strptime(&text, TIME_FMT_DECODE_STR).ok()
	}
}

#[cfg(feature = "chrono_time")]
#[test]
fn parse_datetime_local() {
	let time = Local::now().with_nanosecond(0).unwrap();
	let text: Object = time.into();
	let time2 = text.as_datetime();
	assert_eq!(time2, Some(time));
}

#[cfg(feature = "chrono_time")]
#[test]
fn parse_datetime_utc() {
	let time = Utc::now().with_nanosecond(0).unwrap();
	let text: Object = time.into();
	let time2 = text.as_datetime();
	assert_eq!(time2, Some(time.with_timezone(&Local)));
}

#[cfg(not(feature = "chrono_time"))]
#[test]
fn parse_datetime() {
	// Tm-based: Ignore tm_wday, tm_yday, tm_isdst and tm_nsec
	// - not important in the date parsing
	let time = Tm {
		tm_wday: 0,
		tm_yday: 0,
		tm_isdst: 0,
		tm_nsec: 0,
		..::time::now()
	};

	let text: Object = time.into();
	let time2 = text.as_datetime();
	assert_eq!(time2, Some(time));
}
<commit_msg>More permissive datetime parsing (#90)<commit_after>use super::Object;
#[cfg(feature = "chrono_time")]
use chrono::prelude::*;
#[cfg(not(feature = "chrono_time"))]
use time::strptime;
use time::{strftime, Tm};

#[cfg(feature = "chrono_time")]
impl From<DateTime<Local>> for Object {
	fn from(date: DateTime<Local>) -> Self {
		let mut timezone_str = date.format("D:%Y%m%d%H%M%S%:z'").to_string().into_bytes();
		convert_utc_offset(&mut timezone_str);
		Object::string_literal(timezone_str)
	}
}

// Find the last `:` and turn it into an `'` to account for PDF weirdness
fn convert_utc_offset(bytes: &mut [u8]) {
	let mut index = bytes.len();
	while let Some(last) = bytes[..index].last_mut() {
		if *last == b':' {
			*last = b'\'';
			break;
		}
		index -= 1;
	}
}

#[cfg(feature = "chrono_time")]
impl From<DateTime<Utc>> for Object {
	fn from(date: DateTime<Utc>) -> Self {
		Object::string_literal(date.format("D:%Y%m%d%H%M%SZ").to_string())
	}
}

impl From<Tm> for Object {
	fn from(date: Tm) -> Self {
		// can only fail if the TIME_FMT_ENCODE_STR would be invalid
		Object::string_literal(if date.tm_utcoff != 0 {
			// D:%Y%m%d%H%M%S:%z'
			//
			// UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive).
			let timezone = strftime("%z", &date).unwrap();
			let timezone_str_start = strftime("%Y%m%d%H%M%S", &date).unwrap();
			let mut timezone_str = format!("D:{}{}:{}'", timezone_str_start, &timezone[..3], &timezone[3..]).into_bytes();
			convert_utc_offset(&mut timezone_str);
			timezone_str
		} else {
			format!("D:{}", strftime("%Y%m%d%H%M%SZ", &date).unwrap()).into_bytes()
		})
	}
}

impl Object {
	// Parses the `D`, `:` and `\` out of a `Object::String` to parse the date time
	fn datetime_string(&self) -> Option<String> {
		if let Object::String(ref bytes, _) = self {
			String::from_utf8(bytes.iter().filter(|b| ![b'D', b':', b'\''].contains(b)).cloned().collect()).ok()
		} else {
			None
		}
	}

	#[cfg(feature = "chrono_time")]
	pub fn as_datetime(&self) -> Option<DateTime<Local>> {
		let text = self.datetime_string()?;
		let from_date = |date| FixedOffset::east(0).from_utc_date(&date).and_hms(0, 0, 0);
		DateTime::parse_from_str(&text, "%Y%m%d%H%M%S%#z")
			.or_else(|_| DateTime::parse_from_str(&text, "%Y%m%d%H%M%#z"))
			.or_else(|_| NaiveDate::parse_from_str(&text, "%Y%m%d").map(from_date))
			.map(|date| date.with_timezone(&Local)).ok()
	}

	/// WARNING: `tm_wday` (weekday), `tm_yday` (day index in year), `tm_isdst`
	/// (daylight saving time) and `tm_nsec` (nanoseconds of the date from 1970)
	/// are set to 0 since they aren't available in the PDF time format. They could,
	/// however, be calculated manually
	#[cfg(not(feature = "chrono_time"))]
	pub fn as_datetime(&self) -> Option<Tm> {
		const TIME_FMT_DECODE_STR: &str = "%Y%m%d%H%M%S%z";
		let text = self.datetime_string()?;
		strptime(&text, TIME_FMT_DECODE_STR).ok()
	}
}

#[cfg(feature = "chrono_time")]
#[test]
fn parse_datetime_local() {
	let time = Local::now().with_nanosecond(0).unwrap();
	let text: Object = time.into();
	let time2 = text.as_datetime();
	assert_eq!(time2, Some(time));
}

#[cfg(feature = "chrono_time")]
#[test]
fn parse_datetime_utc() {
	let time = Utc::now().with_nanosecond(0).unwrap();
	let text: Object = time.into();
	let time2 = text.as_datetime();
	assert_eq!(time2, Some(time.with_timezone(&Local)));
}

#[cfg(feature = "chrono_time")]
#[test]
fn parse_datetime_seconds_missing() {
	// this is the example from the PDF reference, version 1.7, chapter 3.8.3
	let text = Object::string_literal("D:199812231952-08'00'");
	assert!(text.as_datetime().is_some());
}

#[cfg(feature = "chrono_time")]
#[test]
fn parse_datetime_time_missing() {
	let text = Object::string_literal("D:20040229");
	assert!(text.as_datetime().is_some());
}

#[cfg(not(feature = "chrono_time"))]
#[test]
fn parse_datetime() {
	// Tm-based: Ignore tm_wday, tm_yday, tm_isdst and tm_nsec
	// - not important in the date parsing
	let time = Tm {
		tm_wday: 0,
		tm_yday: 0,
		tm_isdst: 0,
		tm_nsec: 0,
		..::time::now()
	};

	let text: Object = time.into();
	let time2 = text.as_datetime();
	assert_eq!(time2, Some(time));
}
<|endoftext|>


<commit_before>// extern crate centralgithook;
extern crate git2;
extern crate clap;
extern crate env_logger;

use std::path::Path;
use super::RepoHost;
use super::Scratch;
use super::ReviewUploadResult;
use super::Hooks;

pub fn dispatch(pargs: Vec<String>, hooks: &Hooks, host: &RepoHost, scratch: &Scratch) -> i32
{
    println!(".\n");
    let hook = &pargs[0];

    // ref-update: fired after push
    // change-merged: fired after gerrit-submit
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");
    let is_project_created = hook.ends_with("project-created");
    let is_create_project = hook.ends_with("validate-project");

    let args = if is_update {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .arg(clap::Arg::with_name("refname").long("refname").takes_value(true).required(true))
            .arg(clap::Arg::with_name("uploader")
                .long("uploader")
                .takes_value(true)
                .required(true))
            .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true).required(true))
            .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else if is_submit {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("change").long("change").takes_value(true))
            .arg(clap::Arg::with_name("change-url")
                .long("change-url")
                .takes_value(true))
            .arg(clap::Arg::with_name("change-owner")
                .long("change-owner")
                .takes_value(true))
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .arg(clap::Arg::with_name("branch").long("branch").takes_value(true).required(true))
            .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
            .arg(clap::Arg::with_name("submitter")
                .long("submitter")
                .takes_value(true))
            .arg(clap::Arg::with_name("commit").long("commit").takes_value(true).required(true))
            .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else if is_project_created {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("head").long("head").takes_value(true).required(true))
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else if is_create_project {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else {
        return 0;
    };

    let (branch, refname) = if is_update {
        let refname = args.value_of("refname").expect("no refname");
        let branch = if refname.starts_with("refs/for/") {
            refname.rsplitn(2, "refs/for/").next().expect("no branchname")
        }
        else {
            refname.rsplitn(2, "refs/heads/").next().expect("no branchname")
        };
        (branch, refname)
    }
    else if is_submit {
        (args.value_of("branch").expect("no branch"), args.value_of("branch").expect("no branch"))
    }
    else if is_project_created {
        let head = args.value_of("head").expect("no head");
        let branch = head.rsplitn(2, "refs/heads/").next().expect("no branchname");
        (branch, head)
    }
    else {
        ("", "")
    };

    if branch != "" && branch != hooks.branch() {
        debug!("branch not controled by centralgit: {} ({})",
               branch,
               hooks.branch());
        return 0;
    }

    let (_, project) = args.value_of("project").expect("no project").split_at(host.prefix().len());

    if is_create_project {
        let rev = scratch.tracking(host, host.central(), hooks.branch())
            .expect("pre_create_project: no central tracking")
            .id();
        hooks.pre_create_project(&scratch, rev, &project);
        return 0;
    }

    let this_project = Path::new(&host.local_path(project)).to_path_buf();

    let is_review = is_update && refname.starts_with("refs/for/");
    let is_module = project != format!("{}{}", host.prefix(), host.central());

    let uploader = args.value_of("uploader").unwrap_or("");
    if is_update && !is_review && !uploader.contains(host.automation_user()) {
        // FIXME: hardcoded
        debug!("==== uploader: {}", uploader);
        debug!("{} {} {}",
               is_update,
               is_review,
               uploader.contains(host.automation_user()));
        println!("===================================================================");
        println!("================= Do not push directly to {}! =================",
                 hooks.branch());
        println!("===================================================================");
        return 1;
    }

    if is_submit {
        // submit to central
        let commit = args.value_of("commit").unwrap_or("");
        hooks.central_submit(&scratch, host, scratch.transfer(commit, &this_project));
    }
    else if is_project_created {
        hooks.project_created(&scratch, host, &project);
        println!("==== project_created");
    }
    else if is_review {
        // module was pushed, get changes to central
        let newrev = args.value_of("newrev").unwrap_or("");
        match hooks.review_upload(&scratch,
                                  host,
                                  scratch.transfer(newrev, &this_project),
                                  project) {
            ReviewUploadResult::RejectNoFF => {
                println!("===================================================================");
                println!("=========== Commit not based on {}, rebase first! =============",
                         hooks.branch());
                println!("===================================================================");
            }
            ReviewUploadResult::NoChanges => {}
            ReviewUploadResult::RejectMerge => {
                println!("===================================================================");
                println!("=================== Do not submit merge commits! ==================");
                println!("===================================================================");
            }
            ReviewUploadResult::Uploaded(oid, initial) => {
                println!("================ Doing actual upload in central git ===============");
                if initial {
                    println!("======================= This is a NEW module ======================");
                }

                println!("{}",
                         scratch.push(host,
                                      oid,
                                      host.central(),
                                      &format!("refs/for/{}", hooks.branch())));
                println!("==== The review upload may have worked, even if it says error below. \
                          Look UP! ====")
            }
            ReviewUploadResult::Central => return 0,
        }

        // stop host from allowing push to module directly
        return 1;
    }
    else if !is_module && is_update && !is_review {
        let oldrev = args.value_of("oldrev").unwrap_or("");
        let is_initial = !is_module && oldrev == "0000000000000000000000000000000000000000";
        if is_initial {
            println!(".\n\n##### INITIAL IMPORT ######");
            let newrev = args.value_of("newrev").unwrap_or("");
            // hooks.central_submit(&scratch, scratch.transfer(newrev, &this_project));
            hooks.central_submit(&scratch, host, scratch.transfer(newrev, &this_project));
            return 0;
        }
        else {
            println!(".\n\n##### INITIAL IMPORT ALREADY HAPPEND ######");
            return 1;
        }
    }
    return 0;
}
<commit_msg>debug output for args<commit_after>// extern crate centralgithook;
extern crate git2;
extern crate clap;
extern crate env_logger;

use std::path::Path;
use super::RepoHost;
use super::Scratch;
use super::ReviewUploadResult;
use super::Hooks;

pub fn dispatch(pargs: Vec<String>, hooks: &Hooks, host: &RepoHost, scratch: &Scratch) -> i32
{
    println!(".\n");
    let hook = &pargs[0];

    debug!("ARGS: {:?}", pargs);

    // ref-update: fired after push
    // change-merged: fired after gerrit-submit
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");
    let is_project_created = hook.ends_with("project-created");
    let is_create_project = hook.ends_with("validate-project");

    let args = if is_update {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .arg(clap::Arg::with_name("refname").long("refname").takes_value(true).required(true))
            .arg(clap::Arg::with_name("uploader")
                .long("uploader")
                .takes_value(true)
                .required(true))
            .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true).required(true))
            .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else if is_submit {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("change").long("change").takes_value(true))
            .arg(clap::Arg::with_name("change-url")
                .long("change-url")
                .takes_value(true))
            .arg(clap::Arg::with_name("change-owner")
                .long("change-owner")
                .takes_value(true))
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .arg(clap::Arg::with_name("branch").long("branch").takes_value(true).required(true))
            .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
            .arg(clap::Arg::with_name("submitter")
                .long("submitter")
                .takes_value(true))
            .arg(clap::Arg::with_name("commit").long("commit").takes_value(true).required(true))
            .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else if is_project_created {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("head").long("head").takes_value(true).required(true))
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else if is_create_project {
        clap::App::new("centralgithook")
            .arg(clap::Arg::with_name("project").long("project").takes_value(true).required(true))
            .get_matches_from(&pargs)
    }
    else {
        return 0;
    };

    let (branch, refname) = if is_update {
        let refname = args.value_of("refname").expect("no refname");
        let branch = if refname.starts_with("refs/for/") {
            refname.rsplitn(2, "refs/for/").next().expect("no branchname")
        }
        else {
            refname.rsplitn(2, "refs/heads/").next().expect("no branchname")
        };
        (branch, refname)
    }
    else if is_submit {
        (args.value_of("branch").expect("no branch"), args.value_of("branch").expect("no branch"))
    }
    else if is_project_created {
        let head = args.value_of("head").expect("no head");
        let branch = head.rsplitn(2, "refs/heads/").next().expect("no branchname");
        (branch, head)
    }
    else {
        ("", "")
    };

    if branch != "" && branch != hooks.branch() {
        debug!("branch not controled by centralgit: {} ({})",
               branch,
               hooks.branch());
        return 0;
    }

    let (_, project) = args.value_of("project").expect("no project").split_at(host.prefix().len());

    if is_create_project {
        let rev = scratch.tracking(host, host.central(), hooks.branch())
            .expect("pre_create_project: no central tracking")
            .id();
        hooks.pre_create_project(&scratch, rev, &project);
        return 0;
    }

    let this_project = Path::new(&host.local_path(project)).to_path_buf();

    let is_review = is_update && refname.starts_with("refs/for/");
    let is_module = project != format!("{}{}", host.prefix(), host.central());

    let uploader = args.value_of("uploader").unwrap_or("");
    if is_update && !is_review && !uploader.contains(host.automation_user()) {
        // FIXME: hardcoded
        debug!("==== uploader: {}", uploader);
        debug!("{} {} {}",
               is_update,
               is_review,
               uploader.contains(host.automation_user()));
        println!("===================================================================");
        println!("================= Do not push directly to {}! =================",
                 hooks.branch());
        println!("===================================================================");
        return 1;
    }

    if is_submit {
        // submit to central
        let commit = args.value_of("commit").unwrap_or("");
        hooks.central_submit(&scratch, host, scratch.transfer(commit, &this_project));
    }
    else if is_project_created {
        hooks.project_created(&scratch, host, &project);
        println!("==== project_created");
    }
    else if is_review {
        // module was pushed, get changes to central
        let newrev = args.value_of("newrev").unwrap_or("");
        match hooks.review_upload(&scratch,
                                  host,
                                  scratch.transfer(newrev, &this_project),
                                  project) {
            ReviewUploadResult::RejectNoFF => {
                println!("===================================================================");
                println!("=========== Commit not based on {}, rebase first! =============",
                         hooks.branch());
                println!("===================================================================");
            }
            ReviewUploadResult::NoChanges => {}
            ReviewUploadResult::RejectMerge => {
                println!("===================================================================");
                println!("=================== Do not submit merge commits! ==================");
                println!("===================================================================");
            }
            ReviewUploadResult::Uploaded(oid, initial) => {
                println!("================ Doing actual upload in central git ===============");
                if initial {
                    println!("======================= This is a NEW module ======================");
                }

                println!("{}",
                         scratch.push(host,
                                      oid,
                                      host.central(),
                                      &format!("refs/for/{}", hooks.branch())));
                println!("==== The review upload may have worked, even if it says error below. \
                          Look UP! ====")
            }
            ReviewUploadResult::Central => return 0,
        }

        // stop host from allowing push to module directly
        return 1;
    }
    else if !is_module && is_update && !is_review {
        let oldrev = args.value_of("oldrev").unwrap_or("");
        let is_initial = !is_module && oldrev == "0000000000000000000000000000000000000000";
        if is_initial {
            println!(".\n\n##### INITIAL IMPORT ######");
            let newrev = args.value_of("newrev").unwrap_or("");
            // hooks.central_submit(&scratch, scratch.transfer(newrev, &this_project));
            hooks.central_submit(&scratch, host, scratch.transfer(newrev, &this_project));
            return 0;
        }
        else {
            println!(".\n\n##### INITIAL IMPORT ALREADY HAPPEND ######");
            return 1;
        }
    }
    return 0;
}
<|endoftext|>


<commit_before>// -*- rust -*-

fn main(vec[str] args) -> () {

  log "This is the rust 'self-hosted' compiler.";
  log "The one written in rust.";
  log "It does nothing yet, it's a placeholder.";
  log "You want rustboot, the compiler next door.";

  auto i = 0;
  for (str filename in args) {
    if (i > 0) {
      auto br = std._io.new_buf_reader(filename);
      log "opened file: " + filename;
      for (u8 b in br.read()) {
        log b;
      }
    }
    i += 1;
  }

}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Add test code that exercises LLVM in rustc. Works.<commit_after>// -*- rust -*-

import std._str;
import lib.llvm.llvm;
import lib.llvm.builder;

fn write_module() {
    auto llmod =
        llvm.LLVMModuleCreateWithNameInContext(_str.buf("rust_out"),
                                               llvm.LLVMGetGlobalContext());

    auto b = builder(llvm.LLVMCreateBuilder());

    llvm.LLVMWriteBitcodeToFile(llmod, _str.buf("rust_out.bc"));
    llvm.LLVMDisposeModule(llmod);
}

fn main(vec[str] args) -> () {

  log "This is the rust 'self-hosted' compiler.";
  log "The one written in rust.";
  log "It does nothing yet, it's a placeholder.";
  log "You want rustboot, the compiler next door.";

  auto i = 0;
  for (str filename in args) {
    if (i > 0) {
      auto br = std._io.new_buf_reader(filename);
      log "opened file: " + filename;
      for (u8 b in br.read()) {
        log b;
      }
    }
    i += 1;
  }

  // Test LLVM module-writing. Nothing interesting yet.
  write_module();

}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>extern crate racer_interner;
#[macro_use]
extern crate serde;
extern crate serde_json;

pub mod mapping;
pub mod metadata;

use crate::metadata::Metadata;
use std::env;
use std::error::Error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::{self, Utf8Error};

#[derive(Debug)]
pub enum ErrorKind {
    Encode(Utf8Error),
    Json(serde_json::Error),
    Io(io::Error),
    Subprocess(String),
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorKind::Encode(e) => fmt::Display::fmt(e, f),
            ErrorKind::Json(e) => fmt::Display::fmt(e, f),
            ErrorKind::Io(e) => fmt::Display::fmt(e, f),
            ErrorKind::Subprocess(s) => write!(f, "stderr: {}", s),
        }
    }
}

impl Error for ErrorKind {
    fn description(&self) -> &str {
        match self {
            ErrorKind::Encode(e) => e.description(),
            ErrorKind::Json(e) => e.description(),
            ErrorKind::Io(e) => e.description(),
            ErrorKind::Subprocess(s) => &s,
        }
    }
}

impl From<Utf8Error> for ErrorKind {
    fn from(e: Utf8Error) -> ErrorKind {
        ErrorKind::Encode(e)
    }
}

impl From<serde_json::Error> for ErrorKind {
    fn from(e: serde_json::Error) -> ErrorKind {
        ErrorKind::Json(e)
    }
}

impl From<io::Error> for ErrorKind {
    fn from(e: io::Error) -> ErrorKind {
        ErrorKind::Io(e)
    }
}

pub fn find_manifest(mut current: &Path) -> Option<PathBuf> {
    let file = "Cargo.toml";
    if current.is_dir() {
        let manifest = current.join(file);
        if manifest.exists() {
            return Some(manifest);
        }
    }
    while let Some(parent) = current.parent() {
        let manifest = parent.join(file);
        if manifest.exists() {
            return Some(manifest);
        }
        current = parent;
    }
    None
}

pub fn run(manifest_path: &Path, frozen: bool) -> Result<Metadata, ErrorKind> {
    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_owned());
    let mut cmd = Command::new(cargo);
    cmd.arg("metadata");
    cmd.arg("--all-features");
    cmd.args(&["--format-version", "1"]);
    cmd.args(&["--color", "never"]);
    cmd.arg("--manifest-path");
    cmd.arg(manifest_path.as_os_str());
    if frozen {
        cmd.arg("--frozen");
    }
    let op = cmd.output()?;
    if !op.status.success() {
        let stderr = String::from_utf8(op.stderr).map_err(|e| e.utf8_error())?;
        return Err(ErrorKind::Subprocess(stderr));
    }
    serde_json::from_slice(&op.stdout).map_err(From::from)
}
<commit_msg>metadata: Don't use deprecated Error::description<commit_after>extern crate racer_interner;
#[macro_use]
extern crate serde;
extern crate serde_json;

pub mod mapping;
pub mod metadata;

use crate::metadata::Metadata;
use std::env;
use std::error::Error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::Utf8Error;

#[derive(Debug)]
pub enum ErrorKind {
    Encode(Utf8Error),
    Json(serde_json::Error),
    Io(io::Error),
    Subprocess(String),
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorKind::Encode(e) => fmt::Display::fmt(e, f),
            ErrorKind::Json(e) => fmt::Display::fmt(e, f),
            ErrorKind::Io(e) => fmt::Display::fmt(e, f),
            ErrorKind::Subprocess(s) => write!(f, "stderr: {}", s),
        }
    }
}

impl Error for ErrorKind {}

impl From<Utf8Error> for ErrorKind {
    fn from(e: Utf8Error) -> ErrorKind {
        ErrorKind::Encode(e)
    }
}

impl From<serde_json::Error> for ErrorKind {
    fn from(e: serde_json::Error) -> ErrorKind {
        ErrorKind::Json(e)
    }
}

impl From<io::Error> for ErrorKind {
    fn from(e: io::Error) -> ErrorKind {
        ErrorKind::Io(e)
    }
}

pub fn find_manifest(mut current: &Path) -> Option<PathBuf> {
    let file = "Cargo.toml";
    if current.is_dir() {
        let manifest = current.join(file);
        if manifest.exists() {
            return Some(manifest);
        }
    }
    while let Some(parent) = current.parent() {
        let manifest = parent.join(file);
        if manifest.exists() {
            return Some(manifest);
        }
        current = parent;
    }
    None
}

pub fn run(manifest_path: &Path, frozen: bool) -> Result<Metadata, ErrorKind> {
    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_owned());
    let mut cmd = Command::new(cargo);
    cmd.arg("metadata");
    cmd.arg("--all-features");
    cmd.args(&["--format-version", "1"]);
    cmd.args(&["--color", "never"]);
    cmd.arg("--manifest-path");
    cmd.arg(manifest_path.as_os_str());
    if frozen {
        cmd.arg("--frozen");
    }
    let op = cmd.output()?;
    if !op.status.success() {
        let stderr = String::from_utf8(op.stderr).map_err(|e| e.utf8_error())?;
        return Err(ErrorKind::Subprocess(stderr));
    }
    serde_json::from_slice(&op.stdout).map_err(From::from)
}
<|endoftext|>


<commit_before>use primal_bit::{BitVec};
use std::{cmp};
use hamming;

use wheel;

mod presieve;

/// A segmented sieve that yields only a small run of primes at a
/// time.
///
/// This is heavily inspired by this [segmented
/// sieve](http://primesieve.org/segmented_sieve.html) code.
#[derive(Debug)]
pub struct StreamingSieve {
    small: Option<::Sieve>,
    // stores which numbers *aren't* prime, i.e. true == composite.
    sieve: BitVec,
    primes: Vec<wheel::State<wheel::Wheel210>>,
    small_primes: Vec<wheel::State<wheel::Wheel30>>,
    large_primes: Vec<wheel::State<wheel::Wheel210>>,
    presieve: presieve::Presieve,

    low: usize,
    current: usize,
    limit: usize,
}

const CACHE: usize = 32 << 10;
const SEG_ELEMS: usize = 8 * CACHE;
const SEG_LEN: usize = SEG_ELEMS * wheel::BYTE_MODULO / wheel::BYTE_SIZE;

impl StreamingSieve {
    /// Create a new instance of the streaming sieve that will
    /// correctly progressively filter primes up to `limit`.
    pub fn new(limit: usize) -> StreamingSieve {
        let low = 0;

        let elems = cmp::min(wheel::bits_for(limit), SEG_ELEMS);
        let presieve = presieve::Presieve::new(elems);
        let current = presieve.smallest_unincluded_prime();

        let small = if limit < current * current {
            None
        } else {
            Some(::Sieve::new((limit as f64).sqrt() as usize + 1))
        };

        StreamingSieve {
            small: small,
            sieve: BitVec::from_elem(elems, false),
            primes: vec![],
            small_primes: vec![],
            large_primes: vec![],
            presieve: presieve,

            low: low,
            current: current,
            limit: limit
        }
    }
    fn split_index(&self, idx: usize) -> (usize, usize) {
        let len = SEG_ELEMS;
        (idx / len,idx % len)
    }
    fn index_for(&self, n: usize) -> (bool, usize, usize) {
        let (b, idx) = wheel::bit_index(n);
        let (base, tweak) = self.split_index(idx);
        (b, base, tweak)
    }

    pub fn count_upto(n: usize) -> usize {
        match n {
            0...1 => 0,
            2 => 1,
            3...4 => 2,
            5...6 => 3,
            7...10 => 4,
            _ => {
                let mut sieve = StreamingSieve::new(n);
                let (includes, base, tweak) = sieve.index_for(n);
                let mut count = match wheel::BYTE_MODULO {
                    30 => 3,
                    _ => unimplemented!()
                };

                for _ in 0..base {
                    let (_, bitv) = sieve.next().unwrap();
                    let bytes = bitv.as_bytes();
                    count += 8 * bytes.len() - hamming::weight(bytes) as usize;
                }
                let (tweak_byte, tweak_bit) = (tweak / 8, tweak % 8);
                let (_, last) = sieve.next().unwrap();
                let bytes = last.as_bytes();
                count += 8 * tweak_byte - hamming::weight(&bytes[..tweak_byte]) as usize;
                if tweak_bit > 0 || includes {
                    let byte = bytes[tweak_byte];
                    for i in 0..tweak_bit + includes as usize {
                        count += (byte & (1 << i) == 0) as usize
                    }
                }
                count
            }
        }
    }

    fn find_new_sieving_primes(&mut self, low: usize, high: usize) {
        if let Some(ref small) = self.small {
            let mut s = self.current;
            assert!(s % 2 == 1);
            while s * s <= high {
                if small.is_prime(s) {
                    if s <= SEG_LEN / 100 {
                        self.small_primes.push(wheel::compute_wheel_elem(wheel::Wheel30, s, low));
                    } else {
                        let elem = wheel::compute_wheel_elem(wheel::Wheel210, s, low);
                        if s < SEG_LEN / 2 {
                            self.primes.push(elem)
                        } else {
                            self.large_primes.push(elem)
                        }
                    }
                }
                s += 2
            }

            self.current = s;
        }
    }

    fn small_primes_sieve<W: wheel::Wheel>(sieve: &mut BitVec,
                                           small_primes: &mut [wheel::State<W>]) {
        let bytes = sieve.as_bytes_mut();
        for wi in small_primes {
            wi.sieve_hardcoded(bytes);
        }
    }

    fn direct_sieve(&mut self) {
        let bytes = self.sieve.as_bytes_mut();

        let mut iter = self.primes.iter_mut();

        while iter.size_hint().0 >= 3 {
            match (iter.next(), iter.next(), iter.next()) {
                (Some(wi1), Some(wi2), Some(wi3)) => {
                    wi1.sieve_triple(wi2, wi3, bytes);
                }
                _ => unreachable!()
            }
        }
        for wi in iter {
            wi.sieve(bytes)
        }
    }

    fn large_primes_sieve(&mut self) {
        let bytes = self.sieve.as_bytes_mut();

        let mut iter = self.large_primes.iter_mut();

        while iter.size_hint().0 >= 2 {
            match (iter.next(), iter.next()) {
                (Some(wi1), Some(wi2)) => {
                    wi1.sieve_pair(wi2, bytes);
                }
                _ => unreachable!()
            }
        }
        for wi in iter {
            wi.sieve(bytes)
        }
    }

    /// Extract the next chunk of filtered primes, the return value is
    /// `Some((low, v))` or `None` if the sieve has reached the limit.
    ///
    /// The vector stores bits for each odd number starting at `low`.
    /// Bit `n` of `v` is set if and only if `low + 2 * n + 1` is
    /// prime.
    ///
    /// NB. the prime 2 is not included in any of these sieves and so
    /// needs special handling.
    pub fn next(&mut self) -> Option<(usize, &BitVec)> {
        if self.low >= self.limit {
            return None
        }

        let low = self.low;
        self.low += SEG_LEN;
        let high = cmp::min(low + SEG_LEN - 1, self.limit);
        self.sieve.clear();
        self.find_new_sieving_primes(low, high);

        self.presieve.apply(&mut self.sieve, low);
        StreamingSieve::small_primes_sieve(&mut self.sieve, &mut self.small_primes);
        self.direct_sieve();
        self.large_primes_sieve();

        if low == 0 {
            // 1 is not prime.
            self.sieve.set(0, true);
            self.presieve.mark_small_primes(&mut self.sieve);
        }

        Some((low, &self.sieve))
    }
}

#[cfg(test)]
mod tests {
    use wheel;
    use super::StreamingSieve;
    fn gcd(x: usize, y: usize) -> usize {
        if y == 0 { x }
        else { gcd(y, x % y) }
    }
    fn coprime_to(x: usize) -> Vec<usize> {
        (1..x).filter(|&n| gcd(n, x) == 1).collect()
    }
    #[test]
    fn test() {
        let coprime = coprime_to(wheel::BYTE_MODULO);
        const LIMIT: usize = 2_000_000;
        let mut sieve = StreamingSieve::new(LIMIT);
        let primes = ::primal_smallsieve::Primes::sieve(LIMIT);

        let mut base = 0;
        let mut index = 0;

        while let Some((_low, next)) = sieve.next() {
            for val in next {
                let i = wheel::BYTE_MODULO * base + coprime[index];
                if i >= LIMIT { break }
                assert!(primes.is_prime(i) == !val,
                        "failed for {} (is prime = {})", i, primes.is_prime(i));

                index += 1;
                if index == wheel::BYTE_SIZE {
                    index = 0;
                    base += 1
                }
            }
        }
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use test::Bencher;
    use super::StreamingSieve;

    fn run(b: &mut Bencher, n: usize) {
        b.iter(|| {
            let mut sieve = StreamingSieve::new(n);
            while sieve.next().is_some() {}
        })
    }

    #[bench]
    fn sieve_small(b: &mut Bencher) {
        run(b, 100)
    }
    #[bench]
    fn sieve_medium(b: &mut Bencher) {
        run(b, 10_000)
    }
    #[bench]
    fn sieve_large(b: &mut Bencher) {
        run(b, 100_000)
    }
    #[bench]
    fn sieve_larger(b: &mut Bencher) {
        run(b, 1_000_000)
    }
    #[bench]
    fn sieve_huge(b: &mut Bencher) {
        run(b, 10_000_000)
    }
}
<commit_msg>sieve: remove unnecessary clear() call.<commit_after>use primal_bit::{BitVec};
use std::{cmp};
use hamming;

use wheel;

mod presieve;

/// A segmented sieve that yields only a small run of primes at a
/// time.
///
/// This is heavily inspired by this [segmented
/// sieve](http://primesieve.org/segmented_sieve.html) code.
#[derive(Debug)]
pub struct StreamingSieve {
    small: Option<::Sieve>,
    // stores which numbers *aren't* prime, i.e. true == composite.
    sieve: BitVec,
    primes: Vec<wheel::State<wheel::Wheel210>>,
    small_primes: Vec<wheel::State<wheel::Wheel30>>,
    large_primes: Vec<wheel::State<wheel::Wheel210>>,
    presieve: presieve::Presieve,

    low: usize,
    current: usize,
    limit: usize,
}

const CACHE: usize = 32 << 10;
const SEG_ELEMS: usize = 8 * CACHE;
const SEG_LEN: usize = SEG_ELEMS * wheel::BYTE_MODULO / wheel::BYTE_SIZE;

impl StreamingSieve {
    /// Create a new instance of the streaming sieve that will
    /// correctly progressively filter primes up to `limit`.
    pub fn new(limit: usize) -> StreamingSieve {
        let low = 0;

        let elems = cmp::min(wheel::bits_for(limit), SEG_ELEMS);
        let presieve = presieve::Presieve::new(elems);
        let current = presieve.smallest_unincluded_prime();

        let small = if limit < current * current {
            None
        } else {
            Some(::Sieve::new((limit as f64).sqrt() as usize + 1))
        };

        StreamingSieve {
            small: small,
            sieve: BitVec::from_elem(elems, false),
            primes: vec![],
            small_primes: vec![],
            large_primes: vec![],
            presieve: presieve,

            low: low,
            current: current,
            limit: limit
        }
    }
    fn split_index(&self, idx: usize) -> (usize, usize) {
        let len = SEG_ELEMS;
        (idx / len,idx % len)
    }
    fn index_for(&self, n: usize) -> (bool, usize, usize) {
        let (b, idx) = wheel::bit_index(n);
        let (base, tweak) = self.split_index(idx);
        (b, base, tweak)
    }

    pub fn count_upto(n: usize) -> usize {
        match n {
            0...1 => 0,
            2 => 1,
            3...4 => 2,
            5...6 => 3,
            7...10 => 4,
            _ => {
                let mut sieve = StreamingSieve::new(n);
                let (includes, base, tweak) = sieve.index_for(n);
                let mut count = match wheel::BYTE_MODULO {
                    30 => 3,
                    _ => unimplemented!()
                };

                for _ in 0..base {
                    let (_, bitv) = sieve.next().unwrap();
                    let bytes = bitv.as_bytes();
                    count += 8 * bytes.len() - hamming::weight(bytes) as usize;
                }
                let (tweak_byte, tweak_bit) = (tweak / 8, tweak % 8);
                let (_, last) = sieve.next().unwrap();
                let bytes = last.as_bytes();
                count += 8 * tweak_byte - hamming::weight(&bytes[..tweak_byte]) as usize;
                if tweak_bit > 0 || includes {
                    let byte = bytes[tweak_byte];
                    for i in 0..tweak_bit + includes as usize {
                        count += (byte & (1 << i) == 0) as usize
                    }
                }
                count
            }
        }
    }

    fn find_new_sieving_primes(&mut self, low: usize, high: usize) {
        if let Some(ref small) = self.small {
            let mut s = self.current;
            assert!(s % 2 == 1);
            while s * s <= high {
                if small.is_prime(s) {
                    if s <= SEG_LEN / 100 {
                        self.small_primes.push(wheel::compute_wheel_elem(wheel::Wheel30, s, low));
                    } else {
                        let elem = wheel::compute_wheel_elem(wheel::Wheel210, s, low);
                        if s < SEG_LEN / 2 {
                            self.primes.push(elem)
                        } else {
                            self.large_primes.push(elem)
                        }
                    }
                }
                s += 2
            }

            self.current = s;
        }
    }

    fn small_primes_sieve<W: wheel::Wheel>(sieve: &mut BitVec,
                                           small_primes: &mut [wheel::State<W>]) {
        let bytes = sieve.as_bytes_mut();
        for wi in small_primes {
            wi.sieve_hardcoded(bytes);
        }
    }

    fn direct_sieve(&mut self) {
        let bytes = self.sieve.as_bytes_mut();

        let mut iter = self.primes.iter_mut();

        while iter.size_hint().0 >= 3 {
            match (iter.next(), iter.next(), iter.next()) {
                (Some(wi1), Some(wi2), Some(wi3)) => {
                    wi1.sieve_triple(wi2, wi3, bytes);
                }
                _ => unreachable!()
            }
        }
        for wi in iter {
            wi.sieve(bytes)
        }
    }

    fn large_primes_sieve(&mut self) {
        let bytes = self.sieve.as_bytes_mut();

        let mut iter = self.large_primes.iter_mut();

        while iter.size_hint().0 >= 2 {
            match (iter.next(), iter.next()) {
                (Some(wi1), Some(wi2)) => {
                    wi1.sieve_pair(wi2, bytes);
                }
                _ => unreachable!()
            }
        }
        for wi in iter {
            wi.sieve(bytes)
        }
    }

    /// Extract the next chunk of filtered primes, the return value is
    /// `Some((low, v))` or `None` if the sieve has reached the limit.
    ///
    /// The vector stores bits for each odd number starting at `low`.
    /// Bit `n` of `v` is set if and only if `low + 2 * n + 1` is
    /// prime.
    ///
    /// NB. the prime 2 is not included in any of these sieves and so
    /// needs special handling.
    pub fn next(&mut self) -> Option<(usize, &BitVec)> {
        if self.low >= self.limit {
            return None
        }

        let low = self.low;
        self.low += SEG_LEN;
        let high = cmp::min(low + SEG_LEN - 1, self.limit);

        self.find_new_sieving_primes(low, high);

        self.presieve.apply(&mut self.sieve, low);
        StreamingSieve::small_primes_sieve(&mut self.sieve, &mut self.small_primes);
        self.direct_sieve();
        self.large_primes_sieve();

        if low == 0 {
            // 1 is not prime.
            self.sieve.set(0, true);
            self.presieve.mark_small_primes(&mut self.sieve);
        }

        Some((low, &self.sieve))
    }
}

#[cfg(test)]
mod tests {
    use wheel;
    use super::StreamingSieve;
    fn gcd(x: usize, y: usize) -> usize {
        if y == 0 { x }
        else { gcd(y, x % y) }
    }
    fn coprime_to(x: usize) -> Vec<usize> {
        (1..x).filter(|&n| gcd(n, x) == 1).collect()
    }
    #[test]
    fn test() {
        let coprime = coprime_to(wheel::BYTE_MODULO);
        const LIMIT: usize = 2_000_000;
        let mut sieve = StreamingSieve::new(LIMIT);
        let primes = ::primal_smallsieve::Primes::sieve(LIMIT);

        let mut base = 0;
        let mut index = 0;

        while let Some((_low, next)) = sieve.next() {
            for val in next {
                let i = wheel::BYTE_MODULO * base + coprime[index];
                if i >= LIMIT { break }
                assert!(primes.is_prime(i) == !val,
                        "failed for {} (is prime = {})", i, primes.is_prime(i));

                index += 1;
                if index == wheel::BYTE_SIZE {
                    index = 0;
                    base += 1
                }
            }
        }
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use test::Bencher;
    use super::StreamingSieve;

    fn run(b: &mut Bencher, n: usize) {
        b.iter(|| {
            let mut sieve = StreamingSieve::new(n);
            while sieve.next().is_some() {}
        })
    }

    #[bench]
    fn sieve_small(b: &mut Bencher) {
        run(b, 100)
    }
    #[bench]
    fn sieve_medium(b: &mut Bencher) {
        run(b, 10_000)
    }
    #[bench]
    fn sieve_large(b: &mut Bencher) {
        run(b, 100_000)
    }
    #[bench]
    fn sieve_larger(b: &mut Bencher) {
        run(b, 1_000_000)
    }
    #[bench]
    fn sieve_huge(b: &mut Bencher) {
        run(b, 10_000_000)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use french_press::alloc::AllocBox;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_obj::JsObjStruct;
use js_types::js_str::JsStrStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum, JsKey, JsPtrTag};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

type VarWithPtr = (JsVar, Option<JsPtrEnum>);

// Helper to avoid repeating this everywhere
fn scalar(v: JsType) -> (JsVar, Option<JsPtrEnum>) {
    (JsVar::new(v), None)
}

/// Evaluate a string containing some JavaScript statements (or sequences of statements).
/// Returns a JsVar which is the return value of those statements.
pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    match parse_Stmt(string) {
        Ok(stmt) => {
            eval_stmt(&stmt, state).0
        }
        Err(e) => panic!("parse error: {:?}", e),
    }
}

/// Evaluate a single JS statement (which may be a block or sequence of statements).
/// Returns tuple of (evaluated final value, return value), where return value requires that
/// `return` be used to generate it.
pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        // var_string = exp;
        Assign(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());

            // Clone the js_var to store into the ScopeManager
            let cloned = js_var.clone();
            match state.alloc(cloned, js_ptr) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }

            (js_var, None)
        },

        // exp;
        BareExp(ref exp) => (eval_exp(exp, &mut state).0, None),

        // var var_string = exp
        Decl(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());
            match state.alloc(js_var.clone(), js_ptr) {
                Ok(_) => (js_var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },

        // Empty statement (?)
        Empty => (JsVar::new(JsUndef), None),

        // if (condition) { if_block } else { else_block }
        If(ref condition, ref if_block, ref else_block) => {
            // evaluate expression
            if eval_exp(&condition, state).0.as_bool() {
                // condition = true, evaluate if-block.
                eval_stmt(&*if_block, state)
            } else {
                // condition = false
                // evaluate else-block if it exists, otherwise return undefined.
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },

        // return exp
        Ret(ref exp) => {
            let js_var = eval_exp(&exp, &mut state).0;
            (js_var.clone(), Some(js_var))
        }

        // a sequence of any two expressions
        Seq(ref s1, ref s2) => {
            eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },

        // while (condition) { block }
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).0.as_bool() {
                    // TODO: check to see if a return stmt has been reached.
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    // condition is no longer true, return a return value
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

/// Evaluate an expression into a JsVar.
pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> (JsVar, Option<JsPtrEnum>) {
    match e {
        // e1 [op] e2
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state).0;
            let val2 = eval_exp(e2, state).0;

            match *op {
                And => scalar(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => scalar(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => scalar(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => scalar(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => scalar(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => scalar(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => scalar(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => scalar(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => scalar(JsNum(val1.as_number() - val2.as_number())),
                Plus  => scalar(JsNum(val1.as_number() + val2.as_number())),
                Slash => scalar(JsNum(val1.as_number() / val2.as_number())),
                Star  => scalar(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => scalar(JsBool(b)),

        // fun_name([arg_exp1, arg_exps])
        &Call(ref fun_name, ref arg_exps) => {
            let fun_binding = eval_exp(fun_name, state).0;

            // Create vector of arguments, evaluated to JsVars.
            let mut args = Vec::new();
            for exp in arg_exps {
                args.push(eval_exp(exp, state));
            }

            match state.load(&fun_binding.binding) {
                Ok((_, Some(JsPtrEnum::JsFn(js_fn_struct)))) => {
                    state.push_scope(e);

                    for param in js_fn_struct.params {
                        let mut arg = if args.is_empty() {
                            scalar(JsUndef)
                        } else {
                            args.remove(0)
                        };

                        arg.0.binding = Binding::new(param.to_owned());
                        state.alloc(arg.0, arg.1)
                            .expect("Unable to store function argument in scope");
                    }

                    let (_, v) = eval_stmt(&js_fn_struct.stmt, state);

                    // Should we yield here? Not sure, so for now it doesn't
                    state.pop_scope(false).expect("Unable to clear scope for function");
                    // TODO handle obj
                    v.map(|x| (x, None)).unwrap_or(scalar(JsUndef))
                }
                Ok(_) => panic!("Invalid call object"),
                _ => panic!("ReferenceError: {:?} is not defined", fun_name)
            }
        },

        // function([param1, params]) { body }
        // function opt_binding([param1, params]) { body }
        &Defun(ref opt_binding, ref params, ref body) => {
            if let &Some(ref binding) = opt_binding {
                let js_fun = JsFnStruct::new(opt_binding, params, &**body);
                state.alloc(JsVar::bind(binding.to_owned(), JsPtr(JsPtrTag::JsFn)),
                    Some(JsPtrEnum::JsFn(js_fun.clone())))
                    .expect("Error storing function into state");
                (JsVar::bind(binding.to_owned(), JsPtr(JsPtrTag::JsFn)),
                Some(JsPtrEnum::JsFn(js_fun)))
            } else {
                panic!("functions without bindings are not yet supported.")
            }
        },

        // var.binding
        &InstanceVar(ref instance_exp, ref var) => {
            // TODO: this needs better type-reasoning and errors
            let (instance_var, var_ptr) = eval_exp(instance_exp, state);
            if let JsPtr(_) = instance_var.t {
                match var_ptr {
                    Some(JsPtrEnum::JsObj(obj_struct)) => {
                        let try_inner = obj_struct.dict.get(&JsKey::JsStr(JsStrStruct::new(var)));
                        if let Some(inner_var) = try_inner {
                            return (inner_var.clone(), None);
                        } else {
                            return scalar(JsUndef);
                        }
                    },
                    // TODO: all JsPtrs can have instance vars/methods, not just JsObjs
                    _ => unimplemented!()
                }
            } else {
                // TODO: Things which are not ptrs can also have instance vars/methods
                unimplemented!()
            }
        },

        &Method(_, _, _) => {
            unimplemented!();
        },

        &Null => {
            scalar(JsNull)
        },

        &Float(f) => scalar(JsType::JsNum(f)),
        &Neg(ref exp) => scalar(JsNum(-eval_exp(exp, state).0.as_number())),
        &Pos(ref exp) => scalar(JsNum(eval_exp(exp, state).0.as_number())),

        &PostDec(ref exp) => (eval_float_post_op!(exp, f, f - 1.0, state), None),
        &PostInc(ref exp) => (eval_float_post_op!(exp, f, f + 1.0, state), None),
        &PreDec(ref exp)  => (eval_float_pre_op!(exp, f, f - 1.0, state),  None),
        &PreInc(ref exp)  => (eval_float_pre_op!(exp, f, f + 1.0, state),  None),

        &NewObject(_, _) => unimplemented!(),
        &Object(ref fields) => {
            let mut kv_tuples = Vec::new();
            for f in fields {
                let f_key = JsKey::JsStr(JsStrStruct::new(&f.0));
                // TODO: handle obj as key/value pair
                let f_exp = eval_exp(&*f.1, state).0;
                kv_tuples.push((f_key, f_exp, None));
            }
            let obj = JsObjStruct::new(None, "", kv_tuples, &mut AllocBox::new());
            (JsVar::new(JsPtr(JsPtrTag::JsObj)), Some(JsPtrEnum::JsObj(obj)))
        },

        &Undefined => scalar(JsUndef),
        &Var(ref var_binding) => {
            state.load(&Binding::new(var_binding.clone()))
                .expect("ReferenceError: {} is not defined")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<commit_msg>add functionality for anonymous functions<commit_after>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use french_press::alloc::AllocBox;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_obj::JsObjStruct;
use js_types::js_str::JsStrStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum, JsKey, JsPtrTag};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

type VarWithPtr = (JsVar, Option<JsPtrEnum>);

// Helper to avoid repeating this everywhere
fn scalar(v: JsType) -> (JsVar, Option<JsPtrEnum>) {
    (JsVar::new(v), None)
}

/// Evaluate a string containing some JavaScript statements (or sequences of statements).
/// Returns a JsVar which is the return value of those statements.
pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    match parse_Stmt(string) {
        Ok(stmt) => {
            eval_stmt(&stmt, state).0
        }
        Err(e) => panic!("parse error: {:?}", e),
    }
}

/// Evaluate a single JS statement (which may be a block or sequence of statements).
/// Returns tuple of (evaluated final value, return value), where return value requires that
/// `return` be used to generate it.
pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        // var_string = exp;
        Assign(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());

            // Clone the js_var to store into the ScopeManager
            let cloned = js_var.clone();
            match state.alloc(cloned, js_ptr) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }

            (js_var, None)
        },

        // exp;
        BareExp(ref exp) => (eval_exp(exp, &mut state).0, None),

        // var var_string = exp
        Decl(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());
            match state.alloc(js_var.clone(), js_ptr) {
                Ok(_) => (js_var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },

        // Empty statement (?)
        Empty => (JsVar::new(JsUndef), None),

        // if (condition) { if_block } else { else_block }
        If(ref condition, ref if_block, ref else_block) => {
            // evaluate expression
            if eval_exp(&condition, state).0.as_bool() {
                // condition = true, evaluate if-block.
                eval_stmt(&*if_block, state)
            } else {
                // condition = false
                // evaluate else-block if it exists, otherwise return undefined.
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },

        // return exp
        Ret(ref exp) => {
            let js_var = eval_exp(&exp, &mut state).0;
            (js_var.clone(), Some(js_var))
        }

        // a sequence of any two expressions
        Seq(ref s1, ref s2) => {
            eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },

        // while (condition) { block }
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).0.as_bool() {
                    // TODO: check to see if a return stmt has been reached.
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    // condition is no longer true, return a return value
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

/// Evaluate an expression into a JsVar.
pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> (JsVar, Option<JsPtrEnum>) {
    match e {
        // e1 [op] e2
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state).0;
            let val2 = eval_exp(e2, state).0;

            match *op {
                And => scalar(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => scalar(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => scalar(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => scalar(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => scalar(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => scalar(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => scalar(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => scalar(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => scalar(JsNum(val1.as_number() - val2.as_number())),
                Plus  => scalar(JsNum(val1.as_number() + val2.as_number())),
                Slash => scalar(JsNum(val1.as_number() / val2.as_number())),
                Star  => scalar(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => scalar(JsBool(b)),

        // fun_name([arg_exp1, arg_exps])
        &Call(ref fun_name, ref arg_exps) => {
            let fun_binding = eval_exp(fun_name, state).0;

            // Create vector of arguments, evaluated to JsVars.
            let mut args = Vec::new();
            for exp in arg_exps {
                args.push(eval_exp(exp, state));
            }

            match state.load(&fun_binding.binding) {
                Ok((_, Some(JsPtrEnum::JsFn(js_fn_struct)))) => {
                    state.push_scope(e);

                    for param in js_fn_struct.params {
                        let mut arg = if args.is_empty() {
                            scalar(JsUndef)
                        } else {
                            args.remove(0)
                        };

                        arg.0.binding = Binding::new(param.to_owned());
                        state.alloc(arg.0, arg.1)
                            .expect("Unable to store function argument in scope");
                    }

                    let (_, v) = eval_stmt(&js_fn_struct.stmt, state);

                    // Should we yield here? Not sure, so for now it doesn't
                    state.pop_scope(false).expect("Unable to clear scope for function");
                    // TODO handle obj
                    v.map(|x| (x, None)).unwrap_or(scalar(JsUndef))
                }
                Ok(_) => panic!("Invalid call object"),
                _ => panic!("ReferenceError: {:?} is not defined", fun_name)
            }
        },

        // function([param1, params]) { body }
        // function opt_binding([param1, params]) { body }
        &Defun(ref opt_binding, ref params, ref body) => {
            let js_fun = JsFnStruct::new(opt_binding, params, &**body);
            let mut var = JsVar::new(JsPtr(JsPtrTag::JsFn));

            if let &Some(ref s) = opt_binding {
                var.binding = Binding::new(s.to_owned());
            }

            if let Err(_) = state.alloc(var.clone(), Some(JsPtrEnum::JsFn(js_fun.clone()))) {
                panic!("error storing function into state");
            }

            (var, Some(JsPtrEnum::JsFn(js_fun)))
        },

        // var.binding
        &InstanceVar(ref instance_exp, ref var) => {
            // TODO: this needs better type-reasoning and errors
            let (instance_var, var_ptr) = eval_exp(instance_exp, state);
            if let JsPtr(_) = instance_var.t {
                match var_ptr {
                    Some(JsPtrEnum::JsObj(obj_struct)) => {
                        let try_inner = obj_struct.dict.get(&JsKey::JsStr(JsStrStruct::new(var)));
                        if let Some(inner_var) = try_inner {
                            return (inner_var.clone(), None);
                        } else {
                            return scalar(JsUndef);
                        }
                    },
                    // TODO: all JsPtrs can have instance vars/methods, not just JsObjs
                    _ => unimplemented!()
                }
            } else {
                // TODO: Things which are not ptrs can also have instance vars/methods
                unimplemented!()
            }
        },

        &Method(_, _, _) => {
            unimplemented!();
        },

        &Null => {
            scalar(JsNull)
        },

        &Float(f) => scalar(JsType::JsNum(f)),
        &Neg(ref exp) => scalar(JsNum(-eval_exp(exp, state).0.as_number())),
        &Pos(ref exp) => scalar(JsNum(eval_exp(exp, state).0.as_number())),

        &PostDec(ref exp) => (eval_float_post_op!(exp, f, f - 1.0, state), None),
        &PostInc(ref exp) => (eval_float_post_op!(exp, f, f + 1.0, state), None),
        &PreDec(ref exp)  => (eval_float_pre_op!(exp, f, f - 1.0, state),  None),
        &PreInc(ref exp)  => (eval_float_pre_op!(exp, f, f + 1.0, state),  None),

        &NewObject(_, _) => unimplemented!(),
        &Object(ref fields) => {
            let mut kv_tuples = Vec::new();
            for f in fields {
                let f_key = JsKey::JsStr(JsStrStruct::new(&f.0));
                // TODO: handle obj as key/value pair
                let f_exp = eval_exp(&*f.1, state).0;
                kv_tuples.push((f_key, f_exp, None));
            }
            let obj = JsObjStruct::new(None, "", kv_tuples, &mut AllocBox::new());
            (JsVar::new(JsPtr(JsPtrTag::JsObj)), Some(JsPtrEnum::JsObj(obj)))
        },

        &Undefined => scalar(JsUndef),
        &Var(ref var_binding) => {
            state.load(&Binding::new(var_binding.clone()))
                .expect("ReferenceError: {} is not defined")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<|endoftext|>