<commit_before>use token::{Lexeme, Token};
use error::{Result, SassError, ErrorKind};

use std::str::CharIndices;
use std::iter::Peekable;

pub struct Tokenizer<'a> {
    text: &'a str,
    chars: Peekable<CharIndices<'a>>,
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Lexeme>;

    fn next(&mut self) -> Option<Result<Lexeme>> {
        match self.parse() {
            Ok(Some(t)) => Some(Ok(t)),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

impl<'a> Tokenizer<'a> {
    pub fn new(text: &str) -> Tokenizer {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    pub fn parse(&mut self) -> Result<Option<Lexeme>> {
        while let Some((char_offset, curr_char)) = self.chars.next() {
            // Skip leading whitespace
            if curr_char.is_whitespace() {
                continue;
            } else {
                let single_char_token = Token::from_char(curr_char);
                if single_char_token.is_some() {
                    let peek_char = self.peek_char();
                    if curr_char == '-' &&
                       peek_char.is_some() &&
                       !peek_char.unwrap().is_whitespace() {

                        if peek_char.unwrap().is_numeric() {
                            return self.number(curr_char, char_offset)
                        } else {
                            return self.ident(curr_char, char_offset)
                        }

                    } else {
                        // We already tested that single_char_token was Some.
                        return Ok(Some(Lexeme {
                            token: single_char_token.unwrap(),
                            offset: Some(char_offset),
                        }))
                    }
                } else {
                    if curr_char.is_numeric() {
                        return self.number(curr_char, char_offset)
                    } else {
                        return self.ident(curr_char, char_offset)
                    }
                }
            }
        }
        return Ok(None)
    }

    fn peek_char(&mut self) -> Option<char> {
        match self.chars.peek() {
            Some(&(_, peek_char)) => Some(peek_char),
            None => None,
        }
    }

    fn ident(&mut self, curr_char: char, start: usize) -> Result<Option<Lexeme>> {
        let mut value = String::new();
        value.push(curr_char);

        while let Some(peek_char) = self.peek_char() {
            // Stop when we reach a non-ident char (hyphens are special)
            if peek_char.is_whitespace() || (
                is_single_char_token(peek_char) && peek_char != '-'
            ) {
                break;
            } else {
                value.push(peek_char);
                self.chars.next();
            }
        }
        Ok(Some(Lexeme { token: Token::Ident(value), offset: Some(start) }))
    }

    fn number(&mut self, curr_char: char, start: usize) -> Result<Option<Lexeme>> {
        let mut value = String::new();
        value.push(curr_char);

        while let Some(peek_char) = self.peek_char() {
            // Stop when we reach a non-numeric char
            if !peek_char.is_numeric() && peek_char != '.' {
                break;
            } else {
                value.push(peek_char);
                self.chars.next();
            }
        }

        let value = match value.parse() {
            Ok(v) => v,
            Err(_) => return Err(SassError {
                offset: start,
                kind: ErrorKind::TokenizerError,
                message: format!(
                    "Tried to parse `{}` into a f32 but failed.",
                    value,
                ),
            })
        };
        Ok(Some(Lexeme { token: Token::Number(value), offset: Some(start) }))
    }
}

fn is_single_char_token(ch: char) -> bool {
    Token::from_char(ch).is_some()
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use error::Result;

    fn expected_lexeme(expected_token: Token, expected_offset: usize) -> Option<Result<Lexeme>> {
        Some(Ok(Lexeme {
            token: expected_token,
            offset: Some(expected_offset),
        }))
    }

    fn expected_ident(expected_value: &str, expected_offset: usize) -> Option<Result<Lexeme>> {
        expected_lexeme(Token::Ident(expected_value.into()), expected_offset)
    }

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_none_for_only_whitespace() {
        let mut tokenizer = Tokenizer::new("     ");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_words() {
        // Without regard to Sass word validity
        let mut tokenizer = Tokenizer::new(" \n  div   aoeu  ");
        assert_eq!(tokenizer.next(), expected_ident("div", 4));
        assert_eq!(tokenizer.next(), expected_ident("aoeu", 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_curly_braces() {
        // Without regard to matching
        let mut tokenizer = Tokenizer::new("{}}a{ blah}");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 2));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 4));
        assert_eq!(tokenizer.next(), expected_ident("blah", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_colon() {
        let mut tokenizer = Tokenizer::new(":invalid: property::");
        // This might be wrong. I have a note somewhere else that colons can start idents.
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 0));
        assert_eq!(tokenizer.next(), expected_ident("invalid", 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 8));
        assert_eq!(tokenizer.next(), expected_ident("property", 10));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 19));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_semicolon() {
        let mut tokenizer = Tokenizer::new(";;\na;\nb\n;");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 1));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 4));
        assert_eq!(tokenizer.next(), expected_ident("b", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 8));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_numbers() {
        let mut tokenizer = Tokenizer::new("border: 0px 1.5 11em;");
        assert_eq!(tokenizer.next(), expected_ident("border", 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(0.0), 8));
        assert_eq!(tokenizer.next(), expected_ident("px", 9));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(1.5), 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(11.0), 16));
        assert_eq!(tokenizer.next(), expected_ident("em", 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 20));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_hyphen() {
        let mut tokenizer = Tokenizer::new("font-weight -webkit -3 - 4-5 a-1 -");
        assert_eq!(tokenizer.next(), expected_ident("font-weight", 0));
        assert_eq!(tokenizer.next(), expected_ident("-webkit", 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-3.0), 20));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Minus, 23));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 25));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-5.0), 26));
        assert_eq!(tokenizer.next(), expected_ident("a-1", 29));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Minus, 33));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_parens() {
        let mut tokenizer = Tokenizer::new("() rgb)()(");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 1));
        assert_eq!(tokenizer.next(), expected_ident("rgb", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 7));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 9));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_slash() {
        let mut tokenizer = Tokenizer::new("/ / 3/4 / 8");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 2));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(3.0), 4));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 5));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(8.0), 10));
        assert_eq!(tokenizer.next(), None);
    }
}
<commit_msg>Extract methods in order to name complex conditionals<commit_after>use token::{Lexeme, Token};
use error::{Result, SassError, ErrorKind};

use std::str::CharIndices;
use std::iter::Peekable;

pub struct Tokenizer<'a> {
    text: &'a str,
    chars: Peekable<CharIndices<'a>>,
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Lexeme>;

    fn next(&mut self) -> Option<Result<Lexeme>> {
        match self.parse() {
            Ok(Some(t)) => Some(Ok(t)),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

impl<'a> Tokenizer<'a> {
    pub fn new(text: &str) -> Tokenizer {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    pub fn parse(&mut self) -> Result<Option<Lexeme>> {
        while let Some((char_offset, curr_char)) = self.chars.next() {
            // Skip leading whitespace
            if curr_char.is_whitespace() {
                continue;
            } else {
                let single_char_token = Token::from_char(curr_char);
                if single_char_token.is_some() && !self.hyphen_starting_shit(curr_char) {
                    // We already tested that single_char_token was Some.
                    return Ok(Some(Lexeme {
                        token: single_char_token.unwrap(),
                        offset: Some(char_offset),
                    }))
                } else {
                    if curr_char.is_numeric() || self.hyphen_starting_number(curr_char) {
                        return self.number(curr_char, char_offset)
                    } else {
                        return self.ident(curr_char, char_offset)
                    }
                }
            }
        }
        return Ok(None)
    }

    fn hyphen_starting_shit(&mut self, curr_char: char) -> bool {
        let peek_char = self.peek_char();
        curr_char == '-' && peek_char.is_some() && !peek_char.unwrap().is_whitespace()
    }

    fn hyphen_starting_number(&mut self, curr_char: char) -> bool {
        let peek_char = self.peek_char();
        curr_char == '-' && peek_char.is_some() && peek_char.unwrap().is_numeric()
    }

    fn peek_char(&mut self) -> Option<char> {
        match self.chars.peek() {
            Some(&(_, peek_char)) => Some(peek_char),
            None => None,
        }
    }

    fn ident(&mut self, curr_char: char, start: usize) -> Result<Option<Lexeme>> {
        let mut value = String::new();
        value.push(curr_char);

        while let Some(peek_char) = self.peek_char() {
            // Stop when we reach a non-ident char (hyphens are special)
            if peek_char.is_whitespace() || (
                is_single_char_token(peek_char) && peek_char != '-'
            ) {
                break;
            } else {
                value.push(peek_char);
                self.chars.next();
            }
        }
        Ok(Some(Lexeme { token: Token::Ident(value), offset: Some(start) }))
    }

    fn number(&mut self, curr_char: char, start: usize) -> Result<Option<Lexeme>> {
        let mut value = String::new();
        value.push(curr_char);

        while let Some(peek_char) = self.peek_char() {
            // Stop when we reach a non-numeric char
            // TODO: disallow two `.`s in one number
            if !peek_char.is_numeric() && peek_char != '.' {
                break;
            } else {
                value.push(peek_char);
                self.chars.next();
            }
        }

        let value = match value.parse() {
            Ok(v) => v,
            Err(_) => return Err(SassError {
                offset: start,
                kind: ErrorKind::TokenizerError,
                message: format!(
                    "Tried to parse `{}` into a f32 but failed.",
                    value,
                ),
            })
        };
        Ok(Some(Lexeme { token: Token::Number(value), offset: Some(start) }))
    }
}

fn is_single_char_token(ch: char) -> bool {
    Token::from_char(ch).is_some()
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use error::Result;

    fn expected_lexeme(expected_token: Token, expected_offset: usize) -> Option<Result<Lexeme>> {
        Some(Ok(Lexeme {
            token: expected_token,
            offset: Some(expected_offset),
        }))
    }

    fn expected_ident(expected_value: &str, expected_offset: usize) -> Option<Result<Lexeme>> {
        expected_lexeme(Token::Ident(expected_value.into()), expected_offset)
    }

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_none_for_only_whitespace() {
        let mut tokenizer = Tokenizer::new("     ");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_words() {
        // Without regard to Sass word validity
        let mut tokenizer = Tokenizer::new(" \n  div   aoeu  ");
        assert_eq!(tokenizer.next(), expected_ident("div", 4));
        assert_eq!(tokenizer.next(), expected_ident("aoeu", 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_curly_braces() {
        // Without regard to matching
        let mut tokenizer = Tokenizer::new("{}}a{ blah}");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 2));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 4));
        assert_eq!(tokenizer.next(), expected_ident("blah", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_colon() {
        let mut tokenizer = Tokenizer::new(":invalid: property::");
        // This might be wrong. I have a note somewhere else that colons can start idents.
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 0));
        assert_eq!(tokenizer.next(), expected_ident("invalid", 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 8));
        assert_eq!(tokenizer.next(), expected_ident("property", 10));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 19));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_semicolon() {
        let mut tokenizer = Tokenizer::new(";;\na;\nb\n;");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 1));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 4));
        assert_eq!(tokenizer.next(), expected_ident("b", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 8));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_numbers() {
        let mut tokenizer = Tokenizer::new("border: 0px 1.5 11em;");
        assert_eq!(tokenizer.next(), expected_ident("border", 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(0.0), 8));
        assert_eq!(tokenizer.next(), expected_ident("px", 9));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(1.5), 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(11.0), 16));
        assert_eq!(tokenizer.next(), expected_ident("em", 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 20));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_hyphen() {
        let mut tokenizer = Tokenizer::new("font-weight -webkit -3 - 4-5 a-1 -");
        assert_eq!(tokenizer.next(), expected_ident("font-weight", 0));
        assert_eq!(tokenizer.next(), expected_ident("-webkit", 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-3.0), 20));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Minus, 23));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 25));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-5.0), 26));
        assert_eq!(tokenizer.next(), expected_ident("a-1", 29));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Minus, 33));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_parens() {
        let mut tokenizer = Tokenizer::new("() rgb)()(");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 1));
        assert_eq!(tokenizer.next(), expected_ident("rgb", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 7));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 9));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_slash() {
        let mut tokenizer = Tokenizer::new("/ / 3/4 / 8");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 2));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(3.0), 4));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 5));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(8.0), 10));
        assert_eq!(tokenizer.next(), None);
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate mime;
extern crate unix_socket;

