<commit_before>#![deny(warnings)]

extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, BufReader, Read, Stderr, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -v -E -T

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -v -E

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -v -T

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::new()
        };
        for arg in env::args().skip(1) {
            if arg.starts_with('-') {
                match arg.as_str() {
                    "-h" | "--help" => {
                        stdout.write(MAN_PAGE.as_bytes()).try(stderr);
                        stdout.flush().try(stderr);
                        exit(0);
                    }
                    "-A" | "--show-all" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                        cat.show_tabs = true;
                    },
                    "-b" | "--number-nonblank" => {
                        cat.number_nonblank = true;
                        cat.number = false;
                    },
                    "-e" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                    },
                    "-E" | "--show-ends" => cat.show_ends = true,
                    "-n" | "--number" => {
                        cat.number = true;
                        cat.number_nonblank = false;
                    },
                    "-s" | "--squeeze-blank" => cat.squeeze_blank = true,
                    "-t" => {
                        cat.show_nonprinting = true;
                        cat.show_tabs = true;
                    },
                    "-T" => cat.show_tabs = true,
                    "-v" | "--show-nonprinting" => {
                        cat.show_nonprinting = true;
                    },
                    _ => {
                        stderr.write(b"invalid option -- '").try(stderr);
                        stderr.write(arg.as_bytes()).try(stderr);
                        stderr.write(b"'\nTry 'cat --help' for more information.\n").try(stderr);
                        stderr.flush().try(stderr);
                        exit(1);
                    }
                }
            } else {
                cat.paths.push(arg);
            }
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();

        if self.paths.is_empty() {
            io::copy(&mut stdin, stdout).try(stderr);
        } else {
            let mut line_count = 1;
            for path in &self.paths {
                if self.number || self.number_nonblank || self.show_ends || self.show_tabs || self.squeeze_blank || self.show_nonprinting {
                    let file = match fs::File::open(&path) {
                        Ok(file) => file,
                        Err(message) => {
                            stderr.write(&path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);;
                            continue
                        }
                    };
                    let mut character_count = 0;
                    let mut last_line_was_blank = false;

                    for byte in BufReader::new(&file).bytes().map(|x| x.unwrap()) {
                        if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                            stdout.write(b"     ").try(stderr);
                            stdout.write(line_count.to_string().as_bytes()).try(stderr);
                            stdout.write(b"  ").try(stderr);
                            line_count += 1;
                        }
                        match byte {
                            0...8 | 11...31 => if self.show_nonprinting {
                                push_caret(stdout, stderr, byte+64);
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            },
                            9 => {
                                if self.show_tabs {
                                    push_caret(stdout, stderr, b'I');
                                } else {
                                    stdout.write(&[byte]).try(stderr);
                                }
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            }
                            10 => {
                                if character_count == 0 {
                                    if self.squeeze_blank && last_line_was_blank {
                                        continue
                                    } else if !last_line_was_blank {
                                        last_line_was_blank = true;
                                    }
                                } else {
                                    last_line_was_blank = false;
                                    character_count = 0;
                                }
                                if self.show_ends {
                                    stdout.write(b"$\n").try(stderr);
                                } else {
                                    stdout.write(b"\n").try(stderr);
                                }
                                stdout.flush().try(stderr);
                            },
                            32...126 => {
                                stdout.write(&[byte]).try(stderr);
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            },
                            127 => if self.show_nonprinting {
                                push_caret(stdout, stderr, b'?');
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            },
                            128...159 => if self.show_nonprinting {
                                stdout.write(b"M-^").try(stderr);
                                stdout.write(&[byte-64]).try(stderr);
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            } else {
                                stdout.write(&[byte]).try(stderr);
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            },
                            _ => if self.show_nonprinting {
                                stdout.write(b"M-").try(stderr);
                                stdout.write(&[byte-128]).try(stderr);
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            } else {
                                stdout.write(&[byte]).try(stderr);
                                count_character(&mut character_count, &self.number, &self.number_nonblank);
                            },
                        }
                    }
                } else if path == "-" {
                    io::copy(&mut stdin, stdout).try(stderr);
                } else {
                    let file = fs::File::open(&path);
                    let mut file = file.try(stderr);

                    io::copy(&mut file, stdout).try(stderr);
                }
            }
        }
        self.exit_status.get()
    }
}

/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<commit_msg>Allow Flags on Stdin<commit_after>#![deny(warnings)]

extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs::{self, File};
use std::io::{self, BufReader, Read, Stderr, StdinLock, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -v -E -T

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -v -E

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -v -T

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::new()
        };
        for arg in env::args().skip(1) {
            if arg.starts_with('-') && &arg != "-" {
                match arg.as_str() {
                    "-h" | "--help" => {
                        stdout.write(MAN_PAGE.as_bytes()).try(stderr);
                        stdout.flush().try(stderr);
                        exit(0);
                    }
                    "-A" | "--show-all" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                        cat.show_tabs = true;
                    },
                    "-b" | "--number-nonblank" => {
                        cat.number_nonblank = true;
                        cat.number = false;
                    },
                    "-e" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                    },
                    "-E" | "--show-ends" => cat.show_ends = true,
                    "-n" | "--number" => {
                        cat.number = true;
                        cat.number_nonblank = false;
                    },
                    "-s" | "--squeeze-blank" => cat.squeeze_blank = true,
                    "-t" => {
                        cat.show_nonprinting = true;
                        cat.show_tabs = true;
                    },
                    "-T" => cat.show_tabs = true,
                    "-v" | "--show-nonprinting" => {
                        cat.show_nonprinting = true;
                    },
                    _ => {
                        stderr.write(b"invalid option -- '").try(stderr);
                        stderr.write(arg.as_bytes()).try(stderr);
                        stderr.write(b"'\nTry 'cat --help' for more information.\n").try(stderr);
                        stderr.flush().try(stderr);
                        exit(1);
                    }
                }
            } else {
                cat.paths.push(arg);
            }
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let stdin = &mut stdin.lock();

        if self.paths.is_empty() {
            io::copy(stdin, stdout).try(stderr);
        } else {
            let flags_enabled = self.number || self.number_nonblank || self.show_ends || self.show_tabs ||
                                self.squeeze_blank || self.show_nonprinting;
            let line_count = &mut 0usize;
            for path in &self.paths {
                if flags_enabled && path == "-" {
                    self.cat_stdin(line_count, stdin, stdout, stderr);
                } else if flags_enabled {
                    let file = match fs::File::open(&path) {
                        Ok(file) => file,
                        Err(message) => {
                            stderr.write(&path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);;
                            continue
                        }
                    };

                    self.cat_file(&file, line_count, stdout, stderr);
                } else if path == "-" {
                    io::copy(stdin, stdout).try(stderr);
                } else {
                    let file = &mut match fs::File::open(&path) {
                        Ok(file) => file,
                        Err(message) => {
                            stderr.write(&path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);;
                            continue
                        }
                    };

                    io::copy(file, stdout).try(stderr);
                }
            }
        }
        self.exit_status.get()
    }

    /// Cats a file based on the flag arguments given to the program.
    fn cat_file(&self, file: &File, line_count: &mut usize, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in BufReader::new(file).bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }

    /// Cats stdin based on the flag arguments given to the program.
    fn cat_stdin(&self, line_count: &mut usize, stdin: &mut StdinLock, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in stdin.bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }
}

