mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-09-10 04:16:30 +00:00
wip
This commit is contained in:
parent
78e25338f8
commit
e4bdea6537
1 changed files with 56 additions and 51 deletions
|
@ -1,4 +1,4 @@
|
||||||
use crate::ast::{Ast, AstError, Block, DjangoFilter, LineOffsets, Node, Span, Tag, Assignment};
|
use crate::ast::{Assignment, Ast, AstError, Block, DjangoFilter, LineOffsets, Node, Span, Tag};
|
||||||
use crate::tagspecs::{TagSpec, TagType};
|
use crate::tagspecs::{TagSpec, TagType};
|
||||||
use crate::tokens::{Token, TokenStream, TokenType};
|
use crate::tokens::{Token, TokenStream, TokenType};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -6,16 +6,11 @@ use thiserror::Error;
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
tokens: TokenStream,
|
tokens: TokenStream,
|
||||||
current: usize,
|
current: usize,
|
||||||
ast: Ast,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn new(tokens: TokenStream) -> Self {
|
pub fn new(tokens: TokenStream) -> Self {
|
||||||
Self {
|
Self { tokens, current: 0 }
|
||||||
tokens,
|
|
||||||
current: 0,
|
|
||||||
ast: Ast::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<(Ast, Vec<AstError>), ParserError> {
|
pub fn parse(&mut self) -> Result<(Ast, Vec<AstError>), ParserError> {
|
||||||
|
@ -42,11 +37,7 @@ impl Parser {
|
||||||
ast.add_node(node);
|
ast.add_node(node);
|
||||||
all_errors.extend(errors);
|
all_errors.extend(errors);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => self.synchronize()?,
|
||||||
if let Err(e) = self.synchronize() {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,21 +54,25 @@ impl Parser {
|
||||||
match token.token_type() {
|
match token.token_type() {
|
||||||
TokenType::DjangoBlock(content) => {
|
TokenType::DjangoBlock(content) => {
|
||||||
self.consume()?;
|
self.consume()?;
|
||||||
self.parse_django_block(&content)
|
self.parse_django_block(content)
|
||||||
}
|
}
|
||||||
TokenType::DjangoVariable(content) => {
|
TokenType::DjangoVariable(content) => {
|
||||||
self.consume()?;
|
self.consume()?;
|
||||||
Ok((self.parse_django_variable(&content)?, vec![]))
|
Ok((self.parse_django_variable(content)?, vec![]))
|
||||||
}
|
|
||||||
TokenType::Text(_) | TokenType::Whitespace(_) | TokenType::Newline |
|
|
||||||
TokenType::HtmlTagOpen(_) | TokenType::HtmlTagClose(_) | TokenType::HtmlTagVoid(_) |
|
|
||||||
TokenType::ScriptTagOpen(_) | TokenType::ScriptTagClose(_) |
|
|
||||||
TokenType::StyleTagOpen(_) | TokenType::StyleTagClose(_) => {
|
|
||||||
Ok((self.parse_text()?, vec![]))
|
|
||||||
}
|
}
|
||||||
|
TokenType::Text(_)
|
||||||
|
| TokenType::Whitespace(_)
|
||||||
|
| TokenType::Newline
|
||||||
|
| TokenType::HtmlTagOpen(_)
|
||||||
|
| TokenType::HtmlTagClose(_)
|
||||||
|
| TokenType::HtmlTagVoid(_)
|
||||||
|
| TokenType::ScriptTagOpen(_)
|
||||||
|
| TokenType::ScriptTagClose(_)
|
||||||
|
| TokenType::StyleTagOpen(_)
|
||||||
|
| TokenType::StyleTagClose(_) => Ok((self.parse_text()?, vec![])),
|
||||||
TokenType::Comment(content, start, end) => {
|
TokenType::Comment(content, start, end) => {
|
||||||
self.consume()?;
|
self.consume()?;
|
||||||
self.parse_comment(&content, &start, end.as_deref())
|
self.parse_comment(content, start, end.as_deref())
|
||||||
}
|
}
|
||||||
TokenType::Eof => Err(ParserError::Ast(AstError::StreamError("AtEnd".to_string()))),
|
TokenType::Eof => Err(ParserError::Ast(AstError::StreamError("AtEnd".to_string()))),
|
||||||
}
|
}
|
||||||
|
@ -85,8 +80,8 @@ impl Parser {
|
||||||
|
|
||||||
fn parse_django_block(&mut self, content: &str) -> Result<(Node, Vec<AstError>), ParserError> {
|
fn parse_django_block(&mut self, content: &str) -> Result<(Node, Vec<AstError>), ParserError> {
|
||||||
let token = self.peek_previous()?;
|
let token = self.peek_previous()?;
|
||||||
let start_pos = token.start().unwrap_or(0) as u32;
|
let start_pos = token.start().unwrap_or(0);
|
||||||
let total_length = token.length().unwrap_or(0) as u32;
|
let total_length = token.length().unwrap_or(0);
|
||||||
let span = Span::new(start_pos, total_length);
|
let span = Span::new(start_pos, total_length);
|
||||||
|
|
||||||
// Parse the tag name and any assignments
|
// Parse the tag name and any assignments
|
||||||
|
@ -131,16 +126,21 @@ impl Parser {
|
||||||
let mut all_errors = Vec::new();
|
let mut all_errors = Vec::new();
|
||||||
|
|
||||||
// Parse child nodes until we find the closing tag
|
// Parse child nodes until we find the closing tag
|
||||||
while let Ok((node, mut errors)) = self.next_node() {
|
while let Ok((node, errors)) = self.next_node() {
|
||||||
if let Node::Block(Block::Closing { tag: closing_tag }) = &node {
|
if let Node::Block(Block::Closing { tag: closing_tag }) = &node {
|
||||||
if let Some(expected_closing) = &spec.closing {
|
if let Some(expected_closing) = &spec.closing {
|
||||||
if closing_tag.name == *expected_closing {
|
if closing_tag.name == *expected_closing {
|
||||||
return Ok((Node::Block(Block::Block {
|
return Ok((
|
||||||
|
Node::Block(Block::Block {
|
||||||
tag,
|
tag,
|
||||||
nodes,
|
nodes,
|
||||||
closing: Some(Box::new(Block::Closing { tag: closing_tag.clone() })),
|
closing: Some(Box::new(Block::Closing {
|
||||||
|
tag: closing_tag.clone(),
|
||||||
|
})),
|
||||||
assignments: Some(assignments),
|
assignments: Some(assignments),
|
||||||
}), all_errors));
|
}),
|
||||||
|
all_errors,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,12 +152,15 @@ impl Parser {
|
||||||
all_errors.push(AstError::UnclosedTag(tag_name.clone()));
|
all_errors.push(AstError::UnclosedTag(tag_name.clone()));
|
||||||
|
|
||||||
// Return the partial block with the error
|
// Return the partial block with the error
|
||||||
Ok((Node::Block(Block::Block {
|
Ok((
|
||||||
|
Node::Block(Block::Block {
|
||||||
tag,
|
tag,
|
||||||
nodes,
|
nodes,
|
||||||
closing: None,
|
closing: None,
|
||||||
assignments: Some(assignments),
|
assignments: Some(assignments),
|
||||||
}), all_errors))
|
}),
|
||||||
|
all_errors,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
TagType::Tag => Ok((Node::Block(Block::Tag { tag }), vec![])),
|
TagType::Tag => Ok((Node::Block(Block::Tag { tag }), vec![])),
|
||||||
TagType::Variable => Ok((Node::Block(Block::Variable { tag }), vec![])),
|
TagType::Variable => Ok((Node::Block(Block::Variable { tag }), vec![])),
|
||||||
|
@ -177,10 +180,7 @@ impl Parser {
|
||||||
|
|
||||||
let parts: Vec<&str> = content.split('|').map(|s| s.trim()).collect();
|
let parts: Vec<&str> = content.split('|').map(|s| s.trim()).collect();
|
||||||
if !parts.is_empty() {
|
if !parts.is_empty() {
|
||||||
bits = parts[0]
|
bits = parts[0].split('.').map(|s| s.trim().to_string()).collect();
|
||||||
.split('.')
|
|
||||||
.map(|s| s.trim().to_string())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for filter_part in parts.iter().skip(1) {
|
for filter_part in parts.iter().skip(1) {
|
||||||
let filter_parts: Vec<&str> = filter_part.split(':').collect();
|
let filter_parts: Vec<&str> = filter_part.split(':').collect();
|
||||||
|
@ -211,15 +211,19 @@ impl Parser {
|
||||||
|
|
||||||
fn parse_text(&mut self) -> Result<Node, ParserError> {
|
fn parse_text(&mut self) -> Result<Node, ParserError> {
|
||||||
let start_token = self.peek()?;
|
let start_token = self.peek()?;
|
||||||
let start_pos = start_token.start().unwrap_or(0) as u32;
|
let start_pos = start_token.start().unwrap_or(0);
|
||||||
let total_length = start_token.length().unwrap_or(0) as u32;
|
let total_length = start_token.length().unwrap_or(0);
|
||||||
let span = Span::new(start_pos, total_length);
|
let span = Span::new(start_pos, total_length);
|
||||||
|
|
||||||
let content = match start_token.token_type() {
|
let content = match start_token.token_type() {
|
||||||
TokenType::Text(text) => text.to_string(),
|
TokenType::Text(text) => text.to_string(),
|
||||||
TokenType::Whitespace(count) => " ".repeat(*count),
|
TokenType::Whitespace(count) => " ".repeat(*count),
|
||||||
TokenType::Newline => "\n".to_string(),
|
TokenType::Newline => "\n".to_string(),
|
||||||
_ => return Err(ParserError::Ast(AstError::InvalidTag("Expected text, whitespace, or newline token".to_string()))),
|
_ => {
|
||||||
|
return Err(ParserError::Ast(AstError::InvalidTag(
|
||||||
|
"Expected text, whitespace, or newline token".to_string(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.consume()?;
|
self.consume()?;
|
||||||
|
@ -234,13 +238,16 @@ impl Parser {
|
||||||
end: Option<&str>,
|
end: Option<&str>,
|
||||||
) -> Result<(Node, Vec<AstError>), ParserError> {
|
) -> Result<(Node, Vec<AstError>), ParserError> {
|
||||||
let start_token = self.peek_previous()?;
|
let start_token = self.peek_previous()?;
|
||||||
let start_pos = start_token.start().unwrap_or(0) as u32;
|
let start_pos = start_token.start().unwrap_or(0);
|
||||||
let total_length = (content.len() + start.len() + end.map_or(0, |e| e.len())) as u32;
|
let total_length = (content.len() + start.len() + end.map_or(0, |e| e.len())) as u32;
|
||||||
let span = Span::new(start_pos, total_length);
|
let span = Span::new(start_pos, total_length);
|
||||||
Ok((Node::Comment {
|
Ok((
|
||||||
|
Node::Comment {
|
||||||
content: content.to_string(),
|
content: content.to_string(),
|
||||||
span,
|
span,
|
||||||
}, vec![]))
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&self) -> Result<Token, ParserError> {
|
fn peek(&self) -> Result<Token, ParserError> {
|
||||||
|
@ -335,7 +342,6 @@ impl ParserError {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::lexer::Lexer;
|
use crate::lexer::Lexer;
|
||||||
use crate::tokens::Token;
|
|
||||||
|
|
||||||
mod html {
|
mod html {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -407,8 +413,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_complex_if_elif() {
|
fn test_parse_complex_if_elif() {
|
||||||
let source =
|
let source = "{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}";
|
||||||
"{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}";
|
|
||||||
let tokens = Lexer::new(source).tokenize().unwrap();
|
let tokens = Lexer::new(source).tokenize().unwrap();
|
||||||
let mut parser = Parser::new(tokens);
|
let mut parser = Parser::new(tokens);
|
||||||
let (ast, errors) = parser.parse().unwrap();
|
let (ast, errors) = parser.parse().unwrap();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue