<commit_before>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use num::bigint::BigUint;
use num::traits::Zero;
use nock::Noun;

use twig::{self, Twig, Rune, Odor};

pub type ParseResult<'a, T> = Result<(&'a [u8], T), &'a [u8]>;

/// Parse Hoon source code into a Twig AST.
///
/// Return trailing input after a successful parse along with the resulting
/// Twig. Return trailing input after the point of failed parse on error.
pub fn ream(mut input: &[u8]) -> ParseResult<Twig> {
    // Eat initial space
    loop {
        if let Ok((tail, _)) = space_or_comment(input) {
            input = tail;
        } else {
            break;
        }
    }
    twig(input)
}

fn twig(input: &[u8]) -> ParseResult<Twig> {
    use twig::Rune::*;

    if let Ok(x) = atom(input) {
        return Ok(x);
    }
    if let Ok(x) = wing(input) {
        return Ok(x);
    }

    // Just crunch through the whole set of rune data and look for all the
    // ones that look like they can be parsed naively.
    for i in twig::RUNES.iter() {
        if i.rune.is_regular() && i.rune.glyph().is_some() {
            if let Ok(x) = rune(input, i.rune) {
                return Ok(x);
            }
        }
    }

    Err(input)
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Form {
    Tall,
    Wide,
}

fn rune<'a>(input: &'a [u8], rune: Rune) -> ParseResult<'a, Twig> {
    assert!(rune.is_regular(),
            "Can't use standard parser on this type of rune!");
    let glyph = rune.glyph().expect("Trying to parse an unprintable rune");
    let (input, form) = try!(rune_start(input, &glyph));
    let (input, args) = try!(rune_args(input, form, rune.arity()));
    Ok((input, Twig::Cell(box Twig::Rune(rune), box args)))
}

/// Parse the start of a rune with the given glyph and determine if the rune
/// is in wide form (.*(p q) or tall form (.*  p  q).
fn rune_start<'a>(input: &'a [u8], glyph: &str) -> ParseResult<'a, Form> {
    assert!(glyph.len() == 2, "Rune isn't 2 characters");
    let (mut tail, _) = try!(tag(input, glyph));

    if let Ok((tail, _)) = tag(tail, "(") {
        return Ok((tail, Form::Wide));
    }

    if let Ok((tail, _)) = gap(tail) {
        return Ok((tail, Form::Tall));
    }

    Err(input)
}

fn rune_args(input: &[u8], form: Form, n: usize) -> ParseResult<Twig> {
    assert!(n > 0);
    if n == 1 {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_end(input, form));
        Ok((input, p))
    } else {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_sep(input, form));
        let (input, ps) = try!(rune_args(input, form, n - 1));
        Ok((input, Twig::Cell(box p, box ps)))
    }
}

fn rune_sep(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => gap(input),
        Form::Wide => tag(input, " "),
    }
}

fn rune_end(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => Ok((input, &input[..0])),
        Form::Wide => tag(input, ")"),
    }
}

fn atom(input: &[u8]) -> ParseResult<Twig> {
    if let Ok((tail, num)) = ud(input) {
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::dtzy), box Twig::Atom(Odor::ud, num))))
    } else {
        Err(input)
    }
}

/// Unsigned decimal
fn ud(input: &[u8]) -> ParseResult<BigUint> {
    let mut idx = 0;
    let mut num = String::new();
    for c in input.iter() {
        if *c >= '0' as u8 && *c <= '9' as u8 {
            num.push(*c as char);
        } else if idx > 0 && *c == '.' as u8 {
            // Ignore separator dots.
            // We're lazy, so we let them show up in any pattern after the
            // initial digit instead of insisting that they group the digits
            // in sized groups.
        } else {
            if idx == 0 {
                return Err(input);
            }
            break;
        }
        idx += 1;
    }

    Ok((&input[idx..], FromStr::from_str(&num).unwrap()))
}

fn wing(input: &[u8]) -> ParseResult<Twig> {
    let mut items = Vec::new();
    let mut tail = input;
    loop {
        if let Ok((t, term)) = term(tail) {
            let item = match str::from_utf8(term).unwrap() {
                // Dot is "subject", all of the preceding context. Instead of
                // an atom, we will insert the Nock formula for getting the
                // entire subject.
                "." => n![0, 1],
                // Buc is the default recursion symbol. Evaluates to atom
                // zero.
                "$" => Noun::Atom(0),
                // The regular terms are replaced with cord atoms.
                _ => Noun::from_bytes(term),
            };
            items.push(item);
            tail = t;
        }
        match wing_dot(tail) {
            Ok((t, _)) => {
                tail = t;
            }
            Err(_) => {
                if items.len() == 0 {
                    return Err(input);
                } else {
                    break;
                }
            }
        }
    }
    assert!(items.len() > 0);
    if items[0] == n![0, 1] {
        // Wings that start with dot produce a different twig, based on
        // experimenting with the real ream. Don't understand the full logic
        // of this yet.
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::cnts),
                       box Twig::Cell(box Twig::Wing(items),
                                      // Empty tram. XXX: Maybe we get a better twig type for trams?
                                      // Should use that here then.
                                      box Twig::Atom(Odor::ud, Zero::zero())))))
    } else {
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::cnzz), box Twig::Wing(items))))
    }
}

fn term(input: &[u8]) -> ParseResult<&[u8]> {
    // Special terms.
    for special in ["$", "."].into_iter() {
        if let Ok(x) = word(input, special) {
            return Ok(x);
        }
    }

    let mut idx = 0;
    for i in input.iter() {
        if idx == 0 && !is_ident_start(*i) {
            break;
        }

        if idx > 0 && !is_ident_middle(*i) {
            break;
        }

        idx += 1;
    }

    if idx == 0 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[..idx]))
    }
}

fn wing_dot(input: &[u8]) -> ParseResult<()> {
    if input.len() < 2 {
        return Err(input);
    }
    let (tail, _) = try!(tag(input, "."));
    if !is_ident_start(tail[0]) && tail[0] != '.' as u8 {
        Err(input)
    } else {
        Ok((tail, ()))
    }
}

fn space_or_comment(input: &[u8]) -> ParseResult<()> {
    let mut tail = input;
    loop {
        if let Ok((t, _)) = comment(tail) {
            tail = t
        } else {
            if tail.len() == 0 {
                break;
            } else if is_whitespace(tail[0]) {
                tail = &tail[1..];
            } else {
                break;
            }
        }
    }
    if tail == input {
        Err(input)
    } else {
        Ok((tail, ()))
    }
}

/// At least one newline or at least two spaces.
///
/// Used to separate tall forms in Hoon.
fn gap(input: &[u8]) -> ParseResult<&[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for i in input.iter() {
        if *i == '\n' as u8 {
            spaces += 2;
        } else if is_whitespace(*i) {
            spaces += 1;
        } else {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[0..idx]))
    }
}

fn comment(input: &[u8]) -> ParseResult<&[u8]> {
    let (input, _) = try!(tag(input, "::"));
    // XXX: The initial bit with the "::" won't make it to the return value.
    split_after(input, '\n' as u8)
}

fn is_whitespace(c: u8) -> bool {
    // Physical tabs not included. A physical tab in the code is a syntax
    // error.
    c == 0x10 || c == 0x13 || c == ' ' as u8
}

fn is_lowercase(c: u8) -> bool {
    c >= 'a' as u8 && c <= 'z' as u8
}

fn is_numeric(c: u8) -> bool {
    c >= '0' as u8 && c <= '9' as u8
}

fn is_ident_start(c: u8) -> bool {
    is_lowercase(c)
}

fn is_ident_middle(c: u8) -> bool {
    is_lowercase(c) || is_numeric(c) || c == '-' as u8
}

fn tag<'a>(input: &'a [u8], prefix: &str) -> ParseResult<'a, &'a [u8]> {
    let prefix = prefix.as_bytes();
    if input.len() < prefix.len() {
        return Err(input);
    }

    for (x, y) in prefix.iter().zip(input.iter()) {
        if x != y {
            return Err(input);
        }
    }

    Ok((&input[prefix.len()..], &input[0..prefix.len()]))
}

/// Parse a full word.
fn word<'a>(input: &'a [u8], s: &str) -> ParseResult<'a, &'a [u8]> {
    let ret = try!(tag(input, s));
    try!(word_break(input));
    Ok(ret)
}

fn word_break(input: &[u8]) -> ParseResult<()> {
    if input.len() == 0 {
        return Ok((input, ()));
    }
    if is_ident_middle(input[0]) {
        return Err(input);
    }
    Ok((input, ()))
}

fn split_after(input: &[u8], c: u8) -> ParseResult<&[u8]> {
    let mut idx = 1;
    for i in input.iter() {
        if *i == c {
            break;
        }
        idx += 1;
    }
    Ok((&input[idx..], &input[0..idx]))
}

#[cfg(test)]
mod test {
    use std::fmt;
    use std::str;
    use super::{ParseResult, ream};

    fn parses<T: fmt::Debug>(ret: ParseResult<T>, expect: &str) {
        assert_eq!(&format!("{:?}", ret.unwrap().1), expect);
    }

