Skip to content

Commit 7b09582

Browse files
WIPip
1 parent c3a48dc commit 7b09582

File tree

4 files changed

+32
-115
lines changed

4 files changed

+32
-115
lines changed

crates/djls-template-ast/src/ast.rs

Lines changed: 1 addition & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,4 @@
11
use crate::tokens::Token;
2-
use crate::tagspecs::TagSpecs;
3-
pub mod validator {
4-
use super::{Ast, AstError, Block, Node, Tag, TagSpecs};
5-
6-
pub struct Validator<'a> {
7-
ast: &'a Ast,
8-
tags: &'a TagSpecs,
9-
errors: Vec<AstError>,
10-
}
11-
12-
impl<'a> Validator<'a> {
13-
pub fn new(ast: &'a Ast, tags: &'a TagSpecs) -> Self {
14-
Self {
15-
ast,
16-
tags,
17-
errors: Vec::new(),
18-
}
19-
}
20-
21-
pub fn validate(&mut self) -> Vec<AstError> {
22-
if self.ast.nodes().is_empty() {
23-
self.errors.push(AstError::EmptyAst);
24-
return self.errors.clone();
25-
}
26-
27-
self.validate_nodes(self.ast.nodes());
28-
self.errors.clone()
29-
}
30-
31-
fn validate_nodes(&mut self, nodes: &[Node]) {
32-
for node in nodes {
33-
match node {
34-
Node::Block(block) => self.validate_block(block),
35-
_ => {}
36-
}
37-
}
38-
}
39-
40-
fn validate_block(&mut self, block: &Block) {
41-
match block {
42-
Block::Container { tag, nodes, closing } => {
43-
self.validate_tag(tag);
44-
self.validate_nodes(nodes);
45-
if let Some(closing) = closing {
46-
self.validate_closing_tag(tag, closing);
47-
}
48-
}
49-
Block::Branch { tag, nodes } => {
50-
self.validate_tag(tag);
51-
self.validate_nodes(nodes);
52-
}
53-
_ => {}
54-
}
55-
}
56-
57-
fn validate_tag(&mut self, tag: &Tag) {
58-
if let Some(spec) = self.tags.get(&tag.name) {
59-
// Validate required arguments
60-
if let Some(args) = &spec.args {
61-
for arg in args {
62-
if arg.required && !tag.bits.iter().any(|bit| bit == &arg.name) {
63-
self.errors.push(AstError::InvalidTagStructure {
64-
tag: tag.name.clone(),
65-
reason: format!("Missing required argument: {}", arg.name),
66-
span: tag.span,
67-
});
68-
}
69-
}
70-
}
71-
}
72-
}
73-
74-
fn validate_closing_tag(&mut self, opening_tag: &Tag, closing: &Block) {
75-
if let Block::Closing { tag: closing_tag } = closing {
76-
if let Some(spec) = self.tags.get(&opening_tag.name) {
77-
if let Some(expected_closing) = &spec.closing {
78-
if closing_tag.name != *expected_closing {
79-
self.errors.push(AstError::UnbalancedStructure {
80-
opening_tag: opening_tag.name.clone(),
81-
expected_closing: expected_closing.clone(),
82-
opening_span: opening_tag.span,
83-
closing_span: Some(closing_tag.span),
84-
});
85-
}
86-
}
87-
}
88-
}
89-
}
90-
}
91-
}
922
use serde::Serialize;
933
use thiserror::Error;
944

