mirror of
https://github.com/latex-lsp/texlab.git
synced 2025-12-23 09:19:21 +00:00
Simplify BibTeX parser
This commit is contained in:
parent
afce772da8
commit
3fbbccfc50
4 changed files with 215 additions and 368 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use crate::range;
|
||||
use crate::syntax::text::{Span, SyntaxNode};
|
||||
use lsp_types::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum BibtexTokenKind {
|
||||
|
|
@ -52,43 +52,34 @@ impl BibtexRoot {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait BibtexVisitor<T> {
|
||||
fn visit_comment(&mut self, comment: Rc<BibtexComment>) -> T;
|
||||
|
||||
fn visit_preamble(&mut self, preamble: Rc<BibtexPreamble>) -> T;
|
||||
|
||||
fn visit_string(&mut self, string: Rc<BibtexString>) -> T;
|
||||
|
||||
fn visit_entry(&mut self, entry: Rc<BibtexEntry>) -> T;
|
||||
|
||||
fn visit_field(&mut self, field: Rc<BibtexField>) -> T;
|
||||
|
||||
fn visit_word(&mut self, word: Rc<BibtexWord>) -> T;
|
||||
|
||||
fn visit_command(&mut self, command: Rc<BibtexCommand>) -> T;
|
||||
|
||||
fn visit_quoted_content(&mut self, content: Rc<BibtexQuotedContent>) -> T;
|
||||
|
||||
fn visit_braced_content(&mut self, content: Rc<BibtexBracedContent>) -> T;
|
||||
|
||||
fn visit_concat(&mut self, concat: Rc<BibtexConcat>) -> T;
|
||||
impl SyntaxNode for BibtexRoot {
|
||||
fn range(&self) -> Range {
|
||||
if self.children.is_empty() {
|
||||
range::create(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(Rc<BibtexComment>),
|
||||
Preamble(Rc<BibtexPreamble>),
|
||||
String(Rc<BibtexString>),
|
||||
Entry(Rc<BibtexEntry>),
|
||||
Comment(BibtexComment),
|
||||
Preamble(BibtexPreamble),
|
||||
String(BibtexString),
|
||||
Entry(BibtexEntry),
|
||||
}
|
||||
|
||||
impl BibtexDeclaration {
|
||||
pub fn accept<T>(&self, visitor: &mut BibtexVisitor<T>) -> T {
|
||||
pub fn accept<'a>(&'a self, visitor: &mut BibtexVisitor<'a>) {
|
||||
match self {
|
||||
BibtexDeclaration::Comment(comment) => visitor.visit_comment(comment.clone()),
|
||||
BibtexDeclaration::Preamble(preamble) => visitor.visit_preamble(preamble.clone()),
|
||||
BibtexDeclaration::String(string) => visitor.visit_string(string.clone()),
|
||||
BibtexDeclaration::Entry(entry) => visitor.visit_entry(entry.clone()),
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -225,7 +216,7 @@ pub struct BibtexEntry {
|
|||
pub left: Option<BibtexToken>,
|
||||
pub key: Option<BibtexToken>,
|
||||
pub comma: Option<BibtexToken>,
|
||||
pub fields: Vec<Rc<BibtexField>>,
|
||||
pub fields: Vec<BibtexField>,
|
||||
pub right: Option<BibtexToken>,
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +226,7 @@ impl BibtexEntry {
|
|||
left: Option<BibtexToken>,
|
||||
key: Option<BibtexToken>,
|
||||
comma: Option<BibtexToken>,
|
||||
fields: Vec<Rc<BibtexField>>,
|
||||
fields: Vec<BibtexField>,
|
||||
right: Option<BibtexToken>,
|
||||
) -> Self {
|
||||
let end = if let Some(ref right) = right {
|
||||
|
|
@ -314,21 +305,21 @@ impl SyntaxNode for BibtexField {
|
|||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum BibtexContent {
|
||||
Word(Rc<BibtexWord>),
|
||||
Command(Rc<BibtexCommand>),
|
||||
QuotedContent(Rc<BibtexQuotedContent>),
|
||||
BracedContent(Rc<BibtexBracedContent>),
|
||||
Concat(Rc<BibtexConcat>),
|
||||
Word(BibtexWord),
|
||||
Command(BibtexCommand),
|
||||
QuotedContent(BibtexQuotedContent),
|
||||
BracedContent(BibtexBracedContent),
|
||||
Concat(Box<BibtexConcat>),
|
||||
}
|
||||
|
||||
impl BibtexContent {
|
||||
pub fn accept<T>(&self, visitor: &mut BibtexVisitor<T>) -> T {
|
||||
pub fn accept<'a>(&'a self, visitor: &mut BibtexVisitor<'a>) {
|
||||
match self {
|
||||
BibtexContent::Word(word) => visitor.visit_word(word.clone()),
|
||||
BibtexContent::Command(command) => visitor.visit_command(command.clone()),
|
||||
BibtexContent::QuotedContent(content) => visitor.visit_quoted_content(content.clone()),
|
||||
BibtexContent::BracedContent(content) => visitor.visit_braced_content(content.clone()),
|
||||
BibtexContent::Concat(concat) => visitor.visit_concat(concat.clone()),
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -336,11 +327,11 @@ impl BibtexContent {
|
|||
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,
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -491,3 +482,80 @@ impl SyntaxNode for BibtexConcat {
|
|||
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 {
|
||||
fn walk_root<'a>(visitor: &mut BibtexVisitor<'a>, root: &'a BibtexRoot) {
|
||||
for declaration in &root.children {
|
||||
declaration.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_preamble<'a>(visitor: &mut BibtexVisitor<'a>, preamble: &'a BibtexPreamble) {
|
||||
if let Some(ref content) = preamble.content {
|
||||
content.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_string<'a>(visitor: &mut BibtexVisitor<'a>, string: &'a BibtexString) {
|
||||
if let Some(ref value) = string.value {
|
||||
value.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_entry<'a>(visitor: &mut BibtexVisitor<'a>, entry: &'a BibtexEntry) {
|
||||
for field in &entry.fields {
|
||||
visitor.visit_field(field);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_field<'a>(visitor: &mut BibtexVisitor<'a>, field: &'a BibtexField) {
|
||||
if let Some(ref content) = field.content {
|
||||
content.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_quoted_content<'a>(visitor: &mut BibtexVisitor<'a>, content: &'a BibtexQuotedContent) {
|
||||
for child in &content.children {
|
||||
child.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_braced_content<'a>(visitor: &mut BibtexVisitor<'a>, content: &'a BibtexBracedContent) {
|
||||
for child in &content.children {
|
||||
child.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_concat<'a>(visitor: &mut BibtexVisitor<'a>, concat: &'a BibtexConcat) {
|
||||
concat.left.accept(visitor);
|
||||
if let Some(ref right) = concat.right {
|
||||
right.accept(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,13 @@ pub struct BibtexLexer<'a> {
|
|||
stream: CharStream<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<CharStream<'a>> for BibtexLexer<'a> {
|
||||
fn from(stream: CharStream<'a>) -> Self {
|
||||
BibtexLexer { stream }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for BibtexLexer<'a> {
|
||||
fn from(text: &'a str) -> Self {
|
||||
let stream = CharStream::new(text);
|
||||
BibtexLexer::from(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BibtexLexer<'a> {
|
||||
pub fn new(text: &'a str) -> Self {
|
||||
BibtexLexer {
|
||||
stream: CharStream::new(text),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&mut self) -> BibtexToken {
|
||||
fn is_type_char(c: char) -> bool {
|
||||
c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'
|
||||
|
|
@ -30,7 +23,7 @@ impl<'a> BibtexLexer<'a> {
|
|||
self.stream.next();
|
||||
}
|
||||
let span = self.stream.end_span();
|
||||
let kind = match span.text.as_ref() {
|
||||
let kind = match span.text.to_lowercase().as_ref() {
|
||||
"@preamble" => BibtexTokenKind::PreambleKind,
|
||||
"@string" => BibtexTokenKind::StringKind,
|
||||
_ => BibtexTokenKind::EntryKind,
|
||||
|
|
@ -103,3 +96,81 @@ impl<'a> Iterator for BibtexLexer<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::syntax::text::Span;
|
||||
use lsp_types::{Position, Range};
|
||||
|
||||
fn verify<'a>(
|
||||
lexer: &mut BibtexLexer<'a>,
|
||||
line: u64,
|
||||
character: u64,
|
||||
text: &str,
|
||||
kind: BibtexTokenKind,
|
||||
) {
|
||||
let start = Position::new(line, character);
|
||||
let end = Position::new(line, character + text.chars().count() as u64);
|
||||
let range = Range::new(start, end);
|
||||
let span = Span::new(range, text.to_owned());
|
||||
let token = BibtexToken::new(span, kind);
|
||||
assert_eq!(Some(token), lexer.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_word() {
|
||||
let mut lexer = BibtexLexer::new("foo bar baz");
|
||||
verify(&mut lexer, 0, 0, "foo", BibtexTokenKind::Word);
|
||||
verify(&mut lexer, 0, 4, "bar", BibtexTokenKind::Word);
|
||||
verify(&mut lexer, 0, 8, "baz", BibtexTokenKind::Word);
|
||||
assert_eq!(None, lexer.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command() {
|
||||
let mut lexer = BibtexLexer::new("\\foo\\bar@baz");
|
||||
verify(&mut lexer, 0, 0, "\\foo", BibtexTokenKind::Command);
|
||||
verify(&mut lexer, 0, 4, "\\bar@baz", BibtexTokenKind::Command);
|
||||
assert_eq!(None, lexer.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape_sequence() {
|
||||
let mut lexer = BibtexLexer::new("\\foo*\n\\%\\**");
|
||||
verify(&mut lexer, 0, 0, "\\foo*", BibtexTokenKind::Command);
|
||||
verify(&mut lexer, 1, 0, "\\%", BibtexTokenKind::Command);
|
||||
verify(&mut lexer, 1, 2, "\\*", BibtexTokenKind::Command);
|
||||
verify(&mut lexer, 1, 4, "*", BibtexTokenKind::Word);
|
||||
assert_eq!(None, lexer.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter() {
|
||||
let mut lexer = BibtexLexer::new("{}()\"");
|
||||
verify(&mut lexer, 0, 0, "{", BibtexTokenKind::BeginBrace);
|
||||
verify(&mut lexer, 0, 1, "}", BibtexTokenKind::EndBrace);
|
||||
verify(&mut lexer, 0, 2, "(", BibtexTokenKind::BeginParen);
|
||||
verify(&mut lexer, 0, 3, ")", BibtexTokenKind::EndParen);
|
||||
verify(&mut lexer, 0, 4, "\"", BibtexTokenKind::Quote);
|
||||
assert_eq!(None, lexer.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kind() {
|
||||
let mut lexer = BibtexLexer::new("@pReAmBlE\n@article\n@string");
|
||||
verify(&mut lexer, 0, 0, "@pReAmBlE", BibtexTokenKind::PreambleKind);
|
||||
verify(&mut lexer, 1, 0, "@article", BibtexTokenKind::EntryKind);
|
||||
verify(&mut lexer, 2, 0, "@string", BibtexTokenKind::StringKind);
|
||||
assert_eq!(None, lexer.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_operator() {
|
||||
let mut lexer = BibtexLexer::new("=,#");
|
||||
verify(&mut lexer, 0, 0, "=", BibtexTokenKind::Assign);
|
||||
verify(&mut lexer, 0, 1, ",", BibtexTokenKind::Comma);
|
||||
verify(&mut lexer, 0, 2, "#", BibtexTokenKind::Concat);
|
||||
assert_eq!(None, lexer.next());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,312 +2,25 @@ pub mod ast;
|
|||
pub mod lexer;
|
||||
pub mod parser;
|
||||
|
||||
use crate::range;
|
||||
use crate::syntax::bibtex::ast::*;
|
||||
use crate::syntax::bibtex::ast::BibtexRoot;
|
||||
use crate::syntax::bibtex::lexer::BibtexLexer;
|
||||
use crate::syntax::bibtex::parser::BibtexParser;
|
||||
use crate::syntax::text::SyntaxNode;
|
||||
use lsp_types::{Position, Range};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum BibtexNodeKind {
|
||||
Comment,
|
||||
Preamble,
|
||||
String,
|
||||
Entry,
|
||||
Field,
|
||||
Word,
|
||||
Command,
|
||||
QuotedContent,
|
||||
BracedContent,
|
||||
Concat,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum BibtexNode {
|
||||
Declaration(BibtexDeclaration),
|
||||
Field(Rc<BibtexField>),
|
||||
Content(BibtexContent),
|
||||
}
|
||||
|
||||
impl BibtexNode {
|
||||
fn kind(&self) -> BibtexNodeKind {
|
||||
match self {
|
||||
BibtexNode::Declaration(declaration) => match declaration {
|
||||
BibtexDeclaration::Comment(_) => BibtexNodeKind::Comment,
|
||||
BibtexDeclaration::Preamble(_) => BibtexNodeKind::Preamble,
|
||||
BibtexDeclaration::String(_) => BibtexNodeKind::String,
|
||||
BibtexDeclaration::Entry(_) => BibtexNodeKind::Entry,
|
||||
},
|
||||
BibtexNode::Field(_) => BibtexNodeKind::Field,
|
||||
BibtexNode::Content(content) => match content {
|
||||
BibtexContent::Word(_) => BibtexNodeKind::Word,
|
||||
BibtexContent::Command(_) => BibtexNodeKind::Command,
|
||||
BibtexContent::QuotedContent(_) => BibtexNodeKind::QuotedContent,
|
||||
BibtexContent::BracedContent(_) => BibtexNodeKind::BracedContent,
|
||||
BibtexContent::Concat(_) => BibtexNodeKind::Concat,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyntaxNode for BibtexNode {
|
||||
fn range(&self) -> Range {
|
||||
match self {
|
||||
BibtexNode::Declaration(declaration) => declaration.range(),
|
||||
BibtexNode::Field(field) => field.range,
|
||||
BibtexNode::Content(content) => content.range(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BibtexFinder {
|
||||
pub position: Option<Position>,
|
||||
pub results: Vec<BibtexNode>,
|
||||
}
|
||||
|
||||
impl BibtexFinder {
|
||||
pub fn new(position: Option<Position>) -> Self {
|
||||
BibtexFinder {
|
||||
position,
|
||||
results: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_range(&self, node: &BibtexNode) -> bool {
|
||||
if let Some(position) = self.position {
|
||||
range::contains(node.range(), position)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BibtexVisitor<()> for BibtexFinder {
|
||||
fn visit_comment(&mut self, comment: Rc<BibtexComment>) {
|
||||
let node = BibtexNode::Declaration(BibtexDeclaration::Comment(Rc::clone(&comment)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_preamble(&mut self, preamble: Rc<BibtexPreamble>) {
|
||||
let node = BibtexNode::Declaration(BibtexDeclaration::Preamble(Rc::clone(&preamble)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
if let Some(ref content) = preamble.content {
|
||||
content.accept(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string(&mut self, string: Rc<BibtexString>) {
|
||||
let node = BibtexNode::Declaration(BibtexDeclaration::String(Rc::clone(&string)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
if let Some(ref content) = string.value {
|
||||
content.accept(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_entry(&mut self, entry: Rc<BibtexEntry>) {
|
||||
let node = BibtexNode::Declaration(BibtexDeclaration::Entry(Rc::clone(&entry)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
for field in &entry.fields {
|
||||
self.visit_field(Rc::clone(&field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_field(&mut self, field: Rc<BibtexField>) {
|
||||
let node = BibtexNode::Field(Rc::clone(&field));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
if let Some(ref content) = field.content {
|
||||
content.accept(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_word(&mut self, word: Rc<BibtexWord>) {
|
||||
let node = BibtexNode::Content(BibtexContent::Word(Rc::clone(&word)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_command(&mut self, command: Rc<BibtexCommand>) {
|
||||
let node = BibtexNode::Content(BibtexContent::Command(Rc::clone(&command)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_quoted_content(&mut self, content: Rc<BibtexQuotedContent>) {
|
||||
let node = BibtexNode::Content(BibtexContent::QuotedContent(Rc::clone(&content)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
for child in &content.children {
|
||||
child.accept(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_braced_content(&mut self, content: Rc<BibtexBracedContent>) {
|
||||
let node = BibtexNode::Content(BibtexContent::BracedContent(Rc::clone(&content)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
for child in &content.children {
|
||||
child.accept(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_concat(&mut self, concat: Rc<BibtexConcat>) {
|
||||
let node = BibtexNode::Content(BibtexContent::Concat(Rc::clone(&concat)));
|
||||
if self.check_range(&node) {
|
||||
self.results.push(node);
|
||||
concat.left.accept(self);
|
||||
if let Some(ref right) = concat.right {
|
||||
right.accept(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BibtexSyntaxTree {
|
||||
pub root: BibtexRoot,
|
||||
pub descendants: Vec<BibtexNode>,
|
||||
}
|
||||
|
||||
impl From<BibtexRoot> for BibtexSyntaxTree {
|
||||
fn from(root: BibtexRoot) -> Self {
|
||||
let mut finder = BibtexFinder::new(None);
|
||||
for child in &root.children {
|
||||
child.accept(&mut finder);
|
||||
}
|
||||
BibtexSyntaxTree {
|
||||
root,
|
||||
descendants: finder.results,
|
||||
}
|
||||
BibtexSyntaxTree { root }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for BibtexSyntaxTree {
|
||||
fn from(text: &str) -> Self {
|
||||
let tokens = BibtexLexer::from(text);
|
||||
let mut parser = BibtexParser::new(tokens);
|
||||
let lexer = BibtexLexer::new(text);
|
||||
let mut parser = BibtexParser::new(lexer);
|
||||
let root = parser.root();
|
||||
BibtexSyntaxTree::from(root)
|
||||
}
|
||||
}
|
||||
|
||||
impl BibtexSyntaxTree {
|
||||
fn find(&self, position: Position) -> Vec<BibtexNode> {
|
||||
let mut finder = BibtexFinder::new(Some(position));
|
||||
for child in &self.root.children {
|
||||
child.accept(&mut finder);
|
||||
}
|
||||
finder.results
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn verify(text: &str, expected: Vec<BibtexNodeKind>) {
|
||||
let actual: Vec<BibtexNodeKind> = BibtexSyntaxTree::from(text)
|
||||
.descendants
|
||||
.iter()
|
||||
.map(|node| node.kind())
|
||||
.collect();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
verify("", Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_preamble() {
|
||||
verify("@preamble", vec![BibtexNodeKind::Preamble]);
|
||||
verify("@preamble{", vec![BibtexNodeKind::Preamble]);
|
||||
verify(
|
||||
"@preamble{\"foo\"",
|
||||
vec![
|
||||
BibtexNodeKind::Preamble,
|
||||
BibtexNodeKind::QuotedContent,
|
||||
BibtexNodeKind::Word,
|
||||
],
|
||||
);
|
||||
verify(
|
||||
"@preamble{\"foo\"}",
|
||||
vec![
|
||||
BibtexNodeKind::Preamble,
|
||||
BibtexNodeKind::QuotedContent,
|
||||
BibtexNodeKind::Word,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
verify("@string", vec![BibtexNodeKind::String]);
|
||||
verify("@string{", vec![BibtexNodeKind::String]);
|
||||
verify("@string{key", vec![BibtexNodeKind::String]);
|
||||
verify(
|
||||
"@string{key=value",
|
||||
vec![BibtexNodeKind::String, BibtexNodeKind::Word],
|
||||
);
|
||||
verify(
|
||||
"@string{key=value}",
|
||||
vec![BibtexNodeKind::String, BibtexNodeKind::Word],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entry() {
|
||||
verify("@article", vec![BibtexNodeKind::Entry]);
|
||||
verify("@article{", vec![BibtexNodeKind::Entry]);
|
||||
verify("@article{key", vec![BibtexNodeKind::Entry]);
|
||||
verify("@article{key,", vec![BibtexNodeKind::Entry]);
|
||||
verify(
|
||||
"@article{key, foo = bar}",
|
||||
vec![
|
||||
BibtexNodeKind::Entry,
|
||||
BibtexNodeKind::Field,
|
||||
BibtexNodeKind::Word,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_content() {
|
||||
verify(
|
||||
"@article{key, foo = {bar baz \\qux}}",
|
||||
vec![
|
||||
BibtexNodeKind::Entry,
|
||||
BibtexNodeKind::Field,
|
||||
BibtexNodeKind::BracedContent,
|
||||
BibtexNodeKind::Word,
|
||||
BibtexNodeKind::Word,
|
||||
BibtexNodeKind::Command,
|
||||
],
|
||||
);
|
||||
verify(
|
||||
"@article{key, foo = bar # baz}",
|
||||
vec![
|
||||
BibtexNodeKind::Entry,
|
||||
BibtexNodeKind::Field,
|
||||
BibtexNodeKind::Concat,
|
||||
BibtexNodeKind::Word,
|
||||
BibtexNodeKind::Word,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use crate::syntax::bibtex::ast::*;
|
||||
use std::iter::Peekable;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct BibtexParser<I: Iterator<Item = BibtexToken>> {
|
||||
tokens: Peekable<I>,
|
||||
|
|
@ -18,18 +17,18 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> {
|
|||
while let Some(ref token) = self.tokens.peek() {
|
||||
match token.kind {
|
||||
BibtexTokenKind::PreambleKind => {
|
||||
children.push(BibtexDeclaration::Preamble(Rc::new(self.preamble())));
|
||||
children.push(BibtexDeclaration::Preamble(self.preamble()));
|
||||
}
|
||||
BibtexTokenKind::StringKind => {
|
||||
children.push(BibtexDeclaration::String(Rc::new(self.string())));
|
||||
children.push(BibtexDeclaration::String(self.string()));
|
||||
}
|
||||
BibtexTokenKind::EntryKind => {
|
||||
children.push(BibtexDeclaration::Entry(Rc::new(self.entry())));
|
||||
children.push(BibtexDeclaration::Entry(self.entry()));
|
||||
}
|
||||
_ => {
|
||||
let token = self.tokens.next().unwrap();
|
||||
let comment = BibtexComment::new(token);
|
||||
children.push(BibtexDeclaration::Comment(Rc::new(comment)));
|
||||
children.push(BibtexDeclaration::Comment(comment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +99,7 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> {
|
|||
|
||||
let mut fields = Vec::new();
|
||||
while self.next_of_kind(BibtexTokenKind::Word) {
|
||||
fields.push(Rc::new(self.field()));
|
||||
fields.push(self.field());
|
||||
}
|
||||
|
||||
let right = self.expect2(BibtexTokenKind::EndBrace, BibtexTokenKind::EndParen);
|
||||
|
|
@ -134,8 +133,8 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> {
|
|||
| BibtexTokenKind::Assign
|
||||
| BibtexTokenKind::Comma
|
||||
| BibtexTokenKind::BeginParen
|
||||
| BibtexTokenKind::EndParen => BibtexContent::Word(Rc::new(BibtexWord::new(token))),
|
||||
BibtexTokenKind::Command => BibtexContent::Command(Rc::new(BibtexCommand::new(token))),
|
||||
| BibtexTokenKind::EndParen => BibtexContent::Word(BibtexWord::new(token)),
|
||||
BibtexTokenKind::Command => BibtexContent::Command(BibtexCommand::new(token)),
|
||||
BibtexTokenKind::Quote => {
|
||||
let mut children = Vec::new();
|
||||
while self.can_match_content() {
|
||||
|
|
@ -145,9 +144,7 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> {
|
|||
children.push(self.content());
|
||||
}
|
||||
let right = self.expect1(BibtexTokenKind::Quote);
|
||||
BibtexContent::QuotedContent(Rc::new(BibtexQuotedContent::new(
|
||||
token, children, right,
|
||||
)))
|
||||
BibtexContent::QuotedContent(BibtexQuotedContent::new(token, children, right))
|
||||
}
|
||||
BibtexTokenKind::BeginBrace => {
|
||||
let mut children = Vec::new();
|
||||
|
|
@ -155,9 +152,7 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> {
|
|||
children.push(self.content());
|
||||
}
|
||||
let right = self.expect1(BibtexTokenKind::EndBrace);
|
||||
BibtexContent::BracedContent(Rc::new(BibtexBracedContent::new(
|
||||
token, children, right,
|
||||
)))
|
||||
BibtexContent::BracedContent(BibtexBracedContent::new(token, children, right))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
|
@ -167,7 +162,7 @@ impl<I: Iterator<Item = BibtexToken>> BibtexParser<I> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
BibtexContent::Concat(Rc::new(BibtexConcat::new(left, operator, right)))
|
||||
BibtexContent::Concat(Box::new(BibtexConcat::new(left, operator, right)))
|
||||
} else {
|
||||
left
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue