<commit_before>use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, Id};

use super::utils;

#[test]
fn required_group_missing_arg() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!( -c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]).required(true))
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group 'req' contains non-existent argument"]
fn non_existing_arg() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flg", "color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name must be unique\n\n\t'req' is already in use"]
fn unique_group_name() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag"]).required(true))
        .group(ArgGroup::new("req").args(["color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn groups_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn arg_group_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .group(ArgGroup::new("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[test]
fn group_single_value() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue"]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn group_empty() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec![""]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(!m.contains_id("grp"));
    assert!(m.get_one::<String>("grp").map(|v| v.as_str()).is_none());
}

#[test]
fn group_required_flags_empty() {
    let result = Command::new("group")
        .arg(arg!(-c --color "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(
            ArgGroup::new("grp")
                .required(true)
                .args(["hostname", "color"]),
        )
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
fn group_multi_value_single_arg() {
    let res = Command::new("group")
        .arg(arg!(-c --color <color> "some option").num_args(1..))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]);
    assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn empty_group() {
    let r = Command::new("empty_group")
        .arg(arg!(-f --flag "some flag"))
        .group(ArgGroup::new("vers").required(true))
        .try_get_matches_from(vec!["empty_prog"]);
    assert!(r.is_err());
    let err = r.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_usage_string() {
    static REQ_GROUP_USAGE: &str = "error: The following required arguments were not provided:
  <base|--delete>

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(cmd, "clap-test", REQ_GROUP_USAGE, true);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string() {
    static REQ_GROUP_CONFLICT_USAGE: &str = "\
error: The argument '--delete' cannot be used with '[base]'

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(
        cmd,
        "clap-test --delete base",
        REQ_GROUP_CONFLICT_USAGE,
        true,
    );
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string_only_options() {
    static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = "\
error: The argument '--delete' cannot be used with '--all'

Usage: clap-test <--all|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!(-a --all "All").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("all_or_delete")
                .args(["all", "delete"])
                .required(true),
        );
    utils::assert_output(
        cmd,
        "clap-test --delete --all",
        REQ_GROUP_CONFLICT_ONLY_OPTIONS,
        true,
    );
}

#[test]
fn required_group_multiple_args() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag").action(ArgAction::SetTrue))
        .arg(arg!(-c --color "some other flag").action(ArgAction::SetTrue))
        .group(
            ArgGroup::new("req")
                .args(["flag", "color"])
                .required(true)
                .multiple(true),
        )
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
    assert!(*m.get_one::<bool>("color").expect("defaulted by clap"));
    assert_eq!(
        &*m.get_many::<Id>("req")
            .unwrap()
            .map(|v| v.as_str())
            .collect::<Vec<_>>(),
        &["flag", "color"]
    );
}

#[test]
fn group_multiple_args_error() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]))
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
}

#[test]
fn group_overrides_required() {
    let command = Command::new("group")
        .arg(arg!(--foo <FOO>).required(true))
        .arg(arg!(--bar <BAR>).required(true))
        .group(ArgGroup::new("group").args(["foo", "bar"]).required(true));
    let result = command.try_get_matches_from(vec!["group", "--foo", "value"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("foo"));
    assert!(!m.contains_id("bar"));
}

#[test]
fn group_usage_use_val_name() {
    static GROUP_USAGE_USE_VAL_NAME: &str = "\
Usage: prog

Arguments:
  [A]

Options:
  -h, --help  Print help information
";
    let cmd = Command::new("prog")
        .arg(Arg::new("a").value_name("A"))
        .group(ArgGroup::new("group").arg("a").required(true));
    utils::assert_output(cmd, "prog --help", GROUP_USAGE_USE_VAL_NAME, false);
}

#[test]
fn group_acts_like_arg() {
    let result = Command::new("prog")
        .arg(
            Arg::new("debug")
                .long("debug")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .arg(
            Arg::new("verbose")
                .long("verbose")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .try_get_matches_from(vec!["prog", "--debug"]);

    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("mode"));
    assert_eq!(m.get_one::<clap::Id>("mode").unwrap(), "debug");
}

/* This is used to be fixed in a hack, we need to find a better way to fix it.
#[test]
fn issue_1794() {
    let cmd = clap::Command::new("hello")
        .bin_name("deno")
        .arg(Arg::new("option1").long("option1").action(ArgAction::SetTrue))
        .arg(Arg::new("pos1").action(ArgAction::Set))
        .arg(Arg::new("pos2").action(ArgAction::Set))
        .group(
            ArgGroup::new("arg1")
                .args(["pos1", "option1"])
                .required(true),
        );

    let m = cmd.clone().try_get_matches_from(&["cmd", "pos1", "pos2"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), Some("pos1"));
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("pos2"));
    assert!(!*m.get_one::<bool>("option1").expect("defaulted by clap"));

    let m = cmd
        .clone()
        .try_get_matches_from(&["cmd", "--option1", "positional"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), None);
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("positional"));
    assert!(*m.get_one::<bool>("option1").expect("defaulted by clap"));
}
*/
<commit_msg>test: Demonstrate bad group error<commit_after>use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, Id};

use super::utils;

#[test]
fn required_group_missing_arg() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!( -c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]).required(true))
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group 'req' contains non-existent argument"]
fn non_existing_arg() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flg", "color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name must be unique\n\n\t'req' is already in use"]
fn unique_group_name() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag"]).required(true))
        .group(ArgGroup::new("req").args(["color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn groups_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn arg_group_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .group(ArgGroup::new("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[test]
fn group_single_value() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue"]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn group_empty() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec![""]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(!m.contains_id("grp"));
    assert!(m.get_one::<String>("grp").map(|v| v.as_str()).is_none());
}

#[test]
fn group_required_flags_empty() {
    let result = Command::new("group")
        .arg(arg!(-c --color "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(
            ArgGroup::new("grp")
                .required(true)
                .args(["hostname", "color"]),
        )
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
fn group_multi_value_single_arg() {
    let res = Command::new("group")
        .arg(arg!(-c --color <color> "some option").num_args(1..))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]);
    assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn empty_group() {
    let r = Command::new("empty_group")
        .arg(arg!(-f --flag "some flag"))
        .group(ArgGroup::new("vers").required(true))
        .try_get_matches_from(vec!["empty_prog"]);
    assert!(r.is_err());
    let err = r.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_usage_string() {
    static REQ_GROUP_USAGE: &str = "error: The following required arguments were not provided:
  <base|--delete>

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(cmd, "clap-test", REQ_GROUP_USAGE, true);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string() {
    static REQ_GROUP_CONFLICT_USAGE: &str = "\
error: The argument '--delete' cannot be used with '[base]'

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(
        cmd,
        "clap-test --delete base",
        REQ_GROUP_CONFLICT_USAGE,
        true,
    );
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string_only_options() {
    static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = "\
error: The argument '--delete' cannot be used with '--all'

Usage: clap-test <--all|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!(-a --all "All").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("all_or_delete")
                .args(["all", "delete"])
                .required(true),
        );
    utils::assert_output(
        cmd,
        "clap-test --delete --all",
        REQ_GROUP_CONFLICT_ONLY_OPTIONS,
        true,
    );
}

#[test]
fn required_group_multiple_args() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag").action(ArgAction::SetTrue))
        .arg(arg!(-c --color "some other flag").action(ArgAction::SetTrue))
        .group(
            ArgGroup::new("req")
                .args(["flag", "color"])
                .required(true)
                .multiple(true),
        )
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
    assert!(*m.get_one::<bool>("color").expect("defaulted by clap"));
    assert_eq!(
        &*m.get_many::<Id>("req")
            .unwrap()
            .map(|v| v.as_str())
            .collect::<Vec<_>>(),
        &["flag", "color"]
    );
}

#[test]
fn group_multiple_args_error() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]))
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
}