use std::ops::DerefMut;
use hyper::Client;
use hyper::client;
use self::hyper::buffer::BufReader;
use self::hyper::http::{ parse_response };
use self::hyper::http::HttpReader::{ ChunkedReader, EofReader };
use self::hyper::header::ContentType;
use hyper::method::Method;
use self::mime::{ Attr, Mime, Value };
use self::mime::TopLevel::Application;
use self::mime::SubLevel::Json;
use std::io::{ Read, Result, Write };
use unix_socket::UnixStream;

#[doc(hidden)]
pub struct Body<'a> {
 read: &'a mut Box<&'a mut Read>,
 size: u64
}

impl<'a> Body<'a> {
  /// Create a new body instance
  pub fn new(read: &'a mut Box<&'a mut Read>, size: u64) -> Body<'a> {
    Body { read: read, size: size }
  }
}

pub trait Transport {
  fn request(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<String>;
  fn stream(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<Box<Read>>;
}

impl Transport for UnixStream {
  fn request(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<String> {
    let mut res = match self.stream(method, endpoint, body) {
      Ok(r) => r,
      Err(e) => panic!("failed request {:?}", e)
    };
    let mut body = String::new();
    res.read_to_string(&mut body).map(|_| body)
  }

  fn stream(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<Box<Read>> {
    let method_str = match method {
      Method::Put    => "PUT",
      Method::Post   => "POST",
      Method::Delete => "DELETE",
               _     => "GET"
    };
    let req = format!("{} {} HTTP/1.0\r\n\r\n", method_str, endpoint);
    try!(self.write_all(req.as_bytes()));
    // read the body -- https://github.com/hyperium/hyper/blob/06d072bca1b4af3507af370cbd0ca2ac8f64fc00/src/client/response.rs#L36-L74
    let cloned = try!(self.try_clone());
    let mut stream = BufReader::new(cloned);
    let _ = parse_response(&mut stream).unwrap();
    Ok(Box::new(EofReader(stream)))
  }
}

impl Transport for (Client, String) {
  fn request(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<String> {
    let mut res = match self.stream(method, endpoint, body) {
      Ok(r) => r,
      Err(e) => panic!("failed request {:?}", e)
    };
    let mut body = String::new();
    res.read_to_string(&mut body).map(|_| body)
  }

  fn stream(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<Box<Read>> {
    let uri = format!("{}{}", self.1, endpoint);
    let req = match method {
      Method::Put    => self.0.put(&uri[..]),
      Method::Post   => self.0.post(&uri[..]),
      Method::Delete => self.0.delete(&uri[..]),
                   _ => self.0.get(&uri[..])
    };
    let embodied = match body {
      Some(Body { read: r, size: l }) => {
        let reader: &mut Read = *r.deref_mut();
        let content_type: Mime = Mime(Application, Json, vec![(Attr::Charset, Value::Utf8)]);
        req.header(ContentType(content_type)).body(client::Body::SizedBody(reader, l))
      },
      _ => req
    };
    let res = match embodied.send() {
      Ok(r) => r,
      Err(e) => panic!("failed request {:?}", e)
    };
    Ok(Box::new(res))
  }
}
<commit_msg>unused iport<commit_after>extern crate hyper;
extern crate mime;
extern crate unix_socket;

use std::ops::DerefMut;
use hyper::Client;
use hyper::client;
use self::hyper::buffer::BufReader;
use self::hyper::http::{ parse_response };
use self::hyper::http::HttpReader::EofReader;
use self::hyper::header::ContentType;
use hyper::method::Method;
use self::mime::{ Attr, Mime, Value };
use self::mime::TopLevel::Application;
use self::mime::SubLevel::Json;
use std::io::{ Read, Result, Write };
use unix_socket::UnixStream;

#[doc(hidden)]
pub struct Body<'a> {
 read: &'a mut Box<&'a mut Read>,
 size: u64
}

impl<'a> Body<'a> {
  /// Create a new body instance
  pub fn new(read: &'a mut Box<&'a mut Read>, size: u64) -> Body<'a> {
    Body { read: read, size: size }
  }
}

pub trait Transport {
  fn request(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<String>;
  fn stream(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<Box<Read>>;
}

impl Transport for UnixStream {
  fn request(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<String> {
    let mut res = match self.stream(method, endpoint, body) {
      Ok(r) => r,
      Err(e) => panic!("failed request {:?}", e)
    };
    let mut body = String::new();
    res.read_to_string(&mut body).map(|_| body)
  }

  fn stream(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<Box<Read>> {
    let method_str = match method {
      Method::Put    => "PUT",
      Method::Post   => "POST",
      Method::Delete => "DELETE",
               _     => "GET"
    };
    let req = format!("{} {} HTTP/1.0\r\n\r\n", method_str, endpoint);
    try!(self.write_all(req.as_bytes()));
    // read the body -- https://github.com/hyperium/hyper/blob/06d072bca1b4af3507af370cbd0ca2ac8f64fc00/src/client/response.rs#L36-L74
    let cloned = try!(self.try_clone());
    let mut stream = BufReader::new(cloned);
    let _ = parse_response(&mut stream).unwrap();
    Ok(Box::new(EofReader(stream)))
  }
}

impl Transport for (Client, String) {
  fn request(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<String> {
    let mut res = match self.stream(method, endpoint, body) {
      Ok(r) => r,
      Err(e) => panic!("failed request {:?}", e)
    };
    let mut body = String::new();
    res.read_to_string(&mut body).map(|_| body)
  }

  fn stream(&mut self, method: Method, endpoint: &str, body: Option<Body>) -> Result<Box<Read>> {
    let uri = format!("{}{}", self.1, endpoint);
    let req = match method {
      Method::Put    => self.0.put(&uri[..]),
      Method::Post   => self.0.post(&uri[..]),
      Method::Delete => self.0.delete(&uri[..]),
                   _ => self.0.get(&uri[..])
    };
    let embodied = match body {
      Some(Body { read: r, size: l }) => {
        let reader: &mut Read = *r.deref_mut();
        let content_type: Mime = Mime(Application, Json, vec![(Attr::Charset, Value::Utf8)]);
        req.header(ContentType(content_type)).body(client::Body::SizedBody(reader, l))
      },
      _ => req
    };
    let res = match embodied.send() {
      Ok(r) => r,
      Err(e) => panic!("failed request {:?}", e)
    };
    Ok(Box::new(res))
  }
}
<|endoftext|>


<commit_before>// Thread parker implementation for Windows.
//
// This uses WaitOnAddress and WakeByAddressSingle if available (Windows 8+).
// This modern API is exactly the same as the futex syscalls the Linux thread
// parker uses. When These APIs are available, the implementation of this
// thread parker matches the Linux thread parker exactly.
//
// However, when the modern API is not available, this implementation falls
// back to NT Keyed Events, which are similar, but have some important
// differences. These are available since Windows XP.
//
// WaitOnAddress first checks the state of the thread parker to make sure it no
// WakeByAddressSingle calls can be missed between updating the parker state
// and calling the function.
//
// NtWaitForKeyedEvent does not have this option, and unconditionally blocks
// without checking the parker state first. Instead, NtReleaseKeyedEvent
// (unlike WakeByAddressSingle) *blocks* until it woke up a thread waiting for
// it by NtWaitForKeyedEvent. This way, we can be sure no events are missed,
// but we need to be careful not to block unpark() if park_timeout() was woken
// up by a timeout instead of unpark().
//
// Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a
// HANDLE (created with NtCreateKeyedEvent). This means that we can be sure
// a succesfully awoken park() was awoken by unpark() and not a
// NtReleaseKeyedEvent call from some other code, as these events are not only
// matched by the key (address of the parker (state)), but also by this HANDLE.
// We lazily allocate this handle the first time it is needed.
//
// The fast path (calling park() after unpark() was already called) and the
// possible states are the same for both implementations. This is used here to
// make sure the fast path does not even check which API to use, but can return
// right away, independent of the used API. Only the slow paths (which will
// actually block/wake a thread) check which API is available and have
// different implementations.
//
// Unfortunately, NT Keyed Events are an undocumented Windows API. However:
// - This API is relatively simple with obvious behaviour, and there are
//   several (unofficial) articles documenting the details. [1]
// - `parking_lot` has been using this API for years (on Windows versions
//   before Windows 8). [2] Many big projects extensively use parking_lot,
//   such as servo and the Rust compiler itself.
// - It is the underlying API used by Windows SRW locks and Windows critical
//   sections. [3] [4]
// - The source code of the implementations of Wine, ReactOs, and Windows XP
//   are available and match the expected behaviour.
// - The main risk with an undocumented API is that it might change in the
//   future. But since we only use it for older versions of Windows, that's not
//   a problem.
// - Even if these functions do not block or wake as we expect (which is
//   unlikely, see all previous points), this implementation would still be
//   memory safe. The NT Keyed Events API is only used to sleep/block in the
//   right place.
//
// [1]: http://www.locklessinc.com/articles/keyed_events/
// [2]: https://github.com/Amanieu/parking_lot/commit/43abbc964e
// [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c
// [4]: Windows Internals, Part 1, ISBN 9780735671300

use crate::convert::TryFrom;
use crate::ptr;
use crate::sync::atomic::{
    AtomicI8, AtomicUsize,
    Ordering::{Acquire, Relaxed, Release},
};
use crate::sys::{c, dur2timeout};
use crate::time::Duration;

pub struct Parker {
    state: AtomicI8,
}

const PARKED: i8 = -1;
const EMPTY: i8 = 0;
const NOTIFIED: i8 = 1;

// Notes about memory ordering:
//
// Memory ordering is only relevant for the relative ordering of operations
// between different variables. Even Ordering::Relaxed guarantees a
// monotonic/consistent order when looking at just a single atomic variable.
//
// So, since this parker is just a single atomic variable, we only need to look
// at the ordering guarantees we need to provide to the 'outside world'.
//
// The only memory ordering guarantee that parking and unparking provide, is
// that things which happened before unpark() are visible on the thread
// returning from park() afterwards. Otherwise, it was effectively unparked
// before unpark() was called while still consuming the 'token'.
//
// In other words, unpark() needs to synchronize with the part of park() that
// consumes the token and returns.
//
// This is done with a release-acquire synchronization, by using
// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
// Ordering::Acquire when reading this state in park() after waking up.
impl Parker {
    pub fn new() -> Self {
        Self { state: AtomicI8::new(EMPTY) }
    }

    // Assumes this is only called by the thread that owns the Parker,
    // which means that `self.state != PARKED`.
    pub unsafe fn park(&self) {
        // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
        // first case.
        if self.state.fetch_sub(1, Acquire) == NOTIFIED {
            return;
        }

        if c::WaitOnAddress::is_available() {
            loop {
                // Wait for something to happen, assuming it's still set to PARKED.
                c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
                // Change NOTIFIED=>EMPTY but leave PARKED alone.
                if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED {
                    // Actually woken up by unpark().
                    return;
                } else {
                    // Spurious wake up. We loop to try again.
                }
            }
        } else {
            // Wait for unpark() to produce this event.
            c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
            // Set the state back to EMPTY (from either PARKED or NOTIFIED).
            // Note that we don't just write EMPTY, but use swap() to also
            // include an acquire-ordered read to synchronize with unpark()'s
            // release-ordered write.
            self.state.swap(EMPTY, Acquire);
        }
    }

    // Assumes this is only called by the thread that owns the Parker,
    // which means that `self.state != PARKED`.
    pub unsafe fn park_timeout(&self, timeout: Duration) {
        // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
        // first case.
        if self.state.fetch_sub(1, Acquire) == NOTIFIED {
            return;
        }

        if c::WaitOnAddress::is_available() {
            // Wait for something to happen, assuming it's still set to PARKED.
            c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
            // Set the state back to EMPTY (from either PARKED or NOTIFIED).
            // Note that we don't just write EMPTY, but use swap() to also
            // include an acquire-ordered read to synchronize with unpark()'s
            // release-ordered write.
            if self.state.swap(EMPTY, Acquire) == NOTIFIED {
                // Actually woken up by unpark().
            } else {
                // Timeout or spurious wake up.
                // We return either way, because we can't easily tell if it was the
                // timeout or not.
            }
        } else {
            // Need to wait for unpark() using NtWaitForKeyedEvent.
            let handle = keyed_event_handle();

            // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
            // values to indicate a relative time on the monotonic clock.
            // This is documented here for the underlying KeWaitForSingleObject function:
            // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
            let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) {
                Ok(t) => -t,
                Err(_) => i64::MIN,
            };

            // Wait for unpark() to produce this event.
            let unparked =
                c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;

            // Set the state back to EMPTY (from either PARKED or NOTIFIED).
            let prev_state = self.state.swap(EMPTY, Acquire);

            if !unparked && prev_state == NOTIFIED {
                // We were awoken by a timeout, not by unpark(), but the state
                // was set to NOTIFIED, which means we *just* missed an
                // unpark(), which is now blocked on us to wait for it.
                // Wait for it to consume the event and unblock that thread.
                c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
            }
        }
    }

    pub fn unpark(&self) {
        // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
        // wake the thread in the first case.
        //
        // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
        // purpose, to make sure every unpark() has a release-acquire ordering
        // with park().
        if self.state.swap(NOTIFIED, Release) == PARKED {
            if c::WakeByAddressSingle::is_available() {
                unsafe {
                    c::WakeByAddressSingle(self.ptr());
                }
            } else {
                // If we run NtReleaseKeyedEvent before the waiting thread runs
                // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
                // If the waiting thread wakes up before we run NtReleaseKeyedEvent
                // (e.g. due to a timeout), this blocks until we do wake up a thread.
                // To prevent this thread from blocking indefinitely in that case,
                // park_impl() will, after seeing the state set to NOTIFIED after
                // waking up, call NtWaitForKeyedEvent again to unblock us.
                unsafe {
                    c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
                }
            }
        }
    }

    fn ptr(&self) -> c::LPVOID {
        &self.state as *const _ as c::LPVOID
    }
}

fn keyed_event_handle() -> c::HANDLE {
    const INVALID: usize = !0;
    static HANDLE: AtomicUsize = AtomicUsize::new(INVALID);
    match HANDLE.load(Relaxed) {
        INVALID => {
            let mut handle = c::INVALID_HANDLE_VALUE;
            unsafe {
                match c::NtCreateKeyedEvent(
                    &mut handle,
                    c::GENERIC_READ | c::GENERIC_WRITE,
                    ptr::null_mut(),
                    0,
                ) {
                    c::STATUS_SUCCESS => {}
                    r => panic!("Unable to create keyed event handle: error {}", r),
                }
            }
            match HANDLE.compare_exchange(INVALID, handle as usize, Relaxed, Relaxed) {
                Ok(_) => handle,
                Err(h) => {
                    // Lost the race to another thread initializing HANDLE before we did.
                    // Closing our handle and using theirs instead.
                    unsafe {
                        c::CloseHandle(handle);
                    }
                    h as c::HANDLE
                }
            }
        }
        handle => handle as c::HANDLE,
    }
}
<commit_msg>Fix compare_and_swap in Windows thread_parker<commit_after>// Thread parker implementation for Windows.
//
// This uses WaitOnAddress and WakeByAddressSingle if available (Windows 8+).
// This modern API is exactly the same as the futex syscalls the Linux thread
// parker uses. When These APIs are available, the implementation of this
// thread parker matches the Linux thread parker exactly.
//
// However, when the modern API is not available, this implementation falls
// back to NT Keyed Events, which are similar, but have some important
// differences. These are available since Windows XP.
//
// WaitOnAddress first checks the state of the thread parker to make sure it no
// WakeByAddressSingle calls can be missed between updating the parker state
// and calling the function.
//
// NtWaitForKeyedEvent does not have this option, and unconditionally blocks
// without checking the parker state first. Instead, NtReleaseKeyedEvent
// (unlike WakeByAddressSingle) *blocks* until it woke up a thread waiting for
// it by NtWaitForKeyedEvent. This way, we can be sure no events are missed,
// but we need to be careful not to block unpark() if park_timeout() was woken
// up by a timeout instead of unpark().
//
// Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a
// HANDLE (created with NtCreateKeyedEvent). This means that we can be sure
// a succesfully awoken park() was awoken by unpark() and not a
// NtReleaseKeyedEvent call from some other code, as these events are not only
// matched by the key (address of the parker (state)), but also by this HANDLE.
// We lazily allocate this handle the first time it is needed.
//
// The fast path (calling park() after unpark() was already called) and the
// possible states are the same for both implementations. This is used here to
// make sure the fast path does not even check which API to use, but can return
// right away, independent of the used API. Only the slow paths (which will
// actually block/wake a thread) check which API is available and have
// different implementations.
//
// Unfortunately, NT Keyed Events are an undocumented Windows API. However:
// - This API is relatively simple with obvious behaviour, and there are
//   several (unofficial) articles documenting the details. [1]
// - `parking_lot` has been using this API for years (on Windows versions
//   before Windows 8). [2] Many big projects extensively use parking_lot,
//   such as servo and the Rust compiler itself.
// - It is the underlying API used by Windows SRW locks and Windows critical
//   sections. [3] [4]
// - The source code of the implementations of Wine, ReactOs, and Windows XP
//   are available and match the expected behaviour.
// - The main risk with an undocumented API is that it might change in the
//   future. But since we only use it for older versions of Windows, that's not
//   a problem.
// - Even if these functions do not block or wake as we expect (which is
//   unlikely, see all previous points), this implementation would still be
//   memory safe. The NT Keyed Events API is only used to sleep/block in the
//   right place.
//
// [1]: http://www.locklessinc.com/articles/keyed_events/
// [2]: https://github.com/Amanieu/parking_lot/commit/43abbc964e
// [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c
// [4]: Windows Internals, Part 1, ISBN 9780735671300

use crate::convert::TryFrom;
use crate::ptr;
use crate::sync::atomic::{
    AtomicI8, AtomicUsize,
    Ordering::{Acquire, Relaxed, Release},
};
use crate::sys::{c, dur2timeout};
use crate::time::Duration;

pub struct Parker {
    state: AtomicI8,
}

const PARKED: i8 = -1;
const EMPTY: i8 = 0;
const NOTIFIED: i8 = 1;

// Notes about memory ordering:
//
// Memory ordering is only relevant for the relative ordering of operations
// between different variables. Even Ordering::Relaxed guarantees a
// monotonic/consistent order when looking at just a single atomic variable.
//
// So, since this parker is just a single atomic variable, we only need to look
// at the ordering guarantees we need to provide to the 'outside world'.
//
// The only memory ordering guarantee that parking and unparking provide, is
// that things which happened before unpark() are visible on the thread
// returning from park() afterwards. Otherwise, it was effectively unparked
// before unpark() was called while still consuming the 'token'.
//
// In other words, unpark() needs to synchronize with the part of park() that
// consumes the token and returns.
//
// This is done with a release-acquire synchronization, by using
// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
// Ordering::Acquire when reading this state in park() after waking up.
impl Parker {
    pub fn new() -> Self {
        Self { state: AtomicI8::new(EMPTY) }
    }

    // Assumes this is only called by the thread that owns the Parker,
    // which means that `self.state != PARKED`.
    pub unsafe fn park(&self) {
        // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
        // first case.
        if self.state.fetch_sub(1, Acquire) == NOTIFIED {
            return;
        }

        if c::WaitOnAddress::is_available() {
            loop {
                // Wait for something to happen, assuming it's still set to PARKED.
                c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
                // Change NOTIFIED=>EMPTY but leave PARKED alone.
                if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire) == NOTIFIED {
                    // Actually woken up by unpark().
                    return;
                } else {
                    // Spurious wake up. We loop to try again.
                }
            }
        } else {
            // Wait for unpark() to produce this event.
            c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
            // Set the state back to EMPTY (from either PARKED or NOTIFIED).
            // Note that we don't just write EMPTY, but use swap() to also
            // include an acquire-ordered read to synchronize with unpark()'s
            // release-ordered write.
            self.state.swap(EMPTY, Acquire);
        }
    }

    // Assumes this is only called by the thread that owns the Parker,
    // which means that `self.state != PARKED`.
    pub unsafe fn park_timeout(&self, timeout: Duration) {
        // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
        // first case.
        if self.state.fetch_sub(1, Acquire) == NOTIFIED {
            return;
        }

        if c::WaitOnAddress::is_available() {
            // Wait for something to happen, assuming it's still set to PARKED.
            c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
            // Set the state back to EMPTY (from either PARKED or NOTIFIED).
            // Note that we don't just write EMPTY, but use swap() to also
            // include an acquire-ordered read to synchronize with unpark()'s
            // release-ordered write.
            if self.state.swap(EMPTY, Acquire) == NOTIFIED {
                // Actually woken up by unpark().
            } else {
                // Timeout or spurious wake up.
                // We return either way, because we can't easily tell if it was the
                // timeout or not.
            }
        } else {
            // Need to wait for unpark() using NtWaitForKeyedEvent.
            let handle = keyed_event_handle();

            // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
            // values to indicate a relative time on the monotonic clock.
            // This is documented here for the underlying KeWaitForSingleObject function:
            // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
            let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) {
                Ok(t) => -t,
                Err(_) => i64::MIN,
            };

            // Wait for unpark() to produce this event.
            let unparked =
                c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;

            // Set the state back to EMPTY (from either PARKED or NOTIFIED).
            let prev_state = self.state.swap(EMPTY, Acquire);

            if !unparked && prev_state == NOTIFIED {
                // We were awoken by a timeout, not by unpark(), but the state
                // was set to NOTIFIED, which means we *just* missed an
                // unpark(), which is now blocked on us to wait for it.
                // Wait for it to consume the event and unblock that thread.
                c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
            }
        }
    }

    pub fn unpark(&self) {
        // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
        // wake the thread in the first case.
        //
        // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
        // purpose, to make sure every unpark() has a release-acquire ordering
        // with park().
        if self.state.swap(NOTIFIED, Release) == PARKED {
            if c::WakeByAddressSingle::is_available() {
                unsafe {
                    c::WakeByAddressSingle(self.ptr());
                }
            } else {
                // If we run NtReleaseKeyedEvent before the waiting thread runs
                // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
                // If the waiting thread wakes up before we run NtReleaseKeyedEvent
                // (e.g. due to a timeout), this blocks until we do wake up a thread.
                // To prevent this thread from blocking indefinitely in that case,
                // park_impl() will, after seeing the state set to NOTIFIED after
                // waking up, call NtWaitForKeyedEvent again to unblock us.
                unsafe {
                    c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
                }
            }
        }
    }

    fn ptr(&self) -> c::LPVOID {
        &self.state as *const _ as c::LPVOID
    }
}

fn keyed_event_handle() -> c::HANDLE {
    const INVALID: usize = !0;
    static HANDLE: AtomicUsize = AtomicUsize::new(INVALID);
    match HANDLE.load(Relaxed) {
        INVALID => {
            let mut handle = c::INVALID_HANDLE_VALUE;
            unsafe {
                match c::NtCreateKeyedEvent(
                    &mut handle,
                    c::GENERIC_READ | c::GENERIC_WRITE,
                    ptr::null_mut(),
                    0,
                ) {
                    c::STATUS_SUCCESS => {}
                    r => panic!("Unable to create keyed event handle: error {}", r),
                }
            }
            match HANDLE.compare_exchange(INVALID, handle as usize, Relaxed, Relaxed) {
                Ok(_) => handle,
                Err(h) => {
                    // Lost the race to another thread initializing HANDLE before we did.
                    // Closing our handle and using theirs instead.
                    unsafe {
                        c::CloseHandle(handle);
                    }
                    h as c::HANDLE
                }
            }
        }
        handle => handle as c::HANDLE,
    }
}
<|endoftext|>


<commit_before>use std;

use parser::js::*;
use parser::js::terminals::*;
use parser::js::statements::*;

#[derive(Debug)]
pub struct UiProgram<'a> {
    pub headers: Option<UiHeaderItemList<'a>>,
    pub members: UiRootMember<'a>,
}

#[derive(Debug)]
pub struct UiHeaderItemList<'a>(pub std::vec::Vec<UiHeaderItem<'a>>);

#[derive(Debug)]
pub enum UiHeaderItem<'a> {
    UiPragma(UiPragma<'a>),
    UiImport(UiImport<'a>),
}

#[derive(Debug)]
pub struct UiPragma<'a> {
    pub pragma_type: UiQualifiedPragmaId<'a>,
}

#[derive(Debug)]
pub struct UiQualifiedPragmaId<'a>(pub &'a str);

#[derive(Debug)]
pub struct UiImport<'a> {
    pub file: UiImportId<'a>,
    pub import_id: Option<&'a str>,
    pub version: Option<NumericLiteral>,
}

#[derive(Debug)]
pub enum UiImportId<'a> {
    UiQualifiedId(UiQualifiedId<'a>),
    StringLiteral(StringLiteral<'a>),
}

#[derive(Debug)]
pub struct UiRootMember<'a>(pub UiObjectMemberList<'a>);

#[derive(Debug)]
pub struct UiObjectMemberList<'a>(pub std::vec::Vec<UiObjectMember<'a>>);

#[derive(Debug)]
pub enum UiObjectMember<'a> {
    UiObjectDefinition(UiObjectDefinition<'a>),
    UiArrayBinding(UiArrayBinding<'a>),
    UiObjectBinding(UiObjectBinding<'a>),
    UiPublicMember(UiPublicMember<'a>),
    UiScriptBinding(UiScriptBinding<'a>),
    UiSourceElement(UiSourceElement<'a>),
}

#[derive(Debug)]
pub struct UiObjectDefinition<'a> {
    pub qualified_type_name_id: UiQualifiedId<'a>,
    pub initializer: UiObjectInitializer<'a>,
}

#[derive(Debug)]
pub struct UiObjectInitializer<'a> {
    pub members: Option<UiObjectMemberList<'a>>,
}

#[derive(Debug)]
pub struct UiArrayBinding<'a> {
    pub qualified_id: UiQualifiedId<'a>,
    pub members: UiArrayMemberList<'a>,
}

#[derive(Debug)]
pub struct UiArrayMemberList<'a>(pub std::vec::Vec<UiObjectMember<'a>>);

#[derive(Debug)]
pub struct UiObjectBinding<'a> {
    pub qualified_id: UiQualifiedId<'a>,
    pub qualified_type_name_id: UiQualifiedId<'a>,
    pub initializer: UiObjectInitializer<'a>,
}

#[derive(Debug)]
pub struct UiPublicMember<'a> {
    pub public_member_type: UiPublicMemberType,
    pub type_modifier: Option<&'a str>,
    pub member_type: Option<UiQualifiedId<'a>>,
    pub name: &'a str,
    pub statement: Option<Box<Statement<'a>>>,
    pub binding: Option<Box<UiObjectMember<'a>>>,
    pub is_default_member: bool,
    pub is_readonly_member: bool,
    pub parameters: Option<UiParameterList<'a>>,
}