/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::BufReader;

extern crate libbig;
use libbig::{BigArchive, ReadError};

extern crate clap;
use clap::{Arg, App, AppSettings, SubCommand};

fn main() {
    let matches = App::new("sagebig")
        .version("0.1.0")
        .author("Taryn Hill <taryn@phrohdoh.com>")
        .about("CLI for libbig")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("list")
            .about("List all entries in an archive")
            .version("0.1.0")
            .author("Taryn Hill <taryn@phrohdoh.com>")
            .arg(Arg::with_name("archive_path")
                .value_name("archive_path")
                .required(true)
                .index(1)))
        .subcommand(SubCommand::with_name("search")
            .about("Locate entries with names containing a string")
            .version("0.1.0")
            .author("Taryn Hill <taryn@phrohdoh.com>")
            .arg(Arg::with_name("archive_path")
                .value_name("archive_path")
                .required(true)
                .index(1))
            .arg(Arg::with_name("query")
                .value_name("query")
                .required(true)
                .index(2)))
        .subcommand(SubCommand::with_name("contains")
            .about("Query an archive to determine if it contains an entry with a name")
            .version("0.1.0")
            .author("Taryn Hill <taryn@phrohdoh.com>")
            .arg(Arg::with_name("archive_path")
                .value_name("archive_path")
                .required(true)
                .index(1))
            .arg(Arg::with_name("query")
                .value_name("query")
                .required(true)
                .index(2)))
        .get_matches();

    let res = match matches.subcommand() {
        ("list", Some(args)) => cmd_list(args),
        ("search", Some(args)) => cmd_search(args),
        ("contains", Some(args)) => cmd_contains(args),
        _ => unreachable!(),
    };

    let code = if let Some(e) = res.err() {
        println!("Error: {:?}", e);
        255
    } else {
        0
    };

    std::process::exit(code);
}

fn load_archive(path: &str) -> Result<BigArchive, ReadError> {
    let f = try!(File::open(&path));
    let mut br = BufReader::new(f);
    Ok(try!(BigArchive::new_from_bufreader(&mut br)))
}

fn cmd_list(args: &clap::ArgMatches) -> Result<(), ReadError> {
    let path = args.value_of("archive_path").unwrap();
    let archive = try!(load_archive(&path));

    for name in archive.get_all_entry_names().collect::<Vec<_>>() {
        let entry = archive.get_entry(name)
            .expect(&format!("Failed to read known entry {} from {}", name, path));
        println!("{:#?}", entry);
    }

    Ok(())
}

fn cmd_search(args: &clap::ArgMatches) -> Result<(), ReadError> {
    let path = args.value_of("archive_path").unwrap();
    let archive = try!(load_archive(&path));
    let query = args.value_of("query").unwrap();

    for name in archive.get_all_entry_names().filter(|n| n.contains(query)).collect::<Vec<_>>() {
        let entry = archive.get_entry(name)
            .expect(&format!("Failed to read known entry {} from {}", name, path));
        println!("{:#?}", entry);
    }

    Ok(())
}

fn cmd_contains(args: &clap::ArgMatches) -> Result<(), ReadError> {
    let path = args.value_of("archive_path").unwrap();
    let archive = try!(load_archive(&path));
    let query = args.value_of("query").unwrap();

    println!("{} contains {}: {}", path, query, archive.contains(query));

    Ok(())
}
<commit_msg>Replace load_archive with inline BigArchive::new_from_path calls<commit_after>extern crate libbig;
use libbig::{BigArchive, ReadError};

extern crate clap;
use clap::{Arg, App, AppSettings, SubCommand};

fn main() {
    let matches = App::new("sagebig")
        .version("0.1.0")
        .author("Taryn Hill <taryn@phrohdoh.com>")
        .about("CLI for libbig")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("list")
            .about("List all entries in an archive")
            .version("0.1.0")
            .author("Taryn Hill <taryn@phrohdoh.com>")
            .arg(Arg::with_name("archive_path")
                .value_name("archive_path")
                .required(true)
                .index(1)))
        .subcommand(SubCommand::with_name("search")
            .about("Locate entries with names containing a string")
            .version("0.1.0")
            .author("Taryn Hill <taryn@phrohdoh.com>")
            .arg(Arg::with_name("archive_path")
                .value_name("archive_path")
                .required(true)
                .index(1))
            .arg(Arg::with_name("query")
                .value_name("query")
                .required(true)
                .index(2)))
        .subcommand(SubCommand::with_name("contains")
            .about("Query an archive to determine if it contains an entry with a name")
            .version("0.1.0")
            .author("Taryn Hill <taryn@phrohdoh.com>")
            .arg(Arg::with_name("archive_path")
                .value_name("archive_path")
                .required(true)
                .index(1))
            .arg(Arg::with_name("query")
                .value_name("query")
                .required(true)
                .index(2)))
        .get_matches();

    let res = match matches.subcommand() {
        ("list", Some(args)) => cmd_list(args),
        ("search", Some(args)) => cmd_search(args),
        ("contains", Some(args)) => cmd_contains(args),
        _ => unreachable!(),
    };

    let code = if let Some(e) = res.err() {
        println!("Error: {:?}", e);
        255
    } else {
        0
    };

    std::process::exit(code);
}

fn cmd_list(args: &clap::ArgMatches) -> Result<(), ReadError> {
    let path = args.value_of("archive_path").unwrap();
    let archive = try!(BigArchive::new_from_path(&path));

    for name in archive.get_all_entry_names().collect::<Vec<_>>() {
        let entry = archive.get_entry(name)
            .expect(&format!("Failed to read known entry {} from {}", name, path));
        println!("{:#?}", entry);
    }

    Ok(())
}

fn cmd_search(args: &clap::ArgMatches) -> Result<(), ReadError> {
    let path = args.value_of("archive_path").unwrap();
    let archive = try!(BigArchive::new_from_path(&path));
    let query = args.value_of("query").unwrap();

    for name in archive.get_all_entry_names().filter(|n| n.contains(query)).collect::<Vec<_>>() {
        let entry = archive.get_entry(name)
            .expect(&format!("Failed to read known entry {} from {}", name, path));
        println!("{:#?}", entry);
    }

    Ok(())
}

