<commit_before>use std::result::Result;

use silkthread_base::*;
use silkthread_base::basic::*;

use super::super::lex::lex_tool::*;
use super::script::*;

///
/// Represents a parse error
///
#[derive(Serialize, Deserialize, Debug)]
pub struct ParseError {

}

///
/// Tool that parses our scripting language
///
pub struct ParseScriptTool {
}

impl ParseError {
    pub fn new() -> ParseError {
        ParseError { }
    }
}

struct ParseState<'a> {
    remaining: &'a [ScriptToken]
}

impl<'a> ParseState<'a> {
    ///
    /// Returns true if a token is considered syntax (gets returned from lookahead)
    ///
    fn is_syntax(token: &ScriptToken) -> bool {
        match token.token {
            ScriptLexerToken::Whitespace    |
            ScriptLexerToken::Comment       => false,

            _                               => true
        }
    }

    ///
    /// Looks ahead to the next syntactically relevant lexer match (and returns the tokens after it)
    ///
    fn lookahead(&self) -> Option<(&'a ScriptToken, &'a [ScriptToken])> {
        let mut index   = 0;
        let len         = self.remaining.len();

        loop {
            if index >= len {
                return None;
            } else if ParseState::is_syntax(&self.remaining[index]) {
                let token       = &self.remaining[index];
                let remaining   = &self.remaining[index+1..len];
                return Some((token, remaining));
            }

            index += 1;
        }
    }

    ///
    /// If the next token matches the specified token, consumes it and returns
    /// its content.
    ///
    fn lookahead_is(&self, token: ScriptLexerToken) -> bool {
        if let Some((lookahead, _remaining)) = self.lookahead() {
            if lookahead.token == token {
                // Token matches
                true
            } else {
                // Token does not match
                false
            }
        } else {
            // End of file
            if token == ScriptLexerToken::EndOfFile {
                // With lookahead_is we can look for the end of file (but we can't accept it because there's no token data associated with it)
                true
            } else {
                false
            }
        }
    }

    ///
    /// If the next token matches the specified token, consumes it and returns
    /// its content.
    ///
    fn accept(&mut self, token: ScriptLexerToken) -> Option<&'a ScriptToken> {
        if let Some((lookahead, remaining)) = self.lookahead() {
            if lookahead.token == token {
                // Token matches: remove it from the input and return it
                self.remaining = remaining;
                Some(lookahead)
            } else {
                // Next token does not match
                None
            }
        } else {
            // Reached the end of file
            None
        }
    }

    ///
    /// Parses a statement
    ///
    fn parse_statement(&mut self) -> Result<Script, ParseError> {
        if self.accept(ScriptLexerToken::Newline).is_some() {
            // Newlines are ignored
            self.parse_statement()

        } else if self.accept(ScriptLexerToken::Let).is_some() {
            // let identifier = expression
            self.parse_let()

        } else if self.accept(ScriptLexerToken::Var).is_some() {
            // var identifier = expression
            self.parse_var()

        } else if self.accept(ScriptLexerToken::Def).is_some() {
            // def fn args { statements }
            self.parse_def()

        } else if self.accept(ScriptLexerToken::If).is_some() {
            // if expression { statements }
            self.parse_if()

        } else if self.accept(ScriptLexerToken::Using).is_some() {
            // using expression { statements }
            self.parse_using()

        } else if self.accept(ScriptLexerToken::While).is_some() {
            // while expression { statements }
            self.parse_while()

        } else if self.accept(ScriptLexerToken::Loop).is_some() {
            // loop { statements }
            self.parse_loop()

        } else if self.accept(ScriptLexerToken::For).is_some() {
            // for identifier in expression { statements }
            self.parse_for()

        } else if self.lookahead_is(ScriptLexerToken::Identifier) {
            // While commands are either <Expression> or <Expression> <Expression>, we
            // force the first expression to be an identifier at the moment
            self.parse_command()

        } else {
            // Unrecognised token
            Err(ParseError::new())
        }
    }

    ///
    /// Parses a command
    ///
    /// Syntax '<expression>', '<expression> <expression>'
    ///
    fn parse_command(&mut self) -> Result<Script, ParseError> {
        // Starts with an expression specifying the command to run
        self.parse_expression().and_then(move |command_expression| {
            // Followed by arguments (or an end-of-expression marker)
            if self.accept(ScriptLexerToken::Newline).is_some()
               || self.lookahead_is(ScriptLexerToken::EndOfFile) {
                // Newline or EOF ends a command
                Ok(Script::RunCommand(command_expression, None))

            } else {
                // Anything else should be an argument expression
                self.parse_expression().and_then(move |argument_expression| {
                    Ok(Script::RunCommand(command_expression, Some(argument_expression)))

                }).and_then(move |command| {
                    // Command must be followed by a newline
                    if self.accept(ScriptLexerToken::Newline).is_some()
                       || self.lookahead_is(ScriptLexerToken::EndOfFile) {
                        Ok(command)
                    } else {
                        Err(ParseError::new())
                    }

                })

            }
        })
    }

    ///
    /// Parses an Expression
    ///
    fn parse_expression(&mut self) -> Result<Expression, ParseError> {
        let left_expr = if self.lookahead_is(ScriptLexerToken::symbol("[")) {
            self.parse_array_expression()

        } else {
            // Simple expression
            self.parse_simple_expression()

        };

        // Look for a RHS
        left_expr.and_then(|left_expr| self.parse_expression_rhs(left_expr))
    }

    ///
    /// Parses the RHS of an expression (if there is one)
    ///
    fn parse_expression_rhs(&mut self, left_expr: Expression) -> Result<Expression, ParseError> {
        if self.accept(ScriptLexerToken::symbol(".")).is_some() {
            // 'a.b' field access
            let right_expr = self.parse_expression();

            right_expr.map(|right_expr| Expression::FieldAccess(Box::new((left_expr, right_expr))))

        } else if self.accept(ScriptLexerToken::symbol("[")).is_some() {
            // a[b] indexing
            let right_expr = self.parse_expression();

            right_expr.and_then(|right_expr| {
                if self.accept(ScriptLexerToken::symbol("]")).is_some() {
                    // Got all of a[b]
                    Ok(Expression::Index(Box::new((left_expr, right_expr))))
                } else {
                    // Missing ']'
                    Err(ParseError::new())
                }
            })

        } else {
            Ok(left_expr)

        }
    }

    ///
    /// Skips any newlines
    ///
    fn skip_newlines(&mut self) {
        while self.accept(ScriptLexerToken::Newline).is_some() { }
    }

    ///
    /// Parses an array expression
    ///
    fn parse_array_expression(&mut self) -> Result<Expression, ParseError> {
        // Opening '['
        if self.accept(ScriptLexerToken::symbol("[")).is_none() {
            // Not an array
            return Err(ParseError::new());
        }

        let mut components = vec![];

        // Array goes until the final ']'
        while self.accept(ScriptLexerToken::symbol("]")).is_none() {
            // Read the next component
            let next_component = self.parse_expression();

            // Add to the components
            match next_component {
                Err(failure)    => return Err(failure),
                Ok(component)   => components.push(component)
            };

            // Components separated by commas. Newlines are ignored
            self.skip_newlines();

            // Followed by a comma or the closing ']'
            if self.accept(ScriptLexerToken::symbol(",")).is_none()
                && !self.lookahead_is(ScriptLexerToken::symbol("]")) {
                // Expected ','
                return Err(ParseError::new());
            }

            // Newlines allowed after the ','
            self.skip_newlines();
        }

        Ok(Expression::Array(components))
    }

    ///
    /// Parses a simple expression
    ///
    fn parse_simple_expression(&mut self) -> Result<Expression, ParseError> {
        if self.accept(ScriptLexerToken::Newline).is_some() {
            // Ignore newlines within an expression
            self.parse_expression()

        } else if let Some(identifier) = self.accept(ScriptLexerToken::Identifier) {
            // Simple expression
            Ok(Expression::Identifier(identifier.clone()))

        } else if let Some(number) = self.accept(ScriptLexerToken::Number) {
            // Simple expression
            Ok(Expression::Number(number.clone()))

        } else if let Some(number) = self.accept(ScriptLexerToken::HexNumber) {
            // Hex numbers work like normal numbers
            Ok(Expression::Number(number.clone()))

        } else if let Some(string) = self.accept(ScriptLexerToken::String) {
            // Simple expression
            Ok(Expression::String(string.clone()))

        } else {
            // Syntax error
            Err(ParseError::new())

        }
    }

    fn parse_let(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_var(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_def(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_if(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_using(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_while(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_loop(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_for(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }
}

impl ParseScriptTool {
    ///
    /// Creates a new tool from the parse script tool
    ///
    pub fn new_tool() -> Box<Tool> {
        Box::new(make_tool(move |script: Vec<LexerMatch>| -> Result<Vec<Script>, ParseError> {
            ParseScriptTool::parse(&script)
        }))
    }

    ///
    /// Tries to parse a script from the output of the lexer
    ///
    pub fn parse(input: &[LexerMatch]) -> Result<Vec<Script>, ParseError> {
        // Convert to script tokens
        let as_script_token: Vec<ScriptToken> = input
            .iter()
            .map(|token| ScriptToken::from_lexer_match(token))
            .collect();

        // Parse until we reach the end of the file
        let mut parser = ParseState { remaining: &as_script_token };
        let mut result = vec![];

        while !parser.lookahead_is(ScriptLexerToken::EndOfFile) {
            let next_statement = parser.parse_statement();

            match next_statement {
                // Fail out if we get a parse failure
                Err(failure)        => return Err(failure),

                // Build out the result otherwise
                Ok(next_statement)  => result.push(next_statement)
            }

            // Swallow any trailing newlines
            parser.skip_newlines();
        }

        Ok(result)
    }
}

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

    ///
    /// Performs lexing
    ///
    fn lex(text: &str) -> Vec<LexerMatch> {
        let lexer = create_lex_script_tool();

        lexer.lex(text)
    }

    fn parse(text: &str) -> Result<Vec<Script>, ParseError> {
        let lexed = lex(text);
        ParseScriptTool::parse(&lexed)
    }

    #[test]
    fn can_parse_command_statement() {
        let statement   = "some-command";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), None) => true, _ => false});
    }

    #[test]
    fn can_parse_field_access() {
        let statement   = "some-command.some-field";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::FieldAccess(_), None) => true, _ => false});
    }

    #[test]
    fn can_parse_array_indexing() {
        let statement   = "some-command[0]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Index(_), None) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_parameter() {
        let statement   = "some-command 1";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Number(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_empty_array_parameter() {
        let statement   = "some-command [ ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_single_array_parameter() {
        let statement   = "some-command [ 1 ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_multi_array_parameter() {
        let statement   = "some-command [ 1, 2, 3 ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_complex_array_parameter() {
        let statement   = "some-command [ some-command.access, indexed[2], 3 ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn cannot_try_to_put_multiple_statements_on_one_line() {
        let statement   = "some-command 1 some-other-command";
        let parsed      = parse(statement);

        assert!(parsed.is_err());
    }

    #[test]
    fn can_parse_multiple_lines() {
        let statement   = "some-command\nsome-other-command";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());
        assert!(parsed.unwrap().len() == 2);
    }
}
<commit_msg>Add a basic message to the ParseError structure<commit_after>use std::result::Result;