#[derive(Debug)]
pub enum UiPublicMemberType {
    Signal,
    Property,
}

#[derive(Debug)]
pub struct UiParameterList<'a>(pub std::vec::Vec<UiParameter<'a>>);

#[derive(Debug)]
pub struct UiParameter<'a> {
    pub parameter_type: UiQualifiedId<'a>,
    pub name: &'a str,
}

#[derive(Debug)]
pub struct UiScriptBinding<'a> {
    pub qualified_id: UiQualifiedId<'a>,
    pub statement: Box<Statement<'a>>,
}

#[derive(Debug)]
pub enum UiSourceElement<'a> {
    FunctionDeclaration(FunctionDeclaration<'a>),
    VariableStatement(VariableStatement<'a>),
}

#[derive(Debug)]
pub struct UiQualifiedId<'a>(pub std::vec::Vec<&'a str>);

pub mod parsing {

    use nom::{IResult, ErrorKind};
    use parser::ui::*;
    use parser::helpers::parsing::*;
    use parser::js::parsing::*;
    use parser::js::terminals::parsing::*;
    use parser::js::expressions::*;
    use parser::js::expressions::parsing::*;
    use parser::js::statements::parsing::*;

    named!(pub ui_program<&str, UiProgram>, do_parse!(
        headers: opt!(ui_header_item_list) >>
        members: ui_root_member >>
        (UiProgram {
            headers: headers,
            members: members,
        })
    ));