fn cmd_contains(args: &clap::ArgMatches) -> Result<(), ReadError> {
    let path = args.value_of("archive_path").unwrap();
    let archive = try!(BigArchive::new_from_path(&path));
    let query = args.value_of("query").unwrap();

    println!("{} contains {}: {}", path, query, archive.contains(query));

    Ok(())
}
<|endoftext|>


<commit_before>// The CPU
#![allow(non_snake_case)]
extern crate sdl2;
use c64::opcodes;
use c64::memory;
use c64::vic;
use c64::cia;
use std::cell::RefCell;
use std::rc::Rc;

use utils;

pub type CPUShared = Rc<RefCell<CPU>>;

// status flags for P register
pub enum StatusFlag
{
    Carry            = 1 << 0,
    Zero             = 1 << 1,
    InterruptDisable = 1 << 2,
    DecimalMode      = 1 << 3,
    Break            = 1 << 4,
    Unused           = 1 << 5,
    Overflow         = 1 << 6,
    Negative         = 1 << 7,
}

pub static NMI_VECTOR:   u16 = 0xFFFA;
pub static RESET_VECTOR: u16 = 0xFFFC;
pub static IRQ_VECTOR:   u16 = 0xFFFE;

pub struct CPU
{
    pub PC: u16, // program counter
    pub SP: u8,  // stack pointer
    pub P: u8,   // processor status
    pub A: u8,   // accumulator
    pub X: u8,   // index register
    pub Y: u8,   // index register
    pub mem_ref: Option<memory::MemShared>, // reference to shared system memory
    pub vic_ref: Option<vic::VICShared>,
    pub cia1_ref: Option<cia::CIAShared>,
    pub cia2_ref: Option<cia::CIAShared>,
    pub ba_low: bool,  // is BA low?
    pub cia_irq: bool,
    pub vic_irq: bool,
    wait_cycles: u8,
    nmi: bool,
    pub prev_PC: u16, // previous program counter - for debugging
    dfff_byte: u8,
    pub op_debugger: utils::OpDebugger
}

impl CPU
{
    pub fn new_shared() -> CPUShared
    {
        Rc::new(RefCell::new(CPU
        {
            PC: 0,
            SP: 0xFF,
            P: 0,
            A: 0,
            X: 0,
            Y: 0,
            mem_ref: None,
            vic_ref: None,
            cia1_ref: None,
            cia2_ref: None,
            ba_low: false,
            cia_irq: false,
            vic_irq: false,
            wait_cycles: 0,
            nmi: false,
            prev_PC: 0,
            dfff_byte: 0x55,
            op_debugger: utils::OpDebugger::new()
        }))
    }

    pub fn set_references(&mut self, memref: memory::MemShared, vicref: vic::VICShared, cia1ref: cia::CIAShared, cia2ref: cia::CIAShared)
    {
        self.mem_ref = Some(memref);
        self.vic_ref = Some(vicref);
        self.cia1_ref = Some(cia1ref);
        self.cia2_ref = Some(cia2ref);
    }

    pub fn set_status_flag(&mut self, flag: StatusFlag, value: bool)
    {
        if value { self.P |=   flag as u8;  }
        else     { self.P &= !(flag as u8); }
    }

    pub fn get_status_flag(&mut self, flag: StatusFlag) -> bool
    {
        self.P & flag as u8 != 0x00
    }

    // these flags will be set in tandem quite often
    pub fn set_zn_flags(&mut self, value: u8)
    {
        self.set_status_flag(StatusFlag::Zero, value == 0x00);
        self.set_status_flag(StatusFlag::Negative, (value as i8) < 0);
    }

    pub fn reset(&mut self)
    {
        // reset program counter
        let pc = self.read_word_le(RESET_VECTOR);
        self.PC = pc;
        self.SP = 0xFF;
        self.ba_low = false;
        self.cia_irq = false;
        self.vic_irq = false;
        self.nmi = false;
    }

    pub fn update(&mut self)
    {
        if self.ba_low { return }

        if self.process_nmi() { self.wait_cycles += 7; return }
        if self.process_irq() { self.wait_cycles += 7; return }

        if self.wait_cycles == 0
        {
            let op = self.next_byte();
            self.wait_cycles += self.process_op(op);
        }
        else
        {
            self.wait_cycles -= 1;
        }
    }

    pub fn next_byte(&mut self) -> u8
    {
        let pc = self.PC;
        let op = self.read_byte(pc);
        self.PC += 1;
        op
    }

    pub fn next_word(&mut self) -> u16
    {
        let word = self.read_word_le(self.PC);
        self.PC += 2;
        word
    }

    // stack memory: $0100 - $01FF (256 byes)
    // TODO: some extra message if stack over/underflow occurs? (right now handled by Rust)
    pub fn push_byte(&mut self, value: u8)
    {
        self.SP -= 0x01;
        let newSP = (self.SP + 0x01) as u16;
        self.write_byte(0x0100 + newSP, value);
    }

    pub fn pop_byte(&mut self) -> u8
    {
        let addr = 0x0100 + (self.SP + 0x01) as u16;
        let value = self.read_byte(addr);
        self.SP += 0x01;
        value
    }

    pub fn push_word(&mut self, value: u16)
    {
        self.SP -= 0x02;
        self.write_word_le(0x0100 + (self.SP + 0x01) as u16, value);
    }

    pub fn pop_word(&mut self) -> u16
    {
        let value = self.read_word_le(0x0100 + (self.SP + 0x01) as u16);
        self.SP += 0x02;
        value
    }

