<commit_before>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::{HashMap, HashSet};

use pest::Error;
use pest::inputs::{Input, Span};
use pest::iterators::Pairs;

use quote::Ident;

use super::parser::{GrammarRule, ParserExpr, ParserNode, ParserRule};

pub fn validate_pairs<I: Input>(pairs: Pairs<GrammarRule, I>) -> Vec<Ident> {
    let mut rust_keywords = HashSet::new();
    rust_keywords.insert("abstract");
    rust_keywords.insert("alignof");
    rust_keywords.insert("as");
    rust_keywords.insert("become");
    rust_keywords.insert("box");
    rust_keywords.insert("break");
    rust_keywords.insert("const");
    rust_keywords.insert("continue");
    rust_keywords.insert("crate");
    rust_keywords.insert("do");
    rust_keywords.insert("else");
    rust_keywords.insert("enum");
    rust_keywords.insert("extern");
    rust_keywords.insert("false");
    rust_keywords.insert("final");
    rust_keywords.insert("fn");
    rust_keywords.insert("for");
    rust_keywords.insert("if");
    rust_keywords.insert("impl");
    rust_keywords.insert("in");
    rust_keywords.insert("let");
    rust_keywords.insert("loop");
    rust_keywords.insert("macro");
    rust_keywords.insert("match");
    rust_keywords.insert("mod");
    rust_keywords.insert("move");
    rust_keywords.insert("mut");
    rust_keywords.insert("offsetof");
    rust_keywords.insert("override");
    rust_keywords.insert("priv");
    rust_keywords.insert("proc");
    rust_keywords.insert("pure");
    rust_keywords.insert("pub");
    rust_keywords.insert("ref");
    rust_keywords.insert("return");
    rust_keywords.insert("Self");
    rust_keywords.insert("self");
    rust_keywords.insert("sizeof");
    rust_keywords.insert("static");
    rust_keywords.insert("struct");
    rust_keywords.insert("super");
    rust_keywords.insert("trait");
    rust_keywords.insert("true");
    rust_keywords.insert("type");
    rust_keywords.insert("typeof");
    rust_keywords.insert("unsafe");
    rust_keywords.insert("unsized");
    rust_keywords.insert("use");
    rust_keywords.insert("virtual");
    rust_keywords.insert("where");
    rust_keywords.insert("while");
    rust_keywords.insert("yield");

    let mut pest_keywords = HashSet::new();
    pest_keywords.insert("any");
    pest_keywords.insert("eoi");
    pest_keywords.insert("peek");
    pest_keywords.insert("pop");
    pest_keywords.insert("push");
    pest_keywords.insert("soi");

    let mut predefined = HashSet::new();
    predefined.insert("any");
    predefined.insert("eoi");
    predefined.insert("peek");
    predefined.insert("pop");
    predefined.insert("soi");

    let definitions: Vec<_> = pairs.clone()
                                   .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                   .map(|pair| {
                                       pair.into_inner()
                                           .next()
                                           .unwrap()
                                           .into_span()
                                   }).collect();
    let called_rules: Vec<_> = pairs.clone()
                                    .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                    .flat_map(|pair| {
                                        pair.into_inner().flatten().skip(1).filter(|pair| {
                                            pair.as_rule() == GrammarRule::identifier
                                        }).map(|pair| {
                                            pair.into_span()
                                        })
                                    }).collect();

    let mut errors = vec![];

    errors.extend(validate_rust_keywords(&definitions, &rust_keywords));
    errors.extend(validate_pest_keywords(&definitions, &pest_keywords));
    errors.extend(validate_already_defined(&definitions));
    errors.extend(validate_undefined(&definitions, &called_rules, &predefined));

    let errors = errors.into_iter().map(|error| {
        format!("grammar error\n\n{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("{}", errors);
    }

    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();
    let called_rules: HashSet<_> = called_rules.iter().map(|span| span.as_str()).collect();

    let defaults = called_rules.difference(&definitions);

    defaults.into_iter().map(|string| Ident::new(*string)).collect()
}

pub fn validate_rust_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    rust_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if rust_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a rust keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_pest_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    pest_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if pest_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a pest keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_already_defined<I: Input>(
    definitions: &Vec<Span<I>>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let mut defined = HashSet::new();

    for definition in definitions {
        let name = definition.as_str();

        if defined.contains(&name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} already defined", name),
                span: definition.clone()
            })
        } else {
            defined.insert(name);
        }
    }

    errors
}

pub fn validate_undefined<I: Input>(
    definitions: &Vec<Span<I>>,
    called_rules: &Vec<Span<I>>,
    predefined: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();

    for rule in called_rules {
        let name = rule.as_str();

        if !definitions.contains(name) && !predefined.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} is undefined", name),
                span: rule.clone()
            })
        }
    }

    errors
}