    #[test]
    fn test_parse() {
       parses(ream("123".as_bytes()), "Cell(Rune(dtzy), Atom(ud, BigUint { data: [123] }))");
       parses(ream("=>($ 3)".as_bytes()), "Cell(Rune(tsgr), Cell(Cell(Rune(cnzz), Wing([\"$\"])), Cell(Rune(dtzy), Atom(ud, BigUint { data: [3] }))))");
    }
}
<commit_msg>Tweaks<commit_after>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use num::bigint::BigUint;
use num::traits::Zero;
use nock::Noun;

use twig::{self, Twig, Rune, Odor};

pub type ParseResult<'a, T> = Result<(&'a [u8], T), &'a [u8]>;

/// Parse Hoon source code into a Twig AST.
///
/// Return trailing input after a successful parse along with the resulting
/// Twig. Return trailing input after the point of failed parse on error.
pub fn ream(mut input: &[u8]) -> ParseResult<Twig> {
    // Eat initial space
    loop {
        if let Ok((tail, _)) = space_or_comment(input) {
            input = tail;
        } else {
            break;
        }
    }
    twig(input)
}

fn twig(input: &[u8]) -> ParseResult<Twig> {
    use twig::Rune::*;

    // Just crunch through the whole set of rune data and look for all the
    // ones that look like they can be parsed naively.
    for i in twig::RUNES.iter() {
        if i.rune.is_regular() && i.rune.glyph().is_some() {
            if let Ok(x) = rune(input, i.rune) {
                return Ok(x);
            }
        }
    }

    if let Ok(x) = atom(input) {
        return Ok(x);
    }
    if let Ok(x) = wing(input) {
        return Ok(x);
    }

    Err(input)
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Form {
    Tall,
    Wide,
}

fn rune<'a>(input: &'a [u8], rune: Rune) -> ParseResult<'a, Twig> {
    assert!(rune.is_regular(),
            "Can't use standard parser on this type of rune!");
    let glyph = rune.glyph().expect("Trying to parse an unprintable rune");
    let (input, form) = try!(rune_start(input, &glyph));
    let (input, args) = try!(rune_args(input, form, rune.arity()));
    Ok((input, Twig::Cell(box Twig::Rune(rune), box args)))
}

/// Parse the start of a rune with the given glyph and determine if the rune
/// is in wide form (.*(p q) or tall form (.*  p  q).
fn rune_start<'a>(input: &'a [u8], glyph: &str) -> ParseResult<'a, Form> {
    assert!(glyph.len() == 2, "Rune isn't 2 characters");
    let (mut tail, _) = try!(tag(input, glyph));

    if let Ok((tail, _)) = tag(tail, "(") {
        return Ok((tail, Form::Wide));
    }

    if let Ok((tail, _)) = gap(tail) {
        return Ok((tail, Form::Tall));
    }

    Err(input)
}

fn rune_args(input: &[u8], form: Form, n: usize) -> ParseResult<Twig> {
    assert!(n > 0);
    if n == 1 {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_end(input, form));
        Ok((input, p))
    } else {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_sep(input, form));
        let (input, ps) = try!(rune_args(input, form, n - 1));
        Ok((input, Twig::Cell(box p, box ps)))
    }
}

fn rune_sep(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => gap(input),
        Form::Wide => tag(input, " "),
    }
}

fn rune_end(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => Ok((input, &input[..0])),
        Form::Wide => tag(input, ")"),
    }
}

fn atom(input: &[u8]) -> ParseResult<Twig> {
    if let Ok((tail, num)) = ud(input) {
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::dtzy), box Twig::Atom(Odor::ud, num))))
    } else {
        Err(input)
    }
}

/// Unsigned decimal
fn ud(input: &[u8]) -> ParseResult<BigUint> {
    let mut idx = 0;
    let mut num = String::new();
    for c in input.iter() {
        if *c >= '0' as u8 && *c <= '9' as u8 {
            num.push(*c as char);
        } else if idx > 0 && *c == '.' as u8 {
            // Ignore separator dots.
            // We're lazy, so we let them show up in any pattern after the
            // initial digit instead of insisting that they group the digits
            // in sized groups.
        } else {
            if idx == 0 {
                return Err(input);
            }
            break;
        }
        idx += 1;
    }

    Ok((&input[idx..], FromStr::from_str(&num).unwrap()))
}

fn wing(input: &[u8]) -> ParseResult<Twig> {
    let mut items = Vec::new();
    let mut tail = input;
    loop {
        if let Ok((t, term)) = term(tail) {
            let item = match str::from_utf8(term).unwrap() {
                // Dot is "subject", all of the preceding context. Instead of
                // an atom, we will insert the Nock formula for getting the
                // entire subject.
                "." => n![0, 1],
                // Buc is the default recursion symbol. Evaluates to atom
                // zero.
                "$" => Noun::Atom(0),
                // The regular terms are replaced with cord atoms.
                _ => Noun::from_bytes(term),
            };
            items.push(item);
            tail = t;
        }
        match wing_dot(tail) {
            Ok((t, _)) => {
                tail = t;
            }
            Err(_) => {
                if items.len() == 0 {
                    return Err(input);
                } else {
                    break;
                }
            }
        }
    }
    assert!(items.len() > 0);
    if items[0] == n![0, 1] {
        // Wings that start with dot produce a different twig, based on
        // experimenting with the real ream. Don't understand the full logic
        // of this yet.
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::cnts),
                       box Twig::Cell(box Twig::Wing(items),
                                      // Empty tram. XXX: Maybe we get a better twig type for trams?
                                      // Should use that here then.
                                      box Twig::Atom(Odor::ud, Zero::zero())))))
    } else {
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::cnzz), box Twig::Wing(items))))
    }
}

fn term(input: &[u8]) -> ParseResult<&[u8]> {
    // Special terms.
    for special in ["$", "."].into_iter() {
        if let Ok(x) = word(input, special) {
            return Ok(x);
        }
    }

    let mut idx = 0;
    for i in input.iter() {
        if idx == 0 && !is_ident_start(*i) {
            break;
        }

        if idx > 0 && !is_ident_middle(*i) {
            break;
        }

        idx += 1;
    }

    if idx == 0 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[..idx]))
    }
}

fn wing_dot(input: &[u8]) -> ParseResult<()> {
    if input.len() < 2 {
        return Err(input);
    }
    let (tail, _) = try!(tag(input, "."));
    if !is_ident_start(tail[0]) && tail[0] != '.' as u8 {
        Err(input)
    } else {
        Ok((tail, ()))
    }
}

fn space_or_comment(input: &[u8]) -> ParseResult<()> {
    let mut tail = input;
    loop {
        if let Ok((t, _)) = comment(tail) {
            tail = t
        } else {
            if tail.len() == 0 {
                break;
            } else if is_whitespace(tail[0]) {
                tail = &tail[1..];
            } else {
                break;
            }
        }
    }
    if tail == input {
        Err(input)
    } else {
        Ok((tail, ()))
    }
}

/// At least one newline or at least two spaces.
///
/// Used to separate tall forms in Hoon.
fn gap(input: &[u8]) -> ParseResult<&[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for i in input.iter() {
        if *i == '\n' as u8 {
            spaces += 2;
        } else if is_whitespace(*i) {
            spaces += 1;
        } else {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[0..idx]))
    }
}

fn comment(input: &[u8]) -> ParseResult<&[u8]> {
    let (input, _) = try!(tag(input, "::"));
    // XXX: The initial bit with the "::" won't make it to the return value.
    split_after(input, '\n' as u8)
}

fn is_whitespace(c: u8) -> bool {
    // Physical tabs not included. A physical tab in the code is a syntax
    // error.
    c == 0x10 || c == 0x13 || c == ' ' as u8
}

fn is_lowercase(c: u8) -> bool {
    c >= 'a' as u8 && c <= 'z' as u8
}

fn is_numeric(c: u8) -> bool {
    c >= '0' as u8 && c <= '9' as u8
}

fn is_ident_start(c: u8) -> bool {
    is_lowercase(c)
}

fn is_ident_middle(c: u8) -> bool {
    is_lowercase(c) || is_numeric(c) || c == '-' as u8
}

fn tag<'a>(input: &'a [u8], prefix: &str) -> ParseResult<'a, &'a [u8]> {
    let prefix = prefix.as_bytes();
    if input.len() < prefix.len() {
        return Err(input);
    }

    for (x, y) in prefix.iter().zip(input.iter()) {
        if x != y {
            return Err(input);
        }
    }

    Ok((&input[prefix.len()..], &input[0..prefix.len()]))
}

/// Parse a full word.
fn word<'a>(input: &'a [u8], s: &str) -> ParseResult<'a, &'a [u8]> {
    let ret = try!(tag(input, s));
    try!(word_break(input));
    Ok(ret)
}

fn word_break(input: &[u8]) -> ParseResult<()> {
    if input.len() == 0 {
        return Ok((input, ()));
    }
    if is_ident_middle(input[0]) {
        return Err(input);
    }
    Ok((input, ()))
}

fn split_after(input: &[u8], c: u8) -> ParseResult<&[u8]> {
    let mut idx = 1;
    for i in input.iter() {
        if *i == c {
            break;
        }
        idx += 1;
    }
    Ok((&input[idx..], &input[0..idx]))
}

#[cfg(test)]
mod test {
    use std::fmt;
    use std::str;
    use super::{ParseResult, ream};

