Skip to content

Commit 12e81be

Browse files
wip
1 parent b695c18 commit 12e81be

File tree

31 files changed

+565
-1454
lines changed

31 files changed

+565
-1454
lines changed

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

Lines changed: 30 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ use crate::tokens::Token;
22
use serde::Serialize;
33
use thiserror::Error;
44

5-
#[derive(Clone, Debug, Default, Serialize)]
6-
pub struct Ast {
5+
#[derive(Clone, Default, Debug, Serialize)]
6+
pub struct NodeList {
77
nodes: Vec<Node>,
88
line_offsets: LineOffsets,
99
}
1010

11-
impl Ast {
11+
impl NodeList {
1212
pub fn nodes(&self) -> &Vec<Node> {
1313
&self.nodes
1414
}
@@ -25,7 +25,7 @@ impl Ast {
2525
self.line_offsets = line_offsets
2626
}
2727

28-
pub fn finalize(&mut self) -> Ast {
28+
pub fn finalize(&mut self) -> NodeList {
2929
self.clone()
3030
}
3131
}
@@ -67,6 +67,28 @@ impl LineOffsets {
6767
}
6868
}
6969

70+
#[derive(Clone, Debug, Serialize)]
71+
pub enum Node {
72+
Tag {
73+
name: String,
74+
bits: Vec<String>,
75+
span: Span,
76+
},
77+
Comment {
78+
content: String,
79+
span: Span,
80+
},
81+
Text {
82+
content: String,
83+
span: Span,
84+
},
85+
Variable {
86+
var: String,
87+
filters: Vec<String>,
88+
span: Span,
89+
},
90+
}
91+
7092
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
7193
pub struct Span {
7294
start: u32,
@@ -95,127 +117,6 @@ impl From<Token> for Span {
95117
}
96118
}
97119

98-
#[derive(Clone, Debug, Serialize)]
99-
pub enum Node {
100-
Block(Block),
101-
Comment {
102-
content: String,
103-
span: Span,
104-
},
105-
Text {
106-
content: String,
107-
span: Span,
108-
},
109-
Variable {
110-
bits: Vec<String>,
111-
filters: Vec<DjangoFilter>,
112-
span: Span,
113-
},
114-
}
115-
116-
impl Node {
117-
pub fn span(&self) -> Option<&Span> {
118-
match self {
119-
Node::Block(block) => Some(&block.tag().span),
120-
Node::Comment { span, .. } => Some(span),
121-
Node::Text { span, .. } => Some(span),
122-
Node::Variable { span, .. } => Some(span),
123-
}
124-
}
125-
126-
pub fn children(&self) -> Option<&Vec<Node>> {
127-
match self {
128-
Node::Block(block) => block.nodes(),
129-
_ => None,
130-
}
131-
}
132-
}
133-
134-
#[derive(Debug, Clone, Serialize)]
135-
pub enum Block {
136-
Branch {
137-
tag: Tag,
138-
nodes: Vec<Node>,
139-
},
140-
Closing {
141-
tag: Tag,
142-
},
143-
Container {
144-
tag: Tag,
145-
nodes: Vec<Node>,
146-
closing: Option<Box<Block>>,
147-
},
148-
Inclusion {
149-
tag: Tag,
150-
template_name: String,
151-
},
152-
Single {
153-
tag: Tag,
154-
},
155-
}
156-
157-
impl Block {
158-
pub fn tag(&self) -> &Tag {
159-
match self {
160-
Self::Branch { tag, .. }
161-
| Self::Container { tag, .. }
162-
| Self::Closing { tag }
163-
| Self::Inclusion { tag, .. }
164-
| Self::Single { tag } => tag,
165-
}
166-
}
167-
168-
pub fn nodes(&self) -> Option<&Vec<Node>> {
169-
match self {
170-
Block::Branch { nodes, .. } => Some(nodes),
171-
Block::Container { nodes, .. } => Some(nodes),
172-
_ => None,
173-
}
174-
}
175-
176-
pub fn closing(&self) -> Option<&Block> {
177-
match self {
178-
Block::Container { closing, .. } => closing.as_deref(),
179-
_ => None,
180-
}
181-
}
182-
183-
pub fn template_name(&self) -> Option<&String> {
184-
match self {
185-
Block::Inclusion { template_name, .. } => Some(template_name),
186-
_ => None,
187-
}
188-
}
189-
}
190-
191-
#[derive(Clone, Debug, Serialize)]
192-
pub struct Tag {
193-
pub name: String,
194-
pub bits: Vec<String>,
195-
pub span: Span,
196-
pub tag_span: Span,
197-
pub assignment: Option<String>,
198-
}
199-
200-
#[derive(Clone, Debug, Serialize)]
201-
pub struct Assignment {
202-
pub target: String,
203-
pub value: String,
204-
}
205-
206-
#[derive(Clone, Debug, Serialize)]
207-
pub struct DjangoFilter {
208-
pub name: String,
209-
pub args: Vec<String>,
210-
pub span: Span,
211-
}
212-
213-
impl DjangoFilter {
214-
pub fn new(name: String, args: Vec<String>, span: Span) -> Self {
215-
Self { name, args, span }
216-
}
217-
}
218-
219120
#[derive(Clone, Debug, Error, Serialize)]
220121
pub enum AstError {
221122
#[error("Empty AST")]
@@ -272,28 +173,25 @@ mod tests {
272173

273174
mod spans_and_positions {
274175
use super::*;
275-
use crate::tagspecs::TagSpecs;
276176

277177
#[test]
278178
fn test_variable_spans() {
279179
let template = "Hello\n{{ user.name }}\nWorld";
280180
let tokens = Lexer::new(template).tokenize().unwrap();
281-
println!("Tokens: {:#?}", tokens); // Add debug print
282-
let tags = TagSpecs::load_builtin_specs().unwrap();
283-
let mut parser = Parser::new(tokens, tags);
284-
let (ast, errors) = parser.parse().unwrap();
181+
let mut parser = Parser::new(tokens);
182+
let (nodelist, errors) = parser.parse().unwrap();
285183
assert!(errors.is_empty());
286184

287185
// Find the variable node
288-
let nodes = ast.nodes();
186+
let nodes = nodelist.nodes();
289187
let var_node = nodes
290188
.iter()
291189
.find(|n| matches!(n, Node::Variable { .. }))
292190
.unwrap();
293191

294192
if let Node::Variable { span, .. } = var_node {
295193
// Variable starts after newline + "{{"
296-
let (line, col) = ast
194+
let (line, col) = nodelist
297195
.line_offsets()
298196
.position_to_line_col(*span.start() as usize);
299197
assert_eq!(
@@ -302,84 +200,8 @@ mod tests {
302200
"Variable should start at line 2, col 3"
303201
);
304202

305-
// Span should be exactly "user.name"
306203
assert_eq!(*span.length(), 9, "Variable span should cover 'user.name'");
307204
}
308205
}
309-
310-
#[test]
311-
fn test_block_spans() {
312-
let nodes = vec![Node::Block(Block::Container {
313-
tag: Tag {
314-
name: "if".to_string(),
315-
bits: vec!["user.is_authenticated".to_string()],
316-
span: Span::new(0, 35),
317-
tag_span: Span::new(0, 35),
318-
assignment: None,
319-
},
320-
nodes: vec![],
321-
closing: None,
322-
})];
323-
324-
let ast = Ast {
325-
nodes,
326-
line_offsets: LineOffsets::new(),
327-
};
328-
329-
let node = &ast.nodes()[0];
330-
if let Node::Block(block) = node {
331-
assert_eq!(block.tag().span.start(), &0);
332-
assert_eq!(block.tag().span.length(), &35);
333-
} else {
334-
panic!("Expected Block node");
335-
}
336-
}
337-
338-
#[test]
339-
fn test_multiline_template() {
340-
let template = "{% if user.active %}\n Welcome!\n{% endif %}";
341-
let tokens = Lexer::new(template).tokenize().unwrap();
342-
let tags = TagSpecs::load_builtin_specs().unwrap();
343-
let mut parser = Parser::new(tokens, tags);
344-
let (ast, errors) = parser.parse().unwrap();
345-
assert!(errors.is_empty());
346-
347-
let nodes = ast.nodes();
348-
if let Node::Block(Block::Container {
349-
tag,
350-
nodes,
351-
closing,
352-
..
353-
}) = &nodes[0]
354-
{
355-
// Check block tag
356-
assert_eq!(tag.name, "if");
357-
assert_eq!(tag.bits, vec!["if", "user.active"]);
358-
359-
// Check nodes
360-
eprintln!("Nodes: {:?}", nodes);
361-
assert_eq!(nodes.len(), 1);
362-
if let Node::Text { content, span } = &nodes[0] {
363-
assert_eq!(content, "Welcome!");
364-
eprintln!("Line offsets: {:?}", ast.line_offsets());
365-
eprintln!("Span: {:?}", span);
366-
let (line, col) = ast.line_offsets().position_to_line_col(span.start as usize);
367-
assert_eq!((line, col), (2, 2), "Content should be on line 2, col 2");
368-
369-
// Check closing tag
370-
if let Block::Closing { tag } =
371-
closing.as_ref().expect("Expected closing tag").as_ref()
372-
{
373-
assert_eq!(tag.name, "endif");
374-
} else {
375-
panic!("Expected closing block");
376-
}
377-
} else {
378-
panic!("Expected text node");
379-
}
380-
} else {
381-
panic!("Expected block node");
382-
}
383-
}
384206
}
385207
}

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

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ mod tagspecs;
66
mod tokens;
77
mod validator;
88

9+
use ast::NodeList;
910
pub use error::{to_lsp_diagnostic, QuickFix, TemplateError};
1011

11-
pub use ast::Ast;
1212
use lexer::Lexer;
1313
pub use parser::{Parser, ParserError};
14-
use tagspecs::TagSpecs;
15-
use validator::Validator;
1614

1715
/// Parses a Django template and returns the AST and any parsing errors.
1816
///
@@ -21,35 +19,27 @@ use validator::Validator;
2119
///
2220
/// Returns a `Result` containing a tuple of `(Ast, Vec<ParserError>)` on success,
2321
/// or a `ParserError` on failure.
24-
pub fn parse_template(
25-
source: &str,
26-
tag_specs: Option<&TagSpecs>,
27-
) -> Result<(Ast, Vec<TemplateError>), TemplateError> {
22+
pub fn parse_template(source: &str) -> Result<(NodeList, Vec<TemplateError>), TemplateError> {
2823
let tokens = Lexer::new(source)
2924
.tokenize()
3025
.map_err(|e| TemplateError::Lexer(e.to_string()))?;
3126

32-
let tag_specs = match tag_specs {
33-
Some(specs) => specs.clone(),
34-
None => TagSpecs::load_builtin_specs()
35-
.map_err(|e| TemplateError::Config(format!("Failed to load builtin specs: {}", e)))?,
36-
};
27+
// let tag_specs = match tag_specs {
28+
// Some(specs) => specs.clone(),
29+
// None => TagSpecs::load_builtin_specs()
30+
// .map_err(|e| TemplateError::Config(format!("Failed to load builtin specs: {}", e)))?,
31+
// };
3732

38-
let mut parser = Parser::new(tokens, tag_specs.clone());
39-
let (ast, parser_errors) = parser
33+
let mut parser = Parser::new(tokens);
34+
let (nodelist, parser_errors) = parser
4035
.parse()
4136
.map_err(|e| TemplateError::Parser(e.to_string()))?;
4237

4338
// Convert parser errors to TemplateError
44-
let mut all_errors = parser_errors
39+
let all_errors = parser_errors
4540
.into_iter()
4641
.map(|e| TemplateError::Parser(e.to_string()))
4742
.collect::<Vec<_>>();
4843

49-
// Run validation
50-
let mut validator = Validator::new(&ast, &tag_specs);
51-
let validation_errors = validator.validate();
52-
all_errors.extend(validation_errors.into_iter().map(TemplateError::Validation));
53-
54-
Ok((ast, all_errors))
44+
Ok((nodelist, all_errors))
5545
}

0 commit comments

Comments
 (0)