#[test]
fn group_overrides_required() {
    let command = Command::new("group")
        .arg(arg!(--foo <FOO>).required(true))
        .arg(arg!(--bar <BAR>).required(true))
        .group(ArgGroup::new("group").args(["foo", "bar"]).required(true));
    let result = command.try_get_matches_from(vec!["group", "--foo", "value"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("foo"));
    assert!(!m.contains_id("bar"));
}

#[test]
fn group_usage_use_val_name() {
    static GROUP_USAGE_USE_VAL_NAME: &str = "\
Usage: prog

Arguments:
  [A]

Options:
  -h, --help  Print help information
";
    let cmd = Command::new("prog")
        .arg(Arg::new("a").value_name("A"))
        .group(ArgGroup::new("group").arg("a").required(true));
    utils::assert_output(cmd, "prog --help", GROUP_USAGE_USE_VAL_NAME, false);
}

#[test]
fn group_acts_like_arg() {
    let result = Command::new("prog")
        .arg(
            Arg::new("debug")
                .long("debug")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .arg(
            Arg::new("verbose")
                .long("verbose")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .try_get_matches_from(vec!["prog", "--debug"]);

    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("mode"));
    assert_eq!(m.get_one::<clap::Id>("mode").unwrap(), "debug");
}

#[test]
fn overlapping_group_in_error() {
    static ERR: &str = "\
error: The argument '--major' cannot be used with '--minor'

Usage: prog <--major|--minor|--other> <--major|--minor>

For more information try '--help'
";

    let cmd = Command::new("prog")
        .group(ArgGroup::new("all").multiple(true))
        .arg(arg!(--major).group("vers").group("all"))
        .arg(arg!(--minor).group("vers").group("all"))
        .arg(arg!(--other).group("all"));

    utils::assert_output(cmd, "prog --major --minor", ERR, true);
}

/* This is used to be fixed in a hack, we need to find a better way to fix it.
#[test]
fn issue_1794() {
    let cmd = clap::Command::new("hello")
        .bin_name("deno")
        .arg(Arg::new("option1").long("option1").action(ArgAction::SetTrue))
        .arg(Arg::new("pos1").action(ArgAction::Set))
        .arg(Arg::new("pos2").action(ArgAction::Set))
        .group(
            ArgGroup::new("arg1")
                .args(["pos1", "option1"])
                .required(true),
        );

    let m = cmd.clone().try_get_matches_from(&["cmd", "pos1", "pos2"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), Some("pos1"));
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("pos2"));
    assert!(!*m.get_one::<bool>("option1").expect("defaulted by clap"));

    let m = cmd
        .clone()
        .try_get_matches_from(&["cmd", "--option1", "positional"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), None);
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("positional"));
    assert!(*m.get_one::<bool>("option1").expect("defaulted by clap"));
}
*/
<|endoftext|>


<commit_before>//! The main entry point for the SpaceAPi server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

mod utils;
mod datastore;
mod redis_store;

use std::io::Write;
use std::net::Ipv4Addr;

use rustc_serialize::json::ToJson;
use iron::{Chain, Request, Response, IronResult, IronError, Iron, Set};
use iron::{status, headers};
use iron::modifiers::Header;

use self::datastore::DataStore;
use self::redis_store::RedisStore;
use spaceapi::Optional::{Value, Absent};

fn build_response_json(people_present: Option<u32>, raspi_temperature: Option<f32>) -> String {
    let people_present_sensor = match people_present {
        Some(count) => Value(vec![
            spaceapi::PeopleNowPresentSensor {
                value: count,
                location: Value("Hackerspace".to_string()),
                name: Absent,
                description: Absent,
                names: Absent,
            }
        ]),
        None => Absent,
    };

    let temperature_sensor = match raspi_temperature {
        Some(degrees) => Value(vec![
            spaceapi::TemperatureSensor {
                value: degrees,
                unit: "°C".to_string(),
                location: "Hackerspace".to_string(),
                name: Value("Raspberry CPU".to_string()),
                description: Absent,
            }
        ]),
        None => Absent,
    };

    let status = spaceapi::Status {

        // Hackerspace properties
        api: "0.13".to_string(),
        space: "coredump".to_string(),
        logo: "https://www.coredump.ch/logo.png".to_string(),
        url: "https://www.coredump.ch/".to_string(),
        location: spaceapi::Location {
            address: Value("Spinnereistrasse 2, 8640 Rapperswil, Switzerland".to_string()),
            lat: 47.22936,
            lon: 8.82949,
        },
        contact: spaceapi::Contact {
            irc: Value("irc://freenode.net/#coredump".to_string()),
            twitter: Value("@coredump_ch".to_string()),
            foursquare: Value("525c20e5498e875d8231b1e5".to_string()),
            email: Value("danilo@coredump.ch".to_string()),
        },

        // Hackerspace features / projects
        spacefed: Value(spaceapi::Spacefed {
            spacenet: false,
            spacesaml: false,
            spacephone: false,
        }),
        projects: Value(vec![
            "https://www.coredump.ch/projekte/".to_string(),
            "https://discourse.coredump.ch/c/projects".to_string(),
            "https://github.com/coredump-ch/".to_string(),
        ]),
        cam: Absent,
        feeds: Value(spaceapi::Feeds {
            blog: Value(spaceapi::Feed {
                _type: Value("rss".to_string()),
                url: "https://www.coredump.ch/feed/".to_string(),
            }),
            wiki: Absent,
            calendar: Absent,
            flickr: Absent,
        }),
        events: Absent,
        radio_show: Absent,

        // SpaceAPI internal usage
        cache: Value(spaceapi::Cache {
            schedule: "m.02".to_string(),
        }),
        issue_report_channels: vec![
            "email".to_string(),
            "twitter".to_string(),
        ],

        // Mutable data
        state: spaceapi::State {
            open: Some(false),
            message: Value("Open every Monday from 20:00".to_string()),
            lastchange: Absent,
            trigger_person: Absent,
            icon: Absent,
        },
        sensors: Value(spaceapi::Sensors {
            people_now_present: people_present_sensor,
            temperature: temperature_sensor,
        }),

    };
    status.to_json().to_string()
}

fn status_endpoint(_: &mut Request) -> IronResult<Response> {
    // Fetch data from datastore
    let datastore = RedisStore::new().unwrap();
    let people_present: Option<u32> = match datastore.retrieve("people_present") {
        Ok(v) => match v.parse::<u32>() {
            Ok(i) => Some(i),
            Err(_) => None,
        },
        Err(_) => None,
    };
    let raspi_temperature: Option<f32> = match datastore.retrieve("raspi_temperature") {
        Ok(v) => match v.parse::<f32>() {
            Ok(i) => Some(i),
            Err(_) => None,
        },
        Err(_) => None,
    };

    // Get response body
    let body = build_response_json(people_present, raspi_temperature);

    // Create response
    let mut response = Response::with((status::Ok, body));

    // Set headers
    response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
    response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
    response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

    Ok(response)
}

pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: spaceapi::Status,
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, status: spaceapi::Status) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: utils::get_port(),
            status: status,
        }
    }

    pub fn serve(&self) {
        println!("Starting HTTP server on {}:{}...", self.host, self.port);
        Iron::new(status_endpoint).http((self.host, self.port)).unwrap();
    }

}

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

    #[test]
    /// Verify that the response JSON looks OK.
    fn verify_response_json() {
        let people_present = Some(23);
        let temperature = Some(42.5);
        let json = build_response_json(people_present, temperature);
        assert_eq!(json, "{\
            \"api\":\"0.13\",\
            \"cache\":{\"schedule\":\"m.02\"},\
            \"contact\":{\
                \"email\":\"danilo@coredump.ch\",\
                \"foursquare\":\"525c20e5498e875d8231b1e5\",\
                \"irc\":\"irc://freenode.net/#coredump\",\
                \"twitter\":\"@coredump_ch\"\
            },\
            \"feeds\":{\
                \"blog\":{\"type\":\"rss\",\"url\":\"https://www.coredump.ch/feed/\"}\
            },\
            \"issue_report_channels\":[\"email\",\"twitter\"],\
            \"location\":{\
                \"address\":\"Spinnereistrasse 2, 8640 Rapperswil, Switzerland\",\
                \"lat\":47.22936,\"lon\":8.82949\
            },\
            \"logo\":\"https://www.coredump.ch/logo.png\",\
            \"projects\":[\
                \"https://www.coredump.ch/projekte/\",\
                \"https://discourse.coredump.ch/c/projects\",\
                \"https://github.com/coredump-ch/\"\
            ],\
            \"sensors\":{\
                \"people_now_present\":[{\
                    \"location\":\"Hackerspace\",\"value\":23\
                }],\
                \"temperature\":[{\
                    \"location\":\"Hackerspace\",\"name\":\"Raspberry CPU\",\
                    \"unit\":\"°C\",\"value\":42.5\
                }]\
            },\
            \"space\":\"coredump\",\
            \"spacefed\":{\"spacenet\":false,\"spacephone\":false,\"spacesaml\":false},\
            \"state\":{\"message\":\"Open every Monday from 20:00\",\"open\":false},\
            \"url\":\"https://www.coredump.ch/\"\
        }");
    }
}
<commit_msg>spaceapi_server: fix unused_imports warnings<commit_after>//! The main entry point for the SpaceAPi server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

