<commit_before>// parser/mod.rs

use std::collections::HashMap;
use std::iter::Peekable;
use std::str::Chars;
use utils::{Result, err_position, Position};
use super::compiler::abstract_tree::{AbstractTree, TokenType};

struct Parser<'a> {
    table: HashMap<char, fn(&mut Parser) -> Result<Option<AbstractTree>>>,
    stream: Peekable<Chars<'a>>,
    position: Position,
}

impl<'a> Parser<'a> {
    fn new(string: &'a str) -> Parser<'a> {
        Parser {
            table: HashMap::new(),
            stream: string.chars().peekable(),
            position: Position(0, 0),
        }
    }

    fn advance_char(&mut self) -> Option<char> {
        let current_char = self.stream.next();
        match current_char {
            Some(c) => {
                if c == '\n' {
                    // column set to 0
                    // line incremented
                    self.position.0 += 1;
                    self.position.1 = 0;
                } else {
                    // column incremented
                    self.position.1 += 1;
                }
            }
            None => {}
        }
        current_char
    }

    fn at_eof(&mut self) -> bool {
        self.current_char().is_none()
    }

    fn read_as(mut self, key: char, f: fn(&mut Parser) -> Result<Option<AbstractTree>>) -> Parser<'a> {
        self.table.insert(key, f);
        self
    }

    fn parse_expression(&mut self) -> Result<Option<AbstractTree>> {
        let current_reader = self.current_reader().unwrap_or(Parser::default_parse);
        current_reader(self)
    }

    fn current_reader(&mut self) -> Option<fn(&mut Parser) -> Result<Option<AbstractTree>>> {
        if self.current_char().is_none() {
            return None;
        }
        self.table.get(self.stream.peek().unwrap()).map(|f| *f)
    }

    fn current_char(&mut self) -> Option<&char> {
        self.stream.peek()
    }

    fn default_parse<'b>(parser: &mut Parser) -> Result<Option<AbstractTree>> {
        let mut chars = String::new();
        let starting_position = parser.position.clone();

        loop {
            // refactor this to a while - let
            // if parser.at_eof() { break; } // I don't think this is necessary
            let current_reader = parser.current_reader();
            match current_reader {
                Some(f) => {
                    break;
                }
                None => {
                    match parser.advance_char() {
                        Some(c) => chars.push(c),
                        None => {
                            break;
                        }
                    }
                }
            }
        }
        if chars.len() == 0 {
            return Ok(None);
        }
        Ok(Some(AbstractTree::Token(TokenType::Symbol, chars, starting_position)))
    }
}

fn no_op(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(None)
}

fn close_paren(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, "close paren".to_string(), Position(0, 0))))
}

fn open_paren(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    let starting_position = parser.position.clone();
    parser.advance_char();
    let mut accumulator = vec![];
    loop {
        let expression = parser.parse_expression();
        match expression {
            Ok(Some(AbstractTree::Token(TokenType::Flag, s, _))) => {
                if s == "close paren".to_string() {
                    break;
                }
            }
            Ok(Some(a)) => accumulator.push(a),
            Ok(None) => {
                if parser.at_eof() {
                    return err_position(starting_position, "hit end of file while reading an open paren".to_string())
                }
            }
            error @ Err(_) => return error,
        }
    }
    Ok(Some(AbstractTree::Node(accumulator, starting_position)))
}

pub fn parse(string: &str) -> Result<Option<AbstractTree>> {
    Parser::new(string)
        .read_as(' ', no_op)
        .read_as(')', close_paren)
        .read_as('(', open_paren)
        .parse_expression()
}

#[cfg(test)]
mod tests {
    use parser::parse;
    use compiler::abstract_tree::AbstractTree::*;
    use compiler::abstract_tree::TokenType::*;
    use utils::{err_position, Position, Error};

    #[test]
    fn test_parse_symbol() {
        assert_eq!(
            parse("symbol").unwrap().unwrap(),
            Token(Symbol, "symbol".to_string(), Position(0,0))
        )
    }

    #[test]
    fn test_parse_parentheses() {
        assert_eq!(
            parse("(hi there)").unwrap().unwrap(),
            Node(vec![Token(Symbol, "hi".to_string(), Position(0,1)), Token(Symbol, "there".to_string(), Position(0,4))], Position(0,0))
        );

        // Try with two levels
        assert_eq!(
            parse("(hi (one) there)").unwrap().unwrap(),
            Node(vec![Token(Symbol, "hi".to_string(), Position(0,1)), Node(vec![Token(Symbol, "one".to_string(), Position(0,5))], Position(0,4)), Token(Symbol, "there".to_string(), Position(0, 10))], Position(0,0))
        );
    }

    #[test]
    fn test_fail_parse_parentheses() {
        match parse("(hi there") {
            Ok(_) => panic!("I'm assertng this should not parse correctly"),
            Err(Error { description: description, position: _ }) => {
                assert_eq!("hit end of file while reading an open paren".to_string(), description);
            }
        }
    }
}
<commit_msg>polish preliminary parser<commit_after>// parser/mod.rs

use std::collections::HashMap;
use std::iter::Peekable;
use std::str::Chars;
use utils::{Result, err_position, Position};
use super::compiler::abstract_tree::{AbstractTree, TokenType};

struct Parser<'a> {
    table: HashMap<char, fn(&mut Parser) -> Result<Option<AbstractTree>>>,
    stream: Peekable<Chars<'a>>,
    position: Position,
}

impl<'a> Parser<'a> {
    fn new(string: &'a str) -> Parser<'a> {
        Parser {
            table: HashMap::new(),
            stream: string.chars().peekable(),
            position: Position(0, 0),
        }
    }

    fn advance_char(&mut self) -> Option<char> {
        let current_char = self.stream.next();
        match current_char {
            Some(c) => {
                if c == '\n' {
                    // column set to 0
                    // line incremented
                    self.position.0 += 1;
                    self.position.1 = 0;
                } else {
                    // column incremented
                    self.position.1 += 1;
                }
            }
            None => {}
        }
        current_char
    }

    fn at_eof(&mut self) -> bool {
        self.current_char().is_none()
    }

    fn read_as(mut self, key: char, f: fn(&mut Parser) -> Result<Option<AbstractTree>>) -> Parser<'a> {
        self.table.insert(key, f);
        self
    }

    fn parse_expression(&mut self) -> Result<Option<AbstractTree>> {
        let current_reader = self.current_reader().unwrap_or(Parser::default_parse);
        current_reader(self)
    }

    fn current_reader(&mut self) -> Option<fn(&mut Parser) -> Result<Option<AbstractTree>>> {
        if self.current_char().is_none() {
            return None;
        }
        self.table.get(self.stream.peek().unwrap()).map(|f| *f)
    }

    fn current_char(&mut self) -> Option<&char> {
        self.stream.peek()
    }

    fn default_parse<'b>(parser: &mut Parser) -> Result<Option<AbstractTree>> {
        let mut chars = String::new();
        let starting_position = parser.position.clone();

        loop {
            // refactor this to a while - let
            // if parser.at_eof() { break; } // I don't think this is necessary
            let current_reader = parser.current_reader();
            match current_reader {
                Some(f) => {
                    break;
                }
                None => {
                    match parser.advance_char() {
                        Some(c) => chars.push(c),
                        None => {
                            break;
                        }
                    }
                }
            }
        }
        if chars.len() == 0 {
            return Ok(None);
        }
        Ok(Some(AbstractTree::Token(TokenType::Symbol, chars, starting_position)))
    }

}

fn no_op(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(None)
}

// consolidate these into one function.
fn close_paren(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, ")".to_string(), Position(0, 0))))
}

fn close_curly(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, "}".to_string(), Position(0, 0))))
}

fn open_paren(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    let starting_position = parser.position.clone();
    parser.advance_char();
    let mut accumulator = vec![];
    loop {
        let expression = parser.parse_expression();
        match expression {
            Ok(Some(AbstractTree::Token(TokenType::Flag, s, p))) => {
                if s == ")".to_string() {
                    break;
                } else {
                    // this way I can ensure ( } doesnt happen
                    return err_position(starting_position,
                                        format!("encountered incorrect flag {},\
                                                at position {:?}, while reading an \
                                                open paren", s, p))
                }
            }
            Ok(Some(a)) => accumulator.push(a),
            Ok(None) => {
                if parser.at_eof() {
                    return err_position(starting_position, "hit end of file while reading an open paren".to_string())
                }
            }
            error @ Err(_) => return error,
        }
    }
    Ok(Some(AbstractTree::Node(accumulator, starting_position)))
}

pub fn parse(string: &str) -> Result<Option<AbstractTree>> {
    Parser::new(string)
        .read_as(' ', no_op)
        .read_as(')', close_paren)
        .read_as('(', open_paren)
        .read_as('}', close_curly)
        // .read_as('{', open_curly)
        .parse_expression()
}

#[cfg(test)]
mod tests {
    use parser::parse;
    use compiler::abstract_tree::AbstractTree::*;
    use compiler::abstract_tree::TokenType::*;
    use utils::{err_position, Position, Error};

    #[test]
    fn test_parse_symbol() {
        assert_eq!(
            parse("symbol").unwrap().unwrap(),
            Token(Symbol, "symbol".to_string(), Position(0,0))
        )
    }

    #[test]
    fn test_parse_parentheses() {
        assert_eq!(
            parse("(hi there)").unwrap().unwrap(),
            Node(vec![Token(Symbol, "hi".to_string(), Position(0,1)), Token(Symbol, "there".to_string(), Position(0,4))], Position(0,0))
        );

        // Try with two levels
        assert_eq!(
            parse("(hi (one) there)").unwrap().unwrap(),
            Node(vec![Token(Symbol, "hi".to_string(), Position(0,1)), Node(vec![Token(Symbol, "one".to_string(), Position(0,5))], Position(0,4)), Token(Symbol, "there".to_string(), Position(0, 10))], Position(0,0))
        );
    }

    #[test]
    fn test_fail_parse_parentheses() {
        match parse("(hi there") {
            Ok(_) => panic!("I'm assertng this should not parse correctly"),
            Err(Error { description: description, position: _ }) => {
                assert_eq!("hit end of file while reading an open paren".to_string(), description);
            }
        }
    }
}
<|endoftext|>