    fn parses<T: fmt::Debug>(ret: ParseResult<T>, expect: &str) {
        assert_eq!(&format!("{:?}", ret.unwrap().1), expect);
    }

    #[test]
    fn test_parse() {
        parses(ream("123".as_bytes()),
               "Cell(Rune(dtzy), Atom(ud, BigUint { data: [123] }))");
        parses(ream("=>($ 3)".as_bytes()),
               "Cell(Rune(tsgr), Cell(Cell(Rune(cnzz), Wing([Atom(0)])), Cell(Rune(dtzy), \
                Atom(ud, BigUint { data: [3] }))))");
        parses(ream("=>\n$  3".as_bytes()),
               "Cell(Rune(tsgr), Cell(Cell(Rune(cnzz), Wing([Atom(0)])), Cell(Rune(dtzy), \
                Atom(ud, BigUint { data: [3] }))))");

        parses(ream(".=(a .+(b))".as_bytes()), "Cell(Rune(dtts), Cell(Cell(Rune(cnzz), Wing([Atom(97)])), Cell(Rune(dtls), Cell(Rune(cnzz), Wing([Atom(98)])))))");
    }
}
<|endoftext|>


<commit_before>use range::Range;

use {
    Whitespace,
    Token,
    UntilAnyOrWhitespace,
    Parameter,
    MetaReader,
    ParseError,
    Select,
    Optional,
};