pub fn validate_ast<I: Input>(rules: &Vec<ParserRule<I>>) {
    let mut errors = vec![];

    errors.extend(validate_left_recursion(rules));

    errors.sort_by_key(|error| {
        match *error {
            Error::CustomErrorSpan { ref span, .. } => span.clone(),
            _ => unreachable!()
        }
    });

    let errors = errors.into_iter().map(|error| {
        format!("{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("grammar error\n\n{}", errors);
    }
}

fn validate_left_recursion<I: Input>(rules: &Vec<ParserRule<I>>) -> Vec<Error<GrammarRule, I>> {
    left_recursion(to_hash_map(rules))
}

fn to_hash_map<I: Input>(rules: &Vec<ParserRule<I>>) -> HashMap<Ident, &ParserNode<I>> {
    let mut hash_map = HashMap::new();

    for rule in rules {
        hash_map.insert(rule.name.clone(), &rule.node);
    }

    hash_map
}

fn left_recursion<I: Input>(rules: HashMap<Ident, &ParserNode<I>>) -> Vec<Error<GrammarRule, I>> {
    fn check_expr<I: Input>(
        mut names: HashSet<Ident>,
        node: &ParserNode<I>,
        rules: &HashMap<Ident, &ParserNode<I>>
    ) -> Option<Error<GrammarRule, I>> {
        match node.expr {
            ParserExpr::Ident(ref other)  => {
                if names.contains(other) {
                    return Some(Error::CustomErrorSpan {
                        message: format!("rule {} is left-recursive; pest::prec_climber might be \
                                          useful in this case", node.span.as_str()),
                        span: node.span.clone()
                    });
                }

                names.insert(other.clone());

                if let Some(node) = rules.get(other) {
                    check_expr(names, node, rules)
                } else {
                    None
                }
            },
            ParserExpr::Seq(ref lhs, _) => check_expr(names, &lhs, rules),
            ParserExpr::Choice(ref lhs, _) => check_expr(names, &lhs, rules),
            ParserExpr::Rep(ref node) => check_expr(names, &node, rules),
            ParserExpr::RepOnce(ref node) => check_expr(names, &node, rules),
            ParserExpr::Opt(ref node) => check_expr(names, &node, rules),
            ParserExpr::PosPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::NegPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::Push(ref node) => check_expr(names, &node, rules),
            _ => None
        }
    }

    let mut errors = vec![];

    for (ref name, ref node) in &rules {
        let mut names = HashSet::new();

        names.insert((*name).clone());

        if let Some(error) = check_expr(names, node, &rules) {
            errors.push(error);
        }
    }

    errors
}

#[cfg(test)]
mod tests {
    use pest::Parser;

    use super::*;
    use super::super::parser::{GrammarParser, consume_rules};

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | let = { \"a\" }
  | ^-^
  |
  = let is a rust keyword")]
    fn rust_keyword() {
        let input = "let = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | any = { \"a\" }
  | ^-^
  |
  = any is a pest keyword")]
    fn pest_keyword() {
        let input = "any = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:13
  |
1 | a = { \"a\" } a = { \"a\" }
  |             ^
  |
  = rule a already defined")]
    fn already_defined() {
        let input = "a = { \"a\" } a = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b }
  |       ^
  |
  = rule b is undefined")]
    fn undefined() {
        let input = "a = { b }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { a }
  |       ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn simple_left_recursion() {
        let input = "a = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b } b = { a }
  |       ^
  |
  = rule b is left-recursive; pest::prec_climber might be useful in this case

 --> 1:17
  |
1 | a = { b } b = { a }
  |                 ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn indirect_left_recursion() {
        let input = "a = { b } b = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }
}
<commit_msg>Added non-failing left-recursion validation.<commit_after>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::{HashMap, HashSet};

use pest::Error;
use pest::inputs::{Input, Span};
use pest::iterators::Pairs;

use quote::Ident;

use super::parser::{GrammarRule, ParserExpr, ParserNode, ParserRule};

pub fn validate_pairs<I: Input>(pairs: Pairs<GrammarRule, I>) -> Vec<Ident> {
    let mut rust_keywords = HashSet::new();
    rust_keywords.insert("abstract");
    rust_keywords.insert("alignof");
    rust_keywords.insert("as");
    rust_keywords.insert("become");
    rust_keywords.insert("box");
    rust_keywords.insert("break");
    rust_keywords.insert("const");
    rust_keywords.insert("continue");
    rust_keywords.insert("crate");
    rust_keywords.insert("do");
    rust_keywords.insert("else");
    rust_keywords.insert("enum");
    rust_keywords.insert("extern");
    rust_keywords.insert("false");
    rust_keywords.insert("final");
    rust_keywords.insert("fn");
    rust_keywords.insert("for");
    rust_keywords.insert("if");
    rust_keywords.insert("impl");
    rust_keywords.insert("in");
    rust_keywords.insert("let");
    rust_keywords.insert("loop");
    rust_keywords.insert("macro");
    rust_keywords.insert("match");
    rust_keywords.insert("mod");
    rust_keywords.insert("move");
    rust_keywords.insert("mut");
    rust_keywords.insert("offsetof");
    rust_keywords.insert("override");
    rust_keywords.insert("priv");
    rust_keywords.insert("proc");
    rust_keywords.insert("pure");
    rust_keywords.insert("pub");
    rust_keywords.insert("ref");
    rust_keywords.insert("return");
    rust_keywords.insert("Self");
    rust_keywords.insert("self");
    rust_keywords.insert("sizeof");
    rust_keywords.insert("static");
    rust_keywords.insert("struct");
    rust_keywords.insert("super");
    rust_keywords.insert("trait");
    rust_keywords.insert("true");
    rust_keywords.insert("type");
    rust_keywords.insert("typeof");
    rust_keywords.insert("unsafe");
    rust_keywords.insert("unsized");
    rust_keywords.insert("use");
    rust_keywords.insert("virtual");
    rust_keywords.insert("where");
    rust_keywords.insert("while");
    rust_keywords.insert("yield");

    let mut pest_keywords = HashSet::new();
    pest_keywords.insert("any");
    pest_keywords.insert("eoi");
    pest_keywords.insert("peek");
    pest_keywords.insert("pop");
    pest_keywords.insert("push");
    pest_keywords.insert("soi");

    let mut predefined = HashSet::new();
    predefined.insert("any");
    predefined.insert("eoi");
    predefined.insert("peek");
    predefined.insert("pop");
    predefined.insert("soi");

    let definitions: Vec<_> = pairs.clone()
                                   .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                   .map(|pair| {
                                       pair.into_inner()
                                           .next()
                                           .unwrap()
                                           .into_span()
                                   }).collect();
    let called_rules: Vec<_> = pairs.clone()
                                    .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                    .flat_map(|pair| {
                                        pair.into_inner().flatten().skip(1).filter(|pair| {
                                            pair.as_rule() == GrammarRule::identifier
                                        }).map(|pair| {
                                            pair.into_span()
                                        })
                                    }).collect();

    let mut errors = vec![];

    errors.extend(validate_rust_keywords(&definitions, &rust_keywords));
    errors.extend(validate_pest_keywords(&definitions, &pest_keywords));
    errors.extend(validate_already_defined(&definitions));
    errors.extend(validate_undefined(&definitions, &called_rules, &predefined));

    let errors = errors.into_iter().map(|error| {
        format!("grammar error\n\n{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("{}", errors);
    }

    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();
    let called_rules: HashSet<_> = called_rules.iter().map(|span| span.as_str()).collect();

    let defaults = called_rules.difference(&definitions);

    defaults.into_iter().map(|string| Ident::new(*string)).collect()
}

pub fn validate_rust_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    rust_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if rust_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a rust keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_pest_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    pest_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if pest_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a pest keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_already_defined<I: Input>(
    definitions: &Vec<Span<I>>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let mut defined = HashSet::new();

    for definition in definitions {
        let name = definition.as_str();

        if defined.contains(&name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} already defined", name),
                span: definition.clone()
            })
        } else {
            defined.insert(name);
        }
    }

    errors
}

pub fn validate_undefined<I: Input>(
    definitions: &Vec<Span<I>>,
    called_rules: &Vec<Span<I>>,
    predefined: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();

    for rule in called_rules {
        let name = rule.as_str();

        if !definitions.contains(name) && !predefined.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} is undefined", name),
                span: rule.clone()
            })
        }
    }

    errors
}

pub fn validate_ast<I: Input>(rules: &Vec<ParserRule<I>>) {
    let mut errors = vec![];

    errors.extend(validate_left_recursion(rules));

    errors.sort_by_key(|error| {
        match *error {
            Error::CustomErrorSpan { ref span, .. } => span.clone(),
            _ => unreachable!()
        }
    });

    let errors = errors.into_iter().map(|error| {
        format!("{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("grammar error\n\n{}", errors);
    }
}

fn validate_left_recursion<I: Input>(rules: &Vec<ParserRule<I>>) -> Vec<Error<GrammarRule, I>> {
    left_recursion(to_hash_map(rules))
}

fn to_hash_map<I: Input>(rules: &Vec<ParserRule<I>>) -> HashMap<Ident, &ParserNode<I>> {
    let mut hash_map = HashMap::new();

    for rule in rules {
        hash_map.insert(rule.name.clone(), &rule.node);
    }

    hash_map
}

fn is_non_failing<I: Input>(expr: &ParserExpr<I>) -> bool {
    match *expr {
        ParserExpr::Str(ref string) => string == "",
        ParserExpr::Opt(_) => true,
        ParserExpr::Rep(_) => true,
        ParserExpr::Seq(ref lhs, ref rhs) => {
            is_non_failing(&lhs.expr) && is_non_failing(&rhs.expr)
        }
        ParserExpr::Choice(ref lhs, ref rhs) => {
            is_non_failing(&lhs.expr) || is_non_failing(&rhs.expr)
        }
        _ => false
    }
}

fn left_recursion<I: Input>(rules: HashMap<Ident, &ParserNode<I>>) -> Vec<Error<GrammarRule, I>> {
    fn check_expr<I: Input>(
        mut names: HashSet<Ident>,
        node: &ParserNode<I>,
        rules: &HashMap<Ident, &ParserNode<I>>
    ) -> Option<Error<GrammarRule, I>> {
        match node.expr {
            ParserExpr::Ident(ref other)  => {
                if names.contains(other) {
                    return Some(Error::CustomErrorSpan {
                        message: format!("rule {} is left-recursive; pest::prec_climber might be \
                                          useful in this case", node.span.as_str()),
                        span: node.span.clone()
                    });
                }

                names.insert(other.clone());

                if let Some(node) = rules.get(other) {
                    check_expr(names, node, rules)
                } else {
                    None
                }
            },
            ParserExpr::Seq(ref lhs, ref rhs) => {
                if is_non_failing(&lhs.expr) {
                    check_expr(names, rhs, rules)
                } else {
                    check_expr(names, lhs, rules)
                }
            }
            ParserExpr::Choice(ref lhs, _) => check_expr(names, &lhs, rules),
            ParserExpr::Rep(ref node) => check_expr(names, &node, rules),
            ParserExpr::RepOnce(ref node) => check_expr(names, &node, rules),
            ParserExpr::Opt(ref node) => check_expr(names, &node, rules),
            ParserExpr::PosPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::NegPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::Push(ref node) => check_expr(names, &node, rules),
            _ => None
        }
    }

    let mut errors = vec![];

    for (ref name, ref node) in &rules {
        let mut names = HashSet::new();

        names.insert((*name).clone());

        if let Some(error) = check_expr(names, node, &rules) {
            errors.push(error);
        }
    }

    errors
}

#[cfg(test)]
mod tests {
    use pest::Parser;

    use super::*;
    use super::super::parser::{GrammarParser, consume_rules};

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | let = { \"a\" }
  | ^-^
  |
  = let is a rust keyword")]
    fn rust_keyword() {
        let input = "let = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | any = { \"a\" }
  | ^-^
  |
  = any is a pest keyword")]
    fn pest_keyword() {
        let input = "any = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:13
  |
1 | a = { \"a\" } a = { \"a\" }
  |             ^
  |
  = rule a already defined")]
    fn already_defined() {
        let input = "a = { \"a\" } a = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b }
  |       ^
  |
  = rule b is undefined")]
    fn undefined() {
        let input = "a = { b }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { a }
  |       ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn simple_left_recursion() {
        let input = "a = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b } b = { a }
  |       ^
  |
  = rule b is left-recursive; pest::prec_climber might be useful in this case

 --> 1:17
  |
1 | a = { b } b = { a }
  |                 ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn indirect_left_recursion() {
        let input = "a = { b } b = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:39
  |
1 | a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }
  |                                       ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn non_failing_left_recursion() {
        let input = "a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    fn valid_recursion() {
        let input = "a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"b\") ~ a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }
}
<|endoftext|>


<commit_before>//! This crate provides the `#[quickcheck]` attribute. Its use is
//! documented in the `quickcheck` crate.

#![crate_name = "quickcheck_macros"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![doc(html_root_url = "http://burntsushi.net/rustdoc/quickcheck")]

#![feature(plugin_registrar, managed_boxes)]

extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::codemap;
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, Modifier};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use rustc::plugin::Registry;

/// For the `#[quickcheck]` attribute. Do not use.
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("quickcheck"),
                                  Modifier(box expand_meta_quickcheck));
}

/// Expands the `#[quickcheck]` attribute.
///
/// Expands:
/// ```
/// #[quickcheck]
/// fn check_something(_: uint) -> bool {
///     true
/// }
/// ```
/// to:
/// ```
/// #[test]
/// fn check_something() {
///     fn check_something(_: uint) -> bool {
///         true
///     }
///     ::quickcheck::quickcheck(check_something)
/// }
/// ```
fn expand_meta_quickcheck(cx: &mut ExtCtxt,
                          span: codemap::Span,
                          _: &ast::MetaItem,
                          item: P<ast::Item>) -> P<ast::Item> {
    match item.node {
        ast::ItemFn(..) | ast::ItemStatic(..) => {
            // Copy original function without attributes
            let prop = P(ast::Item {attrs: Vec::new(), ..(*item).clone()});
            // ::quickcheck::quickcheck
            let check_ident = token::str_to_ident("quickcheck");
            let check_path = vec!(check_ident, check_ident);
            // Wrap original function in new outer function, calling ::quickcheck::quickcheck()
            let fn_decl = P(codemap::respan(span, ast::DeclItem(prop.clone())));
            let inner_fn = P(codemap::respan(span, ast::StmtDecl(fn_decl, ast::DUMMY_NODE_ID)));
            let inner_ident = cx.expr_ident(span, prop.ident);
            let check_call = cx.expr_call_global(span, check_path, vec![inner_ident]);
            let body = cx.block(span, vec![inner_fn], Some(check_call));
            let test = cx.item_fn(span, item.ident, Vec::new(), cx.ty_nil(), body);

            // Copy attributes from original function
            let mut attrs = item.attrs.clone();
            // Add #[test] attribute
            attrs.push(cx.attribute(span, cx.meta_word(span, token::intern_and_get_ident("test"))));
            // Attach the attributes to the outer function
            P(ast::Item {attrs: attrs, ..(*test).clone()})
        },
        _ => {
            cx.span_err(span, "#[quickcheck] only supported on statics and functions");
            item
        }
    }
}
<commit_msg>`managed_boxes` feature gate (an Gc) has been removed from the language<commit_after>//! This crate provides the `#[quickcheck]` attribute. Its use is
//! documented in the `quickcheck` crate.

#![crate_name = "quickcheck_macros"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![doc(html_root_url = "http://burntsushi.net/rustdoc/quickcheck")]

#![feature(plugin_registrar)]

extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::codemap;
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, Modifier};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use rustc::plugin::Registry;

/// For the `#[quickcheck]` attribute. Do not use.
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("quickcheck"),
                                  Modifier(box expand_meta_quickcheck));
}

/// Expands the `#[quickcheck]` attribute.
///
/// Expands:
/// ```
/// #[quickcheck]
/// fn check_something(_: uint) -> bool {
///     true
/// }
/// ```
/// to:
/// ```
/// #[test]
/// fn check_something() {
///     fn check_something(_: uint) -> bool {
///         true
///     }
///     ::quickcheck::quickcheck(check_something)
/// }
/// ```
fn expand_meta_quickcheck(cx: &mut ExtCtxt,
                          span: codemap::Span,
                          _: &ast::MetaItem,
                          item: P<ast::Item>) -> P<ast::Item> {
    match item.node {
        ast::ItemFn(..) | ast::ItemStatic(..) => {
            // Copy original function without attributes
            let prop = P(ast::Item {attrs: Vec::new(), ..(*item).clone()});
            // ::quickcheck::quickcheck
            let check_ident = token::str_to_ident("quickcheck");
            let check_path = vec!(check_ident, check_ident);
            // Wrap original function in new outer function, calling ::quickcheck::quickcheck()
            let fn_decl = P(codemap::respan(span, ast::DeclItem(prop.clone())));
            let inner_fn = P(codemap::respan(span, ast::StmtDecl(fn_decl, ast::DUMMY_NODE_ID)));
            let inner_ident = cx.expr_ident(span, prop.ident);
            let check_call = cx.expr_call_global(span, check_path, vec![inner_ident]);
            let body = cx.block(span, vec![inner_fn], Some(check_call));
            let test = cx.item_fn(span, item.ident, Vec::new(), cx.ty_nil(), body);

            // Copy attributes from original function
            let mut attrs = item.attrs.clone();
            // Add #[test] attribute
            attrs.push(cx.attribute(span, cx.meta_word(span, token::intern_and_get_ident("test"))));
            // Attach the attributes to the outer function
            P(ast::Item {attrs: attrs, ..(*test).clone()})
        },
        _ => {
            cx.span_err(span, "#[quickcheck] only supported on statics and functions");
            item
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::gl_context::GLContextFactory;
use crate::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThread};
use canvas_traits::webgl::webgl_channel;
use canvas_traits::webgl::DOMToTextureCommand;
use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler};
use euclid::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use gleam::gl::Gl;
use servo_config::prefs::PREFS;
use std::rc::Rc;

/// WebGL Threading API entry point that lives in the constellation.
pub struct WebGLThreads(WebGLSender<WebGLMsg>);

impl WebGLThreads {
    /// Creates a new WebGLThreads object
    pub fn new(
        gl_factory: GLContextFactory,
        webrender_gl: Rc<dyn gl::Gl>,
        webrender_api_sender: webrender_api::RenderApiSender,
        webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
    ) -> (
        WebGLThreads,
        Box<dyn webrender::ExternalImageHandler>,
        Option<Box<dyn webrender::OutputImageHandler>>,
    ) {
        // This implementation creates a single `WebGLThread` for all the pipelines.
        let channel = WebGLThread::start(
            gl_factory,
            webrender_api_sender,
            webvr_compositor.map(|c| WebVRRenderWrapper(c)),
        );
        let output_handler = if PREFS.is_dom_to_texture_enabled() {
            Some(Box::new(OutputHandler::new(
                webrender_gl.clone(),
                channel.clone(),
            )))
        } else {
            None
        };
        let external =
            WebGLExternalImageHandler::new(WebGLExternalImages::new(webrender_gl, channel.clone()));
        (
            WebGLThreads(channel),
            Box::new(external),
            output_handler.map(|b| b as Box<_>),
        )
    }

    /// Gets the WebGLThread handle for each script pipeline.
    pub fn pipeline(&self) -> WebGLPipeline {
        // This mode creates a single thread, so the existing WebGLChan is just cloned.
        WebGLPipeline(WebGLChan(self.0.clone()))
    }

    /// Sends a exit message to close the WebGLThreads and release all WebGLContexts.
    pub fn exit(&self) -> Result<(), &'static str> {
        self.0
            .send(WebGLMsg::Exit)
            .map_err(|_| "Failed to send Exit message")
    }
}

/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
struct WebGLExternalImages {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<(u32, Size2D<i32>, usize)>,
        WebGLReceiver<(u32, Size2D<i32>, usize)>,
    ),
}

impl WebGLExternalImages {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
        }
    }
}

impl WebGLExternalImageApi for WebGLExternalImages {
    fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>) {
        // WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue.
        // The WebGLMsg::Lock message inserts a fence in the WebGL command queue.
        self.webgl_channel
            .send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone()))
            .unwrap();
        let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap();
        // The next glWaitSync call is run on the WR thread and it's used to synchronize the two
        // flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture.
        // glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem.
        self.webrender_gl
            .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
        (image_id, size)
    }

    fn unlock(&mut self, ctx_id: WebGLContextId) {
        self.webgl_channel.send(WebGLMsg::Unlock(ctx_id)).unwrap();
    }
}

/// Wrapper to send WebVR commands used in `WebGLThread`.
struct WebVRRenderWrapper(Box<dyn WebVRRenderHandler>);

impl WebVRRenderHandler for WebVRRenderWrapper {
    fn handle(&mut self, gl: &dyn Gl, command: WebVRCommand, texture: Option<(u32, Size2D<i32>)>) {
        self.0.handle(gl, command, texture);
    }
}

/// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait.
type OutputHandlerData = Option<(u32, Size2D<i32>)>;
struct OutputHandler {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<OutputHandlerData>,
        WebGLReceiver<OutputHandlerData>,
    ),
    sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>,
}

impl OutputHandler {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
            sync_objects: Default::default(),
        }
    }
}