    named!(ui_header_item_list<&str, UiHeaderItemList>, conv!(UiHeaderItemList(many1!(
        alt!(
            conv!(UiHeaderItem::UiPragma(ui_pragma))
            |
            conv!(UiHeaderItem::UiImport(ui_import))
        )
    ))));

    named!(ui_pragma<&str, UiPragma>, do_parse!(
        keyword!("pragma") >>
        pragma_type: ui_qualified_pragma_id >>
        automatic_semicolon >>
        (UiPragma {
            pragma_type: pragma_type
        })
    ));

    named!(ui_import<&str, UiImport>, do_parse!(
        keyword!("import") >>
        file: ui_import_id >>
        version: opt!(numeric_literal) >>
        import_id: opt!(do_parse!(
            keyword!("as") >>
            import_id: js_identifier >>
            (import_id)
        )) >>
        automatic_semicolon >>
        (UiImport {
            file: file,
            version: version,
            import_id: import_id,
        })
    ));

    named!(ui_import_id<&str, UiImportId>, alt!(
        conv!(UiImportId::StringLiteral(string_literal))
        |
        conv!(UiImportId::UiQualifiedId(ui_qualified_id))
    ));

    fn ui_qualified_pragma_id(i: &str) -> IResult<&str, UiQualifiedPragmaId> {
        let (new_i, expression) = try_parse!(i, member_expression);
        if let Expression::IdentifierExpression(ie) = expression {
            IResult::Done(new_i, UiQualifiedPragmaId(ie.0))
        } else {
            IResult::Error(ErrorKind::Custom(1))
        }
    }