<commit_before>pub use super::{Args, Command, CommandGroup, CommandType};

use std::collections::HashMap;
use std::default::Default;
use std::sync::Arc;
use client::Context;
use model::{Message, Permissions};

pub struct CreateCommand(pub Command);

impl CreateCommand {
    /// Adds multiple aliases.
    pub fn batch_known_as(mut self, names: Vec<&str>) -> Self {
        self.0.aliases.extend(
            names.into_iter().map(|n| n.to_owned()),
        );

        self
    }

    /// Adds a ratelimit bucket.
    pub fn bucket(mut self, bucket: &str) -> Self {
        self.0.bucket = Some(bucket.to_owned());

        self
    }

    /// Adds a "check" to a command, which checks whether or not the command's
    /// function should be called.
    ///
    /// # Examples
    ///
    /// Ensure that the user who created a message, calling a "ping" command,
    /// is the owner.
    ///
    /// ```rust,no_run
    /// # use serenity::prelude::EventHandler;
    /// # struct Handler;
    /// # impl EventHandler for Handler {}
    /// use serenity::client::{Client, Context};
    /// use serenity::framework::standard::{Args, Command, StandardFramework};
    /// use serenity::model::Message;
    /// use std::env;
    /// use std::sync::Arc;
    ///
    /// let mut client = Client::new(&env::var("DISCORD_TOKEN").unwrap(), Handler);
    ///
    /// client.with_framework(StandardFramework::new()
    ///     .configure(|c| c.prefix("~"))
    ///     .command("ping", |c| c
    ///         .check(owner_check)
    ///         .desc("Replies to a ping with a pong")
    ///         .exec(ping)));
    ///
    /// fn ping(_context: &mut Context, message: &Message, _args: Args) -> Result<(),
    /// String> {
    ///     let _ = message.channel_id.say("Pong!");
    ///
    ///     Ok(())
    /// }
    ///
    /// fn owner_check(_context: &mut Context, message: &Message, _: &mut Args, _:
    /// &Arc<Command>) -> bool {
    ///     // replace with your user ID
    ///     message.author.id == 7
    /// }
    /// ```
    pub fn check<F>(mut self, check: F) -> Self
        where F: Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool
                     + Send
                     + Sync
                     + 'static {
        self.0.checks.push(Box::new(check));

        self
    }

    /// Description, used by other commands.
    pub fn desc(mut self, desc: &str) -> Self {
        self.0.desc = Some(desc.to_owned());

        self
    }

    /// Whether command can be used only privately or not.
    pub fn dm_only(mut self, dm_only: bool) -> Self {
        self.0.dm_only = dm_only;

        self
    }

    /// Example arguments, used by other commands.
    pub fn example(mut self, example: &str) -> Self {
        self.0.example = Some(example.to_owned());

        self
    }

    /// A function that can be called when a command is received.
    /// You can return `Err(string)` if there's an error.
    ///
    /// See [`exec_str`] if you _only_ need to return a string on command use.
    ///
    /// [`exec_str`]: #method.exec_str
    pub fn exec<F>(mut self, func: F) -> Self
        where F: Fn(&mut Context, &Message, Args) -> Result<(), String> + Send + Sync + 'static {
        self.0.exec = CommandType::Basic(Box::new(func));

        self
    }

    /// Sets a function that's called when a command is called that can access
    /// the internal HashMap of commands, used specifically for creating a help
    /// command.
    ///
    /// You can return `Err(string)` if there's an error.
    pub fn exec_help<F>(mut self, f: F) -> Self
        where F: Fn(&mut Context,
                    &Message,
                    HashMap<String, Arc<CommandGroup>>,
                    Args)
                    -> Result<(), String>
                     + 'static {
        self.0.exec = CommandType::WithCommands(Box::new(f));

        self
    }

    /// Sets a string to be sent in the channel of context on command. This can
    /// be useful for an `about`, `invite`, `ping`, etc. command.
    ///
    /// # Examples
    ///
    /// Create a command named "ping" that returns "Pong!":
    ///
    /// ```rust,ignore
    /// client.with_framework(|f| f
    ///     .command("ping", |c| c.exec_str("Pong!")));
    /// ```
    pub fn exec_str(mut self, content: &str) -> Self {
        self.0.exec = CommandType::StringResponse(content.to_owned());

        self
    }

    /// Whether command can be used only in guilds or not.
    pub fn guild_only(mut self, guild_only: bool) -> Self {
        self.0.guild_only = guild_only;

        self
    }

    /// Whether command should be displayed in help list or not, used by other commands.
    pub fn help_available(mut self, help_available: bool) -> Self {
        self.0.help_available = help_available;

        self
    }

    /// Adds an alias, allowing users to use the command under a different name.
    pub fn known_as(mut self, name: &str) -> Self {
        self.0.aliases.push(name.to_owned());

        self
    }

    /// Maximum amount of arguments that can be passed.
    pub fn max_args(mut self, max_args: i32) -> Self {
        self.0.max_args = Some(max_args);

        self
    }

    /// Minumum amount of arguments that should be passed.
    pub fn min_args(mut self, min_args: i32) -> Self {
        self.0.min_args = Some(min_args);

        self
    }

    /// Whether command can be used only privately or not.
    pub fn owners_only(mut self, owners_only: bool) -> Self {
        self.0.owners_only = owners_only;

        self
    }

    /// The permissions that a user must have in the contextual channel in order
    /// for the command to be processed.
    pub fn required_permissions(mut self, permissions: Permissions) -> Self {
        self.0.required_permissions = permissions;

        self
    }

    /// Command usage schema, used by other commands.
    pub fn usage(mut self, usage: &str) -> Self {
        self.0.usage = Some(usage.to_owned());

        self
    }
}

impl Default for Command {
    fn default() -> Command {
        Command {
            aliases: Vec::new(),
            checks: Vec::default(),
            exec: CommandType::Basic(Box::new(|_, _, _| Ok(()))),
            desc: None,
            usage: None,
            example: None,
            min_args: None,
            bucket: None,
            max_args: None,
            required_permissions: Permissions::empty(),
            dm_only: false,
            guild_only: false,
            help_available: true,
            owners_only: false,
        }
    }
}
<commit_msg>Add num_args (#156)<commit_after>pub use super::{Args, Command, CommandGroup, CommandType};

use std::collections::HashMap;
use std::default::Default;
use std::sync::Arc;
use client::Context;
use model::{Message, Permissions};

pub struct CreateCommand(pub Command);

impl CreateCommand {
    /// Adds multiple aliases.
    pub fn batch_known_as(mut self, names: Vec<&str>) -> Self {
        self.0.aliases.extend(
            names.into_iter().map(|n| n.to_owned()),
        );

        self
    }

    /// Adds a ratelimit bucket.
    pub fn bucket(mut self, bucket: &str) -> Self {
        self.0.bucket = Some(bucket.to_owned());

        self
    }

    /// Adds a "check" to a command, which checks whether or not the command's
    /// function should be called.
    ///
    /// # Examples
    ///
    /// Ensure that the user who created a message, calling a "ping" command,
    /// is the owner.
    ///
    /// ```rust,no_run
    /// # use serenity::prelude::EventHandler;
    /// # struct Handler;
    /// # impl EventHandler for Handler {}
    /// use serenity::client::{Client, Context};
    /// use serenity::framework::standard::{Args, Command, StandardFramework};
    /// use serenity::model::Message;
    /// use std::env;
    /// use std::sync::Arc;
    ///
    /// let mut client = Client::new(&env::var("DISCORD_TOKEN").unwrap(), Handler);
    ///
    /// client.with_framework(StandardFramework::new()
    ///     .configure(|c| c.prefix("~"))
    ///     .command("ping", |c| c
    ///         .check(owner_check)
    ///         .desc("Replies to a ping with a pong")
    ///         .exec(ping)));
    ///
    /// fn ping(_context: &mut Context, message: &Message, _args: Args) -> Result<(),
    /// String> {
    ///     let _ = message.channel_id.say("Pong!");
    ///
    ///     Ok(())
    /// }
    ///
    /// fn owner_check(_context: &mut Context, message: &Message, _: &mut Args, _:
    /// &Arc<Command>) -> bool {
    ///     // replace with your user ID
    ///     message.author.id == 7
    /// }
    /// ```
    pub fn check<F>(mut self, check: F) -> Self
        where F: Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool
                     + Send
                     + Sync
                     + 'static {
        self.0.checks.push(Box::new(check));

        self
    }

    /// Description, used by other commands.
    pub fn desc(mut self, desc: &str) -> Self {
        self.0.desc = Some(desc.to_owned());

        self
    }

    /// Whether command can be used only privately or not.
    pub fn dm_only(mut self, dm_only: bool) -> Self {
        self.0.dm_only = dm_only;

        self
    }

    /// Example arguments, used by other commands.
    pub fn example(mut self, example: &str) -> Self {
        self.0.example = Some(example.to_owned());

        self
    }

    /// A function that can be called when a command is received.
    /// You can return `Err(string)` if there's an error.
    ///
    /// See [`exec_str`] if you _only_ need to return a string on command use.
    ///
    /// [`exec_str`]: #method.exec_str
    pub fn exec<F>(mut self, func: F) -> Self
        where F: Fn(&mut Context, &Message, Args) -> Result<(), String> + Send + Sync + 'static {
        self.0.exec = CommandType::Basic(Box::new(func));

        self
    }

    /// Sets a function that's called when a command is called that can access
    /// the internal HashMap of commands, used specifically for creating a help
    /// command.
    ///
    /// You can return `Err(string)` if there's an error.
    pub fn exec_help<F>(mut self, f: F) -> Self
        where F: Fn(&mut Context,
                    &Message,
                    HashMap<String, Arc<CommandGroup>>,
                    Args)
                    -> Result<(), String>
                     + 'static {
        self.0.exec = CommandType::WithCommands(Box::new(f));

        self
    }

    /// Sets a string to be sent in the channel of context on command. This can
    /// be useful for an `about`, `invite`, `ping`, etc. command.
    ///
    /// # Examples
    ///
    /// Create a command named "ping" that returns "Pong!":
    ///
    /// ```rust,ignore
    /// client.with_framework(|f| f
    ///     .command("ping", |c| c.exec_str("Pong!")));
    /// ```
    pub fn exec_str(mut self, content: &str) -> Self {
        self.0.exec = CommandType::StringResponse(content.to_owned());

        self
    }

    /// Whether command can be used only in guilds or not.
    pub fn guild_only(mut self, guild_only: bool) -> Self {
        self.0.guild_only = guild_only;

        self
    }

    /// Whether command should be displayed in help list or not, used by other commands.
    pub fn help_available(mut self, help_available: bool) -> Self {
        self.0.help_available = help_available;

        self
    }

    /// Adds an alias, allowing users to use the command under a different name.
    pub fn known_as(mut self, name: &str) -> Self {
        self.0.aliases.push(name.to_owned());

        self
    }

    /// Maximum amount of arguments that can be passed.
    pub fn max_args(mut self, max_args: i32) -> Self {
        self.0.max_args = Some(max_args);

        self
    }

    /// Minumum amount of arguments that should be passed.
    pub fn min_args(mut self, min_args: i32) -> Self {
        self.0.min_args = Some(min_args);

        self
    }

    /// Exact number of arguments that should be passed.
    pub fn num_args(mut self, num_args: i32) -> Self {
        self.0.min_args = Some(num_args);
        self.0.max_args = Some(num_args);

        self
    }

    /// Whether command can be used only privately or not.
    pub fn owners_only(mut self, owners_only: bool) -> Self {
        self.0.owners_only = owners_only;

        self
    }

    /// The permissions that a user must have in the contextual channel in order
    /// for the command to be processed.
    pub fn required_permissions(mut self, permissions: Permissions) -> Self {
        self.0.required_permissions = permissions;

        self
    }

    /// Command usage schema, used by other commands.
    pub fn usage(mut self, usage: &str) -> Self {
        self.0.usage = Some(usage.to_owned());

        self
    }
}