/// Bridge between the WR frame outputs and WebGL to implement DOMToTexture synchronization.
impl webrender::OutputImageHandler for OutputHandler {
    fn lock(
        &mut self,
        id: webrender_api::PipelineId,
    ) -> Option<(u32, webrender_api::DeviceIntSize)> {
        // Insert a fence in the WR command queue
        let gl_sync = self
            .webrender_gl
            .fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
        // The lock command adds a WaitSync call on the WebGL command flow.
        let command = DOMToTextureCommand::Lock(id, gl_sync as usize, self.lock_channel.0.clone());
        self.webgl_channel
            .send(WebGLMsg::DOMToTextureCommand(command))
            .unwrap();
        self.lock_channel.1.recv().unwrap().map(|(tex_id, size)| {
            (
                tex_id,
                webrender_api::DeviceIntSize::new(size.width, size.height),
            )
        })
    }

    fn unlock(&mut self, id: webrender_api::PipelineId) {
        if let Some(gl_sync) = self.sync_objects.remove(&id) {
            // Flush the Sync object into the GPU's command queue to guarantee that it it's signaled.
            self.webrender_gl.flush();
            // Mark the sync object for deletion.
            self.webrender_gl.delete_sync(gl_sync);
        }
    }
}
<commit_msg>Auto merge of #22945 - peterjoel:patch-1, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::gl_context::GLContextFactory;
use crate::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThread};
use canvas_traits::webgl::webgl_channel;
use canvas_traits::webgl::DOMToTextureCommand;
use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler};
use euclid::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use servo_config::prefs::PREFS;
use std::rc::Rc;