    named!(ui_root_member<&str, UiRootMember>, do_parse!(
        ui_object_definition: ui_object_definition >>
        (UiRootMember(UiObjectMemberList(vec![UiObjectMember::UiObjectDefinition(ui_object_definition)])))
    ));

    named!(ui_object_definition<&str, UiObjectDefinition>, do_parse!(
        qualified_type_name_id: ui_qualified_id >>
        initializer: ui_object_initializer >>
        (UiObjectDefinition {
            qualified_type_name_id: qualified_type_name_id,
            initializer: initializer,
        })
    ));

    named!(ui_object_initializer<&str, UiObjectInitializer>, do_parse!(
        keyword!("{") >>
        members: opt!(ui_object_member_list) >>
        keyword!("}") >>
        (UiObjectInitializer {
            members: members
        })
    ));

    named!(ui_object_member_list<&str, UiObjectMemberList>, conv!(UiObjectMemberList(many1!(ui_object_member))));

    named!(pub ui_object_member<&str, UiObjectMember>, alt!(
        conv!(UiObjectMember::UiSourceElement(conv!(UiSourceElement::FunctionDeclaration(function_declaration))))
        |
        conv!(UiObjectMember::UiSourceElement(conv!(UiSourceElement::VariableStatement(variable_statement))))
        |
        do_parse!(
            keyword!("signal") >>
            name: identifier >>
            parameters: opt!(do_parse!(
                keyword!("(") >>
                parameters: opt!(ui_parameter_list) >>
                keyword!(")") >>
                (parameters)
            )) >>
            automatic_semicolon >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: None,
                parameters: match parameters {
                    Some(p) => p,
                    None => None,
                },
                member_type: None,
                public_member_type: UiPublicMemberType::Signal,
                statement: None,
                is_default_member: false,
                is_readonly_member: false,
                binding: None,
            }))
        )
        |
        do_parse!(
            keyword!("property") >>
            type_modifier: identifier >>
            keyword!("<") >>
            member_type: ui_property_type >>
            keyword!(">") >>
            name: js_identifier >>
            keyword!(":") >>
            keyword!("[") >>
            members: ui_array_member_list >>
            keyword!("]") >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: Some(type_modifier),
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: false,
                is_readonly_member: false,
                binding: Some(Box::new(UiObjectMember::UiArrayBinding(UiArrayBinding {
                    qualified_id: UiQualifiedId(vec![name]),
                    members: members,
                }))),
            }))
        )
        |
        do_parse!(
            default: opt!(keyword!("default")) >>
            keyword!("property") >>
            type_modifier: identifier >>
            keyword!("<") >>
            member_type: ui_property_type >>
            keyword!(">") >>
            name: js_identifier >>
            automatic_semicolon >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: Some(type_modifier),
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: default.is_some(),
                is_readonly_member: false,
                binding: None,
            }))
        )
        |
        do_parse!(
            readonly: opt!(keyword!("readonly")) >>
            keyword!("property") >>
            member_type: ui_property_type >>
            name: js_identifier >>
            keyword!(":") >>
            qualified_type_name_id: ui_qualified_id >>
            initializer: ui_object_initializer >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: None,
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: false,
                is_readonly_member: readonly.is_some(),
                binding: Some(Box::new(UiObjectMember::UiObjectBinding(UiObjectBinding {
                    qualified_id: UiQualifiedId(vec![name]),
                    qualified_type_name_id: qualified_type_name_id,
                    initializer: initializer,
                }))),
            }))
        )
        |
        do_parse!(
            default_readonly: opt!(alt!(
                keyword!("default")
                |
                keyword!("readonly")
            )) >>
            keyword!("property") >>
            member_type: ui_property_type >>
            name: js_identifier >>
            keyword!(":") >>
            statement: ui_script_statement >>
            ({
                let (default, readonly) = match default_readonly {
                    Some("default") => (true, false),
                    Some("readonly") => (false, true),
                    _ => (false, false)
                };
                UiObjectMember::UiPublicMember(UiPublicMember {
                    name: name,
                    type_modifier: None,
                    parameters: None,
                    member_type: Some(member_type),
                    public_member_type: UiPublicMemberType::Property,
                    statement: Some(Box::new(statement)),
                    is_default_member: default,
                    is_readonly_member: readonly,
                    binding: None,
                })
            })
        )
        |
        do_parse!(
            default: opt!(keyword!("default")) >>
            keyword!("property") >>
            member_type: ui_property_type >>
            name: js_identifier >>
            automatic_semicolon >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: None,
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: default.is_some(),
                is_readonly_member: false,
                binding: None,
            }))
        )
        |
        do_parse!(
            qualified_id: ui_qualified_id >>
            keyword!(":") >>
            statement: ui_script_statement >>
            (UiObjectMember::UiScriptBinding(UiScriptBinding {
                qualified_id: qualified_id,
                statement: Box::new(statement),
            }))
        )
        |
        conv!(UiObjectMember::UiObjectDefinition(ui_object_definition))
        |
        do_parse!(
            qualified_id: ui_qualified_id >>
            keyword!(":") >>
            keyword!("[") >>
            members: ui_array_member_list >>
            keyword!("]") >>
            (UiObjectMember::UiArrayBinding(UiArrayBinding {
                qualified_id: qualified_id,
                members: members,
            }))
        )
        |
        do_parse!(
            qualified_id: ui_qualified_id >>
            keyword!(":") >>
            qualified_type_name_id: ui_qualified_id >>
            initializer: ui_object_initializer >>
            (UiObjectMember::UiObjectBinding(UiObjectBinding {
                qualified_id: qualified_id,
                qualified_type_name_id: qualified_type_name_id,
                initializer: initializer,
            }))
        )
        |
        do_parse!(
            qualified_type_name_id: ui_qualified_id >>
            keyword!("on") >>
            qualified_id: ui_qualified_id >>
            initializer: ui_object_initializer >>
            (UiObjectMember::UiObjectBinding(UiObjectBinding {
                qualified_id: qualified_id,
                qualified_type_name_id: qualified_type_name_id,
                initializer: initializer,
            }))
        )
    ));

    named!(ui_script_statement<&str, Statement>, alt!(
        conv!(Statement::ExpressionStatement(expression_statement))
        |
        conv!(Statement::Block(block))
        |
        conv!(Statement::EmptyStatement(empty_statement))
        |
        conv!(Statement::IfStatement(if_statement))
        |
        conv!(Statement::WithStatement(with_statement))
        |
        conv!(Statement::SwitchStatement(switch_statement))
        |
        conv!(Statement::TryStatement(try_statement))
    ));

    named!(ui_array_member_list<&str, UiArrayMemberList>, conv!(UiArrayMemberList(separated_nonempty_list!(
        keyword!(","),
        conv!(UiObjectMember::UiObjectDefinition(ui_object_definition))
    ))));

    named!(ui_parameter_list<&str, UiParameterList>, conv!(UiParameterList(separated_nonempty_list!(
        keyword!(","),
        do_parse!(
            parameter_type: ui_property_type >>
            name: js_identifier >>
            (UiParameter {
                parameter_type: parameter_type,
                name: name,
            })
        )
    ))));

    named!(ui_property_type<&str, UiQualifiedId>, alt!(
        do_parse!(
            string: alt!(
                keyword!("var")
                |
                reserved_word
                |
                identifier
            ) >>
            (UiQualifiedId(vec![string]))
        )
        |
        conv!(UiQualifiedId(separated_nonempty_list!(
            keyword!("."),
            identifier
        )))
    ));

    named!(reserved_word<&str, &str>, alt!(
        keyword!("const")
        |
        keyword!("super")
        |
        keyword!("import")
    ));

    fn ui_qualified_id(i: &str) -> IResult<&str, UiQualifiedId> {
        let (new_i, expression) = try_parse!(i, member_expression);
        let mut expr_it = &expression;

        if let Expression::ArrayMemberExpression(ref ame) = *expr_it { //TODO: warning ignored annotation
            expr_it = &ame.base;
        };

        let mut names = vec![];

        while let Expression::FieldMemberExpression(ref fme) = *expr_it {
            names.push(fme.name);
            expr_it = &fme.base;
        }

        if let Expression::IdentifierExpression(ref ie) = *expr_it {
            names.push(ie.0);
            IResult::Done(new_i, UiQualifiedId(names))
        } else {
            IResult::Error(ErrorKind::Custom(0))
        }
    }

}<commit_msg>Add unit test for ui_qualified_pragma_id<commit_after>use std;

