<commit_before>use std::collections::HashSet;
use std::hash::{Hash, Writer};
use std::fmt;

#[deriving(Show, PartialEq, Clone)]
pub enum Token<'a> {
    Definition {
        name: &'a str,
        arity: uint,
        body: Vec<&'a str>,
    },
    Item(LangItem<'a>),
    Comment(&'a str),
}

#[deriving(PartialEq, Show, Clone)]
pub enum LangItem<'a> {
    PapyNumber(i32),
    PapyString(&'a str),
    PapyName(&'a str),
}

#[deriving(Show, PartialEq, Clone)]
struct Argument<'a> {
    name: &'a str,
    value: LangItem<'a>,
}

struct Symbol<'a> {
    name: &'a str,
    arity: uint,
    function: fn(name: &'a str, args: &Vec<Argument<'a>>, symbols: &SymbolTable<'a>) -> Vec<Token<'a>>,
}

pub struct SymbolTable<'a> {
    symbols: HashSet<Symbol<'a>>,
}

impl<'a> Eq for Symbol<'a> {}

impl<'a> Clone for Symbol<'a> {
    fn clone(&self) -> Symbol<'a> {
        Symbol {
            name: self.name.clone(),
            arity: self.arity.clone(),
            function: self.function,
        }
    }
}

impl<'a> PartialEq for Symbol<'a> {
    fn eq(&self, other: &Symbol) -> bool {
        self.name == other.name && self.arity == other.arity
    }
}

impl<'a, H: Writer> Hash<H> for Symbol<'a> {
 fn hash(&self, state: &mut H) {
    //TODO: learn2Hash, need to distribute moar i guess... stract says so, so it must be.
    self.name.hash(state);// AM I DOING THIS RIGHT? HOW DO HASH
 }
}

impl<'a> fmt::Show for Symbol<'a> {
    fn fmt(&self,f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Symbol {{name: {} }}", self.name)
    }
}

impl<'a> fmt::Show for SymbolTable<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SymbolTable {{symbols: {}}}", self.symbols)
    }
}

impl<'a> Clone for SymbolTable<'a> {
    fn clone(&self) -> SymbolTable<'a> {
        SymbolTable {
            symbols: self.symbols.clone()
        }
    }
}

impl<'a> SymbolTable<'a> {
    pub fn new() -> SymbolTable<'a> {
        let mut symbols: HashSet<Symbol<'a>> = HashSet::new();
        ////// builtin functions
        symbols.insert(Symbol {
            name: "+",
            arity: 2,
            function: { //need moar unboxed closures
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x + y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "*",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x * y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "/",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x / y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "-",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x - y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "switch",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x)), Item(PapyNumber(y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });

        SymbolTable {
            symbols: symbols,
        }
    }

    ///Takes a Definition Token and adds it to `self`
    pub fn add_symbol(&mut self, token: &Token<'a>) {

        self.symbols.insert(Symbol {
            //FIXME ugh
            name: match *token {
                Definition(name, _, _) => name,
                _ => fail!("token \"{}\" is not a definition!", token),
            },
            arity: match *token {
                Definition(_, arity, _) => arity,
                _ => fail!("token \"{}\" is not a definition!", token),
            },
            function: { //NEED MOAR UNBOXED CLOSURES to get rid of the symbols arg.
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, symbols: &SymbolTable<'a>) -> Vec<Token<'a>> {
                    let mut local_stack: Vec<Token<'a>> = vec![];
                    for arg in args.iter() {
                        match *arg {
                            Argument {name, value: PapyNumber(num)} => local_stack.push(Item(PapyNumber(num))),
                            Argument {name, value: PapyString(string)}=> local_stack.push(Item(PapyString(string))) ,
                            Argument {name: arg_name, value: PapyName(name)} => {
                                let sym = symbols.get(name);
                                let mut local_args = vec![];
                                for i in range(0, sym.arity) {
                                    local_args.push(match local_stack.pop() {
                                        Some(Item(val)) => {
                                            Argument {
                                                name: arg_name,
                                                value: val
                                            }
                                        },
                                        Some(_) => fail!("HOW DID WE GET HERE"),
                                        None => fail!("couldnt pop from local stack! not enough args")

                                    }); //FIXME unwrap
                                }
                                (sym.function)(sym.name, &local_args, symbols);
                            },
                        }
                    }
                    local_stack
                };
                func
            },

        });

    }
    fn contains_name(&self, name: &'a str ) -> bool {
        self.symbols.iter().any(|symbol| symbol.name == name)
    }
    fn get(&self, name: &'a str) -> Symbol<'a> { //TODO use Option instead of failure.
        match self.symbols.iter().filter(|symbol| symbol.name == name).last() {
            Some(val) => *val,
            None => fail!("symbol with name \"{}\" does not exist!", name)
        }
    }
}

/// Takes in an &str and returns a token representation of it
pub fn tokenize_str<'a>(text: &'a str) -> Token<'a> {
    let cap = regex!(r##"(?:(?P<definition>^def .*:.*end)|(?P<comment>#.*)|(?P<item>\s?^[\w\+-\*\?!]*))\s*"##)
        .captures(text).unwrap();

    if cap.pos(1).is_some() { //iterators instead?
        let whole_def = cap.name("definition").trim();
        println!("whole_def: {}", whole_def)
        let parts: Vec<&'a str> = whole_def.split_str(":").collect();
        let left = parts[0].split_str(" ").collect::<Vec<&'a str>>();
        let name = left[1];
        let arity = left[2..].len();
        let body = parts[1].split_str("end").collect::<Vec<&'a str>>()[0];
        println!("body of {}: {}", name, body);
        Definition {
            name: name,
            arity: arity,
            body: vec![body],
        }
    }
    else if cap.pos(2).is_some() {
        Comment(cap.name("comment").trim())
    }
    else if cap.pos(3).is_some() {
        let item = cap.name("item");
        match from_str(item) { //TODO string support
            Some(val) => {Item(PapyNumber(val)) },
            None => { Item(PapyName(item)) },
        }
    }
    else  {
        fail!("unknown token in \"{}\". try again!", text)
    }
}

/// Takes in an &str and returns a token representation of it
pub fn scan_str<'a, S: Str>(text: S) -> Token<'a> {

    match scan! {
        text,

        "#" comment:&str => Comment(comment),
        #[tokenizer="IdentsAndInts"]
        "def" name:&str arity:uint ":" [(?!"end") body_tokens:&str]* "end" => {
            Definition {
                name: name,
                arity: arity,
                body: body_tokens,
            }
        },
        num:i32 => Item(PapyNumber(num)),
        "`" string:&str "`" => Item(PapyString(string)),
        name:&str => Item(PapyName(name))

    } {
        Ok(tok) => tok,
        Err(reason) => fail!("could not parse input string: {}", reason)
    }
}

pub fn run_stack<'a>(tokens: Vec<Token<'a>>, symbol_table: &SymbolTable<'a>) -> Vec<Token<'a>>{
    let mut stack: Vec<Token<'a>> = vec![];
    for token in tokens.into_iter() {
        if let Item(item) = token {
            match item {
                PapyNumber(num) => {stack.push(Item(PapyNumber(num)))},
                PapyString(string) => {stack.push(Item(PapyString(string)))},
                PapyName(name) => {
                    if !symbol_table.contains_name(name) { // making this line was a lot of work D:
                        fail!("undefined symbol: \"{}\". aborting!", name)
                    }
                    let mut args = vec![];
                    let symbol = symbol_table.get(name);
                    for i in range(0, symbol.arity) {
                        args.push(match stack.pop() {
                            Some(Item(x)) => {
                                Argument {
                                    name: "",
                                    value: x,
                                }
                            },
                            Some(_) => fail!("HOW DID WE GET HERE!"),
                            None => fail!("failure to pop from stack!"),
                        });
                    }
                    let result = (symbol.function)(name, &args, symbol_table);
                    stack.extend(result.into_iter());
                }
            }
        }
    }
    stack
}
<commit_msg>removed old tokenization support<commit_after>use std::collections::HashSet;
use std::hash::{Hash, Writer};
use std::fmt;

#[deriving(Show, PartialEq, Clone)]
pub enum Token<'a> {
    Definition {
        name: &'a str,
        arity: uint,
        body: Vec<&'a str>,
    },
    Item(LangItem<'a>),
    Comment(&'a str),
}

#[deriving(PartialEq, Show, Clone)]
pub enum LangItem<'a> {
    PapyNumber(i32),
    PapyString(&'a str),
    PapyName(&'a str),
}

#[deriving(Show, PartialEq, Clone)]
struct Argument<'a> {
    name: &'a str,
    value: LangItem<'a>,
}

struct Symbol<'a> {
    name: &'a str,
    arity: uint,
    function: fn(name: &'a str, args: &Vec<Argument<'a>>, symbols: &SymbolTable<'a>) -> Vec<Token<'a>>,
}

pub struct SymbolTable<'a> {
    symbols: HashSet<Symbol<'a>>,
}

impl<'a> Eq for Symbol<'a> {}

impl<'a> Clone for Symbol<'a> {
    fn clone(&self) -> Symbol<'a> {
        Symbol {
            name: self.name.clone(),
            arity: self.arity.clone(),
            function: self.function,
        }
    }
}

impl<'a> PartialEq for Symbol<'a> {
    fn eq(&self, other: &Symbol) -> bool {
        self.name == other.name && self.arity == other.arity
    }
}

impl<'a, H: Writer> Hash<H> for Symbol<'a> {
 fn hash(&self, state: &mut H) {
    //TODO: learn2Hash, need to distribute moar i guess... stract says so, so it must be.
    self.name.hash(state);// AM I DOING THIS RIGHT? HOW DO HASH
 }
}

impl<'a> fmt::Show for Symbol<'a> {
    fn fmt(&self,f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Symbol {{name: {} }}", self.name)
    }
}

impl<'a> fmt::Show for SymbolTable<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SymbolTable {{symbols: {}}}", self.symbols)
    }
}

impl<'a> Clone for SymbolTable<'a> {
    fn clone(&self) -> SymbolTable<'a> {
        SymbolTable {
            symbols: self.symbols.clone()
        }
    }
}