/// WebGL Threading API entry point that lives in the constellation.
pub struct WebGLThreads(WebGLSender<WebGLMsg>);

impl WebGLThreads {
    /// Creates a new WebGLThreads object
    pub fn new(
        gl_factory: GLContextFactory,
        webrender_gl: Rc<dyn gl::Gl>,
        webrender_api_sender: webrender_api::RenderApiSender,
        webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
    ) -> (
        WebGLThreads,
        Box<dyn webrender::ExternalImageHandler>,
        Option<Box<dyn webrender::OutputImageHandler>>,
    ) {
        // This implementation creates a single `WebGLThread` for all the pipelines.
        let channel = WebGLThread::start(
            gl_factory,
            webrender_api_sender,
            webvr_compositor.map(|c| WebVRRenderWrapper(c)),
        );
        let output_handler = if PREFS.is_dom_to_texture_enabled() {
            Some(Box::new(OutputHandler::new(
                webrender_gl.clone(),
                channel.clone(),
            )))
        } else {
            None
        };
        let external =
            WebGLExternalImageHandler::new(WebGLExternalImages::new(webrender_gl, channel.clone()));
        (
            WebGLThreads(channel),
            Box::new(external),
            output_handler.map(|b| b as Box<_>),
        )
    }

    /// Gets the WebGLThread handle for each script pipeline.
    pub fn pipeline(&self) -> WebGLPipeline {
        // This mode creates a single thread, so the existing WebGLChan is just cloned.
        WebGLPipeline(WebGLChan(self.0.clone()))
    }

    /// Sends a exit message to close the WebGLThreads and release all WebGLContexts.
    pub fn exit(&self) -> Result<(), &'static str> {
        self.0
            .send(WebGLMsg::Exit)
            .map_err(|_| "Failed to send Exit message")
    }
}

/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
struct WebGLExternalImages {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<(u32, Size2D<i32>, usize)>,
        WebGLReceiver<(u32, Size2D<i32>, usize)>,
    ),
}

impl WebGLExternalImages {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
        }
    }
}

impl WebGLExternalImageApi for WebGLExternalImages {
    fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>) {
        // WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue.
        // The WebGLMsg::Lock message inserts a fence in the WebGL command queue.
        self.webgl_channel
            .send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone()))
            .unwrap();
        let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap();
        // The next glWaitSync call is run on the WR thread and it's used to synchronize the two
        // flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture.
        // glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem.
        self.webrender_gl
            .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
        (image_id, size)
    }

    fn unlock(&mut self, ctx_id: WebGLContextId) {
        self.webgl_channel.send(WebGLMsg::Unlock(ctx_id)).unwrap();
    }
}

/// Wrapper to send WebVR commands used in `WebGLThread`.
struct WebVRRenderWrapper(Box<dyn WebVRRenderHandler>);

impl WebVRRenderHandler for WebVRRenderWrapper {
    fn handle(
        &mut self,
        gl: &dyn gl::Gl,
        command: WebVRCommand,
        texture: Option<(u32, Size2D<i32>)>,
    ) {
        self.0.handle(gl, command, texture);
    }
}

/// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait.
type OutputHandlerData = Option<(u32, Size2D<i32>)>;
struct OutputHandler {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<OutputHandlerData>,
        WebGLReceiver<OutputHandlerData>,
    ),
    sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>,
}

impl OutputHandler {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
            sync_objects: Default::default(),
        }
    }
}

/// Bridge between the WR frame outputs and WebGL to implement DOMToTexture synchronization.
impl webrender::OutputImageHandler for OutputHandler {
    fn lock(
        &mut self,
        id: webrender_api::PipelineId,
    ) -> Option<(u32, webrender_api::DeviceIntSize)> {
        // Insert a fence in the WR command queue
        let gl_sync = self
            .webrender_gl
            .fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
        // The lock command adds a WaitSync call on the WebGL command flow.
        let command = DOMToTextureCommand::Lock(id, gl_sync as usize, self.lock_channel.0.clone());
        self.webgl_channel
            .send(WebGLMsg::DOMToTextureCommand(command))
            .unwrap();
        self.lock_channel.1.recv().unwrap().map(|(tex_id, size)| {
            (
                tex_id,
                webrender_api::DeviceIntSize::new(size.width, size.height),
            )
        })
    }

    fn unlock(&mut self, id: webrender_api::PipelineId) {
        if let Some(gl_sync) = self.sync_objects.remove(&id) {
            // Flush the Sync object into the GPU's command queue to guarantee that it it's signaled.
            self.webrender_gl.flush();
            // Mark the sync object for deletion.
            self.webrender_gl.delete_sync(gl_sync);
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cssparser::ToCss;
use element_state::ElementState;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selector_parser::{attr_equals_selector_is_shareable, attr_exists_selector_is_shareable};
use selectors::parser::AttrSelector;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

/// NOTE: The boolean field represents whether this element is an anonymous box.
///
/// This is just for convenience, instead of recomputing it. Also, note that
/// Atom is always a static atom, so if space is a concern, we can use the
/// raw pointer and use the lower bit to represent it without space overhead.
///
/// FIXME(emilio): we know all these atoms are static. Patches are starting to
/// pile up, but a further potential optimisation is generating bindings without
/// `-no-gen-bitfield-methods` (that was removed to compile on stable, but it no
/// longer depends on it), and using the raw *mut nsIAtom (properly asserting
/// we're a static atom).
///
/// This should allow us to avoid random FFI overhead when cloning/dropping
/// pseudos.
///
/// Also, we can further optimize PartialEq and hash comparing/hashing only the
/// atoms.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElement(Atom, bool);

impl PseudoElement {
    #[inline]
    pub fn as_atom(&self) -> &Atom {
        &self.0
    }

    #[inline]
    fn is_anon_box(&self) -> bool {
        self.1
    }

    #[inline]
    pub fn from_atom_unchecked(atom: Atom, is_anon_box: bool) -> Self {
        if cfg!(debug_assertions) {
            // Do the check on debug regardless.
            match Self::from_atom(&*atom, true) {
                Some(pseudo) => {
                    assert_eq!(pseudo.is_anon_box(), is_anon_box);
                    return pseudo;
                }
                None => panic!("Unknown pseudo: {:?}", atom),
            }
        }

        PseudoElement(atom, is_anon_box)
    }

    #[inline]
    fn from_atom(atom: &WeakAtom, _in_ua: bool) -> Option<Self> {
        macro_rules! pseudo_element {
            ($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
                if atom == &*$atom {
                    return Some(PseudoElement($atom, $is_anon_box));
                }
            }}
        }

        include!("generated/gecko_pseudo_element_helper.rs");

        None
    }

    #[inline]
    fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
        use std::ascii::AsciiExt;
        macro_rules! pseudo_element {
            ($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
                if !$is_anon_box || in_ua_stylesheet {
                    if s.eq_ignore_ascii_case(&$pseudo_str_with_colon[1..]) {
                        return Some(PseudoElement($atom, $is_anon_box))
                    }
                }
            }}
        }

        include!("generated/gecko_pseudo_element_helper.rs");

        None
    }
}

impl ToCss for PseudoElement {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        // FIXME: why does the atom contain one colon? Pseudo-element has two
        debug_assert!(self.0.as_slice().starts_with(&[b':' as u16]) &&
                      !self.0.as_slice().starts_with(&[b':' as u16, b':' as u16]));
        try!(dest.write_char(':'));
        write!(dest, "{}", self.0)
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum NonTSPseudoClass {
    AnyLink,
    Link,
    Visited,
    Active,
    Focus,
    Fullscreen,
    Hover,
    Enabled,
    Disabled,
    Checked,
    Indeterminate,
    ReadWrite,
    ReadOnly,
}

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use self::NonTSPseudoClass::*;
        dest.write_str(match *self {
            AnyLink => ":any-link",
            Link => ":link",
            Visited => ":visited",
            Active => ":active",
            Focus => ":focus",
            Fullscreen => ":fullscreen",
            Hover => ":hover",
            Enabled => ":enabled",
            Disabled => ":disabled",
            Checked => ":checked",
            Indeterminate => ":indeterminate",
            ReadWrite => ":read-write",
            ReadOnly => ":read-only",
        })
    }
}