impl Default for Command {
    fn default() -> Command {
        Command {
            aliases: Vec::new(),
            checks: Vec::default(),
            exec: CommandType::Basic(Box::new(|_, _, _| Ok(()))),
            desc: None,
            usage: None,
            example: None,
            min_args: None,
            bucket: None,
            max_args: None,
            required_permissions: Permissions::empty(),
            dm_only: false,
            guild_only: false,
            help_available: true,
            owners_only: false,
        }
    }
}
<|endoftext|>


<commit_before>//! The configuration that the password generator uses to generate a password

pub mod defaults;

/// The base configuration struct for the password generator. Pass this configuration to the
/// generator to create a password.
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct Configuration {
    pub words: WordConfiguration,
    pub seperator: SeperatorConfiguration,
    pub padding_digits: PaddingDigitConfiguration,
    pub padding_symbols: PaddingSymbolConfiguration,
}

impl Configuration {
    /// Creates a new configuration with the default settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::default();
    /// // Print out the default configuration
    /// println!("{:?}", config);
    /// ```
    pub fn default() -> Configuration {
        return defaults::configuration_default();
    }

    /// Creates a new configuration with the Apple ID settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::appleid();
    /// // Print out the appleid configuration
    /// println!("{:?}", config);
    /// ```
    pub fn appleid() -> Configuration {
        return defaults::configuration_appleid();
    }

    /// Creates a new configuration with the NTML settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::ntml();
    /// // Print out the ntml configuration
    /// println!("{:?}", config);
    /// ```
    pub fn ntml() -> Configuration {
        return defaults::configuration_ntml();
    }

    /// Creates a new configuration with the XKCD settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::xkcd();
    /// // Print out the xkcd configuration
    /// println!("{:?}", config);
    /// ```
    pub fn xkcd() -> Configuration {
        return defaults::configuration_xkcd();
    }

    /// Creates a new configuration with the web16 settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::web16();
    /// // Print out the web16 configuration
    /// println!("{:?}", config);
    /// ```
    pub fn web16() -> Configuration {
        return defaults::configuration_web16();
    }

    /// Creates a new configuration with the web32 settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::web32();
    /// // Print out the web32 configuration
    /// println!("{:?}", config);
    /// ```
    pub fn web32() -> Configuration {
        return defaults::configuration_web32();
    }