impl<'a> SymbolTable<'a> {
    pub fn new() -> SymbolTable<'a> {
        let mut symbols: HashSet<Symbol<'a>> = HashSet::new();
        ////// builtin functions
        symbols.insert(Symbol {
            name: "+",
            arity: 2,
            function: { //need moar unboxed closures
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x + y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "*",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x * y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "/",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x / y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "-",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x - y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });
        symbols.insert(Symbol {
            name: "switch",
            arity: 2,
            function: {
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, _symbols: &SymbolTable) -> Vec<Token<'a>> {
                    match (args[0], args[1]) {
                        (Argument {name: _, value: PapyNumber(x)}, Argument {name: _,value: PapyNumber(y)}) => {
                            vec![Item(PapyNumber(x)), Item(PapyNumber(y))]
                        },
                        (Argument {name: _, value: PapyString(_) }, Argument {name: _, value: PapyString(_)}) => fail!("cant add strings! They also dont exist yet!"),
                        (_, _) => fail!("types need to be the same! Not PapyNumber + PapyNumber, or PapyString + PapyString!"),
                    }
                };
                func
            },
        });

        SymbolTable {
            symbols: symbols,
        }
    }

    ///Takes a Definition Token and adds it to `self`
    pub fn add_symbol(&mut self, token: &Token<'a>) {

        self.symbols.insert(Symbol {
            //FIXME ugh
            name: match *token {
                Definition(name, _, _) => name,
                _ => fail!("token \"{}\" is not a definition!", token),
            },
            arity: match *token {
                Definition(_, arity, _) => arity,
                _ => fail!("token \"{}\" is not a definition!", token),
            },
            function: { //NEED MOAR UNBOXED CLOSURES to get rid of the symbols arg.
                fn func<'a>(_name: &'a str, args: &Vec<Argument<'a>>, symbols: &SymbolTable<'a>) -> Vec<Token<'a>> {
                    let mut local_stack: Vec<Token<'a>> = vec![];
                    for arg in args.iter() {
                        match *arg {
                            Argument {name, value: PapyNumber(num)} => local_stack.push(Item(PapyNumber(num))),
                            Argument {name, value: PapyString(string)}=> local_stack.push(Item(PapyString(string))) ,
                            Argument {name: arg_name, value: PapyName(name)} => {
                                let sym = symbols.get(name);
                                let mut local_args = vec![];
                                for i in range(0, sym.arity) {
                                    local_args.push(match local_stack.pop() {
                                        Some(Item(val)) => {
                                            Argument {
                                                name: arg_name,
                                                value: val
                                            }
                                        },
                                        Some(_) => fail!("HOW DID WE GET HERE"),
                                        None => fail!("couldnt pop from local stack! not enough args")

                                    }); //FIXME unwrap
                                }
                                (sym.function)(sym.name, &local_args, symbols);
                            },
                        }
                    }
                    local_stack
                };
                func
            },

        });

    }
    fn contains_name(&self, name: &'a str ) -> bool {
        self.symbols.iter().any(|symbol| symbol.name == name)
    }
    fn get(&self, name: &'a str) -> Symbol<'a> { //TODO use Option instead of failure.
        match self.symbols.iter().filter(|symbol| symbol.name == name).last() {
            Some(val) => *val,
            None => fail!("symbol with name \"{}\" does not exist!", name)
        }
    }
}

/// Takes in an &str and returns a token representation of it
pub fn scan_str<'a, S: Str>(text: S) -> Token<'a> {

    match scan! {
        text,

        "#" comment:&str => Comment(comment),
        #[tokenizer="IdentsAndInts"]
        "def" name:&str arity:uint ":" [(?!"end") body_tokens:&str]* "end" => {
            Definition {
                name: name,
                arity: arity,
                body: body_tokens,
            }
        },
        num:i32 => Item(PapyNumber(num)),
        "`" string:&str "`" => Item(PapyString(string)),
        name:&str => Item(PapyName(name))

    } {
        Ok(tok) => tok,
        Err(reason) => fail!("could not parse input string: {}", reason)
    }
}

pub fn run_stack<'a>(tokens: Vec<Token<'a>>, symbol_table: &SymbolTable<'a>) -> Vec<Token<'a>>{
    let mut stack: Vec<Token<'a>> = vec![];
    for token in tokens.into_iter() {
        if let Item(item) = token {
            match item {
                PapyNumber(num) => {stack.push(Item(PapyNumber(num)))},
                PapyString(string) => {stack.push(Item(PapyString(string)))},
                PapyName(name) => {
                    if !symbol_table.contains_name(name) { // making this line was a lot of work D:
                        fail!("undefined symbol: \"{}\". aborting!", name)
                    }
                    let mut args = vec![];
                    let symbol = symbol_table.get(name);
                    for i in range(0, symbol.arity) {
                        args.push(match stack.pop() {
                            Some(Item(x)) => {
                                Argument {
                                    name: "",
                                    value: x,
                                }
                            },
                            Some(_) => fail!("HOW DID WE GET HERE!"),
                            None => fail!("failure to pop from stack!"),
                        });
                    }
                    let result = (symbol.function)(name, &args, symbol_table);
                    stack.extend(result.into_iter());
                }
            }
        }
    }
    stack
}
<|endoftext|>


<commit_before>//! Pattern-sequence data structures and implementations.

use std::iter::FromIterator;

use util::set::{self, Contains};
use super::{Atom, Class, Element, Repetition, Union, flatten_vec};

// To hide the implementation details, we wrap the type alias in
// a private submodule.
mod seq_impl {
    use std::slice;
    use ::pattern::Element;
    pub type Inner<T> = Vec<Element<T>>;
    pub type Iter<'a,T> = slice::Iter<'a,Element<T>>;
}

/// Sequence of patterns.
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Sequence<T: Atom>(seq_impl::Inner<T>);

impl<T: Atom> Sequence<T> {
    /// Create a new sequence from the given vector.
    pub fn new(v: Vec<Element<T>>) -> Self {
        if v.len() < 2 {
            panic!("Sequences must have at least two elements");
        }
        Sequence(v)
    }

    /// Get the number of elements in the sequence.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Fetch an iterator over the elements in the sequence
    pub fn iter<'a>(&'a self) -> SequenceIterator<'a,T> {
        SequenceIterator(self.0.iter())
    }

    fn into_inner(self) -> seq_impl::Inner<T> {
        self.0
    }

    /// Transforms each contained atom using the supplied function or closure
    /// to produce a new sequence.
    pub fn map_atoms<U, F>(self, f: F) -> Sequence<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        self.0.into_iter().map(|elt| elt.map_atoms(&f)).collect()
    }

    /// Flatten the sequence and apply implementation-defined optimizations.
    pub fn reduce(&mut self) {
        for elt in self.0.iter_mut() {
            elt.reduce();
        }

        flatten_vec(&mut self.0,
                    |elt| match elt { &Element::Sequence(ref s) => Some(s.len()), _ => None },
                    |elt| match elt { Element::Sequence(s) => s.into_inner(), _ => unreachable!() });
    }
}

impl<T: Atom> FromIterator<Element<T>> for Sequence<T> {
    fn from_iter<I>(iter: I) -> Self
        where I: IntoIterator<Item=Element<T>>
    {
        Sequence::new(iter.into_iter().collect())
    }
}

impl<T: Atom> set::Contains<T> for Sequence<T> {
    /// A sequence always contains at least two elements, and thus will never
    /// match a single atom.
    #[inline(always)]
    fn contains(&self, _: T) -> bool {
        false
    }
}

// A sequence `A` can be a subset of another sequence `B` *if* both have the
// same length and each element in `A` is a subset of the corresponding element
// in `B`:
//
// A ⊂ B ⇔ |A|=|B|=n ∧ Aᵢ ⊂ Bᵢ ∀ i∈[0,n]
impl<T: Atom> set::IsSubsetOf<Sequence<T>> for Sequence<T> {
    fn is_subset_of(&self, other: &Sequence<T>) -> bool {
        other.len() == self.len() &&
            self.iter().zip(other.iter()).all(|(u, v)| u.is_subset_of(v))
    }
}

impl<T: Atom> set::IsSubsetOf<Class<T>> for Sequence<T> {
    /// A sequence is never a subset of an atom class because sequences are
    /// guaranteed to contain at least two elements.
    #[inline(always)]
    fn is_subset_of(&self, _: &Class<T>) -> bool {
        false
    }
}

impl<T: Atom> set::IsSubsetOf<Union<T>> for Sequence<T> {
    /// A sequence is a subset of a union whenever it is a subset of one of the
    /// union's members.
    fn is_subset_of(&self, union: &Union<T>) -> bool {
        union.iter().any(|m| self.is_subset_of(m))
    }
}

impl<T: Atom> set::IsSubsetOf<Repetition<T>> for Sequence<T> {
    /// A sequence is a subset of a repetition if the sequence's length is
    /// contained by the repetition's count and all members of the sequence are
    /// subsets of the repeated element.
    fn is_subset_of(&self, rep: &Repetition<T>) -> bool {
        rep.count().contains(self.len()) && self.iter().all(|elt| elt.is_subset_of(rep.element()))
    }
}

impl<T: Atom> set::IsSubsetOf<Element<T>> for Sequence<T> {
    fn is_subset_of(&self, elt: &Element<T>) -> bool {
        match elt {
            &Element::Tagged{ref element, ..} => self.is_subset_of(&**element),
            &Element::Sequence(ref s) => self.is_subset_of(s),
            &Element::Union(ref u) => self.is_subset_of(u),
            &Element::Repeat(ref r) => self.is_subset_of(r),

            &Element::Anchor(_) => false,
            &Element::Atom(_) => false,
            &Element::Class(_) => false,
            &Element::Wildcard => false,
        }
    }
}

// ----------------------------------------------------------------

/// Iterator over the patterns within a sequence.
pub struct SequenceIterator<'a,T: 'a + Atom>(seq_impl::Iter<'a,T>);

impl<'a,T: 'a + Atom> Iterator for SequenceIterator<'a,T> {
    type Item = &'a Element<T>;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }
}

<commit_msg>pattern/sequence: impl Index<usize> for Sequence<T><commit_after>//! Pattern-sequence data structures and implementations.

use std::ops::Index;
use std::iter::FromIterator;

use util::set::{self, Contains};
use super::{Atom, Class, Element, Repetition, Union, flatten_vec};

// To hide the implementation details, we wrap the type alias in
// a private submodule.
mod seq_impl {
    use std::slice;
    use ::pattern::Element;
    pub type Inner<T> = Vec<Element<T>>;
    pub type Iter<'a,T> = slice::Iter<'a,Element<T>>;
}

/// Sequence of patterns.
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Sequence<T: Atom>(seq_impl::Inner<T>);

impl<T: Atom> Sequence<T> {
    /// Create a new sequence from the given vector.
    pub fn new(v: Vec<Element<T>>) -> Self {
        if v.len() < 2 {
            panic!("Sequences must have at least two elements");
        }
        Sequence(v)
    }