mod utils;
mod datastore;
mod redis_store;

use std::net::Ipv4Addr;

use rustc_serialize::json::ToJson;
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers};
use iron::modifiers::Header;

use self::datastore::DataStore;
use self::redis_store::RedisStore;
use spaceapi::Optional::{Value, Absent};

fn build_response_json(people_present: Option<u32>, raspi_temperature: Option<f32>) -> String {
    let people_present_sensor = match people_present {
        Some(count) => Value(vec![
            spaceapi::PeopleNowPresentSensor {
                value: count,
                location: Value("Hackerspace".to_string()),
                name: Absent,
                description: Absent,
                names: Absent,
            }
        ]),
        None => Absent,
    };

    let temperature_sensor = match raspi_temperature {
        Some(degrees) => Value(vec![
            spaceapi::TemperatureSensor {
                value: degrees,
                unit: "°C".to_string(),
                location: "Hackerspace".to_string(),
                name: Value("Raspberry CPU".to_string()),
                description: Absent,
            }
        ]),
        None => Absent,
    };

    let status = spaceapi::Status {

        // Hackerspace properties
        api: "0.13".to_string(),
        space: "coredump".to_string(),
        logo: "https://www.coredump.ch/logo.png".to_string(),
        url: "https://www.coredump.ch/".to_string(),
        location: spaceapi::Location {
            address: Value("Spinnereistrasse 2, 8640 Rapperswil, Switzerland".to_string()),
            lat: 47.22936,
            lon: 8.82949,
        },
        contact: spaceapi::Contact {
            irc: Value("irc://freenode.net/#coredump".to_string()),
            twitter: Value("@coredump_ch".to_string()),
            foursquare: Value("525c20e5498e875d8231b1e5".to_string()),
            email: Value("danilo@coredump.ch".to_string()),
        },

        // Hackerspace features / projects
        spacefed: Value(spaceapi::Spacefed {
            spacenet: false,
            spacesaml: false,
            spacephone: false,
        }),
        projects: Value(vec![
            "https://www.coredump.ch/projekte/".to_string(),
            "https://discourse.coredump.ch/c/projects".to_string(),
            "https://github.com/coredump-ch/".to_string(),
        ]),
        cam: Absent,
        feeds: Value(spaceapi::Feeds {
            blog: Value(spaceapi::Feed {
                _type: Value("rss".to_string()),
                url: "https://www.coredump.ch/feed/".to_string(),
            }),
            wiki: Absent,
            calendar: Absent,
            flickr: Absent,
        }),
        events: Absent,
        radio_show: Absent,

        // SpaceAPI internal usage
        cache: Value(spaceapi::Cache {
            schedule: "m.02".to_string(),
        }),
        issue_report_channels: vec![
            "email".to_string(),
            "twitter".to_string(),
        ],

        // Mutable data
        state: spaceapi::State {
            open: Some(false),
            message: Value("Open every Monday from 20:00".to_string()),
            lastchange: Absent,
            trigger_person: Absent,
            icon: Absent,
        },
        sensors: Value(spaceapi::Sensors {
            people_now_present: people_present_sensor,
            temperature: temperature_sensor,
        }),

    };
    status.to_json().to_string()
}

