<commit_before>//! A compiler from an LR(1) table to a [recursive ascent] parser.
//!
//! [recursive ascent]: https://en.wikipedia.org/wiki/Recursive_ascent_parser

use intern::{InternedString};
use grammar::repr::{Grammar, NonterminalString, Symbol, TerminalString, Types};
use lr1::{Lookahead, State, StateIndex};
use rust::RustWrite;
use std::io::{self, Write};
use util::Sep;

pub type Path = Vec<InternedString>;

pub fn compile<'grammar,W:Write>(
    grammar: &'grammar Grammar,
    user_start_symbol: NonterminalString,
    start_symbol: NonterminalString,
    states: &[State<'grammar>],
    out: &mut RustWrite<W>)
    -> io::Result<()>
{
    let mut ascent = RecursiveAscent::new(grammar, user_start_symbol, start_symbol, states, out);
    ascent.write()
}

struct RecursiveAscent<'ascent,'grammar:'ascent,W:Write+'ascent> {
    grammar: &'grammar Grammar,
    prefix: &'grammar str,
    types: &'grammar Types,
    user_start_symbol: NonterminalString,
    start_symbol: NonterminalString,
    states: &'ascent [State<'grammar>],
    state_prefixes: Vec<&'grammar [Symbol]>,
    out: &'ascent mut RustWrite<W>,
}

impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
    fn new(grammar: &'grammar Grammar,
           user_start_symbol: NonterminalString,
           start_symbol: NonterminalString,
           states: &'ascent [State<'grammar>],
           out: &'ascent mut RustWrite<W>)
           -> RecursiveAscent<'ascent,'grammar,W>
    {
        RecursiveAscent {
            grammar: grammar,
            prefix: &grammar.prefix,
            types: &grammar.types,
            states: states,
            state_prefixes: states.iter().map(|s| s.prefix()).collect(),
            user_start_symbol: user_start_symbol,
            start_symbol: start_symbol,
            out: out,
        }
    }

    fn write(&mut self) -> io::Result<()> {
        try!(self.write_start_fn());

        rust!(self.out, "");
        rust!(self.out, "mod {}parse{} {{",
              self.prefix, self.start_symbol);

        rust!(self.out, "#![allow(non_snake_case, unused_mut, unused_variables)]");
        rust!(self.out, "");

        try!(self.write_uses());

        rust!(self.out, "");
        try!(self.write_return_type_defn());

        for i in 0..self.states.len() {
            rust!(self.out, "");
            try!(self.write_state_fn(StateIndex(i)));
        }

        rust!(self.out, "}}");

        Ok(())
    }

    fn write_uses(&mut self) -> io::Result<()> {
        for u in &self.grammar.uses {
            rust!(self.out, "use {};", u);
        }
        Ok(())
    }

    fn write_return_type_defn(&mut self) -> io::Result<()> {
        rust!(self.out, "pub enum {}Nonterminal {{", self.prefix);

        // make an enum with one variant per nonterminal; I considered
        // making different enums per state, but this would mean we
        // have to unwrap and rewrap as we pass up the stack, which
        // seems silly
        for &nt in self.grammar.productions.keys() {
            rust!(self.out, "{}({}),", nt, self.types.nonterminal_type(nt));
        }

        rust!(self.out, "}}");
        Ok(())
    }

    fn write_start_fn(&mut self) -> io::Result<()> {
        let terminal_type = self.types.terminal_enum_type();
        rust!(self.out, "#[allow(non_snake_case)]");
        rust!(self.out, "pub fn parse_{}<", self.user_start_symbol);
        rust!(self.out, "  {}TOKENS: IntoIterator<Item={}>,", self.prefix, terminal_type);
        rust!(self.out, ">(");
        rust!(self.out, "{}tokens: {}TOKENS,", self.prefix, self.prefix);
        rust!(self.out, ") -> Result<(Option<{}>, {}), Option<{}>> {{",
              terminal_type, self.types.nonterminal_type(self.start_symbol), terminal_type);
        rust!(self.out, "let mut {}tokens = {}tokens.into_iter();", self.prefix, self.prefix);
        rust!(self.out, "let {}lookahead = {}tokens.next();", self.prefix, self.prefix);
        rust!(self.out, "match try!({}parse{}::{}state0({}lookahead, &mut {}tokens)) {{",
              self.prefix, self.start_symbol, self.prefix, self.prefix, self.prefix);
        rust!(self.out, "({}lookahead, {}parse{}::{}Nonterminal::{}({}nt)) => \
                         Ok(({}lookahead, {}nt)),",
              self.prefix, self.prefix, self.start_symbol, self.prefix, self.start_symbol,
              self.prefix, self.prefix, self.prefix);
        rust!(self.out, "_ => unreachable!(),");
        rust!(self.out, "}}");
        rust!(self.out, "}}");

        Ok(())
    }

    fn write_state_fn(&mut self, this_index: StateIndex) -> io::Result<()> {
        let this_state = &self.states[this_index.0];
        let this_prefix = self.state_prefixes[this_index.0];
        let terminal_type = self.types.terminal_enum_type();

        // Leave a comment explaining what this state is.
        rust!(self.out, "// State {}", this_index.0);
        for item in this_state.items.vec.iter() {
            rust!(self.out, "//   {:?}", item);
        }
        rust!(self.out, "//");
        for (token, action) in &this_state.tokens {
            rust!(self.out, "//   {:?} -> {:?}", token, action);
        }
        rust!(self.out, "//");
        for (nt, state) in &this_state.gotos {
            rust!(self.out, "//   {:?} -> {:?}", nt, state);
        }

        // set to true if goto actions are worth generating
        let mut fallthrough = false;

        rust!(self.out, "pub fn {}state{}<", self.prefix, this_index.0);
        rust!(self.out, "  {}TOKENS: Iterator<Item={}>,", self.prefix, terminal_type);
        rust!(self.out, ">(");
        rust!(self.out, "mut {}lookahead: Option<{}>,", self.prefix, terminal_type);
        rust!(self.out, "{}tokens: &mut {}TOKENS,", self.prefix, self.prefix);
        for i in 0..this_prefix.len() {
            rust!(self.out, "{}sym{}: &mut Option<{}>,",
                  self.prefix, i, this_prefix[i].ty(&self.types));
        }
        rust!(self.out, ") -> Result<(Option<{}>, {}Nonterminal), Option<{}>> {{",
              terminal_type, self.prefix, terminal_type);

        rust!(self.out, "let mut {}result: (Option<{}>, {}Nonterminal);",
              self.prefix, terminal_type, self.prefix);

        rust!(self.out, "match {}lookahead {{", self.prefix);

        // first emit shifts:
        for (token, next_index) in
            this_state.tokens.iter()
                             .filter_map(|(token, action)| action.shift().map(|n| (token, n)))
        {
            match *token {
                Lookahead::Terminal(s) => {
                    let sym_name = format!("{}sym{}", self.prefix, this_prefix.len());
                    try!(self.consume_terminal(s, sym_name))
                }
                Lookahead::EOF =>
                    unreachable!("should never have to shift EOF")
            }

            // "shift" the lookahead onto the "stack" by taking its address
            rust!(self.out, "let {}lookahead = {}tokens.next();",
                  self.prefix, self.prefix);

            // transition to the new state
            let transition =
                self.transition(this_prefix, next_index, "lookahead", "tokens");
            rust!(self.out, "{}result = {};", self.prefix, transition);

            rust!(self.out, "}}");
            fallthrough = true;
        }

        // now emit reduces:
        for (token, production) in
            this_state.tokens.iter()
                             .filter_map(|(token, action)| action.reduce().map(|p| (token, p)))
        {
            match *token {
                Lookahead::Terminal(s) =>
                    try!(self.match_terminal(s)),
                Lookahead::EOF =>
                    rust!(self.out, "None => {{"),
            }

            let n = this_prefix.len(); // number of symbols we have on the stack
            let m = production.symbols.len(); // number action code wants
            assert!(n >= m);
            let transfer_syms = self.pop_syms(n, m);

            // "pop" the items off the stack
            for sym in &transfer_syms {
                rust!(self.out, "let {} = {}.take().unwrap();", sym, sym);
            }

            // invoke the action code
            rust!(self.out, "let {}nt = super::{}action{}({});",
                  self.prefix,
                  self.prefix,
                  production.action_fn.index(),
                  Sep(", ", &transfer_syms));

            // wrap up the result along with the (unused) lookahead
            if !transfer_syms.is_empty() {
                // if we popped anything off of the stack, then this frame is done
                rust!(self.out, "return Ok(({}lookahead, {}Nonterminal::{}({}nt)));",
                      self.prefix, self.prefix, production.nonterminal, self.prefix);
            } else {
                // otherwise, pop back
                rust!(self.out, "result = ({}lookahead, {}Nonterminal::{}({}nt));",
                      self.prefix, self.prefix, production.nonterminal, self.prefix);
                fallthrough = true;
            }

            rust!(self.out, "}}");
        }

        // if we hit this, the next token is not recognized, so generate an error
        rust!(self.out, "_ => {{");
        rust!(self.out, "return Err({}lookahead);", self.prefix);
        rust!(self.out, "}}");

        rust!(self.out, "}}"); // match

        // finally, emit gotos (if relevant)
        if fallthrough && !this_state.gotos.is_empty() {
            if this_prefix.len() > 0 {
                rust!(self.out, "while {}sym{}.is_some() {{", self.prefix, this_prefix.len() - 1);
            } else {
                rust!(self.out, "loop {{");
            }

            rust!(self.out, "let ({}lookahead, {}nt) = {}result;",
                  self.prefix, self.prefix, self.prefix);

            rust!(self.out, "match {}nt {{", self.prefix);
            for (&nt, &next_index) in &this_state.gotos {
                rust!(self.out, "{}Nonterminal::{}({}nt) => {{", self.prefix, nt, self.prefix);
                rust!(self.out, "let {}sym{} = &mut Some({}nt);",
                      self.prefix, this_prefix.len(), self.prefix);
                let transition = self.transition(this_prefix, next_index, "lookahead", "tokens");
                rust!(self.out, "{}result = {};", self.prefix, transition);
                rust!(self.out, "}}");
            }

            // errors are not possible in the goto phase; a missing entry
            // indicates parse successfully completed, so just bail out
            if this_state.gotos.len() != self.grammar.productions.keys().len() {
                rust!(self.out, "_ => {{");
                rust!(self.out, "return Ok(({}lookahead, {}nt));", self.prefix, self.prefix);
                rust!(self.out, "}}");
            }

            rust!(self.out, "}}"); // match

            rust!(self.out, "}}"); // while/loop

            if this_prefix.len() > 0 {
                rust!(self.out, "return Ok({}result);", self.prefix);
            }
        } else if fallthrough {
            rust!(self.out, "return Ok({}result);", self.prefix);
        }

        rust!(self.out, "}}"); // fn

        Ok(())
    }

    fn pop_syms(&self, depth: usize, to_pop: usize) -> Vec<String> {
        (depth-to_pop .. depth).map(|i| format!("{}sym{}", self.prefix, i)).collect()
    }

    fn transition(&self,
                  prefix: &[Symbol],
                  next_index: StateIndex,
                  lookahead: &str,
                  tokens: &str)
                  -> String
    {
        // depth of stack, including the newly shifted token
        let n = prefix.len() + 1;

        // number of tokens next state expects; will
        // always be at least 1 for the newly shifted
        // token
        let m = self.state_prefixes[next_index.0].len();
        assert!(m >= 1);

        let transfer_syms = self.pop_syms(n, m);

        // invoke next state, transferring the top `m` tokens
        format!("try!({}state{}({}{}, {}{}, {}))",
                self.prefix, next_index.0,
                self.prefix, lookahead,
                self.prefix, tokens,
                Sep(", ", &transfer_syms))
    }

    /// Emit a pattern that matches `id` but doesn't extract any data.
    fn match_terminal(&mut self, id: TerminalString) -> io::Result<()> {
        let pattern = self.grammar.pattern(id)
                                  .map(&mut |_| "_");
        rust!(self.out, "Some({}) => {{", pattern);
        Ok(())
    }

    /// Emit a pattern that matches `id` and extracts its value, storing
    /// that value as `let_name`.
    fn consume_terminal(&mut self, id: TerminalString, let_name: String) -> io::Result<()> {
        let mut pattern_names = vec![];
        let pattern = self.grammar.pattern(id).map(&mut |_| {
            let index = pattern_names.len();
            pattern_names.push(format!("{}tok{}", self.prefix, index));
            pattern_names.last().cloned().unwrap()
        });

        if pattern_names.is_empty() {
            rust!(self.out, "Some({}tok @ {}) => {{", self.prefix, pattern);
            rust!(self.out, "let mut {} = &mut Some({}tok);",
                  let_name, self.prefix);
        } else {
            rust!(self.out, "Some({}) => {{", pattern);
            rust!(self.out, "let mut {} = &mut Some(({}));",
                  let_name, pattern_names.connect(", "));
        }

        Ok(())
    }
}