impl NonTSPseudoClass {
    pub fn state_flag(&self) -> ElementState {
        use element_state::*;
        use self::NonTSPseudoClass::*;
        match *self {
            Active => IN_ACTIVE_STATE,
            Focus => IN_FOCUS_STATE,
            Fullscreen => IN_FULLSCREEN_STATE,
            Hover => IN_HOVER_STATE,
            Enabled => IN_ENABLED_STATE,
            Disabled => IN_DISABLED_STATE,
            Checked => IN_CHECKED_STATE,
            Indeterminate => IN_INDETERMINATE_STATE,
            ReadOnly | ReadWrite => IN_READ_WRITE_STATE,

            AnyLink |
            Link |
            Visited => ElementState::empty(),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct SelectorImpl;

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElement = PseudoElement;
    type NonTSPseudoClass = NonTSPseudoClass;

    fn attr_exists_selector_is_shareable(attr_selector: &AttrSelector<Self>) -> bool {
        attr_exists_selector_is_shareable(attr_selector)
    }

    fn attr_equals_selector_is_shareable(attr_selector: &AttrSelector<Self>,
                                         value: &Self::AttrValue) -> bool {
        attr_equals_selector_is_shareable(attr_selector, value)
    }
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        use self::NonTSPseudoClass::*;
        let pseudo_class = match_ignore_ascii_case! { &name,
            "any-link" => AnyLink,
            "link" => Link,
            "visited" => Visited,
            "active" => Active,
            "focus" => Focus,
            "fullscreen" => Fullscreen,
            "hover" => Hover,
            "enabled" => Enabled,
            "disabled" => Disabled,
            "checked" => Checked,
            "indeterminate" => Indeterminate,
            "read-write" => ReadWrite,
            "read-only" => ReadOnly,
            _ => return Err(())
        };

        Ok(pseudo_class)
    }

    fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
        match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
            Some(pseudo) => Ok(pseudo),
            None => Err(()),
        }
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        if Self::pseudo_is_before_or_after(pseudo) {
            return PseudoElementCascadeType::Eager
        }

        if pseudo.is_anon_box() {
            return PseudoElementCascadeType::Precomputed
        }

        PseudoElementCascadeType::Lazy
    }

    #[inline]
    pub fn each_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement)
    {
        macro_rules! pseudo_element {
            ($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
                fun(PseudoElement($atom, $is_anon_box));
            }}
        }

        include!("generated/gecko_pseudo_element_helper.rs")
    }

    #[inline]
    pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
        *pseudo.as_atom() == atom!(":before") ||
        *pseudo.as_atom() == atom!(":after")
    }

    #[inline]
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<commit_msg>style: Document Gecko's selector parser.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Gecko-specific bits for selector-parsing.

use cssparser::ToCss;
use element_state::ElementState;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selector_parser::{attr_equals_selector_is_shareable, attr_exists_selector_is_shareable};
use selectors::parser::AttrSelector;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

/// A representation of a CSS pseudo-element.
///
/// In Gecko, we represent pseudo-elements as plain `Atom`s.
///
/// The boolean field represents whether this element is an anonymous box. This
/// is just for convenience, instead of recomputing it.
///
/// Also, note that the `Atom` member is always a static atom, so if space is a
/// concern, we can use the raw pointer and use the lower bit to represent it
/// without space overhead.
///
/// FIXME(emilio): we know all these atoms are static. Patches are starting to
/// pile up, but a further potential optimisation is generating bindings without
/// `-no-gen-bitfield-methods` (that was removed to compile on stable, but it no
/// longer depends on it), and using the raw *mut nsIAtom (properly asserting
/// we're a static atom).
///
/// This should allow us to avoid random FFI overhead when cloning/dropping
/// pseudos.
///
/// Also, we can further optimize PartialEq and hash comparing/hashing only the
/// atoms.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElement(Atom, bool);

impl PseudoElement {
    /// Get the pseudo-element as an atom.
    #[inline]
    pub fn as_atom(&self) -> &Atom {
        &self.0
    }

    /// Whether this pseudo-element is an anonymous box.
    #[inline]
    fn is_anon_box(&self) -> bool {
        self.1
    }

    /// Construct a pseudo-element from an `Atom`, receiving whether it is also
    /// an anonymous box, and don't check it on release builds.
    ///
    /// On debug builds we assert it's the result we expect.
    #[inline]
    pub fn from_atom_unchecked(atom: Atom, is_anon_box: bool) -> Self {
        if cfg!(debug_assertions) {
            // Do the check on debug regardless.
            match Self::from_atom(&*atom, true) {
                Some(pseudo) => {
                    assert_eq!(pseudo.is_anon_box(), is_anon_box);
                    return pseudo;
                }
                None => panic!("Unknown pseudo: {:?}", atom),
            }
        }

        PseudoElement(atom, is_anon_box)
    }

    #[inline]
    fn from_atom(atom: &WeakAtom, _in_ua: bool) -> Option<Self> {
        macro_rules! pseudo_element {
            ($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
                if atom == &*$atom {
                    return Some(PseudoElement($atom, $is_anon_box));
                }
            }}
        }

        include!("generated/gecko_pseudo_element_helper.rs");

        None
    }

    /// Constructs an atom from a string of text, and whether we're in a
    /// user-agent stylesheet.
    ///
    /// If we're not in a user-agent stylesheet, we will never parse anonymous
    /// box pseudo-elements.
    ///
    /// Returns `None` if the pseudo-element is not recognised.
    #[inline]
    fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
        use std::ascii::AsciiExt;
        macro_rules! pseudo_element {
            ($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
                if !$is_anon_box || in_ua_stylesheet {
                    if s.eq_ignore_ascii_case(&$pseudo_str_with_colon[1..]) {
                        return Some(PseudoElement($atom, $is_anon_box))
                    }
                }
            }}
        }

        include!("generated/gecko_pseudo_element_helper.rs");

        None
    }
}

impl ToCss for PseudoElement {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        // FIXME: why does the atom contain one colon? Pseudo-element has two
        debug_assert!(self.0.as_slice().starts_with(&[b':' as u16]) &&
                      !self.0.as_slice().starts_with(&[b':' as u16, b':' as u16]));
        try!(dest.write_char(':'));
        write!(dest, "{}", self.0)
    }
}

/// Our representation of a non tree-structural pseudo-class.
///
/// FIXME(emilio): Find a way to autogenerate this.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum NonTSPseudoClass {
    /// :any-link
    AnyLink,
    /// :link
    Link,
    /// :visited
    Visited,
    /// :active
    Active,
    /// :focus
    Focus,
    /// :fullscreen
    Fullscreen,
    /// :hover
    Hover,
    /// :enabled
    Enabled,
    /// :disabled
    Disabled,
    /// :checked
    Checked,
    /// :indeterminate
    Indeterminate,
    /// :read-write
    ReadWrite,
    /// :read-only
    ReadOnly,
}

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use self::NonTSPseudoClass::*;
        dest.write_str(match *self {
            AnyLink => ":any-link",
            Link => ":link",
            Visited => ":visited",
            Active => ":active",
            Focus => ":focus",
            Fullscreen => ":fullscreen",
            Hover => ":hover",
            Enabled => ":enabled",
            Disabled => ":disabled",
            Checked => ":checked",
            Indeterminate => ":indeterminate",
            ReadWrite => ":read-write",
            ReadOnly => ":read-only",
        })
    }
}

impl NonTSPseudoClass {
    /// Get the state flag associated with a pseudo-class, if any.
    pub fn state_flag(&self) -> ElementState {
        use element_state::*;
        use self::NonTSPseudoClass::*;
        match *self {
            Active => IN_ACTIVE_STATE,
            Focus => IN_FOCUS_STATE,
            Fullscreen => IN_FULLSCREEN_STATE,
            Hover => IN_HOVER_STATE,
            Enabled => IN_ENABLED_STATE,
            Disabled => IN_DISABLED_STATE,
            Checked => IN_CHECKED_STATE,
            Indeterminate => IN_INDETERMINATE_STATE,
            ReadOnly | ReadWrite => IN_READ_WRITE_STATE,

            AnyLink |
            Link |
            Visited => ElementState::empty(),
        }
    }
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq)]
pub struct SelectorImpl;

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElement = PseudoElement;
    type NonTSPseudoClass = NonTSPseudoClass;

    fn attr_exists_selector_is_shareable(attr_selector: &AttrSelector<Self>) -> bool {
        attr_exists_selector_is_shareable(attr_selector)
    }

    fn attr_equals_selector_is_shareable(attr_selector: &AttrSelector<Self>,
                                         value: &Self::AttrValue) -> bool {
        attr_equals_selector_is_shareable(attr_selector, value)
    }
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        use self::NonTSPseudoClass::*;
        let pseudo_class = match_ignore_ascii_case! { &name,
            "any-link" => AnyLink,
            "link" => Link,
            "visited" => Visited,
            "active" => Active,
            "focus" => Focus,
            "fullscreen" => Fullscreen,
            "hover" => Hover,
            "enabled" => Enabled,
            "disabled" => Disabled,
            "checked" => Checked,
            "indeterminate" => Indeterminate,
            "read-write" => ReadWrite,
            "read-only" => ReadOnly,
            _ => return Err(())
        };

        Ok(pseudo_class)
    }

    fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
        match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
            Some(pseudo) => Ok(pseudo),
            None => Err(()),
        }
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    /// Returns the kind of cascade type that a given pseudo is going to use.
    ///
    /// In Gecko we only compute ::before and ::after eagerly. We save the rules
    /// for anonymous boxes separately, so we resolve them as precomputed
    /// pseudos.
    ///
    /// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        if Self::pseudo_is_before_or_after(pseudo) {
            return PseudoElementCascadeType::Eager
        }

        if pseudo.is_anon_box() {
            return PseudoElementCascadeType::Precomputed
        }

        PseudoElementCascadeType::Lazy
    }

    #[inline]
    /// Executes a function for each pseudo-element.
    pub fn each_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement),
    {
        macro_rules! pseudo_element {
            ($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
                fun(PseudoElement($atom, $is_anon_box));
            }}
        }

        include!("generated/gecko_pseudo_element_helper.rs")
    }

    #[inline]
    /// Returns whether the given pseudo-element is `::before` or `::after`.
    pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
        *pseudo.as_atom() == atom!(":before") ||
        *pseudo.as_atom() == atom!(":after")
    }

    #[inline]
    /// Returns the relevant state flag for a given non-tree-structural
    /// pseudo-class.
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<|endoftext|>