use parser::js::*;
use parser::js::terminals::*;
use parser::js::statements::*;

#[derive(Debug)]
pub struct UiProgram<'a> {
    pub headers: Option<UiHeaderItemList<'a>>,
    pub members: UiRootMember<'a>,
}

#[derive(Debug)]
pub struct UiHeaderItemList<'a>(pub std::vec::Vec<UiHeaderItem<'a>>);

#[derive(Debug)]
pub enum UiHeaderItem<'a> {
    UiPragma(UiPragma<'a>),
    UiImport(UiImport<'a>),
}

#[derive(Debug)]
pub struct UiPragma<'a> {
    pub pragma_type: UiQualifiedPragmaId<'a>,
}

#[derive(Debug, PartialEq)]
pub struct UiQualifiedPragmaId<'a>(pub &'a str);

#[derive(Debug)]
pub struct UiImport<'a> {
    pub file: UiImportId<'a>,
    pub import_id: Option<&'a str>,
    pub version: Option<NumericLiteral>,
}

#[derive(Debug)]
pub enum UiImportId<'a> {
    UiQualifiedId(UiQualifiedId<'a>),
    StringLiteral(StringLiteral<'a>),
}

#[derive(Debug)]
pub struct UiRootMember<'a>(pub UiObjectMemberList<'a>);

#[derive(Debug)]
pub struct UiObjectMemberList<'a>(pub std::vec::Vec<UiObjectMember<'a>>);

#[derive(Debug)]
pub enum UiObjectMember<'a> {
    UiObjectDefinition(UiObjectDefinition<'a>),
    UiArrayBinding(UiArrayBinding<'a>),
    UiObjectBinding(UiObjectBinding<'a>),
    UiPublicMember(UiPublicMember<'a>),
    UiScriptBinding(UiScriptBinding<'a>),
    UiSourceElement(UiSourceElement<'a>),
}

#[derive(Debug)]
pub struct UiObjectDefinition<'a> {
    pub qualified_type_name_id: UiQualifiedId<'a>,
    pub initializer: UiObjectInitializer<'a>,
}

#[derive(Debug)]
pub struct UiObjectInitializer<'a> {
    pub members: Option<UiObjectMemberList<'a>>,
}

#[derive(Debug)]
pub struct UiArrayBinding<'a> {
    pub qualified_id: UiQualifiedId<'a>,
    pub members: UiArrayMemberList<'a>,
}

#[derive(Debug)]
pub struct UiArrayMemberList<'a>(pub std::vec::Vec<UiObjectMember<'a>>);

#[derive(Debug)]
pub struct UiObjectBinding<'a> {
    pub qualified_id: UiQualifiedId<'a>,
    pub qualified_type_name_id: UiQualifiedId<'a>,
    pub initializer: UiObjectInitializer<'a>,
}

#[derive(Debug)]
pub struct UiPublicMember<'a> {
    pub public_member_type: UiPublicMemberType,
    pub type_modifier: Option<&'a str>,
    pub member_type: Option<UiQualifiedId<'a>>,
    pub name: &'a str,
    pub statement: Option<Box<Statement<'a>>>,
    pub binding: Option<Box<UiObjectMember<'a>>>,
    pub is_default_member: bool,
    pub is_readonly_member: bool,
    pub parameters: Option<UiParameterList<'a>>,
}

#[derive(Debug)]
pub enum UiPublicMemberType {
    Signal,
    Property,
}

#[derive(Debug)]
pub struct UiParameterList<'a>(pub std::vec::Vec<UiParameter<'a>>);

#[derive(Debug)]
pub struct UiParameter<'a> {
    pub parameter_type: UiQualifiedId<'a>,
    pub name: &'a str,
}

#[derive(Debug)]
pub struct UiScriptBinding<'a> {
    pub qualified_id: UiQualifiedId<'a>,
    pub statement: Box<Statement<'a>>,
}

#[derive(Debug)]
pub enum UiSourceElement<'a> {
    FunctionDeclaration(FunctionDeclaration<'a>),
    VariableStatement(VariableStatement<'a>),
}

#[derive(Debug)]
pub struct UiQualifiedId<'a>(pub std::vec::Vec<&'a str>);

pub mod parsing {

    use nom::{IResult, ErrorKind};
    use parser::ui::*;
    use parser::helpers::parsing::*;
    use parser::js::parsing::*;
    use parser::js::terminals::parsing::*;
    use parser::js::expressions::*;
    use parser::js::expressions::parsing::*;
    use parser::js::statements::parsing::*;

    named!(pub ui_program<&str, UiProgram>, do_parse!(
        headers: opt!(ui_header_item_list) >>
        members: ui_root_member >>
        (UiProgram {
            headers: headers,
            members: members,
        })
    ));

    named!(ui_header_item_list<&str, UiHeaderItemList>, conv!(UiHeaderItemList(many1!(
        alt!(
            conv!(UiHeaderItem::UiPragma(ui_pragma))
            |
            conv!(UiHeaderItem::UiImport(ui_import))
        )
    ))));

    named!(ui_pragma<&str, UiPragma>, do_parse!(
        keyword!("pragma") >>
        pragma_type: ui_qualified_pragma_id >>
        automatic_semicolon >>
        (UiPragma {
            pragma_type: pragma_type
        })
    ));

    named!(ui_import<&str, UiImport>, do_parse!(
        keyword!("import") >>
        file: ui_import_id >>
        version: opt!(numeric_literal) >>
        import_id: opt!(do_parse!(
            keyword!("as") >>
            import_id: js_identifier >>
            (import_id)
        )) >>
        automatic_semicolon >>
        (UiImport {
            file: file,
            version: version,
            import_id: import_id,
        })
    ));

    named!(ui_import_id<&str, UiImportId>, alt!(
        conv!(UiImportId::StringLiteral(string_literal))
        |
        conv!(UiImportId::UiQualifiedId(ui_qualified_id))
    ));

    fn ui_qualified_pragma_id(i: &str) -> IResult<&str, UiQualifiedPragmaId> {
        let (new_i, expression) = try_parse!(i, member_expression);
        if let Expression::IdentifierExpression(ie) = expression {
            IResult::Done(new_i, UiQualifiedPragmaId(ie.0))
        } else {
            IResult::Error(ErrorKind::Custom(1))
        }
    }

    named!(ui_root_member<&str, UiRootMember>, do_parse!(
        ui_object_definition: ui_object_definition >>
        (UiRootMember(UiObjectMemberList(vec![UiObjectMember::UiObjectDefinition(ui_object_definition)])))
    ));

    named!(ui_object_definition<&str, UiObjectDefinition>, do_parse!(
        qualified_type_name_id: ui_qualified_id >>
        initializer: ui_object_initializer >>
        (UiObjectDefinition {
            qualified_type_name_id: qualified_type_name_id,
            initializer: initializer,
        })
    ));

    named!(ui_object_initializer<&str, UiObjectInitializer>, do_parse!(
        keyword!("{") >>
        members: opt!(ui_object_member_list) >>
        keyword!("}") >>
        (UiObjectInitializer {
            members: members
        })
    ));

    named!(ui_object_member_list<&str, UiObjectMemberList>, conv!(UiObjectMemberList(many1!(ui_object_member))));