    pub fn write_byte(&mut self, addr: u16, value: u8) -> bool
    {
        let mut on_vic_write: vic::VICCallbackAction = vic::VICCallbackAction::None;
        let mut on_cia_write: cia::CIACallbackAction = cia::CIACallbackAction::None;
        let mem_write_ok: bool;
        let io_enabled = as_ref!(self.mem_ref).io_on;

        match addr
        {
            // VIC-II address space
            0xD000...0xD3FF => {
                if io_enabled
                {
                    mem_write_ok = true;
                    as_mut!(self.vic_ref).write_register(addr, value, &mut on_vic_write);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            // color RAM address space
            0xD800...0xDBFF => {
                if io_enabled
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value & 0x0F);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            // CIA1 address space
            0xDC00...0xDCFF => {
                if io_enabled
                {
                    mem_write_ok = as_mut!(self.cia1_ref).write_register(addr, value, &mut on_cia_write);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            // CIA2 address space
            0xDD00...0xDDFF => {
                if io_enabled
                {
                    mem_write_ok = as_mut!(self.cia2_ref).write_register(addr, value, &mut on_cia_write);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            _ => mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value),
        }

        // on VIC register write perform necessary action on the CPU
        match on_vic_write
        {
            vic::VICCallbackAction::TriggerVICIrq => self.trigger_vic_irq(),
            vic::VICCallbackAction::ClearVICIrq   => self.clear_vic_irq(),
            _ => (),
        }

        match on_cia_write
        {
            cia::CIACallbackAction::TriggerCIAIRQ => self.trigger_cia_irq(),
            cia::CIACallbackAction::ClearCIAIRQ   => self.clear_cia_irq(),
            cia::CIACallbackAction::TriggerNMI    => self.trigger_nmi(),
            cia::CIACallbackAction::ClearNMI      => self.clear_nmi(),
            _ => (),
        }

        mem_write_ok
    }

    pub fn read_byte(&mut self, addr: u16) -> u8
    {
        let byte: u8;
        let mut on_cia_read: cia::CIACallbackAction = cia::CIACallbackAction::None;
        let io_enabled = as_ref!(self.mem_ref).io_on;
        match addr
        {
            // VIC-II address space
            0xD000...0xD3FF => {
                if io_enabled
                {
                    byte = as_mut!(self.vic_ref).read_register(addr);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            // color RAM address space
            0xD800...0xDBFF => {
                if io_enabled
                {
                    byte = (as_ref!(self.mem_ref).read_byte(addr) & 0x0F) | (as_ref!(self.vic_ref).last_byte & 0xF0);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            // CIA1 address space
            0xDC00...0xDCFF => {
                if io_enabled
                {
                    byte = as_mut!(self.cia1_ref).read_register(addr, &mut on_cia_read);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            // CIA2 address space
            0xDD00...0xDDFF => {
                if io_enabled
                {
                    byte = as_mut!(self.cia2_ref).read_register(addr, &mut on_cia_read);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            0xDF00...0xDF9F => {
                if io_enabled
                {
                    byte = as_ref!(self.vic_ref).last_byte;
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            0xDFFF => {
                if io_enabled
                {
                    self.dfff_byte = !self.dfff_byte;
                    byte = self.dfff_byte;
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            _ => byte = as_mut!(self.mem_ref).read_byte(addr)
        }

        match on_cia_read
        {
            cia::CIACallbackAction::TriggerCIAIRQ => self.trigger_cia_irq(),
            cia::CIACallbackAction::ClearCIAIRQ   => self.clear_cia_irq(),
            cia::CIACallbackAction::TriggerNMI    => self.trigger_nmi(),
            cia::CIACallbackAction::ClearNMI      => self.clear_nmi(),
            _ => (),
        }

        byte
    }

    pub fn read_word_le(&self, addr: u16) -> u16
    {
        as_ref!(self.mem_ref).read_word_le(addr)
    }

    pub fn write_word_le(&self, addr: u16, value: u16) -> bool
    {
        as_ref!(self.mem_ref).write_word_le(addr, value)
    }

    fn process_nmi(&mut self) -> bool
    {
        // 7 cycles
        if self.nmi
        {
            let curr_pc = self.PC;
            let curr_p = self.P;
            self.push_word(curr_pc);
            self.push_byte(curr_p);
            self.set_status_flag(StatusFlag::InterruptDisable, true);
            self.PC = as_ref!(self.mem_ref).read_word_le(NMI_VECTOR);
            self.nmi = false;
            true
        }
        else
        {
            false
        }
    }

    fn process_irq(&mut self) -> bool
    {
        // 7 cycles
        if (self.cia_irq || self.vic_irq) && !self.get_status_flag(StatusFlag::InterruptDisable)
        {
            self.set_status_flag(StatusFlag::Break, false);
            let curr_pc = self.PC;
            let curr_p = self.P;
            //println!("PC {} P {}", curr_pc, curr_p);
            self.push_word(curr_pc);
            self.push_byte(curr_p);
            self.set_status_flag(StatusFlag::InterruptDisable, true);
            self.PC = as_ref!(self.mem_ref).read_word_le(IRQ_VECTOR);
            self.cia_irq = false;
            self.vic_irq = false;
            true
        }
        else
        {
            false
        }
    }

    pub fn trigger_vic_irq(&mut self)
    {
        // TODO:
        //println!("VIC irq");
        self.vic_irq = true;
    }

    pub fn clear_vic_irq(&mut self)
    {
        // TODO
        self.vic_irq = false;
    }

    pub fn trigger_nmi(&mut self)
    {
        // TODO
        //println!("NMI irq");
        self.nmi = true;
    }

    pub fn clear_nmi(&mut self)
    {
        // TODO
        self.nmi = false;
    }

    pub fn trigger_cia_irq(&mut self)
    {
        // TODO
        //println!("CIA irq");
        self.cia_irq = true;
    }

    pub fn clear_cia_irq(&mut self)
    {
        // TODO
        self.cia_irq = false;
    }

    fn process_op(&mut self, opcode: u8) -> u8
    {
        //utils::debug_instruction(opcode, self);
        self.prev_PC = self.PC;
        match opcodes::get_instruction(opcode, self)
        {
            Some((instruction, num_cycles, addr_mode)) => {
                //utils::debug_instruction(opcode, Some((&instruction, num_cycles, &addr_mode)), self);
                instruction.run(&addr_mode, self);
                num_cycles
            },
            None => panic!("No instruction - this should never happen! (0x{:02X} at ${:04X})", opcode, self.PC)
        }
    }
}
<commit_msg>handle nmi and irq slightly different<commit_after>// The CPU
#![allow(non_snake_case)]
extern crate sdl2;
use c64::opcodes;
use c64::memory;
use c64::vic;
use c64::cia;
use std::cell::RefCell;
use std::rc::Rc;

use utils;

pub type CPUShared = Rc<RefCell<CPU>>;

// status flags for P register
pub enum StatusFlag
{
    Carry            = 1 << 0,
    Zero             = 1 << 1,
    InterruptDisable = 1 << 2,
    DecimalMode      = 1 << 3,
    Break            = 1 << 4,
    Unused           = 1 << 5,
    Overflow         = 1 << 6,
    Negative         = 1 << 7,
}

pub static NMI_VECTOR:   u16 = 0xFFFA;
pub static RESET_VECTOR: u16 = 0xFFFC;
pub static IRQ_VECTOR:   u16 = 0xFFFE;

pub struct CPU
{
    pub PC: u16, // program counter
    pub SP: u8,  // stack pointer
    pub P: u8,   // processor status
    pub A: u8,   // accumulator
    pub X: u8,   // index register
    pub Y: u8,   // index register
    pub mem_ref: Option<memory::MemShared>, // reference to shared system memory
    pub vic_ref: Option<vic::VICShared>,
    pub cia1_ref: Option<cia::CIAShared>,
    pub cia2_ref: Option<cia::CIAShared>,
    pub ba_low: bool,  // is BA low?
    pub cia_irq: bool,
    pub vic_irq: bool,
    wait_cycles: u8,
    nmi: bool,
    pub prev_PC: u16, // previous program counter - for debugging
    dfff_byte: u8,
    pub op_debugger: utils::OpDebugger
}

impl CPU
{
    pub fn new_shared() -> CPUShared
    {
        Rc::new(RefCell::new(CPU
        {
            PC: 0,
            SP: 0xFF,
            P: 0,
            A: 0,
            X: 0,
            Y: 0,
            mem_ref: None,
            vic_ref: None,
            cia1_ref: None,
            cia2_ref: None,
            ba_low: false,
            cia_irq: false,
            vic_irq: false,
            wait_cycles: 0,
            nmi: false,
            prev_PC: 0,
            dfff_byte: 0x55,
            op_debugger: utils::OpDebugger::new()
        }))
    }

    pub fn set_references(&mut self, memref: memory::MemShared, vicref: vic::VICShared, cia1ref: cia::CIAShared, cia2ref: cia::CIAShared)
    {
        self.mem_ref = Some(memref);
        self.vic_ref = Some(vicref);
        self.cia1_ref = Some(cia1ref);
        self.cia2_ref = Some(cia2ref);
    }

    pub fn set_status_flag(&mut self, flag: StatusFlag, value: bool)
    {
        if value { self.P |=   flag as u8;  }
        else     { self.P &= !(flag as u8); }
    }

    pub fn get_status_flag(&mut self, flag: StatusFlag) -> bool
    {
        self.P & flag as u8 != 0x00
    }

    // these flags will be set in tandem quite often
    pub fn set_zn_flags(&mut self, value: u8)
    {
        self.set_status_flag(StatusFlag::Zero, value == 0x00);
        self.set_status_flag(StatusFlag::Negative, (value as i8) < 0);
    }

    pub fn reset(&mut self)
    {
        // reset program counter
        let pc = self.read_word_le(RESET_VECTOR);
        self.PC = pc;
        self.SP = 0xFF;
        self.ba_low = false;
        self.cia_irq = false;
        self.vic_irq = false;
        self.nmi = false;
    }

    pub fn update(&mut self)
    {
        if self.ba_low { return }

        if self.process_nmi() { self.wait_cycles += 7; }
        else if self.process_irq() { self.wait_cycles += 7; }

        if self.wait_cycles == 0
        {
            let op = self.next_byte();
            self.wait_cycles += self.process_op(op);
        }
        else
        {
            self.wait_cycles -= 1;
        }
    }

    pub fn next_byte(&mut self) -> u8
    {
        let pc = self.PC;
        let op = self.read_byte(pc);
        self.PC += 1;
        op
    }

    pub fn next_word(&mut self) -> u16
    {
        let word = self.read_word_le(self.PC);
        self.PC += 2;
        word
    }

    // stack memory: $0100 - $01FF (256 byes)
    // TODO: some extra message if stack over/underflow occurs? (right now handled by Rust)
    pub fn push_byte(&mut self, value: u8)
    {
        self.SP -= 0x01;
        let newSP = (self.SP + 0x01) as u16;
        self.write_byte(0x0100 + newSP, value);
    }

    pub fn pop_byte(&mut self) -> u8
    {
        let addr = 0x0100 + (self.SP + 0x01) as u16;
        let value = self.read_byte(addr);
        self.SP += 0x01;
        value
    }

    pub fn push_word(&mut self, value: u16)
    {
        self.SP -= 0x02;
        self.write_word_le(0x0100 + (self.SP + 0x01) as u16, value);
    }

    pub fn pop_word(&mut self) -> u16
    {
        let value = self.read_word_le(0x0100 + (self.SP + 0x01) as u16);
        self.SP += 0x02;
        value
    }

    pub fn write_byte(&mut self, addr: u16, value: u8) -> bool
    {
        let mut on_vic_write: vic::VICCallbackAction = vic::VICCallbackAction::None;
        let mut on_cia_write: cia::CIACallbackAction = cia::CIACallbackAction::None;
        let mem_write_ok: bool;
        let io_enabled = as_ref!(self.mem_ref).io_on;

        match addr
        {
            // VIC-II address space
            0xD000...0xD3FF => {
                if io_enabled
                {
                    mem_write_ok = true;
                    as_mut!(self.vic_ref).write_register(addr, value, &mut on_vic_write);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            // color RAM address space
            0xD800...0xDBFF => {
                if io_enabled
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value & 0x0F);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            // CIA1 address space
            0xDC00...0xDCFF => {
                if io_enabled
                {
                    mem_write_ok = as_mut!(self.cia1_ref).write_register(addr, value, &mut on_cia_write);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            // CIA2 address space
            0xDD00...0xDDFF => {
                if io_enabled
                {
                    mem_write_ok = as_mut!(self.cia2_ref).write_register(addr, value, &mut on_cia_write);
                }
                else
                {
                    mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value);
                }
            },
            _ => mem_write_ok = as_mut!(self.mem_ref).write_byte(addr, value),
        }

        // on VIC register write perform necessary action on the CPU
        match on_vic_write
        {
            vic::VICCallbackAction::TriggerVICIrq => self.trigger_vic_irq(),
            vic::VICCallbackAction::ClearVICIrq   => self.clear_vic_irq(),
            _ => (),
        }

        match on_cia_write
        {
            cia::CIACallbackAction::TriggerCIAIRQ => self.trigger_cia_irq(),
            cia::CIACallbackAction::ClearCIAIRQ   => self.clear_cia_irq(),
            cia::CIACallbackAction::TriggerNMI    => self.trigger_nmi(),
            cia::CIACallbackAction::ClearNMI      => self.clear_nmi(),
            _ => (),
        }

        mem_write_ok
    }

    pub fn read_byte(&mut self, addr: u16) -> u8
    {
        let byte: u8;
        let mut on_cia_read: cia::CIACallbackAction = cia::CIACallbackAction::None;
        let io_enabled = as_ref!(self.mem_ref).io_on;
        match addr
        {
            // VIC-II address space
            0xD000...0xD3FF => {
                if io_enabled
                {
                    byte = as_mut!(self.vic_ref).read_register(addr);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            // color RAM address space
            0xD800...0xDBFF => {
                if io_enabled
                {
                    byte = (as_ref!(self.mem_ref).read_byte(addr) & 0x0F) | (as_ref!(self.vic_ref).last_byte & 0xF0);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            // CIA1 address space
            0xDC00...0xDCFF => {
                if io_enabled
                {
                    byte = as_mut!(self.cia1_ref).read_register(addr, &mut on_cia_read);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            // CIA2 address space
            0xDD00...0xDDFF => {
                if io_enabled
                {
                    byte = as_mut!(self.cia2_ref).read_register(addr, &mut on_cia_read);
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            0xDF00...0xDF9F => {
                if io_enabled
                {
                    byte = as_ref!(self.vic_ref).last_byte;
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            0xDFFF => {
                if io_enabled
                {
                    self.dfff_byte = !self.dfff_byte;
                    byte = self.dfff_byte;
                }
                else
                {
                    byte = as_mut!(self.mem_ref).read_byte(addr);
                }
            },
            _ => byte = as_mut!(self.mem_ref).read_byte(addr)
        }

        match on_cia_read
        {
            cia::CIACallbackAction::TriggerCIAIRQ => self.trigger_cia_irq(),
            cia::CIACallbackAction::ClearCIAIRQ   => self.clear_cia_irq(),
            cia::CIACallbackAction::TriggerNMI    => self.trigger_nmi(),
            cia::CIACallbackAction::ClearNMI      => self.clear_nmi(),
            _ => (),
        }

        byte
    }

    pub fn read_word_le(&self, addr: u16) -> u16
    {
        as_ref!(self.mem_ref).read_word_le(addr)
    }

    pub fn write_word_le(&self, addr: u16, value: u16) -> bool
    {
        as_ref!(self.mem_ref).write_word_le(addr, value)
    }

    fn process_nmi(&mut self) -> bool
    {
        // 7 cycles
        if self.nmi
        {
            let curr_pc = self.PC;
            let curr_p = self.P;
            self.push_word(curr_pc);
            self.push_byte(curr_p);
            self.set_status_flag(StatusFlag::InterruptDisable, true);
            self.PC = as_ref!(self.mem_ref).read_word_le(NMI_VECTOR);
            self.nmi = false;
            true
        }
        else
        {
            false
        }
    }

    fn process_irq(&mut self) -> bool
    {
        // 7 cycles
        if (self.cia_irq || self.vic_irq) && !self.get_status_flag(StatusFlag::InterruptDisable)
        {
            self.set_status_flag(StatusFlag::Break, false);
            let curr_pc = self.PC;
            let curr_p = self.P;
            //println!("PC {} P {}", curr_pc, curr_p);
            self.push_word(curr_pc);
            self.push_byte(curr_p);
            self.set_status_flag(StatusFlag::InterruptDisable, true);
            self.PC = as_ref!(self.mem_ref).read_word_le(IRQ_VECTOR);
            self.cia_irq = false;
            self.vic_irq = false;
            true
        }
        else
        {
            false
        }
    }

    pub fn trigger_vic_irq(&mut self)
    {
        // TODO:
        //println!("VIC irq");
        self.vic_irq = true;
    }

    pub fn clear_vic_irq(&mut self)
    {
        // TODO
        self.vic_irq = false;
    }

    pub fn trigger_nmi(&mut self)
    {
        // TODO
        //println!("NMI irq");
        self.nmi = true;
    }

    pub fn clear_nmi(&mut self)
    {
        // TODO
        self.nmi = false;
    }

    pub fn trigger_cia_irq(&mut self)
    {
        // TODO
        //println!("CIA irq");
        self.cia_irq = true;
    }

    pub fn clear_cia_irq(&mut self)
    {
        // TODO
        self.cia_irq = false;
    }

    fn process_op(&mut self, opcode: u8) -> u8
    {
        //utils::debug_instruction(opcode, self);
        self.prev_PC = self.PC;
        match opcodes::get_instruction(opcode, self)
        {
            Some((instruction, num_cycles, addr_mode)) => {
                //utils::debug_instruction(opcode, Some((&instruction, num_cycles, &addr_mode)), self);
                instruction.run(&addr_mode, self);
                num_cycles
            },
            None => panic!("No instruction - this should never happen! (0x{:02X} at ${:04X})", opcode, self.PC)
        }
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate url;

use std::from_str::from_str;
use std::io::net::tcp::{TcpListener,TcpStream};
use std::io::{Acceptor,Listener,Process};
use url::Url;

// Note: Error handling could be improved
fn main() {
	let browserCommand = "firefox";
	let address = "127.0.0.1:7777";

	// Prepare a socket listening on localhost:7777
	let addr = from_str(address).expect(format!("Invalid address: {}", address));
	let listener = TcpListener::bind(addr).unwrap();
	let mut acceptor = listener.listen().unwrap();

	// Infinite loop to keep handling new connections.
	loop {
		let tcpStream = acceptor.accept().unwrap();
		debug!("Accepted new connection");
		spawn(proc() {
			handleClient(tcpStream, browserCommand)
		});
	}
}

// Accept a new connection and listen for URLs
fn handleClient(tcpStream: TcpStream, browserCommand: &'static str) {
	debug!("Spawned new task");

	let mut tcpStream = tcpStream;

	// Note that as soon as read_to_str() returns, everything sent
	// to the socket after this point will be discarded as once
	// we're done working with the content we've just read, the
	// tcpStream will be freed.
	let message = tcpStream.read_to_str().unwrap();

	// Iterate over the lines in the received message
	for line in message.lines() {
		debug!("Current line is: {}", line);

		// This tries to convert the line to a Url struct
		let url = from_str(line);

		// On failure it returns None; on success a valid URL
		match url {
			None     => { info!("No Url found") }
			Some(u)  => {
				debug!("Found Url in: {}", line);
				if checkUrl(&u) {
					spawnProcess(&u, browserCommand)
				}
			}
		}
	}
}

// Check that the URL is actually usable
fn checkUrl(u: &Url) -> bool {
	(u.scheme == ~"http" || u.scheme == ~"https" ) && u.host != ~""
}

// Spawn a browser to access the URL
fn spawnProcess(u: &Url, command: &'static str) {
	debug!("Spawning process {} {}", command, u.to_str());
	let mut child = Process::new(command, [u.to_str()]).unwrap();
	child.wait();
}
<commit_msg>Update for Rust 0.11<commit_after>#![feature(phase)]
#[phase(plugin, link)] extern crate log;
extern crate url;

use std::from_str::from_str;
use std::io::net::tcp::{TcpListener,TcpStream};
use std::io::{Acceptor,Listener,Command};
use std::string::String;
use url::Url;

// Note: Error handling could be improved
fn main() {
	let browserCommand = "firefox";
	let address = "127.0.0.1";
	let port = 7777;

	// Prepare a socket listening on localhost:7777
	let listener = TcpListener::bind(address, port).unwrap();
	let mut acceptor = listener.listen().unwrap();

	// Infinite loop to keep handling new connections.
	loop {
		let tcpStream = acceptor.accept().unwrap();
		debug!("Accepted new connection");
		spawn(proc() {
			handle_client(tcpStream, browserCommand)
		});
	}
}

// Accept a new connection and listen for URLs
fn handle_client(tcpStream: TcpStream, browserCommand: &'static str) {
	debug!("Spawned new task");

	let mut tcpStream = tcpStream;

	// Note that as soon as read_to_str() returns, everything sent
	// to the socket after this point will be discarded as once
	// we're done working with the content we've just read, the
	// tcpStream will be freed.
	let message = tcpStream.read_to_str().unwrap();

	// Iterate over the lines in the received message
	for line in message.as_slice().split('\n') {
		debug!("Current line is: {}", line);

		// This tries to convert the line to a Url struct
		let url = from_str(line);

		// On failure it returns None; on success a valid URL
		match url {
			None     => { info!("No Url found") }
			Some(u)  => {
				debug!("Found Url in: {}", line);
				if check_url(&u) {
					spawn_process(&u, browserCommand)
				}
			}
		}
	}
}

// Check that the URL is actually usable
fn check_url(u: &Url) -> bool {
	(u.scheme == String::from_str("http") ||
	 u.scheme == String::from_str("https"))
		&& !u.host.is_empty()
}

// Spawn a browser to access the URL
fn spawn_process(u: &Url, command: &'static str) {
	debug!("Spawning process: {} {}", command, u.to_str());
	let mut child = match Command::new(command).arg(u.to_str()).spawn() {
		Ok(child) => child,
		Err(e)    => fail!("Failed to spawn process: {}. {}", command, e),
	};
	child.wait().unwrap().success();
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use ast::Expr;
use val::Val;

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError,
    IncorrectSpecialForm,
    IncorrectNumberOfArguments,
    IncorrectTypeOfArgument
}

pub struct Context {
    env: HashMap<String, Val>,
}

impl Context {
    pub fn new() -> Context {
        let mut ctx = Context {
            env: HashMap::new(),
        };
        ctx.env.insert("nil".to_string(), Val::List(vec![]));
        ctx.env.insert("true".to_string(), Val::Bool(true));
        ctx.env.insert("false".to_string(), Val::Bool(false));
        ctx
    }

    pub fn eval(&mut self, s: &Expr) -> Result<Val, EvalError> {
        match *s {
            Expr::Number(n) => {
                Ok(Val::Number(n))
            },
            Expr::Symbol(ref name) => {
                if let Some(v) = self.env.get(name) {
                    Ok(v.clone())
                } else {
                    Err(EvalError::EvalError)
                }
            },
            Expr::String(ref s) => {
                Ok(Val::String(s.clone()))
            },
            Expr::List(ref l) => {
                if let Expr::Symbol(ref n) = *l.first().unwrap() {
                    match &n[..] {
                        "def" => {
                            self.eval_def(s)
                        },
                        "+" => {
                            self.eval_plus(s)
                        },
                        "-" => {
                            self.eval_minus(s)
                        },
                        "/" => {
                            self.eval_div(s)
                        },
                        "*" => {
                            self.eval_mul(s)
                        },
                        _ => {
                            Err(EvalError::EvalError)
                        }
                    }
                } else {
                    Err(EvalError::EvalError)
                }
            }
        }
    }

    fn eval_def(&mut self, s: &Expr) -> Result<Val, EvalError> {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "def" => {
                        if l.len() == 3 {
                            if let Expr::Symbol(ref n) = l[1] {
                                self.eval(&l[2]).and_then(|v| {
                                    self.env.insert(n.clone(), v.clone());
                                    Ok(v)
                                })
                            } else {
                                Err(EvalError::EvalError)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_plus(&mut self, s: &Expr) -> Result<Val, EvalError> {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "+" => {
                        if l.len() > 1 {
                            let mut a = 0_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Val::Number(n)) => {
                                        a += n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Val::Number(a))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_minus(&mut self, s: &Expr) -> Result<Val, EvalError> {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "-" => {
                        if l.len() > 1 {
                            if let Expr::Number(n) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Val::Number(n)) => {
                                            a -= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Val::Number(a))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_div(&mut self, s: &Expr) -> Result<Val, EvalError> {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "/" => {
                        if l.len() > 1 {
                            if let Expr::Number(n) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Val::Number(n)) => {
                                            a /= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Val::Number(a))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_mul(&mut self, s: &Expr) -> Result<Val, EvalError> {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "*" => {
                        if l.len() > 1 {
                            let mut a = 1_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Val::Number(n)) => {
                                        a *= n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Val::Number(a))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::Expr;
    use val::Val;
    use super::Context;
    use super::EvalError::EvalError;

    #[test]
    fn test_eval_number_to_itself() {
        let num = 10_f64;
        let mut ctx = Context::new();
        let expected_result = Val::Number(num);
        let actual_result = ctx.eval(&Expr::Number(num));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_string_to_itself() {
        let s = "rust is awesome";
        let mut ctx = Context::new();
        let expected_result = Val::String(s.to_string());
        let actual_result = ctx.eval(&Expr::String(s.to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_undefined_symbol_to_error() {
        let mut ctx = Context::new();
        let expected_result = EvalError;
        let actual_result = ctx.eval(&Expr::Symbol("a".to_string()));
        assert_eq!(expected_result, actual_result.err().unwrap());
    }

    #[test]
    fn test_eval_true_to_matching_bool() {
        let mut ctx = Context::new();
        let expected_result = Val::Bool(true);
        let actual_result = ctx.eval(&Expr::Symbol("true".to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_false_to_matching_bool() {
        let mut ctx = Context::new();
        let expected_result = Val::Bool(false);
        let actual_result = ctx.eval(&Expr::Symbol("false".to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nil_to_empty_list() {
        let mut ctx = Context::new();
        let expected_result = Val::List(vec![]);
        let actual_result = ctx.eval(&Expr::Symbol("nil".to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_def_special_form() {
        let num = 1_f64;
        let mut ctx = Context::new();
        let expected_result = Val::Number(num);
        let actual_input = Expr::List(vec![Expr::Symbol("def".to_string()),
                                           Expr::Symbol("a".to_string()),
                                           Expr::Number(num)]);
        let actual_result = ctx.eval(&actual_input);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nested_plus_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("+".to_string()),
                                           Expr::List(vec![Expr::Symbol("+".to_string()),
                                                           Expr::Number(1_f64),
                                                           Expr::Number(2_f64)]),
                                           Expr::Number(3_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(6_f64);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_minus_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("-".to_string()),
                                           Expr::Number(3_f64),
                                           Expr::Number(2_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(1_f64);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_div_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("/".to_string()),
                                           Expr::Number(3_f64),
                                           Expr::Number(2_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(1.5);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_mul_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("*".to_string()),
                                           Expr::Number(3.5),
                                           Expr::Number(2_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(7_f64);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_plus_special_form_using_defined_symbols() {
        let mut ctx = Context::new();
        ctx.eval(&Expr::List(vec![Expr::Symbol("def".to_string()),
                                  Expr::Symbol("a".to_string()),
                                  Expr::Number(1_f64)])).ok().unwrap();
        ctx.eval(&Expr::List(vec![Expr::Symbol("def".to_string()),
                                  Expr::Symbol("b".to_string()),
                                  Expr::Number(2_f64)])).ok().unwrap();
        let expected_result = Val::Number(3_f64);
        let actual_result = ctx.eval(&Expr::List(vec![Expr::Symbol("+".to_string()),
                                                      Expr::Symbol("a".to_string()),
                                                      Expr::Symbol("b".to_string())]));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }
}
<commit_msg>Make aliase for eval result type<commit_after>use std::collections::HashMap;
use ast::Expr;
use val::Val;

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError,
    IncorrectSpecialForm,
    IncorrectNumberOfArguments,
    IncorrectTypeOfArgument
}

type EvalResult = Result<Val, EvalError>;

pub struct Context {
    env: HashMap<String, Val>,
}

impl Context {
    pub fn new() -> Context {
        let mut ctx = Context {
            env: HashMap::new(),
        };
        ctx.env.insert("nil".to_string(), Val::List(vec![]));
        ctx.env.insert("true".to_string(), Val::Bool(true));
        ctx.env.insert("false".to_string(), Val::Bool(false));
        ctx
    }

    pub fn eval(&mut self, s: &Expr) -> EvalResult {
        match *s {
            Expr::Number(n) => {
                Ok(Val::Number(n))
            },
            Expr::Symbol(ref name) => {
                if let Some(v) = self.env.get(name) {
                    Ok(v.clone())
                } else {
                    Err(EvalError::EvalError)
                }
            },
            Expr::String(ref s) => {
                Ok(Val::String(s.clone()))
            },
            Expr::List(ref l) => {
                if let Expr::Symbol(ref n) = *l.first().unwrap() {
                    match &n[..] {
                        "def" => {
                            self.eval_def(s)
                        },
                        "+" => {
                            self.eval_plus(s)
                        },
                        "-" => {
                            self.eval_minus(s)
                        },
                        "/" => {
                            self.eval_div(s)
                        },
                        "*" => {
                            self.eval_mul(s)
                        },
                        _ => {
                            Err(EvalError::EvalError)
                        }
                    }
                } else {
                    Err(EvalError::EvalError)
                }
            }
        }
    }

    fn eval_def(&mut self, s: &Expr) -> EvalResult {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "def" => {
                        if l.len() == 3 {
                            if let Expr::Symbol(ref n) = l[1] {
                                self.eval(&l[2]).and_then(|v| {
                                    self.env.insert(n.clone(), v.clone());
                                    Ok(v)
                                })
                            } else {
                                Err(EvalError::EvalError)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_plus(&mut self, s: &Expr) -> EvalResult {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "+" => {
                        if l.len() > 1 {
                            let mut a = 0_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Val::Number(n)) => {
                                        a += n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Val::Number(a))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_minus(&mut self, s: &Expr) -> EvalResult {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "-" => {
                        if l.len() > 1 {
                            if let Expr::Number(n) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Val::Number(n)) => {
                                            a -= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Val::Number(a))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_div(&mut self, s: &Expr) -> EvalResult {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "/" => {
                        if l.len() > 1 {
                            if let Expr::Number(n) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Val::Number(n)) => {
                                            a /= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Val::Number(a))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_mul(&mut self, s: &Expr) -> EvalResult {
        if let Expr::List(ref l) = *s {
            if let Expr::Symbol(ref n) = *l.first().unwrap() {
                match &n[..] {
                    "*" => {
                        if l.len() > 1 {
                            let mut a = 1_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Val::Number(n)) => {
                                        a *= n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Val::Number(a))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::Expr;
    use val::Val;
    use super::Context;
    use super::EvalError::EvalError;

    #[test]
    fn test_eval_number_to_itself() {
        let num = 10_f64;
        let mut ctx = Context::new();
        let expected_result = Val::Number(num);
        let actual_result = ctx.eval(&Expr::Number(num));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_string_to_itself() {
        let s = "rust is awesome";
        let mut ctx = Context::new();
        let expected_result = Val::String(s.to_string());
        let actual_result = ctx.eval(&Expr::String(s.to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_undefined_symbol_to_error() {
        let mut ctx = Context::new();
        let expected_result = EvalError;
        let actual_result = ctx.eval(&Expr::Symbol("a".to_string()));
        assert_eq!(expected_result, actual_result.err().unwrap());
    }

    #[test]
    fn test_eval_true_to_matching_bool() {
        let mut ctx = Context::new();
        let expected_result = Val::Bool(true);
        let actual_result = ctx.eval(&Expr::Symbol("true".to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_false_to_matching_bool() {
        let mut ctx = Context::new();
        let expected_result = Val::Bool(false);
        let actual_result = ctx.eval(&Expr::Symbol("false".to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nil_to_empty_list() {
        let mut ctx = Context::new();
        let expected_result = Val::List(vec![]);
        let actual_result = ctx.eval(&Expr::Symbol("nil".to_string()));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_def_special_form() {
        let num = 1_f64;
        let mut ctx = Context::new();
        let expected_result = Val::Number(num);
        let actual_input = Expr::List(vec![Expr::Symbol("def".to_string()),
                                           Expr::Symbol("a".to_string()),
                                           Expr::Number(num)]);
        let actual_result = ctx.eval(&actual_input);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nested_plus_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("+".to_string()),
                                           Expr::List(vec![Expr::Symbol("+".to_string()),
                                                           Expr::Number(1_f64),
                                                           Expr::Number(2_f64)]),
                                           Expr::Number(3_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(6_f64);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_minus_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("-".to_string()),
                                           Expr::Number(3_f64),
                                           Expr::Number(2_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(1_f64);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_div_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("/".to_string()),
                                           Expr::Number(3_f64),
                                           Expr::Number(2_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(1.5);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_mul_special_form() {
        let mut ctx = Context::new();
        let actual_input = Expr::List(vec![Expr::Symbol("*".to_string()),
                                           Expr::Number(3.5),
                                           Expr::Number(2_f64)]);
        let actual_result = ctx.eval(&actual_input);
        let expected_result = Val::Number(7_f64);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_plus_special_form_using_defined_symbols() {
        let mut ctx = Context::new();
        ctx.eval(&Expr::List(vec![Expr::Symbol("def".to_string()),
                                  Expr::Symbol("a".to_string()),
                                  Expr::Number(1_f64)])).ok().unwrap();
        ctx.eval(&Expr::List(vec![Expr::Symbol("def".to_string()),
                                  Expr::Symbol("b".to_string()),
                                  Expr::Number(2_f64)])).ok().unwrap();
        let expected_result = Val::Number(3_f64);
        let actual_result = ctx.eval(&Expr::List(vec![Expr::Symbol("+".to_string()),
                                                      Expr::Symbol("a".to_string()),
                                                      Expr::Symbol("b".to_string())]));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }
}
<|endoftext|>