<commit_before>extern crate bus;
extern crate time;
extern crate num_cpus;

use bus::Bus;

fn helper(buf: usize, iter: usize, rxs: usize) -> u64 {
    use std::thread;

    let mut c = Bus::new(buf);
    let wait = (0..rxs)
        .map(|_| c.add_rx())
        .map(|mut rx| {
            thread::spawn(move || {
                loop {
                    if let Ok(true) = unsafe { rx.recv() } {
                        break;
                    }
                    thread::yield_now();
                }
            })
        })
        .collect::<Vec<_>>();

    let start = time::precise_time_ns();
    for _ in 0..iter {
        while let Err(_) = c.broadcast(false) {
            thread::yield_now();
        }
    }

    while let Err(_) = c.broadcast(true) {
        thread::yield_now();
    }

    for w in wait.into_iter() {
        w.join().unwrap();
    }

    time::precise_time_ns() - start
}

fn main() {
    let num = 2_000_000;

    println!("1 {} {:.*} μs/op",
             10,
             2,
             helper(10, num, 1) as f64 / num as f64);

    for threads in 1..num_cpus::get() {
        println!("{} {} {:.*} μs/op",
                 threads,
                 1_000,
                 2,
                 helper(1_000, num, threads) as f64 / num as f64);
    }
}
<commit_msg>Slightly smarter benchmark<commit_after>extern crate bus;
extern crate time;
extern crate num_cpus;

use bus::Bus;

fn helper(buf: usize, iter: usize, rxs: usize) -> u64 {
    use std::thread;
    use std::time::Duration;

    let mut c = Bus::new(buf);
    let wait = (0..rxs)
        .map(|_| c.add_rx())
        .map(|mut rx| {
            thread::spawn(move || {
                let w = Duration::new(0, 10);
                loop {
                    match unsafe { rx.recv() } {
                        Ok(true) => break,
                        Err(..) => thread::sleep(w),
                        _ => continue,
                    }
                }
            })
        })
        .collect::<Vec<_>>();

    let w = Duration::new(0, 10);
    let start = time::precise_time_ns();
    for _ in 0..iter {
        while let Err(_) = c.broadcast(false) {
            thread::sleep(w);
        }
    }

    while let Err(_) = c.broadcast(true) {
        thread::yield_now();
    }

    for w in wait.into_iter() {
        w.join().unwrap();
    }

    time::precise_time_ns() - start
}

fn main() {
    let num = 2_000_000;

    println!("1 {} {:.*} μs/op",
             10,
             2,
             helper(10, num, 1) as f64 / num as f64);

    for threads in 1..(num_cpus::get() + 1) {
        println!("{} {} {:.*} μs/op",
                 threads,
                 1_000,
                 2,
                 helper(1_000, num, threads) as f64 / num as f64);
    }
}
<|endoftext|>


<commit_before>#![crate_name = "hotel"]
#![crate_type = "rlib"]
#![no_std]
#![feature(asm,const_fn)]

extern crate cortexm3;
extern crate common;
extern crate hil;
extern crate support;

pub mod chip;
pub mod gpio;
pub mod pinmux;
pub mod pmu;
pub mod timeus;
pub mod uart;

unsafe extern "C" fn unhandled_interrupt() {
    let mut interrupt_number: u32;

    // IPSR[8:0] holds the currently active interrupt
    asm!(
        "mrs    r0, ipsr                    "
        : "={r0}"(interrupt_number)
        :
        : "r0"
        :
        );

    interrupt_number = interrupt_number & 0x1ff;

    panic!("Unhandled Interrupt. ISR {} is active.", interrupt_number);
}

extern {
    // _estack is not really a function, but it makes the types work
    // You should never actually invoke it!!
    fn _estack();

    // Defined in src/main/main.rs
    fn main();

    fn SVC_Handler();

    fn generic_isr();

    fn systick_handler();

    static mut _ero : u32;
    static mut _sdata : u32;
    static mut _edata : u32;
    static mut _sbss : u32;
    static mut _ebss : u32;
}

// marks the end of the app code segment with a null byte
#[link_section=".endapp"]
pub static ENDAPP: usize = 0;

#[link_section=".vectors"]
#[no_mangle]
pub static ISR_VECTOR: [Option<unsafe extern fn()>; 16] = [
    /* Stack top */     Option::Some(_estack),
    /* Reset */         Option::Some(reset_handler),
    /* NMI */           Option::Some(unhandled_interrupt),
    /* Hard Fault */    Option::Some(unhandled_interrupt),
    /* MemManage */     Option::Some(unhandled_interrupt),
    /* BusFault */      Option::Some(unhandled_interrupt),
    /* UsageFault*/     Option::Some(unhandled_interrupt),
    None, None, None, None,
    /* SVC */           Option::Some(SVC_Handler),
    /* DebugMon */      Option::Some(unhandled_interrupt),
    None,
    /* PendSV */        Option::Some(unhandled_interrupt),
    /* SysTick */       Option::Some(systick_handler),
];

#[link_section=".vectors"]
#[no_mangle]
pub static IRQS: [unsafe extern fn(); 0] = [generic_isr; 0];

#[no_mangle]
pub static INTERRUPT_TABLE: [Option<unsafe extern fn()>; 0] = [];

unsafe extern "C" fn reset_handler() {
    // Relocate data segment.
    // Assumes data starts right after text segment as specified by the linker
    // file.
    let mut pdest  = &mut _sdata as *mut u32;
    let pend  = &mut _edata as *mut u32;
    let mut psrc = &_ero as *const u32;

    if psrc != pdest {
        while (pdest as *const u32) < pend {
            *pdest = *psrc;
            pdest = pdest.offset(1);
            psrc = psrc.offset(1);
        }
    }

    // Clear the zero segment (BSS)
    let pzero = &_ebss as *const u32;
    pdest = &mut _sbss as *mut u32;

    while (pdest as *const u32) < pzero {
        *pdest = 0;
        pdest = pdest.offset(1);
    }

    main()
}

<commit_msg>Add no_mangle to ENDAPP to prevent it from being removed<commit_after>#![crate_name = "hotel"]
#![crate_type = "rlib"]
#![no_std]
#![feature(asm,const_fn)]

extern crate cortexm3;
extern crate common;
extern crate hil;
extern crate support;

pub mod chip;
pub mod gpio;
pub mod pinmux;
pub mod pmu;
pub mod timeus;
pub mod uart;

unsafe extern "C" fn unhandled_interrupt() {
    let mut interrupt_number: u32;

    // IPSR[8:0] holds the currently active interrupt
    asm!(
        "mrs    r0, ipsr                    "
        : "={r0}"(interrupt_number)
        :
        : "r0"
        :
        );

    interrupt_number = interrupt_number & 0x1ff;

    panic!("Unhandled Interrupt. ISR {} is active.", interrupt_number);
}

extern {
    // _estack is not really a function, but it makes the types work
    // You should never actually invoke it!!
    fn _estack();

    // Defined in src/main/main.rs
    fn main();

    fn SVC_Handler();

    fn generic_isr();

    fn systick_handler();

    static mut _ero : u32;
    static mut _sdata : u32;
    static mut _edata : u32;
    static mut _sbss : u32;
    static mut _ebss : u32;
}

// marks the end of the app code segment with a null byte
#[link_section=".endapp"]
#[no_mangle]
pub static ENDAPP: usize = 0;