<commit_msg>fix indenting :)<commit_after>//! A compiler from an LR(1) table to a [recursive ascent] parser.
//!
//! [recursive ascent]: https://en.wikipedia.org/wiki/Recursive_ascent_parser

use intern::{InternedString};
use grammar::repr::{Grammar, NonterminalString, Symbol, TerminalString, Types};
use lr1::{Lookahead, State, StateIndex};
use rust::RustWrite;
use std::io::{self, Write};
use util::Sep;

pub type Path = Vec<InternedString>;

pub fn compile<'grammar,W:Write>(
    grammar: &'grammar Grammar,
    user_start_symbol: NonterminalString,
    start_symbol: NonterminalString,
    states: &[State<'grammar>],
    out: &mut RustWrite<W>)
    -> io::Result<()>
{
    let mut ascent = RecursiveAscent::new(grammar, user_start_symbol, start_symbol, states, out);
    ascent.write()
}

struct RecursiveAscent<'ascent,'grammar:'ascent,W:Write+'ascent> {
    grammar: &'grammar Grammar,
    prefix: &'grammar str,
    types: &'grammar Types,
    user_start_symbol: NonterminalString,
    start_symbol: NonterminalString,
    states: &'ascent [State<'grammar>],
    state_prefixes: Vec<&'grammar [Symbol]>,
    out: &'ascent mut RustWrite<W>,
}

impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
    fn new(grammar: &'grammar Grammar,
           user_start_symbol: NonterminalString,
           start_symbol: NonterminalString,
           states: &'ascent [State<'grammar>],
           out: &'ascent mut RustWrite<W>)
           -> RecursiveAscent<'ascent,'grammar,W>
    {
        RecursiveAscent {
            grammar: grammar,
            prefix: &grammar.prefix,
            types: &grammar.types,
            states: states,
            state_prefixes: states.iter().map(|s| s.prefix()).collect(),
            user_start_symbol: user_start_symbol,
            start_symbol: start_symbol,
            out: out,
        }
    }

    fn write(&mut self) -> io::Result<()> {
        try!(self.write_start_fn());

        rust!(self.out, "");
        rust!(self.out, "mod {}parse{} {{",
              self.prefix, self.start_symbol);

        rust!(self.out, "#![allow(non_snake_case, unused_mut, unused_variables)]");
        rust!(self.out, "");

        try!(self.write_uses());

        rust!(self.out, "");
        try!(self.write_return_type_defn());

        for i in 0..self.states.len() {
            rust!(self.out, "");
            try!(self.write_state_fn(StateIndex(i)));
        }

        rust!(self.out, "}}");

        Ok(())
    }

    fn write_uses(&mut self) -> io::Result<()> {
        for u in &self.grammar.uses {
            rust!(self.out, "use {};", u);
        }
        Ok(())
    }

    fn write_return_type_defn(&mut self) -> io::Result<()> {
        rust!(self.out, "pub enum {}Nonterminal {{", self.prefix);

        // make an enum with one variant per nonterminal; I considered
        // making different enums per state, but this would mean we
        // have to unwrap and rewrap as we pass up the stack, which
        // seems silly
        for &nt in self.grammar.productions.keys() {
            rust!(self.out, "{}({}),", nt, self.types.nonterminal_type(nt));
        }

        rust!(self.out, "}}");
        Ok(())
    }

    fn write_start_fn(&mut self) -> io::Result<()> {
        let terminal_type = self.types.terminal_enum_type();
        rust!(self.out, "#[allow(non_snake_case)]");
        rust!(self.out, "pub fn parse_{}<", self.user_start_symbol);
        rust!(self.out, "    {}TOKENS: IntoIterator<Item={}>,", self.prefix, terminal_type);
        rust!(self.out, ">(");
        rust!(self.out, "{}tokens: {}TOKENS,", self.prefix, self.prefix);
        rust!(self.out, ") -> Result<(Option<{}>, {}), Option<{}>> {{",
              terminal_type, self.types.nonterminal_type(self.start_symbol), terminal_type);
        rust!(self.out, "let mut {}tokens = {}tokens.into_iter();", self.prefix, self.prefix);
        rust!(self.out, "let {}lookahead = {}tokens.next();", self.prefix, self.prefix);
        rust!(self.out, "match try!({}parse{}::{}state0({}lookahead, &mut {}tokens)) {{",
              self.prefix, self.start_symbol, self.prefix, self.prefix, self.prefix);
        rust!(self.out, "({}lookahead, {}parse{}::{}Nonterminal::{}({}nt)) => \
                         Ok(({}lookahead, {}nt)),",
              self.prefix, self.prefix, self.start_symbol, self.prefix, self.start_symbol,
              self.prefix, self.prefix, self.prefix);
        rust!(self.out, "_ => unreachable!(),");
        rust!(self.out, "}}");
        rust!(self.out, "}}");

        Ok(())
    }

    fn write_state_fn(&mut self, this_index: StateIndex) -> io::Result<()> {
        let this_state = &self.states[this_index.0];
        let this_prefix = self.state_prefixes[this_index.0];
        let terminal_type = self.types.terminal_enum_type();

        // Leave a comment explaining what this state is.
        rust!(self.out, "// State {}", this_index.0);
        for item in this_state.items.vec.iter() {
            rust!(self.out, "//   {:?}", item);
        }
        rust!(self.out, "//");
        for (token, action) in &this_state.tokens {
            rust!(self.out, "//   {:?} -> {:?}", token, action);
        }
        rust!(self.out, "//");
        for (nt, state) in &this_state.gotos {
            rust!(self.out, "//   {:?} -> {:?}", nt, state);
        }

        // set to true if goto actions are worth generating
        let mut fallthrough = false;

        rust!(self.out, "pub fn {}state{}<", self.prefix, this_index.0);
        rust!(self.out, "    {}TOKENS: Iterator<Item={}>,", self.prefix, terminal_type);
        rust!(self.out, ">(");
        rust!(self.out, "mut {}lookahead: Option<{}>,", self.prefix, terminal_type);
        rust!(self.out, "{}tokens: &mut {}TOKENS,", self.prefix, self.prefix);
        for i in 0..this_prefix.len() {
            rust!(self.out, "{}sym{}: &mut Option<{}>,",
                  self.prefix, i, this_prefix[i].ty(&self.types));
        }
        rust!(self.out, ") -> Result<(Option<{}>, {}Nonterminal), Option<{}>> {{",
              terminal_type, self.prefix, terminal_type);

        rust!(self.out, "let mut {}result: (Option<{}>, {}Nonterminal);",
              self.prefix, terminal_type, self.prefix);

        rust!(self.out, "match {}lookahead {{", self.prefix);

        // first emit shifts:
        for (token, next_index) in
            this_state.tokens.iter()
                             .filter_map(|(token, action)| action.shift().map(|n| (token, n)))
        {
            match *token {
                Lookahead::Terminal(s) => {
                    let sym_name = format!("{}sym{}", self.prefix, this_prefix.len());
                    try!(self.consume_terminal(s, sym_name))
                }
                Lookahead::EOF =>
                    unreachable!("should never have to shift EOF")
            }

            // "shift" the lookahead onto the "stack" by taking its address
            rust!(self.out, "let {}lookahead = {}tokens.next();",
                  self.prefix, self.prefix);

            // transition to the new state
            let transition =
                self.transition(this_prefix, next_index, "lookahead", "tokens");
            rust!(self.out, "{}result = {};", self.prefix, transition);

            rust!(self.out, "}}");
            fallthrough = true;
        }

        // now emit reduces:
        for (token, production) in
            this_state.tokens.iter()
                             .filter_map(|(token, action)| action.reduce().map(|p| (token, p)))
        {
            match *token {
                Lookahead::Terminal(s) =>
                    try!(self.match_terminal(s)),
                Lookahead::EOF =>
                    rust!(self.out, "None => {{"),
            }

            let n = this_prefix.len(); // number of symbols we have on the stack
            let m = production.symbols.len(); // number action code wants
            assert!(n >= m);
            let transfer_syms = self.pop_syms(n, m);

            // "pop" the items off the stack
            for sym in &transfer_syms {
                rust!(self.out, "let {} = {}.take().unwrap();", sym, sym);
            }

            // invoke the action code
            rust!(self.out, "let {}nt = super::{}action{}({});",
                  self.prefix,
                  self.prefix,
                  production.action_fn.index(),
                  Sep(", ", &transfer_syms));

            // wrap up the result along with the (unused) lookahead
            if !transfer_syms.is_empty() {
                // if we popped anything off of the stack, then this frame is done
                rust!(self.out, "return Ok(({}lookahead, {}Nonterminal::{}({}nt)));",
                      self.prefix, self.prefix, production.nonterminal, self.prefix);
            } else {
                // otherwise, pop back
                rust!(self.out, "result = ({}lookahead, {}Nonterminal::{}({}nt));",
                      self.prefix, self.prefix, production.nonterminal, self.prefix);
                fallthrough = true;
            }

            rust!(self.out, "}}");
        }

        // if we hit this, the next token is not recognized, so generate an error
        rust!(self.out, "_ => {{");
        rust!(self.out, "return Err({}lookahead);", self.prefix);
        rust!(self.out, "}}");

        rust!(self.out, "}}"); // match

        // finally, emit gotos (if relevant)
        if fallthrough && !this_state.gotos.is_empty() {
            if this_prefix.len() > 0 {
                rust!(self.out, "while {}sym{}.is_some() {{", self.prefix, this_prefix.len() - 1);
            } else {
                rust!(self.out, "loop {{");
            }

            rust!(self.out, "let ({}lookahead, {}nt) = {}result;",
                  self.prefix, self.prefix, self.prefix);

            rust!(self.out, "match {}nt {{", self.prefix);
            for (&nt, &next_index) in &this_state.gotos {
                rust!(self.out, "{}Nonterminal::{}({}nt) => {{", self.prefix, nt, self.prefix);
                rust!(self.out, "let {}sym{} = &mut Some({}nt);",
                      self.prefix, this_prefix.len(), self.prefix);
                let transition = self.transition(this_prefix, next_index, "lookahead", "tokens");
                rust!(self.out, "{}result = {};", self.prefix, transition);
                rust!(self.out, "}}");
            }

            // errors are not possible in the goto phase; a missing entry
            // indicates parse successfully completed, so just bail out
            if this_state.gotos.len() != self.grammar.productions.keys().len() {
                rust!(self.out, "_ => {{");
                rust!(self.out, "return Ok(({}lookahead, {}nt));", self.prefix, self.prefix);
                rust!(self.out, "}}");
            }

            rust!(self.out, "}}"); // match

            rust!(self.out, "}}"); // while/loop

            if this_prefix.len() > 0 {
                rust!(self.out, "return Ok({}result);", self.prefix);
            }
        } else if fallthrough {
            rust!(self.out, "return Ok({}result);", self.prefix);
        }

        rust!(self.out, "}}"); // fn

        Ok(())
    }

    fn pop_syms(&self, depth: usize, to_pop: usize) -> Vec<String> {
        (depth-to_pop .. depth).map(|i| format!("{}sym{}", self.prefix, i)).collect()
    }

    fn transition(&self,
                  prefix: &[Symbol],
                  next_index: StateIndex,
                  lookahead: &str,
                  tokens: &str)
                  -> String
    {
        // depth of stack, including the newly shifted token
        let n = prefix.len() + 1;

        // number of tokens next state expects; will
        // always be at least 1 for the newly shifted
        // token
        let m = self.state_prefixes[next_index.0].len();
        assert!(m >= 1);

        let transfer_syms = self.pop_syms(n, m);

        // invoke next state, transferring the top `m` tokens
        format!("try!({}state{}({}{}, {}{}, {}))",
                self.prefix, next_index.0,
                self.prefix, lookahead,
                self.prefix, tokens,
                Sep(", ", &transfer_syms))
    }

    /// Emit a pattern that matches `id` but doesn't extract any data.
    fn match_terminal(&mut self, id: TerminalString) -> io::Result<()> {
        let pattern = self.grammar.pattern(id)
                                  .map(&mut |_| "_");
        rust!(self.out, "Some({}) => {{", pattern);
        Ok(())
    }

    /// Emit a pattern that matches `id` and extracts its value, storing
    /// that value as `let_name`.
    fn consume_terminal(&mut self, id: TerminalString, let_name: String) -> io::Result<()> {
        let mut pattern_names = vec![];
        let pattern = self.grammar.pattern(id).map(&mut |_| {
            let index = pattern_names.len();
            pattern_names.push(format!("{}tok{}", self.prefix, index));
            pattern_names.last().cloned().unwrap()
        });

        if pattern_names.is_empty() {
            rust!(self.out, "Some({}tok @ {}) => {{", self.prefix, pattern);
            rust!(self.out, "let mut {} = &mut Some({}tok);",
                  let_name, self.prefix);
        } else {
            rust!(self.out, "Some({}) => {{", pattern);
            rust!(self.out, "let mut {} = &mut Some(({}));",
                  let_name, pattern_names.connect(", "));
        }

        Ok(())
    }
}