    /// Creates a new configuration with the wifi settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::wifi();
    /// // Print out the wifi configuration
    /// println!("{:?}", config);
    /// ```
    pub fn wifi() -> Configuration {
        return defaults::configuration_wifi();
    }
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct WordConfiguration {
    pub num_words: u8,
    pub min_length: u8,
    pub max_length: u8,

    pub transformations: WordTransformations,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum WordTransformations {
    CapitaliseFirst,
    CapitaliseNonFirst,
    LowerCase,
    UpperCase,
    AlternatingLowerUpper,
    RandomLowerUpper,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct SeperatorConfiguration {
    pub seperator_type: SeperatorTypes,
    pub seperators: Vec<char>,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum SeperatorTypes {
    SingleCharacter,
    RandomCharacter,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PaddingDigitConfiguration {
    pub num_before: u8,
    pub num_after: u8,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PaddingSymbolConfiguration {
    pub padding_type: PaddingTypes,
    pub padding_character_type: PaddingCharTypes,
    pub padding_chars: Vec<char>,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum PaddingTypes {
    // Pad to the length given
    Adaptive(u32),
    // Padding before and padding after
    Fixed(u8, u8),
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum PaddingCharTypes {
    SingleCharacter,
    RandomCharacter,
    SeperatorCharacter,
}
<commit_msg>Improved the configuration documentation<commit_after>//! The configuration that the password generator uses to generate a password
//!
//! For most passwords, you will just want to use one of the preset password configurations.
//! These presets can be found in the defaults module but convenience methods have also been
//! provided.
//!
//! # Examples
//!
//! ```
//! use xkcd_pass::Configuration;
//!
//! // Make a new default configuration
//! let default_config = Configuration::default();
//! // Make a new xkcd configuration
//! let xkcd_config = Configuration::xkcd();
//! ```

pub mod defaults;

/// The base configuration struct for the password generator. Pass this configuration to the
/// generator to create a password.
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct Configuration {
    /// The configuration for the quantity and style of words generated
    pub words: WordConfiguration,
    /// The configuration for the seperator characters between words
    pub seperator: SeperatorConfiguration,
    /// The configuration for the padding digits before and after the password
    pub padding_digits: PaddingDigitConfiguration,
    /// The configuration for the padding symbols before and after the password
    pub padding_symbols: PaddingSymbolConfiguration,
}

impl Configuration {
    /// Creates a new configuration with the default settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::default();
    /// // Print out the default configuration
    /// println!("{:?}", config);
    /// ```
    pub fn default() -> Configuration {
        return defaults::configuration_default();
    }

    /// Creates a new configuration with the Apple ID settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::appleid();
    /// // Print out the appleid configuration
    /// println!("{:?}", config);
    /// ```
    pub fn appleid() -> Configuration {
        return defaults::configuration_appleid();
    }

    /// Creates a new configuration with the NTML settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::ntml();
    /// // Print out the ntml configuration
    /// println!("{:?}", config);
    /// ```
    pub fn ntml() -> Configuration {
        return defaults::configuration_ntml();
    }

    /// Creates a new configuration with the XKCD settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::xkcd();
    /// // Print out the xkcd configuration
    /// println!("{:?}", config);
    /// ```
    pub fn xkcd() -> Configuration {
        return defaults::configuration_xkcd();
    }

    /// Creates a new configuration with the web16 settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::web16();
    /// // Print out the web16 configuration
    /// println!("{:?}", config);
    /// ```
    pub fn web16() -> Configuration {
        return defaults::configuration_web16();
    }

    /// Creates a new configuration with the web32 settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::web32();
    /// // Print out the web32 configuration
    /// println!("{:?}", config);
    /// ```
    pub fn web32() -> Configuration {
        return defaults::configuration_web32();
    }

    /// Creates a new configuration with the wifi settings loaded in.
    ///
    /// ```
    /// use xkcd_pass::configuration::Configuration;
    ///
    /// let config = Configuration::wifi();
    /// // Print out the wifi configuration
    /// println!("{:?}", config);
    /// ```
    pub fn wifi() -> Configuration {
        return defaults::configuration_wifi();
    }
}

/// The configuration for the quantity and style of words generated
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct WordConfiguration {
    /// The number of words that should be in the password
    pub num_words: u8,
    /// The minimum length of the words in the password
    pub min_length: u8,
    /// The maximum length of the words in the password
    pub max_length: u8,

    /// The transformations that should be applied to the words
    pub transformations: WordTransformations,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum WordTransformations {
    /// Capitalise the first letter of every word. i.e `Random` and `Word`
    CapitaliseFirst,
    /// Capitalise all but the first letter of every word. i.e `rANDOM` and `wORD`
    CapitaliseNonFirst,
    /// Convert all the words into lowercase. i.e `lower` and `case`
    LowerCase,
    /// Convert all the words into uppercase. i.e `UPPER` and `CASE`
    UpperCase,
    /// Alternate the words between lower and upper case in the password. i.e `randomWORDSinPASSWORD`
    AlternatingLowerUpper,
    /// Randomly convert each word in the password to uppercase or lowercase. i.e `ArandomWORDPASSWORD`
    RandomLowerUpper,
}

/// The configuration for the seperator characters between words
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct SeperatorConfiguration {
    /// The method in which the seperator chosen during password generation
    pub seperator_type: SeperatorTypes,
    /// The possible seperator characters that could be chosen. Must contain at least 1 character
    pub seperators: Vec<char>,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum SeperatorTypes {
    /// Use the same character (the first character in the seperators vector) for the seperator
    /// between all of the words
    SingleCharacter,
    /// Use a random character for the seperator between each word
    RandomCharacter,
}

/// The configuration for the padding digits before and after the password
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PaddingDigitConfiguration {
    /// The number of digits to pad before the password
    pub num_before: u8,
    /// The number of digits to pad after the password
    pub num_after: u8,
}

/// The configuration for the padding symbols before and after the password
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub struct PaddingSymbolConfiguration {
    /// The padding style to use for the password
    pub padding_type: PaddingTypes,
    /// The method in which the padding characters are chosen
    pub padding_character_type: PaddingCharTypes,
    /// The choices of characters that can pad the password. Must contain at least 1 character
    pub padding_chars: Vec<char>,
}

/// The padding style to use for the password
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum PaddingTypes {
    /// Pad the password to the length given. If the password is only 10 characters long and this
    /// is set to 16, 6 extra characters of padding will be added to the end of the password
    Adaptive(u32),
    /// A fixed amount of padding before and padding after the password. The first value is the
    /// amount of padding before the password and the second value is the amount of padding
    /// after the password
    Fixed(u8, u8),
}

/// The method in which the padding characters are chosen
#[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum PaddingCharTypes {
    /// Only a single character will be used for the padding. The first character in the
    /// `padding_chars` vector in `PaddingSymbolConfiguration` is used
    SingleCharacter,
    /// Choose a random character to pad the password from the `padding_chars` vector in
    /// `PaddingSymbolConfiguration`
    RandomCharacter,
    /// Use the same character as the one chosen for the seperator character
    SeperatorCharacter,
}
<|endoftext|>


<commit_before>//!
//! Metapackage for renderer
//!

extern crate rand;
use self::rand::Rng;

// Convert numbers to chars
use std::char;

// `Console` is needed as Console is a trait that console::Root extends
use core::tcod::{Console, console};

use core::GlobalLog;

use core::world::World;

use core::world::dungeon::Dungeon;
// Used to expliclty reference constants
use core::world::dungeon::map::{tile, Pos, Tile};

mod entity;
pub use self::entity::Entity;

// Use camera privately
mod camera;
use self::camera::Camera;

// Use RGB publicly
pub mod rgb;
pub use self::rgb::RGB;

///
/// Tile colors
///

// Water colors
const WATER_COLORS : [RGB; 3] = [
  RGB(51, 133, 200),
  RGB(57, 144, 200),
  RGB(54, 138, 200)
];

///
/// Tile color manipulation
///

// Used to darken tiles that are out of sight
pub const DARKEN_FAC : RGB = RGB(10, 10, 10);
// Used to lighten tiles that are in the FoV
pub const YELLOW_FAC : RGB = RGB(27, 24, 22);

///
/// Modify a tile's fg and bg color
///
fn amplify_col(t: &Tile, factor: RGB) -> Tile {
  let mut replace = t.clone();
  replace.fg = t.fg + factor;
  replace.bg = t.bg + factor;
  return replace;
}

///
/// Modify a tile's fg and bg color
///
fn reduce_col(t: &Tile, factor: RGB) -> Tile {
  let mut replace = t.clone();
  replace.fg = t.fg - factor;
  replace.bg = t.bg - factor;
  return replace;
}

///
/// Darken a tile's fg and bg color
///
fn darken(t: &Tile) -> Tile {
  reduce_col(t, DARKEN_FAC)
}

///
/// Make a tile's fg and bg color more yellowish
///
fn yellowish(t: &Tile) -> Tile {
  amplify_col(t, YELLOW_FAC)
}

///
/// The renderer
///
/// Tracks the player and automatically scrolls the screen around to match where they go.
/// This will never try to draw things outside of the given dimensions due to the way it handles
/// determining whether something should be drawn or not.
///
pub struct Renderer {
  // Camera object
  camera: Camera,
  screen: Pos,
  console_height: isize,
  panel_width: isize,
  pub show_scent: bool,
  pub fov: bool,
  pub show_sound: bool
}

impl Renderer {

  ///
  /// Render for each monster as a visible colored entity
  ///
  fn debug_render_scent_map(&mut self, con: &mut console::Root, dungeon: &Dungeon) {

    for x in 0..dungeon.width {
      for y in 0..dungeon.height {
        // Pretty much just random, Player is red, bugs are green, cats are yellow and dogs are blue
        let mut color = RGB(
          dungeon[x][y].scents[0].val + 50 + dungeon[x][y].scents[3].val,
          dungeon[x][y].scents[1].val + 25 + dungeon[x][y].scents[3].val,
          dungeon[x][y].scents[2].val + 50
        );
        // Iterate over scents, context of what scent it is isn't necessary
        for s in 0..tile::Scent::Num as usize {
          if dungeon[x][y].scents[s].val > 0 {
            self.draw_entity(con, Pos::new(x as isize, y as isize), &Tile::new(
              "Debug Scent",
              ' ',
              RGB(255, 255, 255),
              color,
              tile::Type::Debug
            ));
            break;
          }
        }
      }
    }

  }

  ///
  /// Render sound as a transparent blue entity
  ///
  fn debug_render_sound_map(&mut self, con: &mut console::Root, dungeon: &Dungeon) {

    for x in 0..dungeon.width {
      for y in 0..dungeon.height {
        // Color is weighted towards blue
        let mut color = RGB(
          dungeon[x][y].get_bg().r(),
          dungeon[x][y].get_bg().g(),
          dungeon[x][y].sound as u8
        );
        if dungeon[x][y].sound > 0 {
          self.draw_entity(con, Pos::new(x as isize, y as isize), &Tile::new(
            "Debug Sound",
            ' ',
            RGB(255, 255, 255),
            color,
            tile::Type::Debug
          ));
        }
      }
    }

  }

  ///
  /// Draw all.
  ///
  pub fn draw_all(&mut self, con: &mut console::Root, world: &mut World) {

    //
    // Console prep
    //

    // Clear console
    con.clear();

    // Move camera to player's position
    self.camera.move_to(world.player.actor.pos);

    //
    // Draw tiles
    //

    // Draw seen tiles
    for x in 0..world.floor.dun.width {
      for y in 0..world.floor.dun.height {
        // If fov is on...
        if self.fov {
          // And it's in the FoV
          if world.tcod_map.is_in_fov(x as i32, y as i32) && self.fov {

            // Update tile if possible
            match &world.floor.dun[x][y].tiletype {
              tile::Type::Water => {
                &world.floor.dun[x][y].set_bg(*rand::thread_rng().choose(&WATER_COLORS).unwrap());
              },
              _ => {}
            }

            // Draw a tile slightly more vibrant than it actually is to emulate torchlight
            self.draw_entity(con, Pos::from_usize(x, y), &yellowish(&world.floor.dun[x][y]));

            // Mark tile as seen if it's in the FoV
            world.floor.dun[x][y].seen = true;

          }

          // And the tile has been seen...
          else if world.floor.dun[x][y].seen {
            // Draw a tile, but darker
            self.draw_entity(con, Pos::from_usize(x, y), &darken(&world.floor.dun[x][y]));
          }

        }

        // [Debug] Otherwise just draw all tiles normally
        else {
          self.draw_entity(con, Pos::new(x as isize, y as isize), &world.floor.dun[x][y]);
        }

      }
    }

    //
    // Debug options
    //

    // Debug scent
    if self.show_scent {
      self.debug_render_scent_map(con, &world.floor.dun);
    }

    // Debug sound
    if self.show_sound {
      self.debug_render_sound_map(con, &world.floor.dun);
    }

    //
    // Draw creatures
    //

    for c in &world.floor.creatures {
      // If fov is on...
      if self.fov {
        // And its in the fov...
        if world.tcod_map.is_in_fov(c.actor.pos.x as i32, c.actor.pos.y as i32) && self.fov {
          self.draw_creature(con, c.actor.pos, &c.actor, world);
        }
      } else {
        self.draw_creature(con, c.actor.pos, &c.actor, world);
      }
    }

    // Draw player. Player is always in the camera since
    // we move the camera over it.
    self.draw_creature(con, world.player.actor.pos, &world.player.actor, world);

    //
    // Draw log
    //

    self.draw_log(con);

    //
    // Draw UI
    //

    for x in 0..self.screen.x {
      con.put_char_ex(
        x as i32,
        (self.screen.y - self.console_height - 1) as i32,
        '-',
        RGB(255, 255, 255).to_tcod(),
        RGB(0, 0, 0).to_tcod()
      );
    }

    for y in 0..(self.screen.y - self.console_height - 1) {
      con.put_char_ex(
        (self.screen.x - self.panel_width - 1) as i32,
        y as i32,
        '|',
        RGB(255, 255, 255).to_tcod(),
        RGB(0, 0, 0).to_tcod()
      );
    }

    con.put_char_ex(
      (self.screen.x - self.panel_width - 1) as i32,
      (self.screen.y - self.console_height - 1) as i32,
      // hyperthonk
      char::from_u32(193).unwrap(),
      RGB(255, 255, 255).to_tcod(),
      RGB(0, 0, 0).to_tcod()
    );

    // Tile player is on
    let tile = &world.floor.dun[world.player.actor.pos];

    con.set_default_foreground(RGB(255, 255, 255).to_tcod());

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      1,
      "Edgequest"
    );

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      2,
      "This is where we live"
    );

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      4,
      format!("{}: {}", "Biome", tile.biome)
    );

    let mut npscent = 0;
    for s in &tile.scents {
      if &s.scent_type != &tile::Scent::Player {
        npscent += s.val; // BUG: Panics here with overflow
      }
    }

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      5,
      format!("{}: {}", "Non-player Scent", npscent)
    );

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      6,
      format!("{}: {}", "Sound", tile.sound)
    );

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      7,
      format!("{}: {}", "Tile", tile.get_name())
    );

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      8,
      format!("{}: {}", "Floor", world.floor_num)
    );

    //
    // Flush changes to root
    //

    con.flush();

  }

  ///
  /// Draw creatures with "transparent" backgrounds
  ///
  fn draw_creature(&self, con: &mut console::Root, pos: Pos, ce: &Entity, world: &World) {
    // Check if it's in the camera first
    if !self.camera.is_in_camera(pos) { return }

    // New pos with respect to camera
    let npos = pos + self.camera.pos;

    con.put_char_ex(
      npos.x as i32,
      npos.y as i32,
      ce.get_glyph(),
      ce.get_fg().to_tcod(),
      // Backgrounds are just inherited from the world.
      if self.fov {
        (world.get_bg_color_at(pos) + YELLOW_FAC).to_tcod()
      } else {
        (world.get_bg_color_at(pos)).to_tcod()
      }
    );

  }

  ///
  /// Put an `Entity` on the console
  ///
  fn draw_entity(&self, con: &mut console::Root, pos: Pos, ce: &Entity) {

    // Check if it's in the camera first
    if !self.camera.is_in_camera(pos) { return }

    // New pos with respect to camera
    let pos = pos + self.camera.pos;

    con.put_char_ex(
      pos.x as i32,
      pos.y as i32,
      ce.get_glyph(),
      ce.get_fg().to_tcod(),
      ce.get_bg().to_tcod()
    );

  }

  ///
  /// Draw the log
  ///
  /// We need to directly manipulate the GlobalLog object so here we use the mutex lock
  ///
  fn draw_log(&self, con: &mut console::Root) {

    // Mutable reference to the mutex
    let log = GlobalLog.lock().unwrap();

    // Enumerate over the last few messages
    for (i, pair) in log.get_latest_range(self.console_height as usize).iter().enumerate() {
      // Y value of text is determined by the index
      let y = self.screen.y - ((log.get_latest_range(self.console_height as usize).len() as isize) - i as isize);
      // Color and string is determined by the content of the slice at that index
      let color = pair.1;
      let s = pair.0;
      // They are then combined to render to the screen at a specific y axis such that the most
      // recent message will appear at the bottom
      con.set_default_foreground(color.to_tcod());
      con.print(0, y as i32, s);
    }

    drop(log);

  }

  ///
  /// Print all renderable characters in the font
  ///
  pub fn tcod_test(&self, con: &mut console::Root) {

    let w = con.width();
    let h = con.height();

    // Clear screen
    for x in 0..w {
      for y in 0..h {
        con.put_char_ex(
          x as i32,
          y as i32,
          ' ',
          RGB(0, 0, 0).to_tcod(),
          RGB(0, 0, 0).to_tcod()
        );
      }
    }

    // Print all 2^8 characters
    for ord in 0..256 {
      con.put_char_ex(
        (ord % w) as i32,
        (ord / w) as i32,
        // Basically go from ascii to a char
        char::from_u32(ord as u32).unwrap(),
        RGB(255, 255, 255).to_tcod(),
        RGB(0, 0, 0).to_tcod()
      );
    }

    // Update console
    con.flush();

    // Wait for keypress
    con.wait_for_keypress(true);

  }

  ///
  /// Return a new `Renderer`
  ///
  /// * `map` - `Pos` that holds the map dimensions
  /// * `screen` - `Pos` that holds the screen dimensions
  ///
  #[inline]
  pub fn new(map: Pos, screen: Pos, console_height: isize, panel_width: isize) -> Renderer {
    Renderer {
      // Camera takes a modified screen value that compensates for the console_height
      // This way the render still knows that that area is "reserved" for the console
      camera: Camera::new(
        map,
        Pos::new(screen.x - panel_width - 1, screen.y - console_height - 1)
      ),
      console_height: console_height, panel_width: panel_width,
      screen: screen,
      show_scent: false, fov: true, show_sound: false
    }
  }

}
<commit_msg>Break functions out of draw_all<commit_after>//!
//! Metapackage for renderer
//!

extern crate rand;
use self::rand::Rng;

// Convert numbers to chars
use std::char;

// `Console` is needed as Console is a trait that console::Root extends
use core::tcod::{Console, console};

use core::GlobalLog;

use core::world::World;

use core::world::dungeon::Dungeon;
// Used to expliclty reference constants
use core::world::dungeon::map::{tile, Pos, Tile};

mod entity;
pub use self::entity::Entity;

// Use camera privately
mod camera;
use self::camera::Camera;

// Use RGB publicly
pub mod rgb;
pub use self::rgb::RGB;

///
/// Tile colors
///

// Water colors
const WATER_COLORS : [RGB; 3] = [
  RGB(51, 133, 200),
  RGB(57, 144, 200),
  RGB(54, 138, 200)
];

///
/// Tile color manipulation
///

// Used to darken tiles that are out of sight
pub const DARKEN_FAC : RGB = RGB(10, 10, 10);
// Used to lighten tiles that are in the FoV
pub const YELLOW_FAC : RGB = RGB(27, 24, 22);

///
/// Modify a tile's fg and bg color
///
fn amplify_col(t: &Tile, factor: RGB) -> Tile {
  let mut replace = t.clone();
  replace.fg = t.fg + factor;
  replace.bg = t.bg + factor;
  return replace;
}

///
/// Modify a tile's fg and bg color
///
fn reduce_col(t: &Tile, factor: RGB) -> Tile {
  let mut replace = t.clone();
  replace.fg = t.fg - factor;
  replace.bg = t.bg - factor;
  return replace;
}

///
/// Darken a tile's fg and bg color
///
fn darken(t: &Tile) -> Tile {
  reduce_col(t, DARKEN_FAC)
}

///
/// Make a tile's fg and bg color more yellowish
///
fn yellowish(t: &Tile) -> Tile {
  amplify_col(t, YELLOW_FAC)
}

///
/// The renderer
///
/// Tracks the player and automatically scrolls the screen around to match where they go.
/// This will never try to draw things outside of the given dimensions due to the way it handles
/// determining whether something should be drawn or not.
///
pub struct Renderer {
  // Camera object
  camera: Camera,
  screen: Pos,
  console_height: isize,
  panel_width: isize,
  pub show_scent: bool,
  pub fov: bool,
  pub show_sound: bool
}

impl Renderer {

  ///
  /// Render for each monster as a visible colored entity
  ///
  fn debug_render_scent_map(&mut self, con: &mut console::Root, dungeon: &Dungeon) {

    for x in 0..dungeon.width {
      for y in 0..dungeon.height {
        // Pretty much just random, Player is red, bugs are green, cats are yellow and dogs are blue
        let mut color = RGB(
          dungeon[x][y].scents[0].val + 50 + dungeon[x][y].scents[3].val,
          dungeon[x][y].scents[1].val + 25 + dungeon[x][y].scents[3].val,
          dungeon[x][y].scents[2].val + 50
        );
        // Iterate over scents, context of what scent it is isn't necessary
        for s in 0..tile::Scent::Num as usize {
          if dungeon[x][y].scents[s].val > 0 {
            self.draw_entity(con, Pos::new(x as isize, y as isize), &Tile::new(
              "Debug Scent",
              ' ',
              RGB(255, 255, 255),
              color,
              tile::Type::Debug
            ));
            break;
          }
        }
      }
    }

  }

  ///
  /// Render sound as a transparent blue entity
  ///
  fn debug_render_sound_map(&mut self, con: &mut console::Root, dungeon: &Dungeon) {

    for x in 0..dungeon.width {
      for y in 0..dungeon.height {
        // Color is weighted towards blue
        let mut color = RGB(
          dungeon[x][y].get_bg().r(),
          dungeon[x][y].get_bg().g(),
          dungeon[x][y].sound as u8
        );
        if dungeon[x][y].sound > 0 {
          self.draw_entity(con, Pos::new(x as isize, y as isize), &Tile::new(
            "Debug Sound",
            ' ',
            RGB(255, 255, 255),
            color,
            tile::Type::Debug
          ));
        }
      }
    }

  }

  ///
  /// Draw all.
  ///
  pub fn draw_all(&mut self, con: &mut console::Root, world: &mut World) {

    //
    // Console prep
    //

    // Clear console
    con.clear();

    // Move camera to player's position
    self.camera.move_to(world.player.actor.pos);

    //
    // Draw world
    //

    self.draw_world(con, world);

    //
    // Draw log
    //

    self.draw_log(con);

    //
    // Draw UI
    //

    self.draw_ui(con, world);

    //
    // Flush changes to root
    //

    con.flush();

  }

  ///
  /// Draw creatures with "transparent" backgrounds
  ///
  fn draw_creature(&self, con: &mut console::Root, pos: Pos, ce: &Entity, world: &World) {
    // Check if it's in the camera first
    if !self.camera.is_in_camera(pos) { return }

    // New pos with respect to camera
    let npos = pos + self.camera.pos;

    con.put_char_ex(
      npos.x as i32,
      npos.y as i32,
      ce.get_glyph(),
      ce.get_fg().to_tcod(),
      // Backgrounds are just inherited from the world.
      if self.fov {
        (world.get_bg_color_at(pos) + YELLOW_FAC).to_tcod()
      } else {
        (world.get_bg_color_at(pos)).to_tcod()
      }
    );

  }

  ///
  /// Put an `Entity` on the console
  ///
  fn draw_entity(&self, con: &mut console::Root, pos: Pos, ce: &Entity) {

    // Check if it's in the camera first
    if !self.camera.is_in_camera(pos) { return }

    // New pos with respect to camera
    let pos = pos + self.camera.pos;

    con.put_char_ex(
      pos.x as i32,
      pos.y as i32,
      ce.get_glyph(),
      ce.get_fg().to_tcod(),
      ce.get_bg().to_tcod()
    );

  }

  ///
  /// Draw the log
  ///
  /// We need to directly manipulate the GlobalLog object so here we use the mutex lock
  ///
  fn draw_log(&self, con: &mut console::Root) {

    // Mutable reference to the mutex
    let log = GlobalLog.lock().unwrap();

    // Enumerate over the last few messages
    for (i, pair) in log.get_latest_range(self.console_height as usize).iter().enumerate() {
      // Y value of text is determined by the index
      let y = self.screen.y - ((log.get_latest_range(self.console_height as usize).len() as isize) - i as isize);
      // Color and string is determined by the content of the slice at that index
      let color = pair.1;
      let s = pair.0;
      // They are then combined to render to the screen at a specific y axis such that the most
      // recent message will appear at the bottom
      con.set_default_foreground(color.to_tcod());
      con.print(0, y as i32, s);
    }

    drop(log);

  }

  ///
  /// Draw UI elements
  ///
  /// NOTE: This function is super basic and is intended to be revised/removed.Option
  ///
  fn draw_ui(&self, con: &mut console::Root, world: &mut World) {

    // Draw horizontal line to split game from the log console
    for x in 0..self.screen.x {
      con.put_char_ex(
        x as i32,
        (self.screen.y - self.console_height - 1) as i32,
        '-',
        RGB(255, 255, 255).to_tcod(),
        RGB(0, 0, 0).to_tcod()
      );
    }

    // Draw horizontal line to split game from the panel
    for y in 0..(self.screen.y - self.console_height - 1) {
      con.put_char_ex(
        (self.screen.x - self.panel_width - 1) as i32,
        y as i32,
        '|',
        RGB(255, 255, 255).to_tcod(),
        RGB(0, 0, 0).to_tcod()
      );
    }

    // Pretty sure this places a piece at the intersection of the panel and console lines
    // but god damn
    con.put_char_ex(
      (self.screen.x - self.panel_width - 1) as i32,
      (self.screen.y - self.console_height - 1) as i32,
      // hyperthonk
      char::from_u32(193).unwrap(),
      RGB(255, 255, 255).to_tcod(),
      RGB(0, 0, 0).to_tcod()
    );

    // Tile player is on
    let tile = &world.floor.dun[world.player.actor.pos];

    //
    //  Draw side panel contents
    //

    // White on black because I'm lazy
    con.set_default_foreground(RGB(255, 255, 255).to_tcod());

    // Gotta remind people what game theyre playing
    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      1,
      "Edgequest"
    );

    // Paying my respects to a legend
    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      2,
      "This is where we live"
    );

    // Biome
    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      4,
      format!("{}: {}", "Biome", tile.biome)
    );