    /// Get the number of elements in the sequence.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Fetch an iterator over the elements in the sequence
    pub fn iter<'a>(&'a self) -> SequenceIterator<'a,T> {
        SequenceIterator(self.0.iter())
    }

    fn into_inner(self) -> seq_impl::Inner<T> {
        self.0
    }

    /// Transforms each contained atom using the supplied function or closure
    /// to produce a new sequence.
    pub fn map_atoms<U, F>(self, f: F) -> Sequence<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        self.0.into_iter().map(|elt| elt.map_atoms(&f)).collect()
    }

    /// Flatten the sequence and apply implementation-defined optimizations.
    pub fn reduce(&mut self) {
        for elt in self.0.iter_mut() {
            elt.reduce();
        }

        flatten_vec(&mut self.0,
                    |elt| match elt { &Element::Sequence(ref s) => Some(s.len()), _ => None },
                    |elt| match elt { Element::Sequence(s) => s.into_inner(), _ => unreachable!() });
    }
}

impl<T: Atom> Index<usize> for Sequence<T> {
    type Output = Element<T>;
    fn index(&self, index: usize) -> &Self::Output {
        &self.0[index]
    }

}

impl<T: Atom> FromIterator<Element<T>> for Sequence<T> {
    fn from_iter<I>(iter: I) -> Self
        where I: IntoIterator<Item=Element<T>>
    {
        Sequence::new(iter.into_iter().collect())
    }
}

impl<T: Atom> set::Contains<T> for Sequence<T> {
    /// A sequence always contains at least two elements, and thus will never
    /// match a single atom.
    #[inline(always)]
    fn contains(&self, _: T) -> bool {
        false
    }
}

// A sequence `A` can be a subset of another sequence `B` *if* both have the
// same length and each element in `A` is a subset of the corresponding element
// in `B`:
//
// A ⊂ B ⇔ |A|=|B|=n ∧ Aᵢ ⊂ Bᵢ ∀ i∈[0,n]
impl<T: Atom> set::IsSubsetOf<Sequence<T>> for Sequence<T> {
    fn is_subset_of(&self, other: &Sequence<T>) -> bool {
        other.len() == self.len() &&
            self.iter().zip(other.iter()).all(|(u, v)| u.is_subset_of(v))
    }
}

impl<T: Atom> set::IsSubsetOf<Class<T>> for Sequence<T> {
    /// A sequence is never a subset of an atom class because sequences are
    /// guaranteed to contain at least two elements.
    #[inline(always)]
    fn is_subset_of(&self, _: &Class<T>) -> bool {
        false
    }
}

impl<T: Atom> set::IsSubsetOf<Union<T>> for Sequence<T> {
    /// A sequence is a subset of a union whenever it is a subset of one of the
    /// union's members.
    fn is_subset_of(&self, union: &Union<T>) -> bool {
        union.iter().any(|m| self.is_subset_of(m))
    }
}

impl<T: Atom> set::IsSubsetOf<Repetition<T>> for Sequence<T> {
    /// A sequence is a subset of a repetition if the sequence's length is
    /// contained by the repetition's count and all members of the sequence are
    /// subsets of the repeated element.
    fn is_subset_of(&self, rep: &Repetition<T>) -> bool {
        rep.count().contains(self.len()) && self.iter().all(|elt| elt.is_subset_of(rep.element()))
    }
}

impl<T: Atom> set::IsSubsetOf<Element<T>> for Sequence<T> {
    fn is_subset_of(&self, elt: &Element<T>) -> bool {
        match elt {
            &Element::Tagged{ref element, ..} => self.is_subset_of(&**element),
            &Element::Sequence(ref s) => self.is_subset_of(s),
            &Element::Union(ref u) => self.is_subset_of(u),
            &Element::Repeat(ref r) => self.is_subset_of(r),

            &Element::Anchor(_) => false,
            &Element::Atom(_) => false,
            &Element::Class(_) => false,
            &Element::Wildcard => false,
        }
    }
}

// ----------------------------------------------------------------

/// Iterator over the patterns within a sequence.
pub struct SequenceIterator<'a,T: 'a + Atom>(seq_impl::Iter<'a,T>);

impl<'a,T: 'a + Atom> Iterator for SequenceIterator<'a,T> {
    type Item = &'a Element<T>;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }
}

<|endoftext|>


<commit_before>use color::PuyoColor;
use field;
use field_bit::FieldBit;
use plain_field::PuyoPlainField;
use simd;
use simdext::*;
use std::mem;

#[derive(Clone, Copy)]
pub struct BitField {
    m: [FieldBit; 3],
}

impl BitField {
    pub fn new() -> BitField {
        BitField {
            m: [FieldBit::new_empty(),
                FieldBit::from_values(0xFFFF, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0xFFFF),
                FieldBit::new_empty(), ]
        }
    }

    pub fn from_plain_field(pf: PuyoPlainField) -> BitField {
        let mut bf = BitField::new();

        // TODO(mayah): We have better algorithm here.
        for x in 0 .. field::MAP_WIDTH {
            for y in 0 .. field::MAP_HEIGHT {
                bf.set_color(x, y, pf.color(x, y))
            }
        }

        bf
    }

    pub fn from_str(s: &str) -> BitField {
        BitField::from_plain_field(PuyoPlainField::from_str(s))
    }

    pub fn color(&self, x: usize, y: usize) -> PuyoColor {
        let b0: u8 = if self.m[0].get(x, y) { 1 } else { 0 };
        let b1: u8 = if self.m[1].get(x, y) { 2 } else { 0 };
        let b2: u8 = if self.m[2].get(x, y) { 4 } else { 0 };

        unsafe {
            mem::transmute(b0 | b1 | b2)
        }
    }

    pub fn set_color(&mut self, x: usize, y: usize, c: PuyoColor) {
        let cc = c as u8;
        for i in 0 .. 3 {
            if (cc & (1 << i)) != 0 {
                self.m[i as usize].set(x, y);
            } else {
                self.m[i as usize].unset(x, y);
            }
        }
    }

    pub fn bits(&self, c: PuyoColor) -> FieldBit {
        let v = match c {
            PuyoColor::EMPTY => {  // 0
                (self.r(0) | self.r(1) | self.r(2)) ^ simd::u16x8::splat(0xFFFF)
            },
            PuyoColor::OJAMA => {  // 1
                mm_andnot_epu16(self.r(2), mm_andnot_epu16(self.r(1), self.r(0)))
            },
            PuyoColor::WALL => {   // 2
                mm_andnot_epu16(self.r(2), mm_andnot_epu16(self.r(0), self.r(1)))
            },
            PuyoColor::IRON => {   // 3
                mm_andnot_epu16(self.r(2), mm_and_epu16(self.r(0), self.r(1)))
            },
            PuyoColor::RED => {    // 4
                mm_andnot_epu16(self.r(0), mm_andnot_epu16(self.r(1), self.r(2)))
            },
            PuyoColor::BLUE => {   // 5
                mm_and_epu16(self.r(0), mm_andnot_epu16(self.r(1), self.r(2)))
            },
            PuyoColor::YELLOW => { // 6
                mm_andnot_epu16(self.r(0), mm_and_epu16(self.r(1), self.r(2)))
            },
            PuyoColor::GREEN => {  // 7
                mm_and_epu16(self.r(0), mm_and_epu16(self.r(1), self.r(2)))
            },
        };

        FieldBit::new(v)
    }

    fn r(&self, i: usize) -> simd::u16x8 {
        self.m[i].as_u16x8()
    }
}

#[cfg(test)]
mod tests {
    use super::BitField;
    use color;
    use color::PuyoColor;
    use field;

    #[test]
    fn test_initial() {
        let bf = BitField::new();
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                if x == 0 || x == 7 || y == 0 || y == 15 {
                    assert_eq!(bf.color(x, y), PuyoColor::WALL);
                } else {
                    assert_eq!(bf.color(x, y), PuyoColor::EMPTY);
                }
            }
        }
    }

    #[test]
    fn test_from_str() {
        let bf = BitField::from_str(concat!(
            "RGBRGB",
            "RGBRGB",
            "RGBRGB"));

        assert_eq!(bf.color(1, 1), PuyoColor::RED);
        assert_eq!(bf.color(2, 1), PuyoColor::GREEN);
        assert_eq!(bf.color(3, 1), PuyoColor::BLUE);
        assert_eq!(bf.color(1, 3), PuyoColor::RED);
        assert_eq!(bf.color(2, 3), PuyoColor::GREEN);
        assert_eq!(bf.color(3, 3), PuyoColor::BLUE);
    }

    #[test]
    fn test_set_color() {
        let colors = [
            PuyoColor::EMPTY, PuyoColor::OJAMA, PuyoColor::WALL, PuyoColor::IRON,
            PuyoColor::RED, PuyoColor::BLUE, PuyoColor::YELLOW, PuyoColor::GREEN,
        ];
        let mut bf = BitField::new();

        for c in colors.iter() {
            bf.set_color(1, 1, *c);
            assert_eq!(*c, bf.color(1, 1));
        }
    }

    #[test]
    fn test_bits() {
        let bf = BitField::from_str(concat!(
            "&&&&&&",
            "OOOOOO",
            "YYYYYY",
            "BBBBBB",
            "GGGGGG",
            "RRRRRR"));

        for x in 0 .. field::MAP_WIDTH {
            for y in 0 .. field::MAP_HEIGHT {
                for c in color::color::ALL_PUYO_COLORS.iter() {
                    assert_eq!(bf.bits(*c).get(x, y), *c == bf.color(x, y));
                }
            }
        }
    }
}
<commit_msg>Add BitField::is_color and is_normal_color.<commit_after>use color::PuyoColor;
use field;
use field_bit::FieldBit;
use plain_field::PuyoPlainField;
use simd;
use simdext::*;
use std::mem;

#[derive(Clone, Copy)]
pub struct BitField {
    m: [FieldBit; 3],
}

impl BitField {
    pub fn new() -> BitField {
        BitField {
            m: [FieldBit::new_empty(),
                FieldBit::from_values(0xFFFF, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0xFFFF),
                FieldBit::new_empty(), ]
        }
    }

    pub fn from_plain_field(pf: PuyoPlainField) -> BitField {
        let mut bf = BitField::new();

        // TODO(mayah): We have better algorithm here.
        for x in 0 .. field::MAP_WIDTH {
            for y in 0 .. field::MAP_HEIGHT {
                bf.set_color(x, y, pf.color(x, y))
            }
        }

        bf
    }

    pub fn from_str(s: &str) -> BitField {
        BitField::from_plain_field(PuyoPlainField::from_str(s))
    }

    pub fn color(&self, x: usize, y: usize) -> PuyoColor {
        let b0: u8 = if self.m[0].get(x, y) { 1 } else { 0 };
        let b1: u8 = if self.m[1].get(x, y) { 2 } else { 0 };
        let b2: u8 = if self.m[2].get(x, y) { 4 } else { 0 };

        unsafe {
            mem::transmute(b0 | b1 | b2)
        }
    }