fn status_endpoint(_: &mut Request) -> IronResult<Response> {
    // Fetch data from datastore
    let datastore = RedisStore::new().unwrap();
    let people_present: Option<u32> = match datastore.retrieve("people_present") {
        Ok(v) => match v.parse::<u32>() {
            Ok(i) => Some(i),
            Err(_) => None,
        },
        Err(_) => None,
    };
    let raspi_temperature: Option<f32> = match datastore.retrieve("raspi_temperature") {
        Ok(v) => match v.parse::<f32>() {
            Ok(i) => Some(i),
            Err(_) => None,
        },
        Err(_) => None,
    };

    // Get response body
    let body = build_response_json(people_present, raspi_temperature);

    // Create response
    let mut response = Response::with((status::Ok, body));

    // Set headers
    response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
    response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
    response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

    Ok(response)
}

pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: spaceapi::Status,
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, status: spaceapi::Status) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: utils::get_port(),
            status: status,
        }
    }

    pub fn serve(&self) {
        println!("Starting HTTP server on {}:{}...", self.host, self.port);
        Iron::new(status_endpoint).http((self.host, self.port)).unwrap();
    }

}

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

    #[test]
    /// Verify that the response JSON looks OK.
    fn verify_response_json() {
        let people_present = Some(23);
        let temperature = Some(42.5);
        let json = build_response_json(people_present, temperature);
        assert_eq!(json, "{\
            \"api\":\"0.13\",\
            \"cache\":{\"schedule\":\"m.02\"},\
            \"contact\":{\
                \"email\":\"danilo@coredump.ch\",\
                \"foursquare\":\"525c20e5498e875d8231b1e5\",\
                \"irc\":\"irc://freenode.net/#coredump\",\
                \"twitter\":\"@coredump_ch\"\
            },\
            \"feeds\":{\
                \"blog\":{\"type\":\"rss\",\"url\":\"https://www.coredump.ch/feed/\"}\
            },\
            \"issue_report_channels\":[\"email\",\"twitter\"],\
            \"location\":{\
                \"address\":\"Spinnereistrasse 2, 8640 Rapperswil, Switzerland\",\
                \"lat\":47.22936,\"lon\":8.82949\
            },\
            \"logo\":\"https://www.coredump.ch/logo.png\",\
            \"projects\":[\
                \"https://www.coredump.ch/projekte/\",\
                \"https://discourse.coredump.ch/c/projects\",\
                \"https://github.com/coredump-ch/\"\
            ],\
            \"sensors\":{\
                \"people_now_present\":[{\
                    \"location\":\"Hackerspace\",\"value\":23\
                }],\
                \"temperature\":[{\
                    \"location\":\"Hackerspace\",\"name\":\"Raspberry CPU\",\
                    \"unit\":\"°C\",\"value\":42.5\
                }]\
            },\
            \"space\":\"coredump\",\
            \"spacefed\":{\"spacenet\":false,\"spacephone\":false,\"spacesaml\":false},\
            \"state\":{\"message\":\"Open every Monday from 20:00\",\"open\":false},\
            \"url\":\"https://www.coredump.ch/\"\
        }");
    }
}
<|endoftext|>


<commit_before>//
// sprocketnes/main.rs
//
// Author: Patrick Walton
//

use apu::Apu;
use audio;
use cpu::Cpu;
use gfx::{Gfx, Scale, Scale1x, Scale2x, Scale3x};
use input::Input;
use input;
use mapper::Mapper;
use mapper;
use mem::MemMap;
use ppu::{Oam, Ppu, Vram};
use rom::Rom;
use util::Save;
use util;

use std::cast;
use std::cell::RefCell;
use std::io::File;
use std::rc::Rc;
use std::str;

#[link(name="SDL")]
#[link(name="SDLmain")]
extern "C" {
}

#[cfg(target_os="macos")]
#[link(name="objc")]
#[link_args="-framework Cocoa"]
extern "C" {
}

#[cfg(debug)]
fn record_fps(last_time: &mut u64, frames: &mut uint) {
    let now = util::current_time_millis();
    if now >= *last_time + 1000 {
        println!("{} FPS", *frames);
        *frames = 0;
        *last_time = now;
    } else {
        *frames += 1;
    }
}

#[cfg(not(debug))]
fn record_fps(_: &mut u64, _: &mut uint) {}