    // Scent of non-players
    let mut npscent = 0;
    for s in &tile.scents {
      if &s.scent_type != &tile::Scent::Player {
        npscent += s.val; // BUG: Panics here with overflow
      }
    }

    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      5,
      format!("{}: {}", "Non-player Scent", npscent)
    );

    // Sound
    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      6,
      format!("{}: {}", "Sound", tile.sound)
    );

    // Tile
    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      7,
      format!("{}: {}", "Tile", tile.get_name())
    );

    // Floor number
    con.print(
      (self.screen.x - self.panel_width + 1) as i32,
      8,
      format!("{}: {}", "Floor", world.floor_num)
    );
  }

  ///
  /// Draw the contents of the world from the player's point of view
  ///
  fn draw_world(&mut self, con: &mut console::Root, world: &mut World) {

    // Draw the world in three steps:
    //
    //  1. Tiles
    //  2. Debug information (I want to move this out... if its not here this fn doesn't need to be &mut self)
    //  3. Creatures

    //
    // Draw tiles
    //

    // Draw seen tiles
    for x in 0..world.floor.dun.width {
      for y in 0..world.floor.dun.height {
        // If fov is on...
        if self.fov {
          // And it's in the FoV
          if world.tcod_map.is_in_fov(x as i32, y as i32) && self.fov {

            // Update tile if possible
            match &world.floor.dun[x][y].tiletype {
              tile::Type::Water => {
                &world.floor.dun[x][y].set_bg(*rand::thread_rng().choose(&WATER_COLORS).unwrap());
              },
              _ => {}
            }

            // Draw a tile slightly more vibrant than it actually is to emulate torchlight
            self.draw_entity(con, Pos::from_usize(x, y), &yellowish(&world.floor.dun[x][y]));

            // Mark tile as seen if it's in the FoV
            world.floor.dun[x][y].seen = true;

          }

          // And the tile has been seen...
          else if world.floor.dun[x][y].seen {
            // Draw a tile, but darker
            self.draw_entity(con, Pos::from_usize(x, y), &darken(&world.floor.dun[x][y]));
          }

        }

        // [Debug] Otherwise just draw all tiles normally
        else {
          self.draw_entity(con, Pos::new(x as isize, y as isize), &world.floor.dun[x][y]);
        }

      }
    }

    //
    // Debug options
    //

    // We need the Renderer to be &mut here because of the way the Renderer is kept in context

    // Debug scent
    if self.show_scent {
      self.debug_render_scent_map(con, &world.floor.dun);
    }

    // Debug sound
    if self.show_sound {
      self.debug_render_sound_map(con, &world.floor.dun);
    }

    //
    // Draw creatures
    //

    for c in &world.floor.creatures {
      // If fov is on...
      if self.fov {
        // And its in the fov...
        if world.tcod_map.is_in_fov(c.actor.pos.x as i32, c.actor.pos.y as i32) && self.fov {
          self.draw_creature(con, c.actor.pos, &c.actor, world);
        }
      } else {
        self.draw_creature(con, c.actor.pos, &c.actor, world);
      }
    }

    // Draw player. Player is always in the camera since
    // we move the camera over it.
    self.draw_creature(con, world.player.actor.pos, &world.player.actor, world);

  }

  ///
  /// Print all renderable characters in the font
  ///
  pub fn tcod_test(&self, con: &mut console::Root) {

    let w = con.width();
    let h = con.height();

    // Clear screen
    for x in 0..w {
      for y in 0..h {
        con.put_char_ex(
          x as i32,
          y as i32,
          ' ',
          RGB(0, 0, 0).to_tcod(),
          RGB(0, 0, 0).to_tcod()
        );
      }
    }

    // Print all 2^8 characters
    for ord in 0..256 {
      con.put_char_ex(
        (ord % w) as i32,
        (ord / w) as i32,
        // Basically go from ascii to a char
        char::from_u32(ord as u32).unwrap(),
        RGB(255, 255, 255).to_tcod(),
        RGB(0, 0, 0).to_tcod()
      );
    }

    // Update console
    con.flush();

    // Wait for keypress
    con.wait_for_keypress(true);

  }

  ///
  /// Return a new `Renderer`
  ///
  /// * `map` - `Pos` that holds the map dimensions
  /// * `screen` - `Pos` that holds the screen dimensions
  ///
  #[inline]
  pub fn new(map: Pos, screen: Pos, console_height: isize, panel_width: isize) -> Renderer {
    Renderer {
      // Camera takes a modified screen value that compensates for the console_height
      // This way the render still knows that that area is "reserved" for the console
      camera: Camera::new(
        map,
        Pos::new(screen.x - panel_width - 1, screen.y - console_height - 1)
      ),
      console_height: console_height, panel_width: panel_width,
      screen: screen,
      show_scent: false, fov: true, show_sound: false
    }
  }

}
<|endoftext|>