    pub fn is_color(&self, x: usize, y: usize, c: PuyoColor) -> bool {
        self.bits(c).get(x, y)
    }

    pub fn is_normal_color(&self, x: usize, y: usize) -> bool {
        self.m[2].get(x, y)
    }

    pub fn set_color(&mut self, x: usize, y: usize, c: PuyoColor) {
        let cc = c as u8;
        for i in 0 .. 3 {
            if (cc & (1 << i)) != 0 {
                self.m[i as usize].set(x, y);
            } else {
                self.m[i as usize].unset(x, y);
            }
        }
    }

    pub fn bits(&self, c: PuyoColor) -> FieldBit {
        let v = match c {
            PuyoColor::EMPTY => {  // 0
                (self.r(0) | self.r(1) | self.r(2)) ^ simd::u16x8::splat(0xFFFF)
            },
            PuyoColor::OJAMA => {  // 1
                mm_andnot_epu16(self.r(2), mm_andnot_epu16(self.r(1), self.r(0)))
            },
            PuyoColor::WALL => {   // 2
                mm_andnot_epu16(self.r(2), mm_andnot_epu16(self.r(0), self.r(1)))
            },
            PuyoColor::IRON => {   // 3
                mm_andnot_epu16(self.r(2), mm_and_epu16(self.r(0), self.r(1)))
            },
            PuyoColor::RED => {    // 4
                mm_andnot_epu16(self.r(0), mm_andnot_epu16(self.r(1), self.r(2)))
            },
            PuyoColor::BLUE => {   // 5
                mm_and_epu16(self.r(0), mm_andnot_epu16(self.r(1), self.r(2)))
            },
            PuyoColor::YELLOW => { // 6
                mm_andnot_epu16(self.r(0), mm_and_epu16(self.r(1), self.r(2)))
            },
            PuyoColor::GREEN => {  // 7
                mm_and_epu16(self.r(0), mm_and_epu16(self.r(1), self.r(2)))
            },
        };

        FieldBit::new(v)
    }

    fn r(&self, i: usize) -> simd::u16x8 {
        self.m[i].as_u16x8()
    }
}

#[cfg(test)]
mod tests {
    use super::BitField;
    use color;
    use color::PuyoColor;
    use field;

    #[test]
    fn test_initial() {
        let bf = BitField::new();
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                if x == 0 || x == 7 || y == 0 || y == 15 {
                    assert_eq!(bf.color(x, y), PuyoColor::WALL);
                } else {
                    assert_eq!(bf.color(x, y), PuyoColor::EMPTY);
                }
            }
        }
    }

    #[test]
    fn test_from_str() {
        let bf = BitField::from_str(concat!(
            "RGBRGB",
            "RGBRGB",
            "RGBRGB"));

        assert_eq!(bf.color(1, 1), PuyoColor::RED);
        assert_eq!(bf.color(2, 1), PuyoColor::GREEN);
        assert_eq!(bf.color(3, 1), PuyoColor::BLUE);
        assert_eq!(bf.color(1, 3), PuyoColor::RED);
        assert_eq!(bf.color(2, 3), PuyoColor::GREEN);
        assert_eq!(bf.color(3, 3), PuyoColor::BLUE);
    }

    #[test]
    fn test_set_color() {
        let colors = [
            PuyoColor::EMPTY, PuyoColor::OJAMA, PuyoColor::WALL, PuyoColor::IRON,
            PuyoColor::RED, PuyoColor::BLUE, PuyoColor::YELLOW, PuyoColor::GREEN,
        ];
        let mut bf = BitField::new();

        for c in colors.iter() {
            bf.set_color(1, 1, *c);
            assert_eq!(*c, bf.color(1, 1));
        }
    }

    #[test]
    fn test_each_cell() {
        let bf = BitField::from_str(concat!(
            "&&&&&&",
            "OOOOOO",
            "YYYYYY",
            "BBBBBB",
            "GGGGGG",
            "RRRRRR"));

        for x in 0 .. field::MAP_WIDTH {
            for y in 0 .. field::MAP_HEIGHT {
                for c in color::color::ALL_PUYO_COLORS.iter() {
                    assert_eq!(bf.bits(*c).get(x, y), *c == bf.color(x, y));
                    assert_eq!(bf.is_color(x, y, *c), bf.is_color(x, y, *c));
                }

                assert_eq!(bf.is_normal_color(x, y), bf.is_normal_color(x, y));
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate cc;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate semver;

use regex::Regex;
use semver::Version;
use std::env;
use std::ffi::OsStr;
use std::io::{self, ErrorKind};
use std::path::PathBuf;
use std::process::Command;

// Environment variables that can guide compilation
//
// When adding new ones, they should also be added to main() to force a
// rebuild if they are changed.
lazy_static! {
    /// A single path to search for LLVM in (containing bin/llvm-config)
    static ref ENV_LLVM_PREFIX: String =
        format!("LLVM_SYS_{}_PREFIX", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If exactly "YES", ignore the version blocklist
    static ref ENV_IGNORE_BLOCKLIST: String =
        format!("LLVM_SYS_{}_IGNORE_BLOCKLIST", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, enforce precise correspondence between crate and binary versions.
    static ref ENV_STRICT_VERSIONING: String =
        format!("LLVM_SYS_{}_STRICT_VERSIONING", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, do not attempt to strip irrelevant options for llvm-config --cflags
    static ref ENV_NO_CLEAN_CFLAGS: String =
        format!("LLVM_SYS_{}_NO_CLEAN_CFLAGS", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set and targeting MSVC, force the debug runtime library
    static ref ENV_USE_DEBUG_MSVCRT: String =
        format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, always link against libffi
    static ref ENV_FORCE_FFI: String =
        format!("LLVM_SYS_{}_FFI_WORKAROUND", env!("CARGO_PKG_VERSION_MAJOR"));
}

lazy_static! {
    /// LLVM version used by this version of the crate.
    static ref CRATE_VERSION: Version = {
        let crate_version = Version::parse(env!("CARGO_PKG_VERSION"))
            .expect("Crate version is somehow not valid semver");
        Version {
            major: crate_version.major / 10,
            minor: crate_version.major % 10,
            .. crate_version
        }
    };

    /// Filesystem path to an llvm-config binary for the correct version.
    static ref LLVM_CONFIG_PATH: Option<PathBuf> = locate_llvm_config();
}

/// Try to find a version of llvm-config that is compatible with this crate.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is set, look for llvm-config ONLY in there. The assumption is
/// that the user know best, and they want to link to a specific build or fork of LLVM.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is NOT set, then look for llvm-config in $PATH.
///
/// Returns None on failure.
fn locate_llvm_config() -> Option<PathBuf> {
    let prefix = env::var_os(&*ENV_LLVM_PREFIX)
        .map(|p| PathBuf::from(p).join("bin"))
        .unwrap_or_else(PathBuf::new);
    for binary_name in llvm_config_binary_names() {
        let binary_name = prefix.join(binary_name);
        match llvm_version(&binary_name) {
            Ok(ref version) if is_compatible_llvm(version) => {
                // Compatible version found. Nice.
                return Some(binary_name);
            }
            Ok(version) => {
                // Version mismatch. Will try further searches, but warn that
                // we're not using the system one.
                println!(
                    "Found LLVM version {} on PATH, but need {}.",
                    version, *CRATE_VERSION
                );
            }
            Err(ref e) if e.kind() == ErrorKind::NotFound => {
                // Looks like we failed to execute any llvm-config. Keep
                // searching.
            }
            // Some other error, probably a weird failure. Give up.
            Err(e) => panic!("Failed to search PATH for llvm-config: {}", e),
        }
    }

    None
}

/// Return an iterator over possible names for the llvm-config binary.
fn llvm_config_binary_names() -> std::vec::IntoIter<String> {
    let mut base_names = vec![
        "llvm-config".into(),
        format!("llvm-config-{}", CRATE_VERSION.major),
        format!(
            "llvm-config-{}.{}",
            CRATE_VERSION.major, CRATE_VERSION.minor
        ),
        format!("llvm-config{}{}", CRATE_VERSION.major, CRATE_VERSION.minor),
    ];

    // On Windows, also search for llvm-config.exe
    if cfg!(target_os = "windows") {
        let mut exe_names = base_names.clone();
        for name in exe_names.iter_mut() {
            name.push_str(".exe");
        }
        base_names.extend(exe_names);
    }

    base_names.into_iter()
}

/// Check whether the given version of LLVM is blocklisted,
/// returning `Some(reason)` if it is.
fn is_blocklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
    static BLOCKLIST: &'static [(u64, u64, u64, &'static str)] = &[];

    if let Some(x) = env::var_os(&*ENV_IGNORE_BLOCKLIST) {
        if &x == "YES" {
            println!(
                "cargo:warning=Ignoring blocklist entry for LLVM {}",
                llvm_version
            );
            return None;
        } else {
            println!(
                "cargo:warning={} is set but not exactly \"YES\"; blocklist is still honored.",
                *ENV_IGNORE_BLOCKLIST
            );
        }
    }

    for &(major, minor, patch, reason) in BLOCKLIST.iter() {
        let bad_version = Version {
            major: major,
            minor: minor,
            patch: patch,
            pre: vec![],
            build: vec![],
        };

        if &bad_version == llvm_version {
            return Some(reason);
        }
    }
    None
}

/// Check whether the given LLVM version is compatible with this version of
/// the crate.
fn is_compatible_llvm(llvm_version: &Version) -> bool {
    if let Some(reason) = is_blocklisted_llvm(llvm_version) {
        println!(
            "Found LLVM {}, which is blocklisted: {}",
            llvm_version, reason
        );
        return false;
    }

    let strict =
        env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
    if strict {
        llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
    } else {
        llvm_version.major >= CRATE_VERSION.major
            || (llvm_version.major == CRATE_VERSION.major
                && llvm_version.minor >= CRATE_VERSION.minor)
    }
}

/// Get the output from running `llvm-config` with the given argument.
///
/// Lazily searches for or compiles LLVM as configured by the environment
/// variables.
fn llvm_config(arg: &str) -> String {
    llvm_config_ex(&*LLVM_CONFIG_PATH.clone().unwrap(), arg)
        .expect("Surprising failure from llvm-config")
}

/// Invoke the specified binary as llvm-config.
///
/// Explicit version of the `llvm_config` function that bubbles errors
/// up.
fn llvm_config_ex<S: AsRef<OsStr>>(binary: S, arg: &str) -> io::Result<String> {
    Command::new(binary)
        .arg(arg)
        .arg("--link-static") // Don't use dylib for >= 3.9
        .output()
        .map(|output| {
            String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8")
        })
}

/// Get the LLVM version using llvm-config.
fn llvm_version<S: AsRef<OsStr>>(binary: &S) -> io::Result<Version> {
    let version_str = llvm_config_ex(binary.as_ref(), "--version")?;

    // LLVM isn't really semver and uses version suffixes to build
    // version strings like '3.8.0svn', so limit what we try to parse
    // to only the numeric bits.
    let re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))??").unwrap();
    let c = re
        .captures(&version_str)
        .expect("Could not determine LLVM version from llvm-config.");

    // some systems don't have a patch number but Version wants it so we just append .0 if it isn't
    // there
    let s = match c.name("patch") {
        None => format!("{}.0", &c[0]),
        Some(_) => c[0].to_string(),
    };
    Ok(Version::parse(&s).unwrap())
}

/// Get the names of the dylibs required by LLVM, including the C++ standard
/// library.
fn get_system_libraries() -> Vec<String> {
    llvm_config("--system-libs")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|flag| {
            if cfg!(target_env = "msvc") {
                // Same as --libnames, foo.lib
                assert!(flag.ends_with(".lib"));
                &flag[..flag.len() - 4]
            } else if cfg!(target_os = "macos") {
                // Linker flags style, -lfoo
                assert!(flag.starts_with("-l"));
                if flag.ends_with(".tbd") && flag.starts_with("-llib") {
                    &flag[5..flag.len() - 4]
                } else {
                    &flag[2..]
                }
            } else {
                // Linker flags style, -lfoo
                assert!(flag.starts_with("-l"));
                &flag[2..]
            }
        })
        .chain(get_system_libcpp())
        .map(str::to_owned)
        .collect::<Vec<String>>()
}

/// Get the library that must be linked for C++, if any.
fn get_system_libcpp() -> Option<&'static str> {
    if cfg!(target_env = "msvc") {
        // MSVC doesn't need an explicit one.
        None
    } else if cfg!(target_os = "macos") {
        // On OS X 10.9 and later, LLVM's libc++ is the default. On earlier
        // releases GCC's libstdc++ is default. Unfortunately we can't
        // reasonably detect which one we need (on older ones libc++ is
        // available and can be selected with -stdlib=lib++), so assume the
        // latest, at the cost of breaking the build on older OS releases
        // when LLVM was built against libstdc++.
        Some("c++")
    } else if cfg!(target_os = "freebsd") {
        Some("c++")
    } else {
        // Otherwise assume GCC's libstdc++.
        // This assumption is probably wrong on some platforms, but would need
        // testing on them.
        Some("stdc++")
    }
}

