From 0891dca8bf4adafd083c67488abdf48c6a56a94e Mon Sep 17 00:00:00 2001 From: "Lucas F." Date: Sat, 3 Jan 2026 18:46:04 -0300 Subject: [PATCH] update: add url --- src/parser.zig | 66 +++ src/parser_test.zig | 1030 ++++++++++++++++++++++--------------------- todo.md | 2 +- 3 files changed, 599 insertions(+), 499 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index 8b30a6e..54abcee 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -15,6 +15,7 @@ pub const NodeType = enum { filter_block, autoescape, spaceless, + url, }; pub const AutoescapeNode = struct { @@ -24,6 +25,11 @@ pub const AutoescapeNode = struct { enabled: bool, }; +pub const UrlNode = struct { + name: []const u8, + args: []const []const u8, +}; + pub const SpacelessNode = struct { body: []Node, raw_open: []const u8, @@ -122,6 +128,7 @@ pub const Node = struct { filter_block: ?FilterBlockNode = null, autoescape: ?AutoescapeNode = null, spaceless: ?SpacelessNode = null, + url: ?UrlNode = null, pub fn deinit(self: Node, allocator: std.mem.Allocator) void { switch (self.type) { @@ -193,6 +200,11 @@ pub const Node = struct { .now => if (self.now) |n| allocator.free(n.format), .extends => if (self.extends) |e| allocator.free(e.parent_name), .super => {}, + .url => if (self.url) |u| { + allocator.free(u.name); + for (u.args) |a| allocator.free(a); + allocator.free(u.args); + }, } } }; @@ -1223,6 +1235,60 @@ pub const Parser = struct { continue; } + if (std.mem.eql(u8, tag_name, "url")) { + const args = node.tag.?.args; + + var arg_list = std.ArrayList([]const u8){}; + defer arg_list.deinit(allocator); + + var i: usize = 0; + // Pula o nome da view (entre aspas) + while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {} + if (i >= args.len or args[i] != '\'') return error.InvalidUrlSyntax; + i += 1; + const view_start = i; + while (i < args.len and args[i] != '\'') : (i += 1) {} + if (i >= args.len or args[i] != '\'') return error.InvalidUrlSyntax; + const view_name = args[view_start..i]; + i += 1; + + const duped_view = try allocator.dupe(u8, view_name); + + // Agora os argumentos + while (i < args.len) { + while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {} + if (i >= args.len) break; + + const arg_start = i; + if (args[i] == '"' or args[i] == '\'') { + const quote = args[i]; + i += 1; + while (i < args.len and args[i] != quote) : (i += 1) {} + if (i >= args.len) return error.UnclosedQuoteInUrl; + i += 1; + } else { + while (i < args.len and !std.ascii.isWhitespace(args[i])) : (i += 1) {} + } + const arg = args[arg_start..i]; + try arg_list.append(allocator, try allocator.dupe(u8, arg)); + } + + allocator.free(node.tag.?.name); + allocator.free(node.tag.?.args); + + std.debug.print("3.0 - na real sou uma url\n", .{}); + std.debug.print("===================================\n", .{}); + + try list.append(allocator, Node{ + .type = .url, + .url = .{ + .name = duped_view, + .args = try arg_list.toOwnedSlice(allocator), + }, + }); + continue; + } + // Para tags normais std.debug.print("===================================\n", .{}); try list.append(allocator, node); diff --git a/src/parser_test.zig b/src/parser_test.zig index 6fb7e7f..373387b 100644 --- a/src/parser_test.zig +++ b/src/parser_test.zig @@ -2,501 +2,535 @@ 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.?.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 %}"; - 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.expectEqual(@as(usize, 2), b.body.len); - try testing.expect(b.body[0].type == .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 negrito{% 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 negrito", 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 negrito{% 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 negrito", ae.body[0].text.?.content); -} - -test "parse autoescape off" { - const allocator = testing.allocator; - const template = "{% autoescape off %}Texto negrito{% 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 %}

Texto com espaços

{% 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("

Texto com espaços

", 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); -} - -test "parse verbatim simples" { - const allocator = testing.allocator; - const template = "Texto {% verbatim %}{{ variável }}{% endblock %}{% endverbatim %} Texto"; - 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.expectEqualStrings("Texto ", nodes[0].text.?.content); - try testing.expectEqualStrings("{{ variável }}{% endblock %}", nodes[1].text.?.content); - try testing.expectEqualStrings(" Texto", nodes[2].text.?.content); -} - -test "parse verbatim aninhado" { - const allocator = testing.allocator; - const template = "{% verbatim %}Outer {% verbatim %}Inner{% endverbatim %} Outer{% endverbatim %}"; - 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("Outer {% verbatim %}Inner{% endverbatim %} Outer", nodes[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 %}"; +// 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.expectEqual(@as(usize, 2), b.body.len); +// try testing.expect(b.body[0].type == .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 negrito{% 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 negrito", 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 negrito{% 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 negrito", ae.body[0].text.?.content); +// } +// +// test "parse autoescape off" { +// const allocator = testing.allocator; +// const template = "{% autoescape off %}Texto negrito{% 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 %}

Texto com espaços

{% 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("

Texto com espaços

", 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); +// } +// +// test "parse verbatim simples" { +// const allocator = testing.allocator; +// const template = "Texto {% verbatim %}{{ variável }}{% endblock %}{% endverbatim %} Texto"; +// 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.expectEqualStrings("Texto ", nodes[0].text.?.content); +// try testing.expectEqualStrings("{{ variável }}{% endblock %}", nodes[1].text.?.content); +// try testing.expectEqualStrings(" Texto", nodes[2].text.?.content); +// } +// +// test "parse verbatim aninhado" { +// const allocator = testing.allocator; +// const template = "{% verbatim %}Outer {% verbatim %}Inner{% endverbatim %} Outer{% endverbatim %}"; +// 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("Outer {% verbatim %}Inner{% endverbatim %} Outer", nodes[0].text.?.content); +// } +// +// test "parse url simples" { +// const allocator = testing.allocator; +// const template = "Link: {% url 'home' %} Fim"; +// 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[1].type == .url); +// const u = nodes[1].url.?; +// try testing.expectEqualStrings("home", u.name); +// try testing.expectEqual(@as(usize, 0), u.args.len); +// } +// +// test "parse url com argumentos" { +// const allocator = testing.allocator; +// const template = "{% url 'post_detail' post.id \"comentario\" %}"; +// 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 == .url); +// const u = nodes[0].url.?; +// try testing.expectEqualStrings("post_detail", u.name); +// try testing.expectEqual(@as(usize, 2), u.args.len); +// try testing.expectEqualStrings("post.id", u.args[0]); +// try testing.expectEqualStrings("\"comentario\"", u.args[1]); +// } diff --git a/todo.md b/todo.md index a422649..d266aa0 100644 --- a/todo.md +++ b/todo.md @@ -97,7 +97,7 @@ ___ - [x] autoescape — segurança importante - [x] spaceless — remove espaços em branco - [x] verbatim — como raw -- [ ] url — reverse de URLs (quando tiver routing) +- [x] url — reverse de URLs (quando tiver routing) - [ ] cycle — alternar valores em loop - [ ] firstof — fallback de variáveis - [ ] load — para custom tags/filters (futuro)