<commit_before>use token::Token;
use book::{Book, compile_str};
use number::Number;
use error::Result;
use templates::odt;
use zipper::Zipper;
use parser::Parser;

use crowbook_text_processing::escape;

/// Rendererer for ODT
///
/// Still very experimental.
pub struct OdtRenderer<'a> {
    book: &'a Book,
    current_numbering: i32,
    current_hide: bool,
    current_chapter: i32,
    automatic_styles: String,
}

impl<'a> OdtRenderer<'a> {
    /// Creates a new OdtRenderer
    pub fn new(book: &'a Book) -> OdtRenderer {
        OdtRenderer {
            book: book,
            current_chapter: 1,
            current_numbering: book.options.get_i32("rendering.num_depth").unwrap(),
            current_hide: false,
            automatic_styles: String::from("
<style:style style:name=\"T1\" \
                                            style:family=\"text\">
  <style:text-properties \
                                            fo:font-style=\"italic\" \
                                            style:font-style-asian=\"italic\" \
                                            style:font-style-complex=\"italic\"/>
</style:style>
\
                                            <style:style style:name=\"T2\" \
                                            style:family=\"text\">
  <style:text-properties \
                                            fo:font-weight=\"bold\" \
                                            style:font-weight-asian=\"bold\" \
                                            style:font-weight-complex=\"bold\"/>
</style:style>"),
        }
    }

    /// Renders a full book
    ///
    /// This will try to generate an ODT file according to self.book options.
    ///
    /// # Returns
    /// * `Ok(s)` where `s` contains the output of the `zip` command
    ///   used to create the ODT file.
    /// * An error if there was somel problem during either the rendering to
    ///   ODT format, or the generation of the ODT file itself.
    pub fn render_book(&mut self) -> Result<String> {
        let content = try!(self.render_content());

        let mut zipper =
            try!(Zipper::new(&self.book.options.get_path("crowbook.temp_dir").unwrap()));

        // Write template.odt there
        try!(zipper.write("template.odt", odt::ODT, false));
        // unzip it
        try!(zipper.unzip("template.odt"));
        // Complete it with content.xml
        try!(zipper.write("content.xml", &content.as_bytes(), false));
        // Zip and copy
        if let Ok(ref file) = self.book.options.get_path("output.odt") {
            zipper.generate_odt(self.book.options.get_str("crowbook.zip.command").unwrap(),
                                file)
        } else {
            panic!(lformat!("odt.render_book called while book.output_odt is not set"));
        }
    }

    /// Render content.xml
    fn render_content(&mut self) -> Result<String> {
        let mut content = String::new();

        for &(n, ref v) in &self.book.chapters {
            self.current_hide = false;
            match n {
                Number::Unnumbered => self.current_numbering = 0,
                Number::Default => {
                    self.current_numbering =
                        self.book.options.get_i32("rendering.num_depth").unwrap()
                }
                Number::Specified(n) => {
                    self.current_numbering = self.book.options.get_i32("numbering").unwrap();
                    self.current_chapter = n;
                }
                Number::Hidden => {
                    self.current_numbering = 0;
                    self.current_hide = true;
                }
                _ => panic!(lformat!("Parts are not supported yet")),
            }
            for token in v {
                content.push_str(&self.parse_token(token));
            }
        }

        let template = try!(compile_str(odt::CONTENT,
                                        &self.book.source,
                                        "could not compile template for content.xml"));
        let data = try!(self.book.get_metadata(|s| Ok(s.to_owned())))
            .insert_str("content", content)
            .insert_str("automatic_styles", &self.automatic_styles)
            .build();

        let mut res: Vec<u8> = vec![];
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!(lformat!("generated content.xml was not utf-8 valid")),
            Ok(res) => Ok(res),
        }
    }

    /// Transform a vector of `Token`s to Odt format
    fn render_vec(&mut self, tokens: &[Token]) -> String {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&self.parse_token(&token));
        }
        res
    }

    fn parse_token(&mut self, token: &Token) -> String {
        match *token {
            Token::Str(ref text) => escape::html(self.book.clean(text.as_str(), false)).into_owned(),
            Token::Paragraph(ref vec) => {
                format!("<text:p text:style-name=\"Text_20_body\">{}</text:p>\n",
                        self.render_vec(vec))
            }
            Token::Header(n, ref vec) => {
                if n == 1 && self.current_hide {
                    return String::new();
                }
                let s = if n == 1 && self.current_numbering >= 1 {
                    let chapter = self.current_chapter;
                    self.current_chapter += 1;
                    let res = self.book.get_chapter_header(chapter, self.render_vec(vec), |s| {
                        Ok(self.render_vec(&try!(Parser::new().parse_inline(s))))
                    });
                    res.unwrap()
                } else {
                    self.render_vec(vec)
                };
                format!("<text:h text:style-name=\"Heading_20_{}\">\n{}</text:h>\n",
                        n,
                        s)
            }
            Token::Emphasis(ref vec) => {
                format!("<text:span text:style-name=\"T1\">{}</text:span>",
                        self.render_vec(vec))
            }
            Token::Strong(ref vec) => {
                format!("<text:span text:style-name=\"T2\">{}</text:span>",
                        self.render_vec(vec))
            }
            Token::List(ref vec) => format!("<text:list>\n{}</text:list>\n", self.render_vec(vec)),
            Token::OrderedList(_, ref vec) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: ordered list not currently implemented for this \
                                       format, fallbacking to unordered one"));
                format!("<text:list>\n{}</text:list>\n", self.render_vec(vec))
            }
            Token::Item(ref vec) => {
                format!("<text:list-item>\n<text:p>{}</text:p></text:list-item>",
                        self.render_vec(vec))
            }
            Token::Link(ref url, _, ref vec) => {
                format!("<text:a xlink:type=\"simple\"  xlink:href=\"{}\">{}</text:a>",
                        url,
                        self.render_vec(vec))
            }
            Token::Code(ref vec) => {
                format!("<text:span text:style-name=\"Preformatted_20_Text\">{}</text:span>",
                        self.render_vec(vec))
            }
            Token::BlockQuote(ref vec) |
            Token::CodeBlock(_, ref vec) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: blockquotes and codeblocks are not currently \
                                       implemented for ODT"));
                format!("<text:p text:style-name=\"Text_20_Body\">{}</text:p>\n",
                        self.render_vec(vec))
            }
            Token::SoftBreak | Token::HardBreak => String::from(" "),
            Token::Rule => String::from("<text:p /><text:p>***</text:p><text:p />"),
            Token::Image(_, _, _) |
            Token::StandaloneImage(_, _, _) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: images not currently implemented for this format"));
                String::from(" ")
            }
            Token::Table(_, _) |
            Token::TableHead(_) |
            Token::TableRow(_) |
            Token::TableCell(_) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: tables are not currently implemented for this format"));
                String::from(" ")
            }
            Token::Footnote(_) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: footnotes are not yet implemented in this format, \
                                       ignoring {:?}",
                                      token));
                String::new()
            }
            Token::Annotation(_, ref vec) => self.render_vec(vec),
            Token::__NonExhaustive => unreachable!(),
        }
    }
}
<commit_msg>Use ? in odt.rs<commit_after>use token::Token;
use book::{Book, compile_str};
use number::Number;
use error::Result;
use templates::odt;
use zipper::Zipper;
use parser::Parser;

use crowbook_text_processing::escape;

/// Rendererer for ODT
///
/// Still very experimental.
pub struct OdtRenderer<'a> {
    book: &'a Book,
    current_numbering: i32,
    current_hide: bool,
    current_chapter: i32,
    automatic_styles: String,
}