/// Get the names of libraries to link against.
fn get_link_libraries() -> Vec<String> {
    // Using --libnames in conjunction with --libdir is particularly important
    // for MSVC when LLVM is in a path with spaces, but it is generally less of
    // a hack than parsing linker flags output from --libs and --ldflags.
    llvm_config("--libnames")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|name| {
            // --libnames gives library filenames. Extract only the name that
            // we need to pass to the linker.
            if cfg!(target_env = "msvc") {
                // LLVMfoo.lib
                assert!(name.ends_with(".lib"));
                &name[..name.len() - 4]
            } else {
                // libLLVMfoo.a
                assert!(name.starts_with("lib") && name.ends_with(".a"));
                &name[3..name.len() - 2]
            }
        })
        .map(str::to_owned)
        .collect::<Vec<String>>()
}

fn get_llvm_cflags() -> String {
    let output = llvm_config("--cflags");

    // llvm-config includes cflags from its own compilation with --cflags that
    // may not be relevant to us. In particularly annoying cases, these might
    // include flags that aren't understood by the default compiler we're
    // using. Unless requested otherwise, clean CFLAGS of options that are
    // known to be possibly-harmful.
    let no_clean = env::var_os(&*ENV_NO_CLEAN_CFLAGS).is_some();
    if no_clean || cfg!(target_env = "msvc") {
        // MSVC doesn't accept -W... options, so don't try to strip them and
        // possibly strip something that should be retained. Also do nothing if
        // the user requests it.
        return output;
    }

    llvm_config("--cflags")
        .split(&[' ', '\n'][..])
        .filter(|word| !word.starts_with("-W"))
        .collect::<Vec<_>>()
        .join(" ")
}

fn is_llvm_debug() -> bool {
    // Has to be either Debug or Release
    llvm_config("--build-mode").contains("Debug")
}

fn main() {
    // Behavior can be significantly affected by these vars.
    println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
    println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLOCKLIST);
    println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
    println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CFLAGS);
    println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
    println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);

    if cfg!(feature = "no-llvm-linking") && cfg!(feature = "disable-alltargets-init") {
        // exit early as we don't need to do anything and llvm-config isn't needed at all
        return;
    }

    if LLVM_CONFIG_PATH.is_none() {
        println!("cargo:rustc-cfg=LLVM_SYS_NOT_FOUND");
        return;
    }

    // Build the extra wrapper functions.
    if !cfg!(feature = "disable-alltargets-init") {
        std::env::set_var("CFLAGS", get_llvm_cflags());
        cc::Build::new()
            .file("wrappers/target.c")
            .compile("targetwrappers");
    }

    if cfg!(feature = "no-llvm-linking") {
        return;
    }

    let libdir = llvm_config("--libdir");

    // Export information to other crates
    println!(
        "cargo:config_path={}",
        LLVM_CONFIG_PATH.clone().unwrap().display()
    ); // will be DEP_LLVM_CONFIG_PATH
    println!("cargo:libdir={}", libdir); // DEP_LLVM_LIBDIR

    // Link LLVM libraries
    println!("cargo:rustc-link-search=native={}", libdir);
    for name in get_link_libraries() {
        println!("cargo:rustc-link-lib=static={}", name);
    }

    // Link system libraries
    for name in get_system_libraries() {
        println!("cargo:rustc-link-lib=dylib={}", name);
    }

    let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
    if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
        println!("cargo:rustc-link-lib={}", "msvcrtd");
    }

    // Link libffi if the user requested this workaround.
    // See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
    let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
    if force_ffi {
        println!("cargo:rustc-link-lib=dylib={}", "ffi");
    }
}
<commit_msg>llvm-config --system-libs can return absolute paths<commit_after>extern crate cc;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate semver;

use regex::Regex;
use semver::Version;
use std::env;
use std::ffi::OsStr;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;

// Environment variables that can guide compilation
//
// When adding new ones, they should also be added to main() to force a
// rebuild if they are changed.
lazy_static! {
    /// A single path to search for LLVM in (containing bin/llvm-config)
    static ref ENV_LLVM_PREFIX: String =
        format!("LLVM_SYS_{}_PREFIX", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If exactly "YES", ignore the version blocklist
    static ref ENV_IGNORE_BLOCKLIST: String =
        format!("LLVM_SYS_{}_IGNORE_BLOCKLIST", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, enforce precise correspondence between crate and binary versions.
    static ref ENV_STRICT_VERSIONING: String =
        format!("LLVM_SYS_{}_STRICT_VERSIONING", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, do not attempt to strip irrelevant options for llvm-config --cflags
    static ref ENV_NO_CLEAN_CFLAGS: String =
        format!("LLVM_SYS_{}_NO_CLEAN_CFLAGS", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set and targeting MSVC, force the debug runtime library
    static ref ENV_USE_DEBUG_MSVCRT: String =
        format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", env!("CARGO_PKG_VERSION_MAJOR"));

    /// If set, always link against libffi
    static ref ENV_FORCE_FFI: String =
        format!("LLVM_SYS_{}_FFI_WORKAROUND", env!("CARGO_PKG_VERSION_MAJOR"));
}

lazy_static! {
    /// LLVM version used by this version of the crate.
    static ref CRATE_VERSION: Version = {
        let crate_version = Version::parse(env!("CARGO_PKG_VERSION"))
            .expect("Crate version is somehow not valid semver");
        Version {
            major: crate_version.major / 10,
            minor: crate_version.major % 10,
            .. crate_version
        }
    };

    /// Filesystem path to an llvm-config binary for the correct version.
    static ref LLVM_CONFIG_PATH: Option<PathBuf> = locate_llvm_config();
}

/// Try to find a version of llvm-config that is compatible with this crate.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is set, look for llvm-config ONLY in there. The assumption is
/// that the user know best, and they want to link to a specific build or fork of LLVM.
///
/// If $LLVM_SYS_<VERSION>_PREFIX is NOT set, then look for llvm-config in $PATH.
///
/// Returns None on failure.
fn locate_llvm_config() -> Option<PathBuf> {
    let prefix = env::var_os(&*ENV_LLVM_PREFIX)
        .map(|p| PathBuf::from(p).join("bin"))
        .unwrap_or_else(PathBuf::new);
    for binary_name in llvm_config_binary_names() {
        let binary_name = prefix.join(binary_name);
        match llvm_version(&binary_name) {
            Ok(ref version) if is_compatible_llvm(version) => {
                // Compatible version found. Nice.
                return Some(binary_name);
            }
            Ok(version) => {
                // Version mismatch. Will try further searches, but warn that
                // we're not using the system one.
                println!(
                    "Found LLVM version {} on PATH, but need {}.",
                    version, *CRATE_VERSION
                );
            }
            Err(ref e) if e.kind() == ErrorKind::NotFound => {
                // Looks like we failed to execute any llvm-config. Keep
                // searching.
            }
            // Some other error, probably a weird failure. Give up.
            Err(e) => panic!("Failed to search PATH for llvm-config: {}", e),
        }
    }

    None
}

/// Return an iterator over possible names for the llvm-config binary.
fn llvm_config_binary_names() -> std::vec::IntoIter<String> {
    let mut base_names = vec![
        "llvm-config".into(),
        format!("llvm-config-{}", CRATE_VERSION.major),
        format!(
            "llvm-config-{}.{}",
            CRATE_VERSION.major, CRATE_VERSION.minor
        ),
        format!("llvm-config{}{}", CRATE_VERSION.major, CRATE_VERSION.minor),
    ];

    // On Windows, also search for llvm-config.exe
    if cfg!(target_os = "windows") {
        let mut exe_names = base_names.clone();
        for name in exe_names.iter_mut() {
            name.push_str(".exe");
        }
        base_names.extend(exe_names);
    }

    base_names.into_iter()
}

/// Check whether the given version of LLVM is blocklisted,
/// returning `Some(reason)` if it is.
fn is_blocklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
    static BLOCKLIST: &'static [(u64, u64, u64, &'static str)] = &[];

    if let Some(x) = env::var_os(&*ENV_IGNORE_BLOCKLIST) {
        if &x == "YES" {
            println!(
                "cargo:warning=Ignoring blocklist entry for LLVM {}",
                llvm_version
            );
            return None;
        } else {
            println!(
                "cargo:warning={} is set but not exactly \"YES\"; blocklist is still honored.",
                *ENV_IGNORE_BLOCKLIST
            );
        }
    }

    for &(major, minor, patch, reason) in BLOCKLIST.iter() {
        let bad_version = Version {
            major: major,
            minor: minor,
            patch: patch,
            pre: vec![],
            build: vec![],
        };

        if &bad_version == llvm_version {
            return Some(reason);
        }
    }
    None
}

/// Check whether the given LLVM version is compatible with this version of
/// the crate.
fn is_compatible_llvm(llvm_version: &Version) -> bool {
    if let Some(reason) = is_blocklisted_llvm(llvm_version) {
        println!(
            "Found LLVM {}, which is blocklisted: {}",
            llvm_version, reason
        );
        return false;
    }

    let strict =
        env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
    if strict {
        llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
    } else {
        llvm_version.major >= CRATE_VERSION.major
            || (llvm_version.major == CRATE_VERSION.major
                && llvm_version.minor >= CRATE_VERSION.minor)
    }
}

/// Get the output from running `llvm-config` with the given argument.
///
/// Lazily searches for or compiles LLVM as configured by the environment
/// variables.
fn llvm_config(arg: &str) -> String {
    llvm_config_ex(&*LLVM_CONFIG_PATH.clone().unwrap(), arg)
        .expect("Surprising failure from llvm-config")
}

/// Invoke the specified binary as llvm-config.
///
/// Explicit version of the `llvm_config` function that bubbles errors
/// up.
fn llvm_config_ex<S: AsRef<OsStr>>(binary: S, arg: &str) -> io::Result<String> {
    Command::new(binary)
        .arg(arg)
        .arg("--link-static") // Don't use dylib for >= 3.9
        .output()
        .map(|output| {
            String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8")
        })
}

/// Get the LLVM version using llvm-config.
fn llvm_version<S: AsRef<OsStr>>(binary: &S) -> io::Result<Version> {
    let version_str = llvm_config_ex(binary.as_ref(), "--version")?;

    // LLVM isn't really semver and uses version suffixes to build
    // version strings like '3.8.0svn', so limit what we try to parse
    // to only the numeric bits.
    let re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))??").unwrap();
    let c = re
        .captures(&version_str)
        .expect("Could not determine LLVM version from llvm-config.");

    // some systems don't have a patch number but Version wants it so we just append .0 if it isn't
    // there
    let s = match c.name("patch") {
        None => format!("{}.0", &c[0]),
        Some(_) => c[0].to_string(),
    };
    Ok(Version::parse(&s).unwrap())
}