use silkthread_base::*;
use silkthread_base::basic::*;

use super::super::lex::lex_tool::*;
use super::script::*;

///
/// Represents a parse error
///
#[derive(Serialize, Deserialize, Debug)]
pub struct ParseError {
    pub message: String
}

///
/// Tool that parses our scripting language
///
pub struct ParseScriptTool {
}

impl ParseError {
    fn new<'a>(state: &ParseState<'a>, message: &str) -> ParseError {
        println!("{}", message);
        println!("{:?}", state.remaining);

        ParseError { message: String::from(message) }
    }
}

struct ParseState<'a> {
    remaining: &'a [ScriptToken]
}

impl<'a> ParseState<'a> {
    ///
    /// Returns true if a token is considered syntax (gets returned from lookahead)
    ///
    fn is_syntax(token: &ScriptToken) -> bool {
        match token.token {
            ScriptLexerToken::Whitespace    |
            ScriptLexerToken::Comment       => false,

            _                               => true
        }
    }

    ///
    /// Looks ahead to the next syntactically relevant lexer match (and returns the tokens after it)
    ///
    fn lookahead(&self) -> Option<(&'a ScriptToken, &'a [ScriptToken])> {
        let mut index   = 0;
        let len         = self.remaining.len();

        loop {
            if index >= len {
                return None;
            } else if ParseState::is_syntax(&self.remaining[index]) {
                let token       = &self.remaining[index];
                let remaining   = &self.remaining[index+1..len];
                return Some((token, remaining));
            }

            index += 1;
        }
    }

    ///
    /// If the next token matches the specified token, consumes it and returns
    /// its content.
    ///
    fn lookahead_is(&self, token: ScriptLexerToken) -> bool {
        if let Some((lookahead, _remaining)) = self.lookahead() {
            if lookahead.token == token {
                // Token matches
                true
            } else {
                // Token does not match
                false
            }
        } else {
            // End of file
            if token == ScriptLexerToken::EndOfFile {
                // With lookahead_is we can look for the end of file (but we can't accept it because there's no token data associated with it)
                true
            } else {
                false
            }
        }
    }

    ///
    /// If the next token matches the specified token, consumes it and returns
    /// its content.
    ///
    fn accept(&mut self, token: ScriptLexerToken) -> Option<&'a ScriptToken> {
        if let Some((lookahead, remaining)) = self.lookahead() {
            if lookahead.token == token {
                // Token matches: remove it from the input and return it
                self.remaining = remaining;
                Some(lookahead)
            } else {
                // Next token does not match
                None
            }
        } else {
            // Reached the end of file
            None
        }
    }

    ///
    /// Parses a statement
    ///
    fn parse_statement(&mut self) -> Result<Script, ParseError> {
        if self.accept(ScriptLexerToken::Newline).is_some() {
            // Newlines are ignored
            self.parse_statement()

        } else if self.accept(ScriptLexerToken::Let).is_some() {
            // let identifier = expression
            self.parse_let()

        } else if self.accept(ScriptLexerToken::Var).is_some() {
            // var identifier = expression
            self.parse_var()

        } else if self.accept(ScriptLexerToken::Def).is_some() {
            // def fn args { statements }
            self.parse_def()

        } else if self.accept(ScriptLexerToken::If).is_some() {
            // if expression { statements }
            self.parse_if()

        } else if self.accept(ScriptLexerToken::Using).is_some() {
            // using expression { statements }
            self.parse_using()

        } else if self.accept(ScriptLexerToken::While).is_some() {
            // while expression { statements }
            self.parse_while()

        } else if self.accept(ScriptLexerToken::Loop).is_some() {
            // loop { statements }
            self.parse_loop()

        } else if self.accept(ScriptLexerToken::For).is_some() {
            // for identifier in expression { statements }
            self.parse_for()

        } else if self.lookahead_is(ScriptLexerToken::Identifier) {
            // While commands are either <Expression> or <Expression> <Expression>, we
            // force the first expression to be an identifier at the moment
            self.parse_command()

        } else {
            // Unrecognised token
            Err(ParseError::new(self, "Token cannot begin a statement"))
        }
    }

    ///
    /// Parses a command
    ///
    /// Syntax '<expression>', '<expression> <expression>'
    ///
    fn parse_command(&mut self) -> Result<Script, ParseError> {
        // Starts with an expression specifying the command to run
        self.parse_expression().and_then(move |command_expression| {
            // Followed by arguments (or an end-of-expression marker)
            if self.accept(ScriptLexerToken::Newline).is_some()
               || self.lookahead_is(ScriptLexerToken::EndOfFile) {
                // Newline or EOF ends a command
                Ok(Script::RunCommand(command_expression, None))

            } else {
                // Anything else should be an argument expression
                self.parse_expression().and_then(move |argument_expression| {
                    Ok(Script::RunCommand(command_expression, Some(argument_expression)))

                }).and_then(move |command| {
                    // Command must be followed by a newline
                    if self.accept(ScriptLexerToken::Newline).is_some()
                       || self.lookahead_is(ScriptLexerToken::EndOfFile) {
                        Ok(command)
                    } else {
                        Err(ParseError::new(self, "Found extra tokens after the end of a command"))
                    }

                })

            }
        })
    }

    ///
    /// Parses an Expression
    ///
    fn parse_expression(&mut self) -> Result<Expression, ParseError> {
        let left_expr = if self.lookahead_is(ScriptLexerToken::symbol("[")) {
            self.parse_array_expression()

        } else {
            // Simple expression
            self.parse_simple_expression()

        };

        // Look for a RHS
        left_expr.and_then(|left_expr| self.parse_expression_rhs(left_expr))
    }

    ///
    /// Parses the RHS of an expression (if there is one)
    ///
    fn parse_expression_rhs(&mut self, left_expr: Expression) -> Result<Expression, ParseError> {
        if self.accept(ScriptLexerToken::symbol(".")).is_some() {
            // 'a.b' field access
            let right_expr = self.parse_expression();

            right_expr.map(|right_expr| Expression::FieldAccess(Box::new((left_expr, right_expr))))

        } else if self.accept(ScriptLexerToken::symbol("[")).is_some() {
            // a[b] indexing
            let right_expr = self.parse_expression();

            right_expr.and_then(|right_expr| {
                if self.accept(ScriptLexerToken::symbol("]")).is_some() {
                    // Got all of a[b]
                    Ok(Expression::Index(Box::new((left_expr, right_expr))))
                } else {
                    // Missing ']'
                    Err(ParseError::new(self, "Missing ']'"))
                }
            })

        } else {
            Ok(left_expr)

        }
    }

    ///
    /// Skips any newlines
    ///
    fn skip_newlines(&mut self) {
        while self.accept(ScriptLexerToken::Newline).is_some() { }
    }

    ///
    /// Parses an array expression
    ///
    fn parse_array_expression(&mut self) -> Result<Expression, ParseError> {
        // Opening '['
        if self.accept(ScriptLexerToken::symbol("[")).is_none() {
            // Not an array
            return Err(ParseError::new(self, "Not an array"));
        }

        let mut components = vec![];

        // Array goes until the final ']'
        while self.accept(ScriptLexerToken::symbol("]")).is_none() {
            // Read the next component
            let next_component = self.parse_expression();

            // Add to the components
            match next_component {
                Err(failure)    => return Err(failure),
                Ok(component)   => components.push(component)
            };

            // Components separated by commas. Newlines are ignored
            self.skip_newlines();

            // Followed by a comma or the closing ']'
            if self.accept(ScriptLexerToken::symbol(",")).is_none()
                && !self.lookahead_is(ScriptLexerToken::symbol("]")) {
                // Expected ','
                return Err(ParseError::new(self, "Expected ',' or ']'"));
            }

            // Newlines allowed after the ','
            self.skip_newlines();
        }

        Ok(Expression::Array(components))
    }

    ///
    /// Parses a simple expression
    ///
    fn parse_simple_expression(&mut self) -> Result<Expression, ParseError> {
        if self.accept(ScriptLexerToken::Newline).is_some() {
            // Ignore newlines within an expression
            self.parse_expression()

        } else if let Some(identifier) = self.accept(ScriptLexerToken::Identifier) {
            // Simple expression
            Ok(Expression::Identifier(identifier.clone()))

        } else if let Some(number) = self.accept(ScriptLexerToken::Number) {
            // Simple expression
            Ok(Expression::Number(number.clone()))

        } else if let Some(number) = self.accept(ScriptLexerToken::HexNumber) {
            // Hex numbers work like normal numbers
            Ok(Expression::Number(number.clone()))

        } else if let Some(string) = self.accept(ScriptLexerToken::String) {
            // Simple expression
            Ok(Expression::String(string.clone()))

        } else {
            // Syntax error
            Err(ParseError::new(self, "Syntax error (was expecting an expression)"))

        }
    }

    fn parse_let(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_var(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_def(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_if(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_using(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_while(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_loop(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }

    fn parse_for(&mut self) -> Result<Script, ParseError> {
        unimplemented!()
    }
}

impl ParseScriptTool {
    ///
    /// Creates a new tool from the parse script tool
    ///
    pub fn new_tool() -> Box<Tool> {
        Box::new(make_tool(move |script: Vec<LexerMatch>| -> Result<Vec<Script>, ParseError> {
            ParseScriptTool::parse(&script)
        }))
    }

    ///
    /// Tries to parse a script from the output of the lexer
    ///
    pub fn parse(input: &[LexerMatch]) -> Result<Vec<Script>, ParseError> {
        // Convert to script tokens
        let as_script_token: Vec<ScriptToken> = input
            .iter()
            .map(|token| ScriptToken::from_lexer_match(token))
            .collect();

        // Parse until we reach the end of the file
        let mut parser = ParseState { remaining: &as_script_token };
        let mut result = vec![];

        while !parser.lookahead_is(ScriptLexerToken::EndOfFile) {
            let next_statement = parser.parse_statement();

            match next_statement {
                // Fail out if we get a parse failure
                Err(failure)        => return Err(failure),

                // Build out the result otherwise
                Ok(next_statement)  => result.push(next_statement)
            }

            // Swallow any trailing newlines
            parser.skip_newlines();
        }

        Ok(result)
    }
}

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

    ///
    /// Performs lexing
    ///
    fn lex(text: &str) -> Vec<LexerMatch> {
        let lexer = create_lex_script_tool();

        lexer.lex(text)
    }

    fn parse(text: &str) -> Result<Vec<Script>, ParseError> {
        let lexed = lex(text);
        ParseScriptTool::parse(&lexed)
    }

    #[test]
    fn can_parse_command_statement() {
        let statement   = "some-command";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), None) => true, _ => false});
    }

    #[test]
    fn can_parse_field_access() {
        let statement   = "some-command.some-field";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::FieldAccess(_), None) => true, _ => false});
    }

    #[test]
    fn can_parse_array_indexing() {
        let statement   = "some-command[0]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Index(_), None) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_parameter() {
        let statement   = "some-command 1";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Number(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_empty_array_parameter() {
        let statement   = "some-command [ ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_single_array_parameter() {
        let statement   = "some-command [ 1 ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_multi_array_parameter() {
        let statement   = "some-command [ 1, 2, 3 ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn can_parse_command_statement_with_complex_array_parameter() {
        let statement   = "some-command [ some-command.access, indexed[2], 3 ]";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());

        let result = parsed.unwrap();
        assert!(result.len() == 1);

        let ref cmd = result[0];
        assert!(match cmd { &Script::RunCommand(Expression::Identifier(_), Some(Expression::Array(_))) => true, _ => false});
    }

    #[test]
    fn cannot_try_to_put_multiple_statements_on_one_line() {
        let statement   = "some-command 1 some-other-command";
        let parsed      = parse(statement);

        assert!(parsed.is_err());
    }

    #[test]
    fn can_parse_multiple_lines() {
        let statement   = "some-command\nsome-other-command";
        let parsed      = parse(statement);

        assert!(parsed.is_ok());
        assert!(parsed.unwrap().len() == 2);
    }
}
<|endoftext|>


