<commit_before>#[derive(Debug)]
pub enum Parsed {
    Error(String),
    Empty,
    Section(String),
    Value(String, String), /* Vector(String, Vec<String>), impossible, because OrderedHashMap field has type String, not Vec */
}

pub fn parse_line(line: &str) -> Parsed {
    let content = match line.split(';').next() {
        Some(value) => value.trim(),
        None => return Parsed::Empty,
    };
    if content.is_empty() {
        return Parsed::Empty;
    }
    // add checks for content
    if content.starts_with('[') {
        if content.ends_with(']') {
            let section_name = content.trim_matches(|c| c == '[' || c == ']').to_owned();
            return Parsed::Section(section_name);
        } else {
            return Parsed::Error("incorrect section syntax".to_owned());
        }
    } else if content.contains('=') {
        let mut pair = content.splitn(2, '=').map(|s| s.trim());
        // if key is None => error
        let key = match pair.next() {
            Some(value) => value.to_owned(),
            None => return Parsed::Error("key is None".to_owned()),
        };
        // if value is None => empty string
        let value = match pair.next() {
            Some(value) => value.to_owned(),
            None => "".to_owned(),
        };
        if key.is_empty() {
            return Parsed::Error("empty key".to_owned());
        }
        return Parsed::Value(key, value);
    }
    Parsed::Error("incorrect syntax".to_owned())
}

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

    #[test]
    fn test_comment() {
        match parse_line(";------") {
            Parsed::Empty => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn test_entry() {
        match parse_line("name1 = 100 ; comment") {
            Parsed::Value(name, text) => {
                assert_eq!(name, String::from("name1"));
                assert_eq!(text, String::from("100"));
            }
            _ => assert!(false),
        }
    }

    #[test]
    fn test_weird_name() {
        match parse_line("_.,:(){}-#@&*| = 100") {
            Parsed::Value(name, text) => {
                assert_eq!(name, String::from("_.,:(){}-#@&*|"));
                assert_eq!(text, String::from("100"));
            }
            _ => assert!(false),
        }
    }

    #[test]
    fn test_text_entry() {
        match parse_line("text_name = hello world!") {
            Parsed::Value(name, text) => {
                assert_eq!(name, String::from("text_name"));
                assert_eq!(text, String::from("hello world!"));
            }
            _ => assert!(false),
        }
    }

    #[test]
    fn test_incorrect_token() {
        match parse_line("[section = 1, 2 = value") {
            Parsed::Error(_) => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn test_incorrect_key_value_line() {
        match parse_line("= 3") {
            Parsed::Error(_) => assert!(true),
            _ => assert!(false),
        }
    }
}
<commit_msg>add parser module documentation<commit_after>//! Parser module
//!
//! Contains `parse_line` routine to parse single line of ini file
//! and `Parsed` enum for parsing result

/// Enum for storing one of 4 possible `parse_line` results
#[derive(Debug)]
pub enum Parsed {
    // parse error
    Error(String),
    // empty line
    Empty,
    // [section]
    Section(String),
    // item = value
    Value(String, String),
}

/// parse single line of ini file
pub fn parse_line(line: &str) -> Parsed {
    let content = match line.split(';').next() {
        Some(value) => value.trim(),
        None => return Parsed::Empty,
    };
    if content.is_empty() {
        return Parsed::Empty;
    }
    // add checks for content
    if content.starts_with('[') {
        if content.ends_with(']') {
            let section_name = content.trim_matches(|c| c == '[' || c == ']').to_owned();
            return Parsed::Section(section_name);
        } else {
            return Parsed::Error("incorrect section syntax".to_owned());
        }
    } else if content.contains('=') {
        let mut pair = content.splitn(2, '=').map(|s| s.trim());
        // if key is None => error
        let key = match pair.next() {
            Some(value) => value.to_owned(),
            None => return Parsed::Error("key is None".to_owned()),
        };
        // if value is None => empty string
        let value = match pair.next() {
            Some(value) => value.to_owned(),
            None => "".to_owned(),
        };
        if key.is_empty() {
            return Parsed::Error("empty key".to_owned());
        }
        return Parsed::Value(key, value);
    }
    Parsed::Error("incorrect syntax".to_owned())
}

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

    #[test]
    fn test_comment() {
        match parse_line(";------") {
            Parsed::Empty => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn test_entry() {
        match parse_line("name1 = 100 ; comment") {
            Parsed::Value(name, text) => {
                assert_eq!(name, String::from("name1"));
                assert_eq!(text, String::from("100"));
            }
            _ => assert!(false),
        }
    }

    #[test]
    fn test_weird_name() {
        match parse_line("_.,:(){}-#@&*| = 100") {
            Parsed::Value(name, text) => {
                assert_eq!(name, String::from("_.,:(){}-#@&*|"));
                assert_eq!(text, String::from("100"));
            }
            _ => assert!(false),
        }
    }

    #[test]
    fn test_text_entry() {
        match parse_line("text_name = hello world!") {
            Parsed::Value(name, text) => {
                assert_eq!(name, String::from("text_name"));
                assert_eq!(text, String::from("hello world!"));
            }
            _ => assert!(false),
        }
    }

    #[test]
    fn test_incorrect_token() {
        match parse_line("[section = 1, 2 = value") {
            Parsed::Error(_) => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn test_incorrect_key_value_line() {
        match parse_line("= 3") {
            Parsed::Error(_) => assert!(true),
            _ => assert!(false),
        }
    }
}
<|endoftext|>


<commit_before>use token::Token;
use tokenizer::Tokenizer;

pub type Program = Vec<Statement>;

#[derive(PartialEq, Debug)]
pub enum Statement {
    NamedFunction {
        name: String,
        definition: Function,
    },
    AnonymousFunction(Function),
    Assignment {
        name: String,
        value: Expr,
    },
    Expression(Expr),
}

pub type Expr = Vec<ExprItem>;

#[derive(PartialEq, Debug)]
pub enum ExprItem {
    AnonymousFunction(Function),
    SingleTerm(Term),
    Group(Expr),
}

#[derive(PartialEq, Debug)]
pub struct Function {
    params: Vec<String>,
    body: Expr,
}

#[derive(PartialEq, Debug)]
pub enum Term {
    Symbol(String),
    Number(f64),
    StringLiteral(String),
}

pub struct Parser {
    lexer: Tokenizer,
}

#[derive(PartialEq, Debug)]
pub enum ParseError {
    SyntaxError,
}

impl Parser {
    pub fn new(lexer: Tokenizer) -> Parser {
        Parser {
            lexer: lexer,
        }
    }

    pub fn parse(&mut self) -> Result<Program, ParseError> {
        let mut lines: Program = Vec::new();

        loop {
            let line = try!(self.line());
            if line.is_none() {
                break;
            }

            let line = line.unwrap();
            lines.push(line);
        }

        Ok(lines)
    }

    fn line(&mut self) -> Result<Option<Statement>, ParseError> {
        Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use scanner::Scanner;
    use tokenizer::Tokenizer;

    #[test]
    fn complete_program() {
        let mut parser = parser_for(r#"
        defineUnit "km/h"

        my_var = 30 km/h
        f x yy = x km/h * 2 + (my_var / -3.5) * (8 ** yy) - 1.3e-3
        print (f 30)

        operator INFIX 6 "$$" (\x y = x * y + x)
        "#);

        let syntax_tree: Program = vec![
            Statement::Expression(vec![
                ExprItem::SingleTerm(
                    Term::Symbol("defineUnit".to_owned())
                ),
                ExprItem::SingleTerm(
                    Term::StringLiteral("km/h".to_owned())
                ),
            ]),
            Statement::Assignment {
                name: "my_var".to_owned(),
                value: vec![
                    ExprItem::SingleTerm(Term::Number(30f64)),
                    ExprItem::SingleTerm(Term::Symbol("km/h".to_owned())),
                ],
            },
            Statement::NamedFunction {
                name: "f".to_owned(),
                definition: Function {
                    params: vec![
                        "x".to_owned(),
                        "yy".to_owned()
                    ],
                    body: vec![
                        ExprItem::SingleTerm(
                            Term::Symbol("x".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Symbol("km/h".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Symbol("*".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Number(2f64)
                        ),
                        ExprItem::SingleTerm(
                            Term::Symbol("+".to_owned())
                        ),
                        ExprItem::Group(vec![
                            ExprItem::SingleTerm(
                                Term::Symbol("my_var".to_owned())
                            ),
                            ExprItem::SingleTerm(
                                Term::Symbol("/".to_owned())
                            ),
                            ExprItem::SingleTerm(
                                Term::Number(-3.5f64)
                            ),
                        ]),
                        ExprItem::SingleTerm(
                            Term::Symbol("*".to_owned())
                        ),
                        ExprItem::Group(vec![
                            ExprItem::SingleTerm(
                                Term::Number(8f64)
                            ),
                            ExprItem::SingleTerm(
                                Term::Symbol("**".to_owned())
                            ),
                            ExprItem::SingleTerm(
                                Term::Symbol("yy".to_owned())
                            ),
                        ]),
                        ExprItem::SingleTerm(
                            Term::Symbol("-".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Number(1.3e-3f64)
                        ),
                    ],
                },
            },
            Statement::Expression(vec![
                ExprItem::SingleTerm(
                    Term::Symbol("print".to_owned())
                ),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(
                        Term::Symbol("f".to_owned())
                    ),
                    ExprItem::SingleTerm(
                        Term::Number(30f64)
                    ),
                ]),
            ]),
            Statement::Expression(vec![
                ExprItem::SingleTerm(
                    Term::Symbol("operator".to_owned())
                ),
                ExprItem::SingleTerm(
                    Term::Symbol("INFIX".to_owned())
                ),
                ExprItem::SingleTerm(
                    Term::Number(6f64)
                ),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(
                        Term::StringLiteral("$$".to_owned())
                    ),
                    ExprItem::Group(vec![
                        ExprItem::AnonymousFunction(
                            Function {
                                params: vec![
                                    "x".to_owned(),
                                    "y".to_owned()
                                ],
                                body: vec![
                                    ExprItem::SingleTerm(
                                        Term::Symbol("x".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("*".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("y".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("+".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("x".to_owned())
                                    ),
                                ],
                            },
                        ),
                    ]),
                ]),
            ]),
        ];

        let parsed = parser.parse().unwrap();
        assert_eq!(parsed, syntax_tree);
    }

    fn parser_for(string: &str) -> Parser {
        Parser::new(Tokenizer::new(Scanner::from_str(string)))
    }
}

<commit_msg>Implementing some more of the parser<commit_after>use token::Token;
use tokenizer::{Tokenizer, LexerError};

pub type Program = Vec<Statement>;

#[derive(PartialEq, Debug)]
pub enum Statement {
    NamedFunction {
        name: String,
        definition: Function,
    },
    AnonymousFunction(Function),
    Assignment {
        name: String,
        value: Expr,
    },
    Expression(Expr),
}

pub type Expr = Vec<ExprItem>;

#[derive(PartialEq, Debug)]
pub enum ExprItem {
    AnonymousFunction(Function),
    SingleTerm(Term),
    Group(Expr),
}

#[derive(PartialEq, Debug)]
pub struct Function {
    params: Vec<String>,
    body: Expr,
}

#[derive(PartialEq, Debug)]
pub enum Term {
    Symbol(String),
    Number(f64),
    StringLiteral(String),
}

pub struct Parser {
    lexer: Tokenizer,
}

// Private type just for internal functions since the public interface
// cannot return option
type ParseResult<T> = Result<Option<T>, ParseError>;

#[derive(PartialEq, Debug)]
pub enum ParseError {
    ExpectedToken(Token),
    SyntaxError(LexerError),
}

impl Parser {
    pub fn new(lexer: Tokenizer) -> Parser {
        Parser {
            lexer: lexer,
        }
    }

    pub fn parse(&mut self) -> Result<Program, ParseError> {
        let mut statements: Program = Vec::new();

        loop {
            let statement = try!(self.statement());
            if statement.is_none() {
                break;
            }

            let statement = statement.unwrap();
            statements.push(statement);
        }

        Ok(statements)
    }

    fn statement(&mut self) -> ParseResult<Statement> {
        let line = try!(self.line());
        if line.is_none() {
            return Ok(None);
        }

        let line = line.unwrap();
        self.dispatch_statement(line)
    }

    fn line(&mut self) -> ParseResult<Vec<Token>> {
        let mut line_tokens: Vec<Token> = Vec::new();

        let mut reached_eof = true;
        for next_token in &mut self.lexer {
            let next_token = try!(
                next_token.map_err(|e| ParseError::SyntaxError(e))
            );

            if next_token == Token::EOL {
                reached_eof = false;
                break;
            }

            line_tokens.push(next_token);
        }

        if reached_eof && line_tokens.is_empty() {
            return Ok(None);
        }

        Ok(Some(line_tokens))
    }

    fn dispatch_statement(&mut self, statement_tokens: Vec<Token>) -> ParseResult<Statement> {
        Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use scanner::Scanner;
    use tokenizer::Tokenizer;

    #[test]
    fn complete_program() {
        let mut parser = parser_for(r#"
        defineUnit "km/h"

        my_var = 30 km/h
        f x yy = x km/h * 2 + (my_var / -3.5) * (8 ** yy) - 1.3e-3
        print (f 30)

        operator INFIX 6 "$$" (\x y = x * y + x)
        "#);

        let syntax_tree: Program = vec![
            Statement::Expression(vec![
                ExprItem::SingleTerm(
                    Term::Symbol("defineUnit".to_owned())
                ),
                ExprItem::SingleTerm(
                    Term::StringLiteral("km/h".to_owned())
                ),
            ]),
            Statement::Assignment {
                name: "my_var".to_owned(),
                value: vec![
                    ExprItem::SingleTerm(Term::Number(30f64)),
                    ExprItem::SingleTerm(Term::Symbol("km/h".to_owned())),
                ],
            },
            Statement::NamedFunction {
                name: "f".to_owned(),
                definition: Function {
                    params: vec![
                        "x".to_owned(),
                        "yy".to_owned()
                    ],
                    body: vec![
                        ExprItem::SingleTerm(
                            Term::Symbol("x".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Symbol("km/h".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Symbol("*".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Number(2f64)
                        ),
                        ExprItem::SingleTerm(
                            Term::Symbol("+".to_owned())
                        ),
                        ExprItem::Group(vec![
                            ExprItem::SingleTerm(
                                Term::Symbol("my_var".to_owned())
                            ),
                            ExprItem::SingleTerm(
                                Term::Symbol("/".to_owned())
                            ),
                            ExprItem::SingleTerm(
                                Term::Number(-3.5f64)
                            ),
                        ]),
                        ExprItem::SingleTerm(
                            Term::Symbol("*".to_owned())
                        ),
                        ExprItem::Group(vec![
                            ExprItem::SingleTerm(
                                Term::Number(8f64)
                            ),
                            ExprItem::SingleTerm(
                                Term::Symbol("**".to_owned())
                            ),
                            ExprItem::SingleTerm(
                                Term::Symbol("yy".to_owned())
                            ),
                        ]),
                        ExprItem::SingleTerm(
                            Term::Symbol("-".to_owned())
                        ),
                        ExprItem::SingleTerm(
                            Term::Number(1.3e-3f64)
                        ),
                    ],
                },
            },
            Statement::Expression(vec![
                ExprItem::SingleTerm(
                    Term::Symbol("print".to_owned())
                ),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(
                        Term::Symbol("f".to_owned())
                    ),
                    ExprItem::SingleTerm(
                        Term::Number(30f64)
                    ),
                ]),
            ]),
            Statement::Expression(vec![
                ExprItem::SingleTerm(
                    Term::Symbol("operator".to_owned())
                ),
                ExprItem::SingleTerm(
                    Term::Symbol("INFIX".to_owned())
                ),
                ExprItem::SingleTerm(
                    Term::Number(6f64)
                ),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(
                        Term::StringLiteral("$$".to_owned())
                    ),
                    ExprItem::Group(vec![
                        ExprItem::AnonymousFunction(
                            Function {
                                params: vec![
                                    "x".to_owned(),
                                    "y".to_owned()
                                ],
                                body: vec![
                                    ExprItem::SingleTerm(
                                        Term::Symbol("x".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("*".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("y".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("+".to_owned())
                                    ),
                                    ExprItem::SingleTerm(
                                        Term::Symbol("x".to_owned())
                                    ),
                                ],
                            },
                        ),
                    ]),
                ]),
            ]),
        ];

        let parsed = parser.parse().unwrap();
        assert_eq!(parsed, syntax_tree);
    }

    fn parser_for(string: &str) -> Parser {
        Parser::new(Tokenizer::new(Scanner::from_str(string)))
    }
}

<|endoftext|>


<commit_before>use ast;
use error::{InputPos, InputSpan, Logger};
use lexer;
use lexer::{Lexer, Token};

pub struct Parser<'a> {
    tokens: Vec<lexer::Token>,
    logger: Logger<'a>,
    index: uint,
}

impl<'a> Parser<'a> {
    pub fn new(mut lexer: Lexer, logger: Logger<'a>) -> Parser<'a> {
        let mut tokens: Vec<lexer::Token> = lexer.collect();
        let end_token = lexer::Token {
            value: lexer::Eof,
            pos: tokens.last().map(|t| t.pos.clone()).unwrap_or(InputPos::start()),
        };
        tokens.push(end_token);
        Parser {
            tokens: tokens,
            logger: logger,
            index: 0,
        }
    }

    fn peek(&self) -> lexer::TokenValue {
        self.tokens[self.index].value.clone()
    }

    fn next_token(&mut self) -> lexer::TokenValue {
        self.index += 1;
        self.tokens[self.index - 1].value.clone()
    }

    fn bump(&mut self) {
        self.index += 1;
    }

    fn expect(&mut self, token: lexer::TokenValue) {
        let span_start = self.current_pos();
        let next = self.next_token();
        if  next != token {
            self.logger.report_error(format!("expected `{}` but found `{}`", token, next),
                InputSpan::new(span_start, self.current_pos()));
            self.fatal_error();
        }
    }

    fn fatal_error(&self) -> ! {
        fail!("");
    }

    fn current_pos(&self) -> InputPos {
        self.tokens[self.index].pos.clone()
    }

    pub fn parse(&mut self) -> ast::Program {
        let mut items = vec![];
        let span_start = self.current_pos();
        let mut span_end = self.current_pos();

        loop {
            match self.try_parse_item() {
                Some(item) => {
                    span_end = item.span().end;
                    items.push(item);
                },
                None => break,
            }
            println!("{}\n", items.last());
        }

        ast::Program {
            items: items,
            span: InputSpan::new(span_start, span_end),
        }
    }

    fn try_parse_item(&mut self) -> Option<ast::Item> {
        let span_start = self.current_pos();
        let item = match self.next_token() {
            lexer::Fn => ast::FunctionItem(self.parse_function()),
            lexer::Struct => ast::StructItem(self.parse_struct_decl()),
            lexer::Let => ast::LetItem(self.parse_let()),

            lexer::Eof => return None,

            invalid => {
                self.logger.report_error(format!("expected `<Item>` but found, `{}`", invalid),
                    InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        };

        Some(item)
    }

    fn parse_function(&mut self) -> ast::FunctionDeclaration {
        let span_start = self.current_pos();

        // Read function name
        let name = self.parse_name();

        // Read function args
        self.expect(lexer::LeftParen);
        let mut params = vec![];

        // Handle case with 0 args
        if self.peek() == lexer::RightParen {
            self.bump();
        }
        // Otherwise parse all the parameters
        else {
            loop {
                let param = self.parse_param();
                params.push(param);

                // Check if there is another argument
                if self.peek() == lexer::Comma {
                    self.bump()
                }
                // Otherwise check for closing paren as break
                else {
                    self.expect(lexer::RightParen);
                    break;
                }
            }
        }

        let type_span_start = self.current_pos();
        // Read function return type
        let rtype = match self.peek() {
            lexer::LeftBrace => ast::Primitive(ast::UnitType),

            lexer::RightArrow => {
                self.bump();
                let type_ = self.parse_type();
                type_
            },

            invalid => {
                self.logger.report_error(format!("expected `{{` or `->` but found `{}`", invalid),
                    InputSpan::new(type_span_start, self.current_pos()));
                self.fatal_error();
            },
        };

        // Read function body
        let body = self.parse_block();

        let span_end = self.current_pos();
        ast::FunctionDeclaration {
            name: name,
            params: params,
            rtype: rtype,
            body: body,
            span: InputSpan::new(span_start, span_end),
        }
    }

    fn parse_struct_decl(&mut self) -> ast::StructDeclaration {
        unimplemented!()
    }

    fn parse_let(&mut self) -> ast::LetStatement {
        let span_start = self.current_pos();
        let (name, opt_type) = self.parse_var_with_type();

        let opt_assignment = match self.next_token() {
            lexer::Assignment => Some(self.parse_assignment(name.clone())),
            lexer::SemiColon => None,
            invalid => {
                self.logger.report_error(format!("expected `=` or `;` but found `{}`",
                    invalid), InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            }
        };

        // If the type wasn't specified for this variable then there needs
        let type_ = match (&opt_type, &opt_assignment) {
            (&Some(ref t), _) => t.clone(),
            (&None, &Some(ref assignment)) => {
                assignment.expression.rtype.clone()
            },
            (&None, &None) => {
                self.logger.report_error(format!("could not determine type for variable `{}`",
                    name), InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        };

        ast::LetStatement {
            name: name,
            var_type: type_,
            assignment: opt_assignment,
            span: InputSpan::new(span_start, self.current_pos()),
        }
    }

    fn parse_name(&mut self) -> String {
        let span_start = self.current_pos();
        match self.next_token() {
            lexer::Ident(name) => name.clone(),

            invalid => {
                self.logger.report_error(format!("expected `<identifer>` but found `{}`", invalid),
                    InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        }
    }

    fn parse_type(&mut self) -> ast::Type {
        let span_start = self.current_pos();
        match self.next_token() {
            lexer::Ident(name) => ast::UserType(name.clone()),
            lexer::Int => ast::Primitive(ast::IntType),
            lexer::Bool => ast::Primitive(ast::BoolType),

            invalid => {
                self.logger.report_error(format!("expected `<Type>` but found `{}`", invalid),
                    InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        }
    }

    fn parse_param(&mut self) -> (String, ast::Type) {
        let span_start = self.current_pos();
        match self.parse_var_with_type() {
            (name, Some(t)) => (name, t),
            (name, None) => {
                let span_end = self.current_pos();
                self.logger.report_error(format!("expected `<Type>` for parameter `{}`", name),
                    InputSpan::new(span_start, span_end));
                self.fatal_error();
            },
        }
    }

    fn parse_var_with_type(&mut self) -> (String, Option<ast::Type>) {
        let name = self.parse_name();

        match self.peek() {
            lexer::Colon => self.bump(),
            _ => return (name, None),
        }

        let type_ = self.parse_type();
        (name, Some(type_))
    }

    fn parse_block(&mut self) -> ast::Block {
        let span_start = self.current_pos();
        self.expect(lexer::LeftBrace);

        let mut statements = vec![];

        loop {
            if self.peek() == lexer::RightBrace {
                self.bump();
                break;
            }
            else {
                let expression = self.parse_expression();
                if self.peek() != lexer::RightBrace {
                    self.expect(lexer::SemiColon);
                    statements.push(expression);
                }
            }
        }

        ast::Block {
            statements: statements,
            span: InputSpan::new(span_start, self.current_pos()),
        }
    }

    fn parse_expression(&mut self) -> ast::Expression {
        let span_start = self.current_pos();

        match self.next_token() {
            lexer::Ident(name) => self.handle_ident(name.to_string()),
            lexer::LitNum(value) => self.handle_num(value),
            lexer::Let => {
                ast::Expression {
                    expr: box ast::LetExpr(self.parse_let()),
                    rtype: ast::Primitive(ast::UnitType),
                }
            },
            lexer::If => self.parse_if(),
            lexer::For => unimplemented!(),
            lexer::While => unimplemented!(),
            lexer::Loop => self.parse_loop(),
            lexer::Break => self.parse_break(),
            lexer::Return => self.parse_return(),
            lexer::Asm => self.parse_asm(),
            invalid => {
                let span_end = self.current_pos();
                self.logger.report_error(format!("expected `<Expression>` but found `{}`", invalid),
                    InputSpan::new(span_start, span_end));
                self.fatal_error();
            },
        }
    }

    fn handle_ident(&mut self, name: String) -> ast::Expression {
        match self.peek() {
            lexer::LeftParen => {
                self.bump();
                self.parse_call(name)
            },

            lexer::Assignment => {
                ast::Expression {
                    expr: box ast::AssignExpr(self.parse_assignment(name)),
                    rtype: ast::Primitive(ast::UnitType),
                }
            }

            lexer::Plus => unimplemented!(),
            lexer::Minus => unimplemented!(),
            lexer::PlusEq => unimplemented!(),
            lexer::MinusEq => unimplemented!(),

            _ => {
                ast::Expression {
                    expr: box ast::VariableExpr(name.clone()),
                    rtype: ast::VariableType(name),
                }
            }
        }
    }

    fn parse_call(&mut self, name: String) -> ast::Expression {
        let span_start = self.current_pos();
        let mut args = vec![];
        loop {
            if self.peek() == lexer::RightParen {
                self.bump();
                break;
            }

            args.push(self.parse_expression());

            // Check if there is another argument
            if self.peek() == lexer::Comma {
                self.bump()
            }
            // Otherwise check for closing paren as break
            else {
                self.expect(lexer::RightParen);
                break;
            }
        }

        let function_call = ast::FunctionCall {
            name: name.clone(),
            args: args,
            span: InputSpan::new(span_start, self.current_pos()),
        };

        ast::Expression {
            expr: box ast::CallExpr(function_call),
            rtype: ast::VariableType(name),
        }
    }

    fn parse_assignment(&mut self, target: String) -> ast::Assignment {
        let span_start = self.current_pos();
        ast::Assignment {
            target: target,
            expression: self.parse_expression(),
            span: InputSpan::new(span_start, self.current_pos()),
        }
    }

    fn handle_num(&mut self, val: int) -> ast::Expression {
        ast::Expression {
            expr: box ast::LitNumExpr(val),
            rtype: ast::Primitive(ast::IntType),
        }
    }

    fn parse_if(&mut self) -> ast::Expression {
        let span_start = self.current_pos();

        let condition = self.parse_expression();
        let body = self.parse_block();
        let else_block = match self.peek() {
            lexer::Else => {
                self.bump();
                if self.next_token() == lexer::If {
                    fail!("FIXME: Handle else if statements");
                }

                Some(self.parse_block())
            },
            _ => None,
        };

        let if_statement = ast::IfStatement {
            condition: condition,
            body: body,
            else_block: else_block,
            span: InputSpan::new(span_start, self.current_pos()),
        };

        let rtype = if_statement.body.rtype();

        ast::Expression {
            expr: box ast::IfExpr(if_statement),
            rtype: rtype,
        }
    }

    fn parse_loop(&mut self) -> ast::Expression {
        let span_start = self.current_pos();
        let body = self.parse_block();

        let loop_statement = ast::LoopStatement {
            body: body,
            span: InputSpan::new(span_start, self.current_pos()),
        };

        ast::Expression {
            expr: box ast::LoopExpr(loop_statement),
            rtype: ast::Primitive(ast::UnitType),
        }
    }

    fn parse_break(&mut self) -> ast::Expression {
        ast::Expression {
            expr: box ast::Break,
            rtype: ast::Primitive(ast::BottomType),
        }
    }

    fn parse_return(&mut self) -> ast::Expression {
        let expression = self.parse_expression();
        let rtype = expression.rtype.clone();
        ast::Expression {
            expr: box ast::Return(expression),
            rtype: rtype,
        }
    }

    fn parse_asm(&mut self) -> ast::Expression {
        let span_start = self.current_pos();
        self.expect(lexer::LeftBrace);

        let mut code = String::new();
        loop {
            match self.next_token() {
                lexer::LitString(ref string) => {
                    code.push_str(string.as_slice());
                    code.push_char('\n');
                },
                lexer::RightBrace => {
                    break;
                },
                invalid => {
                    self.logger.report_error(
                        format!("expected `\"<string>\"` or `}}` but found, `{}`", invalid),
                        InputSpan::new(span_start, self.current_pos()));
                    self.fatal_error();
                },
            }

            match self.next_token() {
                lexer::RightBrace => break,
                lexer::Comma => continue,
                invalid => {
                    self.logger.report_error(
                        format!("expected `,` or `}}` but found, `{}`", invalid),
                        InputSpan::new(span_start, self.current_pos()));
                    self.fatal_error();
                },
            }
        }

        ast::Expression {
            expr: box ast::AsmOpExpr(code),
            rtype: ast::Primitive(ast::AnyType),
        }
    }
}
<commit_msg>Allow implicit semicolons at the end of if statements.<commit_after>use ast;
use error::{InputPos, InputSpan, Logger};
use lexer;
use lexer::{Lexer, Token};

pub struct Parser<'a> {
    tokens: Vec<lexer::Token>,
    logger: Logger<'a>,
    index: uint,
    fake_semicolon: bool,
}

impl<'a> Parser<'a> {
    pub fn new(mut lexer: Lexer, logger: Logger<'a>) -> Parser<'a> {
        let mut tokens: Vec<lexer::Token> = lexer.collect();
        let end_token = lexer::Token {
            value: lexer::Eof,
            pos: tokens.last().map(|t| t.pos.clone()).unwrap_or(InputPos::start()),
        };
        tokens.push(end_token);
        Parser {
            tokens: tokens,
            logger: logger,
            index: 0,
            fake_semicolon: false,
        }
    }

    fn peek(&self) -> lexer::TokenValue {
        self.tokens[self.index].value.clone()
    }

    fn next_token(&mut self) -> lexer::TokenValue {
        self.index += 1;
        self.tokens[self.index - 1].value.clone()
    }

    fn bump(&mut self) {
        self.index += 1;
    }

    fn expect(&mut self, token: lexer::TokenValue) {
        let span_start = self.current_pos();
        let next = self.next_token();
        if next != token {
            self.logger.report_error(format!("expected `{}` but found `{}`", token, next),
                InputSpan::new(span_start, self.current_pos()));
            self.fatal_error();
        }
    }

    fn fatal_error(&self) -> ! {
        fail!("");
    }

    fn current_pos(&self) -> InputPos {
        self.tokens[self.index].pos.clone()
    }

    pub fn parse(&mut self) -> ast::Program {
        let mut items = vec![];
        let span_start = self.current_pos();
        let mut span_end = self.current_pos();

        loop {
            match self.try_parse_item() {
                Some(item) => {
                    span_end = item.span().end;
                    items.push(item);
                },
                None => break,
            }
            println!("{}\n", items.last());
        }

        ast::Program {
            items: items,
            span: InputSpan::new(span_start, span_end),
        }
    }

    fn try_parse_item(&mut self) -> Option<ast::Item> {
        let span_start = self.current_pos();
        let item = match self.next_token() {
            lexer::Fn => ast::FunctionItem(self.parse_function()),
            lexer::Struct => ast::StructItem(self.parse_struct_decl()),
            lexer::Let => ast::LetItem(self.parse_let()),

            lexer::Eof => return None,

            invalid => {
                self.logger.report_error(format!("expected `<Item>` but found, `{}`", invalid),
                    InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        };

        Some(item)
    }

    fn parse_function(&mut self) -> ast::FunctionDeclaration {
        let span_start = self.current_pos();

        // Read function name
        let name = self.parse_name();

        // Read function args
        self.expect(lexer::LeftParen);
        let mut params = vec![];

        // Handle case with 0 args
        if self.peek() == lexer::RightParen {
            self.bump();
        }
        // Otherwise parse all the parameters
        else {
            loop {
                let param = self.parse_param();
                params.push(param);

                // Check if there is another argument
                if self.peek() == lexer::Comma {
                    self.bump()
                }
                // Otherwise check for closing paren as break
                else {
                    self.expect(lexer::RightParen);
                    break;
                }
            }
        }

        let type_span_start = self.current_pos();
        // Read function return type
        let rtype = match self.peek() {
            lexer::LeftBrace => ast::Primitive(ast::UnitType),

            lexer::RightArrow => {
                self.bump();
                let type_ = self.parse_type();
                type_
            },

            invalid => {
                self.logger.report_error(format!("expected `{{` or `->` but found `{}`", invalid),
                    InputSpan::new(type_span_start, self.current_pos()));
                self.fatal_error();
            },
        };

        // Read function body
        let body = self.parse_block();

        let span_end = self.current_pos();
        ast::FunctionDeclaration {
            name: name,
            params: params,
            rtype: rtype,
            body: body,
            span: InputSpan::new(span_start, span_end),
        }
    }

    fn parse_struct_decl(&mut self) -> ast::StructDeclaration {
        unimplemented!()
    }

    fn parse_let(&mut self) -> ast::LetStatement {
        let span_start = self.current_pos();
        let (name, opt_type) = self.parse_var_with_type();

        let opt_assignment = match self.next_token() {
            lexer::Assignment => Some(self.parse_assignment(name.clone())),
            lexer::SemiColon => None,
            invalid => {
                self.logger.report_error(format!("expected `=` or `;` but found `{}`",
                    invalid), InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            }
        };

        // If the type wasn't specified for this variable then there needs
        let type_ = match (&opt_type, &opt_assignment) {
            (&Some(ref t), _) => t.clone(),
            (&None, &Some(ref assignment)) => {
                assignment.expression.rtype.clone()
            },
            (&None, &None) => {
                self.logger.report_error(format!("could not determine type for variable `{}`",
                    name), InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        };

        ast::LetStatement {
            name: name,
            var_type: type_,
            assignment: opt_assignment,
            span: InputSpan::new(span_start, self.current_pos()),
        }
    }

    fn parse_name(&mut self) -> String {
        let span_start = self.current_pos();
        match self.next_token() {
            lexer::Ident(name) => name.clone(),

            invalid => {
                self.logger.report_error(format!("expected `<identifer>` but found `{}`", invalid),
                    InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        }
    }

    fn parse_type(&mut self) -> ast::Type {
        let span_start = self.current_pos();
        match self.next_token() {
            lexer::Ident(name) => ast::UserType(name.clone()),
            lexer::Int => ast::Primitive(ast::IntType),
            lexer::Bool => ast::Primitive(ast::BoolType),

            invalid => {
                self.logger.report_error(format!("expected `<Type>` but found `{}`", invalid),
                    InputSpan::new(span_start, self.current_pos()));
                self.fatal_error();
            },
        }
    }

    fn parse_param(&mut self) -> (String, ast::Type) {
        let span_start = self.current_pos();
        match self.parse_var_with_type() {
            (name, Some(t)) => (name, t),
            (name, None) => {
                let span_end = self.current_pos();
                self.logger.report_error(format!("expected `<Type>` for parameter `{}`", name),
                    InputSpan::new(span_start, span_end));
                self.fatal_error();
            },
        }
    }

    fn parse_var_with_type(&mut self) -> (String, Option<ast::Type>) {
        let name = self.parse_name();

        match self.peek() {
            lexer::Colon => self.bump(),
            _ => return (name, None),
        }

        let type_ = self.parse_type();
        (name, Some(type_))
    }

    fn parse_block(&mut self) -> ast::Block {
        let span_start = self.current_pos();
        self.expect(lexer::LeftBrace);

        let mut statements = vec![];

        loop {
            if self.peek() == lexer::RightBrace {
                self.bump();
                break;
            }
            else {
                let expression = self.parse_expression();
                if self.peek() != lexer::RightBrace {
                    if !self.fake_semicolon {
                        self.expect(lexer::SemiColon);
                    }
                    self.fake_semicolon = false;
                    statements.push(expression);
                }
            }
        }

        ast::Block {
            statements: statements,
            span: InputSpan::new(span_start, self.current_pos()),
        }
    }

    fn parse_expression(&mut self) -> ast::Expression {
        let span_start = self.current_pos();

        match self.next_token() {
            lexer::Ident(name) => self.handle_ident(name.to_string()),
            lexer::LitNum(value) => self.handle_num(value),
            lexer::Let => {
                ast::Expression {
                    expr: box ast::LetExpr(self.parse_let()),
                    rtype: ast::Primitive(ast::UnitType),
                }
            },
            lexer::If => self.parse_if(),
            lexer::For => unimplemented!(),
            lexer::While => unimplemented!(),
            lexer::Loop => self.parse_loop(),
            lexer::Break => self.parse_break(),
            lexer::Return => self.parse_return(),
            lexer::Asm => self.parse_asm(),
            invalid => {
                let span_end = self.current_pos();
                self.logger.report_error(format!("expected `<Expression>` but found `{}`", invalid),
                    InputSpan::new(span_start, span_end));
                self.fatal_error();
            },
        }
    }

    fn handle_ident(&mut self, name: String) -> ast::Expression {
        match self.peek() {
            lexer::LeftParen => {
                self.bump();
                self.parse_call(name)
            },

            lexer::Assignment => {
                ast::Expression {
                    expr: box ast::AssignExpr(self.parse_assignment(name)),
                    rtype: ast::Primitive(ast::UnitType),
                }
            }

            lexer::Plus => unimplemented!(),
            lexer::Minus => unimplemented!(),
            lexer::PlusEq => unimplemented!(),
            lexer::MinusEq => unimplemented!(),

            _ => {
                ast::Expression {
                    expr: box ast::VariableExpr(name.clone()),
                    rtype: ast::VariableType(name),
                }
            }
        }
    }

    fn parse_call(&mut self, name: String) -> ast::Expression {
        let span_start = self.current_pos();
        let mut args = vec![];
        loop {
            if self.peek() == lexer::RightParen {
                self.bump();
                break;
            }

            args.push(self.parse_expression());

            // Check if there is another argument
            if self.peek() == lexer::Comma {
                self.bump()
            }
            // Otherwise check for closing paren as break
            else {
                self.expect(lexer::RightParen);
                break;
            }
        }

        let function_call = ast::FunctionCall {
            name: name.clone(),
            args: args,
            span: InputSpan::new(span_start, self.current_pos()),
        };

        ast::Expression {
            expr: box ast::CallExpr(function_call),
            rtype: ast::VariableType(name),
        }
    }

    fn parse_assignment(&mut self, target: String) -> ast::Assignment {
        let span_start = self.current_pos();
        ast::Assignment {
            target: target,
            expression: self.parse_expression(),
            span: InputSpan::new(span_start, self.current_pos()),
        }
    }

    fn handle_num(&mut self, val: int) -> ast::Expression {
        ast::Expression {
            expr: box ast::LitNumExpr(val),
            rtype: ast::Primitive(ast::IntType),
        }
    }

    fn parse_if(&mut self) -> ast::Expression {
        let span_start = self.current_pos();

        let condition = self.parse_expression();
        let body = self.parse_block();
        let else_block = match self.peek() {
            lexer::Else => {
                self.bump();
                if self.next_token() == lexer::If {
                    fail!("FIXME: Handle else if statements");
                }

                Some(self.parse_block())
            },
            _ => None,
        };

        // Insert an implicit semicolon if there wasn't one at the end of the if statement
        if self.peek() != lexer::SemiColon {
            self.fake_semicolon = true;
        }

        let if_statement = ast::IfStatement {
            condition: condition,
            body: body,
            else_block: else_block,
            span: InputSpan::new(span_start, self.current_pos()),
        };

        let rtype = if_statement.body.rtype();

        ast::Expression {
            expr: box ast::IfExpr(if_statement),
            rtype: rtype,
        }
    }

    fn parse_loop(&mut self) -> ast::Expression {
        let span_start = self.current_pos();
        let body = self.parse_block();

        let loop_statement = ast::LoopStatement {
            body: body,
            span: InputSpan::new(span_start, self.current_pos()),
        };

        ast::Expression {
            expr: box ast::LoopExpr(loop_statement),
            rtype: ast::Primitive(ast::UnitType),
        }
    }

    fn parse_break(&mut self) -> ast::Expression {
        ast::Expression {
            expr: box ast::Break,
            rtype: ast::Primitive(ast::BottomType),
        }
    }

    fn parse_return(&mut self) -> ast::Expression {
        let expression = self.parse_expression();
        let rtype = expression.rtype.clone();
        ast::Expression {
            expr: box ast::Return(expression),
            rtype: rtype,
        }
    }

    fn parse_asm(&mut self) -> ast::Expression {
        let span_start = self.current_pos();
        self.expect(lexer::LeftBrace);

        let mut code = String::new();
        loop {
            match self.next_token() {
                lexer::LitString(ref string) => {
                    code.push_str(string.as_slice());
                    code.push_char('\n');
                },
                lexer::RightBrace => {
                    break;
                },
                invalid => {
                    self.logger.report_error(
                        format!("expected `\"<string>\"` or `}}` but found, `{}`", invalid),
                        InputSpan::new(span_start, self.current_pos()));
                    self.fatal_error();
                },
            }

            match self.next_token() {
                lexer::RightBrace => break,
                lexer::Comma => continue,
                invalid => {
                    self.logger.report_error(
                        format!("expected `,` or `}}` but found, `{}`", invalid),
                        InputSpan::new(span_start, self.current_pos()));
                    self.fatal_error();
                },
            }
        }

        ast::Expression {
            expr: box ast::AsmOpExpr(code),
            rtype: ast::Primitive(ast::AnyType),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::*;
use std::io::{Result, Write};
use case::CaseExt;

use env::Env;
use file_saver::*;
use library;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use super::super::general::{self, tabs};
use traits::*;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  PathBuf::from(&env.config.target_path)
        .join(file_name_sys("lib"));
    println!("Generating file {:?}", path);

    save_to_file(path, &mut |w| generate_lib(w, env));
}

fn generate_lib<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib_ffi(w));
    } else {
        try!(statics::only_for_glib(w));
    }
    if env.config.library_name == "Gtk" {
        try!(statics::only_for_gtk(w));
    }

    let ns = env.library.namespace(library::MAIN_NAMESPACE);
    let classes = prepare(ns);
    let interfaces = prepare(ns);

    try!(generate_aliases(w, env, &prepare(ns)));
    try!(generate_enums(w, &ns.name, &prepare(ns)));
    try!(generate_bitfields(w, &ns.name, &prepare(ns)));
    try!(generate_unions(w, &ns.name, &prepare(ns)));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &prepare_records(ns)));
    try!(generate_classes_structs(w, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, ""));
    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));

    //TODO: other functions
    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0}_ffi;", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &library::Namespace) -> Vec<&T>
where library::Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn prepare_records(ns: &library::Namespace) -> Vec<&library::Record> {
    let mut vec = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(rec) = typ.maybe_ref_as::<library::Record>() {
            // We don't want the FooBarPrivate and similar records where FooBar is a type
            if ["Private"].iter()
                    .filter_map(|s| strip_suffix(&rec.name, s))
                    .any(|s| ns.index.get(s).is_some()) {
                continue;
            }
            vec.push(rec);
        }
    }
    vec.sort();
    vec
}

fn generate_aliases<W: Write>(w: &mut W, env: &Env, items: &[&library::Alias])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        let (comment, c_type) = match ffi_type(env, item.typ, &item.target_c_type) {
            Ok(x) => ("", x),
            Err(x) => ("//", x),
        };
        try!(writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type));
    }

    Ok(())
}

fn generate_bitfields<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Bitfield])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "bitflags! {{\n{}#[repr(C)]\n{0}flags {}: i32 {{", tabs(1), item.name));
        for member in &item.members {
            try!(writeln!(w, "{}const {} = {},",
                          tabs(2), strip_prefix(ns_name, &member.c_identifier), member.value));
        }
        try!(writeln!(w, "{}}}\n}}", tabs(1)));
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_enums<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Enumeration])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        if item.members.len() == 1 {
            try!(writeln!(w, "pub type {} = i32;", item.name));
            try!(writeln!(w, "pub const {}: {} = {};",
                          item.members[0].c_identifier, item.name, item.members[0].value));
            try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
            try!(writeln!(w, ""));
            continue;
        }

        let mut vals = HashMap::new();
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.name));
        for member in &item.members {
            if vals.get(&member.value).is_some() {
                continue;
            }
            try!(writeln!(w, "{}{} = {},",
                          tabs(1), &prepare_enum_member_name(&member.name), member.value));
            vals.insert(member.value.clone(), member.name.clone());
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            try!(writeln!(w, "pub const {}: {} = {1}::{};",
                          strip_prefix(ns_name, &member.c_identifier),
                          item.name, &prepare_enum_member_name(vals.get(&member.value).unwrap())));
        }
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_unions<W: Write>(w: &mut W, _ns_name: &str, items: &[&library::Union])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "pub type {} = c_void;", item.name));
        try!(writeln!(w, "pub type {} = {};", item.c_type.as_ref().unwrap(), item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs<W: Write>(w: &mut W, classes: &[&library::Class]) -> Result<()> {
    try!(writeln!(w, ""));
    for klass in classes {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", klass.c_type));
    }

    Ok(())
}

fn generate_interfaces_structs<W: Write>(w: &mut W, interfaces: &[&library::Interface]) -> Result<()> {
    try!(writeln!(w, ""));
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", interface.c_type));
    }

    Ok(())
}

fn generate_records<W: Write>(w: &mut W, env: &Env, records: &[&library::Record]) -> Result<()> {
    try!(writeln!(w, ""));
    for record in records {
        let mut lines = Vec::new();
        let mut commented = false;
        for field in &record.fields {
            if env.library.type_(field.typ).maybe_ref_as::<library::Union>().is_some()
                    || field.bits.is_some() {
                warn!("Record `{}` field `{}` not expressible in Rust, truncated",
                      record.name, field.name);
                lines.push(format!("{}_truncated_record_marker: (),", tabs(1)));
                break;
            }
            let vis = if field.private { "" } else { "pub " };

            if let Some(ref c_type) = field.c_type {
                let name = mangle_keywords(&*field.name);
                let c_type = ffi_type(env, field.typ, c_type);
                lines.push(format!("{}{}{}: {},", tabs(1), vis, name, c_type.as_str()));
                if c_type.is_err() {
                    commented = true;
                }
            }
            else {
                let name = mangle_keywords(&*field.name);
                if let Some(ref func) =
                        env.library.type_(field.typ).maybe_ref_as::<library::Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("{}{}{}: fn{},", tabs(1), vis, name, sig));
                    commented |= com;
                }
                else {
                    lines.push(format!("{}{}{}: [{:?}],", tabs(1), vis, name, field.typ));
                    commented = true;
                }
            }
        }
        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {};\n", comment, record.c_type));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.c_type));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}
<commit_msg>Remove redundant argument in generate_unions<commit_after>use std::collections::HashMap;
use std::path::*;
use std::io::{Result, Write};
use case::CaseExt;

use env::Env;
use file_saver::*;
use library;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use super::super::general::{self, tabs};
use traits::*;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  PathBuf::from(&env.config.target_path)
        .join(file_name_sys("lib"));
    println!("Generating file {:?}", path);

    save_to_file(path, &mut |w| generate_lib(w, env));
}

fn generate_lib<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib_ffi(w));
    } else {
        try!(statics::only_for_glib(w));
    }
    if env.config.library_name == "Gtk" {
        try!(statics::only_for_gtk(w));
    }

    let ns = env.library.namespace(library::MAIN_NAMESPACE);
    let classes = prepare(ns);
    let interfaces = prepare(ns);

    try!(generate_aliases(w, env, &prepare(ns)));
    try!(generate_enums(w, &ns.name, &prepare(ns)));
    try!(generate_bitfields(w, &ns.name, &prepare(ns)));
    try!(generate_unions(w, &prepare(ns)));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &prepare_records(ns)));
    try!(generate_classes_structs(w, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, ""));
    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));

    //TODO: other functions
    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0}_ffi;", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &library::Namespace) -> Vec<&T>
where library::Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn prepare_records(ns: &library::Namespace) -> Vec<&library::Record> {
    let mut vec = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(rec) = typ.maybe_ref_as::<library::Record>() {
            // We don't want the FooBarPrivate and similar records where FooBar is a type
            if ["Private"].iter()
                    .filter_map(|s| strip_suffix(&rec.name, s))
                    .any(|s| ns.index.get(s).is_some()) {
                continue;
            }
            vec.push(rec);
        }
    }
    vec.sort();
    vec
}

fn generate_aliases<W: Write>(w: &mut W, env: &Env, items: &[&library::Alias])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        let (comment, c_type) = match ffi_type(env, item.typ, &item.target_c_type) {
            Ok(x) => ("", x),
            Err(x) => ("//", x),
        };
        try!(writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type));
    }

    Ok(())
}

fn generate_bitfields<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Bitfield])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "bitflags! {{\n{}#[repr(C)]\n{0}flags {}: i32 {{", tabs(1), item.name));
        for member in &item.members {
            try!(writeln!(w, "{}const {} = {},",
                          tabs(2), strip_prefix(ns_name, &member.c_identifier), member.value));
        }
        try!(writeln!(w, "{}}}\n}}", tabs(1)));
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_enums<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Enumeration])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        if item.members.len() == 1 {
            try!(writeln!(w, "pub type {} = i32;", item.name));
            try!(writeln!(w, "pub const {}: {} = {};",
                          item.members[0].c_identifier, item.name, item.members[0].value));
            try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
            try!(writeln!(w, ""));
            continue;
        }

        let mut vals = HashMap::new();
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.name));
        for member in &item.members {
            if vals.get(&member.value).is_some() {
                continue;
            }
            try!(writeln!(w, "{}{} = {},",
                          tabs(1), &prepare_enum_member_name(&member.name), member.value));
            vals.insert(member.value.clone(), member.name.clone());
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            try!(writeln!(w, "pub const {}: {} = {1}::{};",
                          strip_prefix(ns_name, &member.c_identifier),
                          item.name, &prepare_enum_member_name(vals.get(&member.value).unwrap())));
        }
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_unions<W: Write>(w: &mut W, items: &[&library::Union])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "pub type {} = c_void;", item.name));
        try!(writeln!(w, "pub type {} = {};", item.c_type.as_ref().unwrap(), item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs<W: Write>(w: &mut W, classes: &[&library::Class]) -> Result<()> {
    try!(writeln!(w, ""));
    for klass in classes {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", klass.c_type));
    }

    Ok(())
}

fn generate_interfaces_structs<W: Write>(w: &mut W, interfaces: &[&library::Interface]) -> Result<()> {
    try!(writeln!(w, ""));
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", interface.c_type));
    }

    Ok(())
}

fn generate_records<W: Write>(w: &mut W, env: &Env, records: &[&library::Record]) -> Result<()> {
    try!(writeln!(w, ""));
    for record in records {
        let mut lines = Vec::new();
        let mut commented = false;
        for field in &record.fields {
            if env.library.type_(field.typ).maybe_ref_as::<library::Union>().is_some()
                    || field.bits.is_some() {
                warn!("Record `{}` field `{}` not expressible in Rust, truncated",
                      record.name, field.name);
                lines.push(format!("{}_truncated_record_marker: (),", tabs(1)));
                break;
            }
            let vis = if field.private { "" } else { "pub " };

            if let Some(ref c_type) = field.c_type {
                let name = mangle_keywords(&*field.name);
                let c_type = ffi_type(env, field.typ, c_type);
                lines.push(format!("{}{}{}: {},", tabs(1), vis, name, c_type.as_str()));
                if c_type.is_err() {
                    commented = true;
                }
            }
            else {
                let name = mangle_keywords(&*field.name);
                if let Some(ref func) =
                        env.library.type_(field.typ).maybe_ref_as::<library::Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("{}{}{}: fn{},", tabs(1), vis, name, sig));
                    commented |= com;
                }
                else {
                    lines.push(format!("{}{}{}: [{:?}],", tabs(1), vis, name, field.typ));
                    commented = true;
                }
            }
        }
        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {};\n", comment, record.c_type));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.c_type));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>
import middle::trans;
import trans::decl_cdecl_fn;
import middle::trans_common::T_f32;
import middle::trans_common::T_f64;
import middle::trans_common::T_fn;
import middle::trans_common::T_bool;
import middle::trans_common::T_i8;
import middle::trans_common::T_i32;
import middle::trans_common::T_int;
import middle::trans_common::T_ivec;
import middle::trans_common::T_nil;
import middle::trans_common::T_opaque_chan_ptr;
import middle::trans_common::T_opaque_ivec;
import middle::trans_common::T_opaque_port_ptr;
import middle::trans_common::T_opaque_vec_ptr;
import middle::trans_common::T_ptr;
import middle::trans_common::T_size_t;
import middle::trans_common::T_str;
import middle::trans_common::T_void;
import lib::llvm::type_names;
import lib::llvm::llvm::ModuleRef;
import lib::llvm::llvm::ValueRef;
import lib::llvm::llvm::TypeRef;

type upcalls =
    {grow_task: ValueRef,
     log_int: ValueRef,
     log_float: ValueRef,
     log_double: ValueRef,
     log_str: ValueRef,
     log_istr: ValueRef,
     trace_word: ValueRef,
     trace_str: ValueRef,
     new_port: ValueRef,
     del_port: ValueRef,
     new_chan: ValueRef,
     flush_chan: ValueRef,
     drop_chan: ValueRef,
     take_chan: ValueRef,
     del_chan: ValueRef,
     clone_chan: ValueRef,
     chan_target_task: ValueRef,
     _yield: ValueRef,
     sleep: ValueRef,
     send: ValueRef,
     recv: ValueRef,
     _fail: ValueRef,
     kill: ValueRef,
     exit: ValueRef,
     malloc: ValueRef,
     free: ValueRef,
     shared_malloc: ValueRef,
     shared_free: ValueRef,
     mark: ValueRef,
     new_str: ValueRef,
     dup_str: ValueRef,
     new_vec: ValueRef,
     vec_append: ValueRef,
     get_type_desc: ValueRef,
     new_task: ValueRef,
     take_task: ValueRef,
     drop_task: ValueRef,
     start_task: ValueRef,
     ivec_resize: ValueRef,
     ivec_spill: ValueRef,
     ivec_resize_shared: ValueRef,
     ivec_spill_shared: ValueRef};

fn declare_upcalls(tn: type_names, tydesc_type: TypeRef,
                   taskptr_type: TypeRef, llmod: ModuleRef) -> @upcalls {
    fn decl(tn: type_names, tydesc_type: TypeRef, taskptr_type: TypeRef,
            llmod: ModuleRef, name: str, tys: TypeRef[], rv: TypeRef) ->
       ValueRef {
        let arg_tys: TypeRef[] = ~[taskptr_type];
        for t: TypeRef  in tys { arg_tys += ~[t]; }
        let fn_ty = T_fn(arg_tys, rv);
        ret trans::decl_cdecl_fn(llmod, "upcall_" + name, fn_ty);
    }
    let dv = bind decl(tn, tydesc_type, taskptr_type, llmod, _, _, T_void());
    let d = bind decl(tn, tydesc_type, taskptr_type, llmod, _, _, _);
    // FIXME: Sigh:.. remove this when I fix the typechecker pushdown.
    // --pcwalton

    let empty_vec: TypeRef[] = ~[];
    ret @{grow_task: dv("grow_task", ~[T_size_t()]),
          log_int: dv("log_int", ~[T_i32(), T_i32()]),
          log_float: dv("log_float", ~[T_i32(), T_f32()]),
          log_double: dv("log_double", ~[T_i32(), T_ptr(T_f64())]),
          log_str: dv("log_str", ~[T_i32(), T_ptr(T_str())]),
          log_istr: dv("log_istr", ~[T_i32(), T_ptr(T_ivec(T_i8()))]),
          trace_word: dv("trace_word", ~[T_int()]),
          trace_str: dv("trace_str", ~[T_ptr(T_i8())]),
          new_port: d("new_port", ~[T_size_t()], T_opaque_port_ptr()),
          del_port: dv("del_port", ~[T_opaque_port_ptr()]),
          new_chan:
              d("new_chan", ~[T_opaque_port_ptr()], T_opaque_chan_ptr()),
          flush_chan: dv("flush_chan", ~[T_opaque_chan_ptr()]),
          drop_chan: dv("drop_chan", ~[T_opaque_chan_ptr()]),
          take_chan: dv("take_chan", ~[T_opaque_chan_ptr()]),
          del_chan: dv("del_chan", ~[T_opaque_chan_ptr()]),
          clone_chan:
              d("clone_chan", ~[taskptr_type, T_opaque_chan_ptr()],
                T_opaque_chan_ptr()),
          chan_target_task:
              d("chan_target_task", ~[T_opaque_chan_ptr()], taskptr_type),
          _yield: dv("yield", empty_vec),
          sleep: dv("sleep", ~[T_size_t()]),
          send: dv("send", ~[T_opaque_chan_ptr(), T_ptr(T_i8())]),
          recv: dv("recv", ~[T_ptr(T_ptr(T_i8())), T_opaque_port_ptr()]),
          _fail: dv("fail", ~[T_ptr(T_i8()), T_ptr(T_i8()), T_size_t()]),
          kill: dv("kill", ~[taskptr_type]),
          exit: dv("exit", empty_vec),
          malloc:
              d("malloc", ~[T_size_t(), T_ptr(tydesc_type)], T_ptr(T_i8())),
          free: dv("free", ~[T_ptr(T_i8()), T_int()]),
          shared_malloc:
              d("shared_malloc", ~[T_size_t(), T_ptr(tydesc_type)],
                T_ptr(T_i8())),
          shared_free: dv("shared_free", ~[T_ptr(T_i8())]),
          mark: d("mark", ~[T_ptr(T_i8())], T_int()),
          new_str: d("new_str", ~[T_ptr(T_i8()), T_size_t()], T_ptr(T_str())),
          dup_str:
              d("dup_str", ~[taskptr_type, T_ptr(T_str())], T_ptr(T_str())),
          new_vec:
              d("new_vec", ~[T_size_t(), T_ptr(tydesc_type)],
                T_opaque_vec_ptr()),
          vec_append:
              d("vec_append",
                ~[T_ptr(tydesc_type), T_ptr(tydesc_type),
                  T_ptr(T_opaque_vec_ptr()), T_opaque_vec_ptr(), T_bool()],
                T_void()),
          get_type_desc:
              d("get_type_desc",
                ~[T_ptr(T_nil()), T_size_t(), T_size_t(), T_size_t(),
                  T_ptr(T_ptr(tydesc_type))], T_ptr(tydesc_type)),
          new_task: d("new_task", ~[T_ptr(T_str())], taskptr_type),
          take_task: dv("take_task", ~[taskptr_type]),
          drop_task: dv("drop_task", ~[taskptr_type]),
          start_task:
              d("start_task", ~[taskptr_type, T_int(), T_int(), T_size_t()],
                taskptr_type),
          ivec_resize:
              d("ivec_resize", ~[T_ptr(T_opaque_ivec()), T_int()], T_void()),
          ivec_spill:
              d("ivec_spill", ~[T_ptr(T_opaque_ivec()), T_int()], T_void()),
          ivec_resize_shared:
              d("ivec_resize_shared", ~[T_ptr(T_opaque_ivec()), T_int()],
                T_void()),
          ivec_spill_shared:
              d("ivec_spill_shared", ~[T_ptr(T_opaque_ivec()), T_int()],
                T_void())};
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>rustc: Declare upcall_cmp_type<commit_after>
import middle::trans;
import trans::decl_cdecl_fn;
import middle::trans_common::T_f32;
import middle::trans_common::T_f64;
import middle::trans_common::T_fn;
import middle::trans_common::T_bool;
import middle::trans_common::T_i1;
import middle::trans_common::T_i8;
import middle::trans_common::T_i32;
import middle::trans_common::T_int;
import middle::trans_common::T_ivec;
import middle::trans_common::T_nil;
import middle::trans_common::T_opaque_chan_ptr;
import middle::trans_common::T_opaque_ivec;
import middle::trans_common::T_opaque_port_ptr;
import middle::trans_common::T_opaque_vec_ptr;
import middle::trans_common::T_ptr;
import middle::trans_common::T_size_t;
import middle::trans_common::T_str;
import middle::trans_common::T_void;
import lib::llvm::type_names;
import lib::llvm::llvm::ModuleRef;
import lib::llvm::llvm::ValueRef;
import lib::llvm::llvm::TypeRef;

type upcalls =
    {grow_task: ValueRef,
     log_int: ValueRef,
     log_float: ValueRef,
     log_double: ValueRef,
     log_str: ValueRef,
     log_istr: ValueRef,
     trace_word: ValueRef,
     trace_str: ValueRef,
     new_port: ValueRef,
     del_port: ValueRef,
     new_chan: ValueRef,
     flush_chan: ValueRef,
     drop_chan: ValueRef,
     take_chan: ValueRef,
     del_chan: ValueRef,
     clone_chan: ValueRef,
     chan_target_task: ValueRef,
     _yield: ValueRef,
     sleep: ValueRef,
     send: ValueRef,
     recv: ValueRef,
     _fail: ValueRef,
     kill: ValueRef,
     exit: ValueRef,
     malloc: ValueRef,
     free: ValueRef,
     shared_malloc: ValueRef,
     shared_free: ValueRef,
     mark: ValueRef,
     new_str: ValueRef,
     dup_str: ValueRef,
     new_vec: ValueRef,
     vec_append: ValueRef,
     get_type_desc: ValueRef,
     new_task: ValueRef,
     take_task: ValueRef,
     drop_task: ValueRef,
     start_task: ValueRef,
     ivec_resize: ValueRef,
     ivec_spill: ValueRef,
     ivec_resize_shared: ValueRef,
     ivec_spill_shared: ValueRef,
     cmp_type: ValueRef};

fn declare_upcalls(tn: type_names, tydesc_type: TypeRef,
                   taskptr_type: TypeRef, llmod: ModuleRef) -> @upcalls {
    fn decl(tn: type_names, llmod: ModuleRef, name: str, tys: TypeRef[],
          rv: TypeRef) -> ValueRef {
        let arg_tys: TypeRef[] = ~[];
        for t: TypeRef  in tys { arg_tys += ~[t]; }
        let fn_ty = T_fn(arg_tys, rv);
        ret trans::decl_cdecl_fn(llmod, "upcall_" + name, fn_ty);
    }
    fn decl_with_taskptr(taskptr_type: TypeRef, tn: type_names,
                         llmod: ModuleRef, name: str, tys: TypeRef[],
                         rv: TypeRef) -> ValueRef {
        ret decl(tn, llmod, name, ~[taskptr_type] + tys, rv);
    }
    let dv = bind decl_with_taskptr(taskptr_type, tn, llmod, _, _, T_void());
    let d = bind decl_with_taskptr(taskptr_type, tn, llmod, _, _, _);
    let dr = bind decl(tn, llmod, _, _, _);

    let empty_vec: TypeRef[] = ~[];
    ret @{grow_task: dv("grow_task", ~[T_size_t()]),
          log_int: dv("log_int", ~[T_i32(), T_i32()]),
          log_float: dv("log_float", ~[T_i32(), T_f32()]),
          log_double: dv("log_double", ~[T_i32(), T_ptr(T_f64())]),
          log_str: dv("log_str", ~[T_i32(), T_ptr(T_str())]),
          log_istr: dv("log_istr", ~[T_i32(), T_ptr(T_ivec(T_i8()))]),
          trace_word: dv("trace_word", ~[T_int()]),
          trace_str: dv("trace_str", ~[T_ptr(T_i8())]),
          new_port: d("new_port", ~[T_size_t()], T_opaque_port_ptr()),
          del_port: dv("del_port", ~[T_opaque_port_ptr()]),
          new_chan:
              d("new_chan", ~[T_opaque_port_ptr()], T_opaque_chan_ptr()),
          flush_chan: dv("flush_chan", ~[T_opaque_chan_ptr()]),
          drop_chan: dv("drop_chan", ~[T_opaque_chan_ptr()]),
          take_chan: dv("take_chan", ~[T_opaque_chan_ptr()]),
          del_chan: dv("del_chan", ~[T_opaque_chan_ptr()]),
          clone_chan:
              d("clone_chan", ~[taskptr_type, T_opaque_chan_ptr()],
                T_opaque_chan_ptr()),
          chan_target_task:
              d("chan_target_task", ~[T_opaque_chan_ptr()], taskptr_type),
          _yield: dv("yield", empty_vec),
          sleep: dv("sleep", ~[T_size_t()]),
          send: dv("send", ~[T_opaque_chan_ptr(), T_ptr(T_i8())]),
          recv: dv("recv", ~[T_ptr(T_ptr(T_i8())), T_opaque_port_ptr()]),
          _fail: dv("fail", ~[T_ptr(T_i8()), T_ptr(T_i8()), T_size_t()]),
          kill: dv("kill", ~[taskptr_type]),
          exit: dv("exit", empty_vec),
          malloc:
              d("malloc", ~[T_size_t(), T_ptr(tydesc_type)], T_ptr(T_i8())),
          free: dv("free", ~[T_ptr(T_i8()), T_int()]),
          shared_malloc:
              d("shared_malloc", ~[T_size_t(), T_ptr(tydesc_type)],
                T_ptr(T_i8())),
          shared_free: dv("shared_free", ~[T_ptr(T_i8())]),
          mark: d("mark", ~[T_ptr(T_i8())], T_int()),
          new_str: d("new_str", ~[T_ptr(T_i8()), T_size_t()], T_ptr(T_str())),
          dup_str:
              d("dup_str", ~[taskptr_type, T_ptr(T_str())], T_ptr(T_str())),
          new_vec:
              d("new_vec", ~[T_size_t(), T_ptr(tydesc_type)],
                T_opaque_vec_ptr()),
          vec_append:
              d("vec_append",
                ~[T_ptr(tydesc_type), T_ptr(tydesc_type),
                  T_ptr(T_opaque_vec_ptr()), T_opaque_vec_ptr(), T_bool()],
                T_void()),
          get_type_desc:
              d("get_type_desc",
                ~[T_ptr(T_nil()), T_size_t(), T_size_t(), T_size_t(),
                  T_ptr(T_ptr(tydesc_type))], T_ptr(tydesc_type)),
          new_task: d("new_task", ~[T_ptr(T_str())], taskptr_type),
          take_task: dv("take_task", ~[taskptr_type]),
          drop_task: dv("drop_task", ~[taskptr_type]),
          start_task:
              d("start_task", ~[taskptr_type, T_int(), T_int(), T_size_t()],
                taskptr_type),
          ivec_resize:
              d("ivec_resize", ~[T_ptr(T_opaque_ivec()), T_int()], T_void()),
          ivec_spill:
              d("ivec_spill", ~[T_ptr(T_opaque_ivec()), T_int()], T_void()),
          ivec_resize_shared:
              d("ivec_resize_shared", ~[T_ptr(T_opaque_ivec()), T_int()],
                T_void()),
          ivec_spill_shared:
              d("ivec_spill_shared", ~[T_ptr(T_opaque_ivec()), T_int()],
                T_void()),
          cmp_type:
              dr("cmp_type", ~[T_ptr(T_i1()), taskptr_type,
                 T_ptr(tydesc_type), T_ptr(T_ptr(tydesc_type)),
                 T_ptr(T_i8()), T_ptr(T_i8()), T_int()],
                 T_void())};
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>use pom::{Input, DataInput};
use std::cmp;
use std::io::{Result, Read, Error, ErrorKind};
use std::path::Path;
use std::fs::File;

use super::{Document, Object, ObjectId};
use super::parser;
use xref::XrefEntry;
use object_stream::ObjectStream;

impl Document {

	/// Load PDF document from specified file path.
	#[inline]
	pub fn load<P: AsRef<Path>>(path: P) -> Result<Document> {
		let file = File::open(path)?;
		let buffer = Vec::with_capacity(file.metadata()?.len() as usize);
		Self::load_internal(file, buffer)
	}

	/// Load PDF document from arbitrary source
	#[inline]
	pub fn load_from<R: Read>(source: R) -> Result<Document> {
		let buffer = Vec::<u8>::new();
		Self::load_internal(source, buffer)
	}

	fn load_internal<R: Read>(mut source: R, mut buffer: Vec<u8>) -> Result<Document> {

		source.read_to_end(&mut buffer)?;

		let mut reader = Reader {
			buffer: buffer,
			document: Document::new(),
		};

		reader.read()?;
		Ok(reader.document)
	}
}

pub struct Reader {
	buffer: Vec<u8>,
	document: Document,
}

impl Reader {
	/// Read whole document.
	fn read(&mut self) -> Result<()> {
		let mut input = DataInput::new(&self.buffer);
		// The document structure can be expressed in PEG as:
		//   document <- header indirect_object* xref trailer xref_start
		let version = parser::header().parse(&mut input)
			.map_err(|_|Error::new(ErrorKind::InvalidData, "Not a valid PDF file (header)."))?;

		let xref_start = Self::get_xref_start(&self.buffer, &mut input)?;
		input.jump_to(xref_start);

		let (mut xref, mut trailer) = parser::xref_and_trailer(&self).parse(&mut input)
			.map_err(|err|Error::new(ErrorKind::InvalidData, format!("Not a valid PDF file (xref_and_trailer).\n{:?}", err)))?;

		// Read previous Xrefs of linearized or incremental updated document.
		let mut prev_xref_start = trailer.remove("Prev");
		while let Some(prev) = prev_xref_start.and_then(|offset|offset.as_i64()) {
			input.jump_to(prev as usize);
			let (prev_xref, mut prev_trailer) = parser::xref_and_trailer(&self).parse(&mut input)
				.map_err(|err|Error::new(ErrorKind::InvalidData, format!("Not a valid PDF file (prev xref_and_trailer).\n{:?}", err)))?;
			xref.extend(prev_xref);

			// Read xref stream in hybrid-reference file
			let prev_xref_stream_start = trailer.remove("XRefStm");
			if let Some(prev) = prev_xref_stream_start.and_then(|offset|offset.as_i64()) {
				input.jump_to(prev as usize);
				let (prev_xref, _) = parser::xref_and_trailer(&self).parse(&mut input)
					.map_err(|_|Error::new(ErrorKind::InvalidData, "Not a valid PDF file (prev xref_and_trailer)."))?;
				xref.extend(prev_xref);
			}

			prev_xref_start = prev_trailer.remove("Prev");
		}

		self.document.version = version;
		self.document.max_id = xref.size - 1;
		self.document.trailer = trailer;
		self.document.reference_table = xref;

		for entry in self.document.reference_table.entries.values().filter(|entry|entry.is_normal()) {
			match *entry {
				XrefEntry::Normal{offset, ..} => {
					let (object_id, mut object) = self.read_object(offset as usize)?;

					match object {
						Object::Stream(ref mut stream) => if stream.dict.type_is(b"ObjStm") {
							self.document.streams.insert(object_id.0, ObjectStream::new(stream));
						},
						_ => {}
					}

					self.document.objects.insert(object_id, object);
				},
				_ => {},
			};
		}

		Ok(())
	}

	/// Get object offset by object id.
	fn get_offset(&self, id: ObjectId) -> Option<u32> {
		if let Some(entry) = self.document.reference_table.get(id.0) {
			match *entry {
				XrefEntry::Normal{offset, generation} => {
					if id.1 == generation { Some(offset) } else { None }
				},
				_ => None,
			}
		} else {
			None
		}
	}

	pub fn get_object(&self, id: ObjectId) -> Option<Object> {
		if let Some(offset) = self.get_offset(id) {
			if let Ok((_, obj)) = self.read_object(offset as usize) {
				return Some(obj);
			}
		}
		return None;
	}

	fn read_object(&self, offset: usize) -> Result<(ObjectId, Object)> {
		let mut input = DataInput::new(&self.buffer);
		input.jump_to(offset);
		parser::indirect_object(self).parse(&mut input)
			.map_err(|err|Error::new(ErrorKind::InvalidData, format!("Not a valid PDF file (read object at {}).\n{:?}", offset, err)))
	}

	fn get_xref_start(buffer: &[u8], input: &mut Input<u8>) -> Result<usize> {
		let seek_pos = buffer.len() - cmp::min(buffer.len(), 512);
		Self::search_substring(buffer, b"%%EOF", seek_pos)
			.and_then(|eof_pos| Self::search_substring(buffer, b"startxref", eof_pos - 25))
			.and_then(|xref_pos| {
				input.jump_to(xref_pos);
				match parser::xref_start().parse(input) {
					Ok(startxref) => Some(startxref as usize),
					_ => None,
				}
			})
			.ok_or(Error::new(ErrorKind::InvalidData, "Not a valid PDF file (xref_start)."))
	}

	fn search_substring(buffer: &[u8], pattern: &[u8], start_pos: usize) -> Option<usize> {
		let mut seek_pos = start_pos;
		let mut index = 0;

		while seek_pos < buffer.len() && index < pattern.len() {
			if buffer[seek_pos] == pattern[index] {
				index += 1;
			} else if index > 0 {
				seek_pos -= index;
				index = 0;
			}
			seek_pos += 1;

			if index == pattern.len() {
				return Some(seek_pos - index);
			}
		}

		return None;
	}
}

#[test]
fn load_document() {

	let mut doc = Document::load("assets/example.pdf").unwrap();
	assert_eq!(doc.version, "1.5");
	doc.save("test_2_load.pdf").unwrap();
}
<commit_msg>Ignore invalid objects when reading all object in xref table<commit_after>use pom::{Input, DataInput};
use std::cmp;
use std::io::{Result, Read, Error, ErrorKind};
use std::path::Path;
use std::fs::File;

use super::{Document, Object, ObjectId};
use super::parser;
use xref::XrefEntry;
use object_stream::ObjectStream;

impl Document {

	/// Load PDF document from specified file path.
	#[inline]
	pub fn load<P: AsRef<Path>>(path: P) -> Result<Document> {
		let file = File::open(path)?;
		let buffer = Vec::with_capacity(file.metadata()?.len() as usize);
		Self::load_internal(file, buffer)
	}

	/// Load PDF document from arbitrary source
	#[inline]
	pub fn load_from<R: Read>(source: R) -> Result<Document> {
		let buffer = Vec::<u8>::new();
		Self::load_internal(source, buffer)
	}

	fn load_internal<R: Read>(mut source: R, mut buffer: Vec<u8>) -> Result<Document> {

		source.read_to_end(&mut buffer)?;

		let mut reader = Reader {
			buffer: buffer,
			document: Document::new(),
		};

		reader.read()?;
		Ok(reader.document)
	}
}

pub struct Reader {
	buffer: Vec<u8>,
	document: Document,
}

impl Reader {
	/// Read whole document.
	fn read(&mut self) -> Result<()> {
		let mut input = DataInput::new(&self.buffer);
		// The document structure can be expressed in PEG as:
		//   document <- header indirect_object* xref trailer xref_start
		let version = parser::header().parse(&mut input)
			.map_err(|_|Error::new(ErrorKind::InvalidData, "Not a valid PDF file (header)."))?;

		let xref_start = Self::get_xref_start(&self.buffer, &mut input)?;
		input.jump_to(xref_start);

		let (mut xref, mut trailer) = parser::xref_and_trailer(&self).parse(&mut input)
			.map_err(|err|Error::new(ErrorKind::InvalidData, format!("Not a valid PDF file (xref_and_trailer).\n{:?}", err)))?;

		// Read previous Xrefs of linearized or incremental updated document.
		let mut prev_xref_start = trailer.remove("Prev");
		while let Some(prev) = prev_xref_start.and_then(|offset|offset.as_i64()) {
			input.jump_to(prev as usize);
			let (prev_xref, mut prev_trailer) = parser::xref_and_trailer(&self).parse(&mut input)
				.map_err(|err|Error::new(ErrorKind::InvalidData, format!("Not a valid PDF file (prev xref_and_trailer).\n{:?}", err)))?;
			xref.extend(prev_xref);

			// Read xref stream in hybrid-reference file
			let prev_xref_stream_start = trailer.remove("XRefStm");
			if let Some(prev) = prev_xref_stream_start.and_then(|offset|offset.as_i64()) {
				input.jump_to(prev as usize);
				let (prev_xref, _) = parser::xref_and_trailer(&self).parse(&mut input)
					.map_err(|_|Error::new(ErrorKind::InvalidData, "Not a valid PDF file (prev xref_and_trailer)."))?;
				xref.extend(prev_xref);
			}

			prev_xref_start = prev_trailer.remove("Prev");
		}

		self.document.version = version;
		self.document.max_id = xref.size - 1;
		self.document.trailer = trailer;
		self.document.reference_table = xref;

		for entry in self.document.reference_table.entries.values().filter(|entry|entry.is_normal()) {
			match *entry {
				XrefEntry::Normal{offset, ..} => {
					let read_result = self.read_object(offset as usize);
					match read_result {
						Ok((object_id, mut object)) => {
							match object {
								Object::Stream(ref mut stream) => if stream.dict.type_is(b"ObjStm") {
									self.document.streams.insert(object_id.0, ObjectStream::new(stream));
								},
								_ => {}
							}
							self.document.objects.insert(object_id, object);
						},
						Err(err) => { println!("{:?}", err); }
					}
				},
				_ => {},
			};
		}

		Ok(())
	}

	/// Get object offset by object id.
	fn get_offset(&self, id: ObjectId) -> Option<u32> {
		if let Some(entry) = self.document.reference_table.get(id.0) {
			match *entry {
				XrefEntry::Normal{offset, generation} => {
					if id.1 == generation { Some(offset) } else { None }
				},
				_ => None,
			}
		} else {
			None
		}
	}

	pub fn get_object(&self, id: ObjectId) -> Option<Object> {
		if let Some(offset) = self.get_offset(id) {
			if let Ok((_, obj)) = self.read_object(offset as usize) {
				return Some(obj);
			}
		}
		return None;
	}

	fn read_object(&self, offset: usize) -> Result<(ObjectId, Object)> {
		let mut input = DataInput::new(&self.buffer);
		input.jump_to(offset);
		parser::indirect_object(self).parse(&mut input)
			.map_err(|err|Error::new(ErrorKind::InvalidData, format!("Not a valid PDF file (read object at {}).\n{:?}", offset, err)))
	}

	fn get_xref_start(buffer: &[u8], input: &mut Input<u8>) -> Result<usize> {
		let seek_pos = buffer.len() - cmp::min(buffer.len(), 512);
		Self::search_substring(buffer, b"%%EOF", seek_pos)
			.and_then(|eof_pos| Self::search_substring(buffer, b"startxref", eof_pos - 25))
			.and_then(|xref_pos| {
				input.jump_to(xref_pos);
				match parser::xref_start().parse(input) {
					Ok(startxref) => Some(startxref as usize),
					_ => None,
				}
			})
			.ok_or(Error::new(ErrorKind::InvalidData, "Not a valid PDF file (xref_start)."))
	}

	fn search_substring(buffer: &[u8], pattern: &[u8], start_pos: usize) -> Option<usize> {
		let mut seek_pos = start_pos;
		let mut index = 0;

		while seek_pos < buffer.len() && index < pattern.len() {
			if buffer[seek_pos] == pattern[index] {
				index += 1;
			} else if index > 0 {
				seek_pos -= index;
				index = 0;
			}
			seek_pos += 1;

			if index == pattern.len() {
				return Some(seek_pos - index);
			}
		}

		return None;
	}
}

#[test]
fn load_document() {

	let mut doc = Document::load("assets/example.pdf").unwrap();
	assert_eq!(doc.version, "1.5");
	doc.save("test_2_load.pdf").unwrap();
}
<|endoftext|>