@@ -388,7 +298,7 @@ mod tests {
388298
.position_to_line_col(*span.start() as usize);
389299
assert_eq!(
390300
(line, col),
391-
(2, 3),
301+
(2, 0),
392302
"Variable should start at line 2, col 3"
393303
);
394304

crates/djls-template-ast/src/lib.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
mod ast;
22
mod error;
33
mod lexer;
4-
use crate::ast::validator::Validator;
54
mod parser;
65
mod tagspecs;
76
mod tokens;
7+
mod validator;
88

9-
pub use error::{TemplateError, to_lsp_diagnostic, QuickFix};
9+
pub use error::{to_lsp_diagnostic, QuickFix, TemplateError};
1010

1111
pub use ast::Ast;
1212
use lexer::Lexer;
1313
pub use parser::{Parser, ParserError};
1414
use tagspecs::TagSpecs;
15+
use validator::Validator;
1516

1617
/// Parses a Django template and returns the AST and any parsing errors.
1718
///
@@ -24,18 +25,19 @@ pub fn parse_template(
2425
source: &str,
2526
tag_specs: Option<&TagSpecs>,
2627
) -> Result<(Ast, Vec<TemplateError>), TemplateError> {
27-
let tokens = Lexer::new(source).tokenize()
28+
let tokens = Lexer::new(source)
29+
.tokenize()
2830
.map_err(|e| TemplateError::Lexer(e.to_string()))?;
2931

3032
let tag_specs = match tag_specs {
3133
Some(specs) => specs.clone(),
32-
None => TagSpecs::load_builtin_specs().map_err(|e| {
33-
TemplateError::Config(format!("Failed to load builtin specs: {}", e))
34-
})?,
34+
None => TagSpecs::load_builtin_specs()
35+
.map_err(|e| TemplateError::Config(format!("Failed to load builtin specs: {}", e)))?,
3536
};
3637

3738
let mut parser = Parser::new(tokens, tag_specs.clone());
38-
let (ast, parser_errors) = parser.parse()
39+
let (ast, parser_errors) = parser
40+
.parse()
3941
.map_err(|e| TemplateError::Parser(e.to_string()))?;
4042

4143
// Convert parser errors to TemplateError

crates/djls-template-ast/src/parser.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,11 @@ impl Parser {
149149

150150
let mut nodes = Vec::new();
151151
let mut closing = None;
152-
153152
while !self.is_at_end() {
153+
eprintln!("not at end");
154154
match self.next_node() {
155155
Ok(Node::Block(Block::Single { tag: inner_tag })) => {
156+
eprintln!("{:?}", inner_tag);
156157
if self.is_closing_tag(&inner_tag, spec) {
157158
closing = Some(Box::new(Block::Closing { tag: inner_tag }));
158159
break;
@@ -179,7 +180,10 @@ impl Parser {
179180

180181
fn is_valid_tag_syntax(&self, tag_name: &str) -> bool {
181182
// Basic syntax validation without span concerns
182-
!tag_name.is_empty() && tag_name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
183+
!tag_name.is_empty()
184+
&& tag_name
185+
.chars()
186+
.all(|c| c.is_ascii_alphanumeric() || c == '_')
183187
}
184188

185189
fn parse_branch_tag(&mut self, branch_tag: Tag, spec: &TagSpec) -> Result<Node, ParserError> {
@@ -397,17 +401,13 @@ pub enum ParserError {
397401
position: usize,
398402
},
399403
#[error("Invalid syntax: {context}")]
400-
InvalidSyntax {
401-
context: String,
402-
},
404+
InvalidSyntax { context: String },
403405
#[error("Empty tag")]
404406
EmptyTag,
405407
#[error("Lexer error: {0}")]
406408
Lexer(#[from] LexerError),
407409
#[error("Stream error: {kind}")]
408-
StreamError {
409-
kind: String,
410-
},
410+
StreamError { kind: String },
411411
#[error("AST error: {0}")]
412412
Ast(#[from] AstError),
413413
}
@@ -698,8 +698,9 @@ mod tests {
698698
let source = "{% if user.is_authenticated %}Welcome";
699699
let tokens = Lexer::new(source).tokenize().unwrap();
700700
let tags = TagSpecs::load_builtin_specs().unwrap();
701-
let mut parser = Parser::new(tokens, tags);
701+
let mut parser = Parser::new(tokens.clone(), tags);
702702
let (ast, errors) = parser.parse().unwrap();
703+
eprintln!("{:?}", tokens);
703704
insta::assert_yaml_snapshot!(ast);
704705
assert_eq!(errors.len(), 1);
705706
assert!(
@@ -712,8 +713,9 @@ mod tests {
712713
let source = "{% for item in items %}{{ item.name }}";
713714
let tokens = Lexer::new(source).tokenize().unwrap();
714715
let tags = TagSpecs::load_builtin_specs().unwrap();
715-
let mut parser = Parser::new(tokens, tags);
716+
let mut parser = Parser::new(tokens.clone(), tags);
716717
let (ast, errors) = parser.parse().unwrap();
718+
eprintln!("{:?}", tokens);
717719
insta::assert_yaml_snapshot!(ast);
718720
assert_eq!(errors.len(), 1);
719721
assert!(

crates/djls-template-ast/src/validator.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@ impl<'a> Validator<'a> {
1919
pub fn validate(&mut self) -> Vec<AstError> {
2020
if self.ast.nodes().is_empty() {
2121
self.errors.push(AstError::EmptyAst);
22-
return self.errors;
22+
} else {
23+
self.validate_nodes(self.ast.nodes());
2324
}
2425

25-
self.validate_nodes(self.ast.nodes());
26-
self.errors
26+
self.errors.clone()
2727
}
2828

2929
fn validate_nodes(&mut self, nodes: &[Node]) {
3030
for node in nodes {
31-
match node {
32-
Node::Block(block) => self.validate_block(block),
33-
_ => {}
31+
if let Node::Block(block) = node {
32+
self.validate_block(block)
3433
}
3534
}
3635
}
@@ -39,7 +38,11 @@ impl<'a> Validator<'a> {
3938
let tag = block.tag();
4039
if let Some(spec) = self.tags.get(&tag.name) {
4140
match block {
42-
Block::Container { tag, nodes, closing } => {
41+
Block::Container {
42+
tag,
43+
nodes,
44+
closing,
45+
} => {
4346
self.validate_container(tag, nodes, closing, spec);
4447
}
4548
Block::Branch { tag, nodes } => {

0 commit comments

Comments
 (0)