<commit_before>use crate::future::Future;
use crate::runtime::task::harness::Harness;
use crate::runtime::task::{Header, Schedule};

use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops;
use std::ptr::NonNull;
use std::task::{RawWaker, RawWakerVTable, Waker};

pub(super) struct WakerRef<'a, S: 'static> {
    waker: ManuallyDrop<Waker>,
    _p: PhantomData<(&'a Header, S)>,
}

/// Returns a `WakerRef` which avoids having to pre-emptively increase the
/// refcount if there is no need to do so.
pub(super) fn waker_ref<T, S>(header: &Header) -> WakerRef<'_, S>
where
    T: Future,
    S: Schedule,
{
    // `Waker::will_wake` uses the VTABLE pointer as part of the check. This
    // means that `will_wake` will always return false when using the current
    // task's waker. (discussion at rust-lang/rust#66281).
    //
    // To fix this, we use a single vtable. Since we pass in a reference at this
    // point and not an *owned* waker, we must ensure that `drop` is never
    // called on this waker instance. This is done by wrapping it with
    // `ManuallyDrop` and then never calling drop.
    let waker = unsafe { ManuallyDrop::new(Waker::from_raw(raw_waker::<T, S>(header))) };

    WakerRef {
        waker,
        _p: PhantomData,
    }
}

impl<S> ops::Deref for WakerRef<'_, S> {
    type Target = Waker;

    fn deref(&self) -> &Waker {
        &self.waker
    }
}

cfg_trace! {
    macro_rules! trace {
        ($harness:expr, $op:expr) => {
            if let Some(id) = $harness.id() {
                tracing::trace!(
                    target: "tokio::task::waker",
                    op = %$op,
                    task.id = id.into_u64(),
                );
            }
        }
    }
}

cfg_not_trace! {
    macro_rules! trace {
        ($harness:expr, $op:expr) => {
            // noop
            let _ = &$harness;
        }
    }
}

unsafe fn clone_waker<T, S>(ptr: *const ()) -> RawWaker
where
    T: Future,
    S: Schedule,
{
    let header = ptr as *const Header;
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.clone");
    (*header).state.ref_inc();
    raw_waker::<T, S>(header)
}

unsafe fn drop_waker<T, S>(ptr: *const ())
where
    T: Future,
    S: Schedule,
{
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.drop");
    harness.drop_reference();
}

unsafe fn wake_by_val<T, S>(ptr: *const ())
where
    T: Future,
    S: Schedule,
{
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.wake");
    harness.wake_by_val();
}

// Wake without consuming the waker
unsafe fn wake_by_ref<T, S>(ptr: *const ())
where
    T: Future,
    S: Schedule,
{
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.wake_by_ref");
    harness.wake_by_ref();
}