impl<'a> OdtRenderer<'a> {
    /// Creates a new OdtRenderer
    pub fn new(book: &'a Book) -> OdtRenderer {
        OdtRenderer {
            book: book,
            current_chapter: 1,
            current_numbering: book.options.get_i32("rendering.num_depth").unwrap(),
            current_hide: false,
            automatic_styles: String::from("
<style:style style:name=\"T1\" \
                                            style:family=\"text\">
  <style:text-properties \
                                            fo:font-style=\"italic\" \
                                            style:font-style-asian=\"italic\" \
                                            style:font-style-complex=\"italic\"/>
</style:style>
\
                                            <style:style style:name=\"T2\" \
                                            style:family=\"text\">
  <style:text-properties \
                                            fo:font-weight=\"bold\" \
                                            style:font-weight-asian=\"bold\" \
                                            style:font-weight-complex=\"bold\"/>
</style:style>"),
        }
    }

    /// Renders a full book
    ///
    /// This will try to generate an ODT file according to self.book options.
    ///
    /// # Returns
    /// * `Ok(s)` where `s` contains the output of the `zip` command
    ///   used to create the ODT file.
    /// * An error if there was somel problem during either the rendering to
    ///   ODT format, or the generation of the ODT file itself.
    pub fn render_book(&mut self) -> Result<String> {
        let content = self.render_content()?;

        let mut zipper =
            Zipper::new(&self.book.options.get_path("crowbook.temp_dir").unwrap())?;

        // Write template.odt there
        zipper.write("template.odt", odt::ODT, false)?;
        // unzip it
        zipper.unzip("template.odt")?;
        // Complete it with content.xml
        zipper.write("content.xml", &content.as_bytes(), false)?;
        // Zip and copy
        if let Ok(ref file) = self.book.options.get_path("output.odt") {
            zipper.generate_odt(self.book.options.get_str("crowbook.zip.command").unwrap(),
                                file)
        } else {
            panic!(lformat!("odt.render_book called while book.output_odt is not set"));
        }
    }

    /// Render content.xml
    fn render_content(&mut self) -> Result<String> {
        let mut content = String::new();

        for &(n, ref v) in &self.book.chapters {
            self.current_hide = false;
            match n {
                Number::Unnumbered => self.current_numbering = 0,
                Number::Default => {
                    self.current_numbering =
                        self.book.options.get_i32("rendering.num_depth").unwrap()
                }
                Number::Specified(n) => {
                    self.current_numbering = self.book.options.get_i32("numbering").unwrap();
                    self.current_chapter = n;
                }
                Number::Hidden => {
                    self.current_numbering = 0;
                    self.current_hide = true;
                }
                _ => panic!(lformat!("Parts are not supported yet")),
            }
            for token in v {
                content.push_str(&self.parse_token(token));
            }
        }

        let template = compile_str(odt::CONTENT,
                                   &self.book.source,
                                   "could not compile template for content.xml")?;
        let data = self.book.get_metadata(|s| Ok(s.to_owned()))?
            .insert_str("content", content)
            .insert_str("automatic_styles", &self.automatic_styles)
            .build();

        let mut res: Vec<u8> = vec![];
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!(lformat!("generated content.xml was not utf-8 valid")),
            Ok(res) => Ok(res),
        }
    }

    /// Transform a vector of `Token`s to Odt format
    fn render_vec(&mut self, tokens: &[Token]) -> String {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&self.parse_token(&token));
        }
        res
    }

    fn parse_token(&mut self, token: &Token) -> String {
        match *token {
            Token::Str(ref text) => escape::html(self.book.clean(text.as_str(), false)).into_owned(),
            Token::Paragraph(ref vec) => {
                format!("<text:p text:style-name=\"Text_20_body\">{}</text:p>\n",
                        self.render_vec(vec))
            }
            Token::Header(n, ref vec) => {
                if n == 1 && self.current_hide {
                    return String::new();
                }
                let s = if n == 1 && self.current_numbering >= 1 {
                    let chapter = self.current_chapter;
                    self.current_chapter += 1;
                    let res = self.book.get_chapter_header(chapter, self.render_vec(vec), |s| {
                        Ok(self.render_vec(&Parser::new().parse_inline(s)?))
                    });
                    res.unwrap()
                } else {
                    self.render_vec(vec)
                };
                format!("<text:h text:style-name=\"Heading_20_{}\">\n{}</text:h>\n",
                        n,
                        s)
            }
            Token::Emphasis(ref vec) => {
                format!("<text:span text:style-name=\"T1\">{}</text:span>",
                        self.render_vec(vec))
            }
            Token::Strong(ref vec) => {
                format!("<text:span text:style-name=\"T2\">{}</text:span>",
                        self.render_vec(vec))
            }
            Token::List(ref vec) => format!("<text:list>\n{}</text:list>\n", self.render_vec(vec)),
            Token::OrderedList(_, ref vec) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: ordered list not currently implemented for this \
                                       format, fallbacking to unordered one"));
                format!("<text:list>\n{}</text:list>\n", self.render_vec(vec))
            }
            Token::Item(ref vec) => {
                format!("<text:list-item>\n<text:p>{}</text:p></text:list-item>",
                        self.render_vec(vec))
            }
            Token::Link(ref url, _, ref vec) => {
                format!("<text:a xlink:type=\"simple\"  xlink:href=\"{}\">{}</text:a>",
                        url,
                        self.render_vec(vec))
            }
            Token::Code(ref vec) => {
                format!("<text:span text:style-name=\"Preformatted_20_Text\">{}</text:span>",
                        self.render_vec(vec))
            }
            Token::BlockQuote(ref vec) |
            Token::CodeBlock(_, ref vec) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: blockquotes and codeblocks are not currently \
                                       implemented for ODT"));
                format!("<text:p text:style-name=\"Text_20_Body\">{}</text:p>\n",
                        self.render_vec(vec))
            }
            Token::SoftBreak | Token::HardBreak => String::from(" "),
            Token::Rule => String::from("<text:p /><text:p>***</text:p><text:p />"),
            Token::Image(_, _, _) |
            Token::StandaloneImage(_, _, _) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: images not currently implemented for this format"));
                String::from(" ")
            }
            Token::Table(_, _) |
            Token::TableHead(_) |
            Token::TableRow(_) |
            Token::TableCell(_) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: tables are not currently implemented for this format"));
                String::from(" ")
            }
            Token::Footnote(_) => {
                self.book
                    .logger
                    .warning(lformat!("ODT: footnotes are not yet implemented in this format, \
                                       ignoring {:?}",
                                      token));
                String::new()
            }
            Token::Annotation(_, ref vec) => self.render_vec(vec),
            Token::__NonExhaustive => unreachable!(),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap};

use num::bigint::{BigInt};

use opcode::{OpCode, BooleanOrInt};
use value::{Value};

pub struct Machine {
    stack: Vec<Value>,
    memo: HashMap<usize, Value>,
    marker: Option<usize>,
}

impl Machine {
    fn new() -> Self {
        Machine {
            stack: Vec::new(),
            memo: HashMap::new(),
            marker: None,
        }
    }

    fn split_off(&mut self, at: usize) -> Vec<Value> {
        if at > self.stack.len() {
            panic!("Stack too small");
        }
        self.stack.split_off(at)
    }

    fn pop(&mut self) -> Value {
        match self.stack.pop() {
            None => panic!("Empty stack"),
            Some(value) => value,
        }
    }