<|endoftext|>


<commit_before>#[doc = "Generate markdown from a document tree"];

import std::io;
import std::io::writer_util;

export mk_pass;

fn mk_pass(
    writer: fn~() -> io::writer
) -> pass {
    ret fn~(
        _srv: astsrv::srv,
        doc: doc::cratedoc
    ) -> doc::cratedoc {
        write_markdown(doc, writer());
        doc
    };
}

type ctxt = {
    w: io::writer
};

fn write_markdown(
    doc: doc::cratedoc,
    writer: io::writer
) {
    let ctxt = {
        w: writer
    };

    write_crate(ctxt, doc);
}

tag hlvl {
    h1 = 1;
    h2 = 2;
    h3 = 3;
}

fn write_header(ctxt: ctxt, lvl: hlvl, title: str) {
    let hashes = str::from_chars(vec::init_elt('#', lvl as uint));
    ctxt.w.write_line(#fmt("%s %s", hashes, title));
    ctxt.w.write_line("");
}

fn write_crate(
    ctxt: ctxt,
    doc: doc::cratedoc
) {
    write_header(ctxt, h1, #fmt("Crate %s", doc.topmod.name));
    write_top_module(ctxt, doc.topmod);
}

fn write_top_module(
    ctxt: ctxt,
    moddoc: doc::moddoc
) {
    write_mod_contents(ctxt, moddoc);
}

fn write_mod(
    ctxt: ctxt,
    moddoc: doc::moddoc
) {
    let fullpath = str::connect(moddoc.path + [moddoc.name], "::");
    write_header(ctxt, h2, #fmt("Module `%s`", fullpath));
    write_mod_contents(ctxt, moddoc);
}

#[test]
fn should_write_full_path_to_mod() {
    let markdown = test::render("mod a { mod b { mod c { } } }");
    assert str::contains(markdown, "## Module `a::b::c`");
}

fn write_mod_contents(
    ctxt: ctxt,
    doc: doc::moddoc
) {
    write_brief(ctxt, doc.brief);
    write_desc(ctxt, doc.desc);

    for fndoc in *doc.fns {
        write_fn(ctxt, fndoc);
    }

    for moddoc in *doc.mods {
        write_mod(ctxt, moddoc);
    }
}

#[test]
fn should_write_crate_brief_description() {
    let markdown = test::render("#[doc(brief = \"this is the crate\")];");
    assert str::contains(markdown, "this is the crate");
}

#[test]
fn should_write_crate_description() {
    let markdown = test::render("#[doc = \"this is the crate\"];");
    assert str::contains(markdown, "this is the crate");
}

fn write_fn(
    ctxt: ctxt,
    doc: doc::fndoc
) {
    write_header(ctxt, h3, #fmt("Function `%s`", doc.name));
    write_brief(ctxt, doc.brief);
    write_desc(ctxt, doc.desc);
    write_args(ctxt, doc.args);
    write_return(ctxt, doc.return);
}

fn write_brief(
    ctxt: ctxt,
    brief: option<str>
) {
    alt brief {
      some(brief) {
        ctxt.w.write_line(brief);
        ctxt.w.write_line("");
      }
      none { }
    }
}

fn write_desc(
    ctxt: ctxt,
    desc: option<str>
) {
    alt desc {
        some(desc) {
            ctxt.w.write_line(desc);
            ctxt.w.write_line("");
        }
        none { }
    }
}

fn write_args(
    ctxt: ctxt,
    args: [doc::argdoc]
) {
    if vec::is_not_empty(args) {
        ctxt.w.write_line("Arguments:");
        ctxt.w.write_line("");
        vec::iter(args) {|arg| write_arg(ctxt, arg) };
        ctxt.w.write_line("");
    }
}

fn write_arg(ctxt: ctxt, arg: doc::argdoc) {
    assert option::is_some(arg.ty);
    ctxt.w.write_str(#fmt(
        "* `%s`: `%s`",
        arg.name,
        option::get(arg.ty)
    ));
    alt arg.desc {
      some(desc) {
        ctxt.w.write_str(#fmt(" - %s", desc));
      }
      none { }
    }
    ctxt.w.write_line("");
}

#[test]
fn should_write_argument_list() {
    let source = "fn a(b: int, c: int) { }";
    let markdown = test::render(source);
    assert str::contains(
        markdown,
        "Arguments:\n\
         \n\
         * `b`: `int`\n\
         * `c`: `int`\n\
         \n"
    );
}

#[test]
fn should_not_write_arguments_if_none() {
    let source = "fn a() { } fn b() { }";
    let markdown = test::render(source);
    assert !str::contains(markdown, "Arguments");
}

#[test]
fn should_write_argument_description() {
    let source = "#[doc(args(a = \"milk\"))] fn f(a: bool) { }";
    let markdown = test::render(source);
    assert str::contains(markdown, "`a`: `bool` - milk");
}

fn write_return(
    ctxt: ctxt,
    return: option<doc::retdoc>
) {
    alt return {
      some(doc) {
        alt doc.ty {
          some(ty) {
            ctxt.w.write_line(#fmt("Returns `%s`", ty));
            ctxt.w.write_line("");
            alt doc.desc {
              some(d) {
                ctxt.w.write_line(d);
              }
              none { }
            }
          }
          none { fail "unimplemented"; }
        }
      }
      none { }
    }
}

#[test]
fn should_write_return_type_on_new_line() {
    let markdown = test::render("fn a() -> int { }");
    assert str::contains(markdown, "\nReturns `int`");
}

#[test]
fn should_write_blank_line_between_return_type_and_next_header() {
    let markdown = test::render(
        "fn a() -> int { } \
         fn b() -> int { }"
    );
    assert str::contains(markdown, "Returns `int`\n\n##");
}

#[test]
fn should_not_write_return_type_when_there_is_none() {
    let markdown = test::render("fn a() { }");
    assert !str::contains(markdown, "Returns");
}

#[cfg(test)]
mod test {
    fn render(source: str) -> str {
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = tystr_pass::mk_pass()(srv, doc);
        let doc = path_pass::mk_pass()(srv, doc);
        let doc = attr_pass::mk_pass()(srv, doc);
        let markdown = write_markdown_str(doc);
        #debug("markdown: %s", markdown);
        markdown
    }

    fn write_markdown_str(
        doc: doc::cratedoc
    ) -> str {
        let buffer = io::mk_mem_buffer();
        let writer = io::mem_buffer_writer(buffer);
        write_markdown(doc, writer);
        ret io::mem_buffer_str(buffer);
    }

    #[test]
    fn write_markdown_should_write_crate_header() {
        let srv = astsrv::mk_srv_from_str("");
        let doc = extract::from_srv(srv, "belch");
        let doc = attr_pass::mk_pass()(srv, doc);
        let markdown = write_markdown_str(doc);
        assert str::contains(markdown, "# Crate belch");
    }

    #[test]
    fn write_markdown_should_write_function_header() {
        let markdown = render("fn func() { }");
        assert str::contains(markdown, "## Function `func`");
    }

    #[test]
    fn write_markdown_should_write_mod_headers() {
        let markdown = render("mod moo { }");
        assert str::contains(markdown, "## Module `moo`");
    }

    #[test]
    fn should_leave_blank_line_after_header() {
        let markdown = render("mod morp { }");
        assert str::contains(markdown, "Module `morp`\n\n");
    }

    #[test]
    fn should_leave_blank_line_between_fn_header_and_brief() {
        let markdown = render("#[doc(brief = \"brief\")] fn a() { }");
        assert str::contains(markdown, "Function `a`\n\nbrief");
    }

    #[test]
    fn should_leave_blank_line_after_brief() {
        let markdown = render("#[doc(brief = \"brief\")] fn a() { }");
        assert str::contains(markdown, "brief\n\n");
    }

    #[test]
    fn should_leave_blank_line_between_brief_and_desc() {
        let markdown = render(
            "#[doc(brief = \"brief\", desc = \"desc\")] fn a() { }"
        );
        assert str::contains(markdown, "brief\n\ndesc");
    }

}<commit_msg>rustdoc: Add a blank line after the return value description<commit_after>#[doc = "Generate markdown from a document tree"];

import std::io;
import std::io::writer_util;

export mk_pass;

fn mk_pass(
    writer: fn~() -> io::writer
) -> pass {
    ret fn~(
        _srv: astsrv::srv,
        doc: doc::cratedoc
    ) -> doc::cratedoc {
        write_markdown(doc, writer());
        doc
    };
}

type ctxt = {
    w: io::writer
};

fn write_markdown(
    doc: doc::cratedoc,
    writer: io::writer
) {
    let ctxt = {
        w: writer
    };

    write_crate(ctxt, doc);
}

tag hlvl {
    h1 = 1;
    h2 = 2;
    h3 = 3;
}

fn write_header(ctxt: ctxt, lvl: hlvl, title: str) {
    let hashes = str::from_chars(vec::init_elt('#', lvl as uint));
    ctxt.w.write_line(#fmt("%s %s", hashes, title));
    ctxt.w.write_line("");
}

fn write_crate(
    ctxt: ctxt,
    doc: doc::cratedoc
) {
    write_header(ctxt, h1, #fmt("Crate %s", doc.topmod.name));
    write_top_module(ctxt, doc.topmod);
}

fn write_top_module(
    ctxt: ctxt,
    moddoc: doc::moddoc
) {
    write_mod_contents(ctxt, moddoc);
}

fn write_mod(
    ctxt: ctxt,
    moddoc: doc::moddoc
) {
    let fullpath = str::connect(moddoc.path + [moddoc.name], "::");
    write_header(ctxt, h2, #fmt("Module `%s`", fullpath));
    write_mod_contents(ctxt, moddoc);
}

#[test]
fn should_write_full_path_to_mod() {
    let markdown = test::render("mod a { mod b { mod c { } } }");
    assert str::contains(markdown, "## Module `a::b::c`");
}

fn write_mod_contents(
    ctxt: ctxt,
    doc: doc::moddoc
) {
    write_brief(ctxt, doc.brief);
    write_desc(ctxt, doc.desc);

    for fndoc in *doc.fns {
        write_fn(ctxt, fndoc);
    }

    for moddoc in *doc.mods {
        write_mod(ctxt, moddoc);
    }
}

#[test]
fn should_write_crate_brief_description() {
    let markdown = test::render("#[doc(brief = \"this is the crate\")];");
    assert str::contains(markdown, "this is the crate");
}

#[test]
fn should_write_crate_description() {
    let markdown = test::render("#[doc = \"this is the crate\"];");
    assert str::contains(markdown, "this is the crate");
}

fn write_fn(
    ctxt: ctxt,
    doc: doc::fndoc
) {
    write_header(ctxt, h3, #fmt("Function `%s`", doc.name));
    write_brief(ctxt, doc.brief);
    write_desc(ctxt, doc.desc);
    write_args(ctxt, doc.args);
    write_return(ctxt, doc.return);
}

fn write_brief(
    ctxt: ctxt,
    brief: option<str>
) {
    alt brief {
      some(brief) {
        ctxt.w.write_line(brief);
        ctxt.w.write_line("");
      }
      none { }
    }
}

fn write_desc(
    ctxt: ctxt,
    desc: option<str>
) {
    alt desc {
        some(desc) {
            ctxt.w.write_line(desc);
            ctxt.w.write_line("");
        }
        none { }
    }
}

fn write_args(
    ctxt: ctxt,
    args: [doc::argdoc]
) {
    if vec::is_not_empty(args) {
        ctxt.w.write_line("Arguments:");
        ctxt.w.write_line("");
        vec::iter(args) {|arg| write_arg(ctxt, arg) };
        ctxt.w.write_line("");
    }
}

fn write_arg(ctxt: ctxt, arg: doc::argdoc) {
    assert option::is_some(arg.ty);
    ctxt.w.write_str(#fmt(
        "* `%s`: `%s`",
        arg.name,
        option::get(arg.ty)
    ));
    alt arg.desc {
      some(desc) {
        ctxt.w.write_str(#fmt(" - %s", desc));
      }
      none { }
    }
    ctxt.w.write_line("");
}

#[test]
fn should_write_argument_list() {
    let source = "fn a(b: int, c: int) { }";
    let markdown = test::render(source);
    assert str::contains(
        markdown,
        "Arguments:\n\
         \n\
         * `b`: `int`\n\
         * `c`: `int`\n\
         \n"
    );
}

#[test]
fn should_not_write_arguments_if_none() {
    let source = "fn a() { } fn b() { }";
    let markdown = test::render(source);
    assert !str::contains(markdown, "Arguments");
}

#[test]
fn should_write_argument_description() {
    let source = "#[doc(args(a = \"milk\"))] fn f(a: bool) { }";
    let markdown = test::render(source);
    assert str::contains(markdown, "`a`: `bool` - milk");
}

fn write_return(
    ctxt: ctxt,
    return: option<doc::retdoc>
) {
    alt return {
      some(doc) {
        alt doc.ty {
          some(ty) {
            ctxt.w.write_line(#fmt("Returns `%s`", ty));
            ctxt.w.write_line("");
            alt doc.desc {
              some(d) {
                ctxt.w.write_line(d);
                ctxt.w.write_line("");
              }
              none { }
            }
          }
          none { fail "unimplemented"; }
        }
      }
      none { }
    }
}

#[test]
fn should_write_return_type_on_new_line() {
    let markdown = test::render("fn a() -> int { }");
    assert str::contains(markdown, "\nReturns `int`");
}

#[test]
fn should_write_blank_line_between_return_type_and_next_header() {
    let markdown = test::render(
        "fn a() -> int { } \
         fn b() -> int { }"
    );
    assert str::contains(markdown, "Returns `int`\n\n##");
}

#[test]
fn should_not_write_return_type_when_there_is_none() {
    let markdown = test::render("fn a() { }");
    assert !str::contains(markdown, "Returns");
}

#[test]
fn should_write_blank_line_after_return_description() {
    let markdown = test::render(
        "#[doc(return = \"blorp\")] fn a() -> int { }"
    );
    assert str::contains(markdown, "blorp\n\n");
}

#[cfg(test)]
mod test {
    fn render(source: str) -> str {
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = tystr_pass::mk_pass()(srv, doc);
        let doc = path_pass::mk_pass()(srv, doc);
        let doc = attr_pass::mk_pass()(srv, doc);
        let markdown = write_markdown_str(doc);
        #debug("markdown: %s", markdown);
        markdown
    }

    fn write_markdown_str(
        doc: doc::cratedoc
    ) -> str {
        let buffer = io::mk_mem_buffer();
        let writer = io::mem_buffer_writer(buffer);
        write_markdown(doc, writer);
        ret io::mem_buffer_str(buffer);
    }

    #[test]
    fn write_markdown_should_write_crate_header() {
        let srv = astsrv::mk_srv_from_str("");
        let doc = extract::from_srv(srv, "belch");
        let doc = attr_pass::mk_pass()(srv, doc);
        let markdown = write_markdown_str(doc);
        assert str::contains(markdown, "# Crate belch");
    }

    #[test]
    fn write_markdown_should_write_function_header() {
        let markdown = render("fn func() { }");
        assert str::contains(markdown, "## Function `func`");
    }

    #[test]
    fn write_markdown_should_write_mod_headers() {
        let markdown = render("mod moo { }");
        assert str::contains(markdown, "## Module `moo`");
    }

    #[test]
    fn should_leave_blank_line_after_header() {
        let markdown = render("mod morp { }");
        assert str::contains(markdown, "Module `morp`\n\n");
    }

    #[test]
    fn should_leave_blank_line_between_fn_header_and_brief() {
        let markdown = render("#[doc(brief = \"brief\")] fn a() { }");
        assert str::contains(markdown, "Function `a`\n\nbrief");
    }

    #[test]
    fn should_leave_blank_line_after_brief() {
        let markdown = render("#[doc(brief = \"brief\")] fn a() { }");
        assert str::contains(markdown, "brief\n\n");
    }

    #[test]
    fn should_leave_blank_line_between_brief_and_desc() {
        let markdown = render(
            "#[doc(brief = \"brief\", desc = \"desc\")] fn a() { }"
        );
        assert str::contains(markdown, "brief\n\ndesc");
    }

}<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::{is_relay_custom_inline_fragment_directive, PointerAddress};
use graphql_ir::{
    Condition, Directive, FragmentDefinition, InlineFragment, LinkedField, OperationDefinition,
    Program, Selection,
};
use schema::TypeReference;

use crate::node_identifier::NodeIdentifier;
use fnv::FnvHashMap;
use std::sync::Arc;

type SeenLinkedFields = FnvHashMap<PointerAddress, Arc<LinkedField>>;

///
/// Transform that flattens inline fragments, fragment spreads, merges linked fields selections.
///
/// Inline fragments are inlined (replaced with their selections) when:
/// - The fragment type matches the type of its parent, and it `is_for_codegen`,
///   or the inline fragment doesn't have directives .
///
/// with the exception that it never flattens the inline fragment with relay
/// directives (@defer, @__clientExtensions).
///
pub fn flatten(program: &Program, is_for_codegen: bool) -> Program {
    let mut next_program = Program::new(Arc::clone(&program.schema));
    let mut transform = FlattenTransform::new(program, is_for_codegen);

    for operation in program.operations() {
        next_program.insert_operation(Arc::new(transform.transform_operation(operation)));
    }
    for fragment in program.fragments() {
        next_program.insert_fragment(Arc::new(transform.transform_fragment(fragment)));
    }
    next_program
}

struct FlattenTransform<'s> {
    program: &'s Program,
    is_for_codegen: bool,
    seen_linked_fields: SeenLinkedFields,
}

impl<'s> FlattenTransform<'s> {
    fn new(program: &'s Program, is_for_codegen: bool) -> Self {
        Self {
            program,
            is_for_codegen,
            seen_linked_fields: Default::default(),
        }
    }

    fn transform_operation(&mut self, operation: &OperationDefinition) -> OperationDefinition {
        OperationDefinition {
            kind: operation.kind,
            name: operation.name,
            type_: operation.type_,
            directives: operation.directives.clone(),
            variable_definitions: operation.variable_definitions.clone(),
            selections: self.tranform_selections(
                &operation.selections,
                &TypeReference::Named(operation.type_),
            ),
        }
    }

    fn transform_fragment(&mut self, fragment: &FragmentDefinition) -> FragmentDefinition {
        FragmentDefinition {
            name: fragment.name,
            type_condition: fragment.type_condition,
            directives: fragment.directives.clone(),
            variable_definitions: fragment.variable_definitions.clone(),
            used_global_variables: fragment.used_global_variables.clone(),
            selections: self.tranform_selections(
                &fragment.selections,
                &TypeReference::Named(fragment.type_condition),
            ),
        }
    }

    fn tranform_selections(
        &mut self,
        selections: &[Selection],
        parent_type: &TypeReference,
    ) -> Vec<Selection> {
        let next_selections = selections
            .iter()
            .map(|s| self.transform_selection(s, parent_type))
            .collect::<Vec<_>>();
        let mut flattened_selections = Vec::with_capacity(next_selections.len());
        self.flatten_selections(&mut flattened_selections, &next_selections, parent_type);

        flattened_selections
    }

    fn transform_linked_field(&mut self, linked_field: &Arc<LinkedField>) -> Arc<LinkedField> {
        let key = PointerAddress::new(Arc::as_ref(linked_field));
        if let Some(prev) = self.seen_linked_fields.get(&key) {
            return Arc::clone(prev);
        }
        let result = Arc::new(LinkedField {
            alias: linked_field.alias,
            definition: linked_field.definition,
            arguments: linked_field.arguments.clone(),
            directives: linked_field.directives.clone(),
            selections: self.tranform_selections(
                &linked_field.selections,
                &self
                    .program
                    .schema
                    .field(linked_field.definition.item)
                    .type_,
            ),
        });
        self.seen_linked_fields.insert(key, Arc::clone(&result));
        result
    }

    fn transform_selection(
        &mut self,
        selection: &Selection,
        parent_type: &TypeReference,
    ) -> Selection {
        match selection {
            Selection::InlineFragment(node) => {
                let next_parent_type: TypeReference = match node.type_condition {
                    Some(type_condition) => TypeReference::Named(type_condition),
                    None => parent_type.clone(),
                };
                Selection::InlineFragment(Arc::new(InlineFragment {
                    type_condition: node.type_condition,
                    directives: node.directives.clone(),
                    selections: self.tranform_selections(&node.selections, &next_parent_type),
                }))
            }
            Selection::LinkedField(node) => {
                Selection::LinkedField(self.transform_linked_field(node))
            }
            Selection::Condition(node) => Selection::Condition(Arc::new(Condition {
                value: node.value.clone(),
                passing_value: node.passing_value,
                selections: self.tranform_selections(&node.selections, parent_type),
            })),
            Selection::FragmentSpread(node) => Selection::FragmentSpread(Arc::clone(node)),
            Selection::ScalarField(node) => Selection::ScalarField(Arc::clone(node)),
        }
    }

    fn flatten_selections(
        &mut self,
        flattened_selections: &mut Vec<Selection>,
        selections: &[Selection],
        parent_type: &TypeReference,
    ) {
        for selection in selections {
            if let Selection::InlineFragment(inline_fragment) = selection {
                if should_flatten_inline_fragment(inline_fragment, parent_type, self.is_for_codegen)
                {
                    self.flatten_selections(
                        flattened_selections,
                        &inline_fragment.selections,
                        parent_type,
                    );
                    continue;
                }
            }

            let flattened_selection = flattened_selections
                .iter_mut()
                .find(|sel| NodeIdentifier::are_equal(&self.program.schema, sel, selection));

            match flattened_selection {
                None => {
                    flattened_selections.push(selection.clone());
                }
                Some(flattened_selection) => {
                    match flattened_selection {
                        Selection::InlineFragment(flattened_node) => {
                            let type_condition: TypeReference = match flattened_node.type_condition
                            {
                                Some(type_condition) => TypeReference::Named(type_condition),
                                None => parent_type.clone(),
                            };

                            let node_selections = match selection {
                                Selection::InlineFragment(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected an InlineFragment."),
                            };

                            *flattened_selection =
                                Selection::InlineFragment(Arc::new(InlineFragment {
                                    type_condition: flattened_node.type_condition,
                                    directives: flattened_node.directives.clone(),
                                    selections: self.merge_selections(
                                        &flattened_node.selections,
                                        &node_selections,
                                        &type_condition,
                                    ),
                                }));
                        }
                        Selection::LinkedField(flattened_node) => {
                            let node_selections = match selection {
                                Selection::LinkedField(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a LinkedField."),
                            };
                            *flattened_selection = Selection::LinkedField(Arc::new(LinkedField {
                                alias: flattened_node.alias,
                                definition: flattened_node.definition,
                                arguments: flattened_node.arguments.clone(),
                                directives: flattened_node.directives.clone(),
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    &self
                                        .program
                                        .schema
                                        .field(flattened_node.definition.item)
                                        .type_,
                                ),
                            }));
                        }
                        Selection::Condition(flattened_node) => {
                            let node_selections = match selection {
                                Selection::Condition(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a Condition."),
                            };

                            *flattened_selection = Selection::Condition(Arc::new(Condition {
                                value: flattened_node.value.clone(),
                                passing_value: flattened_node.passing_value,
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    parent_type,
                                ),
                            }));
                        }
                        Selection::ScalarField(_) => {}
                        Selection::FragmentSpread(_) => {}
                    };
                }
            }
        }
    }

    fn merge_selections(
        &mut self,
        selections_a: &[Selection],
        selections_b: &[Selection],
        parent_type: &TypeReference,
    ) -> Vec<Selection> {
        let mut flattened_selections = Vec::with_capacity(selections_a.len());
        self.flatten_selections(&mut flattened_selections, selections_a, parent_type);
        self.flatten_selections(&mut flattened_selections, selections_b, parent_type);
        flattened_selections
    }
}

fn should_flatten_inline_with_directives(directives: &[Directive], is_for_codegen: bool) -> bool {
    if is_for_codegen {
        !directives
            .iter()
            .any(is_relay_custom_inline_fragment_directive)
    } else {
        directives.is_empty()
    }
}

fn should_flatten_inline_fragment(
    inline_fragment: &InlineFragment,
    parent_type: &TypeReference,
    is_for_codegen: bool,
) -> bool {
    match inline_fragment.type_condition {
        None => should_flatten_inline_with_directives(&inline_fragment.directives, is_for_codegen),
        Some(type_condition) => {
            type_condition == parent_type.inner()
                && should_flatten_inline_with_directives(
                    &inline_fragment.directives,
                    is_for_codegen,
                )
        }
    }
}
<commit_msg>parallelize flatten transform<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::{is_relay_custom_inline_fragment_directive, PointerAddress};
use graphql_ir::{
    Condition, Directive, FragmentDefinition, InlineFragment, LinkedField, OperationDefinition,
    Program, Selection,
};
use schema::TypeReference;

use crate::node_identifier::NodeIdentifier;
use fnv::FnvHashMap;
use parking_lot::RwLock;
use rayon::prelude::*;
use schema::Schema;
use std::sync::Arc;

type SeenLinkedFields = Arc<RwLock<FnvHashMap<PointerAddress, Arc<LinkedField>>>>;

///
/// Transform that flattens inline fragments, fragment spreads, merges linked fields selections.
///
/// Inline fragments are inlined (replaced with their selections) when:
/// - The fragment type matches the type of its parent, and it `is_for_codegen`,
///   or the inline fragment doesn't have directives .
///
/// with the exception that it never flattens the inline fragment with relay
/// directives (@defer, @__clientExtensions).
///
pub fn flatten(program: &Program, is_for_codegen: bool) -> Program {
    let next_program = Arc::new(RwLock::new(Program::new(Arc::clone(&program.schema))));
    let transform = FlattenTransform::new(program, is_for_codegen);

    program.par_operations().for_each(|operation| {
        let operation = Arc::new(transform.transform_operation(operation));
        {
            let mut next_program = next_program.write();
            next_program.insert_operation(operation);
        }
    });
    program.par_fragments().for_each(|fragment| {
        let fragment = Arc::new(transform.transform_fragment(fragment));
        {
            let mut next_program = next_program.write();
            next_program.insert_fragment(fragment);
        }
    });
    Arc::try_unwrap(next_program).unwrap().into_inner()
}

struct FlattenTransform {
    schema: Arc<Schema>,
    is_for_codegen: bool,
    seen_linked_fields: SeenLinkedFields,
}

impl FlattenTransform {
    fn new(program: &'_ Program, is_for_codegen: bool) -> Self {
        Self {
            schema: Arc::clone(&program.schema),
            is_for_codegen,
            seen_linked_fields: Default::default(),
        }
    }

    fn transform_operation(&self, operation: &OperationDefinition) -> OperationDefinition {
        OperationDefinition {
            kind: operation.kind,
            name: operation.name,
            type_: operation.type_,
            directives: operation.directives.clone(),
            variable_definitions: operation.variable_definitions.clone(),
            selections: self.tranform_selections(
                &operation.selections,
                &TypeReference::Named(operation.type_),
            ),
        }
    }

    fn transform_fragment(&self, fragment: &FragmentDefinition) -> FragmentDefinition {
        FragmentDefinition {
            name: fragment.name,
            type_condition: fragment.type_condition,
            directives: fragment.directives.clone(),
            variable_definitions: fragment.variable_definitions.clone(),
            used_global_variables: fragment.used_global_variables.clone(),
            selections: self.tranform_selections(
                &fragment.selections,
                &TypeReference::Named(fragment.type_condition),
            ),
        }
    }

    fn tranform_selections(
        &self,
        selections: &[Selection],
        parent_type: &TypeReference,
    ) -> Vec<Selection> {
        let next_selections = selections
            .iter()
            .map(|s| self.transform_selection(s, parent_type))
            .collect::<Vec<_>>();
        let mut flattened_selections = Vec::with_capacity(next_selections.len());
        self.flatten_selections(&mut flattened_selections, &next_selections, parent_type);

        flattened_selections
    }

    fn transform_linked_field(&self, linked_field: &Arc<LinkedField>) -> Arc<LinkedField> {
        let key = PointerAddress::new(Arc::as_ref(linked_field));
        {
            let seen_linked_fields = self.seen_linked_fields.read();
            if let Some(prev) = seen_linked_fields.get(&key) {
                return Arc::clone(prev);
            }
        }
        let type_ = &self
            .schema
            .field(linked_field.definition.item)
            .type_
            .clone();
        let result = Arc::new(LinkedField {
            alias: linked_field.alias,
            definition: linked_field.definition,
            arguments: linked_field.arguments.clone(),
            directives: linked_field.directives.clone(),
            selections: self.tranform_selections(&linked_field.selections, &type_),
        });
        let mut seen_linked_fields = self.seen_linked_fields.write();
        // If another thread computed this in the meantime, use that result
        if let Some(prev) = seen_linked_fields.get(&key) {
            return Arc::clone(prev);
        }
        seen_linked_fields.insert(key, Arc::clone(&result));
        result
    }

    fn transform_selection(&self, selection: &Selection, parent_type: &TypeReference) -> Selection {
        match selection {
            Selection::InlineFragment(node) => {
                let next_parent_type: TypeReference = match node.type_condition {
                    Some(type_condition) => TypeReference::Named(type_condition),
                    None => parent_type.clone(),
                };
                Selection::InlineFragment(Arc::new(InlineFragment {
                    type_condition: node.type_condition,
                    directives: node.directives.clone(),
                    selections: self.tranform_selections(&node.selections, &next_parent_type),
                }))
            }
            Selection::LinkedField(node) => {
                Selection::LinkedField(self.transform_linked_field(node))
            }
            Selection::Condition(node) => Selection::Condition(Arc::new(Condition {
                value: node.value.clone(),
                passing_value: node.passing_value,
                selections: self.tranform_selections(&node.selections, parent_type),
            })),
            Selection::FragmentSpread(node) => Selection::FragmentSpread(Arc::clone(node)),
            Selection::ScalarField(node) => Selection::ScalarField(Arc::clone(node)),
        }
    }

    fn flatten_selections(
        &self,
        flattened_selections: &mut Vec<Selection>,
        selections: &[Selection],
        parent_type: &TypeReference,
    ) {
        for selection in selections {
            if let Selection::InlineFragment(inline_fragment) = selection {
                if should_flatten_inline_fragment(inline_fragment, parent_type, self.is_for_codegen)
                {
                    self.flatten_selections(
                        flattened_selections,
                        &inline_fragment.selections,
                        parent_type,
                    );
                    continue;
                }
            }

            let flattened_selection = flattened_selections
                .iter_mut()
                .find(|sel| NodeIdentifier::are_equal(&self.schema, sel, selection));

            match flattened_selection {
                None => {
                    flattened_selections.push(selection.clone());
                }
                Some(flattened_selection) => {
                    match flattened_selection {
                        Selection::InlineFragment(flattened_node) => {
                            let type_condition: TypeReference = match flattened_node.type_condition
                            {
                                Some(type_condition) => TypeReference::Named(type_condition),
                                None => parent_type.clone(),
                            };

                            let node_selections = match selection {
                                Selection::InlineFragment(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected an InlineFragment."),
                            };

                            *flattened_selection =
                                Selection::InlineFragment(Arc::new(InlineFragment {
                                    type_condition: flattened_node.type_condition,
                                    directives: flattened_node.directives.clone(),
                                    selections: self.merge_selections(
                                        &flattened_node.selections,
                                        &node_selections,
                                        &type_condition,
                                    ),
                                }));
                        }
                        Selection::LinkedField(flattened_node) => {
                            let node_selections = match selection {
                                Selection::LinkedField(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a LinkedField."),
                            };
                            let type_ = &self
                                .schema
                                .field(flattened_node.definition.item)
                                .type_
                                .clone();
                            *flattened_selection = Selection::LinkedField(Arc::new(LinkedField {
                                alias: flattened_node.alias,
                                definition: flattened_node.definition,
                                arguments: flattened_node.arguments.clone(),
                                directives: flattened_node.directives.clone(),
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    &type_,
                                ),
                            }));
                        }
                        Selection::Condition(flattened_node) => {
                            let node_selections = match selection {
                                Selection::Condition(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a Condition."),
                            };

                            *flattened_selection = Selection::Condition(Arc::new(Condition {
                                value: flattened_node.value.clone(),
                                passing_value: flattened_node.passing_value,
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    parent_type,
                                ),
                            }));
                        }
                        Selection::ScalarField(_) => {}
                        Selection::FragmentSpread(_) => {}
                    };
                }
            }
        }
    }

    fn merge_selections(
        &self,
        selections_a: &[Selection],
        selections_b: &[Selection],
        parent_type: &TypeReference,
    ) -> Vec<Selection> {
        let mut flattened_selections = Vec::with_capacity(selections_a.len());
        self.flatten_selections(&mut flattened_selections, selections_a, parent_type);
        self.flatten_selections(&mut flattened_selections, selections_b, parent_type);
        flattened_selections
    }
}

fn should_flatten_inline_with_directives(directives: &[Directive], is_for_codegen: bool) -> bool {
    if is_for_codegen {
        !directives
            .iter()
            .any(is_relay_custom_inline_fragment_directive)
    } else {
        directives.is_empty()
    }
}

fn should_flatten_inline_fragment(
    inline_fragment: &InlineFragment,
    parent_type: &TypeReference,
    is_for_codegen: bool,
) -> bool {
    match inline_fragment.type_condition {
        None => should_flatten_inline_with_directives(&inline_fragment.directives, is_for_codegen),
        Some(type_condition) => {
            type_condition == parent_type.inner()
                && should_flatten_inline_with_directives(
                    &inline_fragment.directives,
                    is_for_codegen,
                )
        }
    }
}
<|endoftext|>


<commit_before>use std::string::String;
use std::vec::Vec;

use byteorder::{BigEndian, WriteBytesExt};

use types::{BERT_LABEL, EXT_VERSION, BertTag};

// TODO: Error handling
pub struct Serializer;

pub struct Deserializer {
//    data: Vec<u8>
}

pub trait Serialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

trait Deserialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

impl Serializer {
    pub fn new() -> Serializer {
        Serializer {}
    }

    pub fn term_to_binary<T>(&self, data: T) -> Vec<u8> where Self: Serialize<T> {
        let mut binary = vec![EXT_VERSION];
        let serialized_data = self.to_bert(data);
        binary.extend(serialized_data.iter().clone());
        binary
    }

    pub fn generate_term(&self, tag: BertTag, data: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![tag as u8];
        binary.extend(data.iter().clone());
        binary
    }

    pub fn convert_string_to_binary(&self, data: &str) -> Vec<u8> {
        let binary_string = data.as_bytes();
        let binary_length = binary_string.len() as u8;
        let mut binary = vec![0u8, binary_length];
        binary.extend(binary_string.iter().clone());
        binary
    }

    pub fn merge_atoms(&self, atom_1: Vec<u8>, atom_2: Vec<u8>) -> Vec<u8> {
        let mut binary: Vec<u8> = atom_1.clone();
        binary.extend(atom_2.iter().clone());
        binary
    }

    pub fn get_small_tuple(&self, arity: u8, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![arity];
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::SmallTuple, binary)
    }

    pub fn get_large_tuple(&self, arity: i32, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(arity).unwrap();
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::LargeTuple, binary)
    }

    pub fn get_atom(&self, name: Vec<u8>) -> Vec<u8> {
        self.generate_term(BertTag::Atom, name)
    }

    pub fn get_nil(&self) -> Vec<u8> {
        vec![BertTag::Nil as u8]
    }

    pub fn get_bert_nil(&self) -> Vec<u8> {
        let bert_atom = self.get_bert_atom();

        let binary_nil_string = self.convert_string_to_binary("nil");
        let nil_atom = self.get_atom(binary_nil_string);

        let mut binary: Vec<u8> = vec![];
        binary.extend(bert_atom.iter().clone());
        binary.extend(nil_atom.iter().clone());
        self.get_small_tuple(2, binary)
    }

    pub fn get_bert_atom(&self) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(BERT_LABEL);
        self.get_atom(binary_string)
    }
}

impl Serialize<u8> for Serializer {
    fn to_bert(&self, data: u8) -> Vec<u8> {
        self.generate_term(BertTag::SmallInteger, vec![data])
    }
}

impl Serialize<i32> for Serializer {
    fn to_bert(&self, data: i32) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::Integer, binary)
    }
}

impl Serialize<f64> for Serializer {
    fn to_bert(&self, data: f64) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_f64::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::NewFloat, binary)
    }
}

impl Serialize<String> for Serializer {
    fn to_bert(&self, data: String) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(&data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl<'a> Serialize<&'a str> for Serializer {
    fn to_bert(&self, data: &'a str) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl Serialize<bool> for Serializer {
    fn to_bert(&self, data: bool) -> Vec<u8> {
        let boolean_string = data.to_string();
        let binary_boolean = self.convert_string_to_binary(&boolean_string);

        let bert_atom = self.get_bert_atom();
        let boolean_atom = self.get_atom(binary_boolean);

        let binary = self.merge_atoms(bert_atom, boolean_atom);
        self.get_small_tuple(2, binary)
    }
}

#[cfg(test)]
mod test_serializer {
    use super::{Serializer};

    #[test]
    fn test_serialize_u8() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(1u8),
            vec![131u8, 97, 1]
        );

        assert_eq!(
            serializer.term_to_binary(255u8),
            vec![131u8, 97, 255]
        );
    }

    #[test]
    fn test_serialize_i32() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-2147483648),
            vec![131u8, 98, 128, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(-1i32),
            vec![131u8, 98, 255, 255, 255, 255]
        );

        assert_eq!(
            serializer.term_to_binary(512i32),
            vec![131u8, 98, 0, 0, 2, 0]
        );

        assert_eq!(
            serializer.term_to_binary(2147483647),
            vec![131u8, 98, 127, 255, 255, 255]
        );
    }

    #[test]
    fn test_serializer_f64() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-3.14f64),
            vec![131, 70, 192, 9, 30, 184, 81, 235, 133, 31]
        );

        assert_eq!(
            serializer.term_to_binary(0.0f64),
            vec![131, 70, 0, 0, 0, 0, 0, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(3.14f64),
            vec![131, 70, 64, 9, 30, 184, 81, 235, 133, 31]
        )

    }

    #[test]
    fn test_serialize_bool() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(true),
            vec![
                131u8,
                104,                            // small tuple tag
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 4, 116, 114, 117, 101   // "true" as atom
            ]
        );

        assert_eq!(
            serializer.term_to_binary(false),
            vec![
                131u8,
                104,                               // small tuple tag
                2,                                 // tuple length
                100, 0, 4, 98, 101, 114, 116,      // "bert" as atom
                100, 0, 5, 102, 97, 108, 115, 101  // "false" as atom
            ]
        );
    }

    #[test]
    fn test_serialize_string() {
        let serializer = Serializer::new();

        // string by value
        assert_eq!(
            serializer.term_to_binary(String::from("test")),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );

        // string by reference
        assert_eq!(
            serializer.term_to_binary("test"),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_get_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_nil(),
            vec![106]
        );
    }

    #[test]
    fn test_get_small_tuple() {
        let serializer = Serializer::new();

        let data = vec![
            107, 0, 4, 116, 101, 115, 116,  // "test" as string
            107, 0, 4,  97, 116, 111, 109   // "atom" as string
        ];
        assert_eq!(
            serializer.get_small_tuple(2, data),
            vec![
                104,
                2,                             // tuple length
                107, 0, 4, 116, 101, 115, 116, // "test" as string
                107, 0, 4,  97, 116, 111, 109  // "atom" as string
            ]
        )
    }

    #[test]
    fn test_get_large_tuple() {
        let serializer = Serializer::new();

        let data = vec![
            107, 0, 4, 116, 101, 115, 116,  // "test" as string
            107, 0, 4,  97, 116, 111, 109   // "atom" as string
        ];
        assert_eq!(
            serializer.get_large_tuple(2, data),
            vec![
                105,
                0, 0, 0, 2,                     // tuple length
                107, 0, 4, 116, 101, 115, 116,  // "test" as string
                107, 0, 4,  97, 116, 111, 109   // "atom" as string
            ]
        )
    }

    #[test]
    fn test_get_bert_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_bert_nil(),
            vec![
                104,
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 3, 110, 105, 108        // "nil" as atom
            ]
        )
    }
}
<commit_msg>Changed getting small/large tuples as private methods<commit_after>use std::string::String;
use std::vec::Vec;