/// A rule describes how some section of a document should be parsed.
pub enum Rule<'a> {
    /// Read whitespace.
    Whitespace(Whitespace),
    /// Match against a token.
    Token(Token<'a>),
    /// Read until any or whitespace.
    UntilAnyOrWhitespace(UntilAnyOrWhitespace<'a>),
    /// Select one of the sub rules.
    /// If the first one does not succeed, try another and so on.
    /// If all sub rules fail, then the rule fails.
    Select(Select<'a>),
    /// Read parameter.
    Parameter(Parameter<'a>),
    /// Read optional.
    Optional(Optional<'a>),
}

impl<'a> Rule<'a> {
    /// Parses rule.
    pub fn parse<M>(
        &self,
        meta_reader: &mut M,
        state: &M::State,
        chars: &[char],
        offset: usize
    ) -> Result<(Range, M::State), (Range, ParseError)>
        where M: MetaReader
    {
        match self {
            &Rule::Whitespace(ref w) => {
                w.parse(chars, offset).map(|r| (r, state.clone()))
            }
            &Rule::Token(ref t) => {
                t.parse(meta_reader, state, chars, offset)
            }
            &Rule::UntilAnyOrWhitespace(ref u) => {
                u.parse(meta_reader, state, chars, offset)
            }
            &Rule::Select(ref s) => {
                s.parse(meta_reader, state, chars, offset)
            }
            &Rule::Parameter(ref p) => {
                p.parse(meta_reader, state, chars, offset)
            }
            &Rule::Optional(ref o) => {
                Ok(o.parse(meta_reader, state, chars, offset))
            }
        }
    }
}
<commit_msg>Added `Rule::Text` and `Rule::Number`<commit_after>use range::Range;

use {
    Whitespace,
    Token,
    UntilAnyOrWhitespace,
    Text,
    Number,
    Parameter,
    MetaReader,
    ParseError,
    Select,
    Optional,
};

/// A rule describes how some section of a document should be parsed.
pub enum Rule<'a> {
    /// Read whitespace.
    Whitespace(Whitespace),
    /// Match against a token.
    Token(Token<'a>),
    /// Read until any or whitespace.
    UntilAnyOrWhitespace(UntilAnyOrWhitespace<'a>),
    /// Read text.
    Text(Text<'a>),
    /// Read number.
    Number(Number<'a>),
    /// Select one of the sub rules.
    /// If the first one does not succeed, try another and so on.
    /// If all sub rules fail, then the rule fails.
    Select(Select<'a>),
    /// Read parameter.
    Parameter(Parameter<'a>),
    /// Read optional.
    Optional(Optional<'a>),
}

impl<'a> Rule<'a> {
    /// Parses rule.
    pub fn parse<M>(
        &self,
        meta_reader: &mut M,
        state: &M::State,
        chars: &[char],
        offset: usize
    ) -> Result<(Range, M::State), (Range, ParseError)>
        where M: MetaReader
    {
        match self {
            &Rule::Whitespace(ref w) => {
                w.parse(chars, offset).map(|r| (r, state.clone()))
            }
            &Rule::Token(ref t) => {
                t.parse(meta_reader, state, chars, offset)
            }
            &Rule::UntilAnyOrWhitespace(ref u) => {
                u.parse(meta_reader, state, chars, offset)
            }
            &Rule::Text(ref t) => {
                t.parse(meta_reader, state, chars, offset)
            }
            &Rule::Number(ref n) => {
                n.parse(meta_reader, state, chars, offset)
            }
            &Rule::Select(ref s) => {
                s.parse(meta_reader, state, chars, offset)
            }
            &Rule::Parameter(ref p) => {
                p.parse(meta_reader, state, chars, offset)
            }
            &Rule::Optional(ref o) => {
                Ok(o.parse(meta_reader, state, chars, offset))
            }
        }
    }
}
<|endoftext|>


<commit_before>macro_rules! try_lock {
    ($x:expr) => (match $x.try_lock() {
        ::std::result::Result::Ok(guard) => guard,
        _ => return,
    });
}

pub mod interactive {
    use super::super::model;
    use ::gtk;
    use ::gtk::WidgetExt;
    use ::mcts::State;
    use ::thud_game::Action;
    use ::thud_game::coordinate::Coordinate;
    use std::collections::HashMap;
    use std::collections::hash_map::Entry;
    use std::sync::{Arc, Mutex};

    pub fn mouse_down(widget: &gtk::DrawingArea, coordinate: &Coordinate,
                      data: Arc<Mutex<model::Interactive>>) {
        let mut data = try_lock!(data);
        data.mouse_down = Some(*coordinate);
        widget.queue_draw();
    }

    pub fn mouse_up(widget: &gtk::DrawingArea, up_coordinate: &Coordinate,
                    data: Arc<Mutex<model::Interactive>>) {
        let mut data = try_lock!(data);
        let down_coordinate = match data.mouse_down {
            Some(c) => c,
            None => return,
        };
        data.mouse_down = None;
        if *up_coordinate != down_coordinate {
                return
        }
        match data.input_mode.clone() {
            model::InputMode::Waiting =>
                select_from(widget, *up_coordinate, &mut data),
            model::InputMode::Selected { from, actions } =>
                select_target(widget, from, *up_coordinate, actions, &mut data),
            model::InputMode::Targeted { from: _, to: _, action, from_actions: _ } =>
                do_action(widget, action, &mut data),
            _ => (),
        }
    }

    /// User clicked on the board square `from`.
    fn select_from(widget: &gtk::DrawingArea, from: Coordinate, data: &mut model::Interactive) {
        match data.state.cells()[from].role() {
            Some(r) if r == *data.state.active_player() => {
                let mut actions: HashMap<Coordinate, Action> = HashMap::new();
                for a in data.state.actions() {
                    if let Some(t) = a.target() {
                        match actions.entry(t) {
                            Entry::Occupied(ref mut e) if a.is_shove() && e.get().is_move() =>
                                *e.get_mut() = a,
                            Entry::Vacant(e) => {
                                e.insert(a);
                            },
                            _ => (),
                        }
                    }
                }
                data.input_mode = model::InputMode::Selected {
                    from: from,
                    actions: actions,
                };
                widget.queue_draw();
            },
            _ => (),
        }
    }

    /// User clicked on the board square `to` after selecting a piece from their
    /// side on the board square `from`.
    fn select_target(widget: &gtk::DrawingArea, from: Coordinate, to: Coordinate,
                     actions: HashMap<Coordinate, Action>, data: &mut model::Interactive) {
        match data.state.cells()[from].role() {
            Some(r) if r != *data.state.active_player() => {
                if let Some(action) = actions.get(&to) {
                    data.input_mode = model::InputMode::Targeted {
                        from: from, to: to, action: *action, from_actions: actions.clone(),
                    };
                    widget.queue_draw();
                }
            },
            _ => (),
        }
    }

    /// User confirmed that they want to perform `action`.
    fn do_action(widget: &gtk::DrawingArea, action: Action, data: &mut model::Interactive) {
        data.state.do_action(&action);
        if data.interactive_roles.is_interactive(data.state.active_role()) {
            data.input_mode = model::InputMode::Waiting;
        } else {
            data.input_mode = model::InputMode::Inactive;
            // TODO: launch AI move.
        }
        widget.queue_draw();
    }
}
<commit_msg>Preparing to get AI moves into thud_gtk.<commit_after>macro_rules! try_lock {
    ($x:expr) => (match $x.try_lock() {
        ::std::result::Result::Ok(guard) => guard,
        _ => return,
    });
}

macro_rules! try_lock_or_return {
    ($x:expr, $retval:expr) => (match $x.try_lock() {
        ::std::result::Result::Ok(guard) => guard,
        _ => return $retval,
    });
}

pub mod interactive {
    use super::super::model;
    use ::gtk;
    use ::gtk::prelude::*;
    use ::mcts::State;
    use ::thud_game::Action;
    use ::thud_game::coordinate::Coordinate;
    use std::collections::HashMap;
    use std::collections::hash_map::Entry;
    use std::sync::{Arc, Mutex};

    pub fn mouse_down(widget: &gtk::DrawingArea, coordinate: &Coordinate,
                      data: Arc<Mutex<model::Interactive>>) {
        let mut data = try_lock!(data);
        data.mouse_down = Some(*coordinate);
        widget.queue_draw();
    }

    pub fn mouse_up(widget: &gtk::DrawingArea, up_coordinate: &Coordinate,
                    data: Arc<Mutex<model::Interactive>>) {
        let mut data = try_lock!(data);
        let down_coordinate = match data.mouse_down {
            Some(c) => c,
            None => return,
        };
        data.mouse_down = None;
        if *up_coordinate != down_coordinate {
                return
        }
        match data.input_mode.clone() {
            model::InputMode::Waiting =>
                select_from(widget, *up_coordinate, &mut data),
            model::InputMode::Selected { from, actions } =>
                select_target(widget, from, *up_coordinate, actions, &mut data),
            model::InputMode::Targeted { from: _, to: _, action, from_actions: _ } =>
                do_action(widget, action, &mut data),
            _ => (),
        }
    }

    pub fn ai_ready(widget: &gtk::DrawingArea, action: Action,
                    data: Arc<Mutex<model::Interactive>>) {
        // TODO: repeat this until success, instead of discarding the AI action
        // if we can't get a lock.
        let mut data = try_lock!(data);
        do_action(widget, action, &mut data);
    }

    /// User clicked on the board square `from`.
    fn select_from(widget: &gtk::DrawingArea, from: Coordinate, data: &mut model::Interactive) {
        match data.state.cells()[from].role() {
            Some(r) if r == *data.state.active_player() => {
                let mut actions: HashMap<Coordinate, Action> = HashMap::new();
                for a in data.state.actions() {
                    if let Some(t) = a.target() {
                        match actions.entry(t) {
                            Entry::Occupied(ref mut e) if a.is_shove() && e.get().is_move() =>
                                *e.get_mut() = a,
                            Entry::Vacant(e) => {
                                e.insert(a);
                            },
                            _ => (),
                        }
                    }
                }
                data.input_mode = model::InputMode::Selected {
                    from: from,
                    actions: actions,
                };
                widget.queue_draw();
            },
            _ => (),
        }
    }

    /// User clicked on the board square `to` after selecting a piece from their
    /// side on the board square `from`.
    fn select_target(widget: &gtk::DrawingArea, from: Coordinate, to: Coordinate,
                     actions: HashMap<Coordinate, Action>, data: &mut model::Interactive) {
        match data.state.cells()[from].role() {
            Some(r) if r != *data.state.active_player() => {
                if let Some(action) = actions.get(&to) {
                    data.input_mode = model::InputMode::Targeted {
                        from: from, to: to, action: *action, from_actions: actions.clone(),
                    };
                    widget.queue_draw();
                }
            },
            _ => (),
        }
    }

    /// User confirmed that they want to perform `action`.
    fn do_action(widget: &gtk::DrawingArea, action: Action, data: &mut model::Interactive) {
        data.state.do_action(&action);
        if data.interactive_roles.is_interactive(data.state.active_role()) {
            data.input_mode = model::InputMode::Waiting;
        } else {
            data.input_mode = model::InputMode::Inactive;
            // TODO: launch AI move.
        }
        widget.queue_draw();
    }
}
<|endoftext|>


<commit_before>//! AST -> IR Translation
//!
//! This module translates the tree-like AST representation into the more linear
//! intermediate representation (IR). In the process, the code is made more
//! explicit. This includes the evaluation order, the storage location of
//! temporary values and (conditional) branch information.
//!
//! # Register-to-register IR
//!
//! As described in *Engineering a Compiler*, there are two types of intermediate
//! representations: memory-to-memory and register-to-register. The former assumes
//! all instructions to operate on memory locations. In this case the register
//! allocator is just an optimization to minimize memory access.
//! On the other hand, register-to-register IRs assume an infinite number of
//! registers. Also, instructions operate ONLY on registers! Here the register
//! allocator is indispensable because the target architecture (usually) doesn't
//! provide enough registers.
//! In this compiler I've choosen the register-to-register model as both
//! *Introduction to Compiler Design*, *Engineering a Compiler* and the LLVM IR
//! use it too.
//!
//! # Storage locations/choice of registers
//!
//! As stated in *Introduction to Compiler Design*, there are two approaches to
//! translating expressions:
//!
//! 1. Each sub-expression chooses a storage location and passes it up to the
//!    parent.
//! 2. The parent determines a storage location for its children's values.
//!
//! In *Introduction to Compiler Design* the first approach is preferred because
//! sub-expressions might alter some values needed by the parent (e.g.
//! `x + (x = 2)` (?). I think such behaviour it not possible in `RusTiny`.
//! Therefore, I might rewrite the translator to use the first approach instead.
//!
//! # Implementation notes
//!
//! Each translation function takes a mutable reference to the current block
//! (except for `translate_fn` which creates the block). That way we can commit
//! the current block and continue with a new block in the middle of a function
//! (see `commit_block_and_continue`). I think passing the pointer along is better
//! than getting a reference to the current block every time we need it using a
//! function.

// TODO: SSA verifier?

use std::collections::{HashMap, HashSet, VecDeque};
use std::mem;
use driver::interner::Ident;
use driver::session;
use front::ast;
use middle::ir::{self, Register};
use front::ast::visit::*;

mod controlflow;
mod expr;

/// Information about the function that's translated currently
#[derive(Debug)]
struct FunctionContext {
    /// The generated function body
    body: Vec<ir::Block>,

    /// The function's arguments
    stack_slots: HashSet<Ident>,

    /// All registers used in this function
    registers: HashMap<Ident, Register>,

    /// The current block's scope
    scope: ast::NodeId,

    /// A return slot used to store the return value
    /// if the return type is non-void
    return_slot: Option<Register>,

    /// The next free register to use
    next_register: u32,

    /// The current loop's exit
    loop_exit: Option<ir::Label>,
}

#[derive(Clone, Copy)]
pub enum VariableKind {
    Local,
    Stack,
    Static,
    Constant,
}

/// The destination of a computation
///
/// Sometimes we want an expression to store its result in a specific register.
/// This is expressed by `Dest::Store(..)`.
/// But sometimes we just want the computation and don't care about where the
/// the result is stored. This behaviour can be requested by using `Dest::Ignore`
#[derive(Clone, Copy, Debug)]
pub enum Dest {
    Store(Register),
    Ignore
}

/// The AST -> IR translator
pub struct Translator {
    ir: ir::Program,
    fcx: Option<FunctionContext>,
    /// As the translator might want to use the same label multiple times,
    /// we always append an index, which is stored here, to it
    next_label: HashMap<Ident, u32>,
}

impl Translator {
    pub fn new() -> Translator {
        Translator {
            ir: ir::Program::new(),
            fcx: None,
            next_label: HashMap::new(),
        }
    }

    /// Access the current function context
    ///
    /// # Panics
    ///
    /// Panics when we're not translating a function at the moment.
    ///
    fn fcx(&mut self) -> &mut FunctionContext {
        self.fcx.as_mut().unwrap()
    }

    /// Get the next free register
    fn next_free_register(&mut self) -> Register {
        let next_register = self.fcx().next_register;
        self.fcx().next_register += 1;

        let id = Ident::from_str(&next_register.to_string());
        self.register_local(id);

        Register::Local(id)
    }

    /// Get the next free label with a given base name (e.g. `id` -> `id2`)
    fn next_free_label(&mut self, basename: Ident) -> ir::Label {
        *self.next_label.entry(basename).or_insert(0) += 1;

        let mut name = basename.to_string();
        name.push_str(&self.next_label[&basename].to_string());
        ir::Label::from_str(&name)
    }

    /// Unwrap a destination
    ///
    /// When the destination contains a register, we return it. Otherwise we
    /// take the next free register.
    fn unwrap_dest(&mut self, dest: Dest) -> Register {
        match dest {
            Dest::Store(r) => {
                match r {
                    Register::Stack(id) => assert!(self.fcx().stack_slots.contains(&id), "Unregistered stack slot: {}", id),
                    Register::Local(id) => assert!(self.fcx().registers.contains_key(&id), "Unregistered register: {}", id)
                }

                r
            },
            Dest::Ignore => self.next_free_register()
        }
    }

    fn with_first_block<F>(&mut self, current: &mut ir::Block, f: F) where F: Fn(&mut ir::Block) -> () {
        match self.fcx().body.first_mut() {
            Some(first) => f(first),
            None => f(current)
        };
    }

    /// Commit the current block and continue working with an empty one
    ///
    /// *Note:* To create a new block we have to give it a label. For that reason
    /// we take a label here.
    fn commit_block_and_continue(&mut self, block: &mut ir::Block, label: ir::Label) {
        // Make sure the current block is finalized
        assert_ne!(block.last, ir::ControlFlowInstruction::NotYetProcessed);

        let mut new_block = ir::Block {
            label: label,
            inst: VecDeque::new(),
            last: ir::ControlFlowInstruction::NotYetProcessed,
            phis: Vec::new(),
        };

        mem::swap(block, &mut new_block);

        // We need to push `new_block` here as it's contains `block` after the
        // swap.
        self.fcx().body.push(new_block);
    }

    /// Commit the current block
    fn commit_block(&mut self, block: ir::Block) {
        self.fcx().body.push(block);
    }

    /// Register a local variable and return its register
    fn register_local(&mut self, id: Ident) -> Register {
        // Find a register
        let mut id_mangled = id;
        let mut i = 1;

        while self.fcx().registers.values().any(|r| *r == Register::Local(id_mangled)) {
            id_mangled = Ident::from_str(&format!("{}{}", id, i));
            i += 1;
        };

        // Register the variable
        let register = Register::Local(id_mangled);
        self.fcx().registers.insert(id, register);
        session().symbol_table.set_register(self.fcx().scope, &id, register);

        register
    }

    fn register_stack_slot(&mut self, id: Ident) -> Register {
        // Find a register
        let mut id_mangled = id;
        let mut i = 1;

        while self.fcx().stack_slots.contains(&id_mangled) {
            id_mangled = Ident::from_str(&format!("{}{}", id, i));
            i += 1;
        };

        // Register the variable
        self.fcx().stack_slots.insert(id_mangled);
        session().symbol_table.set_slot(self.fcx().scope, &id, Register::Stack(id_mangled));

        Register::Stack(id_mangled)
    }

    fn lookup_register(&mut self, reg: Register) -> Register {
        trace!("symbol table: {:#?}", session().symbol_table);

        let var = session().symbol_table.resolve_variable(self.fcx().scope, &reg.ident())
            .expect(&format!("variable {} not yet declared", reg));

        match reg {
            Register::Local(_) => var.reg.unwrap(),
            Register::Stack(_) => var.slot.unwrap(),
        }
    }

    fn variable_kind(&mut self, name: &Ident) -> VariableKind {
        if self.fcx().registers.contains_key(name) {
            VariableKind::Local
        } else if self.fcx().stack_slots.contains(name) {
            VariableKind::Stack
        } else {
            let symtable = &session().symbol_table;
            match symtable.lookup_symbol(name) {
                Some(ast::Symbol::Static { .. }) => {
                    VariableKind::Static
                },
                Some(ast::Symbol::Constant { .. }) => {
                    VariableKind::Constant
                },
                _ => panic!("{} is not a variable", name)
            }
        }
    }

    /// Translate a function
    fn trans_fn(&mut self,
                name: Ident,
                bindings: &[ast::Binding],
                ret_ty: ast::Type,
                body: &ast::Node<ast::Block>) {
        let is_void = ret_ty == ast::Type::Unit;

        // Prepare function context
        self.fcx = Some(FunctionContext {
            body: Vec::new(),
            stack_slots: HashSet::new(),
            registers: HashMap::new(),
            return_slot: None,
            scope: body.id,
            next_register: 0,
            loop_exit: None
        });

        // Prepare ast block
        let mut block = ir::Block {
            label: self.next_free_label(Ident::from_str("entry-block")),
            inst: VecDeque::new(),
            last: ir::ControlFlowInstruction::NotYetProcessed,
            phis: Vec::new(),
        };

        // Register arguments as stack slots
        for binding in bindings.iter().rev() {
            self.register_stack_slot(*binding.name);
        };

        // Translate ast block
        if is_void {
            self.trans_block(body, &mut block, Dest::Ignore);
        } else {
            // Store block value in temporary register...
            // NOTE: If the block contains a return expression, the ret_value register we pass
            // is skipped.
            let ret_slot = self.register_stack_slot(Ident::from_str("ret_slot"));
            self.fcx().return_slot = Some(ret_slot);

            let ret_value = self.next_free_register();
            self.trans_block(body, &mut block, Dest::Store(ret_value));
            // ... and store it in the memory slot
            if !block.finalized() {
                // If the block contains a 'return' expr, it already stores the result in the
                // return slot, so we don't have to do
                block.store(ir::Value::Register(ret_value), ir::Value::Register(ret_slot));
            };
        };

        // Finalize the function
        if is_void && !block.finalized() {
            block.ret(None);
        } else {
            // Build the return block
            // FIXME: If there is a single store to the return slot,
            //        return it directly and skip the alloca/store
            let ret_slot = self.fcx().return_slot.unwrap();
            self.with_first_block(&mut block, |block| block.alloc(ret_slot));

            let return_label = self.next_free_label(Ident::from_str("return"));
            if !block.finalized() {
                block.jump(return_label);
            };

            // %reg = mem[%return_slot]
            // ret %reg
            self.commit_block_and_continue(&mut block, return_label);
            let return_value = self.next_free_register();
            block.load(ir::Value::Register(ret_slot), return_value);
            block.ret(Some(ir::Value::Register(return_value)));
        };

        self.commit_block(block);

        // Emit the symbol
        let fcx = self.fcx.take().unwrap();
        self.ir.emit(ir::Symbol::Function {
            name: name,
            body: fcx.body,
            args: bindings.iter().map(|b| *b.name).collect(),
        });
    }

    /// Translate an AST code block
    fn trans_block(&mut self,
                   b: &ast::Node<ast::Block>,
                   block: &mut ir::Block,
                   dest: Dest) {
        with_reset!(self.fcx().scope, b.id, {
            for stmt in &b.stmts {
                self.trans_stmt(stmt, block);
            }

            self.trans_expr(&b.expr, block, dest);
        });
    }

    /// Translate a statement
    fn trans_stmt(&mut self,
                  stmt: &ast::Statement,
                  block: &mut ir::Block) {
        match *stmt {
            ast::Statement::Declaration { ref binding, ref value } => {
                // Allocate memory on stack for the binding
                let dst = self.register_stack_slot(*binding.name);
                self.with_first_block(block, |block| block.alloc(dst));

                // Store the expression in the new slot
                let value = self.trans_expr_to_value(value, block);
                block.store_reg(value, dst);
            },
            ast::Statement::Expression { ref val } => {
                // We don't care where the value of the expression is stored,
                // thus `Dest::Ignore`.
                self.trans_expr(val, block, Dest::Ignore);
            }
        }
    }
}

impl<'v> Visitor<'v> for Translator {
    fn visit_symbol(&mut self, s: &'v ast::Node<ast::Symbol>) {
        match **s {
            ast::Symbol::Static { ref binding, ref value } => {
                self.ir.emit(ir::Symbol::Global {
                    name: *binding.name,
                    value: ir::Immediate(value.unwrap_literal().as_u32())
                });
            },
            ast::Symbol::Constant { .. } => {
                // Will be inlined on usage
            },
            ast::Symbol::Function { ref name, ref bindings, ref ret_ty, ref body } => {
                // Get the Binding out of the Node<Binding>
                let bindings: Vec<_> = bindings.iter().map(|b| **b).collect();

                self.trans_fn(**name, &bindings, *ret_ty, body);
            }
        }
    }
}

pub fn translate(ast: &[ast::Node<ast::Symbol>]) -> ir::Program {
    let mut visitor = Translator::new();
    walk_program(&mut visitor, ast);

    visitor.ir
}<commit_msg>[docs] Add a few notes regarding AST -> IR translation<commit_after>//! AST -> IR Translation
//!
//! This module translates the tree-like AST representation into the more linear
//! intermediate representation (IR). In the process, the code is made more
//! explicit. This includes the evaluation order, the storage location of
//! temporary values and (conditional) branch information.
//!
//! # Register-to-register IR
//!
//! As described in *Engineering a Compiler*, there are two types of intermediate
//! representations: memory-to-memory and register-to-register. The former assumes
//! all instructions to operate on memory locations. In this case the register
//! allocator is just an optimization to minimize memory access.
//! On the other hand, register-to-register IRs assume an infinite number of
//! registers. Also, instructions operate ONLY on registers! Here the register
//! allocator is indispensable because the target architecture (usually) doesn't
//! provide enough registers.
//! In this compiler I've choosen the register-to-register model as both
//! *Introduction to Compiler Design*, *Engineering a Compiler* and the LLVM IR
//! use it too.
//!
//! # Storage locations/choice of registers
//!
//! As stated in *Introduction to Compiler Design*, there are two approaches to
//! translating expressions:
//!
//! 1. Each sub-expression chooses a storage location and passes it up to the
//!    parent.
//! 2. The parent determines a storage location for its children's values.
//!
//! In *Introduction to Compiler Design* the first approach is preferred because
//! sub-expressions might alter some values needed by the parent (e.g.
//! `x + (x = 2)` (?). I think such behaviour it not possible in `RusTiny`.
//! Therefore, I might rewrite the translator to use the first approach instead.
//!
//! # Implementation notes
//!
//! Each translation function takes a mutable reference to the current block
//! (except for `translate_fn` which creates the block). That way we can commit
//! the current block and continue with a new block in the middle of a function
//! (see `commit_block_and_continue`). I think passing the pointer along is better
//! than having to call a function to get a reference to the current block every
//! time we need to access it.

// TODO: SSA verifier?

use std::collections::{HashMap, HashSet, VecDeque};
use std::mem;
use driver::interner::Ident;
use driver::session;
use front::ast;
use middle::ir::{self, Register};
use front::ast::visit::*;

mod controlflow;
mod expr;

/// Information about the function that we're translating right now
///
/// ## Stack Slots and Registers
///
/// As RusTiny allows name shadowing in nested scopes, we need to
/// keep track of the alternative names we assign in case of
/// duplicates (`a` might be come `a1` or `a2` if shadowed). We
/// store these alternative names in `stack_slots` and `register`
/// respectively. Along with that we attach the alternative name
/// to symbol table. The symbol table not only stores variable
/// names but also keeps track of the scope the variable belongs
/// to.
///
/// This allows us to quickly check if a register name is already
/// in use by scanning `stack_slots` and `register` but we also
/// can retrieve the alternative name we assigned to a register
/// using the symbol table.
#[derive(Debug)]
struct FunctionContext {
    /// The generated function body
    body: Vec<ir::Block>,

    /// The function's arguments
    stack_slots: HashSet<Ident>,

    /// All registers used in this function
    registers: HashMap<Ident, Register>,  // FIXME: Can we use a HashSet<Ident> instead here?

    /// The current block's scope
    scope: ast::NodeId,

    /// A return slot used to store the return value
    /// if the return type is non-void
    return_slot: Option<Register>,

    /// The next free register to use
    next_register: u32,

    /// The current loop's exit
    loop_exit: Option<ir::Label>,
}

#[derive(Clone, Copy)]
pub enum VariableKind {
    Local,
    Stack,
    Static,
    Constant,
}

/// The destination of a computation
///
/// Sometimes we want an expression to store its result in a specific register.
/// This is expressed by `Dest::Store(..)`.
/// But sometimes we just want the computation and don't care about where the
/// the result is stored. This behaviour can be requested by using `Dest::Ignore`
#[derive(Clone, Copy, Debug)]
pub enum Dest {
    Store(Register),
    Ignore
}

/// The AST -> IR translator
pub struct Translator {
    ir: ir::Program,
    fcx: Option<FunctionContext>,
    /// As the translator might want to use the same label multiple times,
    /// we always append an index to it, which is stored here
    next_label: HashMap<Ident, u32>,
}

impl Translator {
    pub fn new() -> Translator {
        Translator {
            ir: ir::Program::new(),
            fcx: None,
            next_label: HashMap::new(),
        }
    }

    /// Access the current function context
    ///
    /// # Panics
    ///
    /// Panics when we're not translating a function at the moment.
    ///
    fn fcx(&mut self) -> &mut FunctionContext {
        self.fcx.as_mut().unwrap()
    }

    /// Get the next free register
    fn next_free_register(&mut self) -> Register {
        let next_register = self.fcx().next_register;
        self.fcx().next_register += 1;

        let id = Ident::from_str(&next_register.to_string());
        self.register_local(id);

        Register::Local(id)
    }

    /// Get the next free label with a given base name (e.g. `id` -> `id2`)
    fn next_free_label(&mut self, basename: Ident) -> ir::Label {
        *self.next_label.entry(basename).or_insert(0) += 1;

        let mut name = basename.to_string();
        name.push_str(&self.next_label[&basename].to_string());
        ir::Label::from_str(&name)
    }

    /// Unwrap a destination
    ///
    /// When the destination contains a register, we return it. Otherwise we
    /// take the next free register.
    fn unwrap_dest(&mut self, dest: Dest) -> Register {
        match dest {
            Dest::Store(r) => {
                // For debugging: make sure the destination actually exists
                match r {
                    Register::Stack(id) => assert!(self.fcx().stack_slots.contains(&id), "Unregistered stack slot: {}", id),
                    Register::Local(id) => assert!(self.fcx().registers.contains_key(&id), "Unregistered register: {}", id)
                }

                r
            },
            Dest::Ignore => self.next_free_register()
        }
    }

    fn with_first_block<F>(&mut self, current: &mut ir::Block, f: F) where F: Fn(&mut ir::Block) -> () {
        match self.fcx().body.first_mut() {
            Some(first) => f(first),
            None => f(current)
        };
    }

    /// Commit the current block and continue working with an empty one
    ///
    /// *Note:* To create a new block we have to give it a label. For that reason
    /// we take a label here.
    fn commit_block_and_continue(&mut self, block: &mut ir::Block, label: ir::Label) {
        // Make sure the current block is finalized
        assert_ne!(block.last, ir::ControlFlowInstruction::NotYetProcessed);

        let mut new_block = ir::Block {
            label: label,
            inst: VecDeque::new(),
            last: ir::ControlFlowInstruction::NotYetProcessed,
            phis: Vec::new(),
        };

        mem::swap(block, &mut new_block);

        // We need to push `new_block` here as it's contains `block` after the
        // swap.
        self.fcx().body.push(new_block);
    }

    /// Commit the current block
    fn commit_block(&mut self, block: ir::Block) {
        self.fcx().body.push(block);
    }

    /// Register a local variable and return its register
    fn register_local(&mut self, id: Ident) -> Register {
        // Find a register name
        let mut id_mangled = id;
        let mut i = 1;

        while self.fcx().registers.values().any(|r| *r == Register::Local(id_mangled)) {
            id_mangled = Ident::from_str(&format!("{}{}", id, i));
            i += 1;
        };

        // Register the variable
        let register = Register::Local(id_mangled);
        self.fcx().registers.insert(id, register);
        session().symbol_table.set_register(self.fcx().scope, &id, register);

        register
    }

    /// Register a stack slot variable and return its register
    fn register_stack_slot(&mut self, id: Ident) -> Register {
        // Find a stack slot name
        let mut id_mangled = id;
        let mut i = 1;

        while self.fcx().stack_slots.contains(&id_mangled) {
            id_mangled = Ident::from_str(&format!("{}{}", id, i));
            i += 1;
        };

        // Register the variable
        self.fcx().stack_slots.insert(id_mangled);
        session().symbol_table.set_slot(self.fcx().scope, &id, Register::Stack(id_mangled));

        Register::Stack(id_mangled)
    }

    /// Look up the alternative name we assigned in case of variable shadowing
    fn lookup_register(&mut self, reg: Register) -> Register {
        let var = session().symbol_table.resolve_variable(self.fcx().scope, &reg.ident())
            .expect(&format!("variable {} not yet declared", reg));

        match reg {
            Register::Local(_) => var.reg.expect(&format!("No register assigned to {}", reg)),
            Register::Stack(_) => var.slot.expect(&format!("No stack slot assigned to {}", reg)),
        }
    }

    /// Determine the kind of variable the name refers to
    fn variable_kind(&mut self, name: &Ident) -> VariableKind {
        if self.fcx().registers.contains_key(name) {
            VariableKind::Local
        } else if self.fcx().stack_slots.contains(name) {
            VariableKind::Stack
        } else {
            match session().symbol_table.lookup_symbol(name) {
                Some(ast::Symbol::Static { .. }) => VariableKind::Static,
                Some(ast::Symbol::Constant { .. }) => VariableKind::Constant,
                _ => panic!("{} is not a variable", name)
            }
        }
    }

    /// Translate a function
    fn trans_fn(&mut self,
                name: Ident,
                bindings: &[ast::Binding],
                ret_ty: ast::Type,
                body: &ast::Node<ast::Block>) {
        let is_void = ret_ty == ast::Type::Unit;

        // Prepare function context
        self.fcx = Some(FunctionContext {
            body: Vec::new(),
            stack_slots: HashSet::new(),
            registers: HashMap::new(),
            return_slot: None,
            scope: body.id,
            next_register: 0,
            loop_exit: None
        });

        // Prepare ast block
        let mut block = ir::Block {
            label: self.next_free_label(Ident::from_str("entry-block")),
            inst: VecDeque::new(),
            last: ir::ControlFlowInstruction::NotYetProcessed,
            phis: Vec::new(),
        };

        // Register arguments as stack slots
        for binding in bindings.iter().rev() {
            self.register_stack_slot(*binding.name);
        };

        // Translate ast block
        if is_void {
            self.trans_block(body, &mut block, Dest::Ignore);
        } else {
            // Store block value in temporary register...
            // NOTE: If the block contains a return expression, the ret_value register we pass
            // is skipped.
            let ret_slot = self.register_stack_slot(Ident::from_str("ret_slot"));
            self.fcx().return_slot = Some(ret_slot);

            let ret_value = self.next_free_register();
            self.trans_block(body, &mut block, Dest::Store(ret_value));
            // ... and store it in the memory slot
            if !block.finalized() {
                // If the block contains a 'return' expr, it already stores the result in the
                // return slot, so we don't have to do
                block.store(ir::Value::Register(ret_value), ir::Value::Register(ret_slot));
            };
        };

        // Finalize the function
        if is_void && !block.finalized() {
            block.ret(None);
        } else {
            // Build the return block
            // FIXME: If there is a single store to the return slot,
            //        return it directly and skip the alloca/store
            let ret_slot = self.fcx().return_slot.unwrap();
            self.with_first_block(&mut block, |block| block.alloc(ret_slot));

            let return_label = self.next_free_label(Ident::from_str("return"));
            if !block.finalized() {
                block.jump(return_label);
            };

            // %reg = mem[%return_slot]
            // ret %reg
            self.commit_block_and_continue(&mut block, return_label);
            let return_value = self.next_free_register();
            block.load(ir::Value::Register(ret_slot), return_value);
            block.ret(Some(ir::Value::Register(return_value)));
        };

        self.commit_block(block);

        // Emit the symbol
        let fcx = self.fcx.take().unwrap();
        self.ir.emit(ir::Symbol::Function {
            name: name,
            body: fcx.body,
            args: bindings.iter().map(|b| *b.name).collect(),
        });
    }

    /// Translate an AST code block
    fn trans_block(&mut self,
                   b: &ast::Node<ast::Block>,
                   block: &mut ir::Block,
                   dest: Dest) {
        with_reset!(self.fcx().scope, b.id, {
            for stmt in &b.stmts {
                self.trans_stmt(stmt, block);
            }

            self.trans_expr(&b.expr, block, dest);
        });
    }

    /// Translate a statement
    fn trans_stmt(&mut self,
                  stmt: &ast::Statement,
                  block: &mut ir::Block) {
        match *stmt {
            ast::Statement::Declaration { ref binding, ref value } => {
                // Allocate memory on stack for the binding
                let dst = self.register_stack_slot(*binding.name);
                self.with_first_block(block, |block| block.alloc(dst));

                // Store the expression in the new slot
                let value = self.trans_expr_to_value(value, block);
                block.store_reg(value, dst);
            },
            ast::Statement::Expression { ref val } => {
                // We don't care where the value of the expression is stored,
                // thus `Dest::Ignore`.
                self.trans_expr(val, block, Dest::Ignore);
            }
        }
    }
}

impl<'v> Visitor<'v> for Translator {
    fn visit_symbol(&mut self, s: &'v ast::Node<ast::Symbol>) {
        match **s {
            ast::Symbol::Static { ref binding, ref value } => {
                self.ir.emit(ir::Symbol::Global {
                    name: *binding.name,
                    value: ir::Immediate(value.unwrap_literal().as_u32())
                });
            },
            ast::Symbol::Constant { .. } => {
                // Will be inlined on usage
            },
            ast::Symbol::Function { ref name, ref bindings, ref ret_ty, ref body } => {
                // Get the Binding out of the Node<Binding>
                let bindings: Vec<_> = bindings.iter().map(|b| **b).collect();

                self.trans_fn(**name, &bindings, *ret_ty, body);
            }
        }
    }
}

pub fn translate(ast: &[ast::Node<ast::Symbol>]) -> ir::Program {
    let mut visitor = Translator::new();
    walk_program(&mut visitor, ast);

    visitor.ir
}<|endoftext|>


<commit_before>use errors::*;
use multirust_dist::{utils, dist};
use multirust_dist::prefix::InstallPrefix;
use multirust_dist::dist::ToolchainDesc;
use multirust_dist::manifestation::{Manifestation, Changes};
use multirust_dist::manifest::Component;
use config::Cfg;
use env_var;
use install::{self, InstallMethod};

use std::process::Command;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::env;

use hyper;
use multirust_dist;

#[derive(Debug)]
pub struct Toolchain<'a> {
    cfg: &'a Cfg,
    name: String,
    path: PathBuf,
}

impl<'a> Toolchain<'a> {
    pub fn from(cfg: &'a Cfg, name: &str) -> Self {
        let path = cfg.toolchains_dir.join(name);
        Toolchain {
            cfg: cfg,
            name: name.to_owned(),
            path: path.clone(),
        }
    }
    pub fn cfg(&self) -> &'a Cfg {
        self.cfg
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn exists(&self) -> bool {
        utils::is_directory(&self.path)
    }
    pub fn verify(&self) -> Result<()> {
        Ok(try!(utils::assert_is_directory(&self.path)))
    }
    pub fn remove(&self) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name));
            return Ok(());
        }
        if let Some(update_hash) = try!(self.update_hash()) {
            try!(utils::remove_file("update hash", &update_hash));
        }
        let handler = self.cfg.notify_handler.as_ref();
        let result = install::uninstall(&self.path, ntfy!(&handler));
        if !self.exists() {
            self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name));
        }
        Ok(try!(result))
    }
    pub fn remove_if_exists(&self) -> Result<()> {
        if self.exists() {
            self.remove()
        } else {
            Ok(())
        }
    }
    pub fn install(&self, install_method: InstallMethod) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name));
        }
        self.cfg
            .notify_handler
            .call(Notification::ToolchainDirectory(&self.path, &self.name));
        let handler = self.cfg.notify_handler.as_ref();
        Ok(try!(install_method.run(&self.path, ntfy!(&handler))))
    }
    pub fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> {
        self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name));
        if !self.exists() {
            self.install(install_method)
        } else {
            self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name));
            Ok(())
        }
    }
    pub fn update_hash(&self) -> Result<Option<PathBuf>> {
        if self.is_custom() {
            Ok(None)
        } else {
            Ok(Some(try!(self.cfg.get_hash_file(&self.name, true))))
        }
    }

    fn download_cfg(&self) -> dist::DownloadCfg {
        dist::DownloadCfg {
            dist_root: &self.cfg.dist_root_url,
            temp_cfg: &self.cfg.temp_cfg,
            notify_handler: ntfy!(&self.cfg.notify_handler),
        }
    }

    pub fn install_from_dist(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install(InstallMethod::Dist(&self.name,
                                         update_hash.as_ref().map(|p| &**p),
                                         self.download_cfg()))
    }
    pub fn install_from_dist_if_not_installed(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install_if_not_installed(InstallMethod::Dist(&self.name,
                                                          update_hash.as_ref().map(|p| &**p),
                                                          self.download_cfg()))
    }
    pub fn is_custom(&self) -> bool {
        ToolchainDesc::from_str(&self.name).is_err()
    }
    pub fn is_tracking(&self) -> bool {
        ToolchainDesc::from_str(&self.name).ok().map(|d| d.is_tracking()) == Some(true)
    }

    pub fn ensure_custom(&self) -> Result<()> {
        if !self.is_custom() {
            Err(Error::Install(multirust_dist::Error::InvalidToolchainName(self.name.to_string())))
        } else {
            Ok(())
        }
    }

    pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> {
        try!(self.ensure_custom());

        try!(self.remove_if_exists());

        // FIXME: This should do all downloads first, then do
        // installs, and do it all in a single transaction.
        for installer in installers {
            let installer_str = installer.to_str().unwrap_or("bogus");
            match installer_str.rfind(".") {
                Some(i) => {
                    let extension = &installer_str[i+1..];
                    if extension != "gz" {
                        return Err(Error::BadInstallerType(extension.to_string()));
                    }
                }
                None => return Err(Error::BadInstallerType(String::from("(none)")))
            }

            // FIXME: Pretty hacky
            let is_url = installer_str.starts_with("file://")
                || installer_str.starts_with("http://")
                || installer_str.starts_with("https://");
            let url = hyper::Url::parse(installer_str).ok();
            let url = if is_url { url } else { None };
            if let Some(url) = url {

                // Download to a local file
                let local_installer = try!(self.cfg.temp_cfg.new_file_with_ext("", ".tar.gz"));
                try!(utils::download_file(url,
                                          &local_installer,
                                          None,
                                          ntfy!(&self.cfg.notify_handler)));
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            } else {
                // If installer is a filename

                // No need to download
                let local_installer = Path::new(installer);

                // Install from file
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            }
        }

        Ok(())
    }

    pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> {
        try!(self.ensure_custom());

        if link {
            self.install(InstallMethod::Link(&try!(utils::to_absolute(src))))
        } else {
            self.install(InstallMethod::Copy(src))
        }
    }

    pub fn create_command<T: AsRef<OsStr>>(&self, binary: T) -> Result<Command> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let mut cmd = Command::new(binary);
        self.set_env(&mut cmd);
        Ok(cmd)
    }

    fn set_env(&self, cmd: &mut Command) {
        let ref cargo_home = self.cfg.multirust_dir.join("cargo");
        let ref bin_path = self.path.join("bin");

        self.set_ldpath(cmd);

        env_var::set_path("PATH", bin_path, cmd);
        env_var::set_default("CARGO_HOME", cargo_home.as_ref(), cmd);
        env_var::inc("RUST_RECURSION_COUNT", cmd);

        // FIXME: This should not be a path, but a toolchain name.
        // Not sure what's going on here.
        cmd.env("MULTIRUST_TOOLCHAIN", &self.path);
        cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir);
    }

    pub fn set_ldpath(&self, cmd: &mut Command) {
        let new_path = self.path.join("lib");

        env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd);
        env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd);
    }

    pub fn doc_path(&self, relative: &str) -> Result<PathBuf> {
        try!(self.verify());

        let parts = vec!["share", "doc", "rust", "html"];
        let mut doc_dir = self.path.clone();
        for part in parts {
            doc_dir.push(part);
        }
        doc_dir.push(relative);

        Ok(doc_dir)
    }
    pub fn open_docs(&self, relative: &str) -> Result<()> {
        try!(self.verify());

        Ok(try!(utils::open_browser(&try!(self.doc_path(relative)))))
    }

    pub fn make_default(&self) -> Result<()> {
        self.cfg.set_default(&self.name)
    }
    pub fn make_override(&self, path: &Path) -> Result<()> {
        Ok(try!(self.cfg.override_db.set(path,
                                         &self.name,
                                         &self.cfg.temp_cfg,
                                         self.cfg.notify_handler.as_ref())))
    }

    pub fn list_components(&self) -> Result<Vec<Component>> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        // FIXME: This toolchain handling is a mess. Just do it once
        // when the toolchain is created.
        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {
            // Return all optional components of the "rust" package for the
            // toolchain's target triple.
            let mut res = Vec::new();

            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            for extension in &targ_pkg.extensions {
                res.push(extension.clone())
            }

            Ok(res)
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn add_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::AddingRequiredComponent(self.name.to_string(), component));
            }

            if !targ_pkg.extensions.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![component],
                remove_extensions: vec![]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn remove_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::RemovingRequiredComponent(self.name.to_string(), component));
            }

            let dist_config = try!(manifestation.read_config()).unwrap();
            if !dist_config.components.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![],
                remove_extensions: vec![component]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn binary_file(&self, name: &str) -> PathBuf {
        let mut path = self.path.clone();
        path.push("bin");
        path.push(name.to_owned() + env::consts::EXE_SUFFIX);
        path
    }
}
<commit_msg>Move some Toolchain methods private<commit_after>use errors::*;
use multirust_dist::{utils, dist};
use multirust_dist::prefix::InstallPrefix;
use multirust_dist::dist::ToolchainDesc;
use multirust_dist::manifestation::{Manifestation, Changes};
use multirust_dist::manifest::Component;
use config::Cfg;
use env_var;
use install::{self, InstallMethod};