#[link_section=".vectors"]
#[no_mangle]
pub static ISR_VECTOR: [Option<unsafe extern fn()>; 16] = [
    /* Stack top */     Option::Some(_estack),
    /* Reset */         Option::Some(reset_handler),
    /* NMI */           Option::Some(unhandled_interrupt),
    /* Hard Fault */    Option::Some(unhandled_interrupt),
    /* MemManage */     Option::Some(unhandled_interrupt),
    /* BusFault */      Option::Some(unhandled_interrupt),
    /* UsageFault*/     Option::Some(unhandled_interrupt),
    None, None, None, None,
    /* SVC */           Option::Some(SVC_Handler),
    /* DebugMon */      Option::Some(unhandled_interrupt),
    None,
    /* PendSV */        Option::Some(unhandled_interrupt),
    /* SysTick */       Option::Some(systick_handler),
];

#[link_section=".vectors"]
#[no_mangle]
pub static IRQS: [unsafe extern fn(); 0] = [generic_isr; 0];

#[no_mangle]
pub static INTERRUPT_TABLE: [Option<unsafe extern fn()>; 0] = [];

unsafe extern "C" fn reset_handler() {
    // Relocate data segment.
    // Assumes data starts right after text segment as specified by the linker
    // file.
    let mut pdest  = &mut _sdata as *mut u32;
    let pend  = &mut _edata as *mut u32;
    let mut psrc = &_ero as *const u32;

    if psrc != pdest {
        while (pdest as *const u32) < pend {
            *pdest = *psrc;
            pdest = pdest.offset(1);
            psrc = psrc.offset(1);
        }
    }

    // Clear the zero segment (BSS)
    let pzero = &_ebss as *const u32;
    pdest = &mut _sbss as *mut u32;

    while (pdest as *const u32) < pzero {
        *pdest = 0;
        pdest = pdest.offset(1);
    }

    main()
}

<|endoftext|>


<commit_before>use crate::{Env, Error, NifResult, Term};

#[macro_use]
pub mod atom;
pub use crate::types::atom::Atom;

#[doc(hidden)]
pub mod binary;
pub use crate::types::binary::{Binary, OwnedBinary};

#[doc(hidden)]
pub mod list;
pub use crate::types::list::ListIterator;

#[doc(hidden)]
pub mod map;
pub use self::map::MapIterator;

#[doc(hidden)]
pub mod primitive;
#[doc(hidden)]
pub mod string;
pub mod tuple;

#[doc(hidden)]
pub mod local_pid;
pub use self::local_pid::LocalPid;

pub mod truthy;

pub mod elixir_struct;

pub trait Encoder {
    fn encode<'a>(&self, env: Env<'a>) -> Term<'a>;
}
pub trait Decoder<'a>: Sized + 'a {
    fn decode(term: Term<'a>) -> NifResult<Self>;
}

impl<'a> Encoder for Term<'a> {
    fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
        self.in_env(env)
    }
}
impl<'a> Decoder<'a> for Term<'a> {
    fn decode(term: Term<'a>) -> NifResult<Self> {
        Ok(term)
    }
}

impl<'a, T> Encoder for &'a T
where
    T: Encoder,
{
    fn encode<'c>(&self, env: Env<'c>) -> Term<'c> {
        <T as Encoder>::encode(self, env)
    }
}

impl<T> Encoder for Option<T>
where
    T: Encoder,
{
    fn encode<'c>(&self, env: Env<'c>) -> Term<'c> {
        match *self {
            Some(ref value) => value.encode(env),
            None => atom::nil().encode(env),
        }
    }
}

impl<'a, T> Decoder<'a> for Option<T>
where
    T: Decoder<'a>,
{
    fn decode(term: Term<'a>) -> NifResult<Self> {
        if let Ok(term) = term.decode::<T>() {
            Ok(Some(term))
        } else {
            let decoded_atom: atom::Atom = term.decode()?;
            if decoded_atom == atom::nil() {
                Ok(None)
            } else {
                Err(Error::BadArg)
            }
        }
    }
}

impl<T, E> Encoder for Result<T, E>
where
    T: Encoder,
    E: Encoder,
{
    fn encode<'c>(&self, env: Env<'c>) -> Term<'c> {
        match *self {
            Ok(ref value) => (atom::ok().encode(env), value.encode(env)).encode(env),
            Err(ref err) => (atom::error().encode(env), err.encode(env)).encode(env),
        }
    }
}

impl<'a, T, E> Decoder<'a> for Result<T, E>
where
    T: Decoder<'a>,
    E: Decoder<'a>,
{
    fn decode(term: Term<'a>) -> NifResult<Self> {
        let (decoded_atom, inner_term): (atom::Atom, Term) = term.decode()?;
        if decoded_atom == atom::ok() {
            let ok_value: T = inner_term.decode()?;
            Ok(Ok(ok_value))
        } else if decoded_atom == atom::error() {
            let err_value: E = inner_term.decode()?;
            Ok(Err(err_value))
        } else {
            Err(Error::BadArg)
        }
    }
}
<commit_msg>Add deprecation warnings<commit_after>use crate::{Env, Error, NifResult, Term};

#[macro_use]
pub mod atom;
pub use crate::types::atom::Atom;

#[doc(hidden)]
pub mod binary;
pub use crate::types::binary::{Binary, OwnedBinary};

#[doc(hidden)]
pub mod list;
pub use crate::types::list::ListIterator;

#[doc(hidden)]
pub mod map;
pub use self::map::MapIterator;

#[doc(hidden)]
pub mod primitive;
#[doc(hidden)]
pub mod string;
pub mod tuple;

#[doc(hidden)]
pub mod local_pid;
pub use self::local_pid::LocalPid;

#[deprecated(since="0.22", note="Please use local_pid instead")]
pub mod pid {
    #[deprecated(since="0.22", note="Please use LocalPid instead")]
    pub use super::LocalPid as Pid;
}
#[deprecated(since="0.22", note="Please use LocalPid instead")]
pub use self::LocalPid as Pid;

pub mod truthy;

pub mod elixir_struct;

pub trait Encoder {
    fn encode<'a>(&self, env: Env<'a>) -> Term<'a>;
}
pub trait Decoder<'a>: Sized + 'a {
    fn decode(term: Term<'a>) -> NifResult<Self>;
}

impl<'a> Encoder for Term<'a> {
    fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
        self.in_env(env)
    }
}
impl<'a> Decoder<'a> for Term<'a> {
    fn decode(term: Term<'a>) -> NifResult<Self> {
        Ok(term)
    }
}

impl<'a, T> Encoder for &'a T
where
    T: Encoder,
{
    fn encode<'c>(&self, env: Env<'c>) -> Term<'c> {
        <T as Encoder>::encode(self, env)
    }
}

impl<T> Encoder for Option<T>
where
    T: Encoder,
{
    fn encode<'c>(&self, env: Env<'c>) -> Term<'c> {
        match *self {
            Some(ref value) => value.encode(env),
            None => atom::nil().encode(env),
        }
    }
}

impl<'a, T> Decoder<'a> for Option<T>
where
    T: Decoder<'a>,
{
    fn decode(term: Term<'a>) -> NifResult<Self> {
        if let Ok(term) = term.decode::<T>() {
            Ok(Some(term))
        } else {
            let decoded_atom: atom::Atom = term.decode()?;
            if decoded_atom == atom::nil() {
                Ok(None)
            } else {
                Err(Error::BadArg)
            }
        }
    }
}

impl<T, E> Encoder for Result<T, E>
where
    T: Encoder,
    E: Encoder,
{
    fn encode<'c>(&self, env: Env<'c>) -> Term<'c> {
        match *self {
            Ok(ref value) => (atom::ok().encode(env), value.encode(env)).encode(env),
            Err(ref err) => (atom::error().encode(env), err.encode(env)).encode(env),
        }
    }
}

impl<'a, T, E> Decoder<'a> for Result<T, E>
where
    T: Decoder<'a>,
    E: Decoder<'a>,
{
    fn decode(term: Term<'a>) -> NifResult<Self> {
        let (decoded_atom, inner_term): (atom::Atom, Term) = term.decode()?;
        if decoded_atom == atom::ok() {
            let ok_value: T = inner_term.decode()?;
            Ok(Ok(ok_value))
        } else if decoded_atom == atom::error() {
            let err_value: E = inner_term.decode()?;
            Ok(Err(err_value))
        } else {
            Err(Error::BadArg)
        }
    }
}
<|endoftext|>


<commit_before>mod event;
mod input;

use std::cell::Cell;

use crate::{
    lexer::Token,
    parser_api::Parser,
    parser_impl::{
        event::{Event, EventProcessor},
        input::{InputPosition, ParserInput},
    },
    SmolStr,
    syntax_node::syntax_error::{
        ParseError,
        SyntaxError,
    },
};

use crate::SyntaxKind::{self, EOF, TOMBSTONE};

pub(crate) trait Sink {
    type Tree;

    /// Adds new leaf to the current branch.
    fn leaf(&mut self, kind: SyntaxKind, text: SmolStr);

    /// Start new branch and make it current.
    fn start_branch(&mut self, kind: SyntaxKind);

    /// Finish current branch and restore previous
    /// branch as current.
    fn finish_branch(&mut self);

    fn error(&mut self, error: SyntaxError);

    /// Complete tree building. Make sure that
    /// `start_branch` and `finish_branch` calls
    /// are paired!
    fn finish(self) -> Self::Tree;
}