use byteorder::{BigEndian, WriteBytesExt};

use types::{BERT_LABEL, EXT_VERSION, BertTag};

// TODO: Error handling
pub struct Serializer;

pub struct Deserializer {
//    data: Vec<u8>
}

pub trait Serialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

trait Deserialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

impl Serializer {
    pub fn new() -> Serializer {
        Serializer {}
    }

    pub fn term_to_binary<T>(&self, data: T) -> Vec<u8> where Self: Serialize<T> {
        let mut binary = vec![EXT_VERSION];
        let serialized_data = self.to_bert(data);
        binary.extend(serialized_data.iter().clone());
        binary
    }

    pub fn generate_term(&self, tag: BertTag, data: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![tag as u8];
        binary.extend(data.iter().clone());
        binary
    }

    pub fn convert_string_to_binary(&self, data: &str) -> Vec<u8> {
        let binary_string = data.as_bytes();
        let binary_length = binary_string.len() as u8;
        let mut binary = vec![0u8, binary_length];
        binary.extend(binary_string.iter().clone());
        binary
    }

    pub fn merge_terms(&self, term_1: Vec<u8>, term_2: Vec<u8>) -> Vec<u8> {
        let mut binary: Vec<u8> = term_1.clone();
        binary.extend(term_2.iter().clone());
        binary
    }