    named!(pub ui_object_member<&str, UiObjectMember>, alt!(
        conv!(UiObjectMember::UiSourceElement(conv!(UiSourceElement::FunctionDeclaration(function_declaration))))
        |
        conv!(UiObjectMember::UiSourceElement(conv!(UiSourceElement::VariableStatement(variable_statement))))
        |
        do_parse!(
            keyword!("signal") >>
            name: identifier >>
            parameters: opt!(do_parse!(
                keyword!("(") >>
                parameters: opt!(ui_parameter_list) >>
                keyword!(")") >>
                (parameters)
            )) >>
            automatic_semicolon >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: None,
                parameters: match parameters {
                    Some(p) => p,
                    None => None,
                },
                member_type: None,
                public_member_type: UiPublicMemberType::Signal,
                statement: None,
                is_default_member: false,
                is_readonly_member: false,
                binding: None,
            }))
        )
        |
        do_parse!(
            keyword!("property") >>
            type_modifier: identifier >>
            keyword!("<") >>
            member_type: ui_property_type >>
            keyword!(">") >>
            name: js_identifier >>
            keyword!(":") >>
            keyword!("[") >>
            members: ui_array_member_list >>
            keyword!("]") >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: Some(type_modifier),
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: false,
                is_readonly_member: false,
                binding: Some(Box::new(UiObjectMember::UiArrayBinding(UiArrayBinding {
                    qualified_id: UiQualifiedId(vec![name]),
                    members: members,
                }))),
            }))
        )
        |
        do_parse!(
            default: opt!(keyword!("default")) >>
            keyword!("property") >>
            type_modifier: identifier >>
            keyword!("<") >>
            member_type: ui_property_type >>
            keyword!(">") >>
            name: js_identifier >>
            automatic_semicolon >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: Some(type_modifier),
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: default.is_some(),
                is_readonly_member: false,
                binding: None,
            }))
        )
        |
        do_parse!(
            readonly: opt!(keyword!("readonly")) >>
            keyword!("property") >>
            member_type: ui_property_type >>
            name: js_identifier >>
            keyword!(":") >>
            qualified_type_name_id: ui_qualified_id >>
            initializer: ui_object_initializer >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: None,
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: false,
                is_readonly_member: readonly.is_some(),
                binding: Some(Box::new(UiObjectMember::UiObjectBinding(UiObjectBinding {
                    qualified_id: UiQualifiedId(vec![name]),
                    qualified_type_name_id: qualified_type_name_id,
                    initializer: initializer,
                }))),
            }))
        )
        |
        do_parse!(
            default_readonly: opt!(alt!(
                keyword!("default")
                |
                keyword!("readonly")
            )) >>
            keyword!("property") >>
            member_type: ui_property_type >>
            name: js_identifier >>
            keyword!(":") >>
            statement: ui_script_statement >>
            ({
                let (default, readonly) = match default_readonly {
                    Some("default") => (true, false),
                    Some("readonly") => (false, true),
                    _ => (false, false)
                };
                UiObjectMember::UiPublicMember(UiPublicMember {
                    name: name,
                    type_modifier: None,
                    parameters: None,
                    member_type: Some(member_type),
                    public_member_type: UiPublicMemberType::Property,
                    statement: Some(Box::new(statement)),
                    is_default_member: default,
                    is_readonly_member: readonly,
                    binding: None,
                })
            })
        )
        |
        do_parse!(
            default: opt!(keyword!("default")) >>
            keyword!("property") >>
            member_type: ui_property_type >>
            name: js_identifier >>
            automatic_semicolon >>
            (UiObjectMember::UiPublicMember(UiPublicMember {
                name: name,
                type_modifier: None,
                parameters: None,
                member_type: Some(member_type),
                public_member_type: UiPublicMemberType::Property,
                statement: None,
                is_default_member: default.is_some(),
                is_readonly_member: false,
                binding: None,
            }))
        )
        |
        do_parse!(
            qualified_id: ui_qualified_id >>
            keyword!(":") >>
            statement: ui_script_statement >>
            (UiObjectMember::UiScriptBinding(UiScriptBinding {
                qualified_id: qualified_id,
                statement: Box::new(statement),
            }))
        )
        |
        conv!(UiObjectMember::UiObjectDefinition(ui_object_definition))
        |
        do_parse!(
            qualified_id: ui_qualified_id >>
            keyword!(":") >>
            keyword!("[") >>
            members: ui_array_member_list >>
            keyword!("]") >>
            (UiObjectMember::UiArrayBinding(UiArrayBinding {
                qualified_id: qualified_id,
                members: members,
            }))
        )
        |
        do_parse!(
            qualified_id: ui_qualified_id >>
            keyword!(":") >>
            qualified_type_name_id: ui_qualified_id >>
            initializer: ui_object_initializer >>
            (UiObjectMember::UiObjectBinding(UiObjectBinding {
                qualified_id: qualified_id,
                qualified_type_name_id: qualified_type_name_id,
                initializer: initializer,
            }))
        )
        |
        do_parse!(
            qualified_type_name_id: ui_qualified_id >>
            keyword!("on") >>
            qualified_id: ui_qualified_id >>
            initializer: ui_object_initializer >>
            (UiObjectMember::UiObjectBinding(UiObjectBinding {
                qualified_id: qualified_id,
                qualified_type_name_id: qualified_type_name_id,
                initializer: initializer,
            }))
        )
    ));

    named!(ui_script_statement<&str, Statement>, alt!(
        conv!(Statement::ExpressionStatement(expression_statement))
        |
        conv!(Statement::Block(block))
        |
        conv!(Statement::EmptyStatement(empty_statement))
        |
        conv!(Statement::IfStatement(if_statement))
        |
        conv!(Statement::WithStatement(with_statement))
        |
        conv!(Statement::SwitchStatement(switch_statement))
        |
        conv!(Statement::TryStatement(try_statement))
    ));

    named!(ui_array_member_list<&str, UiArrayMemberList>, conv!(UiArrayMemberList(separated_nonempty_list!(
        keyword!(","),
        conv!(UiObjectMember::UiObjectDefinition(ui_object_definition))
    ))));

    named!(ui_parameter_list<&str, UiParameterList>, conv!(UiParameterList(separated_nonempty_list!(
        keyword!(","),
        do_parse!(
            parameter_type: ui_property_type >>
            name: js_identifier >>
            (UiParameter {
                parameter_type: parameter_type,
                name: name,
            })
        )
    ))));

    named!(ui_property_type<&str, UiQualifiedId>, alt!(
        do_parse!(
            string: alt!(
                keyword!("var")
                |
                reserved_word
                |
                identifier
            ) >>
            (UiQualifiedId(vec![string]))
        )
        |
        conv!(UiQualifiedId(separated_nonempty_list!(
            keyword!("."),
            identifier
        )))
    ));

    named!(reserved_word<&str, &str>, alt!(
        keyword!("const")
        |
        keyword!("super")
        |
        keyword!("import")
    ));

    fn ui_qualified_id(i: &str) -> IResult<&str, UiQualifiedId> {
        let (new_i, expression) = try_parse!(i, member_expression);
        let mut expr_it = &expression;

        if let Expression::ArrayMemberExpression(ref ame) = *expr_it { //TODO: warning ignored annotation
            expr_it = &ame.base;
        };

        let mut names = vec![];

        while let Expression::FieldMemberExpression(ref fme) = *expr_it {
            names.push(fme.name);
            expr_it = &fme.base;
        }

        if let Expression::IdentifierExpression(ref ie) = *expr_it {
            names.push(ie.0);
            IResult::Done(new_i, UiQualifiedId(names))
        } else {
            IResult::Error(ErrorKind::Custom(0))
        }
    }

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

        #[test]
        fn ui_qualified_pragma_id() {
            assert!(super::ui_qualified_pragma_id("").is_incomplete());

            assert_eq!(
                super::ui_qualified_pragma_id(" test "),
                IResult::Done(" ", UiQualifiedPragmaId("test"))
            );

            assert_eq!(
                super::ui_qualified_pragma_id(" test.field "),
                IResult::Error(ErrorKind::Custom(1))
            );
        }
    }

}<|endoftext|>


<commit_before>//! Interface for reading object files.

use alloc::vec::Vec;
use core::cmp;

use crate::common::{
    FileFlags, RelocationEncoding, RelocationKind, SectionFlags, SectionKind, SymbolFlags,
    SymbolKind, SymbolScope,
};

mod util;

mod any;
pub use any::*;

pub mod coff;

pub mod elf;

pub mod macho;

pub mod pe;

mod traits;
pub use traits::*;

#[cfg(feature = "wasm")]
pub mod wasm;

/// The native executable file for the target platform.
#[cfg(all(target_os = "linux", target_pointer_width = "32"))]
pub type NativeFile<'data> = elf::ElfFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "linux", target_pointer_width = "64"))]
pub type NativeFile<'data> = elf::ElfFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "32"))]
pub type NativeFile<'data> = macho::MachOFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "64"))]
pub type NativeFile<'data> = macho::MachOFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "32"))]
pub type NativeFile<'data> = pe::PeFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "64"))]
pub type NativeFile<'data> = pe::PeFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
pub type NativeFile<'data> = wasm::WasmFile<'data>;

/// The index used to identify a section of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SectionIndex(pub usize);

/// The index used to identify a symbol of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SymbolIndex(pub usize);

/// The section where a symbol is defined.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolSection {
    /// The section is unknown.
    Unknown,
    /// The section is not applicable for this symbol (such as file symbols).
    None,
    /// The symbol is undefined.
    Undefined,
    /// The symbol has an absolute value.
    Absolute,
    /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
    Common,
    /// The symbol is defined in the given section.
    Section(SectionIndex),
}

impl SymbolSection {
    /// Returns the section index for the section where the symbol is defined.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn index(self) -> Option<SectionIndex> {
        if let SymbolSection::Section(index) = self {
            Some(index)
        } else {
            None
        }
    }
}

/// A symbol table entry.
#[derive(Debug)]
pub struct Symbol<'data> {
    name: Option<&'data str>,
    address: u64,
    size: u64,
    kind: SymbolKind,
    section: SymbolSection,
    weak: bool,
    scope: SymbolScope,
    flags: SymbolFlags<SectionIndex>,
}

impl<'data> Symbol<'data> {
    /// Return the kind of this symbol.
    #[inline]
    pub fn kind(&self) -> SymbolKind {
        self.kind
    }

    /// Returns the section where the symbol is defined.
    #[inline]
    pub fn section(&self) -> SymbolSection {
        self.section
    }

    /// Returns the section index for the section containing this symbol.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn section_index(&self) -> Option<SectionIndex> {
        self.section.index()
    }

    /// Return true if the symbol is undefined.
    #[inline]
    pub fn is_undefined(&self) -> bool {
        self.section == SymbolSection::Undefined
    }

    /// Return true if the symbol is common data.
    ///
    /// Note: does not check for `SymbolSection::Section` with `SectionKind::Common`.
    #[inline]
    fn is_common(&self) -> bool {
        self.section == SymbolSection::Common
    }

    /// Return true if the symbol is weak.
    #[inline]
    pub fn is_weak(&self) -> bool {
        self.weak
    }

    /// Return true if the symbol visible outside of the compilation unit.
    ///
    /// This treats `SymbolScope::Unknown` as global.
    #[inline]
    pub fn is_global(&self) -> bool {
        !self.is_local()
    }

    /// Return true if the symbol is only visible within the compilation unit.
    #[inline]
    pub fn is_local(&self) -> bool {
        self.scope == SymbolScope::Compilation
    }

    /// Returns the symbol scope.
    #[inline]
    pub fn scope(&self) -> SymbolScope {
        self.scope
    }

    /// Symbol flags that are specific to each file format.
    #[inline]
    pub fn flags(&self) -> SymbolFlags<SectionIndex> {
        self.flags
    }

    /// The name of the symbol.
    #[inline]
    pub fn name(&self) -> Option<&'data str> {
        self.name
    }

    /// The address of the symbol. May be zero if the address is unknown.
    #[inline]
    pub fn address(&self) -> u64 {
        self.address
    }

    /// The size of the symbol. May be zero if the size is unknown.
    #[inline]
    pub fn size(&self) -> u64 {
        self.size
    }
}

/// A map from addresses to symbols.
#[derive(Debug)]
pub struct SymbolMap<'data> {
    symbols: Vec<Symbol<'data>>,
}