//
// Argument parsing
//

struct Options {
    rom_path: ~str,
    scale: Scale,
}

fn usage() {
    println!("usage: sprocketnes [options] <path-to-rom>");
    println!("options:");
    println!("    -1 scale by 1x (default)");
    println!("    -2 scale by 2x");
    println!("    -3 scale by 3x");
}

fn parse_args(argc: i32, argv: **u8) -> Option<Options> {
    let mut options = Options {
        rom_path: "".to_str(),
        scale: Scale1x,
    };

    for i in range(1, argc as int) {
        let arg = unsafe {
            str::raw::from_c_str(cast::transmute(*argv.offset(i)))
        };

        if "-1" == arg {
            options.scale = Scale1x;
        } else if "-2" == arg {
            options.scale = Scale2x;
        } else if "-3" == arg {
            options.scale = Scale3x;
        } else if arg[0] == ('-' as u8) {
            usage();
            return None;
        } else {
            options.rom_path = arg;
        }
    }

    if options.rom_path.len() == 0 {
        usage();
        return None;
    }

    Some(options)
}

//
// Entry point and main loop
//

pub fn start(argc: i32, argv: **u8) {
    let options = match parse_args(argc, argv) {
        Some(options) => options,
        None => return,
    };

    let rom_path: &str = options.rom_path;
    let rom = ~Rom::from_path(&Path::new(rom_path));
    println!("Loaded ROM:\n{}", rom.header.to_str());

    let mut gfx = Gfx::new(options.scale);
    let audio_buffer = audio::open();

    let mapper: ~Mapper:Freeze+Send = mapper::create_mapper(rom);
    let mapper = Rc::from_mut(RefCell::new(mapper));
    let ppu = Ppu::new(Vram::new(mapper.clone()), Oam::new());
    let input = Input::new();
    let apu = Apu::new(audio_buffer);
    let memmap = MemMap::new(ppu, input, mapper, apu);
    let mut cpu = Cpu::new(memmap);

    // TODO: Add a flag to not reset for nestest.log
    cpu.reset();

    let mut last_time = util::current_time_millis();
    let mut frames = 0;

    loop {
        cpu.step();

        let ppu_result = cpu.mem.ppu.step(cpu.cy);
        if ppu_result.vblank_nmi {
            cpu.nmi();
        } else if ppu_result.scanline_irq {
            cpu.irq();
        }

        if ppu_result.new_frame {
            gfx.tick();
            gfx.composite(cpu.mem.ppu.screen);
            gfx.screen.flip();
            record_fps(&mut last_time, &mut frames);

            match cpu.mem.input.check_input() {
                input::Continue => {}
                input::Quit => break,
                input::SaveState => {
                    cpu.save(&mut File::create(&Path::new("state.sav")).unwrap());
                    gfx.status_line.set("Saved state".to_str());
                }
                input::LoadState => {
                    cpu.load(&mut File::open(&Path::new("state.sav")).unwrap());
                    gfx.status_line.set("Loaded state".to_str());
                }
            }
        }

        cpu.mem.apu.step(cpu.cy);
    }

    audio::close();
}

<commit_msg>Use Rc::new<commit_after>//
// sprocketnes/main.rs
//
// Author: Patrick Walton
//

use apu::Apu;
use audio;
use cpu::Cpu;
use gfx::{Gfx, Scale, Scale1x, Scale2x, Scale3x};
use input::Input;
use input;
use mapper::Mapper;
use mapper;
use mem::MemMap;
use ppu::{Oam, Ppu, Vram};
use rom::Rom;
use util::Save;
use util;

use std::cast;
use std::cell::RefCell;
use std::io::File;
use std::rc::Rc;
use std::str;

#[link(name="SDL")]
#[link(name="SDLmain")]
extern "C" {
}

#[cfg(target_os="macos")]
#[link(name="objc")]
#[link_args="-framework Cocoa"]
extern "C" {
}

#[cfg(debug)]
fn record_fps(last_time: &mut u64, frames: &mut uint) {
    let now = util::current_time_millis();
    if now >= *last_time + 1000 {
        println!("{} FPS", *frames);
        *frames = 0;
        *last_time = now;
    } else {
        *frames += 1;
    }
}

#[cfg(not(debug))]
fn record_fps(_: &mut u64, _: &mut uint) {}

//
// Argument parsing
//

struct Options {
    rom_path: ~str,
    scale: Scale,
}

fn usage() {
    println!("usage: sprocketnes [options] <path-to-rom>");
    println!("options:");
    println!("    -1 scale by 1x (default)");
    println!("    -2 scale by 2x");
    println!("    -3 scale by 3x");
}

fn parse_args(argc: i32, argv: **u8) -> Option<Options> {
    let mut options = Options {
        rom_path: "".to_str(),
        scale: Scale1x,
    };

    for i in range(1, argc as int) {
        let arg = unsafe {
            str::raw::from_c_str(cast::transmute(*argv.offset(i)))
        };

        if "-1" == arg {
            options.scale = Scale1x;
        } else if "-2" == arg {
            options.scale = Scale2x;
        } else if "-3" == arg {
            options.scale = Scale3x;
        } else if arg[0] == ('-' as u8) {
            usage();
            return None;
        } else {
            options.rom_path = arg;
        }
    }

    if options.rom_path.len() == 0 {
        usage();
        return None;
    }

    Some(options)
}

//
// Entry point and main loop
//