    pub fn get_atom(&self, name: Vec<u8>) -> Vec<u8> {
        self.generate_term(BertTag::Atom, name)
    }

    pub fn get_nil(&self) -> Vec<u8> {
        vec![BertTag::Nil as u8]
    }

    pub fn get_bert_nil(&self) -> Vec<u8> {
        let bert_atom = self.get_bert_atom();

        let binary_nil_string = self.convert_string_to_binary("nil");
        let nil_atom = self.get_atom(binary_nil_string);

        let mut binary: Vec<u8> = vec![];
        binary.extend(bert_atom.iter().clone());
        binary.extend(nil_atom.iter().clone());
        self.get_small_tuple(2, binary)
    }

    pub fn get_bert_atom(&self) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(BERT_LABEL);
        self.get_atom(binary_string)
    }

    fn get_small_tuple(&self, arity: u8, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![arity];
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::SmallTuple, binary)
    }

    fn get_large_tuple(&self, arity: i32, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(arity).unwrap();
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::LargeTuple, binary)
    }
}

impl Serialize<u8> for Serializer {
    fn to_bert(&self, data: u8) -> Vec<u8> {
        self.generate_term(BertTag::SmallInteger, vec![data])
    }
}

impl Serialize<i32> for Serializer {
    fn to_bert(&self, data: i32) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::Integer, binary)
    }
}