    fn execute(&mut self, opcode: OpCode) {
        match opcode {
            OpCode::Proto(_) => (),
            OpCode::Stop => (),  // TODO: !!!

            OpCode::Int(value) => {
                self.stack.push(match value {
                    BooleanOrInt::Boolean(v) => Value::Bool(v),
                    BooleanOrInt::Int(v) => Value::Int(BigInt::from(v)),
                })
            },
            OpCode::BinInt(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::BinInt1(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::BinInt2(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::Long(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::Long1(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::Long4(i) => self.stack.push(Value::Int(BigInt::from(i))),

            OpCode::String(s) => self.stack.push(Value::String(s)),
            OpCode::BinString(s) => self.stack.push(Value::String(s)),
            OpCode::ShortBinString(s) => self.stack.push(Value::String(s)),

            OpCode::None => self.stack.push(Value::None),

            OpCode::NewTrue => self.stack.push(Value::Bool(true)),
            OpCode::NewFalse => self.stack.push(Value::Bool(false)),

            OpCode::Unicode(s) => self.stack.push(Value::Unicode(s)),
            OpCode::BinUnicode(s) => self.stack.push(Value::Unicode(s)),

            OpCode::Float(i) => self.stack.push(Value::Float(i)),
            OpCode::BinFloat(i) => self.stack.push(Value::Float(i)),

            OpCode::EmptyList => self.stack.push(Value::List(Vec::new())),
            OpCode::Append => {
                let v = self.pop();
                match self.stack.last_mut() {
                    None => panic!("Empty stack"),
                    Some(&mut Value::List(ref mut list)) => list.push(v),
                    _ => panic!("Invalid value on stack"),
                }
            },
            OpCode::Appends => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let values = self.split_off(marker);
                        match self.stack.last_mut() {
                            None => panic!("Empty stack"),
                            Some(&mut Value::List(ref mut list)) => {
                                list.extend(values);
                            },
                            _ => panic!("Invalid value on stack"),
                        }
                    }
                }
            },
            OpCode::List => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let values = self.split_off(marker);
                        self.stack.push(Value::List(values));
                    }
                }
            },

            OpCode::EmptyTuple => self.stack.push(Value::Tuple(Vec::new())),
            OpCode::Tuple => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let values = self.split_off(marker);
                        self.stack.push(Value::Tuple(values));
                    }
                }
            },
            OpCode::Tuple1 => {
                let v1 = self.pop();
                self.stack.push(Value::Tuple(vec![v1]))
            },
            OpCode::Tuple2 => {
                let v1 = self.pop();
                let v2 = self.pop();
                self.stack.push(Value::Tuple(vec![v1, v2]))
            },
            OpCode::Tuple3 => {
                let v1 = self.pop();
                let v2 = self.pop();
                let v3 = self.pop();
                self.stack.push(Value::Tuple(vec![v1, v2, v3]))
            }

            OpCode::EmptyDict => self.stack.push(Value::Dict(Vec::new())),
            OpCode::Dict => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let mut values = self.split_off(marker);
                        let mut dict = Vec::new();

                        for i in 0 .. values.len() / 2 { // TODO: Check panic
                            let key = values.remove(2 * i);
                            let value = values.remove(2 * i + 1);
                            dict.push((key, value));
                        }
                        self.stack.push(Value::Dict(dict));
                    }
                }
            },
            OpCode::SetItem => {
                let value = self.pop();
                let key = self.pop();
                match self.stack.last_mut() {
                    None => panic!("Empty stack"),
                    Some(&mut Value::Dict(ref mut dict)) => dict.push((key, value)),
                    _ => panic!("Invalid value on stack"),
                }
            },
            OpCode::SetItems => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let mut values = self.split_off(marker);

                        match self.stack.last_mut() {
                            None => panic!("Empty stack"),
                            Some(&mut Value::Dict(ref mut dict)) => {
                                for i in 0 .. values.len() / 2 { // TODO: Check panic
                                    let key = values.remove(2 * i);
                                    let value = values.remove(2 * i + 1);
                                    dict.push((key, value));
                                }
                            },
                            _ => panic!("Invalid value on stack"),
                        }
                    }
                }
            },

            _ => panic!("Not implemented")
        }
    }
}<commit_msg>Implement pickle machine for stack manipulation<commit_after>use std::collections::{HashMap};

use num::bigint::{BigInt};

use opcode::{OpCode, BooleanOrInt};
use value::{Value};

pub struct Machine {
    stack: Vec<Value>,
    memo: HashMap<usize, Value>,
    marker: Option<usize>,
}

impl Machine {
    fn new() -> Self {
        Machine {
            stack: Vec::new(),
            memo: HashMap::new(),
            marker: None,
        }
    }

    fn split_off(&mut self, at: usize) -> Vec<Value> {
        if at > self.stack.len() {
            panic!("Stack too small");
        }
        self.stack.split_off(at)
    }

    fn pop(&mut self) -> Value {
        match self.stack.pop() {
            None => panic!("Empty stack"),
            Some(value) => value,
        }
    }

    fn execute(&mut self, opcode: OpCode) {
        match opcode {
            OpCode::Proto(_) => (),
            OpCode::Stop => (),  // TODO: !!!

            OpCode::Int(value) => {
                self.stack.push(match value {
                    BooleanOrInt::Boolean(v) => Value::Bool(v),
                    BooleanOrInt::Int(v) => Value::Int(BigInt::from(v)),
                })
            },
            OpCode::BinInt(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::BinInt1(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::BinInt2(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::Long(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::Long1(i) => self.stack.push(Value::Int(BigInt::from(i))),
            OpCode::Long4(i) => self.stack.push(Value::Int(BigInt::from(i))),

            OpCode::String(s) => self.stack.push(Value::String(s)),
            OpCode::BinString(s) => self.stack.push(Value::String(s)),
            OpCode::ShortBinString(s) => self.stack.push(Value::String(s)),

            OpCode::None => self.stack.push(Value::None),

            OpCode::NewTrue => self.stack.push(Value::Bool(true)),
            OpCode::NewFalse => self.stack.push(Value::Bool(false)),

            OpCode::Unicode(s) => self.stack.push(Value::Unicode(s)),
            OpCode::BinUnicode(s) => self.stack.push(Value::Unicode(s)),

            OpCode::Float(i) => self.stack.push(Value::Float(i)),
            OpCode::BinFloat(i) => self.stack.push(Value::Float(i)),

            OpCode::EmptyList => self.stack.push(Value::List(Vec::new())),
            OpCode::Append => {
                let v = self.pop();
                match self.stack.last_mut() {
                    None => panic!("Empty stack"),
                    Some(&mut Value::List(ref mut list)) => list.push(v),
                    _ => panic!("Invalid value on stack"),
                }
            },
            OpCode::Appends => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let values = self.split_off(marker);
                        match self.stack.last_mut() {
                            None => panic!("Empty stack"),
                            Some(&mut Value::List(ref mut list)) => {
                                list.extend(values);
                            },
                            _ => panic!("Invalid value on stack"),
                        }
                    }
                }
            },
            OpCode::List => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let values = self.split_off(marker);
                        self.stack.push(Value::List(values));
                    }
                }
            },

            OpCode::EmptyTuple => self.stack.push(Value::Tuple(Vec::new())),
            OpCode::Tuple => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let values = self.split_off(marker);
                        self.stack.push(Value::Tuple(values));
                    }
                }
            },
            OpCode::Tuple1 => {
                let v1 = self.pop();
                self.stack.push(Value::Tuple(vec![v1]))
            },
            OpCode::Tuple2 => {
                let v1 = self.pop();
                let v2 = self.pop();
                self.stack.push(Value::Tuple(vec![v1, v2]))
            },
            OpCode::Tuple3 => {
                let v1 = self.pop();
                let v2 = self.pop();
                let v3 = self.pop();
                self.stack.push(Value::Tuple(vec![v1, v2, v3]))
            }

            OpCode::EmptyDict => self.stack.push(Value::Dict(Vec::new())),
            OpCode::Dict => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let mut values = self.split_off(marker);
                        let mut dict = Vec::new();

                        for i in 0 .. values.len() / 2 { // TODO: Check panic
                            let key = values.remove(2 * i);
                            let value = values.remove(2 * i + 1);
                            dict.push((key, value));
                        }
                        self.stack.push(Value::Dict(dict));
                    }
                }
            },
            OpCode::SetItem => {
                let value = self.pop();
                let key = self.pop();
                match self.stack.last_mut() {
                    None => panic!("Empty stack"),
                    Some(&mut Value::Dict(ref mut dict)) => dict.push((key, value)),
                    _ => panic!("Invalid value on stack"),
                }
            },
            OpCode::SetItems => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        let mut values = self.split_off(marker);

                        match self.stack.last_mut() {
                            None => panic!("Empty stack"),
                            Some(&mut Value::Dict(ref mut dict)) => {
                                for i in 0 .. values.len() / 2 { // TODO: Check panic
                                    let key = values.remove(2 * i);
                                    let value = values.remove(2 * i + 1);
                                    dict.push((key, value));
                                }
                            },
                            _ => panic!("Invalid value on stack"),
                        }
                    }
                }
            },

            OpCode::Pop => {
                self.pop();
            },
            OpCode::Dup => {
                let value = match self.stack.last() {
                    None => panic!("Empty stack"),
                    Some(ref v) => (*v).clone(),
                };
                self.stack.push(value)
            },
            OpCode::Mark => {
                self.marker = Some(self.stack.len())
            },
            OpCode::PopMark => {
                match self.marker {
                    None => panic!("Empty marker"),
                    Some(marker) => {
                        self.split_off(marker);
                    }
                }
            },

            _ => panic!("Not implemented")
        }
    }
}<|endoftext|>