fn raw_waker<T, S>(header: *const Header) -> RawWaker
where
    T: Future,
    S: Schedule,
{
    let ptr = header as *const ();
    let vtable = &RawWakerVTable::new(
        clone_waker::<T, S>,
        wake_by_val::<T, S>,
        wake_by_ref::<T, S>,
        drop_waker::<T, S>,
    );
    RawWaker::new(ptr, vtable)
}
<commit_msg>tracing: emit waker op as str instead as Debug (#3853)<commit_after>use crate::future::Future;
use crate::runtime::task::harness::Harness;
use crate::runtime::task::{Header, Schedule};

use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops;
use std::ptr::NonNull;
use std::task::{RawWaker, RawWakerVTable, Waker};

pub(super) struct WakerRef<'a, S: 'static> {
    waker: ManuallyDrop<Waker>,
    _p: PhantomData<(&'a Header, S)>,
}

/// Returns a `WakerRef` which avoids having to pre-emptively increase the
/// refcount if there is no need to do so.
pub(super) fn waker_ref<T, S>(header: &Header) -> WakerRef<'_, S>
where
    T: Future,
    S: Schedule,
{
    // `Waker::will_wake` uses the VTABLE pointer as part of the check. This
    // means that `will_wake` will always return false when using the current
    // task's waker. (discussion at rust-lang/rust#66281).
    //
    // To fix this, we use a single vtable. Since we pass in a reference at this
    // point and not an *owned* waker, we must ensure that `drop` is never
    // called on this waker instance. This is done by wrapping it with
    // `ManuallyDrop` and then never calling drop.
    let waker = unsafe { ManuallyDrop::new(Waker::from_raw(raw_waker::<T, S>(header))) };

    WakerRef {
        waker,
        _p: PhantomData,
    }
}

impl<S> ops::Deref for WakerRef<'_, S> {
    type Target = Waker;

    fn deref(&self) -> &Waker {
        &self.waker
    }
}

cfg_trace! {
    macro_rules! trace {
        ($harness:expr, $op:expr) => {
            if let Some(id) = $harness.id() {
                tracing::trace!(
                    target: "tokio::task::waker",
                    op = $op,
                    task.id = id.into_u64(),
                );
            }
        }
    }
}

cfg_not_trace! {
    macro_rules! trace {
        ($harness:expr, $op:expr) => {
            // noop
            let _ = &$harness;
        }
    }
}

unsafe fn clone_waker<T, S>(ptr: *const ()) -> RawWaker
where
    T: Future,
    S: Schedule,
{
    let header = ptr as *const Header;
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.clone");
    (*header).state.ref_inc();
    raw_waker::<T, S>(header)
}

unsafe fn drop_waker<T, S>(ptr: *const ())
where
    T: Future,
    S: Schedule,
{
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.drop");
    harness.drop_reference();
}

unsafe fn wake_by_val<T, S>(ptr: *const ())
where
    T: Future,
    S: Schedule,
{
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.wake");
    harness.wake_by_val();
}

// Wake without consuming the waker
unsafe fn wake_by_ref<T, S>(ptr: *const ())
where
    T: Future,
    S: Schedule,
{
    let ptr = NonNull::new_unchecked(ptr as *mut Header);
    let harness = Harness::<T, S>::from_raw(ptr);
    trace!(harness, "waker.wake_by_ref");
    harness.wake_by_ref();
}

fn raw_waker<T, S>(header: *const Header) -> RawWaker
where
    T: Future,
    S: Schedule,
{
    let ptr = header as *const ();
    let vtable = &RawWakerVTable::new(
        clone_waker::<T, S>,
        wake_by_val::<T, S>,
        wake_by_ref::<T, S>,
        drop_waker::<T, S>,
    );
    RawWaker::new(ptr, vtable)
}
<|endoftext|>


<commit_before>extern crate array_ext;
extern crate custom_gl as gl;
extern crate custom_egl as egl;

mod types;
#[macro_use]
mod typeinfo;
mod shader;
mod eglw;

use std::mem;
use std::ffi::CStr;
use std::ptr;
use std::cell::Cell;
use gl::types::*;
use typeinfo::TypeInfo;
use array_ext::Array;
use shader::{Shader, Program};

pub use egl::NativeDisplayType;
pub use egl::NativeWindowType;
pub use eglw::Surface;

#[derive(Clone, Copy)]
#[repr(C)]
struct Vertex
{
    pos: [i16; 2],
    col: [f32; 4],
    texc: [f32; 2],
}

impl_typeinfo!(Vertex, pos, col, texc);

#[derive(PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
enum PrimType
{
    Points = gl::POINTS,
    Lines = gl::LINES,
    Triangles = gl::TRIANGLES,
}

pub struct DrawEngine
{
    egl_disp: eglw::Display,
    prog: Program,
    vbo_vert: GLuint,
    vbo_idx: GLuint,
    default_tex: GLuint,
    cur_ty: Cell<PrimType>,
    vert_off: Cell<usize>,
    idx_off: Cell<usize>,
    vert_len: Cell<usize>,
    idx_len: Cell<usize>,
    cur_tex: Cell<GLuint>,
    pub max_verts: usize,
    pub max_idxs: usize,
}

impl DrawEngine
{
    pub fn new(xdisp: NativeDisplayType) -> Result<Self, &'static str>
    {
        let egl_disp = try!(eglw::Display::new(xdisp));

        unsafe
        {
            let vendor = CStr::from_ptr(gl::GetString(gl::VENDOR) as *const _);
            let renderer = CStr::from_ptr(gl::GetString(gl::RENDERER) as *const _);
            let version = CStr::from_ptr(gl::GetString(gl::VERSION) as *const _);
            let exts = CStr::from_ptr(gl::GetString(gl::EXTENSIONS) as *const _);
            println!("GL vendor: {:?}\nGL renderer: {:?}\nGL version: {:?}\nGL extensions: {:?}",
                vendor, renderer, version, exts);
        }

        unsafe
        {
            let prog = Program::new(&[
                Shader::new(gl::VERTEX_SHADER, &[include_str!("test.vert.glsl")]).unwrap_or_else(|e| panic!("vert: {}", e)),
                Shader::new(gl::FRAGMENT_SHADER, &[include_str!("test.frag.glsl")]).unwrap_or_else(|e| panic!("frag: {}", e)),
            ]).unwrap_or_else(|e| panic!("link: {}", e));
            prog.set_active();

            // vertex buffer
            let mut vbo_vert = 0;
            gl::GenBuffers(1, &mut vbo_vert);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo_vert);
            let size = mem::size_of::<Vertex>();
            Vertex::visit_fields(|name, offset, count, ty| {
                let num = prog.get_attrib(name).unwrap();
                gl::VertexAttribPointer(num, count as GLint, ty as GLenum, gl::FALSE, size as GLsizei, offset as *const _);
                gl::EnableVertexAttribArray(num);
            });

            // index buffer
            let mut vbo_idx = 0;
            gl::GenBuffers(1, &mut vbo_idx);
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, vbo_idx);

            // 1x1 white texture
            let mut tex = 0;
            gl::GenTextures(1, &mut tex);
            gl::BindTexture(gl::TEXTURE_2D, tex);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexImage2D(gl::TEXTURE_2D, 0, gl::LUMINANCE as GLint, 1, 1, 0, gl::LUMINANCE, gl::UNSIGNED_BYTE, [255u8].as_ptr() as *const _);

            let eng = DrawEngine{
                egl_disp: egl_disp,
                prog: prog,
                vbo_vert: vbo_vert,
                vbo_idx: vbo_idx,
                default_tex: tex,
                cur_ty: Cell::new(PrimType::Triangles),
                vert_off: Cell::new(0),
                idx_off: Cell::new(0),
                vert_len: Cell::new(0),
                idx_len: Cell::new(0),
                cur_tex: Cell::new(0),
                // allocate 1mb
                max_verts: 32768,
                max_idxs: 65536,
            };

            eng.alloc_vert();
            eng.alloc_idx();

            Ok(eng)
        }
    }

    pub fn create_window_surface(&self, win: NativeWindowType) -> Result<Surface, &'static str>
    {
        self.egl_disp.create_window_surface(win)
    }

    pub fn begin_draw<'a>(&'a self, surface: &'a Surface, (width, height): (u32, u32)) -> DrawContext
    {
        if !surface.is_current()
        {
            let (w, h) = (width as f32, height as f32);
            let tf = [
                2.0/w,    0.0, 0.0, 0.0,
                  0.0, -2.0/h, 0.0, 0.0,
                  0.0,    0.0, 1.0, 0.0,
                 -1.0,    1.0, 0.0, 1.0,
            ];
            surface.make_current();
            let loc_tf = self.prog.get_uniform("tf").unwrap() as GLint;
            unsafe
            {
                gl::Viewport(0, 0, width as GLsizei, height as GLsizei);
                gl::UniformMatrix4fv(loc_tf, 1, gl::FALSE, tf.as_ptr());
            }
        }

        DrawContext::new(self, surface)
    }

    fn alloc_vert(&self)
    {
        self.vert_off.set(0);
        let size = self.max_verts * mem::size_of::<Vertex>();
        unsafe { gl::BufferData(gl::ARRAY_BUFFER, size as GLsizeiptr, ptr::null(), gl::STREAM_DRAW) };
    }

    fn alloc_idx(&self)
    {
        self.idx_off.set(0);
        let size = self.max_idxs * mem::size_of::<u16>();
        unsafe { gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, size as GLsizeiptr, ptr::null(), gl::STREAM_DRAW) };
    }

    fn clear(&self, r: f32, g: f32, b: f32, a: f32)
    {
        self.vert_len.set(0);
        self.idx_len.set(0);
        unsafe
        {
            gl::ClearColor(r, g, b, a);
            gl::Clear(gl::COLOR_BUFFER_BIT);
        }
    }

    fn push_elems<T: Array<u16>>(&self, ty: PrimType, tex: Option<GLuint>, verts: &[Vertex], idxs: T)
    {
        assert!(verts.len() <= self.max_verts && idxs.len() <= self.max_idxs);
        let tex = tex.unwrap_or(self.default_tex);

        let vert_start = self.vert_off.get() + self.vert_len.get();
        let idx_start = self.idx_off.get() + self.idx_len.get();

        let oom_vert = vert_start + verts.len() > self.max_verts;
        let oom_idx = idx_start + idxs.len() > self.max_idxs;
        let new_tex = self.cur_tex.get() != tex;

        if oom_vert || oom_idx || new_tex || self.cur_ty.get() != ty
        {
            self.commit();
            self.cur_ty.set(ty);

            if oom_vert { self.alloc_vert(); }
            if oom_idx { self.alloc_idx(); }

            if new_tex
            {
                self.cur_tex.set(tex);
                unsafe { gl::BindTexture(gl::TEXTURE_2D, tex) };
            }
        }

        let vert_size = vert_start * mem::size_of::<Vertex>();
        let idx_size = idx_start * mem::size_of::<u16>();
        let idxs = idxs.map(|idx| idx + vert_start as u16);
        unsafe
        {
            gl::BufferSubData(gl::ARRAY_BUFFER, vert_size as GLsizeiptr, mem::size_of_val(verts) as GLintptr, verts.as_ptr() as *const _);
            gl::BufferSubData(gl::ELEMENT_ARRAY_BUFFER, idx_size as GLsizeiptr, mem::size_of_val(&idxs) as GLintptr, idxs.as_ptr() as *const _);
        }
        self.vert_len.set(self.vert_len.get() + verts.len());
        self.idx_len.set(self.idx_len.get() + idxs.len());
    }

    fn commit(&self)
    {
        if self.vert_len.get() == 0 { return }

        let offset = self.idx_off.get() * mem::size_of::<i16>();
        unsafe { gl::DrawElements(self.cur_ty.get() as GLenum, self.idx_len.get() as GLsizei, gl::UNSIGNED_SHORT, offset as *const _) };

        self.vert_off.set(self.vert_off.get() + self.vert_len.get());
        self.idx_off.set(self.idx_off.get() + self.idx_len.get());

        self.vert_len.set(0);
        self.idx_len.set(0);
    }
}

impl Drop for DrawEngine
{
    fn drop(&mut self)
    {
        unsafe
        {
            gl::DeleteBuffers(2, [self.vbo_vert, self.vbo_idx].as_ptr());
            gl::DeleteTextures(1, &self.default_tex);
        }
    }
}

pub struct DrawContext<'a>
{
    eng: &'a DrawEngine,
    surface: &'a Surface<'a>,
}

impl<'a> DrawContext<'a>
{
    fn new(eng: &'a DrawEngine, surface: &'a Surface) -> Self
    {
        DrawContext{
            eng: eng,
            surface: surface,
        }
    }

    pub fn clear(&self, color: [f32; 4])
    {
        self.eng.clear(color[0], color[1], color[2], color[3]);
    }

    pub fn draw_point(&self, pos: [i16; 2], color: [f32; 4])
    {
        self.eng.push_elems(PrimType::Points, None,
            &[Vertex{ pos: pos, col: color, texc: [0.0, 0.0] }], [0]);
    }

    pub fn draw_line(&self, p0: [i16; 2], p1: [i16; 2], color: [f32; 4])
    {
        self.eng.push_elems(PrimType::Lines, None, &[
            Vertex{ pos: p0, col: color, texc: [0.0, 0.0] },
            Vertex{ pos: p1, col: color, texc: [0.0, 0.0] },
        ], [0, 1]);
    }