impl Serialize<f64> for Serializer {
    fn to_bert(&self, data: f64) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_f64::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::NewFloat, binary)
    }
}

impl Serialize<String> for Serializer {
    fn to_bert(&self, data: String) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(&data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl<'a> Serialize<&'a str> for Serializer {
    fn to_bert(&self, data: &'a str) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl Serialize<bool> for Serializer {
    fn to_bert(&self, data: bool) -> Vec<u8> {
        let boolean_string = data.to_string();
        let binary_boolean = self.convert_string_to_binary(&boolean_string);

        let bert_atom = self.get_bert_atom();
        let boolean_atom = self.get_atom(binary_boolean);

        let binary = self.merge_terms(bert_atom, boolean_atom);
        self.get_small_tuple(2, binary)
    }
}

#[cfg(test)]
mod test_serializer {
    use super::{Serializer};

    #[test]
    fn test_serialize_u8() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(1u8),
            vec![131u8, 97, 1]
        );

        assert_eq!(
            serializer.term_to_binary(255u8),
            vec![131u8, 97, 255]
        );
    }

    #[test]
    fn test_serialize_i32() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-2147483648),
            vec![131u8, 98, 128, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(-1i32),
            vec![131u8, 98, 255, 255, 255, 255]
        );

        assert_eq!(
            serializer.term_to_binary(512i32),
            vec![131u8, 98, 0, 0, 2, 0]
        );