<commit_before>use std::error::Error;
use std::io::Write;
use std::io::stderr;

use ansi_term::Colour::Red;

/// Print an Error type and its cause recursively
///
/// The error is printed with "Error NNNN :" as prefix, where "NNNN" is a number which increases
/// which each recursion into the errors cause. The error description is used to visualize what
/// failed and if there is a cause "-- caused by:" is appended, and the cause is printed on the next
/// line.
///
/// Example output:
///
/// ```ignore
/// Error    1 : Some error -- caused by:
/// Error    2 : Some other error -- caused by:
/// Error    3 : Yet another Error -- caused by:
/// ...
///
/// Error <NNNN> : <Error description>
/// ```
pub fn trace_error(e: &Error) {
    print_trace_maxdepth(count_error_causes(e), e, ::std::u64::MAX);
    write!(stderr(), "\n").ok();
}

/// Convenience function: calls `trace_error()` with `e` and afterwards `std::process::exit()`
/// with `code`
pub fn trace_error_exit(e: &Error, code: i32) {
    use std::process::exit;

    trace_error(e);
    exit(code);
}

/// Print an Error type and its cause recursively, but only `max` levels
///
/// Output is the same as for `trace_error()`, though there are only `max` levels printed.
pub fn trace_error_maxdepth(e: &Error, max: u64) {
    let n = count_error_causes(e);
    let msg = Red.blink().paint(format!("{}/{} Levels of errors will be printed\n",
                                        (if max > n { n } else { max }), n));
    write!(stderr(), "{}", msg).ok();
    print_trace_maxdepth(n, e, max);
    write!(stderr(), "").ok();
}

/// Print an Error type and its cause recursively with the debug!() macro
///
/// Output is the same as for `trace_error()`.
pub fn trace_error_dbg(e: &Error) {
    print_trace_dbg(0, e);
}

/// Helper function for `trace_error()` and `trace_error_maxdepth()`.
///
/// Returns the cause of the last processed error in the recursion, so `None` if all errors where
/// processed.
fn print_trace_maxdepth(idx: u64, e: &Error, max: u64) -> Option<&Error> {
    if e.cause().is_some() && idx > 0 {
        match print_trace_maxdepth(idx - 1, e.cause().unwrap(), max) {
            None    => write!(stderr(), "\n").ok(),
            Some(_) => write!(stderr(), " -- caused:\n").ok(),
        };
    } else {
        write!(stderr(), "\n").ok();
    }
    write!(stderr(), "{}: {}", Red.paint(format!("ERROR[{:>4}]", idx)), e.description()).ok();
    e.cause()
}

/// Count errors in `Error::cause()` recursively
fn count_error_causes(e: &Error) -> u64 {
    1 + if e.cause().is_some() { count_error_causes(e.cause().unwrap()) } else { 0 }
}

fn print_trace_dbg(idx: u64, e: &Error) {
    debug!("{}: {}", Red.blink().paint(format!("ERROR[{:>4}]", idx)), e.description());
    if e.cause().is_some() {
        print_trace_dbg(idx + 1, e.cause().unwrap());
    }
}

<commit_msg>Adding debug information in trace_error_exit()<commit_after>use std::error::Error;
use std::io::Write;
use std::io::stderr;

use ansi_term::Colour::Red;

/// Print an Error type and its cause recursively
///
/// The error is printed with "Error NNNN :" as prefix, where "NNNN" is a number which increases
/// which each recursion into the errors cause. The error description is used to visualize what
/// failed and if there is a cause "-- caused by:" is appended, and the cause is printed on the next
/// line.
///
/// Example output:
///
/// ```ignore
/// Error    1 : Some error -- caused by:
/// Error    2 : Some other error -- caused by:
/// Error    3 : Yet another Error -- caused by:
/// ...
///
/// Error <NNNN> : <Error description>
/// ```
pub fn trace_error(e: &Error) {
    print_trace_maxdepth(count_error_causes(e), e, ::std::u64::MAX);
    write!(stderr(), "\n").ok();
}

/// Convenience function: calls `trace_error()` with `e` and afterwards `std::process::exit()`
/// with `code`
pub fn trace_error_exit(e: &Error, code: i32) {
    use std::process::exit;

    debug!("Tracing error...");
    trace_error(e);
    debug!("Calling exit()");
    exit(code);
}

/// Print an Error type and its cause recursively, but only `max` levels
///
/// Output is the same as for `trace_error()`, though there are only `max` levels printed.
pub fn trace_error_maxdepth(e: &Error, max: u64) {
    let n = count_error_causes(e);
    let msg = Red.blink().paint(format!("{}/{} Levels of errors will be printed\n",
                                        (if max > n { n } else { max }), n));
    write!(stderr(), "{}", msg).ok();
    print_trace_maxdepth(n, e, max);
    write!(stderr(), "").ok();
}

/// Print an Error type and its cause recursively with the debug!() macro
///
/// Output is the same as for `trace_error()`.
pub fn trace_error_dbg(e: &Error) {
    print_trace_dbg(0, e);
}

/// Helper function for `trace_error()` and `trace_error_maxdepth()`.
///
/// Returns the cause of the last processed error in the recursion, so `None` if all errors where
/// processed.
fn print_trace_maxdepth(idx: u64, e: &Error, max: u64) -> Option<&Error> {
    if e.cause().is_some() && idx > 0 {
        match print_trace_maxdepth(idx - 1, e.cause().unwrap(), max) {
            None    => write!(stderr(), "\n").ok(),
            Some(_) => write!(stderr(), " -- caused:\n").ok(),
        };
    } else {
        write!(stderr(), "\n").ok();
    }
    write!(stderr(), "{}: {}", Red.paint(format!("ERROR[{:>4}]", idx)), e.description()).ok();
    e.cause()
}

/// Count errors in `Error::cause()` recursively
fn count_error_causes(e: &Error) -> u64 {
    1 + if e.cause().is_some() { count_error_causes(e.cause().unwrap()) } else { 0 }
}

fn print_trace_dbg(idx: u64, e: &Error) {
    debug!("{}: {}", Red.blink().paint(format!("ERROR[{:>4}]", idx)), e.description());
    if e.cause().is_some() {
        print_trace_dbg(idx + 1, e.cause().unwrap());
    }
}

<|endoftext|>


<commit_before>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, AtomicBool, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  if moves.is_empty() {
    *best_move = None;
    return field.score(player);
  }
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board);
        if cur_estimation > cur_alpha {
          cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board);
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  } else {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move);
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}

pub fn minimax_with_time(field: &mut Field, player: Player, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  let guard = thread::scoped(|| {
    thread::sleep_ms(time);
    should_stop.store(true, Ordering::Relaxed);
  });
  let enemy = player.next();
  let mut depth = 1;
  let mut best_move = None;
  let mut cur_best_move = None;
  let mut enemy_best_move = None;
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  while !should_stop.load(Ordering::Relaxed) {
    let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut cur_best_move);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    best_move = if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move) < estimation {
      cur_best_move
    } else {
      None
    };
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    depth += 1;
    trajectories_pruning = TrajectoriesPruning::inc_exists(field, player, depth, &mut empty_board, &trajectories_pruning);
  }
  drop(guard);
  best_move
}
<commit_msg>Time control improved.<commit_after>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, AtomicBool, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>, should_stop: &AtomicBool) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  if moves.is_empty() || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board);
        if cur_estimation > cur_alpha {
          if !should_stop.load(Ordering::Relaxed) {
            cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board);
          } else {
            debug!(target: MINIMAX_STR, "Time-out! Next estimation ma be approximated.");
          }
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  } else {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let should_stop = AtomicBool::new(false);
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move, &should_stop);
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}