/// Get the names of the dylibs required by LLVM, including the C++ standard
/// library.
fn get_system_libraries() -> Vec<String> {
    llvm_config("--system-libs")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|flag| {
            if cfg!(target_env = "msvc") {
                // Same as --libnames, foo.lib
                assert!(flag.ends_with(".lib"));
                &flag[..flag.len() - 4]
            } else if cfg!(target_os = "macos") {
                // Linker flags style, -lfoo
                assert!(flag.starts_with("-l"));
                if flag.ends_with(".tbd") && flag.starts_with("-llib") {
                    &flag[5..flag.len() - 4]
                } else {
                    &flag[2..]
                }
            } else {
                if flag.starts_with("-l") {
                    // Linker flags style, -lfoo
                    return flag[2..].to_owned();
                }

                let maybe_lib = Path::new(&flag);
                if maybe_lib.is_file() {
                    // Library on disk, likely an absolute path to a .so
                    maybe_lib
                        .parent()
                        .map(|p| println!("cargo:rustc-link-search={}", p.display()));
                    &maybe_lib.file_stem().unwrap().to_str().unwrap()[3..]
                } else {
                    panic!("Unable to parse result of llvm-config --system-libs")
                }
            }
            .to_owned()
        })
        .chain(get_system_libcpp().map(str::to_owned))
        .collect::<Vec<String>>()
}

/// Get the library that must be linked for C++, if any.
fn get_system_libcpp() -> Option<&'static str> {
    if cfg!(target_env = "msvc") {
        // MSVC doesn't need an explicit one.
        None
    } else if cfg!(target_os = "macos") {
        // On OS X 10.9 and later, LLVM's libc++ is the default. On earlier
        // releases GCC's libstdc++ is default. Unfortunately we can't
        // reasonably detect which one we need (on older ones libc++ is
        // available and can be selected with -stdlib=lib++), so assume the
        // latest, at the cost of breaking the build on older OS releases
        // when LLVM was built against libstdc++.
        Some("c++")
    } else if cfg!(target_os = "freebsd") {
        Some("c++")
    } else {
        // Otherwise assume GCC's libstdc++.
        // This assumption is probably wrong on some platforms, but would need
        // testing on them.
        Some("stdc++")
    }
}

/// Get the names of libraries to link against.
fn get_link_libraries() -> Vec<String> {
    // Using --libnames in conjunction with --libdir is particularly important
    // for MSVC when LLVM is in a path with spaces, but it is generally less of
    // a hack than parsing linker flags output from --libs and --ldflags.
    llvm_config("--libnames")
        .split(&[' ', '\n'] as &[char])
        .filter(|s| !s.is_empty())
        .map(|name| {
            // --libnames gives library filenames. Extract only the name that
            // we need to pass to the linker.
            if cfg!(target_env = "msvc") {
                // LLVMfoo.lib
                assert!(name.ends_with(".lib"));
                &name[..name.len() - 4]
            } else {
                // libLLVMfoo.a
                assert!(name.starts_with("lib") && name.ends_with(".a"));
                &name[3..name.len() - 2]
            }
        })
        .map(str::to_owned)
        .collect::<Vec<String>>()
}

fn get_llvm_cflags() -> String {
    let output = llvm_config("--cflags");

    // llvm-config includes cflags from its own compilation with --cflags that
    // may not be relevant to us. In particularly annoying cases, these might
    // include flags that aren't understood by the default compiler we're
    // using. Unless requested otherwise, clean CFLAGS of options that are
    // known to be possibly-harmful.
    let no_clean = env::var_os(&*ENV_NO_CLEAN_CFLAGS).is_some();
    if no_clean || cfg!(target_env = "msvc") {
        // MSVC doesn't accept -W... options, so don't try to strip them and
        // possibly strip something that should be retained. Also do nothing if
        // the user requests it.
        return output;
    }

    llvm_config("--cflags")
        .split(&[' ', '\n'][..])
        .filter(|word| !word.starts_with("-W"))
        .collect::<Vec<_>>()
        .join(" ")
}

fn is_llvm_debug() -> bool {
    // Has to be either Debug or Release
    llvm_config("--build-mode").contains("Debug")
}

fn main() {
    // Behavior can be significantly affected by these vars.
    println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
    println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLOCKLIST);
    println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
    println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CFLAGS);
    println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
    println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);

    if cfg!(feature = "no-llvm-linking") && cfg!(feature = "disable-alltargets-init") {
        // exit early as we don't need to do anything and llvm-config isn't needed at all
        return;
    }

    if LLVM_CONFIG_PATH.is_none() {
        println!("cargo:rustc-cfg=LLVM_SYS_NOT_FOUND");
        return;
    }

    // Build the extra wrapper functions.
    if !cfg!(feature = "disable-alltargets-init") {
        std::env::set_var("CFLAGS", get_llvm_cflags());
        cc::Build::new()
            .file("wrappers/target.c")
            .compile("targetwrappers");
    }

    if cfg!(feature = "no-llvm-linking") {
        return;
    }

    let libdir = llvm_config("--libdir");

    // Export information to other crates
    println!(
        "cargo:config_path={}",
        LLVM_CONFIG_PATH.clone().unwrap().display()
    ); // will be DEP_LLVM_CONFIG_PATH
    println!("cargo:libdir={}", libdir); // DEP_LLVM_LIBDIR

    // Link LLVM libraries
    println!("cargo:rustc-link-search=native={}", libdir);
    for name in get_link_libraries() {
        println!("cargo:rustc-link-lib=static={}", name);
    }

    // Link system libraries
    for name in get_system_libraries() {
        println!("cargo:rustc-link-lib=dylib={}", name);
    }

    let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
    if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
        println!("cargo:rustc-link-lib={}", "msvcrtd");
    }

    // Link libffi if the user requested this workaround.
    // See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
    let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
    if force_ffi {
        println!("cargo:rustc-link-lib=dylib={}", "ffi");
    }
}
<|endoftext|>


<commit_before>use std::{env, fs};
use std::path::{Path, PathBuf};

extern crate gcc;

#[cfg(unix)]
const OS_DEPENDENT: &'static [&'static str] = &["glslang", "OSDependent", "Linux"];
#[cfg(win)]
const OS_DEPENDENT: &'static [&'static str] = &["glslang", "OSDependent", "Windows"];

const SEARCH_DIRS: &'static [&'static [&'static str]] = &[
    &["glslang"],
    &["glslang", "gen"],
    &["glslang", "GenericCodeGen"],
    &["glslang", "Include"],
    &["glslang", "MachineIndependent"],
    &["glslang", "MachineIndependent", "preprocessor"],
    &["glslang", "Public"],
    OS_DEPENDENT,
    &["OGLCompilersDLL"],
];

fn main() {
    let src_root = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
        .join("extern").join("glslang");

    let mut config = gcc::Config::new();
    config.cpp(true);

    for file in find_sources(&src_root) {
        config.file(file);
    }

    for dir in resolve_search_dirs(&src_root) {
        config.include(dir);
    }

    config.compile("libglslang.a");
}

fn find_sources<P: AsRef<Path>>(root: &P) -> Vec<PathBuf> {
    let mut sources = Vec::new();

    for search_dir in resolve_search_dirs(root) {

        for file in fs::read_dir(search_dir).unwrap().filter_map(|e| {
            let path = e.unwrap().path();

            let mut ok = false;

            if let Some(ext) = path.extension() {
                if is_file(&path) && ext == "cpp" {
                    ok = true;
                }
            }

            if ok { Some(path) } else { None }
        }) {
            sources.push(file)
        }
    }

    sources
}

fn resolve_search_dirs<P: AsRef<Path>>(root: &P) -> Vec<PathBuf> {
    SEARCH_DIRS.iter().map(|dir| {
        let mut buf = root.as_ref().to_owned();

        for component in *dir {
            buf.push(component)
        }

        buf
    }).collect()
}

fn is_file<P: AsRef<Path>>(path: &P) -> bool {
    fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)
}
<commit_msg>fix build.rs on windows<commit_after>use std::{env, fs};
use std::path::{Path, PathBuf};

extern crate gcc;

#[cfg(unix)]
const OS_DEPENDENT: &'static [&'static str] = &["glslang", "OSDependent", "Linux"];
#[cfg(windows)]
const OS_DEPENDENT: &'static [&'static str] = &["glslang", "OSDependent", "Windows"];

const SEARCH_DIRS: &'static [&'static [&'static str]] = &[
    &["glslang"],
    &["glslang", "gen"],
    &["glslang", "GenericCodeGen"],
    &["glslang", "Include"],
    &["glslang", "MachineIndependent"],
    &["glslang", "MachineIndependent", "preprocessor"],
    &["glslang", "Public"],
    OS_DEPENDENT,
    &["OGLCompilersDLL"],
];

fn main() {
    let src_root = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
        .join("extern").join("glslang");

    let mut config = gcc::Config::new();
    config.cpp(true);

    for file in find_sources(&src_root) {
        config.file(file);
    }

    for dir in resolve_search_dirs(&src_root) {
        config.include(dir);
    }

    config.compile("libglslang.a");
}

fn find_sources<P: AsRef<Path>>(root: &P) -> Vec<PathBuf> {
    let mut sources = Vec::new();

    for search_dir in resolve_search_dirs(root) {

        for file in fs::read_dir(search_dir).unwrap().filter_map(|e| {
            let path = e.unwrap().path();

            let mut ok = false;

            if let Some(ext) = path.extension() {
                if is_file(&path) && ext == "cpp" {
                    ok = true;
                }
            }

            if ok { Some(path) } else { None }
        }) {
            sources.push(file)
        }
    }

    sources
}

fn resolve_search_dirs<P: AsRef<Path>>(root: &P) -> Vec<PathBuf> {
    SEARCH_DIRS.iter().map(|dir| {
        let mut buf = root.as_ref().to_owned();

        for component in *dir {
            buf.push(component)
        }

        buf
    }).collect()
}

fn is_file<P: AsRef<Path>>(path: &P) -> bool {
    fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{changed_since::changed_since_impl, context::XContext, Result};
use anyhow::anyhow;
use guppy::graph::DependencyDirection;
use std::collections::BTreeSet;
use structopt::StructOpt;
use x_core::WorkspaceStatus;

/// Arguments for the Cargo package selector.
#[derive(Debug, StructOpt)]
pub struct SelectedPackageArgs {
    #[structopt(long, short, number_of_values = 1)]
    /// Run on the provided packages
    pub(crate) package: Vec<String>,
    #[structopt(long, short)]
    /// Run on packages changed since the merge base of this commit
    changed_since: Option<String>,
    #[structopt(long)]
    /// Run on all packages in the workspace
    pub(crate) workspace: bool,
    #[structopt(long)]
    /// Run on production code (non-test-only code, reachable from default members)
    pub(crate) production: bool,
}

impl SelectedPackageArgs {
    pub fn to_selected_packages<'a>(&'a self, xctx: &'a XContext) -> Result<SelectedPackages<'a>> {
        // Mutually exclusive options -- only one of these can be provided.
        {
            let mut exclusive = vec![];
            if self.changed_since.is_some() {
                exclusive.push("--changed-since");
            }
            if !self.package.is_empty() {
                exclusive.push("--package");
            }
            if self.workspace {
                exclusive.push("--workspace");
            }
            if self.production {
                exclusive.push("--production");
            }

            if exclusive.len() > 1 {
                let err_msg = exclusive.join(", ");
                return Err(anyhow!("can only specify one of {}", err_msg));
            }
        }

        if self.workspace {
            Ok(SelectedPackages::workspace())
        } else if !self.package.is_empty() {
            Ok(SelectedPackages::includes(
                self.package.iter().map(|s| s.as_str()),
            ))
        } else if let Some(base) = &self.changed_since {
            let affected_set = changed_since_impl(&xctx, &base)?;

            Ok(SelectedPackages::includes(
                affected_set
                    .packages(DependencyDirection::Forward)
                    .map(|package| package.name()),
            ))
        } else if self.production {
            SelectedPackages::production(xctx)
        } else {
            SelectedPackages::default_cwd(xctx)
        }
    }
}

/// Package selector for Cargo commands.
///
/// This may represent any of the following:
/// * the entire workspace
/// * a single package without arguments
/// * a list of packages
///
/// This may also exclude a set of packages. Note that currently, excludes only work in the "entire
/// workspace" and "list of packages" situations. They are ignored if a specific local package is
/// being built. (This is an extension on top of Cargo itself, which only supports --exclude
/// together with --workspace.)
///
/// Excludes are applied after includes. This allows changed-since to support excludes, even if only
/// a subset of the workspace changes.
#[derive(Clone, Debug)]
pub struct SelectedPackages<'a> {
    pub(super) includes: SelectedInclude<'a>,
    pub(super) excludes: BTreeSet<&'a str>,
}

impl<'a> SelectedPackages<'a> {
    /// Returns a new `CargoPackages` that selects all packages in this workspace.
    pub fn workspace() -> Self {
        Self {
            includes: SelectedInclude::Workspace,
            excludes: BTreeSet::new(),
        }
    }

    /// Returns a new `CargoPackages` that selects production crates (non-test-only code).
    pub fn production(xctx: &'a XContext) -> Result<Self> {
        let default_members = xctx.core().default_members()?;
        let workspace = xctx.core().package_graph()?.workspace();

        let selected = workspace.iter().filter_map(|package| {
            if default_members.status_of(package.id()) != WorkspaceStatus::Absent {
                Some(package.name())
            } else {
                None
            }
        });
        Ok(Self {
            includes: SelectedInclude::Includes(selected.collect()),
            excludes: BTreeSet::new(),
        })
    }

    /// Returns a new `CargoPackages` that selects the default set of packages for the current
    /// working directory. This may either be the entire workspace or a set of packages inside the
    /// workspace.
    pub fn default_cwd(xctx: &'a XContext) -> Result<Self> {
        let includes = if xctx.core().current_dir_is_root() {
            SelectedInclude::Workspace
        } else {
            // Select all packages that begin with the current rel dir.
            let rel = xctx.core().current_rel_dir();
            let workspace = xctx.core().package_graph()?.workspace();
            let selected = workspace.iter_by_path().filter_map(|(path, package)| {
                // If we're in devtools, run tests for all packages inside devtools.
                // If we're in devtools/x/src, run tests for devtools/x.
                if path.starts_with(rel) || rel.starts_with(path) {
                    Some(package.name())
                } else {
                    None
                }
            });
            SelectedInclude::Includes(selected.collect())
        };
        Ok(Self {
            includes,
            excludes: BTreeSet::new(),
        })
    }

    /// Returns a new `CargoPackages` that selects the specified packages.
    pub fn includes(package_names: impl IntoIterator<Item = &'a str>) -> Self {
        Self {
            includes: SelectedInclude::Includes(package_names.into_iter().collect()),
            excludes: BTreeSet::new(),
        }
    }

    /// Adds excludes for this `CargoPackages`.
    ///
    /// The excludes are currently ignored if the local package is built.
    pub fn add_excludes(&mut self, exclude_names: impl IntoIterator<Item = &'a str>) -> &mut Self {
        self.excludes.extend(exclude_names);
        self
    }

    // ---
    // Helper methods
    // ---

    pub(super) fn should_invoke(&self) -> bool {
        match &self.includes {
            SelectedInclude::Workspace => true,
            SelectedInclude::Includes(includes) => {
                // If everything in the include set is excluded, a command invocation isn't needed.
                includes.iter().any(|p| !self.excludes.contains(p))
            }
        }
    }
}

#[derive(Clone, Debug)]
pub(super) enum SelectedInclude<'a> {
    Workspace,
    Includes(Vec<&'a str>),
}

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

    #[test]
    fn test_should_invoke() {
        let packages = SelectedPackages::workspace();
        assert!(packages.should_invoke(), "workspace => invoke");

        let mut packages = SelectedPackages::includes(vec!["foo", "bar"]);
        packages.add_excludes(vec!["foo"]);
        assert!(packages.should_invoke(), "non-empty packages => invoke");

        let packages = SelectedPackages::includes(vec![]);
        assert!(!packages.should_invoke(), "no packages => do not invoke");

        let mut packages = SelectedPackages::includes(vec!["foo", "bar"]);
        packages.add_excludes(vec!["foo", "bar"]);
        assert!(
            !packages.should_invoke(),
            "all packages excluded => do not invoke"
        );
    }
}
<commit_msg>[x] make --changed-since work with other package selectors<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{changed_since::changed_since_impl, context::XContext, Result};
use anyhow::anyhow;
use guppy::graph::DependencyDirection;
use std::collections::BTreeSet;
use structopt::StructOpt;
use x_core::WorkspaceStatus;

/// Arguments for the Cargo package selector.
#[derive(Debug, StructOpt)]
pub struct SelectedPackageArgs {
    #[structopt(long, short, number_of_values = 1)]
    /// Run on the provided packages
    pub(crate) package: Vec<String>,
    #[structopt(long, short)]
    /// Run on packages changed since the merge base of this commit
    changed_since: Option<String>,
    #[structopt(long)]
    /// Run on all packages in the workspace
    pub(crate) workspace: bool,
    #[structopt(long)]
    /// Run on production code (non-test-only code, reachable from default members)
    pub(crate) production: bool,
}

impl SelectedPackageArgs {
    pub fn to_selected_packages<'a>(&'a self, xctx: &'a XContext) -> Result<SelectedPackages<'a>> {
        // Mutually exclusive options -- only one of these can be provided.
        {
            let mut exclusive = vec![];
            if !self.package.is_empty() {
                exclusive.push("--package");
            }
            if self.workspace {
                exclusive.push("--workspace");
            }
            if self.production {
                exclusive.push("--production");
            }

            if exclusive.len() > 1 {
                let err_msg = exclusive.join(", ");
                return Err(anyhow!("can only specify one of {}", err_msg));
            }
        }

        let mut includes = if self.workspace {
            SelectedInclude::Workspace
        } else if !self.package.is_empty() {
            SelectedInclude::includes(self.package.iter().map(|s| s.as_str()))
        } else if self.production {
            SelectedInclude::production(xctx)?
        } else {
            SelectedInclude::default_cwd(xctx)?
        };

        // Intersect with --changed-since if specified.
        if let Some(base) = &self.changed_since {
            let affected_set = changed_since_impl(&xctx, &base)?;

            includes = includes.intersection(
                affected_set
                    .packages(DependencyDirection::Forward)
                    .map(|package| package.name()),
            );
        }

        Ok(SelectedPackages::new(includes))
    }
}