        assert_eq!(
            serializer.term_to_binary(2147483647),
            vec![131u8, 98, 127, 255, 255, 255]
        );
    }

    #[test]
    fn test_serializer_f64() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-3.14f64),
            vec![131, 70, 192, 9, 30, 184, 81, 235, 133, 31]
        );

        assert_eq!(
            serializer.term_to_binary(0.0f64),
            vec![131, 70, 0, 0, 0, 0, 0, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(3.14f64),
            vec![131, 70, 64, 9, 30, 184, 81, 235, 133, 31]
        )

    }

    #[test]
    fn test_serialize_bool() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(true),
            vec![
                131u8,
                104,                            // small tuple tag
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 4, 116, 114, 117, 101   // "true" as atom
            ]
        );

        assert_eq!(
            serializer.term_to_binary(false),
            vec![
                131u8,
                104,                               // small tuple tag
                2,                                 // tuple length
                100, 0, 4, 98, 101, 114, 116,      // "bert" as atom
                100, 0, 5, 102, 97, 108, 115, 101  // "false" as atom
            ]
        );
    }

    #[test]
    fn test_serialize_string() {
        let serializer = Serializer::new();

        // string by value
        assert_eq!(
            serializer.term_to_binary(String::from("test")),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );

        // string by reference
        assert_eq!(
            serializer.term_to_binary("test"),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_get_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_nil(),
            vec![106]
        );
    }

    #[test]
    fn test_get_bert_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_bert_nil(),
            vec![
                104,
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 3, 110, 105, 108        // "nil" as atom
            ]
        )
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#![allow(uppercase_variables)]
#![allow(unused_imports)]