pub fn minimax_with_time(field: &mut Field, player: Player, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  let guard = thread::scoped(|| {
    thread::sleep_ms(time);
    should_stop.store(true, Ordering::Relaxed);
  });
  let enemy = player.next();
  let mut depth = 1;
  let mut best_move = None;
  let mut cur_best_move = None;
  let mut enemy_best_move = None;
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  while !should_stop.load(Ordering::Relaxed) {
    let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut cur_best_move, &should_stop);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    best_move = if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation {
      cur_best_move
    } else {
      None
    };
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    depth += 1;
    trajectories_pruning = TrajectoriesPruning::inc_exists(field, player, depth, &mut empty_board, &trajectories_pruning);
  }
  drop(guard);
  best_move
}
<|endoftext|>


<commit_before>use std::fmt::{Display, Error, Formatter};
use std::str::FromStr;

#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct NodeId {
    pub name: String,
    pub addr: String
}

impl Display for NodeId {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        try!(fmt.write_fmt(format_args!("{}@{}", self.name, self.addr)));
        Ok(())
    }
}

impl FromStr for NodeId {
    type Err = String;

    fn from_str(s: &str) -> Result<NodeId, String> {
        let v: Vec<&str> = s.split("@").collect();
        if v.len() != 2 {
            return Err("Invalid Pid format - Must be of form 'name@addr'".to_string())
        }
        Ok(NodeId {
            name: v[0].to_string(),
            addr: v[1].to_string()
        })
    }
}
<commit_msg>Update node_id.rs<commit_after>use std::fmt::{Display, Error, Formatter};
use std::str::FromStr;

#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct NodeId {
    pub name: String,
    pub addr: String
}

impl Display for NodeId {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        try!(fmt.write_fmt(format_args!("{}@{}", self.name, self.addr)));
        Ok(())
    }
}

impl FromStr for NodeId {
    type Err = String;

    fn from_str(s: &str) -> Result<NodeId, String> {
        let v: Vec<&str> = s.split("@").collect();
        if v.len() != 2 {
            return Err("Invalid NodeId format - Must be of form 'name@addr'".to_string())
        }
        Ok(NodeId {
            name: v[0].to_string(),
            addr: v[1].to_string()
        })
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate natord;

use file::File;
use column::{Column, SizeFormat};
use column::Column::*;
use output::View;
use term::dimensions;

use std::ascii::AsciiExt;
use std::slice::Iter;
use std::fmt;

use self::Misfire::*;

/// The *Options* struct represents a parsed version of the user's
/// command-line options.
#[derive(PartialEq, Debug)]
pub struct Options {
    pub list_dirs: bool,
    pub path_strs: Vec<String>,
    reverse: bool,
    show_invisibles: bool,
    sort_field: SortField,
    pub view: View,
}

impl Options {

    /// Call getopts on the given slice of command-line strings.
    pub fn getopts(args: &[String]) -> Result<Options, Misfire> {
        let opts = &[
            getopts::optflag("1", "oneline",   "display one entry per line"),
            getopts::optflag("a", "all",       "show dot-files"),
            getopts::optflag("b", "binary",    "use binary prefixes in file sizes"),
            getopts::optflag("B", "bytes",     "list file sizes in bytes, without prefixes"),
            getopts::optflag("d", "list-dirs", "list directories as regular files"),
            getopts::optflag("g", "group",     "show group as well as user"),
            getopts::optflag("h", "header",    "show a header row at the top"),
            getopts::optflag("H", "links",     "show number of hard links"),
            getopts::optflag("l", "long",      "display extended details and attributes"),
            getopts::optflag("i", "inode",     "show each file's inode number"),
            getopts::optflag("r", "reverse",   "reverse order of files"),
            getopts::optopt ("s", "sort",      "field to sort by", "WORD"),
            getopts::optflag("S", "blocks",    "show number of file system blocks"),
            getopts::optflag("x", "across",    "sort multi-column view entries across"),
            getopts::optflag("?", "help",      "show list of command-line options"),
        ];

        let matches = match getopts::getopts(args, opts) {
            Ok(m) => m,
            Err(e) => return Err(Misfire::InvalidOptions(e)),
        };

        if matches.opt_present("help") {
            return Err(Misfire::Help(getopts::usage("Usage:\n  exa [options] [files...]", opts)));
        }

        let sort_field = match matches.opt_str("sort") {
            Some(word) => try!(SortField::from_word(word)),
            None => SortField::Name,
        };

        Ok(Options {
            list_dirs:       matches.opt_present("list-dirs"),
            path_strs:       if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
            reverse:         matches.opt_present("reverse"),
            show_invisibles: matches.opt_present("all"),
            sort_field:      sort_field,
            view:            try!(view(&matches)),
        })
    }

    pub fn path_strings(&self) -> Iter<String> {
        self.path_strs.iter()
    }

    /// Transform the files somehow before listing them.
    pub fn transform_files<'a>(&self, unordered_files: Vec<File<'a>>) -> Vec<File<'a>> {
        let mut files: Vec<File<'a>> = unordered_files.into_iter()
            .filter(|f| self.should_display(f))
            .collect();

        match self.sort_field {
            SortField::Unsorted => {},
            SortField::Name => files.sort_by(|a, b| natord::compare(a.name.as_slice(), b.name.as_slice())),
            SortField::Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)),
            SortField::FileInode => files.sort_by(|a, b| a.stat.unstable.inode.cmp(&b.stat.unstable.inode)),
            SortField::Extension => files.sort_by(|a, b| {
                let exts  = a.ext.clone().map(|e| e.to_ascii_lowercase()).cmp(&b.ext.clone().map(|e| e.to_ascii_lowercase()));
                let names = a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase());
                exts.cmp(&names)
            }),
        }

        if self.reverse {
            files.reverse();
        }

        files
    }

    fn should_display(&self, f: &File) -> bool {
        if self.show_invisibles {
            true
        }
        else {
            !f.name.as_slice().starts_with(".")
        }
    }
}

/// User-supplied field to sort by
#[derive(PartialEq, Debug)]
pub enum SortField {
    Unsorted, Name, Extension, Size, FileInode
}

impl Copy for SortField { }

impl SortField {

    /// Find which field to use based on a user-supplied word.
    fn from_word(word: String) -> Result<SortField, Misfire> {
        match word.as_slice() {
            "name"  => Ok(SortField::Name),
            "size"  => Ok(SortField::Size),
            "ext"   => Ok(SortField::Extension),
            "none"  => Ok(SortField::Unsorted),
            "inode" => Ok(SortField::FileInode),
            field   => Err(SortField::none(field))
        }
    }

    /// How to display an error when the word didn't match with anything.
    fn none(field: &str) -> Misfire {
        Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--sort {}", field)))
    }
}

/// One of these things could happen instead of listing files.
#[derive(PartialEq, Debug)]
pub enum Misfire {

    /// The getopts crate didn't like these arguments.
    InvalidOptions(getopts::Fail),

    /// The user asked for help. This isn't strictly an error, which is why
    /// this enum isn't named Error!
    Help(String),

    /// Two options were given that conflict with one another
    Conflict(&'static str, &'static str),

    /// An option was given that does nothing when another one either is or
    /// isn't present.
    Useless(&'static str, bool, &'static str),
}

impl Misfire {
    /// The OS return code this misfire should signify.
    pub fn error_code(&self) -> isize {
        if let Help(_) = *self { 2 }
                          else { 3 }
    }
}

impl fmt::Display for Misfire {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidOptions(ref e) => write!(f, "{}", e),
            Help(ref text)        => write!(f, "{}", text),
            Conflict(a, b)        => write!(f, "Option --{} conflicts with option {}", a, b),
            Useless(a, false, b)  => write!(f, "Option --{} is useless without option --{}", a, b),
            Useless(a, true, b)   => write!(f, "Option --{} is useless given option --{}", a, b),
        }
    }
}