pub fn start(argc: i32, argv: **u8) {
    let options = match parse_args(argc, argv) {
        Some(options) => options,
        None => return,
    };

    let rom_path: &str = options.rom_path;
    let rom = ~Rom::from_path(&Path::new(rom_path));
    println!("Loaded ROM:\n{}", rom.header.to_str());

    let mut gfx = Gfx::new(options.scale);
    let audio_buffer = audio::open();

    let mapper: ~Mapper:Freeze+Send = mapper::create_mapper(rom);
    let mapper = Rc::new(RefCell::new(mapper));
    let ppu = Ppu::new(Vram::new(mapper.clone()), Oam::new());
    let input = Input::new();
    let apu = Apu::new(audio_buffer);
    let memmap = MemMap::new(ppu, input, mapper, apu);
    let mut cpu = Cpu::new(memmap);

    // TODO: Add a flag to not reset for nestest.log
    cpu.reset();

    let mut last_time = util::current_time_millis();
    let mut frames = 0;

    loop {
        cpu.step();

        let ppu_result = cpu.mem.ppu.step(cpu.cy);
        if ppu_result.vblank_nmi {
            cpu.nmi();
        } else if ppu_result.scanline_irq {
            cpu.irq();
        }

        if ppu_result.new_frame {
            gfx.tick();
            gfx.composite(cpu.mem.ppu.screen);
            gfx.screen.flip();
            record_fps(&mut last_time, &mut frames);

            match cpu.mem.input.check_input() {
                input::Continue => {}
                input::Quit => break,
                input::SaveState => {
                    cpu.save(&mut File::create(&Path::new("state.sav")).unwrap());
                    gfx.status_line.set("Saved state".to_str());
                }
                input::LoadState => {
                    cpu.load(&mut File::open(&Path::new("state.sav")).unwrap());
                    gfx.status_line.set("Loaded state".to_str());
                }
            }
        }

        cpu.mem.apu.step(cpu.cy);
    }

    audio::close();
}

<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{
  Matrix,
  Mat4,
  Mat3,
  ToMat4
};
use cgmath::vector::{
  Vec3,
  Vec2
};
use cgmath::projection;
use cgmath::angle;

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

pub mod misc {
  use cgmath::matrix::Mat4;
  use std;

  pub fn print_mat4(name: &str, mat: Mat4<f32>) {
    println!("{}:", name);
    for i in range(0u, 4) {
      for j in range(0u, 4) {
        print!("{} ", *mat.cr(i, j));
      }
      println("");
    }
    println("");
  }

  /// usage: let s = mvp_matrix; println(type_of(&s));
  pub fn type_of<T>(_: &T) -> &'static str {
    unsafe {
      (*std::unstable::intrinsics::get_tydesc::<T>()).name
    }
  }
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, shader_type: GLenum) -> GLuint {
  let shader = gl::CreateShader(shader_type);
  unsafe {
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrix_id: GLint,
  projection_matrix: Mat4<f32>,
  window: glfw::Window
}

fn get_projection_matrix() -> Mat4<f32> {
  let fov = angle::deg(45.0f32);
  let ratio = 4.0 / 3.0;
  let display_range_min = 0.1;
  let display_range_max = 100.0;
  projection::perspective(
    fov, ratio, display_range_min, display_range_max
  )
}

impl Win {
  fn new() -> Win {
    let mut win = Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrix_id: 0,
      projection_matrix: get_projection_matrix(),
      window: glfw::Window::create(
        WIN_SIZE.x,
        WIN_SIZE.y,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };
    win.init_opengl();
    win
  }

  fn init_opengl(&mut self) {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    self.window.make_context_current();
    self.window.set_cursor_pos_callback(~CursorPosContext);
    self.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    self.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    self.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    self.program = link_program(self.vertex_shader, self.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut self.vertex_array_obj);
      gl::BindVertexArray(self.vertex_array_obj);

      // Create a Vertex Buffer Object
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);

      // Copy vertex data to VBO
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(self.program);
      gl::BindFragDataLocation(
        self.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        self.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);

      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      self.matrix_id = gl::GetUniformLocation(
        self.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }
  }

  fn cleanup(&self) {
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let mut mvp_matrix = self.projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);

      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform for each model
      // you render, since the model_view_proj_matrix will be
      // different (at least the M part).
      gl::UniformMatrix4fv(self.matrix_id, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);
    self.window.swap_buffers();
  }

  fn is_running(&self) -> bool {
    return !self.window.should_close()
  }

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    let distance = 1.0;
    if action != glfw::Press {
      return;
    }
    match key {
      glfw::KeyEscape | glfw::KeyQ
                     => window.set_should_close(true),
      glfw::KeySpace => println!("space"),
      glfw::KeyUp    => unsafe { CAMERA_POS.y -= distance },
      glfw::KeyDown  => unsafe { CAMERA_POS.y += distance },
      glfw::KeyRight => unsafe { CAMERA_POS.x -= distance },
      glfw::KeyLeft  => unsafe { CAMERA_POS.x += distance },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<commit_msg>Fixed glfw::Window::create(..) call<commit_after>// See LICENSE file for copyright and license details.

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{
  Matrix,
  Mat4,
  Mat3,
  ToMat4
};
use cgmath::vector::{
  Vec3,
  Vec2
};
use cgmath::projection;
use cgmath::angle;

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

pub mod misc {
  use cgmath::matrix::Mat4;
  use std;

  pub fn print_mat4(name: &str, mat: Mat4<f32>) {
    println!("{}:", name);
    for i in range(0u, 4) {
      for j in range(0u, 4) {
        print!("{} ", *mat.cr(i, j));
      }
      println("");
    }
    println("");
  }

  /// usage: let s = mvp_matrix; println(type_of(&s));
  pub fn type_of<T>(_: &T) -> &'static str {
    unsafe {
      (*std::unstable::intrinsics::get_tydesc::<T>()).name
    }
  }
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, shader_type: GLenum) -> GLuint {
  let shader = gl::CreateShader(shader_type);
  unsafe {
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrix_id: GLint,
  projection_matrix: Mat4<f32>,
  window: glfw::Window
}

fn get_projection_matrix() -> Mat4<f32> {
  let fov = angle::deg(45.0f32);
  let ratio = 4.0 / 3.0;
  let display_range_min = 0.1;
  let display_range_max = 100.0;
  projection::perspective(
    fov, ratio, display_range_min, display_range_max
  )
}

impl Win {
  fn new() -> Win {
    let mut win = Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrix_id: 0,
      projection_matrix: get_projection_matrix(),
      window: glfw::Window::create(
        WIN_SIZE.x as u32,
        WIN_SIZE.y as u32,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };
    win.init_opengl();
    win
  }

  fn init_opengl(&mut self) {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    self.window.make_context_current();
    self.window.set_cursor_pos_callback(~CursorPosContext);
    self.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    self.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    self.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    self.program = link_program(self.vertex_shader, self.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut self.vertex_array_obj);
      gl::BindVertexArray(self.vertex_array_obj);

      // Create a Vertex Buffer Object
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);

      // Copy vertex data to VBO
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(self.program);
      gl::BindFragDataLocation(
        self.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        self.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);

      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      self.matrix_id = gl::GetUniformLocation(
        self.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }
  }

  fn cleanup(&self) {
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let mut mvp_matrix = self.projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);

      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform for each model
      // you render, since the model_view_proj_matrix will be
      // different (at least the M part).
      gl::UniformMatrix4fv(self.matrix_id, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);
    self.window.swap_buffers();
  }

  fn is_running(&self) -> bool {
    return !self.window.should_close()
  }

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    let distance = 1.0;
    if action != glfw::Press {
      return;
    }
    match key {
      glfw::KeyEscape | glfw::KeyQ
                     => window.set_should_close(true),
      glfw::KeySpace => println!("space"),
      glfw::KeyUp    => unsafe { CAMERA_POS.y -= distance },
      glfw::KeyDown  => unsafe { CAMERA_POS.y += distance },
      glfw::KeyRight => unsafe { CAMERA_POS.x -= distance },
      glfw::KeyLeft  => unsafe { CAMERA_POS.x += distance },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;
use isbfc::Token;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
             .long("dumpir")
             .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions")
             .args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
             .short("g")
             .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
             .short("o")
             .help("Output file name")
             .takes_value(true)
             .empty_values(false)
             .value_name("file"))
        .arg(Arg::with_name("tape_size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false)
             .value_name("bytes"))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let out_name = matches.value_of("out_name").unwrap_or(name);
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(&code);
    let tokens = optimize(tokens);

    if matches.is_present("dump_ir") {
        dump_ir(tokens);
    } else if matches.is_present("output_asm") {
        let output = compile(tokens, tape_size);
        let mut asmfile = File::create(format!("{}.s", name)).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let output = compile(tokens, tape_size);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn().unwrap();
    }
}