    pub fn draw_triangle(&self, p0: [i16; 2], p1: [i16; 2], p2: [i16; 2], color: [f32; 4])
    {
        self.eng.push_elems(PrimType::Triangles, None, &[
            Vertex{ pos: p0, col: color, texc: [0.0, 0.0] },
            Vertex{ pos: p1, col: color, texc: [0.0, 0.0] },
            Vertex{ pos: p2, col: color, texc: [0.0, 0.0] },
        ], [0, 1, 2]);
    }

    pub fn draw_rect(&self, pos: [i16; 2], width: u16, height: u16, color: [f32; 4])
    {
        let (x, y) = (pos[0], pos[1]);
        let xw = x + width as i16;
        let yh = y + height as i16;

        self.eng.push_elems(PrimType::Triangles, None, &[
            Vertex{ pos: pos,      col: color, texc: [0.0, 0.0] },
            Vertex{ pos: [xw, y],  col: color, texc: [0.0, 0.0] },
            Vertex{ pos: [xw, yh], col: color, texc: [0.0, 0.0] },
            Vertex{ pos: [ x, yh], col: color, texc: [0.0, 0.0] },
        ], [
            0, 1, 2,
            2, 3, 0,
        ]);
    }
}

impl<'a> Drop for DrawContext<'a>
{
    fn drop(&mut self)
    {
        self.eng.commit();
        self.surface.swap_buffers();
    }
}
<commit_msg>use glDrawArrays for points<commit_after>extern crate array_ext;
extern crate custom_gl as gl;
extern crate custom_egl as egl;

mod types;
#[macro_use]
mod typeinfo;
mod shader;
mod eglw;

use std::mem;
use std::ffi::CStr;
use std::ptr;
use std::cell::Cell;
use gl::types::*;
use typeinfo::TypeInfo;
use array_ext::Array;
use shader::{Shader, Program};

pub use egl::NativeDisplayType;
pub use egl::NativeWindowType;
pub use eglw::Surface;

#[derive(Clone, Copy)]
#[repr(C)]
struct Vertex
{
    pos: [i16; 2],
    col: [f32; 4],
    texc: [f32; 2],
}

impl_typeinfo!(Vertex, pos, col, texc);

#[derive(PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
enum PrimType
{
    Points = gl::POINTS,
    Lines = gl::LINES,
    Triangles = gl::TRIANGLES,
}

pub struct DrawEngine
{
    egl_disp: eglw::Display,
    prog: Program,
    vbo_vert: GLuint,
    vbo_idx: GLuint,
    default_tex: GLuint,
    cur_ty: Cell<PrimType>,
    vert_off: Cell<usize>,
    idx_off: Cell<usize>,
    vert_len: Cell<usize>,
    idx_len: Cell<usize>,
    cur_tex: Cell<GLuint>,
    pub max_verts: usize,
    pub max_idxs: usize,
}

impl DrawEngine
{
    pub fn new(xdisp: NativeDisplayType) -> Result<Self, &'static str>
    {
        let egl_disp = try!(eglw::Display::new(xdisp));

        unsafe
        {
            let vendor = CStr::from_ptr(gl::GetString(gl::VENDOR) as *const _);
            let renderer = CStr::from_ptr(gl::GetString(gl::RENDERER) as *const _);
            let version = CStr::from_ptr(gl::GetString(gl::VERSION) as *const _);
            let exts = CStr::from_ptr(gl::GetString(gl::EXTENSIONS) as *const _);
            println!("GL vendor: {:?}\nGL renderer: {:?}\nGL version: {:?}\nGL extensions: {:?}",
                vendor, renderer, version, exts);
        }

        unsafe
        {
            let prog = Program::new(&[
                Shader::new(gl::VERTEX_SHADER, &[include_str!("test.vert.glsl")]).unwrap_or_else(|e| panic!("vert: {}", e)),
                Shader::new(gl::FRAGMENT_SHADER, &[include_str!("test.frag.glsl")]).unwrap_or_else(|e| panic!("frag: {}", e)),
            ]).unwrap_or_else(|e| panic!("link: {}", e));
            prog.set_active();

            // vertex buffer
            let mut vbo_vert = 0;
            gl::GenBuffers(1, &mut vbo_vert);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo_vert);
            let size = mem::size_of::<Vertex>();
            Vertex::visit_fields(|name, offset, count, ty| {
                let num = prog.get_attrib(name).unwrap();
                gl::VertexAttribPointer(num, count as GLint, ty as GLenum, gl::FALSE, size as GLsizei, offset as *const _);
                gl::EnableVertexAttribArray(num);
            });

            // index buffer
            let mut vbo_idx = 0;
            gl::GenBuffers(1, &mut vbo_idx);
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, vbo_idx);

            // 1x1 white texture
            let mut tex = 0;
            gl::GenTextures(1, &mut tex);
            gl::BindTexture(gl::TEXTURE_2D, tex);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexImage2D(gl::TEXTURE_2D, 0, gl::LUMINANCE as GLint, 1, 1, 0, gl::LUMINANCE, gl::UNSIGNED_BYTE, [255u8].as_ptr() as *const _);

            let eng = DrawEngine{
                egl_disp: egl_disp,
                prog: prog,
                vbo_vert: vbo_vert,
                vbo_idx: vbo_idx,
                default_tex: tex,
                cur_ty: Cell::new(PrimType::Triangles),
                vert_off: Cell::new(0),
                idx_off: Cell::new(0),
                vert_len: Cell::new(0),
                idx_len: Cell::new(0),
                cur_tex: Cell::new(0),
                // allocate 1mb
                max_verts: 32768,
                max_idxs: 65536,
            };

            eng.alloc_vert();
            eng.alloc_idx();

            Ok(eng)
        }
    }

    pub fn create_window_surface(&self, win: NativeWindowType) -> Result<Surface, &'static str>
    {
        self.egl_disp.create_window_surface(win)
    }

    pub fn begin_draw<'a>(&'a self, surface: &'a Surface, (width, height): (u32, u32)) -> DrawContext
    {
        if !surface.is_current()
        {
            let (w, h) = (width as f32, height as f32);
            let tf = [
                2.0/w,    0.0, 0.0, 0.0,
                  0.0, -2.0/h, 0.0, 0.0,
                  0.0,    0.0, 1.0, 0.0,
                 -1.0,    1.0, 0.0, 1.0,
            ];
            surface.make_current();
            let loc_tf = self.prog.get_uniform("tf").unwrap() as GLint;
            unsafe
            {
                gl::Viewport(0, 0, width as GLsizei, height as GLsizei);
                gl::UniformMatrix4fv(loc_tf, 1, gl::FALSE, tf.as_ptr());
            }
        }

        DrawContext::new(self, surface)
    }

    fn alloc_vert(&self)
    {
        self.vert_off.set(0);
        let size = self.max_verts * mem::size_of::<Vertex>();
        unsafe { gl::BufferData(gl::ARRAY_BUFFER, size as GLsizeiptr, ptr::null(), gl::STREAM_DRAW) };
    }

    fn alloc_idx(&self)
    {
        self.idx_off.set(0);
        let size = self.max_idxs * mem::size_of::<u16>();
        unsafe { gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, size as GLsizeiptr, ptr::null(), gl::STREAM_DRAW) };
    }

    fn clear(&self, r: f32, g: f32, b: f32, a: f32)
    {
        self.vert_len.set(0);
        self.idx_len.set(0);
        unsafe
        {
            gl::ClearColor(r, g, b, a);
            gl::Clear(gl::COLOR_BUFFER_BIT);
        }
    }

    fn push_elems<T: Array<u16>>(&self, ty: PrimType, tex: Option<GLuint>, verts: &[Vertex], idxs: T)
    {
        assert!(verts.len() <= self.max_verts && idxs.len() <= self.max_idxs);
        let tex = tex.unwrap_or(self.default_tex);

        let vert_start = self.vert_off.get() + self.vert_len.get();
        let idx_start = self.idx_off.get() + self.idx_len.get();

        let oom_vert = vert_start + verts.len() > self.max_verts;
        let oom_idx = idx_start + idxs.len() > self.max_idxs;
        let new_tex = self.cur_tex.get() != tex;

        if oom_vert || oom_idx || new_tex || self.cur_ty.get() != ty
        {
            self.commit();
            self.cur_ty.set(ty);

            if oom_vert { self.alloc_vert(); }
            if oom_idx { self.alloc_idx(); }

            if new_tex
            {
                self.cur_tex.set(tex);
                unsafe { gl::BindTexture(gl::TEXTURE_2D, tex) };
            }
        }

        let vert_size = vert_start * mem::size_of::<Vertex>();
        unsafe { gl::BufferSubData(gl::ARRAY_BUFFER, vert_size as GLsizeiptr, mem::size_of_val(verts) as GLintptr, verts.as_ptr() as *const _) };
        self.vert_len.set(self.vert_len.get() + verts.len());

        if !idxs.is_empty()
        {
            let idxs = idxs.map(|idx| idx + vert_start as u16);
            let idx_size = idx_start * mem::size_of::<u16>();
            unsafe { gl::BufferSubData(gl::ELEMENT_ARRAY_BUFFER, idx_size as GLsizeiptr, mem::size_of_val(&idxs) as GLintptr, idxs.as_ptr() as *const _) };
            self.idx_len.set(self.idx_len.get() + idxs.len());
        }
    }

    fn commit(&self)
    {
        if self.vert_len.get() == 0 { return }

        if self.cur_ty.get() != PrimType::Points
        {
            let offset = self.idx_off.get() * mem::size_of::<i16>();
            unsafe { gl::DrawElements(self.cur_ty.get() as GLenum, self.idx_len.get() as GLsizei, gl::UNSIGNED_SHORT, offset as *const _) };
        }
        else
        {
            unsafe { gl::DrawArrays(self.cur_ty.get() as GLenum, self.vert_off.get() as GLint, self.vert_len.get() as GLsizei) };
        }

        self.vert_off.set(self.vert_off.get() + self.vert_len.get());
        self.idx_off.set(self.idx_off.get() + self.idx_len.get());

        self.vert_len.set(0);
        self.idx_len.set(0);
    }
}