/// Turns the Getopts results object into a View object.
fn view(matches: &getopts::Matches) -> Result<View, Misfire> {
    if matches.opt_present("long") {
        if matches.opt_present("across") {
            Err(Misfire::Useless("across", true, "long"))
        }
        else if matches.opt_present("oneline") {
            Err(Misfire::Useless("across", true, "long"))
        }
        else {
            Ok(View::Details(try!(columns(matches)), matches.opt_present("header")))
        }
    }
    else if matches.opt_present("binary") {
        Err(Misfire::Useless("binary", false, "long"))
    }
    else if matches.opt_present("bytes") {
        Err(Misfire::Useless("bytes", false, "long"))
    }
    else if matches.opt_present("oneline") {
        if matches.opt_present("across") {
            Err(Misfire::Useless("across", true, "oneline"))
        }
        else {
            Ok(View::Lines)
        }
    }
    else {
        match dimensions() {
            None => Ok(View::Lines),
            Some((width, _)) => Ok(View::Grid(matches.opt_present("across"), width)),
        }
    }
}

/// Finds out which file size the user has asked for.
fn file_size(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
    let binary = matches.opt_present("binary");
    let bytes = matches.opt_present("bytes");

    match (binary, bytes) {
        (true,  true ) => Err(Misfire::Conflict("binary", "bytes")),
        (true,  false) => Ok(SizeFormat::BinaryBytes),
        (false, true ) => Ok(SizeFormat::JustBytes),
        (false, false) => Ok(SizeFormat::DecimalBytes),
    }
}