fn dump_ir(tokens: Vec<Token>) {
    for token in tokens.iter() {
        match *token {
            Output =>
                println!("output"),
            Input =>
                println!("input"),
            Loop =>
                println!("loop"),
            EndLoop =>
                println!("endloop"),
            Move(offset) =>
                println!("move(offset={})", offset),
            Add(offset, value) =>
                println!("add(offset={}, value={})", offset, value),
            Set(offset, value) =>
                println!("set(offset={}, value={})", offset, value),
            MulCopy(src, dest, mul) =>
                println!("mulcopy(src={}, dest={}, mul={})", src, dest, mul),
            Scan(offset) =>
                println!("scan(offset={})", offset),
            LoadOut(offset, add) =>
                println!("loadout(offset={}, add={})", offset, add),
            LoadOutSet(value) =>
                println!("loadoutset(value={})", value),
            If(offset) =>
                println!("if(offset={})", offset),
            EndIf =>
                println!("endif"),
        }
    }
}
<commit_msg>Handle combination of -o and -S<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;
use isbfc::Token;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
             .long("dumpir")
             .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions")
             .args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
             .short("g")
             .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
             .short("o")
             .help("Output file name")
             .takes_value(true)
             .empty_values(false)
             .value_name("file"))
        .arg(Arg::with_name("tape_size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false)
             .value_name("bytes"))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(&code);
    let tokens = optimize(tokens);

    if matches.is_present("dump_ir") {
        dump_ir(tokens);
    } else if matches.is_present("output_asm") {
        let output = compile(tokens, tape_size);
        let def_name = format!("{}.s", name);
        let out_name = matches.value_of("out_name").unwrap_or(&def_name);
        let mut asmfile = File::create(out_name).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let output = compile(tokens, tape_size);
        let out_name = matches.value_of("out_name").unwrap_or(name);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn().unwrap();
    }
}

fn dump_ir(tokens: Vec<Token>) {
    for token in tokens.iter() {
        match *token {
            Output =>
                println!("output"),
            Input =>
                println!("input"),
            Loop =>
                println!("loop"),
            EndLoop =>
                println!("endloop"),
            Move(offset) =>
                println!("move(offset={})", offset),
            Add(offset, value) =>
                println!("add(offset={}, value={})", offset, value),
            Set(offset, value) =>
                println!("set(offset={}, value={})", offset, value),
            MulCopy(src, dest, mul) =>
                println!("mulcopy(src={}, dest={}, mul={})", src, dest, mul),
            Scan(offset) =>
                println!("scan(offset={})", offset),
            LoadOut(offset, add) =>
                println!("loadout(offset={}, add={})", offset, add),
            LoadOutSet(value) =>
                println!("loadoutset(value={})", value),
            If(offset) =>
                println!("if(offset={})", offset),
            EndIf =>
                println!("endif"),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![allow(dead_code)]

//! Types used only within the transactor.  These should not be exposed outside of this crate.

use std::collections::HashMap;
use std::rc::Rc;

use errors;
use errors::ErrorKind;
use types::{
    AVMap,
    AVPair,
    Entid,
    TypedValue,
};
use mentat_tx::entities::{
    OpType,
    TempId,
};

#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum Term<E, V> {
    AddOrRetract(OpType, E, Entid, V),
}

#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum Either<L, R> {
    Left(L),
    Right(R),
}

use self::Either::*;

pub type EntidOr<T> = Either<Entid, T>;
pub type TypedValueOr<T> = Either<TypedValue, T>;

pub type TempIdHandle = Rc<TempId>;
pub type TempIdMap = HashMap<TempIdHandle, Entid>;

pub type LookupRef = Rc<AVPair>;

/// Internal representation of an entid on its way to resolution.  We either have the simple case (a
/// numeric entid), a lookup-ref that still needs to be resolved (an atomized [a v] pair), or a temp
/// ID that needs to be upserted or allocated (an atomized tempid).
#[derive(Clone,Debug,Eq,Hash,Ord,PartialOrd,PartialEq)]
pub enum LookupRefOrTempId {
    LookupRef(LookupRef),
    TempId(TempIdHandle)
}

pub type TermWithTempIdsAndLookupRefs = Term<EntidOr<LookupRefOrTempId>, TypedValueOr<LookupRefOrTempId>>;
pub type TermWithTempIds = Term<EntidOr<TempIdHandle>, TypedValueOr<TempIdHandle>>;
pub type TermWithoutTempIds = Term<Entid, TypedValue>;
pub type Population = Vec<TermWithTempIds>;

impl TermWithTempIds {
    // These have no tempids by definition, and just need to be unwrapped.  This operation might
    // also be called "lowering" or "level lowering", but the concept of "unwrapping" is common in
    // Rust and seems appropriate here.
    pub fn unwrap(self) -> TermWithoutTempIds {
        match self {
            Term::AddOrRetract(op, Left(n), a, Left(v)) => Term::AddOrRetract(op, n, a, v),
            _ => unreachable!(),
        }
    }
}

/// Given an `EntidOr` or a `TypedValueOr`, replace any internal `LookupRef` with the entid from
/// the given map.  Fail if any `LookupRef` cannot be replaced.
///
/// `lift` allows to specify how the entid found is mapped into the output type.  (This could
/// also be an `Into` or `From` requirement.)
///
/// The reason for this awkward expression is that we're parameterizing over the _type constructor_
/// (`EntidOr` or `TypedValueOr`), which is not trivial to express in Rust.  This only works because
/// they're both the same `Result<...>` type with different parameterizations.
pub fn replace_lookup_ref<T, U>(lookup_map: &AVMap, desired_or: Either<T, LookupRefOrTempId>, lift: U) -> errors::Result<Either<T, TempIdHandle>> where U: FnOnce(Entid) -> T {
    match desired_or {
        Left(desired) => Ok(Left(desired)), // N.b., must unwrap here -- the ::Left types are different!
        Right(other) => {
            match other {
                LookupRefOrTempId::TempId(t) => Ok(Right(t)),
                LookupRefOrTempId::LookupRef(av) => lookup_map.get(&*av)
                    .map(|x| lift(*x)).map(Left)
                    // XXX TODO: fix this error kind!
                    .ok_or_else(|| ErrorKind::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
            }
        }
    }
}
<commit_msg>Pre: Crib map_{left,right} for Either.<commit_after>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![allow(dead_code)]

//! Types used only within the transactor.  These should not be exposed outside of this crate.

use std::collections::HashMap;
use std::rc::Rc;

use errors;
use errors::ErrorKind;
use types::{
    AVMap,
    AVPair,
    Entid,
    TypedValue,
};
use mentat_tx::entities::{
    OpType,
    TempId,
};

#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum Term<E, V> {
    AddOrRetract(OpType, E, Entid, V),
}

#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum Either<L, R> {
    Left(L),
    Right(R),
}

// Cribbed from https://github.com/bluss/either/blob/f793721f3fdeb694f009e731b23a2858286bc0d6/src/lib.rs#L219-L259.
impl<L, R> Either<L, R> {
    pub fn map_left<F, M>(self, f: F) -> Either<M, R>
        where F: FnOnce(L) -> M
    {
        match self {
            Left(l) => Left(f(l)),
            Right(r) => Right(r),
        }
    }

    pub fn map_right<F, S>(self, f: F) -> Either<L, S>
        where F: FnOnce(R) -> S
    {
        match self {
            Left(l) => Left(l),
            Right(r) => Right(f(r)),
        }
    }
}

use self::Either::*;

pub type EntidOr<T> = Either<Entid, T>;
pub type TypedValueOr<T> = Either<TypedValue, T>;

pub type TempIdHandle = Rc<TempId>;
pub type TempIdMap = HashMap<TempIdHandle, Entid>;

pub type LookupRef = Rc<AVPair>;

/// Internal representation of an entid on its way to resolution.  We either have the simple case (a
/// numeric entid), a lookup-ref that still needs to be resolved (an atomized [a v] pair), or a temp
/// ID that needs to be upserted or allocated (an atomized tempid).
#[derive(Clone,Debug,Eq,Hash,Ord,PartialOrd,PartialEq)]
pub enum LookupRefOrTempId {
    LookupRef(LookupRef),
    TempId(TempIdHandle)
}

pub type TermWithTempIdsAndLookupRefs = Term<EntidOr<LookupRefOrTempId>, TypedValueOr<LookupRefOrTempId>>;
pub type TermWithTempIds = Term<EntidOr<TempIdHandle>, TypedValueOr<TempIdHandle>>;
pub type TermWithoutTempIds = Term<Entid, TypedValue>;
pub type Population = Vec<TermWithTempIds>;

impl TermWithTempIds {
    // These have no tempids by definition, and just need to be unwrapped.  This operation might
    // also be called "lowering" or "level lowering", but the concept of "unwrapping" is common in
    // Rust and seems appropriate here.
    pub fn unwrap(self) -> TermWithoutTempIds {
        match self {
            Term::AddOrRetract(op, Left(n), a, Left(v)) => Term::AddOrRetract(op, n, a, v),
            _ => unreachable!(),
        }
    }
}

/// Given an `EntidOr` or a `TypedValueOr`, replace any internal `LookupRef` with the entid from
/// the given map.  Fail if any `LookupRef` cannot be replaced.
///
/// `lift` allows to specify how the entid found is mapped into the output type.  (This could
/// also be an `Into` or `From` requirement.)
///
/// The reason for this awkward expression is that we're parameterizing over the _type constructor_
/// (`EntidOr` or `TypedValueOr`), which is not trivial to express in Rust.  This only works because
/// they're both the same `Result<...>` type with different parameterizations.
pub fn replace_lookup_ref<T, U>(lookup_map: &AVMap, desired_or: Either<T, LookupRefOrTempId>, lift: U) -> errors::Result<Either<T, TempIdHandle>> where U: FnOnce(Entid) -> T {
    match desired_or {
        Left(desired) => Ok(Left(desired)), // N.b., must unwrap here -- the ::Left types are different!
        Right(other) => {
            match other {
                LookupRefOrTempId::TempId(t) => Ok(Right(t)),
                LookupRefOrTempId::LookupRef(av) => lookup_map.get(&*av)
                    .map(|x| lift(*x)).map(Left)
                    // XXX TODO: fix this error kind!
                    .ok_or_else(|| ErrorKind::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
            }
        }
    }
}
<|endoftext|>