<commit_before>#![feature(asm)]
#![feature(rand)]

extern crate syscall;
extern crate raw_cpuid;
extern crate rand;

use std::fs::File;
use std::io::{Read, Write};
use std::thread;

use rand::chacha::ChaChaRng;
use rand::Rng;

use raw_cpuid::CpuId;

use syscall::{Packet, Result, SchemeMut};

//TODO: Use a CSPRNG, allow write of entropy
struct RandScheme {
   prng: ChaChaRng
}

impl SchemeMut for RandScheme {
    fn open(&mut self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        Ok(0)
    }

    fn dup(&mut self, file: usize, _buf: &[u8]) -> Result<usize> {
        Ok(file)
    }

    fn read(&mut self, _file: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        for chunk in buf.chunks_mut(8) {
            let mut rand = self.prng.next_u64();
            for b in chunk.iter_mut() {
                *b = rand as u8;
                rand = rand >> 8;
                i += 1;
            }
        }
        Ok(i)
    }

    fn close(&mut self, _file: usize) -> Result<usize> {
        Ok(0)
    }
}

fn main(){
    let mut has_rdrand = CpuId::new().get_feature_info().unwrap().has_rdrand();
    thread::spawn(move || {
        let mut socket = File::create(":rand").expect("rand: failed to create rand scheme");
        let mut rng = ChaChaRng::new_unseeded();
        if has_rdrand {
            let mut rand: u64;
            unsafe {
                asm!("rdrand rax"
                    : "={rax}"(rand)
                    :
                    :
                    : "intel", "volatile");
            }
            rng.set_counter(0, rand);
        }
        let mut scheme = RandScheme{prng: rng};
        loop {
            let mut packet = Packet::default();
            socket.read(&mut packet).expect("rand: failed to read events from rand scheme");
            scheme.handle(&mut packet);
            socket.write(&packet).expect("rand: failed to write responses to rand scheme");
        }
    });
}
<commit_msg>Print seed indicator<commit_after>#![feature(asm)]
#![feature(rand)]

extern crate syscall;
extern crate raw_cpuid;
extern crate rand;

use std::fs::File;
use std::io::{Read, Write};
use std::thread;

use rand::chacha::ChaChaRng;
use rand::Rng;

use raw_cpuid::CpuId;

use syscall::{Packet, Result, SchemeMut};

//TODO: Use a CSPRNG, allow write of entropy
struct RandScheme {
   prng: ChaChaRng
}

impl SchemeMut for RandScheme {
    fn open(&mut self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        Ok(0)
    }

    fn dup(&mut self, file: usize, _buf: &[u8]) -> Result<usize> {
        Ok(file)
    }

    fn read(&mut self, _file: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        for chunk in buf.chunks_mut(8) {
            let mut rand = self.prng.next_u64();
            for b in chunk.iter_mut() {
                *b = rand as u8;
                rand = rand >> 8;
                i += 1;
            }
        }
        Ok(i)
    }

    fn close(&mut self, _file: usize) -> Result<usize> {
        Ok(0)
    }
}

fn main(){
    let has_rdrand = CpuId::new().get_feature_info().unwrap().has_rdrand();

    thread::spawn(move || {
        let mut socket = File::create(":rand").expect("rand: failed to create rand scheme");

        let mut rng = ChaChaRng::new_unseeded();

        if has_rdrand {
            println!("rand: seeding with rdrand");
            let rand: u64;
            unsafe {
                asm!("rdrand rax"
                    : "={rax}"(rand)
                    :
                    :
                    : "intel", "volatile");
            }
            rng.set_counter(0, rand);
        } else {
            println!("rand: unseeded");
        }

        let mut scheme = RandScheme{prng: rng};
        loop {
            let mut packet = Packet::default();
            socket.read(&mut packet).expect("rand: failed to read events from rand scheme");
            scheme.handle(&mut packet);
            socket.write(&packet).expect("rand: failed to write responses to rand scheme");
        }
    });
}
<|endoftext|>


<commit_before>// run-pass

// Test that functional record update/struct update syntax works inside
// a closure when the feature `capture_disjoint_fields` is enabled.

#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>

struct S {
    a: String,
    b: String,
}

fn main() {
    let a = String::new();
    let b = String::new();
    let s = S {a, b};

    let c = || {
        let s2 = S {
            a: format!("New a"),
            ..s
        };
        println!("{} {}", s2.a, s2.b);
    };

    c();
}
<commit_msg>Add a test for syntax like: ..t.s<commit_after>// run-pass