/// Turns the Getopts results object into a list of columns for the columns
/// view, depending on the passed-in command-line arguments.
fn columns(matches: &getopts::Matches) -> Result<Vec<Column>, Misfire> {
    let mut columns = vec![];

    if matches.opt_present("inode") {
        columns.push(Inode);
    }

    columns.push(Permissions);

    if matches.opt_present("links") {
        columns.push(HardLinks);
    }

    // Fail early here if two file size flags are given
    columns.push(FileSize(try!(file_size(matches))));

    if matches.opt_present("blocks") {
        columns.push(Blocks);
    }

    columns.push(User);

    if matches.opt_present("group") {
        columns.push(Group);
    }

    columns.push(FileName);
    Ok(columns)
}

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

    use std::fmt;

    fn is_helpful(misfire: Result<Options, Misfire>) -> bool {
        match misfire {
            Err(Help(_)) => true,
            _            => false,
        }
    }

    impl fmt::Display for Options {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{:?}", self)
        }
    }

    #[test]
    fn help() {
        let opts = Options::getopts(&[ "--help".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn help_with_file() {
        let opts = Options::getopts(&[ "--help".to_string(), "me".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn files() {
        let opts = Options::getopts(&[ "this file".to_string(), "that file".to_string() ]);
        assert_eq!(opts.unwrap().path_strs, vec![ "this file".to_string(), "that file".to_string() ])
    }

    #[test]
    fn no_args() {
        let opts = Options::getopts(&[]);
        assert_eq!(opts.unwrap().path_strs, vec![ ".".to_string() ])
    }

    #[test]
    fn file_sizes() {
        let opts = Options::getopts(&[ "--long".to_string(), "--binary".to_string(), "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Conflict("binary", "bytes"))
    }

    #[test]
    fn just_binary() {
        let opts = Options::getopts(&[ "--binary".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("binary", false, "long"))
    }

    #[test]
    fn just_bytes() {
        let opts = Options::getopts(&[ "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("bytes", false, "long"))
    }

    #[test]
    fn long_across() {
        let opts = Options::getopts(&[ "--long".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "long"))
    }

    #[test]
    fn oneline_across() {
        let opts = Options::getopts(&[ "--oneline".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "oneline"))
    }
}
<commit_msg>Actually use the is_dotfile method<commit_after>extern crate getopts;
extern crate natord;

use file::File;
use column::{Column, SizeFormat};
use column::Column::*;
use output::View;
use term::dimensions;

use std::ascii::AsciiExt;
use std::slice::Iter;
use std::fmt;

use self::Misfire::*;

/// The *Options* struct represents a parsed version of the user's
/// command-line options.
#[derive(PartialEq, Debug)]
pub struct Options {
    pub list_dirs: bool,
    pub path_strs: Vec<String>,
    reverse: bool,
    show_invisibles: bool,
    sort_field: SortField,
    pub view: View,
}

impl Options {

    /// Call getopts on the given slice of command-line strings.
    pub fn getopts(args: &[String]) -> Result<Options, Misfire> {
        let opts = &[
            getopts::optflag("1", "oneline",   "display one entry per line"),
            getopts::optflag("a", "all",       "show dot-files"),
            getopts::optflag("b", "binary",    "use binary prefixes in file sizes"),
            getopts::optflag("B", "bytes",     "list file sizes in bytes, without prefixes"),
            getopts::optflag("d", "list-dirs", "list directories as regular files"),
            getopts::optflag("g", "group",     "show group as well as user"),
            getopts::optflag("h", "header",    "show a header row at the top"),
            getopts::optflag("H", "links",     "show number of hard links"),
            getopts::optflag("l", "long",      "display extended details and attributes"),
            getopts::optflag("i", "inode",     "show each file's inode number"),
            getopts::optflag("r", "reverse",   "reverse order of files"),
            getopts::optopt ("s", "sort",      "field to sort by", "WORD"),
            getopts::optflag("S", "blocks",    "show number of file system blocks"),
            getopts::optflag("x", "across",    "sort multi-column view entries across"),
            getopts::optflag("?", "help",      "show list of command-line options"),
        ];

        let matches = match getopts::getopts(args, opts) {
            Ok(m) => m,
            Err(e) => return Err(Misfire::InvalidOptions(e)),
        };

        if matches.opt_present("help") {
            return Err(Misfire::Help(getopts::usage("Usage:\n  exa [options] [files...]", opts)));
        }

        let sort_field = match matches.opt_str("sort") {
            Some(word) => try!(SortField::from_word(word)),
            None => SortField::Name,
        };

        Ok(Options {
            list_dirs:       matches.opt_present("list-dirs"),
            path_strs:       if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
            reverse:         matches.opt_present("reverse"),
            show_invisibles: matches.opt_present("all"),
            sort_field:      sort_field,
            view:            try!(view(&matches)),
        })
    }

    pub fn path_strings(&self) -> Iter<String> {
        self.path_strs.iter()
    }

    /// Transform the files somehow before listing them.
    pub fn transform_files<'a>(&self, mut files: Vec<File<'a>>) -> Vec<File<'a>> {

        if !self.show_invisibles {
            files = files.into_iter().filter(|f| !f.is_dotfile()).collect();
        }

        match self.sort_field {
            SortField::Unsorted => {},
            SortField::Name => files.sort_by(|a, b| natord::compare(a.name.as_slice(), b.name.as_slice())),
            SortField::Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)),
            SortField::FileInode => files.sort_by(|a, b| a.stat.unstable.inode.cmp(&b.stat.unstable.inode)),
            SortField::Extension => files.sort_by(|a, b| {
                let exts  = a.ext.clone().map(|e| e.to_ascii_lowercase()).cmp(&b.ext.clone().map(|e| e.to_ascii_lowercase()));
                let names = a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase());
                exts.cmp(&names)
            }),
        }

        if self.reverse {
            files.reverse();
        }

        files
    }
}

/// User-supplied field to sort by
#[derive(PartialEq, Debug)]
pub enum SortField {
    Unsorted, Name, Extension, Size, FileInode
}

impl Copy for SortField { }

impl SortField {

    /// Find which field to use based on a user-supplied word.
    fn from_word(word: String) -> Result<SortField, Misfire> {
        match word.as_slice() {
            "name"  => Ok(SortField::Name),
            "size"  => Ok(SortField::Size),
            "ext"   => Ok(SortField::Extension),
            "none"  => Ok(SortField::Unsorted),
            "inode" => Ok(SortField::FileInode),
            field   => Err(SortField::none(field))
        }
    }

    /// How to display an error when the word didn't match with anything.
    fn none(field: &str) -> Misfire {
        Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--sort {}", field)))
    }
}

/// One of these things could happen instead of listing files.
#[derive(PartialEq, Debug)]
pub enum Misfire {

    /// The getopts crate didn't like these arguments.
    InvalidOptions(getopts::Fail),

    /// The user asked for help. This isn't strictly an error, which is why
    /// this enum isn't named Error!
    Help(String),

    /// Two options were given that conflict with one another
    Conflict(&'static str, &'static str),

    /// An option was given that does nothing when another one either is or
    /// isn't present.
    Useless(&'static str, bool, &'static str),
}

impl Misfire {
    /// The OS return code this misfire should signify.
    pub fn error_code(&self) -> isize {
        if let Help(_) = *self { 2 }
                          else { 3 }
    }
}

impl fmt::Display for Misfire {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidOptions(ref e) => write!(f, "{}", e),
            Help(ref text)        => write!(f, "{}", text),
            Conflict(a, b)        => write!(f, "Option --{} conflicts with option {}", a, b),
            Useless(a, false, b)  => write!(f, "Option --{} is useless without option --{}", a, b),
            Useless(a, true, b)   => write!(f, "Option --{} is useless given option --{}", a, b),
        }
    }
}

/// Turns the Getopts results object into a View object.
fn view(matches: &getopts::Matches) -> Result<View, Misfire> {
    if matches.opt_present("long") {
        if matches.opt_present("across") {
            Err(Misfire::Useless("across", true, "long"))
        }
        else if matches.opt_present("oneline") {
            Err(Misfire::Useless("across", true, "long"))
        }
        else {
            Ok(View::Details(try!(columns(matches)), matches.opt_present("header")))
        }
    }
    else if matches.opt_present("binary") {
        Err(Misfire::Useless("binary", false, "long"))
    }
    else if matches.opt_present("bytes") {
        Err(Misfire::Useless("bytes", false, "long"))
    }
    else if matches.opt_present("oneline") {
        if matches.opt_present("across") {
            Err(Misfire::Useless("across", true, "oneline"))
        }
        else {
            Ok(View::Lines)
        }
    }
    else {
        match dimensions() {
            None => Ok(View::Lines),
            Some((width, _)) => Ok(View::Grid(matches.opt_present("across"), width)),
        }
    }
}

/// Finds out which file size the user has asked for.
fn file_size(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
    let binary = matches.opt_present("binary");
    let bytes = matches.opt_present("bytes");

    match (binary, bytes) {
        (true,  true ) => Err(Misfire::Conflict("binary", "bytes")),
        (true,  false) => Ok(SizeFormat::BinaryBytes),
        (false, true ) => Ok(SizeFormat::JustBytes),
        (false, false) => Ok(SizeFormat::DecimalBytes),
    }
}

/// Turns the Getopts results object into a list of columns for the columns
/// view, depending on the passed-in command-line arguments.
fn columns(matches: &getopts::Matches) -> Result<Vec<Column>, Misfire> {
    let mut columns = vec![];

    if matches.opt_present("inode") {
        columns.push(Inode);
    }

    columns.push(Permissions);

    if matches.opt_present("links") {
        columns.push(HardLinks);
    }

    // Fail early here if two file size flags are given
    columns.push(FileSize(try!(file_size(matches))));

    if matches.opt_present("blocks") {
        columns.push(Blocks);
    }

    columns.push(User);

    if matches.opt_present("group") {
        columns.push(Group);
    }

    columns.push(FileName);
    Ok(columns)
}

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

    use std::fmt;

    fn is_helpful(misfire: Result<Options, Misfire>) -> bool {
        match misfire {
            Err(Help(_)) => true,
            _            => false,
        }
    }

    impl fmt::Display for Options {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{:?}", self)
        }
    }

    #[test]
    fn help() {
        let opts = Options::getopts(&[ "--help".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn help_with_file() {
        let opts = Options::getopts(&[ "--help".to_string(), "me".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn files() {
        let opts = Options::getopts(&[ "this file".to_string(), "that file".to_string() ]);
        assert_eq!(opts.unwrap().path_strs, vec![ "this file".to_string(), "that file".to_string() ])
    }

    #[test]
    fn no_args() {
        let opts = Options::getopts(&[]);
        assert_eq!(opts.unwrap().path_strs, vec![ ".".to_string() ])
    }

    #[test]
    fn file_sizes() {
        let opts = Options::getopts(&[ "--long".to_string(), "--binary".to_string(), "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Conflict("binary", "bytes"))
    }

    #[test]
    fn just_binary() {
        let opts = Options::getopts(&[ "--binary".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("binary", false, "long"))
    }

    #[test]
    fn just_bytes() {
        let opts = Options::getopts(&[ "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("bytes", false, "long"))
    }

    #[test]
    fn long_across() {
        let opts = Options::getopts(&[ "--long".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "long"))
    }

    #[test]
    fn oneline_across() {
        let opts = Options::getopts(&[ "--oneline".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "oneline"))
    }
}
<|endoftext|>


<commit_before>//! Compiler plugin for Rust-PHF
//!
//! See the documentation for the `phf` crate for more details.
#[crate_id="github.com/sfackler/rust-phf/phf_mac"];
#[crate_type="dylib"];
#[doc(html_root_url="http://www.rust-ci.org/sfackler/rust-phf/doc")];
#[feature(managed_boxes, macro_registrar, quote)];

extern crate collections;
extern crate extra;
extern crate syntax;
extern crate time;
extern crate phf;

use collections::HashMap;
use std::rand;
use std::os;
use std::vec;
use syntax::ast;
use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF, FAT_ARROW};

static DEFAULT_LAMBDA: uint = 5;

#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("phf_map"),
             NormalTT(~BasicMacroExpander {
                expander: expand_mphf_map,
                span: None
             },
             None));
}

struct Entry {
    key_str: InternedString,
    key: @Expr,
    value: @Expr
}

fn expand_mphf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let mut entries = match parse_entries(cx, tts) {
        Some(entries) => entries,
        None => return MacResult::dummy_expr(sp)
    };

    entries.sort_by(|a, b| a.key_str.cmp(&b.key_str));
    if has_duplicates(cx, sp, entries) {
        return MacResult::dummy_expr(sp);
    }

    let start = time::precise_time_s();
    let state;
    loop {
        match generate_hash(entries) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = time::precise_time_s() - start;
    if os::getenv("PHF_STATS").is_some() {
        cx.span_note(sp, format!("PHF generation took {} seconds", time));
    }

    create_map(cx, sp, entries, state)
}

fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<~[Entry]> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.iter().map(|x| x.clone())
                                                    .collect());
    let mut entries = ~[];

    let mut bad = false;
    while parser.token != EOF {
        let key = cx.expand_expr(parser.parse_expr());

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(key.span, "expected string literal");
                        bad = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(key.span, "expected string literal");
                bad = true;
                InternedString::new("")
            }
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_err(parser.span, "expected `=>`");
            return None;
        }

        let value = parser.parse_expr();

        entries.push(Entry {
            key_str: key_str,
            key: key,
            value: value
        });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if entries.len() > phf::MAX_SIZE {
        cx.span_err(parser.span,
                    format!("maps with more than {} entries are not supported",
                            phf::MAX_SIZE));
        return None;
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn has_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut in_dup = false;
    for window in entries.windows(2) {
        let ref a = window[0];
        let ref b = window[1];
        if a.key_str == b.key_str {
            dups = true;
            if !in_dup {
                cx.span_err(sp, format!("duplicate key \"{}\"", a.key_str));
                cx.span_note(a.key.span, "one occurrence here");
                in_dup = true;
            }
            cx.span_note(b.key.span, "one occurrence here");
        } else {
            in_dup = false;
        }
    }

    dups
}

struct HashState {
    k1: u64,
    k2: u64,
    disps: ~[(uint, uint)],
    map: ~[Option<uint>],
}

fn generate_hash(entries: &[Entry]) -> Option<HashState> {
    struct Bucket {
        idx: uint,
        keys: ~[uint],
    }

    struct Hashes {
        g: uint,
        f1: uint,
        f2: uint,
    }

    let k1 = rand::random();
    let k2 = rand::random();

    let hashes = entries.iter().map(|entry| {
            let (g, f1, f2) = phf::hash(entry.key_str.get(), k1, k2);
            Hashes {
                g: g,
                f1: f1,
                f2: f2
            }
        }).to_owned_vec();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = vec::from_fn(buckets_len,
                                   |i| Bucket { idx: i, keys: ~[] });

    for (i, hash) in hashes.iter().enumerate() {
        buckets[hash.g % buckets_len].keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| b.keys.len().cmp(&a.keys.len()));

    let table_len = entries.len();
    let mut map = vec::from_elem(table_len, None);
    let mut disps = vec::from_elem(buckets_len, None);
    let mut try_map = HashMap::new();
    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len) {
            'disps_l: for d2 in range(0, table_len) {
                try_map.clear();
                for &key in bucket.keys.iter() {
                    let idx = phf::displace(hashes[key].f1, hashes[key].f2,
                                            d1, d2) % table_len;
                    if try_map.find(&idx).is_some() || map[idx].is_some() {
                        continue 'disps_l;
                    }
                    try_map.insert(idx, key);
                }

                // We've picked a good set of disps
                disps[bucket.idx] = Some((d1, d2));
                for (&idx, &key) in try_map.iter() {
                    map[idx] = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    let disps = disps.move_iter().map(|i| i.expect("should have a bucket"))
            .collect();

    Some(HashState {
        k1: k1,
        k2: k2,
        disps: disps,
        map: map,
    })
}

fn create_map(cx: &mut ExtCtxt, sp: Span, entries: ~[Entry], state: HashState)
              -> MacResult {
    let len = entries.len();
    let k1 = state.k1;
    let k2 = state.k2;
    let disps = state.disps.iter().map(|&(d1, d2)| {
            quote_expr!(&*cx, ($d1, $d2))
        }).collect();
    let disps = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(disps, MutImmutable),
        span: sp,
    };
    let entries = state.map.iter().map(|&idx| {
            match idx {
                Some(idx) => {
                    let Entry { key, value, .. } = entries[idx];
                    quote_expr!(&*cx, Some(($key, $value)))
                }
                None => quote_expr!(&*cx, None),
            }
        }).collect();
    let entries = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(entries, MutImmutable),
        span: sp,
    };

    MRExpr(quote_expr!(cx, PhfMap {
        len: $len,
        k1: $k1,
        k2: $k2,
        disps: &'static $disps,
        entries: &'static $entries,
    }))
}
<commit_msg>Update for rand split<commit_after>//! Compiler plugin for Rust-PHF
//!
//! See the documentation for the `phf` crate for more details.
#[crate_id="github.com/sfackler/rust-phf/phf_mac"];
#[crate_type="dylib"];
#[doc(html_root_url="http://www.rust-ci.org/sfackler/rust-phf/doc")];
#[feature(managed_boxes, macro_registrar, quote)];

extern crate collections;
extern crate extra;
extern crate rand;
extern crate syntax;
extern crate time;
extern crate phf;

use collections::HashMap;
use std::os;
use std::vec;
use syntax::ast;
use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF, FAT_ARROW};

static DEFAULT_LAMBDA: uint = 5;

#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("phf_map"),
             NormalTT(~BasicMacroExpander {
                expander: expand_mphf_map,
                span: None
             },
             None));
}

struct Entry {
    key_str: InternedString,
    key: @Expr,
    value: @Expr
}

fn expand_mphf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let mut entries = match parse_entries(cx, tts) {
        Some(entries) => entries,
        None => return MacResult::dummy_expr(sp)
    };

    entries.sort_by(|a, b| a.key_str.cmp(&b.key_str));
    if has_duplicates(cx, sp, entries) {
        return MacResult::dummy_expr(sp);
    }

    let start = time::precise_time_s();
    let state;
    loop {
        match generate_hash(entries) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = time::precise_time_s() - start;
    if os::getenv("PHF_STATS").is_some() {
        cx.span_note(sp, format!("PHF generation took {} seconds", time));
    }

    create_map(cx, sp, entries, state)
}

fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<~[Entry]> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.iter().map(|x| x.clone())
                                                    .collect());
    let mut entries = ~[];

    let mut bad = false;
    while parser.token != EOF {
        let key = cx.expand_expr(parser.parse_expr());

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(key.span, "expected string literal");
                        bad = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(key.span, "expected string literal");
                bad = true;
                InternedString::new("")
            }
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_err(parser.span, "expected `=>`");
            return None;
        }

        let value = parser.parse_expr();

        entries.push(Entry {
            key_str: key_str,
            key: key,
            value: value
        });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if entries.len() > phf::MAX_SIZE {
        cx.span_err(parser.span,
                    format!("maps with more than {} entries are not supported",
                            phf::MAX_SIZE));
        return None;
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn has_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut in_dup = false;
    for window in entries.windows(2) {
        let ref a = window[0];
        let ref b = window[1];
        if a.key_str == b.key_str {
            dups = true;
            if !in_dup {
                cx.span_err(sp, format!("duplicate key \"{}\"", a.key_str));
                cx.span_note(a.key.span, "one occurrence here");
                in_dup = true;
            }
            cx.span_note(b.key.span, "one occurrence here");
        } else {
            in_dup = false;
        }
    }

    dups
}

struct HashState {
    k1: u64,
    k2: u64,
    disps: ~[(uint, uint)],
    map: ~[Option<uint>],
}

fn generate_hash(entries: &[Entry]) -> Option<HashState> {
    struct Bucket {
        idx: uint,
        keys: ~[uint],
    }

    struct Hashes {
        g: uint,
        f1: uint,
        f2: uint,
    }

    let k1 = rand::random();
    let k2 = rand::random();

    let hashes = entries.iter().map(|entry| {
            let (g, f1, f2) = phf::hash(entry.key_str.get(), k1, k2);
            Hashes {
                g: g,
                f1: f1,
                f2: f2
            }
        }).to_owned_vec();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = vec::from_fn(buckets_len,
                                   |i| Bucket { idx: i, keys: ~[] });

    for (i, hash) in hashes.iter().enumerate() {
        buckets[hash.g % buckets_len].keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| b.keys.len().cmp(&a.keys.len()));

    let table_len = entries.len();
    let mut map = vec::from_elem(table_len, None);
    let mut disps = vec::from_elem(buckets_len, None);
    let mut try_map = HashMap::new();
    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len) {
            'disps_l: for d2 in range(0, table_len) {
                try_map.clear();
                for &key in bucket.keys.iter() {
                    let idx = phf::displace(hashes[key].f1, hashes[key].f2,
                                            d1, d2) % table_len;
                    if try_map.find(&idx).is_some() || map[idx].is_some() {
                        continue 'disps_l;
                    }
                    try_map.insert(idx, key);
                }

                // We've picked a good set of disps
                disps[bucket.idx] = Some((d1, d2));
                for (&idx, &key) in try_map.iter() {
                    map[idx] = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    let disps = disps.move_iter().map(|i| i.expect("should have a bucket"))
            .collect();

    Some(HashState {
        k1: k1,
        k2: k2,
        disps: disps,
        map: map,
    })
}

fn create_map(cx: &mut ExtCtxt, sp: Span, entries: ~[Entry], state: HashState)
              -> MacResult {
    let len = entries.len();
    let k1 = state.k1;
    let k2 = state.k2;
    let disps = state.disps.iter().map(|&(d1, d2)| {
            quote_expr!(&*cx, ($d1, $d2))
        }).collect();
    let disps = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(disps, MutImmutable),
        span: sp,
    };
    let entries = state.map.iter().map(|&idx| {
            match idx {
                Some(idx) => {
                    let Entry { key, value, .. } = entries[idx];
                    quote_expr!(&*cx, Some(($key, $value)))
                }
                None => quote_expr!(&*cx, None),
            }
        }).collect();
    let entries = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(entries, MutImmutable),
        span: sp,
    };

    MRExpr(quote_expr!(cx, PhfMap {
        len: $len,
        k1: $k1,
        k2: $k2,
        disps: &'static $disps,
        entries: &'static $entries,
    }))
}
<|endoftext|>