impl<'data> SymbolMap<'data> {
    /// Get the symbol containing the given address.
    pub fn get(&self, address: u64) -> Option<&Symbol<'data>> {
        self.symbols
            .binary_search_by(|symbol| {
                if address < symbol.address {
                    cmp::Ordering::Greater
                } else if address < symbol.address + symbol.size {
                    cmp::Ordering::Equal
                } else {
                    cmp::Ordering::Less
                }
            })
            .ok()
            .and_then(|index| self.symbols.get(index))
    }

    /// Get all symbols in the map.
    #[inline]
    pub fn symbols(&self) -> &[Symbol<'data>] {
        &self.symbols
    }

    /// Return true for symbols that should be included in the map.
    fn filter(symbol: &Symbol<'_>) -> bool {
        match symbol.kind() {
            SymbolKind::Unknown | SymbolKind::Text | SymbolKind::Data => {}
            SymbolKind::Null
            | SymbolKind::Section
            | SymbolKind::File
            | SymbolKind::Label
            | SymbolKind::Tls => {
                return false;
            }
        }
        !symbol.is_undefined() && !symbol.is_common() && symbol.size() > 0
    }
}

/// The target referenced by a relocation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RelocationTarget {
    /// The target is a symbol.
    Symbol(SymbolIndex),
    /// The target is a section.
    Section(SectionIndex),
}

/// A relocation entry.
#[derive(Debug)]
pub struct Relocation {
    kind: RelocationKind,
    encoding: RelocationEncoding,
    size: u8,
    target: RelocationTarget,
    addend: i64,
    implicit_addend: bool,
}

impl Relocation {
    /// The operation used to calculate the result of the relocation.
    #[inline]
    pub fn kind(&self) -> RelocationKind {
        self.kind
    }

    /// Information about how the result of the relocation operation is encoded in the place.
    #[inline]
    pub fn encoding(&self) -> RelocationEncoding {
        self.encoding
    }

    /// The size in bits of the place of the relocation.
    ///
    /// If 0, then the size is determined by the relocation kind.
    #[inline]
    pub fn size(&self) -> u8 {
        self.size
    }

    /// The target of the relocation.
    #[inline]
    pub fn target(&self) -> RelocationTarget {
        self.target
    }

    /// The addend to use in the relocation calculation.
    #[inline]
    pub fn addend(&self) -> i64 {
        self.addend
    }

    /// Set the addend to use in the relocation calculation.
    #[inline]
    pub fn set_addend(&mut self, addend: i64) {
        self.addend = addend
    }

    /// Returns true if there is an implicit addend stored in the data at the offset
    /// to be relocated.
    #[inline]
    pub fn has_implicit_addend(&self) -> bool {
        self.implicit_addend
    }
}

fn data_range(data: &[u8], data_address: u64, range_address: u64, size: u64) -> Option<&[u8]> {
    if range_address >= data_address {
        let start_offset = (range_address - data_address) as usize;
        let end_offset = start_offset + size as usize;
        if end_offset <= data.len() {
            return Some(&data[start_offset..end_offset]);
        }
    }
    None
}
<commit_msg>Derive Clone for Symbol<commit_after>//! Interface for reading object files.

use alloc::vec::Vec;
use core::cmp;

use crate::common::{
    FileFlags, RelocationEncoding, RelocationKind, SectionFlags, SectionKind, SymbolFlags,
    SymbolKind, SymbolScope,
};

mod util;

mod any;
pub use any::*;

pub mod coff;

pub mod elf;

pub mod macho;

pub mod pe;

mod traits;
pub use traits::*;

#[cfg(feature = "wasm")]
pub mod wasm;

/// The native executable file for the target platform.
#[cfg(all(target_os = "linux", target_pointer_width = "32"))]
pub type NativeFile<'data> = elf::ElfFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "linux", target_pointer_width = "64"))]
pub type NativeFile<'data> = elf::ElfFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "32"))]
pub type NativeFile<'data> = macho::MachOFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "64"))]
pub type NativeFile<'data> = macho::MachOFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "32"))]
pub type NativeFile<'data> = pe::PeFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "64"))]
pub type NativeFile<'data> = pe::PeFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
pub type NativeFile<'data> = wasm::WasmFile<'data>;

/// The index used to identify a section of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SectionIndex(pub usize);

/// The index used to identify a symbol of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SymbolIndex(pub usize);

/// The section where a symbol is defined.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolSection {
    /// The section is unknown.
    Unknown,
    /// The section is not applicable for this symbol (such as file symbols).
    None,
    /// The symbol is undefined.
    Undefined,
    /// The symbol has an absolute value.
    Absolute,
    /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
    Common,
    /// The symbol is defined in the given section.
    Section(SectionIndex),
}

impl SymbolSection {
    /// Returns the section index for the section where the symbol is defined.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn index(self) -> Option<SectionIndex> {
        if let SymbolSection::Section(index) = self {
            Some(index)
        } else {
            None
        }
    }
}

/// A symbol table entry.
#[derive(Clone, Debug)]
pub struct Symbol<'data> {
    name: Option<&'data str>,
    address: u64,
    size: u64,
    kind: SymbolKind,
    section: SymbolSection,
    weak: bool,
    scope: SymbolScope,
    flags: SymbolFlags<SectionIndex>,
}

impl<'data> Symbol<'data> {
    /// Return the kind of this symbol.
    #[inline]
    pub fn kind(&self) -> SymbolKind {
        self.kind
    }

    /// Returns the section where the symbol is defined.
    #[inline]
    pub fn section(&self) -> SymbolSection {
        self.section
    }

    /// Returns the section index for the section containing this symbol.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn section_index(&self) -> Option<SectionIndex> {
        self.section.index()
    }

    /// Return true if the symbol is undefined.
    #[inline]
    pub fn is_undefined(&self) -> bool {
        self.section == SymbolSection::Undefined
    }

    /// Return true if the symbol is common data.
    ///
    /// Note: does not check for `SymbolSection::Section` with `SectionKind::Common`.
    #[inline]
    fn is_common(&self) -> bool {
        self.section == SymbolSection::Common
    }

    /// Return true if the symbol is weak.
    #[inline]
    pub fn is_weak(&self) -> bool {
        self.weak
    }

    /// Return true if the symbol visible outside of the compilation unit.
    ///
    /// This treats `SymbolScope::Unknown` as global.
    #[inline]
    pub fn is_global(&self) -> bool {
        !self.is_local()
    }

    /// Return true if the symbol is only visible within the compilation unit.
    #[inline]
    pub fn is_local(&self) -> bool {
        self.scope == SymbolScope::Compilation
    }

    /// Returns the symbol scope.
    #[inline]
    pub fn scope(&self) -> SymbolScope {
        self.scope
    }

    /// Symbol flags that are specific to each file format.
    #[inline]
    pub fn flags(&self) -> SymbolFlags<SectionIndex> {
        self.flags
    }

    /// The name of the symbol.
    #[inline]
    pub fn name(&self) -> Option<&'data str> {
        self.name
    }

    /// The address of the symbol. May be zero if the address is unknown.
    #[inline]
    pub fn address(&self) -> u64 {
        self.address
    }

    /// The size of the symbol. May be zero if the size is unknown.
    #[inline]
    pub fn size(&self) -> u64 {
        self.size
    }
}

/// A map from addresses to symbols.
#[derive(Debug)]
pub struct SymbolMap<'data> {
    symbols: Vec<Symbol<'data>>,
}

impl<'data> SymbolMap<'data> {
    /// Get the symbol containing the given address.
    pub fn get(&self, address: u64) -> Option<&Symbol<'data>> {
        self.symbols
            .binary_search_by(|symbol| {
                if address < symbol.address {
                    cmp::Ordering::Greater
                } else if address < symbol.address + symbol.size {
                    cmp::Ordering::Equal
                } else {
                    cmp::Ordering::Less
                }
            })
            .ok()
            .and_then(|index| self.symbols.get(index))
    }

    /// Get all symbols in the map.
    #[inline]
    pub fn symbols(&self) -> &[Symbol<'data>] {
        &self.symbols
    }

    /// Return true for symbols that should be included in the map.
    fn filter(symbol: &Symbol<'_>) -> bool {
        match symbol.kind() {
            SymbolKind::Unknown | SymbolKind::Text | SymbolKind::Data => {}
            SymbolKind::Null
            | SymbolKind::Section
            | SymbolKind::File
            | SymbolKind::Label
            | SymbolKind::Tls => {
                return false;
            }
        }
        !symbol.is_undefined() && !symbol.is_common() && symbol.size() > 0
    }
}

/// The target referenced by a relocation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RelocationTarget {
    /// The target is a symbol.
    Symbol(SymbolIndex),
    /// The target is a section.
    Section(SectionIndex),
}

/// A relocation entry.
#[derive(Debug)]
pub struct Relocation {
    kind: RelocationKind,
    encoding: RelocationEncoding,
    size: u8,
    target: RelocationTarget,
    addend: i64,
    implicit_addend: bool,
}

impl Relocation {
    /// The operation used to calculate the result of the relocation.
    #[inline]
    pub fn kind(&self) -> RelocationKind {
        self.kind
    }

    /// Information about how the result of the relocation operation is encoded in the place.
    #[inline]
    pub fn encoding(&self) -> RelocationEncoding {
        self.encoding
    }

    /// The size in bits of the place of the relocation.
    ///
    /// If 0, then the size is determined by the relocation kind.
    #[inline]
    pub fn size(&self) -> u8 {
        self.size
    }

    /// The target of the relocation.
    #[inline]
    pub fn target(&self) -> RelocationTarget {
        self.target
    }

    /// The addend to use in the relocation calculation.
    #[inline]
    pub fn addend(&self) -> i64 {
        self.addend
    }

    /// Set the addend to use in the relocation calculation.
    #[inline]
    pub fn set_addend(&mut self, addend: i64) {
        self.addend = addend
    }

    /// Returns true if there is an implicit addend stored in the data at the offset
    /// to be relocated.
    #[inline]
    pub fn has_implicit_addend(&self) -> bool {
        self.implicit_addend
    }
}

fn data_range(data: &[u8], data_address: u64, range_address: u64, size: u64) -> Option<&[u8]> {
    if range_address >= data_address {
        let start_offset = (range_address - data_address) as usize;
        let end_offset = start_offset + size as usize;
        if end_offset <= data.len() {
            return Some(&data[start_offset..end_offset]);
        }
    }
    None
}
<|endoftext|>