luau-syntax 0.732.0

Luau lexer, parser, AST, CST, and source utilities
Documentation
use super::*;

pub(in crate::parser) struct ParsedName<'ast> {
    pub(in crate::parser) name: AstName<'ast>,
    pub(in crate::parser) location: Location,
}

impl<'source, 'ast> Parser<'source, 'ast, '_, '_> {
    pub(in crate::parser) fn expect_and_consume(
        &mut self,
        expected: Token<'source, 'ast>,
        context: &'static str,
    ) -> bool {
        let expected_kind = expected.kind();
        if self.current.kind() == expected_kind {
            self.advance();
            true
        } else {
            self.report_parse_error(self.located(format!(
                "Expected {} when parsing {context}, got {}",
                expected, self.current
            )));
            if self.peek().kind() == expected_kind {
                self.advance();
                self.advance();
            }
            false
        }
    }

    pub(in crate::parser) fn expect_and_consume_keyword(
        &mut self,
        expected: R,
        context: &'static str,
    ) -> bool {
        self.expect_and_consume(Token::Reserved(expected), context)
    }

    pub(in crate::parser) fn parse_index_name(
        &mut self,
        context: Option<&'static str>,
        op_position: Position,
    ) -> Result<ParsedName<'ast>> {
        match self.current {
            Token::Ident(name) => {
                let location = self.current_token_location();
                self.advance();
                Ok(ParsedName { name, location })
            }
            Token::Reserved(word) if self.current_position().line == op_position.line => {
                self.report_parse_error(self.identifier_error(context));
                let location = self.current_token_location();
                let name = self.intern_name(word.as_str());
                self.advance();
                Ok(ParsedName { name, location })
            }
            _ => {
                self.report_parse_error(self.identifier_error(context));
                let location = self.current_token_location();
                Ok(ParsedName {
                    name: self.name_error,
                    location: Location::new(location.begin, location.begin),
                })
            }
        }
    }

    pub(in crate::parser) fn parse_name(&mut self, context: &'static str) -> ParsedName<'ast> {
        match self.current {
            Token::Ident(name) => {
                let location = self.current_token_location();
                self.advance();
                ParsedName { name, location }
            }
            _ => {
                let location = self.current_token_location();
                self.report_parse_error(self.identifier_error(Some(context)));
                ParsedName {
                    name: self.name_error,
                    location: Location::new(location.begin, location.begin),
                }
            }
        }
    }

    #[inline(always)]
    pub(in crate::parser) fn advance(&mut self) {
        let mut update_previous_location = true;
        loop {
            self.lexer
                .advance_lexeme_with_previous(false, update_previous_location);
            update_previous_location = false;
            let location = self.lexer.current_location();
            let token = self.lexer.current_token();

            match token {
                Token::Comment(text) => {
                    self.capture_comment(CommentKind::Line, location);
                    self.capture_hotcomment(text, location);
                }
                Token::BlockComment(_) => {
                    self.capture_comment(CommentKind::Block, location);
                }
                Token::BrokenComment => {
                    self.capture_comment(CommentKind::Broken, location);
                    self.current = token;
                    return;
                }
                _ => {
                    self.current = token;
                    if self.current.kind() != TokenKind::Eof {
                        self.finish_hotcomment_header();
                    }
                    return;
                }
            }
        }
    }

    pub(in crate::parser) fn peek(&mut self) -> Token<'source, 'ast> {
        self.lexer.lookahead_token(true)
    }

    pub(in crate::parser) fn peek_nth(&mut self, n: usize) -> Token<'source, 'ast> {
        if n == 0 {
            return Token::Eof;
        }

        self.lexer.lookahead_nth_token(n, true)
    }

    #[inline(always)]
    pub(in crate::parser) fn current_location(&self) -> Location {
        self.current_token_location()
    }

    #[inline(always)]
    pub(in crate::parser) fn current_position(&self) -> Position {
        self.current_token_location().begin
    }

    #[inline(always)]
    pub(in crate::parser) fn current_token_location(&self) -> Location {
        self.lexer.current_location()
    }

    #[inline(always)]
    pub(in crate::parser) fn previous_token_end_position(&self) -> Position {
        self.previous_token_location().end
    }

    #[inline(always)]
    pub(in crate::parser) fn previous_token_location(&self) -> Location {
        self.lexer.previous_location()
    }

    #[inline(always)]
    pub(in crate::parser) fn current_line(&self) -> usize {
        self.current_token_location().begin.line as usize + 1
    }

    #[inline(always)]
    pub(in crate::parser) fn current_column(&self) -> usize {
        self.current_token_location().begin.column as usize + 1
    }

    #[inline(always)]
    pub(in crate::parser) fn previous_line(&self) -> usize {
        self.previous_token_location().begin.line as usize + 1
    }

    pub(in crate::parser) fn source_line_count(&self) -> usize {
        let source = self.lexer.source();
        self.current_token_location().end.line as usize
            + usize::from(!source.is_empty() && !source.ends_with(b"\n"))
    }

    #[inline(always)]
    pub(in crate::parser) fn current_source_string(&self) -> AstString<'ast> {
        match self.current {
            Token::Number(source)
            | Token::InterpStringBegin(source)
            | Token::InterpStringMid(source)
            | Token::InterpStringEnd(source)
            | Token::InterpStringSimple(source) => self.ast_string(source),
            Token::QuotedString { value, .. } | Token::RawString { value, .. } => {
                self.ast_string(value)
            }
            _ => {
                let (start, end) = self.lexer.current_span();
                self.ast_string(&self.lexer.source()[start..end])
            }
        }
    }

    pub(in crate::parser) fn check_binary_confusables(
        &mut self,
        limit: u8,
    ) -> Option<BinaryPriority> {
        match self.current {
            Token::Ampersand | Token::Pipe | Token::Bang => {}
            _ => return None,
        }

        let adjacent = self.lexer.lookahead_lexeme(true);
        if adjacent.start != self.lexer.current_span().1 {
            return None;
        }
        let op_position = self.current_position();
        let location = Location::new(self.current_token_location().begin, adjacent.location.end);
        match self.current {
            Token::Bang if adjacent.token.kind() == TokenKind::Equal && 3 > limit => {
                self.advance();
                self.report_parse_error(ParseError::new(
                    location,
                    "Unexpected '!='; did you mean '~='?",
                ));
                Some(BinaryPriority {
                    op: BinaryOp::NotEqual,
                    left: 3,
                    right: 3,
                    op_position: Some(op_position),
                })
            }
            Token::Ampersand if adjacent.token.kind() == TokenKind::Ampersand && 2 > limit => {
                self.advance();
                self.report_parse_error(ParseError::new(
                    location,
                    "Unexpected '&&'; did you mean 'and'?",
                ));
                Some(BinaryPriority {
                    op: BinaryOp::And,
                    left: 2,
                    right: 2,
                    op_position: Some(op_position),
                })
            }
            Token::Pipe if adjacent.token.kind() == TokenKind::Pipe && 1 > limit => {
                self.advance();
                self.report_parse_error(ParseError::new(
                    location,
                    "Unexpected '||'; did you mean 'or'?",
                ));
                Some(BinaryPriority {
                    op: BinaryOp::Or,
                    left: 1,
                    right: 1,
                    op_position: Some(op_position),
                })
            }
            _ => None,
        }
    }

    pub(in crate::parser) fn parse_interpolated_literal(
        &self,
        literal: &[u8],
    ) -> Result<AstString<'ast>> {
        fixup_string_bytes(literal)
            .map(|bytes| self.ast_string(&bytes))
            .ok_or_else(|| self.interpolated_malformed_escape_error())
    }

    pub(in crate::parser) fn expect_function_left_paren(&mut self) -> Result<()> {
        if self.current.kind() == TokenKind::LeftParen {
            self.advance();
            Ok(())
        } else {
            self.report_parse_error(self.located(format!(
                "Expected '(' when parsing function, got {}",
                self.current
            )));
            if self.peek().kind() == TokenKind::LeftParen {
                self.advance();
                self.advance();
            }
            Ok(())
        }
    }
}