impl Drop for DrawEngine
{
    fn drop(&mut self)
    {
        unsafe
        {
            gl::DeleteBuffers(2, [self.vbo_vert, self.vbo_idx].as_ptr());
            gl::DeleteTextures(1, &self.default_tex);
        }
    }
}

pub struct DrawContext<'a>
{
    eng: &'a DrawEngine,
    surface: &'a Surface<'a>,
}

impl<'a> DrawContext<'a>
{
    fn new(eng: &'a DrawEngine, surface: &'a Surface) -> Self
    {
        DrawContext{
            eng: eng,
            surface: surface,
        }
    }

    pub fn clear(&self, color: [f32; 4])
    {
        self.eng.clear(color[0], color[1], color[2], color[3]);
    }

    pub fn draw_point(&self, pos: [i16; 2], color: [f32; 4])
    {
        self.eng.push_elems(PrimType::Points, None,
            &[Vertex{ pos: pos, col: color, texc: [0.0, 0.0] }], []);
    }

    pub fn draw_line(&self, p0: [i16; 2], p1: [i16; 2], color: [f32; 4])
    {
        self.eng.push_elems(PrimType::Lines, None, &[
            Vertex{ pos: p0, col: color, texc: [0.0, 0.0] },
            Vertex{ pos: p1, col: color, texc: [0.0, 0.0] },
        ], [0, 1]);
    }

    pub fn draw_triangle(&self, p0: [i16; 2], p1: [i16; 2], p2: [i16; 2], color: [f32; 4])
    {
        self.eng.push_elems(PrimType::Triangles, None, &[
            Vertex{ pos: p0, col: color, texc: [0.0, 0.0] },
            Vertex{ pos: p1, col: color, texc: [0.0, 0.0] },
            Vertex{ pos: p2, col: color, texc: [0.0, 0.0] },
        ], [0, 1, 2]);
    }

    pub fn draw_rect(&self, pos: [i16; 2], width: u16, height: u16, color: [f32; 4])
    {
        let (x, y) = (pos[0], pos[1]);
        let xw = x + width as i16;
        let yh = y + height as i16;

        self.eng.push_elems(PrimType::Triangles, None, &[
            Vertex{ pos: pos,      col: color, texc: [0.0, 0.0] },
            Vertex{ pos: [xw, y],  col: color, texc: [0.0, 0.0] },
            Vertex{ pos: [xw, yh], col: color, texc: [0.0, 0.0] },
            Vertex{ pos: [ x, yh], col: color, texc: [0.0, 0.0] },
        ], [
            0, 1, 2,
            2, 3, 0,
        ]);
    }
}

impl<'a> Drop for DrawContext<'a>
{
    fn drop(&mut self)
    {
        self.eng.commit();
        self.surface.swap_buffers();
    }
}
<|endoftext|>


<commit_before>use build;
use log::Level;
use session::{ColorConfig, Session};
use std::default::Default;
use std::env;
use std::env::current_dir;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::rc::Rc;

/// Configure various aspects of how LALRPOP works.
/// Intended for use within a `build.rs` script.
/// To get the default configuration, use `Configuration::new`.
#[derive(Clone, Default)]
pub struct Configuration {
    session: Session,
}

impl Configuration {
    /// Creates the default configuration; equivalent to `Configuration::default`.
    pub fn new() -> Configuration {
        Configuration::default()
    }

    /// Always use ANSI colors in output, even if output does not appear to be a TTY.
    pub fn always_use_colors(&mut self) -> &mut Configuration {
        self.session.color_config = ColorConfig::Yes;
        self
    }

    /// Never use ANSI colors in output, even if output appears to be a TTY.
    pub fn never_use_colors(&mut self) -> &mut Configuration {
        self.session.color_config = ColorConfig::No;
        self
    }

    /// Use ANSI colors in output if output appears to be a TTY, but
    /// not otherwise. This is the default.
    pub fn use_colors_if_tty(&mut self) -> &mut Configuration {
        self.session.color_config = ColorConfig::IfTty;
        self
    }

    /// Specify a custom directory to search for input files.  This
    /// directory is recursively searched for `.lalrpop` files to be
    /// considered as input files.  This configuration setting also
    /// impacts where output files are placed; paths are made relative
    /// to the input path before being resolved relative to the output
    /// path.  By default, the input directory is the current working
    /// directory.
    pub fn set_in_dir<P>(&mut self, dir: P) -> &mut Self
    where
        P: Into<PathBuf>,
    {
        self.session.in_dir = Some(dir.into());
        self
    }

    /// Specify a custom directory to use when writing output files.
    /// By default, the output directory is the same as the input
    /// directory.
    pub fn set_out_dir<P>(&mut self, dir: P) -> &mut Self
    where
        P: Into<PathBuf>,
    {
        self.session.out_dir = Some(dir.into());
        self
    }

    /// Apply `cargo` directory location conventions, by setting the
    /// input directory to `src` and the output directory to
    /// `$OUT_DIR`.
    pub fn use_cargo_dir_conventions(&mut self) -> &mut Self {
        self.set_in_dir("src")
            .set_out_dir(env::var("OUT_DIR").unwrap());
        self
    }

    /// Write output files in the same directory of the input files.
    ///
    /// If this option is enabled, you have to load the parser as a module:
    ///
    /// ```no_run
    /// mod parser; // synthesized from parser.lalrpop
    /// ```
    ///
    /// This was the default behaviour up to version 0.15.
    pub fn generate_in_source_tree(&mut self) -> &mut Self {
        self.set_in_dir(Path::new("."))
            .set_out_dir(Path::new("."))
    }

    /// If true, always convert `.lalrpop` files into `.rs` files, even if the
    /// `.rs` file is newer. Default is false.
    pub fn force_build(&mut self, val: bool) -> &mut Configuration {
        self.session.force_build = val;
        self
    }

    /// If true, emit comments into the generated code. This makes the
    /// generated code significantly larger. Default is false.
    pub fn emit_comments(&mut self, val: bool) -> &mut Configuration {
        self.session.emit_comments = val;
        self
    }

    /// If true, emit report file about generated code.
    pub fn emit_report(&mut self, val: bool) -> &mut Configuration {
        self.session.emit_report = val;
        self
    }

    /// Minimal logs: only for errors that halt progress.
    pub fn log_quiet(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Taciturn);
        self
    }

    /// Informative logs: give some high-level indications of
    /// progress (default).
    pub fn log_info(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Informative);
        self
    }

    /// Verbose logs: more than info, but still not overwhelming.
    pub fn log_verbose(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Verbose);
        self
    }

    /// Debug logs: better redirect this to a file. Intended for
    /// debugging LALRPOP itself.
    pub fn log_debug(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Debug);
        self
    }

    /// Enables "unit-testing" configuration. This is only for
    /// lalrpop-test.
    #[doc(hidden)]
    pub fn unit_test(&mut self) -> &mut Configuration {
        self.session.unit_test = true;
        self
    }

    /// Process all files according to the `set_in_dir` and
    /// `set_out_dir` configuration.
    pub fn process(&self) -> Result<(), Box<Error>> {
        let root = if let Some(ref d) = self.session.in_dir {
            d.as_path()
        } else {
            Path::new(".")
        };
        self.process_dir(root)
    }

    /// Process all files in the current directory, which -- unless you
    /// have changed it -- is typically the root of the crate being compiled.
    pub fn process_current_dir(&self) -> Result<(), Box<Error>> {
        self.process_dir(try!(current_dir()))
    }

    /// Process all `.lalrpop` files in `path`.
    pub fn process_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<Error>> {
        let mut session = self.session.clone();

        // If in/out dir are empty, use cargo conventions by default.
        // See https://github.com/lalrpop/lalrpop/issues/280
        if session.in_dir.is_none() {
            let mut in_dir = try!(env::current_dir());
            in_dir.push("src");
            session.in_dir = Some(in_dir);
        }

        if session.out_dir.is_none() {
            let out_dir = env::var_os("OUT_DIR").expect("missing OUT_DIR variable");
            session.out_dir = Some(PathBuf::from(out_dir));
        }

        let session = Rc::new(session);
        try!(build::process_dir(session, path));
        Ok(())
    }

    /// Process the given `.lalrpop` file.
    pub fn process_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<Error>> {
        let session = Rc::new(self.session.clone());
        try!(build::process_file(session, path));
        Ok(())
    }
}

/// Process all files in the current directory, which -- unless you
/// have changed it -- is typically the root of the crate being compiled.
///
/// Equivalent to `Configuration::new().process_current_dir()`.
pub fn process_root() -> Result<(), Box<Error>> {
    Configuration::new().process_current_dir()
}

/// Deprecated in favor of `Configuration`. Try:
///
/// ```rust
/// Configuration::new().force_build(true).process_current_dir()
/// ```
///
/// instead.
pub fn process_root_unconditionally() -> Result<(), Box<Error>> {
    Configuration::new().force_build(true).process_current_dir()
}
<commit_msg>Return an error when OUT_DIR is not available<commit_after>use build;
use log::Level;
use session::{ColorConfig, Session};
use std::default::Default;
use std::env;
use std::env::current_dir;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::rc::Rc;

/// Configure various aspects of how LALRPOP works.
/// Intended for use within a `build.rs` script.
/// To get the default configuration, use `Configuration::new`.
#[derive(Clone, Default)]
pub struct Configuration {
    session: Session,
}

impl Configuration {
    /// Creates the default configuration; equivalent to `Configuration::default`.
    pub fn new() -> Configuration {
        Configuration::default()
    }

    /// Always use ANSI colors in output, even if output does not appear to be a TTY.
    pub fn always_use_colors(&mut self) -> &mut Configuration {
        self.session.color_config = ColorConfig::Yes;
        self
    }

    /// Never use ANSI colors in output, even if output appears to be a TTY.
    pub fn never_use_colors(&mut self) -> &mut Configuration {
        self.session.color_config = ColorConfig::No;
        self
    }

    /// Use ANSI colors in output if output appears to be a TTY, but
    /// not otherwise. This is the default.
    pub fn use_colors_if_tty(&mut self) -> &mut Configuration {
        self.session.color_config = ColorConfig::IfTty;
        self
    }

    /// Specify a custom directory to search for input files.  This
    /// directory is recursively searched for `.lalrpop` files to be
    /// considered as input files.  This configuration setting also
    /// impacts where output files are placed; paths are made relative
    /// to the input path before being resolved relative to the output
    /// path.  By default, the input directory is the current working
    /// directory.
    pub fn set_in_dir<P>(&mut self, dir: P) -> &mut Self
    where
        P: Into<PathBuf>,
    {
        self.session.in_dir = Some(dir.into());
        self
    }

    /// Specify a custom directory to use when writing output files.
    /// By default, the output directory is the same as the input
    /// directory.
    pub fn set_out_dir<P>(&mut self, dir: P) -> &mut Self
    where
        P: Into<PathBuf>,
    {
        self.session.out_dir = Some(dir.into());
        self
    }

    /// Apply `cargo` directory location conventions, by setting the
    /// input directory to `src` and the output directory to
    /// `$OUT_DIR`.
    pub fn use_cargo_dir_conventions(&mut self) -> &mut Self {
        self.set_in_dir("src")
            .set_out_dir(env::var("OUT_DIR").unwrap());
        self
    }

    /// Write output files in the same directory of the input files.
    ///
    /// If this option is enabled, you have to load the parser as a module:
    ///
    /// ```no_run
    /// mod parser; // synthesized from parser.lalrpop
    /// ```
    ///
    /// This was the default behaviour up to version 0.15.
    pub fn generate_in_source_tree(&mut self) -> &mut Self {
        self.set_in_dir(Path::new("."))
            .set_out_dir(Path::new("."))
    }

    /// If true, always convert `.lalrpop` files into `.rs` files, even if the
    /// `.rs` file is newer. Default is false.
    pub fn force_build(&mut self, val: bool) -> &mut Configuration {
        self.session.force_build = val;
        self
    }

    /// If true, emit comments into the generated code. This makes the
    /// generated code significantly larger. Default is false.
    pub fn emit_comments(&mut self, val: bool) -> &mut Configuration {
        self.session.emit_comments = val;
        self
    }

    /// If true, emit report file about generated code.
    pub fn emit_report(&mut self, val: bool) -> &mut Configuration {
        self.session.emit_report = val;
        self
    }

    /// Minimal logs: only for errors that halt progress.
    pub fn log_quiet(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Taciturn);
        self
    }

    /// Informative logs: give some high-level indications of
    /// progress (default).
    pub fn log_info(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Informative);
        self
    }

    /// Verbose logs: more than info, but still not overwhelming.
    pub fn log_verbose(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Verbose);
        self
    }

    /// Debug logs: better redirect this to a file. Intended for
    /// debugging LALRPOP itself.
    pub fn log_debug(&mut self) -> &mut Configuration {
        self.session.log.set_level(Level::Debug);
        self
    }

    /// Enables "unit-testing" configuration. This is only for
    /// lalrpop-test.
    #[doc(hidden)]
    pub fn unit_test(&mut self) -> &mut Configuration {
        self.session.unit_test = true;
        self
    }

    /// Process all files according to the `set_in_dir` and
    /// `set_out_dir` configuration.
    pub fn process(&self) -> Result<(), Box<Error>> {
        let root = if let Some(ref d) = self.session.in_dir {
            d.as_path()
        } else {
            Path::new(".")
        };
        self.process_dir(root)
    }

    /// Process all files in the current directory, which -- unless you
    /// have changed it -- is typically the root of the crate being compiled.
    pub fn process_current_dir(&self) -> Result<(), Box<Error>> {
        self.process_dir(try!(current_dir()))
    }

    /// Process all `.lalrpop` files in `path`.
    pub fn process_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<Error>> {
        let mut session = self.session.clone();

        // If in/out dir are empty, use cargo conventions by default.
        // See https://github.com/lalrpop/lalrpop/issues/280
        if session.in_dir.is_none() {
            let mut in_dir = try!(env::current_dir());
            in_dir.push("src");
            session.in_dir = Some(in_dir);
        }

        if session.out_dir.is_none() {
            let out_dir = match env::var_os("OUT_DIR") {
                Some(var) => var,
                None => return Err("missing OUT_DIR variable")?,
            };
            session.out_dir = Some(PathBuf::from(out_dir));
        }

        let session = Rc::new(session);
        try!(build::process_dir(session, path));
        Ok(())
    }

    /// Process the given `.lalrpop` file.
    pub fn process_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<Error>> {
        let session = Rc::new(self.session.clone());
        try!(build::process_file(session, path));
        Ok(())
    }
}

/// Process all files in the current directory, which -- unless you
/// have changed it -- is typically the root of the crate being compiled.
///
/// Equivalent to `Configuration::new().process_current_dir()`.
pub fn process_root() -> Result<(), Box<Error>> {
    Configuration::new().process_current_dir()
}

/// Deprecated in favor of `Configuration`. Try:
///
/// ```rust
/// Configuration::new().force_build(true).process_current_dir()
/// ```
///
/// instead.
pub fn process_root_unconditionally() -> Result<(), Box<Error>> {
    Configuration::new().force_build(true).process_current_dir()
}
<|endoftext|>


<commit_before>extern crate time;

use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};

const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S:%f";

impl ToSql for time::Timespec {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        let time_string = time::at_utc(*self)
            .strftime(SQLITE_DATETIME_FMT)
            .unwrap()
            .to_string();
        Ok(ToSqlOutput::from(time_string))
    }
}

impl FromSql for time::Timespec {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        value
            .as_str()
            .and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
                          Ok(tm) => Ok(tm.to_timespec()),
                          Err(err) => Err(FromSqlError::Other(Box::new(err))),
                      })
    }
}

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

    fn checked_memory_handle() -> Connection {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
            .unwrap();
        db
    }

    #[test]
    fn test_timespec() {
        let db = checked_memory_handle();

        let ts = time::Timespec {
            sec: 10_000,
            nsec: 0,
        };
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
            .unwrap();

        let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
            .unwrap();
        assert_eq!(from, ts);
    }
}
<commit_msg>added more tests<commit_after>extern crate time;

use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};

const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S:%f";

impl ToSql for time::Timespec {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        let time_string = time::at_utc(*self)
            .strftime(SQLITE_DATETIME_FMT)
            .unwrap()
            .to_string();
        Ok(ToSqlOutput::from(time_string))
    }
}

impl FromSql for time::Timespec {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        value
            .as_str()
            .and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
                          Ok(tm) => Ok(tm.to_timespec()),
                          Err(err) => Err(FromSqlError::Other(Box::new(err))),
                      })
    }
}

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

    fn checked_memory_handle() -> Connection {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
            .unwrap();
        db
    }

    #[test]
    fn test_timespec() {
        let db = checked_memory_handle();

        let mut ts_vec = vec![];

        ts_vec.push(time::Timespec::new(10_000, 0));//January 1, 1970 2:46:40 AM
        ts_vec.push(time::Timespec::new(10_000, 1000));//January 1, 1970 2:46:40 AM (and one microsecond)
        ts_vec.push(time::Timespec::new(1500391124, 1_000_000));//July 18, 2017
        ts_vec.push(time::Timespec::new(2000000000, 2_000_000));//May 18, 2033
        ts_vec.push(time::Timespec::new(3000000000, 999_999_999));//January 24, 2065
        ts_vec.push(time::Timespec::new(10000000000, 0));//November 20, 2286

        for ts in ts_vec {

            db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
                .unwrap();

            let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
                .unwrap();

            db.execute("DELETE FROM foo", &[]).unwrap();

            assert_eq!(from, ts);
        }

    }
}
<|endoftext|>


<commit_before>//! This library is used to gather all error codes into one place,
//! the goal being to make their maintenance easier.

macro_rules! register_diagnostics {
    ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
        pub static DIAGNOSTICS: &[(&str, Option<&str>)] = &[
            $( (stringify!($ecode), Some($message)), )*
            $( (stringify!($code), None), )*
        ];
    )
}

mod error_codes;
pub use error_codes::DIAGNOSTICS;
<commit_msg>Rollup merge of #78760 - jyn514:deny-invalid-codeblocks, r=GuillaumeGomez<commit_after>#![deny(invalid_codeblock_attributes)]
//! This library is used to gather all error codes into one place,
//! the goal being to make their maintenance easier.

macro_rules! register_diagnostics {
    ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
        pub static DIAGNOSTICS: &[(&str, Option<&str>)] = &[
            $( (stringify!($ecode), Some($message)), )*
            $( (stringify!($code), None), )*
        ];
    )
}

mod error_codes;
pub use error_codes::DIAGNOSTICS;
<|endoftext|>


<commit_before>use super::{super::heap::Pointer, Event, FiberId, FullTracer, InFiberEvent};
use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst::{Cst, CstDb, CstKind},
        hir::Id,
    },
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;
use std::collections::HashMap;
use tracing::debug;

// Stack traces are a reduced view of the tracing state that represent the stack
// trace at a given moment in time.