extern crate test;
extern crate ndarray;

use ndarray::{Array, S, Si};
use ndarray::{arr0, arr1, arr2};

use test::black_box;

#[bench]
fn bench_std_add(bench: &mut test::Bencher)
{
    let mut a = arr2(&[&[1., 2., 2.],
                              &[3., 4., 4.],
                              &[3., 4., 4.],
                              &[3., 4., 4.],
                              &[5., 6., 6.]]);
    let b = a.clone();
    bench.iter(|| a.iadd(&b));
}

#[bench]
fn bench_std_iter_1d(bench: &mut test::Bencher)
{
    let a = arr1::<f32>(&[1., 2., 2.,
                         3., 4., 4.,
                         3., 4., 4.,
                         3., 4., 4.,
                         5., 6., 6.]);
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_1d_raw(bench: &mut test::Bencher)
{
    let a = arr1::<f32>(&[1., 2., 2.,
                         3., 4., 4.,
                         3., 4., 4.,
                         3., 4., 4.,
                         5., 6., 6.]);
    bench.iter(|| for &elt in a.raw_data().iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_2d(bench: &mut test::Bencher)
{
    let a = arr2(&[&[1., 2., 2.],
                          &[3., 4., 4.],
                          &[3., 4., 4.],
                          &[3., 4., 4.],
                          &[5., 6., 6.]]);
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_1d_large(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024));
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_1d_raw_large(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024));
    bench.iter(|| for &elt in a.raw_data().iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_2d_large(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((16, 64));
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn assign_scalar_2d(bench: &mut test::Bencher)
{
    let mut a = arr2(&[&[1., 2., 2.],
                              &[3., 4., 4.],
                              &[3., 4., 4.],
                              &[3., 4., 4.],
                              &[5., 6., 6.]]);
    a.swap_axes(0, 1);
    bench.iter(|| a.assign_scalar(&3.))
}

#[bench]
fn bench_iter_diag(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024, 1024));
    bench.iter(|| for elt in a.diag_iter() { black_box(elt) })
}

#[bench]
fn bench_row_iter(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024, 1024));
    let it = a.row_iter(17);
    bench.iter(|| for elt in it.clone() { black_box(elt) })
}

#[bench]
fn bench_col_iter(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024, 1024));
    bench.iter(|| for elt in a.col_iter(17) { black_box(elt) })
}

#[bench]
fn bench_mat_mul(bench: &mut test::Bencher)
{
    let a = arr2(&[&[1., 2., 2.],
                          &[3., 4., 4.],
                          &[3., 4., 4.],
                          &[3., 4., 4.],
                          &[5., 6., 6.]]);
    let mut at = a.clone();
    at.swap_axes(0, 1);
    bench.iter(|| at.mat_mul(&a));
}

#[bench]
fn lst_squares(bench: &mut test::Bencher)
{
    let xs = arr2(&[&[ 2.,  3.],
                           &[-2., -1.],
                           &[ 1.,  5.],
                           &[-1.,  2.]]);
    let b = arr1(&[1., -1., 2., 1.]);
    bench.iter(|| ndarray::linalg::least_squares(&xs, &b));
}
<commit_msg>Fix compliation of benches<commit_after>#![allow(uppercase_variables)]
#![allow(unused_imports)]

extern crate test;
extern crate ndarray;

use ndarray::{Array, S, Si};
use ndarray::{arr0, arr1, arr2};

use test::black_box;

#[bench]
fn bench_std_add(bench: &mut test::Bencher)
{
    let mut a = arr2::<f32, _>(&[[1., 2., 2.],
                              [3., 4., 4.],
                              [3., 4., 4.],
                              [3., 4., 4.],
                              [5., 6., 6.]]);
    let b = a.clone();
    bench.iter(|| a.iadd(&b));
}

#[bench]
fn bench_std_iter_1d(bench: &mut test::Bencher)
{
    let a = arr1::<f32>(&[1., 2., 2.,
                         3., 4., 4.,
                         3., 4., 4.,
                         3., 4., 4.,
                         5., 6., 6.]);
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_1d_raw(bench: &mut test::Bencher)
{
    let a = arr1::<f32>(&[1., 2., 2.,
                         3., 4., 4.,
                         3., 4., 4.,
                         3., 4., 4.,
                         5., 6., 6.]);
    bench.iter(|| for &elt in a.raw_data().iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_2d(bench: &mut test::Bencher)
{
    let a = arr2::<f32, _>(&[[1., 2., 2.],
                          [3., 4., 4.],
                          [3., 4., 4.],
                          [3., 4., 4.],
                          [5., 6., 6.]]);
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_1d_large(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024));
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_1d_raw_large(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024));
    bench.iter(|| for &elt in a.raw_data().iter() { black_box(elt) })
}

#[bench]
fn bench_std_iter_2d_large(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((16, 64));
    bench.iter(|| for &elt in a.iter() { black_box(elt) })
}

#[bench]
fn assign_scalar_2d(bench: &mut test::Bencher)
{
    let mut a = arr2::<f32, _>(&[[1., 2., 2.],
                              [3., 4., 4.],
                              [3., 4., 4.],
                              [3., 4., 4.],
                              [5., 6., 6.]]);
    a.swap_axes(0, 1);
    bench.iter(|| a.assign_scalar(&3.))
}

#[bench]
fn bench_iter_diag(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024, 1024));
    bench.iter(|| for elt in a.diag_iter() { black_box(elt) })
}

#[bench]
fn bench_row_iter(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024, 1024));
    let it = a.row_iter(17);
    bench.iter(|| for elt in it.clone() { black_box(elt) })
}

#[bench]
fn bench_col_iter(bench: &mut test::Bencher)
{
    let a = Array::<f32, _>::zeros((1024, 1024));
    bench.iter(|| for elt in a.col_iter(17) { black_box(elt) })
}

#[bench]
fn bench_mat_mul(bench: &mut test::Bencher)
{
    let a = arr2::<f32, _>(&[[1., 2., 2.],
                          [3., 4., 4.],
                          [3., 4., 4.],
                          [3., 4., 4.],
                          [5., 6., 6.]]);
    let mut at = a.clone();
    at.swap_axes(0, 1);
    bench.iter(|| at.mat_mul(&a));
}

#[bench]
fn lst_squares(bench: &mut test::Bencher)
{
    let xs = arr2::<f32, _>(&[[ 2.,  3.],
                           [-2., -1.],
                           [ 1.,  5.],
                           [-1.,  2.]]);
    let b = arr1(&[1., -1., 2., 1.]);
    bench.iter(|| ndarray::linalg::least_squares(&xs, &b));
}
<|endoftext|>


<commit_before>
extern crate petgraph;
extern crate test;

use petgraph::graph::Graph;

#[bench]
fn bench_inser(b: &mut test::Bencher) {
    let mut og = Graph::new();
    let fst = og.add_node(0);
    for x in range(1, 125) {
        let n = og.add_node(x);
        og.add_edge(fst, n, ());
    }
    b.iter(|| {
        og.add_node(1)
    })
}

#[bench]
fn bench_remove(b: &mut test::Bencher) {
    // removal is very slow in a big graph.
    // and this one doesn't even have many nodes.
    let mut og = Graph::new();
    let fst = og.add_node(0);
    let mut prev = fst;
    for x in range(1, 1250) {
        let n = og.add_node(x);
        og.add_edge(prev, n, ());
        prev = n;
    }
    //println!("{}", og);
    b.iter(|| {
        for _ in range(0, 100) {
            og.remove_node(fst);
        }
    })
}
<commit_msg>Fix compilation of benches<commit_after>
extern crate petgraph;
extern crate test;

use petgraph::graph::Graph;

#[bench]
fn bench_inser(b: &mut test::Bencher) {
    let mut og = Graph::new();
    let fst = og.add_node(0i32);
    for x in (1..125) {
        let n = og.add_node(x);
        og.add_edge(fst, n, ());
    }
    b.iter(|| {
        og.add_node(1)
    })
}

#[bench]
fn bench_remove(b: &mut test::Bencher) {
    // removal is very slow in a big graph.
    // and this one doesn't even have many nodes.
    let mut og = Graph::new();
    let fst = og.add_node(0i32);
    let mut prev = fst;
    for x in (1..1250) {
        let n = og.add_node(x);
        og.add_edge(prev, n, ());
        prev = n;
    }
    //println!("{}", og);
    b.iter(|| {
        for _ in (0 .. 100) {
            og.remove_node(fst);
        }
    })
}
<|endoftext|>