// Test that functional record update/struct update syntax works inside
// a closure when the feature `capture_disjoint_fields` is enabled.

#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>

#[derive(Clone)]
struct S {
    a: String,
    b: String,
}

struct T {
    a: String,
    s: S,
}

fn main() {
    let a = String::new();
    let b = String::new();
    let c = String::new();
    let s = S {a, b};
    let t = T {
        a: c,
        s: s.clone()
    };

    let c = || {
        let s2 = S {
            a: format!("New s2"),
            ..s
        };
        let s3 = S {
            a: format!("New s3"),
            ..t.s
        };
        println!("{} {}", s2.a, s2.b);
        println!("{} {} {}", s3.a, s3.b, t.a);
    };

    c();
}
<|endoftext|>


<commit_before>//! Interpreter for compiled code.

use cell_gc::GcHeapSession;
use cell_gc::collections::VecRef;
use compile::{op, CodeRef};
use env::{Environment, EnvironmentRef, StaticEnvironmentRef};
use errors::Result;
use value::{Lambda, Pair, Value};

/// A potentially partially evaluated value.
///
/// When the interpreter calls a builtin, usually the builtin runs, computes a
/// Value, and returns it. But a small number of builtins (`apply`, `eval`) can
/// tail-call Scheme code. This must be done without recursively invoking the
/// interpreter. That would consume some system stack space; it wouldn't be a
/// true tail call. So instead, the builtin returns a special Trampoline to the
/// interpreter that means "I did the part of the computation that's written in
/// Rust; the rest is Scheme code and here's how to run it". The word "returns"
/// is the key: the stack space that was expended to call the builtin is
/// reclaimed when it returns.
pub enum Trampoline<'h> {
    /// A completely evaluated value.
    Value(Value<'h>),

    /// A value that can be computed by calling a particular procedure.
    TailCall {
        func: Value<'h>,
        args: Vec<Value<'h>>,
    },

    /// A value that can be computed by running the given code.
    TailEval {
        code: CodeRef<'h>,
        env: EnvironmentRef<'h>
    }
}

fn new_call_env<'h>(
    hs: &mut GcHeapSession<'h>,
    senv: StaticEnvironmentRef<'h>,
    has_rest: bool,
    parent: EnvironmentRef<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<EnvironmentRef<'h>> {
    let names = senv.names();
    let n_names = names.len();
    let n_required_params = n_names - has_rest as usize;

    let n_actual = args.len();
    if n_actual < n_required_params {
        return Err("not enough arguments".into());
    }
    if has_rest {
        let mut rest_list = Value::Nil;
        for arg in args.drain(n_required_params..).rev() {
            rest_list = Value::Cons(hs.alloc(Pair {
                car: arg,
                cdr: rest_list,
            }));
        }
        args.push(rest_list);
    } else if n_actual > n_required_params {
        return Err("too many arguments".into());
    }

    let args = hs.alloc(args);  // move values into heap
    Ok(Environment::new(hs, Some(parent), senv, args))
}

pub fn apply<'h>(
    hs: &mut GcHeapSession<'h>,
    mut fval: Value<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<Value<'h>> {
    loop {
        match fval {
            Value::Builtin(f) => {
                match (f.0)(hs, args)? {
                    Trampoline::Value(v) => return Ok(v),
                    Trampoline::TailCall { func: new_fval, args: new_args } => {
                        fval = new_fval;
                        args = new_args;
                    }
                    Trampoline::TailEval { code, env } => return eval_compiled(hs, &env, code),
                }
            }

            Value::Lambda(lambda) => {
                let code = lambda.code();
                let parent = lambda.env();
                let senv = code.environments().get(0);
                let names = senv.names();
                let n_names = names.len();
                let has_rest = code.rest();

                let n_required_params = n_names - has_rest as usize;
                if args.len() < n_required_params {
                    return Err("not enough arguments".into());
                }
                if has_rest {
                    let mut rest_list = Value::Nil;
                    for v in args.drain(n_required_params..).rev() {
                        rest_list = Value::Cons(hs.alloc(Pair {
                            car: v,
                            cdr: rest_list,
                        }));
                    }
                    args.push(rest_list);
                } else if args.len() > n_required_params {
                    return Err("too many arguments".into());
                }

                let values = hs.alloc(args);
                let env = Environment::new(hs, Some(parent), senv, values);
                return eval_compiled(hs, &env, code);
            }

            _ => {
                return Err("not a procedure".into());
            }
        }
    }
}

// Fully evaluate the given code in the given environment.
pub fn eval_compiled<'h>(
    hs: &mut GcHeapSession<'h>,
    env: &EnvironmentRef<'h>,
    mut code: CodeRef<'h>,
) -> Result<Value<'h>> {
    assert_eq!(code.environments().get(0), env.senv(),
               "code can only run in the environment for which it was compiled");

    // The stack is a sequence of concatenated stack frames. Each frame is laid out like this:
    //   #(code pc env operand0 ...)

    let mut stack = Vec::<Value<'h>>::with_capacity(1024);
    let mut constants = code.constants();
    let mut environments = code.environments();
    let mut insns = code.insns();
    let mut env = env.clone();
    let mut pc = 0;

    macro_rules! return_value {
        ($value:ident) => {
            if stack.is_empty() {
                return Ok($value);
            }

            // Read the caller's stack frame back into local variables.
            env = stack.pop().unwrap().as_environment("internal error").unwrap();
            pc = stack.pop().unwrap().as_int("internal error").unwrap() as usize;
            code = stack.pop().unwrap().as_code("internal error").unwrap();
            constants = code.constants();
            environments = code.environments();
            insns = code.insns();

            // Push the return value to its operand stack.
            stack.push($value);
        }
    }

    loop {
        let op_code = insns.get(pc);
        pc += 1;
        match op_code {
            op::RETURN => {
                // The return value is the last thing left on the current
                // frame's operand stack.
                let value = stack.pop().unwrap();
                return_value!(value);
            }

            op::POP => {
                stack.pop().unwrap();
            }

            op::CONSTANT => {
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(constants.get(i));
            }

            op::GET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad GetDynamic insn"),
                };
                stack.push(env.dynamic_get(&symbol)?);
            }

            op::GET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(env.get(up_count, i));
            }

            op::LAMBDA => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let fn_code = match constants.get(i) {
                    Value::Code(code) => code,
                    _ => panic!("internal error: bad Lambda insn"),
                };
                let fn_value = Value::Lambda(hs.alloc(Lambda {
                    code: fn_code,
                    env: env.clone(),
                }));
                stack.push(fn_value);
            }

            op::CALL | op::TAIL_CALL => {
                let argc = insns.get(pc) as usize;
                pc += 1;

                let top = stack.len();
                let mut args = stack.split_off(top - argc);
                let mut fval = stack.pop().unwrap();

                loop {
                    match fval {
                        Value::Lambda(lambda) => {
                            if op_code == op::CALL {
                                stack.push(Value::Code(code));
                                assert!(pc <= i32::max_value() as usize);
                                stack.push(Value::Int(pc as i32));
                                stack.push(Value::Environment(env));
                            } else {
                                // Tail call. Our caller's frame is already on top
                                // of stack. Assert that it's correctly laid out.
                                let top = stack.len();
                                if top != 0 {
                                    assert!(stack[top - 3].is_code());
                                    assert!(stack[top - 2].is_int());
                                    assert!(stack[top - 1].is_environment());
                                }
                            }

                            code = lambda.code();
                            constants = code.constants();
                            environments = code.environments();
                            insns = code.insns();
                            pc = 0;
                            env = new_call_env(hs, code.environments().get(0), code.rest(), lambda.env(), args)?;
                            break;
                        }
                        Value::Builtin(f) => {
                            match (f.0)(hs, args)? {
                                Trampoline::Value(v) => {
                                    if op_code == op::TAIL_CALL {
                                        return_value!(v);
                                    } else {
                                        stack.push(v);
                                    }
                                    break;
                                }
                                Trampoline::TailCall { func: new_fval, args: new_args } => {
                                    fval = new_fval;
                                    args = new_args;
                                }
                                Trampoline::TailEval { code: eval_code, env: eval_env } => {
                                    code = eval_code;
                                    constants = code.constants();
                                    environments = code.environments();
                                    insns = code.insns();
                                    pc = 0;
                                    env = eval_env;
                                    break;
                                }
                            }
                        }
                        _ => return Err("not a procedure".into())
                    }
                }
            }

            op::JUMP_IF_FALSE => {
                let offset = match stack.pop().unwrap() {
                    Value::Bool(false) => insns.get(pc) as usize,
                    _ => 1
                };
                pc += offset;
            }

            op::JUMP => {
                pc += insns.get(pc) as usize;
            }

            op::DEFINE => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad Define insn"),
                };
                let value = stack.pop().unwrap();
                env.define(symbol.unwrap(), value);
            }

            op::SET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                env.set(up_count, i, stack.pop().unwrap());
            }

            op::SET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id.unwrap(),
                    _ => panic!("internal error: bad Set insn"),
                };
                let value = stack.pop().unwrap();
                env.dynamic_set(&symbol, value)?;
            }

            op::PUSH_ENV => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let senv = environments.get(i);
                let values: VecRef<'h, Value<'h>> = hs.alloc(
                    (0..senv.names().len())
                        .map(|_| Value::Unspecified)
                        .collect::<Vec<Value<'h>>>(),
                );
                env = Environment::new(hs, Some(env), senv, values);
            }

            op::POP_ENV => {
                env = env.parent().unwrap();
            }

            op::SAVE => {
                // Capture the current function activation's continuation as a
                // vector, a snapshot of the stack. This opcode is used only in
                // call/cc.
                //
                // Note that this does not capture the continuation of the
                // current *instruction*, which would have to include the
                // current values of code, pc, and env. What call/cc needs to
                // capture is the continuation of call/cc.
                let v = hs.alloc(stack.clone());
                stack.push(Value::Vector(v));
            }

            op::RESTORE => {
                // Resume a previously captured continuation. The continuation
                // and the value we will pass to it are taken from just-so
                // locations in the current environment. This opcode is used
                // exclusively in the code for continuation-lambdas produced by
                // call/cc.
                let arg = env.get(0, 0);
                let new_stack = env.get(1, 0)
                    .as_vector("internal error").unwrap();
                stack.clear();
                stack.extend(new_stack);
                return_value!(arg);
            }

            _ => panic!("internal error: invalid opcode {}", op_code),
        }
    }
}
<commit_msg>lisp: Fix `(eval)`, which was busted.<commit_after>//! Interpreter for compiled code.