/// Package selector for Cargo commands.
///
/// This may represent any of the following:
/// * the entire workspace
/// * a single package without arguments
/// * a list of packages
///
/// This may also exclude a set of packages. Note that currently, excludes only work in the "entire
/// workspace" and "list of packages" situations. They are ignored if a specific local package is
/// being built. (This is an extension on top of Cargo itself, which only supports --exclude
/// together with --workspace.)
///
/// Excludes are applied after includes. This allows changed-since to support excludes, even if only
/// a subset of the workspace changes.
#[derive(Clone, Debug)]
pub struct SelectedPackages<'a> {
    pub(super) includes: SelectedInclude<'a>,
    pub(super) excludes: BTreeSet<&'a str>,
}

impl<'a> SelectedPackages<'a> {
    pub(super) fn new(includes: SelectedInclude<'a>) -> Self {
        Self {
            includes,
            excludes: BTreeSet::new(),
        }
    }

    /// Adds excludes for this `SelectedPackages`.
    pub fn add_excludes(&mut self, exclude_names: impl IntoIterator<Item = &'a str>) -> &mut Self {
        self.excludes.extend(exclude_names);
        self
    }

    // ---
    // Helper methods
    // ---

    pub(super) fn should_invoke(&self) -> bool {
        match &self.includes {
            SelectedInclude::Workspace => true,
            SelectedInclude::Includes(includes) => {
                // If everything in the include set is excluded, a command invocation isn't needed.
                includes.iter().any(|p| !self.excludes.contains(p))
            }
        }
    }
}

#[derive(Clone, Debug)]
pub(super) enum SelectedInclude<'a> {
    Workspace,
    Includes(BTreeSet<&'a str>),
}

impl<'a> SelectedInclude<'a> {
    /// Returns a `SelectedInclude` that selects production crates (non-test-only code).
    pub fn production(xctx: &'a XContext) -> Result<Self> {
        let default_members = xctx.core().default_members()?;
        let workspace = xctx.core().package_graph()?.workspace();

        let selected = workspace.iter().filter_map(|package| {
            if default_members.status_of(package.id()) != WorkspaceStatus::Absent {
                Some(package.name())
            } else {
                None
            }
        });
        Ok(SelectedInclude::Includes(selected.collect()))
    }

    /// Returns a `SelectedInclude` that selects the default set of packages for the current
    /// working directory. This may either be the entire workspace or a set of packages inside the
    /// workspace.
    pub fn default_cwd(xctx: &'a XContext) -> Result<Self> {
        if xctx.core().current_dir_is_root() {
            Ok(SelectedInclude::Workspace)
        } else {
            // Select all packages that begin with the current rel dir.
            let rel = xctx.core().current_rel_dir();
            let workspace = xctx.core().package_graph()?.workspace();
            let selected = workspace.iter_by_path().filter_map(|(path, package)| {
                // If we're in devtools, run tests for all packages inside devtools.
                // If we're in devtools/x/src, run tests for devtools/x.
                if path.starts_with(rel) || rel.starts_with(path) {
                    Some(package.name())
                } else {
                    None
                }
            });
            Ok(SelectedInclude::Includes(selected.collect()))
        }
    }

    /// Returns a `SelectedInclude` that selects the specified packages.
    pub fn includes(package_names: impl IntoIterator<Item = &'a str>) -> Self {
        SelectedInclude::Includes(package_names.into_iter().collect())
    }

    /// Intersects this `SelectedInclude` with the given names.
    pub fn intersection(&self, names: impl IntoIterator<Item = &'a str>) -> Self {
        let names = names.into_iter().collect();
        match self {
            SelectedInclude::Workspace => SelectedInclude::Includes(names),
            SelectedInclude::Includes(includes) => {
                SelectedInclude::Includes(includes.intersection(&names).copied().collect())
            }
        }
    }
}

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

    #[test]
    fn test_should_invoke() {
        let packages = SelectedPackages::new(SelectedInclude::Workspace);
        assert!(packages.should_invoke(), "workspace => invoke");

        let mut packages = SelectedPackages::new(SelectedInclude::Includes(
            vec!["foo", "bar"].into_iter().collect(),
        ));
        packages.add_excludes(vec!["foo"]);
        assert!(packages.should_invoke(), "non-empty packages => invoke");

        let packages = SelectedPackages::new(SelectedInclude::Includes(BTreeSet::new()));
        assert!(!packages.should_invoke(), "no packages => do not invoke");

        let mut packages = SelectedPackages::new(SelectedInclude::Includes(
            vec!["foo", "bar"].into_iter().collect(),
        ));
        packages.add_excludes(vec!["foo", "bar"]);
        assert!(
            !packages.should_invoke(),
            "all packages excluded => do not invoke"
        );
    }
}
<|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 js::jsapi::JSVal;
use js::{JSVAL_FALSE, JSVAL_TRUE};
use js::glue::{RUST_UINT_TO_JSVAL, RUST_JSVAL_TO_INT, RUST_DOUBLE_TO_JSVAL, RUST_JSVAL_TO_DOUBLE};

pub trait JSValConvertible {
    fn to_jsval(&self) -> JSVal;
    fn from_jsval(val: JSVal) -> Option<Self>;
}

impl JSValConvertible for i64 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_DOUBLE_TO_JSVAL(*self as f64)
        }
    }

    fn from_jsval(val: JSVal) -> Option<i64> {
        unsafe {
            Some(RUST_JSVAL_TO_DOUBLE(val) as i64)
        }
    }
}

impl JSValConvertible for u32 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_UINT_TO_JSVAL(*self)
        }
    }

    fn from_jsval(val: JSVal) -> Option<u32> {
        unsafe {
            Some(RUST_JSVAL_TO_INT(val) as u32)
        }
    }
}

impl JSValConvertible for i32 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_UINT_TO_JSVAL(*self as u32)
        }
    }

    fn from_jsval(val: JSVal) -> Option<i32> {
        unsafe {
            Some(RUST_JSVAL_TO_INT(val) as i32)
        }
    }
}

impl JSValConvertible for u16 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_UINT_TO_JSVAL(*self as u32)
        }
    }

    fn from_jsval(val: JSVal) -> Option<u16> {
        unsafe {
            Some(RUST_JSVAL_TO_INT(val) as u16)
        }
    }
}

impl JSValConvertible for bool {
    fn to_jsval(&self) -> JSVal {
        if *self {
            JSVAL_TRUE
        } else {
            JSVAL_FALSE
        }
    }

    fn from_jsval(val: JSVal) -> Option<bool> {
        if val == JSVAL_TRUE {
            Some(true)
        } else if val == JSVAL_FALSE {
            Some(false)
        } else {
            None
        }
    }
}

impl JSValConvertible for f32 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_DOUBLE_TO_JSVAL(*self as f64)
        }
    }

    fn from_jsval(val: JSVal) -> Option<f32> {
        unsafe {
            Some(RUST_JSVAL_TO_DOUBLE(val) as f32)
        }
    }
}

impl JSValConvertible for f64 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_DOUBLE_TO_JSVAL(*self as f64)
        }
    }

    fn from_jsval(val: JSVal) -> Option<f64> {
        unsafe {
            Some(RUST_JSVAL_TO_DOUBLE(val) as f64)
        }
    }
}
<commit_msg>Make JSVal conversions actually fallible.<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/. */

use js::jsapi::JSVal;
use js::{JSVAL_FALSE, JSVAL_TRUE};
use js::glue::{RUST_UINT_TO_JSVAL, RUST_JSVAL_TO_INT, RUST_DOUBLE_TO_JSVAL};
use js::glue::{RUST_JSVAL_TO_DOUBLE, RUST_JSVAL_IS_INT, RUST_JSVAL_IS_DOUBLE};
use js::glue::{RUST_JSVAL_IS_BOOLEAN, RUST_JSVAL_TO_BOOLEAN};

pub trait JSValConvertible {
    fn to_jsval(&self) -> JSVal;
    fn from_jsval(val: JSVal) -> Option<Self>;
}

impl JSValConvertible for i64 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_DOUBLE_TO_JSVAL(*self as f64)
        }
    }

    fn from_jsval(val: JSVal) -> Option<i64> {
        unsafe {
            if RUST_JSVAL_IS_INT(val) != 0 {
                Some(RUST_JSVAL_TO_DOUBLE(val) as i64)
            } else {
                None
            }
        }
    }
}

impl JSValConvertible for u32 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_UINT_TO_JSVAL(*self)
        }
    }

    fn from_jsval(val: JSVal) -> Option<u32> {
        unsafe {
            if RUST_JSVAL_IS_INT(val) != 0 {
                Some(RUST_JSVAL_TO_INT(val) as u32)
            } else {
                None
            }
        }
    }
}

impl JSValConvertible for i32 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_UINT_TO_JSVAL(*self as u32)
        }
    }

    fn from_jsval(val: JSVal) -> Option<i32> {
        unsafe {
            if RUST_JSVAL_IS_INT(val) != 0 {
                Some(RUST_JSVAL_TO_INT(val) as i32)
            } else {
                None
            }
        }
    }
}

impl JSValConvertible for u16 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_UINT_TO_JSVAL(*self as u32)
        }
    }

    fn from_jsval(val: JSVal) -> Option<u16> {
        unsafe {
            if RUST_JSVAL_IS_INT(val) != 0 {
                Some(RUST_JSVAL_TO_INT(val) as u16)
            } else {
                None
            }
        }
    }
}

impl JSValConvertible for bool {
    fn to_jsval(&self) -> JSVal {
        if *self {
            JSVAL_TRUE
        } else {
            JSVAL_FALSE
        }
    }

    fn from_jsval(val: JSVal) -> Option<bool> {
        unsafe {
            if RUST_JSVAL_IS_BOOLEAN(val) != 0 {
                Some(RUST_JSVAL_TO_BOOLEAN(val) != 0)
            } else {
                None
            }
        }
    }
}

impl JSValConvertible for f32 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_DOUBLE_TO_JSVAL(*self as f64)
        }
    }

    fn from_jsval(val: JSVal) -> Option<f32> {
        unsafe {
            if RUST_JSVAL_IS_DOUBLE(val) != 0 {
                Some(RUST_JSVAL_TO_DOUBLE(val) as f32)
            } else {
                None
            }
        }
    }
}

impl JSValConvertible for f64 {
    fn to_jsval(&self) -> JSVal {
        unsafe {
            RUST_DOUBLE_TO_JSVAL(*self as f64)
        }
    }

    fn from_jsval(val: JSVal) -> Option<f64> {
        unsafe {
            if RUST_JSVAL_IS_DOUBLE(val) != 0 {
                Some(RUST_JSVAL_TO_DOUBLE(val) as f64)
            } else {
                None
            }
        }
    }
}
<|endoftext|>