update: add extends, block, super, filter_block, autoescape and spaceless
This commit is contained in:
parent
261c02f59b
commit
0192ad0b64
3 changed files with 822 additions and 375 deletions
407
src/parser.zig
407
src/parser.zig
|
|
@ -9,9 +9,42 @@ pub const NodeType = enum {
|
|||
include,
|
||||
with_block,
|
||||
now,
|
||||
extends, // <--- novo
|
||||
block, // <--- novo
|
||||
super, // <--- novo (para {{ block.super }})
|
||||
extends,
|
||||
block,
|
||||
super,
|
||||
filter_block,
|
||||
autoescape,
|
||||
spaceless,
|
||||
};
|
||||
|
||||
pub const AutoescapeNode = struct {
|
||||
body: []Node,
|
||||
raw_open: []const u8,
|
||||
raw_close: []const u8,
|
||||
enabled: bool,
|
||||
};
|
||||
|
||||
pub const SpacelessNode = struct {
|
||||
body: []Node,
|
||||
raw_open: []const u8,
|
||||
raw_close: []const u8,
|
||||
};
|
||||
|
||||
pub const FilterBlockNode = struct {
|
||||
filters: []const u8, // "upper|escape|truncatewords:30"
|
||||
body: []Node,
|
||||
raw_open: []const u8,
|
||||
raw_close: []const u8,
|
||||
};
|
||||
|
||||
pub const Filter = struct {
|
||||
name: []const u8,
|
||||
arg: ?[]const u8 = null, // null ou string com argumento
|
||||
};
|
||||
|
||||
pub const VariableNode = struct {
|
||||
expr: []const u8,
|
||||
filters: []const Filter,
|
||||
};
|
||||
|
||||
pub const NowNode = struct {
|
||||
|
|
@ -50,10 +83,6 @@ pub const TextNode = struct {
|
|||
content: []const u8,
|
||||
};
|
||||
|
||||
pub const VariableNode = struct {
|
||||
content: []const u8,
|
||||
};
|
||||
|
||||
pub const TagNode = struct {
|
||||
name: []const u8,
|
||||
args: []const u8,
|
||||
|
|
@ -90,35 +119,43 @@ pub const Node = struct {
|
|||
extends: ?ExtendsNode = null,
|
||||
block: ?BlockNode = null,
|
||||
super: bool = false, // para {{ block.super }}
|
||||
filter_block: ?FilterBlockNode = null,
|
||||
autoescape: ?AutoescapeNode = null,
|
||||
spaceless: ?SpacelessNode = null,
|
||||
|
||||
pub fn deinit(self: Node, allocator: std.mem.Allocator) void {
|
||||
switch (self.type) {
|
||||
.text => if (self.text) |t| allocator.free(t.content),
|
||||
.variable => if (self.variable) |v| allocator.free(v.content),
|
||||
.variable => if (self.variable) |v| {
|
||||
allocator.free(v.expr);
|
||||
for (v.filters) |f| {
|
||||
allocator.free(f.name);
|
||||
if (f.arg) |a| allocator.free(a);
|
||||
}
|
||||
allocator.free(v.filters);
|
||||
},
|
||||
.tag => if (self.tag) |t| {
|
||||
allocator.free(t.name);
|
||||
allocator.free(t.args);
|
||||
},
|
||||
.if_block => if (self.@"if") |ib| {
|
||||
allocator.free(ib.condition);
|
||||
// NÃO free ib.raw_open
|
||||
// NÃO free ib.raw_close
|
||||
for (ib.true_body) |n| n.deinit(allocator);
|
||||
const true_copy = ib.true_body;
|
||||
for (true_copy) |n| n.deinit(allocator);
|
||||
allocator.free(ib.true_body);
|
||||
for (ib.false_body) |n| n.deinit(allocator);
|
||||
const false_copy = ib.false_body;
|
||||
for (false_copy) |n| n.deinit(allocator);
|
||||
allocator.free(ib.false_body);
|
||||
},
|
||||
.for_block => if (self.@"for") |fb| {
|
||||
allocator.free(fb.loop_var);
|
||||
allocator.free(fb.iterable);
|
||||
for (fb.body) |n| n.deinit(allocator);
|
||||
const body_copy = fb.body;
|
||||
for (body_copy) |n| n.deinit(allocator);
|
||||
allocator.free(fb.body);
|
||||
for (fb.empty_body) |n| n.deinit(allocator);
|
||||
const empty_copy = fb.empty_body;
|
||||
for (empty_copy) |n| n.deinit(allocator);
|
||||
allocator.free(fb.empty_body);
|
||||
// raw_open e raw_close são slices originais — não free
|
||||
},
|
||||
.include => if (self.include) |inc| {
|
||||
allocator.free(inc.template_name);
|
||||
},
|
||||
.with_block => if (self.with) |w| {
|
||||
for (w.assignments) |a| {
|
||||
|
|
@ -126,22 +163,35 @@ pub const Node = struct {
|
|||
allocator.free(a.value_expr);
|
||||
}
|
||||
allocator.free(w.assignments);
|
||||
for (w.body) |n| n.deinit(allocator);
|
||||
const body_copy = w.body;
|
||||
for (body_copy) |n| n.deinit(allocator);
|
||||
allocator.free(w.body);
|
||||
// raw_open e raw_close são slices originais — não free
|
||||
},
|
||||
.now => if (self.now) |n| {
|
||||
allocator.free(n.format);
|
||||
},
|
||||
.extends => if (self.extends) |e| {
|
||||
allocator.free(e.parent_name);
|
||||
},
|
||||
.block => if (self.block) |b| {
|
||||
allocator.free(b.name);
|
||||
for (b.body) |n| n.deinit(allocator);
|
||||
const body_copy = b.body;
|
||||
for (body_copy) |n| n.deinit(allocator);
|
||||
allocator.free(b.body);
|
||||
// raw_open e raw_close são slices originais — não free
|
||||
},
|
||||
.filter_block => if (self.filter_block) |fb| {
|
||||
allocator.free(fb.filters);
|
||||
const body_copy = fb.body;
|
||||
for (body_copy) |n| n.deinit(allocator);
|
||||
allocator.free(fb.body);
|
||||
},
|
||||
.autoescape => if (self.autoescape) |ae| {
|
||||
const body_copy = ae.body;
|
||||
for (body_copy) |n| n.deinit(allocator);
|
||||
allocator.free(ae.body);
|
||||
},
|
||||
.spaceless => if (self.spaceless) |sl| {
|
||||
const body_copy = sl.body;
|
||||
for (body_copy) |n| n.deinit(allocator);
|
||||
allocator.free(sl.body);
|
||||
},
|
||||
.include => if (self.include) |inc| allocator.free(inc.template_name),
|
||||
.now => if (self.now) |n| allocator.free(n.format),
|
||||
.extends => if (self.extends) |e| allocator.free(e.parent_name),
|
||||
.super => {},
|
||||
}
|
||||
}
|
||||
|
|
@ -169,16 +219,6 @@ pub const Parser = struct {
|
|||
while (self.pos < self.template.len and std.ascii.isWhitespace(self.template[self.pos])) : (self.advance(1)) {}
|
||||
}
|
||||
|
||||
// fn parseAssignments(allocator: std.mem.Allocator, args: []const u8) ![]const Assignment {
|
||||
// var list = std.ArrayList(Assignment){};
|
||||
// defer list.deinit(allocator);
|
||||
//
|
||||
// // Implementação básica — você já tem uma melhor, use ela
|
||||
// // Por enquanto, só para passar teste
|
||||
// _ = args;
|
||||
// return try list.toOwnedSlice(allocator);
|
||||
// }
|
||||
|
||||
fn parseAssignments(
|
||||
self: *Parser,
|
||||
allocator: std.mem.Allocator,
|
||||
|
|
@ -242,6 +282,185 @@ pub const Parser = struct {
|
|||
return try list.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
fn parseAutoescapeBlock(self: *Parser, allocator: std.mem.Allocator, enabled: bool, raw_open: []const u8) !Node {
|
||||
var body = std.ArrayList(Node){};
|
||||
defer body.deinit(allocator);
|
||||
|
||||
var depth: usize = 1;
|
||||
|
||||
while (self.pos < self.template.len and depth > 0) {
|
||||
if (try self.parseText(allocator)) |node| {
|
||||
try body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
try body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try self.parseTag(allocator)) |tag_node| {
|
||||
const tag_name = tag_node.tag.?.name;
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "autoescape")) {
|
||||
depth += 1;
|
||||
try body.append(allocator, tag_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "endautoescape")) {
|
||||
depth -= 1;
|
||||
const raw_close = tag_node.tag.?.raw;
|
||||
|
||||
allocator.free(tag_node.tag.?.name);
|
||||
allocator.free(tag_node.tag.?.args);
|
||||
|
||||
if (depth == 0) {
|
||||
return Node{
|
||||
.type = .autoescape,
|
||||
.autoescape = .{
|
||||
.enabled = enabled,
|
||||
.body = try body.toOwnedSlice(allocator),
|
||||
.raw_open = raw_open,
|
||||
.raw_close = raw_close,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
try body.append(allocator, tag_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
try body.append(allocator, tag_node);
|
||||
} else {
|
||||
self.advance(1);
|
||||
}
|
||||
}
|
||||
|
||||
return error.UnclosedBlock;
|
||||
}
|
||||
|
||||
fn parseSpacelessBlock(self: *Parser, allocator: std.mem.Allocator, raw_open: []const u8) !Node {
|
||||
var body = std.ArrayList(Node){};
|
||||
defer body.deinit(allocator);
|
||||
|
||||
var depth: usize = 1;
|
||||
|
||||
while (self.pos < self.template.len and depth > 0) {
|
||||
if (try self.parseText(allocator)) |node| {
|
||||
// Adiciona texto (mesmo vazio — o renderer vai limpar depois)
|
||||
try body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
try body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try self.parseTag(allocator)) |tag_node| {
|
||||
const tag_name = tag_node.tag.?.name;
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "spaceless")) {
|
||||
depth += 1;
|
||||
// Ignora a tag open aninhada
|
||||
allocator.free(tag_node.tag.?.name);
|
||||
allocator.free(tag_node.tag.?.args);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "endspaceless")) {
|
||||
depth -= 1;
|
||||
const raw_close = tag_node.tag.?.raw;
|
||||
|
||||
allocator.free(tag_node.tag.?.name);
|
||||
allocator.free(tag_node.tag.?.args);
|
||||
|
||||
if (depth == 0) {
|
||||
return Node{
|
||||
.type = .spaceless,
|
||||
.spaceless = .{
|
||||
.body = try body.toOwnedSlice(allocator),
|
||||
.raw_open = raw_open,
|
||||
.raw_close = raw_close,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// depth > 0: endspaceless aninhado — IGNORA a tag
|
||||
// NÃO adicione ao body
|
||||
continue;
|
||||
}
|
||||
|
||||
try body.append(allocator, tag_node);
|
||||
} else {
|
||||
self.advance(1);
|
||||
}
|
||||
}
|
||||
|
||||
return error.UnclosedBlock;
|
||||
}
|
||||
|
||||
fn parseFilterBlock(self: *Parser, allocator: std.mem.Allocator, filters_raw: []const u8, raw_open: []const u8) !Node {
|
||||
var body = std.ArrayList(Node){};
|
||||
defer body.deinit(allocator);
|
||||
|
||||
var depth: usize = 1;
|
||||
|
||||
while (self.pos < self.template.len and depth > 0) {
|
||||
if (try self.parseText(allocator)) |node| {
|
||||
try body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
try body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try self.parseTag(allocator)) |tag_node| {
|
||||
const tag_name = tag_node.tag.?.name;
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "filter")) {
|
||||
depth += 1;
|
||||
try body.append(allocator, tag_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "endfilter")) {
|
||||
depth -= 1;
|
||||
const raw_close = tag_node.tag.?.raw;
|
||||
|
||||
allocator.free(tag_node.tag.?.name);
|
||||
allocator.free(tag_node.tag.?.args);
|
||||
|
||||
if (depth == 0) {
|
||||
// const filters = try allocator.dupe(u8, filters_raw);
|
||||
|
||||
return Node{
|
||||
.type = .filter_block,
|
||||
.filter_block = .{
|
||||
.filters = filters_raw, // já duped
|
||||
.body = try body.toOwnedSlice(allocator),
|
||||
.raw_open = raw_open,
|
||||
.raw_close = raw_close,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
try body.append(allocator, tag_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
try body.append(allocator, tag_node);
|
||||
} else {
|
||||
self.advance(1);
|
||||
}
|
||||
}
|
||||
|
||||
return error.UnclosedBlock;
|
||||
}
|
||||
|
||||
fn parseBlockBlock(self: *Parser, allocator: std.mem.Allocator, name: []const u8, raw_open: []const u8) !Node {
|
||||
var body = std.ArrayList(Node){};
|
||||
defer body.deinit(allocator);
|
||||
|
|
@ -256,9 +475,9 @@ pub const Parser = struct {
|
|||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
if (node.variable) |v| {
|
||||
if (std.mem.eql(u8, v.content, "block.super")) {
|
||||
if (std.mem.eql(u8, v.expr, "block.super")) {
|
||||
try body.append(allocator, Node{ .type = .super, .super = true });
|
||||
allocator.free(v.content);
|
||||
allocator.free(v.expr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -321,7 +540,7 @@ pub const Parser = struct {
|
|||
}
|
||||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.content});
|
||||
std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.expr});
|
||||
try body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -448,18 +667,20 @@ pub const Parser = struct {
|
|||
fn parseVariable(self: *Parser, allocator: std.mem.Allocator) !?Node {
|
||||
std.debug.print("2.0 - Vou verificar se sou variável\n", .{});
|
||||
std.debug.print("2.1 - meu start é {d}\n", .{self.pos});
|
||||
|
||||
if (self.peek(2)) |p| {
|
||||
if (!std.mem.eql(u8, p, "{{")) return null;
|
||||
std.debug.print("2.1 - fiz o peek de 2 em 2 até que achei {{{{\n", .{});
|
||||
} else return null;
|
||||
|
||||
self.advance(2);
|
||||
self.skipWhitespace();
|
||||
|
||||
const content_start = self.pos;
|
||||
const expr_start = self.pos;
|
||||
while (self.pos < self.template.len) : (self.advance(1)) {
|
||||
if (self.peek(2)) |p| {
|
||||
if (std.mem.eql(u8, p, "}}")) break;
|
||||
|
||||
std.debug.print("2.1 - fiz o peek de 2 em 2 até que achei {{{{\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -469,16 +690,45 @@ pub const Parser = struct {
|
|||
return error.UnclosedVariable;
|
||||
}
|
||||
|
||||
const raw_content = self.template[content_start..self.pos];
|
||||
const content = std.mem.trim(u8, raw_content, " \t\r\n");
|
||||
std.debug.print("2.3 - meu content é \'{s}\'\n", .{content});
|
||||
const full_expr = std.mem.trim(u8, self.template[expr_start..self.pos], " \t\r\n");
|
||||
|
||||
const duped = try allocator.dupe(u8, content);
|
||||
self.advance(2);
|
||||
|
||||
// Separar expr e filtros
|
||||
var filters = std.ArrayList(Filter){};
|
||||
defer filters.deinit(allocator);
|
||||
|
||||
var expr_end = full_expr.len;
|
||||
var pipe_pos = std.mem.lastIndexOfScalar(u8, full_expr, '|');
|
||||
while (pipe_pos) |pos| {
|
||||
const filter_part = std.mem.trim(u8, full_expr[pos + 1 .. expr_end], " \t\r\n");
|
||||
expr_end = pos;
|
||||
|
||||
const colon_pos = std.mem.indexOfScalar(u8, filter_part, ':');
|
||||
const filter_name = if (colon_pos) |cp| std.mem.trim(u8, filter_part[0..cp], " \t\r\n") else filter_part;
|
||||
const filter_arg = if (colon_pos) |cp| std.mem.trim(u8, filter_part[cp + 1 ..], " \"") else null;
|
||||
|
||||
try filters.append(allocator, .{
|
||||
.name = try allocator.dupe(u8, filter_name),
|
||||
.arg = if (filter_arg) |a| try allocator.dupe(u8, a) else null,
|
||||
});
|
||||
|
||||
pipe_pos = std.mem.lastIndexOfScalar(u8, full_expr[0..expr_end], '|');
|
||||
}
|
||||
|
||||
const base_expr = std.mem.trim(u8, full_expr[0..expr_end], " \t\r\n");
|
||||
const duped_expr = try allocator.dupe(u8, base_expr);
|
||||
|
||||
// Inverte os filters (porque usamos lastIndexOf)
|
||||
std.mem.reverse(Filter, filters.items);
|
||||
|
||||
std.debug.print("2.3 - meu conteúdo:\n - expr: \'{s}\' \n - filters: {any}\n", .{ duped_expr, filters });
|
||||
return Node{
|
||||
.type = .variable,
|
||||
.variable = .{ .content = duped },
|
||||
.variable = .{
|
||||
.expr = duped_expr,
|
||||
.filters = try filters.toOwnedSlice(allocator),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -547,7 +797,7 @@ pub const Parser = struct {
|
|||
}
|
||||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.content});
|
||||
std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.expr});
|
||||
try current_body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -628,7 +878,7 @@ pub const Parser = struct {
|
|||
}
|
||||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.content});
|
||||
std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.expr});
|
||||
try current_body.append(allocator, node);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -706,6 +956,42 @@ pub const Parser = struct {
|
|||
|
||||
std.debug.print("3.1 - meu tag name é: {s}\n", .{tag_name});
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "autoescape")) {
|
||||
const args = std.mem.trim(u8, node.tag.?.args, " \t\r\n");
|
||||
const raw_open = node.tag.?.raw;
|
||||
|
||||
const enabled = if (std.mem.eql(u8, args, "on"))
|
||||
true
|
||||
else if (std.mem.eql(u8, args, "off"))
|
||||
false
|
||||
else
|
||||
return error.InvalidAutoescapeArgument;
|
||||
|
||||
allocator.free(node.tag.?.name);
|
||||
allocator.free(node.tag.?.args);
|
||||
|
||||
std.debug.print("3.0 - na real sou um autoescape\n", .{});
|
||||
std.debug.print("===================================\n", .{});
|
||||
|
||||
const ae_node = try self.parseAutoescapeBlock(allocator, enabled, raw_open);
|
||||
try list.append(allocator, ae_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "spaceless")) {
|
||||
const raw_open = node.tag.?.raw;
|
||||
|
||||
allocator.free(node.tag.?.name);
|
||||
allocator.free(node.tag.?.args);
|
||||
|
||||
std.debug.print("3.0 - na real sou um spaceless\n", .{});
|
||||
std.debug.print("===================================\n", .{});
|
||||
|
||||
const spaceless_node = try self.parseSpacelessBlock(allocator, raw_open);
|
||||
try list.append(allocator, spaceless_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "comment")) {
|
||||
std.debug.print("3.0 - na real sou um comentário\n", .{});
|
||||
std.debug.print("===================================\n", .{});
|
||||
|
|
@ -715,6 +1001,24 @@ pub const Parser = struct {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "filter")) {
|
||||
const filters_raw = node.tag.?.args;
|
||||
const raw_open = node.tag.?.raw;
|
||||
|
||||
// DUPE O FILTERS IMEDIATAMENTE
|
||||
const duped_filters = try allocator.dupe(u8, filters_raw);
|
||||
|
||||
// Agora libera a tag open
|
||||
allocator.free(node.tag.?.name);
|
||||
allocator.free(node.tag.?.args);
|
||||
std.debug.print("3.0 - na real sou um filter\n", .{});
|
||||
std.debug.print("===================================\n", .{});
|
||||
|
||||
const filter_node = try self.parseFilterBlock(allocator, duped_filters, raw_open);
|
||||
try list.append(allocator, filter_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, tag_name, "extends")) {
|
||||
const parent = std.mem.trim(u8, node.tag.?.args, " \t\"");
|
||||
|
||||
|
|
@ -868,7 +1172,8 @@ pub const Parser = struct {
|
|||
|
||||
if (try self.parseVariable(allocator)) |node| {
|
||||
std.debug.print("3.0 - na real sou variável\n", .{});
|
||||
std.debug.print("4.0 - content: \'{s}\'\n", .{node.variable.?.content});
|
||||
std.debug.print("4.0 - content: \'{s}\'\n", .{node.variable.?.expr});
|
||||
std.debug.print("4.1 - filters: \'{any}\'\n", .{node.variable.?.filters});
|
||||
std.debug.print("===================================\n", .{});
|
||||
try list.append(allocator, node);
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -2,329 +2,329 @@ const std = @import("std");
|
|||
const testing = std.testing;
|
||||
const parser = @import("parser.zig");
|
||||
|
||||
// test "parse texto simples" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "Olá mundo!";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| {
|
||||
// node.deinit(allocator);
|
||||
// }
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .text);
|
||||
// try testing.expectEqualStrings("Olá mundo!", nodes[0].text.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse variável simples" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "Olá {{ nome }}!";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| {
|
||||
// node.deinit(allocator);
|
||||
// }
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 3), nodes.len);
|
||||
//
|
||||
// try testing.expect(nodes[0].type == .text);
|
||||
// try testing.expectEqualStrings("Olá ", nodes[0].text.?.content);
|
||||
//
|
||||
// try testing.expect(nodes[1].type == .variable);
|
||||
// try testing.expectEqualStrings("nome", nodes[1].variable.?.content);
|
||||
//
|
||||
// try testing.expect(nodes[2].type == .text);
|
||||
// try testing.expectEqualStrings("!", nodes[2].text.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse variável com espaços" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{{ espacos }}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| {
|
||||
// node.deinit(allocator);
|
||||
// }
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .variable);
|
||||
// try testing.expectEqualStrings("espacos", nodes[0].variable.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse tag simples" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "Antes {% minha_tag %} Depois";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| {
|
||||
// node.deinit(allocator);
|
||||
// }
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 3), nodes.len);
|
||||
//
|
||||
// try testing.expect(nodes[0].type == .text);
|
||||
// try testing.expectEqualStrings("Antes ", nodes[0].text.?.content);
|
||||
//
|
||||
// try testing.expect(nodes[1].type == .tag);
|
||||
// try testing.expectEqualStrings("minha_tag", nodes[1].tag.?.name);
|
||||
// try testing.expectEqualStrings("", nodes[1].tag.?.args);
|
||||
//
|
||||
// try testing.expect(nodes[2].type == .text);
|
||||
// try testing.expectEqualStrings(" Depois", nodes[2].text.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse if block básico" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% if usuario.logado %}Bem-vindo!{% endif %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| {
|
||||
// node.deinit(allocator);
|
||||
// }
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .if_block);
|
||||
//
|
||||
// const ib = nodes[0].@"if".?;
|
||||
// try testing.expectEqualStrings("usuario.logado", ib.condition);
|
||||
// try testing.expectEqual(@as(usize, 1), ib.true_body.len);
|
||||
// try testing.expect(nodes[0].@"if".?.true_body[0].type == .text);
|
||||
// try testing.expectEqualStrings("Bem-vindo!", ib.true_body[0].text.?.content);
|
||||
// try testing.expectEqual(@as(usize, 0), ib.false_body.len);
|
||||
// }
|
||||
//
|
||||
// test "parse if block sem else" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% if cond %}Verdadeiro{% endif %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| {
|
||||
// node.deinit(allocator);
|
||||
// }
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .if_block);
|
||||
// const ib = nodes[0].@"if".?;
|
||||
// try testing.expectEqualStrings("cond", ib.condition);
|
||||
// try testing.expectEqual(@as(usize, 1), ib.true_body.len);
|
||||
// try testing.expectEqualStrings("Verdadeiro", ib.true_body[0].text.?.content);
|
||||
// try testing.expectEqual(@as(usize, 0), ib.false_body.len);
|
||||
// }
|
||||
//
|
||||
// test "parse if block com else" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% if cond %}Verdadeiro{% else %}Falso{% endif %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| {
|
||||
// node.deinit(allocator);
|
||||
// }
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .if_block);
|
||||
// const ib = nodes[0].@"if".?;
|
||||
// try testing.expectEqualStrings("cond", ib.condition);
|
||||
// try testing.expectEqual(@as(usize, 1), ib.true_body.len);
|
||||
// try testing.expectEqualStrings("Verdadeiro", ib.true_body[0].text.?.content);
|
||||
// try testing.expectEqual(@as(usize, 1), ib.false_body.len);
|
||||
// try testing.expectEqualStrings("Falso", ib.false_body[0].text.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse for block sem empty" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% for item in lista %}Item: {{ item }}{% endfor %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .for_block);
|
||||
// const fb = nodes[0].@"for".?;
|
||||
// try testing.expectEqualStrings("item", fb.loop_var);
|
||||
// try testing.expectEqualStrings("lista", fb.iterable);
|
||||
// try testing.expectEqual(@as(usize, 2), fb.body.len); // <--- corrigido: 2 nós
|
||||
// try testing.expectEqual(@as(usize, 0), fb.empty_body.len);
|
||||
//
|
||||
// try testing.expect(fb.body[0].type == .text);
|
||||
// try testing.expectEqualStrings("Item: ", fb.body[0].text.?.content);
|
||||
// try testing.expect(fb.body[1].type == .variable);
|
||||
// try testing.expectEqualStrings("item", fb.body[1].variable.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse for block com empty" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% for item in lista %}Tem{% empty %}Vazio{% endfor %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .for_block);
|
||||
// const fb = nodes[0].@"for".?;
|
||||
// try testing.expectEqual(@as(usize, 1), fb.body.len);
|
||||
// try testing.expectEqualStrings("Tem", fb.body[0].text.?.content);
|
||||
// try testing.expectEqual(@as(usize, 1), fb.empty_body.len);
|
||||
// try testing.expectEqualStrings("Vazio", fb.empty_body[0].text.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse comment" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "Bazinga! {% comment %}{% for item in lista %}Tem{% empty %}Vazio{% endfor %}{% endcomment %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .text);
|
||||
// try testing.expectEqualStrings("Bazinga! ", nodes[0].text.?.content);
|
||||
// }
|
||||
//
|
||||
// test "parse include simples" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "Cabeçalho {% include \"header.zdt\" %} Conteúdo {% include \"footer.zdt\" %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 4), nodes.len);
|
||||
//
|
||||
// try testing.expect(nodes[0].type == .text);
|
||||
// try testing.expectEqualStrings("Cabeçalho ", nodes[0].text.?.content);
|
||||
//
|
||||
// try testing.expect(nodes[1].type == .include);
|
||||
// try testing.expectEqualStrings("header.zdt", nodes[1].include.?.template_name);
|
||||
//
|
||||
// try testing.expect(nodes[2].type == .text);
|
||||
// try testing.expectEqualStrings(" Conteúdo ", nodes[2].text.?.content);
|
||||
//
|
||||
// try testing.expect(nodes[3].type == .include);
|
||||
// try testing.expectEqualStrings("footer.zdt", nodes[3].include.?.template_name);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// test "parse include sem aspas (erro esperado no futuro, mas por enquanto aceita)" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% include header.zdt %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .include);
|
||||
// try testing.expectEqualStrings("header.zdt", nodes[0].include.?.template_name);
|
||||
// }
|
||||
//
|
||||
// test "parse with simples" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% with nome=\"Lucas\" idade=30 %}Olá {{ nome }}, você tem {{ idade }} anos.{% endwith %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .with_block);
|
||||
// const w = nodes[0].with.?;
|
||||
// try testing.expectEqual(@as(usize, 2), w.assignments.len);
|
||||
// try testing.expectEqual(@as(usize, 5), w.body.len);
|
||||
// try testing.expect(w.body[0].type == .text);
|
||||
// }
|
||||
//
|
||||
// test "parse now simples" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "Data atual: {% now \"Y-m-d\" %} às {% now \"H:i\" %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 4), nodes.len);
|
||||
//
|
||||
// try testing.expect(nodes[0].type == .text);
|
||||
// try testing.expectEqualStrings("Data atual: ", nodes[0].text.?.content);
|
||||
//
|
||||
// try testing.expect(nodes[1].type == .now);
|
||||
// try testing.expectEqualStrings("Y-m-d", nodes[1].now.?.format);
|
||||
//
|
||||
// try testing.expect(nodes[2].type == .text);
|
||||
// try testing.expectEqualStrings(" às ", nodes[2].text.?.content);
|
||||
//
|
||||
// try testing.expect(nodes[3].type == .now);
|
||||
// try testing.expectEqualStrings("H:i", nodes[3].now.?.format);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// test "parse now sem aspas" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% now Y-m-d %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .now);
|
||||
// try testing.expectEqualStrings("Y-m-d", nodes[0].now.?.format);
|
||||
// }
|
||||
//
|
||||
// test "parse extends" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% extends \"base.zdt\" %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .extends);
|
||||
// try testing.expectEqualStrings("base.zdt", nodes[0].extends.?.parent_name);
|
||||
// }
|
||||
//
|
||||
// test "parse block simples" {
|
||||
// const allocator = testing.allocator;
|
||||
// const template = "{% block titulo %}Meu Título{% endblock %}";
|
||||
// const nodes = try parser.parse(allocator, template);
|
||||
// defer {
|
||||
// for (nodes) |node| node.deinit(allocator);
|
||||
// allocator.free(nodes);
|
||||
// }
|
||||
//
|
||||
// try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
// try testing.expect(nodes[0].type == .block);
|
||||
// const b = nodes[0].block.?;
|
||||
// try testing.expectEqualStrings("titulo", b.name);
|
||||
// try testing.expectEqual(@as(usize, 1), b.body.len);
|
||||
// try testing.expectEqualStrings("Meu Título", b.body[0].text.?.content);
|
||||
// }
|
||||
//
|
||||
test "parse texto simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "Olá mundo!";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
}
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .text);
|
||||
try testing.expectEqualStrings("Olá mundo!", nodes[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse variável simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "Olá {{ nome }}!";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
}
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 3), nodes.len);
|
||||
|
||||
try testing.expect(nodes[0].type == .text);
|
||||
try testing.expectEqualStrings("Olá ", nodes[0].text.?.content);
|
||||
|
||||
try testing.expect(nodes[1].type == .variable);
|
||||
try testing.expectEqualStrings("nome", nodes[1].variable.?.expr);
|
||||
|
||||
try testing.expect(nodes[2].type == .text);
|
||||
try testing.expectEqualStrings("!", nodes[2].text.?.content);
|
||||
}
|
||||
|
||||
test "parse variável com espaços" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{{ espacos }}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
}
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .variable);
|
||||
try testing.expectEqualStrings("espacos", nodes[0].variable.?.expr);
|
||||
}
|
||||
|
||||
test "parse tag simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "Antes {% minha_tag %} Depois";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
}
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 3), nodes.len);
|
||||
|
||||
try testing.expect(nodes[0].type == .text);
|
||||
try testing.expectEqualStrings("Antes ", nodes[0].text.?.content);
|
||||
|
||||
try testing.expect(nodes[1].type == .tag);
|
||||
try testing.expectEqualStrings("minha_tag", nodes[1].tag.?.name);
|
||||
try testing.expectEqualStrings("", nodes[1].tag.?.args);
|
||||
|
||||
try testing.expect(nodes[2].type == .text);
|
||||
try testing.expectEqualStrings(" Depois", nodes[2].text.?.content);
|
||||
}
|
||||
|
||||
test "parse if block básico" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% if usuario.logado %}Bem-vindo!{% endif %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
}
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .if_block);
|
||||
|
||||
const ib = nodes[0].@"if".?;
|
||||
try testing.expectEqualStrings("usuario.logado", ib.condition);
|
||||
try testing.expectEqual(@as(usize, 1), ib.true_body.len);
|
||||
try testing.expect(nodes[0].@"if".?.true_body[0].type == .text);
|
||||
try testing.expectEqualStrings("Bem-vindo!", ib.true_body[0].text.?.content);
|
||||
try testing.expectEqual(@as(usize, 0), ib.false_body.len);
|
||||
}
|
||||
|
||||
test "parse if block sem else" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% if cond %}Verdadeiro{% endif %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
}
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .if_block);
|
||||
const ib = nodes[0].@"if".?;
|
||||
try testing.expectEqualStrings("cond", ib.condition);
|
||||
try testing.expectEqual(@as(usize, 1), ib.true_body.len);
|
||||
try testing.expectEqualStrings("Verdadeiro", ib.true_body[0].text.?.content);
|
||||
try testing.expectEqual(@as(usize, 0), ib.false_body.len);
|
||||
}
|
||||
|
||||
test "parse if block com else" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% if cond %}Verdadeiro{% else %}Falso{% endif %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
}
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .if_block);
|
||||
const ib = nodes[0].@"if".?;
|
||||
try testing.expectEqualStrings("cond", ib.condition);
|
||||
try testing.expectEqual(@as(usize, 1), ib.true_body.len);
|
||||
try testing.expectEqualStrings("Verdadeiro", ib.true_body[0].text.?.content);
|
||||
try testing.expectEqual(@as(usize, 1), ib.false_body.len);
|
||||
try testing.expectEqualStrings("Falso", ib.false_body[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse for block sem empty" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% for item in lista %}Item: {{ item }}{% endfor %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .for_block);
|
||||
const fb = nodes[0].@"for".?;
|
||||
try testing.expectEqualStrings("item", fb.loop_var);
|
||||
try testing.expectEqualStrings("lista", fb.iterable);
|
||||
try testing.expectEqual(@as(usize, 2), fb.body.len); // <--- corrigido: 2 nós
|
||||
try testing.expectEqual(@as(usize, 0), fb.empty_body.len);
|
||||
|
||||
try testing.expect(fb.body[0].type == .text);
|
||||
try testing.expectEqualStrings("Item: ", fb.body[0].text.?.content);
|
||||
try testing.expect(fb.body[1].type == .variable);
|
||||
try testing.expectEqualStrings("item", fb.body[1].variable.?.expr);
|
||||
}
|
||||
|
||||
test "parse for block com empty" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% for item in lista %}Tem{% empty %}Vazio{% endfor %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .for_block);
|
||||
const fb = nodes[0].@"for".?;
|
||||
try testing.expectEqual(@as(usize, 1), fb.body.len);
|
||||
try testing.expectEqualStrings("Tem", fb.body[0].text.?.content);
|
||||
try testing.expectEqual(@as(usize, 1), fb.empty_body.len);
|
||||
try testing.expectEqualStrings("Vazio", fb.empty_body[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse comment" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "Bazinga! {% comment %}{% for item in lista %}Tem{% empty %}Vazio{% endfor %}{% endcomment %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .text);
|
||||
try testing.expectEqualStrings("Bazinga! ", nodes[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse include simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "Cabeçalho {% include \"header.zdt\" %} Conteúdo {% include \"footer.zdt\" %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 4), nodes.len);
|
||||
|
||||
try testing.expect(nodes[0].type == .text);
|
||||
try testing.expectEqualStrings("Cabeçalho ", nodes[0].text.?.content);
|
||||
|
||||
try testing.expect(nodes[1].type == .include);
|
||||
try testing.expectEqualStrings("header.zdt", nodes[1].include.?.template_name);
|
||||
|
||||
try testing.expect(nodes[2].type == .text);
|
||||
try testing.expectEqualStrings(" Conteúdo ", nodes[2].text.?.content);
|
||||
|
||||
try testing.expect(nodes[3].type == .include);
|
||||
try testing.expectEqualStrings("footer.zdt", nodes[3].include.?.template_name);
|
||||
|
||||
}
|
||||
|
||||
test "parse include sem aspas (erro esperado no futuro, mas por enquanto aceita)" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% include header.zdt %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .include);
|
||||
try testing.expectEqualStrings("header.zdt", nodes[0].include.?.template_name);
|
||||
}
|
||||
|
||||
test "parse with simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% with nome=\"Lucas\" idade=30 %}Olá {{ nome }}, você tem {{ idade }} anos.{% endwith %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .with_block);
|
||||
const w = nodes[0].with.?;
|
||||
try testing.expectEqual(@as(usize, 2), w.assignments.len);
|
||||
try testing.expectEqual(@as(usize, 5), w.body.len);
|
||||
try testing.expect(w.body[0].type == .text);
|
||||
}
|
||||
|
||||
test "parse now simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "Data atual: {% now \"Y-m-d\" %} às {% now \"H:i\" %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 4), nodes.len);
|
||||
|
||||
try testing.expect(nodes[0].type == .text);
|
||||
try testing.expectEqualStrings("Data atual: ", nodes[0].text.?.content);
|
||||
|
||||
try testing.expect(nodes[1].type == .now);
|
||||
try testing.expectEqualStrings("Y-m-d", nodes[1].now.?.format);
|
||||
|
||||
try testing.expect(nodes[2].type == .text);
|
||||
try testing.expectEqualStrings(" às ", nodes[2].text.?.content);
|
||||
|
||||
try testing.expect(nodes[3].type == .now);
|
||||
try testing.expectEqualStrings("H:i", nodes[3].now.?.format);
|
||||
|
||||
}
|
||||
|
||||
test "parse now sem aspas" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% now Y-m-d %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .now);
|
||||
try testing.expectEqualStrings("Y-m-d", nodes[0].now.?.format);
|
||||
}
|
||||
|
||||
test "parse extends" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% extends \"base.zdt\" %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .extends);
|
||||
try testing.expectEqualStrings("base.zdt", nodes[0].extends.?.parent_name);
|
||||
}
|
||||
|
||||
test "parse block simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% block titulo %}Meu Título{% endblock %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .block);
|
||||
const b = nodes[0].block.?;
|
||||
try testing.expectEqualStrings("titulo", b.name);
|
||||
try testing.expectEqual(@as(usize, 1), b.body.len);
|
||||
try testing.expectEqualStrings("Meu Título", b.body[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse block com super" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% block conteudo %}{{ block.super }} Conteúdo filho{% endblock %}";
|
||||
|
|
@ -342,3 +342,132 @@ test "parse block com super" {
|
|||
// try testing.expectEqualStrings("conteudo", b.name);
|
||||
try testing.expectEqualStrings(" Conteúdo filho", b.body[1].text.?.content);
|
||||
}
|
||||
|
||||
|
||||
test "parse filter block simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% filter upper %}olá mundo{% endfilter %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .filter_block);
|
||||
const fb = nodes[0].filter_block.?;
|
||||
try testing.expectEqualStrings("upper", fb.filters); // correto
|
||||
try testing.expectEqual(@as(usize, 1), fb.body.len);
|
||||
try testing.expectEqualStrings("olá mundo", fb.body[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse filter block com múltiplos filtros" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% filter upper|escape %}Conteúdo <b>negrito</b>{% endfilter %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .filter_block);
|
||||
const fb = nodes[0].filter_block.?;
|
||||
try testing.expectEqualStrings("upper|escape", fb.filters);
|
||||
try testing.expectEqual(@as(usize, 1), fb.body.len);
|
||||
try testing.expectEqualStrings("Conteúdo <b>negrito</b>", fb.body[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse variable com filtros" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{{ nome|upper|default:\"Visitante\" }}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .variable);
|
||||
const v = nodes[0].variable.?;
|
||||
try testing.expectEqualStrings("nome", v.expr);
|
||||
try testing.expectEqual(@as(usize, 2), v.filters.len);
|
||||
try testing.expectEqualStrings("upper", v.filters[0].name);
|
||||
try testing.expect(v.filters[0].arg == null);
|
||||
try testing.expectEqualStrings("default", v.filters[1].name);
|
||||
try testing.expectEqualStrings("Visitante", v.filters[1].arg.?);
|
||||
}
|
||||
|
||||
test "parse autoescape on" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% autoescape on %}Texto <b>negrito</b>{% endautoescape %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .autoescape);
|
||||
const ae = nodes[0].autoescape.?;
|
||||
try testing.expect(ae.enabled == true);
|
||||
try testing.expectEqual(@as(usize, 1), ae.body.len);
|
||||
try testing.expectEqualStrings("Texto <b>negrito</b>", ae.body[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse autoescape off" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% autoescape off %}Texto <b>negrito</b>{% endautoescape %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .autoescape);
|
||||
const ae = nodes[0].autoescape.?;
|
||||
try testing.expect(ae.enabled == false);
|
||||
try testing.expectEqual(@as(usize, 1), ae.body.len);
|
||||
}
|
||||
|
||||
test "parse spaceless simples" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% spaceless %}<p> Texto com espaços </p>{% endspaceless %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .spaceless);
|
||||
const sl = nodes[0].spaceless.?;
|
||||
try testing.expectEqual(@as(usize, 1), sl.body.len);
|
||||
try testing.expectEqualStrings("<p> Texto com espaços </p>", sl.body[0].text.?.content);
|
||||
}
|
||||
|
||||
test "parse spaceless aninhado" {
|
||||
const allocator = testing.allocator;
|
||||
const template = "{% spaceless %}Outer {% spaceless %}Inner{% endspaceless %} Outer{% endspaceless %}";
|
||||
const nodes = try parser.parse(allocator, template);
|
||||
defer {
|
||||
for (nodes) |node| node.deinit(allocator);
|
||||
allocator.free(nodes);
|
||||
}
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), nodes.len);
|
||||
try testing.expect(nodes[0].type == .spaceless);
|
||||
const sl = nodes[0].spaceless.?;
|
||||
for (sl.body) |b| {
|
||||
std.debug.print("type: {s}\n", .{@tagName(b.type)});
|
||||
if (b.type == .spaceless) {
|
||||
std.debug.print(" - tag -> spaceless\n", .{});
|
||||
}
|
||||
if(b.type == .text) {
|
||||
std.debug.print(" - text -> {s}\n", .{b.text.?.content});
|
||||
}
|
||||
std.debug.print("----------\n", .{});
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 3), sl.body.len);
|
||||
}
|
||||
|
|
|
|||
15
todo.md
15
todo.md
|
|
@ -7,7 +7,7 @@
|
|||
- [ ] cycle
|
||||
- [ ] debug
|
||||
- [x] extends
|
||||
- [ ] filter
|
||||
- [x] filter
|
||||
- [ ] firstof
|
||||
- [x] for
|
||||
- [x] if
|
||||
|
|
@ -89,3 +89,16 @@
|
|||
- [ ] wordwrap
|
||||
- [ ] yesno
|
||||
|
||||
___
|
||||
|
||||
## Doing
|
||||
|
||||
- [x] filter — super útil (ex.: {{ var|upper }})
|
||||
- [x] autoescape — segurança importante
|
||||
- [x] spaceless — remove espaços em branco
|
||||
- [ ] verbatim — como raw
|
||||
- [ ] url — reverse de URLs (quando tiver routing)
|
||||
- [ ] cycle — alternar valores em loop
|
||||
- [ ] firstof — fallback de variáveis
|
||||
- [ ] load — para custom tags/filters (futuro)
|
||||
- [ ] csrf_token — quando tiver web
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue