diff options
Diffstat (limited to 'support/texlab/src/syntax/bibtex/ast.rs')
-rw-r--r-- | support/texlab/src/syntax/bibtex/ast.rs | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/support/texlab/src/syntax/bibtex/ast.rs b/support/texlab/src/syntax/bibtex/ast.rs new file mode 100644 index 0000000000..09f2ee5b88 --- /dev/null +++ b/support/texlab/src/syntax/bibtex/ast.rs @@ -0,0 +1,577 @@ +use crate::range::RangeExt; +use crate::syntax::text::{Span, SyntaxNode}; +use lsp_types::Range; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum BibtexTokenKind { + PreambleKind, + StringKind, + EntryKind, + Word, + Command, + Assign, + Comma, + Concat, + Quote, + BeginBrace, + EndBrace, + BeginParen, + EndParen, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexToken { + pub span: Span, + pub kind: BibtexTokenKind, +} + +impl BibtexToken { + pub fn new(span: Span, kind: BibtexTokenKind) -> Self { + BibtexToken { span, kind } + } + + pub fn text(&self) -> &str { + &self.span.text + } +} + +impl SyntaxNode for BibtexToken { + fn range(&self) -> Range { + self.span.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexRoot { + pub children: Vec<BibtexDeclaration>, +} + +impl BibtexRoot { + pub fn new(children: Vec<BibtexDeclaration>) -> Self { + BibtexRoot { children } + } +} + +impl SyntaxNode for BibtexRoot { + fn range(&self) -> Range { + if self.children.is_empty() { + Range::new_simple(0, 0, 0, 0) + } else { + Range::new( + self.children[0].start(), + self.children[self.children.len() - 1].end(), + ) + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum BibtexDeclaration { + Comment(Box<BibtexComment>), + Preamble(Box<BibtexPreamble>), + String(Box<BibtexString>), + Entry(Box<BibtexEntry>), +} + +impl BibtexDeclaration { + pub fn accept<'a, T: BibtexVisitor<'a>>(&'a self, visitor: &mut T) { + match self { + BibtexDeclaration::Comment(comment) => visitor.visit_comment(comment), + BibtexDeclaration::Preamble(preamble) => visitor.visit_preamble(preamble), + BibtexDeclaration::String(string) => visitor.visit_string(string), + BibtexDeclaration::Entry(entry) => visitor.visit_entry(entry), + } + } +} + +impl SyntaxNode for BibtexDeclaration { + fn range(&self) -> Range { + match self { + BibtexDeclaration::Comment(comment) => comment.range, + BibtexDeclaration::Preamble(preamble) => preamble.range, + BibtexDeclaration::String(string) => string.range, + BibtexDeclaration::Entry(entry) => entry.range, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexComment { + pub range: Range, + pub token: BibtexToken, +} + +impl BibtexComment { + pub fn new(token: BibtexToken) -> Self { + BibtexComment { + range: token.range(), + token, + } + } +} + +impl SyntaxNode for BibtexComment { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexPreamble { + pub range: Range, + pub ty: BibtexToken, + pub left: Option<BibtexToken>, + pub content: Option<BibtexContent>, + pub right: Option<BibtexToken>, +} + +impl BibtexPreamble { + pub fn new( + ty: BibtexToken, + left: Option<BibtexToken>, + content: Option<BibtexContent>, + right: Option<BibtexToken>, + ) -> Self { + let end = if let Some(ref right) = right { + right.end() + } else if let Some(ref content) = content { + content.end() + } else if let Some(ref left) = left { + left.end() + } else { + ty.end() + }; + BibtexPreamble { + range: Range::new(ty.start(), end), + ty, + left, + content, + right, + } + } +} + +impl SyntaxNode for BibtexPreamble { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexString { + pub range: Range, + pub ty: BibtexToken, + pub left: Option<BibtexToken>, + pub name: Option<BibtexToken>, + pub assign: Option<BibtexToken>, + pub value: Option<BibtexContent>, + pub right: Option<BibtexToken>, +} + +impl BibtexString { + pub fn new( + ty: BibtexToken, + left: Option<BibtexToken>, + name: Option<BibtexToken>, + assign: Option<BibtexToken>, + value: Option<BibtexContent>, + right: Option<BibtexToken>, + ) -> Self { + let end = if let Some(ref right) = right { + right.end() + } else if let Some(ref value) = value { + value.end() + } else if let Some(ref assign) = assign { + assign.end() + } else if let Some(ref name) = name { + name.end() + } else if let Some(ref left) = left { + left.end() + } else { + ty.end() + }; + + BibtexString { + range: Range::new(ty.start(), end), + ty, + left, + name, + assign, + value, + right, + } + } +} + +impl SyntaxNode for BibtexString { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexEntry { + pub range: Range, + pub ty: BibtexToken, + pub left: Option<BibtexToken>, + pub key: Option<BibtexToken>, + pub comma: Option<BibtexToken>, + pub fields: Vec<BibtexField>, + pub right: Option<BibtexToken>, +} + +impl BibtexEntry { + pub fn new( + ty: BibtexToken, + left: Option<BibtexToken>, + key: Option<BibtexToken>, + comma: Option<BibtexToken>, + fields: Vec<BibtexField>, + right: Option<BibtexToken>, + ) -> Self { + let end = if let Some(ref right) = right { + right.end() + } else if !fields.is_empty() { + fields[fields.len() - 1].range.end + } else if let Some(ref comma) = comma { + comma.end() + } else if let Some(ref key) = key { + key.end() + } else if let Some(ref left) = left { + left.end() + } else { + ty.end() + }; + + BibtexEntry { + range: Range::new(ty.start(), end), + ty, + left, + key, + comma, + fields, + right, + } + } + + pub fn is_comment(&self) -> bool { + self.ty.text().to_lowercase() == "@comment" + } + + pub fn find_field(&self, name: &str) -> Option<&BibtexField> { + self.fields + .iter() + .find(|field| field.name.text().to_lowercase() == name) + } +} + +impl SyntaxNode for BibtexEntry { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexField { + pub range: Range, + pub name: BibtexToken, + pub assign: Option<BibtexToken>, + pub content: Option<BibtexContent>, + pub comma: Option<BibtexToken>, +} + +impl BibtexField { + pub fn new( + name: BibtexToken, + assign: Option<BibtexToken>, + content: Option<BibtexContent>, + comma: Option<BibtexToken>, + ) -> Self { + let end = if let Some(ref comma) = comma { + comma.end() + } else if let Some(ref content) = content { + content.end() + } else if let Some(ref assign) = assign { + assign.end() + } else { + name.end() + }; + + BibtexField { + range: Range::new(name.start(), end), + name, + assign, + content, + comma, + } + } +} + +impl SyntaxNode for BibtexField { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum BibtexContent { + Word(BibtexWord), + Command(BibtexCommand), + QuotedContent(BibtexQuotedContent), + BracedContent(BibtexBracedContent), + Concat(Box<BibtexConcat>), +} + +impl BibtexContent { + pub fn accept<'a, T: BibtexVisitor<'a>>(&'a self, visitor: &mut T) { + match self { + BibtexContent::Word(word) => visitor.visit_word(word), + BibtexContent::Command(command) => visitor.visit_command(command), + BibtexContent::QuotedContent(content) => visitor.visit_quoted_content(content), + BibtexContent::BracedContent(content) => visitor.visit_braced_content(content), + BibtexContent::Concat(concat) => visitor.visit_concat(concat), + } + } +} + +impl SyntaxNode for BibtexContent { + fn range(&self) -> Range { + match self { + BibtexContent::Word(word) => word.range(), + BibtexContent::Command(command) => command.range(), + BibtexContent::QuotedContent(content) => content.range(), + BibtexContent::BracedContent(content) => content.range(), + BibtexContent::Concat(concat) => concat.range(), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexWord { + pub range: Range, + pub token: BibtexToken, +} + +impl BibtexWord { + pub fn new(token: BibtexToken) -> Self { + BibtexWord { + range: token.range(), + token, + } + } +} + +impl SyntaxNode for BibtexWord { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexCommand { + pub range: Range, + pub token: BibtexToken, +} + +impl BibtexCommand { + pub fn new(token: BibtexToken) -> Self { + BibtexCommand { + range: token.range(), + token, + } + } +} + +impl SyntaxNode for BibtexCommand { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexQuotedContent { + pub range: Range, + pub left: BibtexToken, + pub children: Vec<BibtexContent>, + pub right: Option<BibtexToken>, +} + +impl BibtexQuotedContent { + pub fn new( + left: BibtexToken, + children: Vec<BibtexContent>, + right: Option<BibtexToken>, + ) -> Self { + let end = if let Some(ref right) = right { + right.end() + } else if !children.is_empty() { + children[children.len() - 1].end() + } else { + left.end() + }; + + BibtexQuotedContent { + range: Range::new(left.start(), end), + left, + children, + right, + } + } +} + +impl SyntaxNode for BibtexQuotedContent { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexBracedContent { + pub range: Range, + pub left: BibtexToken, + pub children: Vec<BibtexContent>, + pub right: Option<BibtexToken>, +} + +impl BibtexBracedContent { + pub fn new( + left: BibtexToken, + children: Vec<BibtexContent>, + right: Option<BibtexToken>, + ) -> Self { + let end = if let Some(ref right) = right { + right.end() + } else if !children.is_empty() { + children[children.len() - 1].end() + } else { + left.end() + }; + + BibtexBracedContent { + range: Range::new(left.start(), end), + left, + children, + right, + } + } +} + +impl SyntaxNode for BibtexBracedContent { + fn range(&self) -> Range { + self.range + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexConcat { + pub range: Range, + pub left: BibtexContent, + pub operator: BibtexToken, + pub right: Option<BibtexContent>, +} + +impl BibtexConcat { + pub fn new(left: BibtexContent, operator: BibtexToken, right: Option<BibtexContent>) -> Self { + let end = if let Some(ref right) = right { + right.end() + } else { + operator.end() + }; + + BibtexConcat { + range: Range::new(left.start(), end), + left, + operator, + right, + } + } +} + +impl SyntaxNode for BibtexConcat { + fn range(&self) -> Range { + self.range + } +} + +pub trait BibtexVisitor<'a> { + fn visit_root(&mut self, root: &'a BibtexRoot); + + fn visit_comment(&mut self, comment: &'a BibtexComment); + + fn visit_preamble(&mut self, preamble: &'a BibtexPreamble); + + fn visit_string(&mut self, string: &'a BibtexString); + + fn visit_entry(&mut self, entry: &'a BibtexEntry); + + fn visit_field(&mut self, field: &'a BibtexField); + + fn visit_word(&mut self, word: &'a BibtexWord); + + fn visit_command(&mut self, command: &'a BibtexCommand); + + fn visit_quoted_content(&mut self, content: &'a BibtexQuotedContent); + + fn visit_braced_content(&mut self, content: &'a BibtexBracedContent); + + fn visit_concat(&mut self, concat: &'a BibtexConcat); +} + +pub struct BibtexWalker; + +impl BibtexWalker { + pub fn walk_root<'a, T: BibtexVisitor<'a>>(visitor: &mut T, root: &'a BibtexRoot) { + for declaration in &root.children { + declaration.accept(visitor); + } + } + + pub fn walk_preamble<'a, T: BibtexVisitor<'a>>(visitor: &mut T, preamble: &'a BibtexPreamble) { + if let Some(ref content) = preamble.content { + content.accept(visitor); + } + } + + pub fn walk_string<'a, T: BibtexVisitor<'a>>(visitor: &mut T, string: &'a BibtexString) { + if let Some(ref value) = string.value { + value.accept(visitor); + } + } + + pub fn walk_entry<'a, T: BibtexVisitor<'a>>(visitor: &mut T, entry: &'a BibtexEntry) { + for field in &entry.fields { + visitor.visit_field(field); + } + } + + pub fn walk_field<'a, T: BibtexVisitor<'a>>(visitor: &mut T, field: &'a BibtexField) { + if let Some(ref content) = field.content { + content.accept(visitor); + } + } + + pub fn walk_quoted_content<'a, T: BibtexVisitor<'a>>( + visitor: &mut T, + content: &'a BibtexQuotedContent, + ) { + for child in &content.children { + child.accept(visitor); + } + } + + pub fn walk_braced_content<'a, T: BibtexVisitor<'a>>( + visitor: &mut T, + content: &'a BibtexBracedContent, + ) { + for child in &content.children { + child.accept(visitor); + } + } + + pub fn walk_concat<'a, T: BibtexVisitor<'a>>(visitor: &mut T, concat: &'a BibtexConcat) { + concat.left.accept(visitor); + if let Some(ref right) = concat.right { + right.accept(visitor); + } + } +} |