From f1261b349e875b842745b63258c3e338cb1fe3bf Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sun, 23 May 2021 03:00:39 +0000 Subject: CTAN sync 202105230300 --- support/texlab/src/syntax/latex/parser.rs | 1809 ++++++++++++++++++++++++++--- 1 file changed, 1666 insertions(+), 143 deletions(-) (limited to 'support/texlab/src/syntax/latex/parser.rs') diff --git a/support/texlab/src/syntax/latex/parser.rs b/support/texlab/src/syntax/latex/parser.rs index 4ace4fbcd6..4333235ba5 100644 --- a/support/texlab/src/syntax/latex/parser.rs +++ b/support/texlab/src/syntax/latex/parser.rs @@ -1,186 +1,1709 @@ -use super::ast::*; -use crate::{ - protocol::{Range, RangeExt}, - syntax::{ - generic_ast::{Ast, AstNodeIndex}, - text::SyntaxNode, - }, +use cstree::GreenNodeBuilder; + +use super::{ + lexer::Lexer, + SyntaxKind::{self, *}, + SyntaxNode, }; -use std::iter::Peekable; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum Scope { - Root, - Group, - Options, +#[derive(Clone)] +pub struct Parse { + pub root: SyntaxNode, +} + +#[derive(Debug, Clone, Copy)] +struct ParserContext { + allow_environment: bool, + allow_comma: bool, +} + +impl Default for ParserContext { + fn default() -> Self { + Self { + allow_environment: true, + allow_comma: true, + } + } } #[derive(Debug)] -pub struct Parser> { - tree: Ast, - tokens: Peekable, +struct Parser<'a> { + lexer: Lexer<'a>, + builder: GreenNodeBuilder<'static, 'static>, } -impl> Parser { - pub fn new(tokens: I) -> Self { +impl<'a> Parser<'a> { + pub fn new(text: &'a str) -> Self { Self { - tree: Ast::new(), - tokens: tokens.peekable(), + lexer: Lexer::new(text), + builder: GreenNodeBuilder::new(), } } - pub fn parse(mut self) -> Tree { - let children = self.content(Scope::Root); + fn eat(&mut self) { + let (kind, text) = self.lexer.eat().unwrap(); + self.builder.token(kind.into(), text); + } + + fn peek(&self) -> Option { + self.lexer.peek() + } + + fn expect(&mut self, kind: SyntaxKind) { + if self.peek() == Some(kind) { + self.eat(); + self.trivia(); + } else { + self.builder.token(MISSING.into(), ""); + } + } - let range = if children.is_empty() { - Range::new_simple(0, 0, 0, 0) + fn expect2(&mut self, kind1: SyntaxKind, kind2: SyntaxKind) { + if self + .peek() + .filter(|&kind| kind == kind1 || kind == kind2) + .is_some() + { + self.eat(); + self.trivia(); } else { - let start = self.tree[children[0]].start(); - let end = self.tree[children[children.len() - 1]].end(); - Range::new(start, end) - }; + self.builder.token(MISSING.into(), ""); + } + } - let root = self.tree.add_node(Node::Root(Root { range })); - self.connect(root, &children); - Tree { - inner: self.tree, - root, + fn trivia(&mut self) { + while self + .peek() + .filter(|&kind| matches!(kind, WHITESPACE | COMMENT)) + .is_some() + { + self.eat(); } } - fn content(&mut self, scope: Scope) -> Vec { - let mut children = Vec::new(); - while let Some(ref token) = self.tokens.peek() { - match token.kind { - TokenKind::Word | TokenKind::BeginOptions => { - children.push(self.text(scope)); - } - TokenKind::Command => { - children.push(self.command()); - } - TokenKind::Comma => { - children.push(self.comma()); - } - TokenKind::Math => { - children.push(self.math()); - } - TokenKind::BeginGroup => { - children.push(self.group(GroupKind::Group)); - } - TokenKind::EndGroup => { - if scope == Scope::Root { - self.tokens.next(); - } else { - return children; - } - } - TokenKind::EndOptions => { - if scope == Scope::Options { - return children; + pub fn parse(mut self) -> Parse { + self.builder.start_node(ROOT.into()); + self.preamble(); + while self.peek().is_some() { + self.content(ParserContext::default()); + } + self.builder.finish_node(); + let (green_node, interner) = self.builder.finish(); + Parse { + root: SyntaxNode::new_root_with_resolver(green_node, interner.unwrap()), + } + } + + fn content(&mut self, context: ParserContext) { + match self.peek().unwrap() { + WHITESPACE | COMMENT => self.eat(), + L_CURLY if context.allow_environment => self.curly_group(), + L_CURLY => self.curly_group_without_environments(), + L_BRACK | L_PAREN => self.mixed_group(), + R_CURLY | R_BRACK | R_PAREN => { + self.builder.start_node(ERROR.into()); + self.eat(); + self.builder.finish_node(); + } + PARAMETER => self.eat(), + WORD | COMMA => self.text(context), + EQUALITY_SIGN => self.eat(), + DOLLAR => self.formula(), + GENERIC_COMMAND_NAME => self.generic_command(), + BEGIN_ENVIRONMENT_NAME if context.allow_environment => self.environment(), + BEGIN_ENVIRONMENT_NAME => self.generic_command(), + END_ENVIRONMENT_NAME => self.generic_command(), + BEGIN_EQUATION_NAME => self.equation(), + END_EQUATION_NAME => self.generic_command(), + MISSING | ERROR => self.eat(), + PART_NAME => self.part(), + CHAPTER_NAME => self.chapter(), + SECTION_NAME => self.section(), + SUBSECTION_NAME => self.subsection(), + SUBSUBSECTION_NAME => self.subsubsection(), + PARAGRAPH_NAME => self.paragraph(), + SUBPARAGRAPH_NAME => self.subparagraph(), + ENUM_ITEM_NAME => self.enum_item(), + CAPTION_NAME => self.caption(), + CITATION_NAME => self.citation(), + PACKAGE_INCLUDE_NAME => self.package_include(), + CLASS_INCLUDE_NAME => self.class_include(), + LATEX_INCLUDE_NAME => self.latex_include(), + BIBLATEX_INCLUDE_NAME => self.biblatex_include(), + BIBTEX_INCLUDE_NAME => self.bibtex_include(), + GRAPHICS_INCLUDE_NAME => self.graphics_include(), + SVG_INCLUDE_NAME => self.svg_include(), + INKSCAPE_INCLUDE_NAME => self.inkscape_include(), + VERBATIM_INCLUDE_NAME => self.verbatim_include(), + IMPORT_NAME => self.import(), + LABEL_DEFINITION_NAME => self.label_definition(), + LABEL_REFERENCE_NAME => self.label_reference(), + LABEL_REFERENCE_RANGE_NAME => self.label_reference_range(), + LABEL_NUMBER_NAME => self.label_number(), + COMMAND_DEFINITION_NAME => self.command_definition(), + MATH_OPERATOR_NAME => self.math_operator(), + GLOSSARY_ENTRY_DEFINITION_NAME => self.glossary_entry_definition(), + GLOSSARY_ENTRY_REFERENCE_NAME => self.glossary_entry_reference(), + ACRONYM_DEFINITION_NAME => self.acronym_definition(), + ACRONYM_DECLARATION_NAME => self.acronym_declaration(), + ACRONYM_REFERENCE_NAME => self.acronym_reference(), + THEOREM_DEFINITION_NAME => self.theorem_definition(), + COLOR_REFERENCE_NAME => self.color_reference(), + COLOR_DEFINITION_NAME => self.color_definition(), + COLOR_SET_DEFINITION_NAME => self.color_set_definition(), + TIKZ_LIBRARY_IMPORT_NAME => self.tikz_library_import(), + ENVIRONMENT_DEFINIITION_NAME => self.environment_definition(), + _ => unreachable!(), + } + } + + fn text(&mut self, context: ParserContext) { + self.builder.start_node(TEXT.into()); + self.eat(); + while self + .peek() + .filter(|&kind| { + matches!(kind, WHITESPACE | COMMENT | WORD | COMMA) + && (context.allow_comma || kind != COMMA) + }) + .is_some() + { + self.eat(); + } + self.builder.finish_node(); + } + + fn group_with_token( + &mut self, + node_kind: SyntaxKind, + content_kind: SyntaxKind, + right_kind: SyntaxKind, + ) { + self.builder.start_node(node_kind.into()); + self.eat(); + self.trivia(); + self.expect(content_kind); + self.expect(right_kind); + self.builder.finish_node(); + } + + fn curly_group(&mut self) { + self.builder.start_node(CURLY_GROUP.into()); + self.eat(); + while self + .peek() + .filter(|&kind| !matches!(kind, R_CURLY | END_ENVIRONMENT_NAME)) + .is_some() + { + self.content(ParserContext::default()); + } + self.expect(R_CURLY); + self.builder.finish_node(); + } + + fn curly_group_without_environments(&mut self) { + self.builder.start_node(CURLY_GROUP.into()); + self.eat(); + while self + .peek() + .filter(|&kind| !matches!(kind, R_CURLY)) + .is_some() + { + self.content(ParserContext { + allow_environment: false, + allow_comma: true, + }); + } + self.expect(R_CURLY); + self.builder.finish_node(); + } + + fn curly_group_word(&mut self) { + self.group_with_token(CURLY_GROUP_WORD, WORD, R_CURLY); + } + + fn curly_group_word_list(&mut self) { + self.builder.start_node(CURLY_GROUP_WORD_LIST.into()); + self.eat(); + + while self + .peek() + .filter(|&kind| matches!(kind, WHITESPACE | COMMENT | WORD | COMMA)) + .is_some() + { + self.eat(); + } + + self.expect(R_CURLY); + self.builder.finish_node(); + } + + fn curly_group_command(&mut self) { + self.group_with_token(CURLY_GROUP_COMMAND, GENERIC_COMMAND_NAME, R_CURLY); + } + + fn brack_group(&mut self) { + self.builder.start_node(BRACK_GROUP.into()); + self.eat(); + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + R_CURLY + | R_BRACK + | PART_NAME + | CHAPTER_NAME + | SECTION_NAME + | SUBSECTION_NAME + | PARAGRAPH_NAME + | SUBPARAGRAPH_NAME + | ENUM_ITEM_NAME + | END_ENVIRONMENT_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); + } + self.expect(R_BRACK); + self.builder.finish_node(); + } + + fn brack_group_word(&mut self) { + self.group_with_token(BRACK_GROUP_WORD, WORD, R_BRACK); + } + + // fn paren_group(&mut self) { + // self.builder.start_node(PAREN_GROUP.into()); + // self.eat(); + // while self + // .peek() + // .filter(|&kind| { + // !matches!( + // kind, + // R_CURLY + // | R_BRACK + // | R_PAREN + // | PART_NAME + // | CHAPTER_NAME + // | SECTION_NAME + // | SUBSECTION_NAME + // | PARAGRAPH_NAME + // | SUBPARAGRAPH_NAME + // | ENUM_ITEM_NAME + // | END_ENVIRONMENT_NAME + // ) + // }) + // .is_some() + // { + // self.content(); + // } + // self.expect(R_PAREN); + // self.builder.finish_node(); + // } + + fn mixed_group(&mut self) { + self.builder.start_node(MIXED_GROUP.into()); + self.eat(); + self.trivia(); + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + R_CURLY + | R_BRACK + | R_PAREN + | PART_NAME + | CHAPTER_NAME + | SECTION_NAME + | SUBSECTION_NAME + | PARAGRAPH_NAME + | SUBPARAGRAPH_NAME + | ENUM_ITEM_NAME + | END_ENVIRONMENT_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); + } + self.expect2(R_BRACK, R_PAREN); + self.builder.finish_node(); + } + + fn key(&mut self) { + self.builder.start_node(KEY.into()); + self.eat(); + while self + .peek() + .filter(|&kind| matches!(kind, WHITESPACE | COMMENT | WORD)) + .is_some() + { + self.eat(); + } + self.builder.finish_node(); + } + + fn value(&mut self) { + self.builder.start_node(VALUE.into()); + self.content(ParserContext { + allow_environment: true, + allow_comma: false, + }); + self.builder.finish_node(); + } + + fn key_value_pair(&mut self) { + self.builder.start_node(KEY_VALUE_PAIR.into()); + self.key(); + if self.peek() == Some(EQUALITY_SIGN) { + self.eat(); + self.trivia(); + if self + .peek() + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME | R_CURLY | R_BRACK | R_PAREN | COMMA + ) + }) + .is_some() + { + self.value(); + } else { + self.builder.token(MISSING.into(), ""); + } + } + + self.builder.finish_node(); + } + + fn key_value_body(&mut self) { + self.builder.start_node(KEY_VALUE_BODY.into()); + while let Some(kind) = self.peek() { + match kind { + WHITESPACE | COMMENT => self.eat(), + WORD => { + self.key_value_pair(); + if self.peek() == Some(COMMA) { + self.eat(); } else { - children.push(self.text(scope)); + break; } } + _ => break, } } - children - } - - fn group(&mut self, kind: GroupKind) -> AstNodeIndex { - let left = self.tokens.next().unwrap(); - let scope = match kind { - GroupKind::Group => Scope::Group, - GroupKind::Options => Scope::Options, - }; - - let children = self.content(scope); - let right_kind = match kind { - GroupKind::Group => TokenKind::EndGroup, - GroupKind::Options => TokenKind::EndOptions, - }; - - let right = if self.next_of_kind(right_kind) { - self.tokens.next() - } else { - None - }; - - let end = right - .as_ref() - .map(SyntaxNode::end) - .or_else(|| children.last().map(|child| self.tree[*child].end())) - .unwrap_or_else(|| left.end()); - let range = Range::new(left.start(), end); - - let node = self.tree.add_node(Node::Group(Group { - range, - left, - kind, - right, - })); - self.connect(node, &children); - node - } - - fn command(&mut self) -> AstNodeIndex { - let name = self.tokens.next().unwrap(); - let mut children = Vec::new(); - while let Some(token) = self.tokens.peek() { - match token.kind { - TokenKind::BeginGroup => children.push(self.group(GroupKind::Group)), - TokenKind::BeginOptions => children.push(self.group(GroupKind::Options)), + self.builder.finish_node(); + } + + fn group_key_value(&mut self, node_kind: SyntaxKind, right_kind: SyntaxKind) { + self.builder.start_node(node_kind.into()); + self.eat(); + self.trivia(); + self.key_value_body(); + self.expect(right_kind); + self.builder.finish_node(); + } + + fn curly_group_key_value(&mut self) { + self.group_key_value(CURLY_GROUP_KEY_VALUE, R_CURLY); + } + + fn brack_group_key_value(&mut self) { + self.group_key_value(BRACK_GROUP_KEY_VALUE, R_BRACK); + } + + fn formula(&mut self) { + self.builder.start_node(FORMULA.into()); + self.eat(); + self.trivia(); + while self + .peek() + .filter(|&kind| !matches!(kind, R_CURLY | END_ENVIRONMENT_NAME | DOLLAR)) + .is_some() + { + self.content(ParserContext::default()); + } + self.expect(DOLLAR); + self.builder.finish_node(); + } + + fn generic_command(&mut self) { + self.builder.start_node(GENERIC_COMMAND.into()); + self.eat(); + while let Some(kind) = self.peek() { + match kind { + WHITESPACE | COMMENT => self.eat(), + L_CURLY => self.curly_group(), + L_BRACK | L_PAREN => self.mixed_group(), _ => break, } } + self.builder.finish_node(); + } + + fn equation(&mut self) { + self.builder.start_node(EQUATION.into()); + self.eat(); + while self + .peek() + .filter(|&kind| !matches!(kind, END_ENVIRONMENT_NAME | R_CURLY | END_EQUATION_NAME)) + .is_some() + { + self.content(ParserContext::default()); + } + self.expect(END_EQUATION_NAME); + self.builder.finish_node(); + } + + fn begin(&mut self) { + self.builder.start_node(BEGIN.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.peek() == Some(L_BRACK) { + self.brack_group(); + } + self.builder.finish_node(); + } - let end = children - .last() - .map(|child| self.tree[*child].end()) - .unwrap_or_else(|| name.end()); - let range = Range::new(name.start(), end); + fn end(&mut self) { + self.builder.start_node(END.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } - let node = self.tree.add_node(Node::Command(Command { range, name })); - self.connect(node, &children); - node + self.builder.finish_node(); } - fn text(&mut self, scope: Scope) -> AstNodeIndex { - let mut words = Vec::new(); - while let Some(ref token) = self.tokens.peek() { - let kind = token.kind; - let opts = kind == TokenKind::EndOptions && scope != Scope::Options; - if kind == TokenKind::Word || kind == TokenKind::BeginOptions || opts { - words.push(self.tokens.next().unwrap()); - } else { - break; - } + fn environment(&mut self) { + self.builder.start_node(ENVIRONMENT.into()); + self.begin(); + + while self + .peek() + .filter(|&kind| kind != END_ENVIRONMENT_NAME) + .is_some() + { + self.content(ParserContext::default()); + } + + if self.peek() == Some(END_ENVIRONMENT_NAME) { + self.end(); + } else { + self.builder.token(MISSING.into(), ""); + } + self.builder.finish_node(); + } + + fn preamble(&mut self) { + self.builder.start_node(PREAMBLE.into()); + while self + .peek() + .filter(|&kind| kind != END_ENVIRONMENT_NAME) + .is_some() + { + self.content(ParserContext::default()); + } + self.builder.finish_node(); + } + + fn part(&mut self) { + self.builder.start_node(PART.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + while self + .peek() + .filter(|&kind| !matches!(kind, END_ENVIRONMENT_NAME | R_CURLY | PART_NAME)) + .is_some() + { + self.content(ParserContext::default()); + } + self.builder.finish_node(); + } + + fn chapter(&mut self) { + self.builder.start_node(CHAPTER.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME | R_CURLY | PART_NAME | CHAPTER_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); + } + self.builder.finish_node(); + } + + fn section(&mut self) { + self.builder.start_node(SECTION.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME | R_CURLY | PART_NAME | CHAPTER_NAME | SECTION_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); + } + self.builder.finish_node(); + } + + fn subsection(&mut self) { + self.builder.start_node(SUBSECTION.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME + | R_CURLY + | PART_NAME + | CHAPTER_NAME + | SECTION_NAME + | SUBSECTION_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); } - let range = Range::new(words[0].start(), words[words.len() - 1].end()); - self.tree.add_node(Node::Text(Text { range, words })) + self.builder.finish_node(); } - fn comma(&mut self) -> AstNodeIndex { - let token = self.tokens.next().unwrap(); - let range = token.range(); - self.tree.add_node(Node::Comma(Comma { range, token })) + fn subsubsection(&mut self) { + self.builder.start_node(SUBSUBSECTION.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME + | R_CURLY + | PART_NAME + | CHAPTER_NAME + | SECTION_NAME + | SUBSECTION_NAME + | SUBSUBSECTION_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); + } + self.builder.finish_node(); } - fn math(&mut self) -> AstNodeIndex { - let token = self.tokens.next().unwrap(); - let range = token.range(); - self.tree.add_node(Node::Math(Math { range, token })) + fn paragraph(&mut self) { + self.builder.start_node(PARAGRAPH.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME + | R_CURLY + | PART_NAME + | CHAPTER_NAME + | SECTION_NAME + | SUBSECTION_NAME + | SUBSUBSECTION_NAME + | PARAGRAPH_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); + } + self.builder.finish_node(); } - fn connect(&mut self, parent: AstNodeIndex, children: &[AstNodeIndex]) { - for child in children { - self.tree.add_edge(parent, *child); + fn subparagraph(&mut self) { + self.builder.start_node(SUBPARAGRAPH.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + while self + .peek() + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME + | R_CURLY + | PART_NAME + | CHAPTER_NAME + | SECTION_NAME + | SUBSECTION_NAME + | SUBSUBSECTION_NAME + | PARAGRAPH_NAME + | SUBPARAGRAPH_NAME + ) + }) + .is_some() + { + self.content(ParserContext::default()); } + self.builder.finish_node(); } - fn next_of_kind(&mut self, kind: TokenKind) -> bool { - self.tokens + fn enum_item(&mut self) { + self.builder.start_node(ENUM_ITEM.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_BRACK) { + self.brack_group(); + } + + while self .peek() - .filter(|token| token.kind == kind) + .filter(|&kind| { + !matches!( + kind, + END_ENVIRONMENT_NAME + | R_CURLY + | PART_NAME + | CHAPTER_NAME + | SECTION_NAME + | SUBSECTION_NAME + | SUBSUBSECTION_NAME + | PARAGRAPH_NAME + | SUBPARAGRAPH_NAME + | ENUM_ITEM_NAME + ) + }) .is_some() + { + self.content(ParserContext::default()); + } + self.builder.finish_node(); + } + + fn caption(&mut self) { + self.builder.start_node(CAPTION.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(L_BRACK) { + self.brack_group(); + } + + if self.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn citation(&mut self) { + self.builder.start_node(CITATION.into()); + self.eat(); + self.trivia(); + for _ in 0..2 { + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group(); + } + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word_list(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn generic_include(&mut self, kind: SyntaxKind, options: bool) { + self.builder.start_node(kind.into()); + self.eat(); + self.trivia(); + if options && self.lexer.peek() == Some(L_BRACK) { + self.brack_group_key_value(); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word_list(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn package_include(&mut self) { + self.generic_include(PACKAGE_INCLUDE, true); + } + + fn class_include(&mut self) { + self.generic_include(CLASS_INCLUDE, true); + } + + fn latex_include(&mut self) { + self.generic_include(LATEX_INCLUDE, false); + } + + fn biblatex_include(&mut self) { + self.generic_include(BIBLATEX_INCLUDE, true); + } + + fn bibtex_include(&mut self) { + self.generic_include(BIBTEX_INCLUDE, false); + } + + fn graphics_include(&mut self) { + self.generic_include(GRAPHICS_INCLUDE, true); + } + + fn svg_include(&mut self) { + self.generic_include(SVG_INCLUDE, true); + } + + fn inkscape_include(&mut self) { + self.generic_include(INKSCAPE_INCLUDE, true); + } + + fn verbatim_include(&mut self) { + self.generic_include(VERBATIM_INCLUDE, false); + } + + fn import(&mut self) { + self.builder.start_node(IMPORT.into()); + self.eat(); + self.trivia(); + + for _ in 0..2 { + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + } + + self.builder.finish_node(); + } + + fn label_definition(&mut self) { + self.builder.start_node(LABEL_DEFINITION.into()); + self.eat(); + self.trivia(); + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + self.builder.finish_node(); + } + + fn label_reference(&mut self) { + self.builder.start_node(LABEL_REFERENCE.into()); + self.eat(); + self.trivia(); + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word_list(); + } else { + self.builder.token(MISSING.into(), ""); + } + self.builder.finish_node(); + } + + fn label_reference_range(&mut self) { + self.builder.start_node(LABEL_REFERENCE_RANGE.into()); + self.eat(); + self.trivia(); + + for _ in 0..2 { + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + } + + self.builder.finish_node(); + } + + fn label_number(&mut self) { + self.builder.start_node(LABEL_NUMBER.into()); + self.eat(); + self.trivia(); + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group(); + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn command_definition(&mut self) { + self.builder.start_node(COMMAND_DEFINITION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_command(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_word(); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn math_operator(&mut self) { + self.builder.start_node(MATH_OPERATOR.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_command(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn glossary_entry_definition(&mut self) { + self.builder.start_node(GLOSSARY_ENTRY_DEFINITION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_key_value(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn glossary_entry_reference(&mut self) { + self.builder.start_node(GLOSSARY_ENTRY_REFERENCE.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_key_value(); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn acronym_definition(&mut self) { + self.builder.start_node(ACRONYM_DEFINITION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_key_value(); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + for _ in 0..2 { + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + } + + self.builder.finish_node(); + } + + fn acronym_declaration(&mut self) { + self.builder.start_node(ACRONYM_DECLARATION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_key_value(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn acronym_reference(&mut self) { + self.builder.start_node(ACRONYM_REFERENCE.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_key_value(); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn theorem_definition(&mut self) { + self.builder.start_node(THEOREM_DEFINITION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_word(); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_word(); + } + + self.builder.finish_node(); + } + + fn color_reference(&mut self) { + self.builder.start_node(COLOR_REFERENCE.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn color_definition(&mut self) { + self.builder.start_node(COLOR_DEFINITION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn color_set_definition(&mut self) { + self.builder.start_node(COLOR_SET_DEFINITION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_word(); + } + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word_list(); + } else { + self.builder.token(MISSING.into(), ""); + } + + for _ in 0..3 { + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + } + + self.builder.finish_node(); + } + + fn tikz_library_import(&mut self) { + self.builder.start_node(TIKZ_LIBRARY_IMPORT.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word_list(); + } else { + self.builder.token(MISSING.into(), ""); + } + + self.builder.finish_node(); + } + + fn environment_definition(&mut self) { + self.builder.start_node(ENVIRONMENT_DEFINITION.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_word(); + } else { + self.builder.token(MISSING.into(), ""); + } + + if self.lexer.peek() == Some(L_BRACK) { + self.brack_group_word(); + } + + for _ in 0..2 { + if self.lexer.peek() == Some(L_CURLY) { + self.curly_group_without_environments(); + } else { + self.builder.token(MISSING.into(), ""); + } + } + + self.builder.finish_node(); + } +} + +pub fn parse(text: &str) -> Parse { + Parser::new(text).parse() +} + +#[cfg(test)] +mod tests { + use insta::assert_debug_snapshot; + + use super::*; + + fn setup(text: &str) -> SyntaxNode { + parse(&text.trim().replace("\r", "")).root + } + + #[test] + fn test_empty() { + assert_debug_snapshot!(setup(r#""#)); + } + + #[test] + fn test_hello_world() { + assert_debug_snapshot!(setup(r#"Hello World!"#)); + } + + #[test] + fn test_generic_command_empty() { + assert_debug_snapshot!(setup(r#"\foo"#)); + } + + #[test] + fn test_generic_command_escape() { + assert_debug_snapshot!(setup(r#"\#"#)); + } + + #[test] + fn test_generic_command_args() { + assert_debug_snapshot!(setup(r#"\foo{bar}[qux]"#)); + } + + #[test] + fn test_inline() { + assert_debug_snapshot!(setup(r#"$x \in [0, \infty)$"#)); + } + + #[test] + fn test_inline_double_dollar() { + assert_debug_snapshot!(setup(r#"$$x \in [0, \infty)$$"#)); + } + + #[test] + fn test_brace_group_simple() { + assert_debug_snapshot!(setup(r#"{hello world}"#)); + } + + #[test] + fn test_brace_group_missing_end() { + assert_debug_snapshot!(setup(r#"{hello world"#)); + } + + #[test] + fn test_unmatched_braces() { + assert_debug_snapshot!(setup(r#"}{"#)); + } + + #[test] + fn test_unmatched_brackets() { + assert_debug_snapshot!(setup(r#"]["#)); + } + + #[test] + fn test_unmatched_brackets_with_group() { + assert_debug_snapshot!(setup(r#"{][}"#)); + } + + #[test] + fn test_escaped_brackets() { + assert_debug_snapshot!(setup(r#"{[}{]}"#)); + } + + #[test] + fn test_parameter() { + assert_debug_snapshot!(setup(r#"#1"#)); + } + + #[test] + fn test_parameter_error() { + assert_debug_snapshot!(setup(r#"#"#)); + } + + #[test] + fn test_environment_simple() { + assert_debug_snapshot!(setup(r#"\begin{foo} Hello World \end{bar}"#)); + } + + #[test] + fn test_environment_nested() { + assert_debug_snapshot!(setup(r#"\begin{foo} \begin{qux} \end{baz} \end{bar}"#)); + } + + #[test] + fn test_environment_nested_missing_braces() { + assert_debug_snapshot!(setup( + r#"\begin{foo \begin{qux Hello World \end{baz} \end{bar"# + )); + } + + #[test] + fn test_structure_siblings() { + assert_debug_snapshot!(setup(r#"\section{Foo} Foo \section{Bar} Bar"#)); + } + + #[test] + fn test_structure_nested() { + assert_debug_snapshot!(setup( + r#"\part{1}\chapter{2}\section{3}\subsection{4}\subsubsection{5}\paragraph{6}\subparagraph{7}"# + )); + } + + #[test] + fn test_structure_enum_item() { + assert_debug_snapshot!(setup( + r#"\begin{enumerate} \item 1 \item[2] 2 \item 3 \end{enumerate}"# + )); + } + + #[test] + fn test_structure_invalid_nesting() { + assert_debug_snapshot!(setup(r#"\section{Foo} \chapter{Bar}"#)); + } + + #[test] + fn test_equation() { + assert_debug_snapshot!(setup(r#"\[ foo bar \]"#)); + } + + #[test] + fn test_equation_missing_end() { + assert_debug_snapshot!(setup(r#"\begin{a} \[ foo bar \end{b}"#)); + } + + #[test] + fn test_equation_missing_begin() { + assert_debug_snapshot!(setup(r#"\begin{a} foo bar \] \end{b}"#)); + } + + #[test] + fn test_caption_minimal() { + assert_debug_snapshot!(setup(r#"\caption{Foo \Bar Baz}"#)); + } + + #[test] + fn test_caption_minimal_error() { + assert_debug_snapshot!(setup(r#"\caption{Foo \Bar Baz"#)); + } + + #[test] + fn test_caption() { + assert_debug_snapshot!(setup(r#"\caption[qux]{Foo \Bar Baz}"#)); + } + + #[test] + fn test_caption_error() { + assert_debug_snapshot!(setup(r#"\caption[qux]{Foo \Bar Baz"#)); + } + + #[test] + fn test_caption_figure() { + assert_debug_snapshot!(setup(r#"\begin{figure}\caption{Foo}\end{figure}"#)); + } + + #[test] + fn test_citation_empty() { + assert_debug_snapshot!(setup(r#"\cite{}"#)); + } + + #[test] + fn test_citation_simple() { + assert_debug_snapshot!(setup(r#"\cite{foo}"#)); + } + + #[test] + fn test_citation_multiple_keys() { + assert_debug_snapshot!(setup(r#"\cite{foo, bar}"#)); + } + + #[test] + fn test_citation_star() { + assert_debug_snapshot!(setup(r#"\nocite{*}"#)); + } + + #[test] + fn test_citation_prenote() { + assert_debug_snapshot!(setup(r#"\cite[foo]{bar}"#)); + } + + #[test] + fn test_citation_prenote_postnote() { + assert_debug_snapshot!(setup(r#"\cite[foo][bar]{baz}"#)); + } + + #[test] + fn test_citation_missing_brace() { + assert_debug_snapshot!(setup(r#"\cite{foo"#)); + } + + #[test] + fn test_citation_redundant_comma() { + assert_debug_snapshot!(setup(r#"\cite{,foo,}"#)); + } + + #[test] + fn test_package_include_empty() { + assert_debug_snapshot!(setup(r#"\usepackage{}"#)); + } + + #[test] + fn test_package_include_simple() { + assert_debug_snapshot!(setup(r#"\usepackage{amsmath}"#)); + } + + #[test] + fn test_package_include_multiple() { + assert_debug_snapshot!(setup(r#"\usepackage{amsmath, lipsum}"#)); + } + + #[test] + fn test_package_include_options() { + assert_debug_snapshot!(setup(r#"\usepackage[foo = bar, baz, qux]{amsmath}"#)); + } + + #[test] + fn test_class_include_empty() { + assert_debug_snapshot!(setup(r#"\documentclass{}"#)); + } + + #[test] + fn test_class_include_simple() { + assert_debug_snapshot!(setup(r#"\documentclass{article}"#)); + } + + #[test] + fn test_class_include_options() { + assert_debug_snapshot!(setup(r#"\documentclass[foo = bar, baz, qux]{article}"#)); + } + + #[test] + fn test_latex_include_simple() { + assert_debug_snapshot!(setup(r#"\include{foo/bar}"#)); + } + + #[test] + fn test_latex_input_simple() { + assert_debug_snapshot!(setup(r#"\input{foo/bar.tex}"#)); + } + + #[test] + fn test_biblatex_include_simple() { + assert_debug_snapshot!(setup(r#"\addbibresource{foo/bar.bib}"#)); + } + + #[test] + fn test_biblatex_include_options() { + assert_debug_snapshot!(setup(r#"\addbibresource[foo=bar, baz]{foo/bar.bib}"#)); + } + + #[test] + fn test_bibtex_include_simple() { + assert_debug_snapshot!(setup(r#"\bibliography{foo/bar}"#)); + } + + #[test] + fn test_graphics_include_simple() { + assert_debug_snapshot!(setup(r#"\includegraphics{foo/bar.pdf}"#)); + } + + #[test] + fn test_graphics_include_options() { + assert_debug_snapshot!(setup(r#"\includegraphics[scale=.5]{foo/bar.pdf}"#)); + } + + #[test] + fn test_svg_include_simple() { + assert_debug_snapshot!(setup(r#"\includesvg{foo/bar.svg}"#)); + } + + #[test] + fn test_svg_include_options() { + assert_debug_snapshot!(setup(r#"\includesvg[scale=.5]{foo/bar.svg}"#)); + } + + #[test] + fn test_inkscape_include_simple() { + assert_debug_snapshot!(setup(r#"\includesvg{foo/bar}"#)); + } + + #[test] + fn test_inkscape_include_options() { + assert_debug_snapshot!(setup(r#"\includesvg[scale=.5]{foo/bar}"#)); + } + + #[test] + fn test_verbatim_include_simple() { + assert_debug_snapshot!(setup(r#"\verbatiminput{foo/bar.txt}"#)); + } + + #[test] + fn test_import_simple() { + assert_debug_snapshot!(setup(r#"\import{foo}{bar}"#)); + } + + #[test] + fn test_import_incomplete() { + assert_debug_snapshot!(setup(r#"\import{foo"#)); + } + + #[test] + fn test_label_definition_simple() { + assert_debug_snapshot!(setup(r#"\label{foo}"#)); + } + + #[test] + fn test_label_reference_simple() { + assert_debug_snapshot!(setup(r#"\ref{foo}"#)); + } + + #[test] + fn test_label_reference_multiple() { + assert_debug_snapshot!(setup(r#"\ref{foo, bar}"#)); + } + + #[test] + fn test_equation_label_reference_simple() { + assert_debug_snapshot!(setup(r#"\eqref{foo}"#)); + } + + #[test] + fn test_label_reference_range_simple() { + assert_debug_snapshot!(setup(r#"\crefrange{foo}{bar}"#)); + } + + #[test] + fn test_label_reference_range_incomplete() { + assert_debug_snapshot!(setup(r#"\crefrange{foo}"#)); + } + + #[test] + fn test_label_reference_range_error() { + assert_debug_snapshot!(setup(r#"\crefrange{foo{bar}"#)); + } + + #[test] + fn test_label_number() { + assert_debug_snapshot!(setup(r#"\newlabel{foo}{{1.1}}"#)); + } + + #[test] + fn test_command_definition_simple() { + assert_debug_snapshot!(setup(r#"\newcommand[1]{\id}{#1}"#)); + } + + #[test] + fn test_command_definition_no_argc() { + assert_debug_snapshot!(setup(r#"\newcommand{\foo}{foo}"#)); + } + + #[test] + fn test_command_definition_no_impl() { + assert_debug_snapshot!(setup(r#"\newcommand{\foo}"#)); + } + + #[test] + fn test_command_definition_no_impl_error() { + assert_debug_snapshot!(setup(r#"\newcommand{\foo"#)); + } + + #[test] + fn test_math_operator_simple() { + assert_debug_snapshot!(setup(r#"\DeclareMathOperator{\foo}{foo}"#)); + } + + #[test] + fn test_math_operator_no_impl() { + assert_debug_snapshot!(setup(r#"\DeclareMathOperator{\foo}"#)); + } + + #[test] + fn test_glossary_entry_definition_simple() { + assert_debug_snapshot!(setup(r#"\newglossaryentry{foo}{bar = baz, qux,}"#)); + } + + #[test] + fn test_glossary_entry_reference_simple() { + assert_debug_snapshot!(setup(r#"\gls{foo}"#)); + } + + #[test] + fn test_glossary_entry_reference_options() { + assert_debug_snapshot!(setup(r#"\gls[foo = bar, qux]{baz}"#)); + } + + #[test] + fn test_acroynm_definition_simple() { + assert_debug_snapshot!(setup(r#"\newacronym{fpsLabel}{FPS}{Frame per Second}"#)); + } + + #[test] + fn test_acroynm_definition_options() { + assert_debug_snapshot!(setup( + r#"\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}"# + )); + } + + #[test] + fn test_acroynm_reference_simple() { + assert_debug_snapshot!(setup(r#"\acrshort{fpsLabel}"#)); + } + + #[test] + fn test_acroynm_reference_options() { + assert_debug_snapshot!(setup(r#"\acrshort[foo=bar,baz]{fpsLabel}"#)); + } + + #[test] + fn test_theorem_definition_only_name() { + assert_debug_snapshot!(setup(r#"\newtheorem{foo}"#)); + } + + #[test] + fn test_theorem_definition_name_with_description() { + assert_debug_snapshot!(setup(r#"\newtheorem{foo}{Foo}"#)); + } + + #[test] + fn test_theorem_definition_name_with_description_and_counter() { + assert_debug_snapshot!(setup(r#"\newtheorem{foo}[bar]{Foo}"#)); + } + + #[test] + fn test_theorem_definition_name_with_counter() { + assert_debug_snapshot!(setup(r#"\newtheorem{foo}[bar]"#)); + } + + #[test] + fn test_theorem_definition_full() { + assert_debug_snapshot!(setup(r#"\newtheorem{foo}[bar]{Foo}[baz]"#)); + } + + #[test] + fn test_color_reference_simple() { + assert_debug_snapshot!(setup(r#"\color{black}"#)); + } + + #[test] + fn test_color_definition_simple() { + assert_debug_snapshot!(setup(r#"\definecolor{foo}{rgb}{255,168,0}"#)); + } + + #[test] + fn test_color_set_definition_simple() { + assert_debug_snapshot!(setup(r#"\definecolorset[ty]{rgb,HTML}{foo}{bar}{baz}"#)); + } + + #[test] + fn test_color_set_definition_error1() { + assert_debug_snapshot!(setup(r#"\definecolorset[ty]{rgb,HTML}{foo}{bar}"#)); + } + + #[test] + fn test_color_set_definition_error2() { + assert_debug_snapshot!(setup(r#"\definecolorset{rgb,HTML}{foo}"#)); + } + + #[test] + fn test_color_set_definition_error3() { + assert_debug_snapshot!(setup(r#"\definecolorset{rgb,HTML}"#)); + } + + #[test] + fn test_color_set_definition_error4() { + assert_debug_snapshot!(setup(r#"\definecolorset"#)); + } + + #[test] + fn test_pgf_library_import_simple() { + assert_debug_snapshot!(setup(r#"\usepgflibrary{foo}"#)); + } + + #[test] + fn test_tikz_library_import_simple() { + assert_debug_snapshot!(setup(r#"\usetikzlibrary{foo}"#)); + } + + #[test] + fn test_environment_definition() { + assert_debug_snapshot!(setup(r#"\newenvironment{bar}[1]{\begin{foo}}{\end{foo}}"#)); + } + + #[test] + fn test_acronym_declaration() { + assert_debug_snapshot!(setup( + r#"\DeclareAcronym{eg}{short = e.g,long = for example,tag = abbrev}"# + )); } } -- cgit v1.2.3