#[derive(Clone)]
pub enum StackEntry {
    Call {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    Needs {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    Module {
        module: Module,
    },
}

impl FullTracer {
    pub fn stack_traces(&self) -> HashMap<FiberId, Vec<StackEntry>> {
        let mut stacks: HashMap<FiberId, Vec<StackEntry>> = HashMap::new();
        for timed_event in &self.events {
            if let Event::InFiber { fiber, event } = &timed_event.event {
                let stack = stacks.entry(*fiber).or_default();
                match event {
                    InFiberEvent::ModuleStarted { module } => {
                        stack.push(StackEntry::Module {
                            module: module.clone(),
                        });
                    }
                    InFiberEvent::ModuleEnded { .. } => {
                        stack.pop().unwrap();
                    }
                    InFiberEvent::CallStarted { id, closure, args } => {
                        stack.push(StackEntry::Call {
                            id: id.clone(),
                            closure: *closure,
                            args: args.clone(),
                        });
                    }
                    InFiberEvent::CallEnded { .. } => {
                        stack.pop().unwrap();
                    }
                    InFiberEvent::NeedsStarted {
                        id,
                        condition,
                        reason,
                    } => {
                        stack.push(StackEntry::Needs {
                            id: id.clone(),
                            condition: *condition,
                            reason: *reason,
                        });
                    }
                    InFiberEvent::NeedsEnded => {
                        stack.pop().unwrap();
                    }
                    _ => {}
                }
            }
        }
        stacks
    }
    pub fn format_stack_trace(&self, db: &Database, stack: &[StackEntry]) -> String {
        let mut lines = vec![];

        for entry in stack.iter().rev() {
            let hir_id = match entry {
                StackEntry::Call { id, .. } => Some(id),
                StackEntry::Needs { id, .. } => Some(id),
                StackEntry::Module { .. } => None,
            };
            let (cst_id, span) = if let Some(hir_id) = hir_id {
                let module = hir_id.module.clone();
                let cst_id = db.hir_to_cst_id(hir_id.clone());
                let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                let span = cst.map(|cst| {
                    (
                        db.offset_to_lsp(module.clone(), cst.span.start),
                        db.offset_to_lsp(module.clone(), cst.span.end),
                    )
                });
                (cst_id, span)
            } else {
                (None, None)
            };
            let caller_location_string = format!(
                "{} {}",
                hir_id
                    .map(|id| format!("{id}"))
                    .unwrap_or_else(|| "<no hir>".to_string()),
                span.map(|((start_line, start_col), (end_line, end_col))| format!(
                    "{}:{} – {}:{}",
                    start_line, start_col, end_line, end_col
                ))
                .unwrap_or_else(|| "<no location>".to_string())
            );
            let call_string = match entry {
                StackEntry::Call { closure, args, .. } => format!(
                    "{} {}",
                    cst_id
                        .and_then(|id| {
                            let cst = db.find_cst(hir_id.unwrap().module.clone(), id);
                            match cst.kind {
                                CstKind::Call { receiver, .. } => receiver.extract_receiver_name(),
                                _ => None,
                            }
                        })
                        .unwrap_or_else(|| closure.format(&self.heap)),
                    args.iter().map(|arg| arg.format(&self.heap)).join(" ")
                ),
                StackEntry::Needs {
                    condition, reason, ..
                } => format!(
                    "needs {} {}",
                    condition.format(&self.heap),
                    reason.format(&self.heap),
                ),
                StackEntry::Module { module } => format!("module {module}"),
            };
            lines.push(format!("{caller_location_string:90} {call_string}"));
        }
        lines.join("\n")
    }
    /// When a VM panics, some child fiber might be responsible for that. This
    /// function returns a formatted stack trace spanning all fibers in the
    /// chain from the panicking root fiber until the concrete failing needs.
    pub fn format_panic_stack_trace_to_root_fiber(&self, db: &Database) -> String {
        let mut panicking_fiber_chain = vec![FiberId::root()];
        for timed_event in self.events.iter().rev() {
            if let Event::FiberPanicked {
                fiber,
                panicked_child,
            } = timed_event.event
            {
                if fiber == *panicking_fiber_chain.last().unwrap() {
                    match panicked_child {
                        Some(child) => panicking_fiber_chain.push(child),
                        None => break,
                    }
                }
            }
        }

        let stack_traces = self.stack_traces();
        debug!("Stack traces: {:?}", stack_traces.keys().collect_vec());
        panicking_fiber_chain
            .into_iter()
            .rev()
            .map(|fiber| self.format_stack_trace(db, &stack_traces[&fiber]))
            .join("\n(fiber boundary)\n")
    }
}

impl Cst {
    fn extract_receiver_name(&self) -> Option<String> {
        match &self.kind {
            CstKind::TrailingWhitespace { child, .. } => child.extract_receiver_name(),
            CstKind::Identifier(identifier) => Some(identifier.to_string()),
            CstKind::Parenthesized { inner, .. } => inner.extract_receiver_name(),
            CstKind::StructAccess { struct_, key, .. } => {
                let struct_string = struct_.extract_receiver_name()?;
                let key = key.extract_receiver_name()?;
                Some(format!("{struct_string}.{key}"))
            }
            _ => None,
        }
    }
}
<commit_msg>Remove logging all stack traces<commit_after>use super::{super::heap::Pointer, Event, FiberId, FullTracer, InFiberEvent};
use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst::{Cst, CstDb, CstKind},
        hir::Id,
    },
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;
use std::collections::HashMap;

// Stack traces are a reduced view of the tracing state that represent the stack
// trace at a given moment in time.

#[derive(Clone)]
pub enum StackEntry {
    Call {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    Needs {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    Module {
        module: Module,
    },
}

impl FullTracer {
    pub fn stack_traces(&self) -> HashMap<FiberId, Vec<StackEntry>> {
        let mut stacks: HashMap<FiberId, Vec<StackEntry>> = HashMap::new();
        for timed_event in &self.events {
            if let Event::InFiber { fiber, event } = &timed_event.event {
                let stack = stacks.entry(*fiber).or_default();
                match event {
                    InFiberEvent::ModuleStarted { module } => {
                        stack.push(StackEntry::Module {
                            module: module.clone(),
                        });
                    }
                    InFiberEvent::ModuleEnded { .. } => {
                        stack.pop().unwrap();
                    }
                    InFiberEvent::CallStarted { id, closure, args } => {
                        stack.push(StackEntry::Call {
                            id: id.clone(),
                            closure: *closure,
                            args: args.clone(),
                        });
                    }
                    InFiberEvent::CallEnded { .. } => {
                        stack.pop().unwrap();
                    }
                    InFiberEvent::NeedsStarted {
                        id,
                        condition,
                        reason,
                    } => {
                        stack.push(StackEntry::Needs {
                            id: id.clone(),
                            condition: *condition,
                            reason: *reason,
                        });
                    }
                    InFiberEvent::NeedsEnded => {
                        stack.pop().unwrap();
                    }
                    _ => {}
                }
            }
        }
        stacks
    }
    pub fn format_stack_trace(&self, db: &Database, stack: &[StackEntry]) -> String {
        let mut lines = vec![];

        for entry in stack.iter().rev() {
            let hir_id = match entry {
                StackEntry::Call { id, .. } => Some(id),
                StackEntry::Needs { id, .. } => Some(id),
                StackEntry::Module { .. } => None,
            };
            let (cst_id, span) = if let Some(hir_id) = hir_id {
                let module = hir_id.module.clone();
                let cst_id = db.hir_to_cst_id(hir_id.clone());
                let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                let span = cst.map(|cst| {
                    (
                        db.offset_to_lsp(module.clone(), cst.span.start),
                        db.offset_to_lsp(module.clone(), cst.span.end),
                    )
                });
                (cst_id, span)
            } else {
                (None, None)
            };
            let caller_location_string = format!(
                "{} {}",
                hir_id
                    .map(|id| format!("{id}"))
                    .unwrap_or_else(|| "<no hir>".to_string()),
                span.map(|((start_line, start_col), (end_line, end_col))| format!(
                    "{}:{} – {}:{}",
                    start_line, start_col, end_line, end_col
                ))
                .unwrap_or_else(|| "<no location>".to_string())
            );
            let call_string = match entry {
                StackEntry::Call { closure, args, .. } => format!(
                    "{} {}",
                    cst_id
                        .and_then(|id| {
                            let cst = db.find_cst(hir_id.unwrap().module.clone(), id);
                            match cst.kind {
                                CstKind::Call { receiver, .. } => receiver.extract_receiver_name(),
                                _ => None,
                            }
                        })
                        .unwrap_or_else(|| closure.format(&self.heap)),
                    args.iter().map(|arg| arg.format(&self.heap)).join(" ")
                ),
                StackEntry::Needs {
                    condition, reason, ..
                } => format!(
                    "needs {} {}",
                    condition.format(&self.heap),
                    reason.format(&self.heap),
                ),
                StackEntry::Module { module } => format!("module {module}"),
            };
            lines.push(format!("{caller_location_string:90} {call_string}"));
        }
        lines.join("\n")
    }
    /// When a VM panics, some child fiber might be responsible for that. This
    /// function returns a formatted stack trace spanning all fibers in the
    /// chain from the panicking root fiber until the concrete failing needs.
    pub fn format_panic_stack_trace_to_root_fiber(&self, db: &Database) -> String {
        let mut panicking_fiber_chain = vec![FiberId::root()];
        for timed_event in self.events.iter().rev() {
            if let Event::FiberPanicked {
                fiber,
                panicked_child,
            } = timed_event.event
            {
                if fiber == *panicking_fiber_chain.last().unwrap() {
                    match panicked_child {
                        Some(child) => panicking_fiber_chain.push(child),
                        None => break,
                    }
                }
            }
        }

        let stack_traces = self.stack_traces();
        panicking_fiber_chain
            .into_iter()
            .rev()
            .map(|fiber| self.format_stack_trace(db, &stack_traces[&fiber]))
            .join("\n(fiber boundary)\n")
    }
}

impl Cst {
    fn extract_receiver_name(&self) -> Option<String> {
        match &self.kind {
            CstKind::TrailingWhitespace { child, .. } => child.extract_receiver_name(),
            CstKind::Identifier(identifier) => Some(identifier.to_string()),
            CstKind::Parenthesized { inner, .. } => inner.extract_receiver_name(),
            CstKind::StructAccess { struct_, key, .. } => {
                let struct_string = struct_.extract_receiver_name()?;
                let key = key.extract_receiver_name()?;
                Some(format!("{struct_string}.{key}"))
            }
            _ => None,
        }
    }
}
<|endoftext|>