use std::process::Command;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::env;

use hyper;
use multirust_dist;

#[derive(Debug)]
pub struct Toolchain<'a> {
    cfg: &'a Cfg,
    name: String,
    path: PathBuf,
}

impl<'a> Toolchain<'a> {
    pub fn from(cfg: &'a Cfg, name: &str) -> Self {
        let path = cfg.toolchains_dir.join(name);
        Toolchain {
            cfg: cfg,
            name: name.to_owned(),
            path: path.clone(),
        }
    }
    pub fn cfg(&self) -> &'a Cfg {
        self.cfg
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn exists(&self) -> bool {
        utils::is_directory(&self.path)
    }
    pub fn verify(&self) -> Result<()> {
        Ok(try!(utils::assert_is_directory(&self.path)))
    }
    pub fn remove(&self) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name));
            return Ok(());
        }
        if let Some(update_hash) = try!(self.update_hash()) {
            try!(utils::remove_file("update hash", &update_hash));
        }
        let handler = self.cfg.notify_handler.as_ref();
        let result = install::uninstall(&self.path, ntfy!(&handler));
        if !self.exists() {
            self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name));
        }
        Ok(try!(result))
    }
    pub fn remove_if_exists(&self) -> Result<()> {
        if self.exists() {
            self.remove()
        } else {
            Ok(())
        }
    }
    fn install(&self, install_method: InstallMethod) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name));
        }
        self.cfg
            .notify_handler
            .call(Notification::ToolchainDirectory(&self.path, &self.name));
        let handler = self.cfg.notify_handler.as_ref();
        Ok(try!(install_method.run(&self.path, ntfy!(&handler))))
    }
    fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> {
        self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name));
        if !self.exists() {
            self.install(install_method)
        } else {
            self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name));
            Ok(())
        }
    }
    pub fn update_hash(&self) -> Result<Option<PathBuf>> {
        if self.is_custom() {
            Ok(None)
        } else {
            Ok(Some(try!(self.cfg.get_hash_file(&self.name, true))))
        }
    }

    fn download_cfg(&self) -> dist::DownloadCfg {
        dist::DownloadCfg {
            dist_root: &self.cfg.dist_root_url,
            temp_cfg: &self.cfg.temp_cfg,
            notify_handler: ntfy!(&self.cfg.notify_handler),
        }
    }

    pub fn install_from_dist(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install(InstallMethod::Dist(&self.name,
                                         update_hash.as_ref().map(|p| &**p),
                                         self.download_cfg()))
    }
    pub fn install_from_dist_if_not_installed(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install_if_not_installed(InstallMethod::Dist(&self.name,
                                                          update_hash.as_ref().map(|p| &**p),
                                                          self.download_cfg()))
    }
    pub fn is_custom(&self) -> bool {
        ToolchainDesc::from_str(&self.name).is_err()
    }
    pub fn is_tracking(&self) -> bool {
        ToolchainDesc::from_str(&self.name).ok().map(|d| d.is_tracking()) == Some(true)
    }

    pub fn ensure_custom(&self) -> Result<()> {
        if !self.is_custom() {
            Err(Error::Install(multirust_dist::Error::InvalidToolchainName(self.name.to_string())))
        } else {
            Ok(())
        }
    }

    pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> {
        try!(self.ensure_custom());

        try!(self.remove_if_exists());

        // FIXME: This should do all downloads first, then do
        // installs, and do it all in a single transaction.
        for installer in installers {
            let installer_str = installer.to_str().unwrap_or("bogus");
            match installer_str.rfind(".") {
                Some(i) => {
                    let extension = &installer_str[i+1..];
                    if extension != "gz" {
                        return Err(Error::BadInstallerType(extension.to_string()));
                    }
                }
                None => return Err(Error::BadInstallerType(String::from("(none)")))
            }

            // FIXME: Pretty hacky
            let is_url = installer_str.starts_with("file://")
                || installer_str.starts_with("http://")
                || installer_str.starts_with("https://");
            let url = hyper::Url::parse(installer_str).ok();
            let url = if is_url { url } else { None };
            if let Some(url) = url {

                // Download to a local file
                let local_installer = try!(self.cfg.temp_cfg.new_file_with_ext("", ".tar.gz"));
                try!(utils::download_file(url,
                                          &local_installer,
                                          None,
                                          ntfy!(&self.cfg.notify_handler)));
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            } else {
                // If installer is a filename

                // No need to download
                let local_installer = Path::new(installer);

                // Install from file
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            }
        }

        Ok(())
    }

    pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> {
        try!(self.ensure_custom());

        if link {
            self.install(InstallMethod::Link(&try!(utils::to_absolute(src))))
        } else {
            self.install(InstallMethod::Copy(src))
        }
    }

    pub fn create_command<T: AsRef<OsStr>>(&self, binary: T) -> Result<Command> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let mut cmd = Command::new(binary);
        self.set_env(&mut cmd);
        Ok(cmd)
    }

    fn set_env(&self, cmd: &mut Command) {
        let ref cargo_home = self.cfg.multirust_dir.join("cargo");
        let ref bin_path = self.path.join("bin");

        self.set_ldpath(cmd);

        env_var::set_path("PATH", bin_path, cmd);
        env_var::set_default("CARGO_HOME", cargo_home.as_ref(), cmd);
        env_var::inc("RUST_RECURSION_COUNT", cmd);

        // FIXME: This should not be a path, but a toolchain name.
        // Not sure what's going on here.
        cmd.env("MULTIRUST_TOOLCHAIN", &self.path);
        cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir);
    }

    pub fn set_ldpath(&self, cmd: &mut Command) {
        let new_path = self.path.join("lib");

        env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd);
        env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd);
    }

    pub fn doc_path(&self, relative: &str) -> Result<PathBuf> {
        try!(self.verify());

        let parts = vec!["share", "doc", "rust", "html"];
        let mut doc_dir = self.path.clone();
        for part in parts {
            doc_dir.push(part);
        }
        doc_dir.push(relative);

        Ok(doc_dir)
    }
    pub fn open_docs(&self, relative: &str) -> Result<()> {
        try!(self.verify());

        Ok(try!(utils::open_browser(&try!(self.doc_path(relative)))))
    }

    pub fn make_default(&self) -> Result<()> {
        self.cfg.set_default(&self.name)
    }
    pub fn make_override(&self, path: &Path) -> Result<()> {
        Ok(try!(self.cfg.override_db.set(path,
                                         &self.name,
                                         &self.cfg.temp_cfg,
                                         self.cfg.notify_handler.as_ref())))
    }

    pub fn list_components(&self) -> Result<Vec<Component>> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        // FIXME: This toolchain handling is a mess. Just do it once
        // when the toolchain is created.
        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {
            // Return all optional components of the "rust" package for the
            // toolchain's target triple.
            let mut res = Vec::new();

            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            for extension in &targ_pkg.extensions {
                res.push(extension.clone())
            }

            Ok(res)
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn add_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::AddingRequiredComponent(self.name.to_string(), component));
            }

            if !targ_pkg.extensions.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![component],
                remove_extensions: vec![]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn remove_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::RemovingRequiredComponent(self.name.to_string(), component));
            }

            let dist_config = try!(manifestation.read_config()).unwrap();
            if !dist_config.components.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![],
                remove_extensions: vec![component]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn binary_file(&self, name: &str) -> PathBuf {
        let mut path = self.path.clone();
        path.push("bin");
        path.push(name.to_owned() + env::consts::EXE_SUFFIX);
        path
    }
}
<|endoftext|>


<commit_before>use std;
import std::sio;

fn main() {
  let cx: sio::ctx = sio::new();
  let srv: sio::server = sio::create_server(cx, "0.0.0.0", 9090);
  sio::close_server(srv);
  sio::destroy(cx);
}
<commit_msg>XFAIL sio-srv<commit_after>xfail-stage1
xfail-stage2
xfail-stage3

use std;
import std::sio;

fn main() {
  let cx: sio::ctx = sio::new();
  let srv: sio::server = sio::create_server(cx, "127.0.0.1", 9090);
  sio::close_server(srv);
  sio::destroy(cx);
}
<|endoftext|>