use cell_gc::GcHeapSession;
use cell_gc::collections::VecRef;
use compile::{op, CodeRef};
use env::{Environment, EnvironmentRef, StaticEnvironmentRef};
use errors::Result;
use value::{Lambda, Pair, Value};

/// A potentially partially evaluated value.
///
/// When the interpreter calls a builtin, usually the builtin runs, computes a
/// Value, and returns it. But a small number of builtins (`apply`, `eval`) can
/// tail-call Scheme code. This must be done without recursively invoking the
/// interpreter. That would consume some system stack space; it wouldn't be a
/// true tail call. So instead, the builtin returns a special Trampoline to the
/// interpreter that means "I did the part of the computation that's written in
/// Rust; the rest is Scheme code and here's how to run it". The word "returns"
/// is the key: the stack space that was expended to call the builtin is
/// reclaimed when it returns.
pub enum Trampoline<'h> {
    /// A completely evaluated value.
    Value(Value<'h>),

    /// A value that can be computed by calling a particular procedure.
    TailCall {
        func: Value<'h>,
        args: Vec<Value<'h>>,
    },

    /// A value that can be computed by running the given code.
    TailEval {
        code: CodeRef<'h>,
        env: EnvironmentRef<'h>
    }
}

fn new_call_env<'h>(
    hs: &mut GcHeapSession<'h>,
    senv: StaticEnvironmentRef<'h>,
    has_rest: bool,
    parent: EnvironmentRef<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<EnvironmentRef<'h>> {
    let names = senv.names();
    let n_names = names.len();
    let n_required_params = n_names - has_rest as usize;

    let n_actual = args.len();
    if n_actual < n_required_params {
        return Err("not enough arguments".into());
    }
    if has_rest {
        let mut rest_list = Value::Nil;
        for arg in args.drain(n_required_params..).rev() {
            rest_list = Value::Cons(hs.alloc(Pair {
                car: arg,
                cdr: rest_list,
            }));
        }
        args.push(rest_list);
    } else if n_actual > n_required_params {
        return Err("too many arguments".into());
    }

    let args = hs.alloc(args);  // move values into heap
    Ok(Environment::new(hs, Some(parent), senv, args))
}

pub fn apply<'h>(
    hs: &mut GcHeapSession<'h>,
    mut fval: Value<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<Value<'h>> {
    loop {
        match fval {
            Value::Builtin(f) => {
                match (f.0)(hs, args)? {
                    Trampoline::Value(v) => return Ok(v),
                    Trampoline::TailCall { func: new_fval, args: new_args } => {
                        fval = new_fval;
                        args = new_args;
                    }
                    Trampoline::TailEval { code, env } => return eval_compiled(hs, &env, code),
                }
            }

            Value::Lambda(lambda) => {
                let code = lambda.code();
                let parent = lambda.env();
                let senv = code.environments().get(0);
                let names = senv.names();
                let n_names = names.len();
                let has_rest = code.rest();

                let n_required_params = n_names - has_rest as usize;
                if args.len() < n_required_params {
                    return Err("not enough arguments".into());
                }
                if has_rest {
                    let mut rest_list = Value::Nil;
                    for v in args.drain(n_required_params..).rev() {
                        rest_list = Value::Cons(hs.alloc(Pair {
                            car: v,
                            cdr: rest_list,
                        }));
                    }
                    args.push(rest_list);
                } else if args.len() > n_required_params {
                    return Err("too many arguments".into());
                }

                let values = hs.alloc(args);
                let env = Environment::new(hs, Some(parent), senv, values);
                return eval_compiled(hs, &env, code);
            }

            _ => {
                return Err("not a procedure".into());
            }
        }
    }
}

// Fully evaluate the given code in the given environment.
pub fn eval_compiled<'h>(
    hs: &mut GcHeapSession<'h>,
    env: &EnvironmentRef<'h>,
    mut code: CodeRef<'h>,
) -> Result<Value<'h>> {
    assert_eq!(code.environments().get(0), env.senv(),
               "code can only run in the environment for which it was compiled");

    // The stack is a sequence of concatenated stack frames. Each frame is laid out like this:
    //   #(code pc env operand0 ...)

    let mut stack = Vec::<Value<'h>>::with_capacity(1024);
    let mut constants = code.constants();
    let mut environments = code.environments();
    let mut insns = code.insns();
    let mut env = env.clone();
    let mut pc = 0;

    macro_rules! push_stack_frame_unless_tail_call {
        ($op_code:expr) => {
            if $op_code == op::CALL {
                stack.push(Value::Code(code));
                assert!(pc <= i32::max_value() as usize);
                stack.push(Value::Int(pc as i32));
                stack.push(Value::Environment(env));
            } else {
                // Tail call. Our caller's frame is already on top
                // of stack. Assert that it's correctly laid out.
                let top = stack.len();
                if top != 0 {
                    assert!(stack[top - 3].is_code());
                    assert!(stack[top - 2].is_int());
                    assert!(stack[top - 1].is_environment());
                }
            }
        }
    }

    macro_rules! return_value {
        ($value:ident) => {
            if stack.is_empty() {
                return Ok($value);
            }

            // Read the caller's stack frame back into local variables.
            env = stack.pop().unwrap().as_environment("internal error").unwrap();
            pc = stack.pop().unwrap().as_int("internal error").unwrap() as usize;
            code = stack.pop().unwrap().as_code("internal error").unwrap();
            constants = code.constants();
            environments = code.environments();
            insns = code.insns();

            // Push the return value to its operand stack.
            stack.push($value);
        }
    }

    loop {
        let op_code = insns.get(pc);
        pc += 1;
        match op_code {
            op::RETURN => {
                // The return value is the last thing left on the current
                // frame's operand stack.
                let value = stack.pop().unwrap();
                return_value!(value);
            }

            op::POP => {
                stack.pop().unwrap();
            }

            op::CONSTANT => {
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(constants.get(i));
            }

            op::GET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad GetDynamic insn"),
                };
                stack.push(env.dynamic_get(&symbol)?);
            }

            op::GET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(env.get(up_count, i));
            }

            op::LAMBDA => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let fn_code = match constants.get(i) {
                    Value::Code(code) => code,
                    _ => panic!("internal error: bad Lambda insn"),
                };
                let fn_value = Value::Lambda(hs.alloc(Lambda {
                    code: fn_code,
                    env: env.clone(),
                }));
                stack.push(fn_value);
            }

            op::CALL | op::TAIL_CALL => {
                let argc = insns.get(pc) as usize;
                pc += 1;

                let top = stack.len();
                let mut args = stack.split_off(top - argc);
                let mut fval = stack.pop().unwrap();

                loop {
                    match fval {
                        Value::Lambda(lambda) => {
                            push_stack_frame_unless_tail_call!(op_code);

                            code = lambda.code();
                            constants = code.constants();
                            environments = code.environments();
                            insns = code.insns();
                            pc = 0;
                            env = new_call_env(hs, code.environments().get(0), code.rest(), lambda.env(), args)?;
                            break;
                        }
                        Value::Builtin(f) => {
                            match (f.0)(hs, args)? {
                                Trampoline::Value(v) => {
                                    if op_code == op::TAIL_CALL {
                                        return_value!(v);
                                    } else {
                                        stack.push(v);
                                    }
                                    break;
                                }
                                Trampoline::TailCall { func: new_fval, args: new_args } => {
                                    fval = new_fval;
                                    args = new_args;
                                }
                                Trampoline::TailEval { code: eval_code, env: eval_env } => {
                                    push_stack_frame_unless_tail_call!(op_code);

                                    code = eval_code;
                                    constants = code.constants();
                                    environments = code.environments();
                                    insns = code.insns();
                                    pc = 0;
                                    env = eval_env;
                                    break;
                                }
                            }
                        }
                        _ => return Err("not a procedure".into())
                    }
                }
            }

            op::JUMP_IF_FALSE => {
                let offset = match stack.pop().unwrap() {
                    Value::Bool(false) => insns.get(pc) as usize,
                    _ => 1
                };
                pc += offset;
            }

            op::JUMP => {
                pc += insns.get(pc) as usize;
            }

            op::DEFINE => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad Define insn"),
                };
                let value = stack.pop().unwrap();
                env.define(symbol.unwrap(), value);
            }

            op::SET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                env.set(up_count, i, stack.pop().unwrap());
            }

            op::SET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id.unwrap(),
                    _ => panic!("internal error: bad Set insn"),
                };
                let value = stack.pop().unwrap();
                env.dynamic_set(&symbol, value)?;
            }

            op::PUSH_ENV => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let senv = environments.get(i);
                let values: VecRef<'h, Value<'h>> = hs.alloc(
                    (0..senv.names().len())
                        .map(|_| Value::Unspecified)
                        .collect::<Vec<Value<'h>>>(),
                );
                env = Environment::new(hs, Some(env), senv, values);
            }

            op::POP_ENV => {
                env = env.parent().unwrap();
            }

            op::SAVE => {
                // Capture the current function activation's continuation as a
                // vector, a snapshot of the stack. This opcode is used only in
                // call/cc.
                //
                // Note that this does not capture the continuation of the
                // current *instruction*, which would have to include the
                // current values of code, pc, and env. What call/cc needs to
                // capture is the continuation of call/cc.
                let v = hs.alloc(stack.clone());
                stack.push(Value::Vector(v));
            }

            op::RESTORE => {
                // Resume a previously captured continuation. The continuation
                // and the value we will pass to it are taken from just-so
                // locations in the current environment. This opcode is used
                // exclusively in the code for continuation-lambdas produced by
                // call/cc.
                let arg = env.get(0, 0);
                let new_stack = env.get(1, 0)
                    .as_vector("internal error").unwrap();
                stack.clear();
                stack.extend(new_stack);
                return_value!(arg);
            }

            _ => panic!("internal error: invalid opcode {}", op_code),
        }
    }
}
<|endoftext|>