/// Parse a sequence of tokens into the representative node tree
pub(crate) fn parse_with<S: Sink>(
    sink: S,
    text: &str,
    tokens: &[Token],
    parser: fn(&mut Parser),
) -> S::Tree {
    let mut events = {
        let input = input::ParserInput::new(text, tokens);
        let parser_impl = ParserImpl::new(&input);
        let mut parser_api = Parser(parser_impl);
        parser(&mut parser_api);
        parser_api.0.into_events()
    };
    EventProcessor::new(sink, text, tokens, &mut events).process().finish()
}

/// Implementation details of `Parser`, extracted
/// to a separate struct in order not to pollute
/// the public API of the `Parser`.
pub(crate) struct ParserImpl<'t> {
    parser_input: &'t ParserInput<'t>,
    pos: InputPosition,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> ParserImpl<'t> {
    pub(crate) fn new(inp: &'t ParserInput<'t>) -> ParserImpl<'t> {
        ParserImpl {
            parser_input: inp,
            pos: InputPosition::new(),
            events: Vec::new(),
            steps: Cell::new(0),
        }
    }

    pub(crate) fn into_events(self) -> Vec<Event> {
        assert_eq!(self.nth(0), EOF);
        self.events
    }

    pub(super) fn next2(&self) -> Option<(SyntaxKind, SyntaxKind)> {
        let c1 = self.parser_input.kind(self.pos);
        let c2 = self.parser_input.kind(self.pos + 1);
        if self.parser_input.token_start_at(self.pos + 1)
            == self.parser_input.token_start_at(self.pos) + self.parser_input.token_len(self.pos)
        {
            Some((c1, c2))
        } else {
            None
        }
    }

    pub(super) fn next3(&self) -> Option<(SyntaxKind, SyntaxKind, SyntaxKind)> {
        let c1 = self.parser_input.kind(self.pos);
        let c2 = self.parser_input.kind(self.pos + 1);
        let c3 = self.parser_input.kind(self.pos + 2);
        if self.parser_input.token_start_at(self.pos + 1)
            == self.parser_input.token_start_at(self.pos) + self.parser_input.token_len(self.pos)
            && self.parser_input.token_start_at(self.pos + 2)
                == self.parser_input.token_start_at(self.pos + 1)
                    + self.parser_input.token_len(self.pos + 1)
        {
            Some((c1, c2, c3))
        } else {
            None
        }
    }

    /// Get the syntax kind of the nth token.
    pub(super) fn nth(&self, n: u32) -> SyntaxKind {
        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);

        self.parser_input.kind(self.pos + n)
    }

    pub(super) fn at_kw(&self, t: &str) -> bool {
        self.parser_input.token_text(self.pos) == t
    }

    /// Start parsing right behind the last event.
    pub(super) fn start(&mut self) -> u32 {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        pos
    }

    /// Advances the parser by one token unconditionally.
    pub(super) fn bump(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1);
    }

    pub(super) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // TODO: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    pub(super) fn bump_compound(&mut self, kind: SyntaxKind, n: u8) {
        self.do_bump(kind, n);
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        self.pos += u32::from(n_raw_tokens);
        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    /// Append one Error event to the back of events.
    pub(super) fn error(&mut self, msg: String) {
        self.push_event(Event::Error { msg: ParseError(msg) })
    }

    /// Complete an event with appending a `Finish` event.
    pub(super) fn complete(&mut self, pos: u32, kind: SyntaxKind) {
        match self.events[pos as usize] {
            Event::Start { kind: ref mut slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        self.push_event(Event::Finish);
    }

    /// Ignore the dummy `Start` event.
    pub(super) fn abandon(&mut self, pos: u32) {
        let idx = pos as usize;
        if idx == self.events.len() - 1 {
            match self.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }

    /// Save the relative distance of a completed event to its forward_parent.
    pub(super) fn precede(&mut self, pos: u32) -> u32 {
        let new_pos = self.start();
        match self.events[pos as usize] {
            Event::Start { ref mut forward_parent, .. } => {
                *forward_parent = Some(new_pos - pos);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}
<commit_msg>make stuff private<commit_after>mod event;
mod input;

use std::cell::Cell;

use crate::{
    lexer::Token,
    parser_api::Parser,
    parser_impl::{
        event::{Event, EventProcessor},
        input::{InputPosition, ParserInput},
    },
    SmolStr,
    syntax_node::syntax_error::{
        ParseError,
        SyntaxError,
    },
};

use crate::SyntaxKind::{self, EOF, TOMBSTONE};

pub(crate) trait Sink {
    type Tree;

    /// Adds new leaf to the current branch.
    fn leaf(&mut self, kind: SyntaxKind, text: SmolStr);

    /// Start new branch and make it current.
    fn start_branch(&mut self, kind: SyntaxKind);

    /// Finish current branch and restore previous
    /// branch as current.
    fn finish_branch(&mut self);

    fn error(&mut self, error: SyntaxError);

    /// Complete tree building. Make sure that
    /// `start_branch` and `finish_branch` calls
    /// are paired!
    fn finish(self) -> Self::Tree;
}

/// Parse a sequence of tokens into the representative node tree
pub(crate) fn parse_with<S: Sink>(
    sink: S,
    text: &str,
    tokens: &[Token],
    parser: fn(&mut Parser),
) -> S::Tree {
    let mut events = {
        let input = input::ParserInput::new(text, tokens);
        let parser_impl = ParserImpl::new(&input);
        let mut parser_api = Parser(parser_impl);
        parser(&mut parser_api);
        parser_api.0.into_events()
    };
    EventProcessor::new(sink, text, tokens, &mut events).process().finish()
}

/// Implementation details of `Parser`, extracted
/// to a separate struct in order not to pollute
/// the public API of the `Parser`.
pub(crate) struct ParserImpl<'t> {
    parser_input: &'t ParserInput<'t>,
    pos: InputPosition,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> ParserImpl<'t> {
    fn new(inp: &'t ParserInput<'t>) -> ParserImpl<'t> {
        ParserImpl {
            parser_input: inp,
            pos: InputPosition::new(),
            events: Vec::new(),
            steps: Cell::new(0),
        }
    }

    fn into_events(self) -> Vec<Event> {
        assert_eq!(self.nth(0), EOF);
        self.events
    }

    pub(super) fn next2(&self) -> Option<(SyntaxKind, SyntaxKind)> {
        let c1 = self.parser_input.kind(self.pos);
        let c2 = self.parser_input.kind(self.pos + 1);
        if self.parser_input.token_start_at(self.pos + 1)
            == self.parser_input.token_start_at(self.pos) + self.parser_input.token_len(self.pos)
        {
            Some((c1, c2))
        } else {
            None
        }
    }

    pub(super) fn next3(&self) -> Option<(SyntaxKind, SyntaxKind, SyntaxKind)> {
        let c1 = self.parser_input.kind(self.pos);
        let c2 = self.parser_input.kind(self.pos + 1);
        let c3 = self.parser_input.kind(self.pos + 2);
        if self.parser_input.token_start_at(self.pos + 1)
            == self.parser_input.token_start_at(self.pos) + self.parser_input.token_len(self.pos)
            && self.parser_input.token_start_at(self.pos + 2)
                == self.parser_input.token_start_at(self.pos + 1)
                    + self.parser_input.token_len(self.pos + 1)
        {
            Some((c1, c2, c3))
        } else {
            None
        }
    }

    /// Get the syntax kind of the nth token.
    pub(super) fn nth(&self, n: u32) -> SyntaxKind {
        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);

        self.parser_input.kind(self.pos + n)
    }

    pub(super) fn at_kw(&self, t: &str) -> bool {
        self.parser_input.token_text(self.pos) == t
    }

    /// Start parsing right behind the last event.
    pub(super) fn start(&mut self) -> u32 {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        pos
    }

    /// Advances the parser by one token unconditionally.
    pub(super) fn bump(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1);
    }

    pub(super) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // TODO: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    pub(super) fn bump_compound(&mut self, kind: SyntaxKind, n: u8) {
        self.do_bump(kind, n);
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        self.pos += u32::from(n_raw_tokens);
        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    /// Append one Error event to the back of events.
    pub(super) fn error(&mut self, msg: String) {
        self.push_event(Event::Error { msg: ParseError(msg) })
    }

    /// Complete an event with appending a `Finish` event.
    pub(super) fn complete(&mut self, pos: u32, kind: SyntaxKind) {
        match self.events[pos as usize] {
            Event::Start { kind: ref mut slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        self.push_event(Event::Finish);
    }

    /// Ignore the dummy `Start` event.
    pub(super) fn abandon(&mut self, pos: u32) {
        let idx = pos as usize;
        if idx == self.events.len() - 1 {
            match self.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }

    /// Save the relative distance of a completed event to its forward_parent.
    pub(super) fn precede(&mut self, pos: u32) -> u32 {
        let new_pos = self.start();
        match self.events[pos as usize] {
            Event::Start { ref mut forward_parent, .. } => {
                *forward_parent = Some(new_pos - pos);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}
<|endoftext|>