From 1e0329a597f03471d59355e7113c0dbcced6ed53 Mon Sep 17 00:00:00 2001 From: "Lucas F." Date: Sat, 3 Jan 2026 21:06:54 -0300 Subject: [PATCH] update: add debug, partialdef and partial --- src/parser.zig | 141 +++++++++++++++++++++++++++++++++++++++++++- src/parser_test.zig | 63 ++++++++++++++++++++ todo.md | 10 ++-- 3 files changed, 207 insertions(+), 7 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index fdce959..addc0fd 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -21,6 +21,20 @@ pub const NodeType = enum { load, csrf_token, lorem, + debug, + partialdef, + partial, +}; + +pub const PartialDefNode = struct { + name: []const u8, + body: []Node, + raw_open: []const u8, + raw_close: []const u8, +}; + +pub const PartialNode = struct { + name: []const u8, }; pub const LoremNode = struct { @@ -156,7 +170,10 @@ pub const Node = struct { firstof: ?FirstOfNode = null, load: ?LoadNode = null, lorem: ?LoremNode = null, + partialdef: ?PartialDefNode = null, + partial: ?PartialNode = null, csrf_token: bool = false, + debug: bool = false, pub fn deinit(self: Node, allocator: std.mem.Allocator) void { switch (self.type) { @@ -251,6 +268,15 @@ pub const Node = struct { if (l.method) |m| allocator.free(m); if (l.format) |f| allocator.free(f); }, + .debug => {}, + .partialdef => if (self.partialdef) |pd| { + allocator.free(pd.name); + for (pd.body) |n| n.deinit(allocator); + allocator.free(pd.body); + }, + .partial => if (self.partial) |p| { + allocator.free(p.name); + }, } } }; @@ -340,6 +366,64 @@ pub const Parser = struct { return try list.toOwnedSlice(allocator); } + fn parsePartialDefBlock(self: *Parser, allocator: std.mem.Allocator, name: []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, "partialdef")) { + depth += 1; + try body.append(allocator, tag_node); + continue; + } + + if (std.mem.eql(u8, tag_name, "endpartialdef")) { + 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 = .partialdef, + .partialdef = .{ + .name = try allocator.dupe(u8, name), + .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 parseAutoescapeBlock(self: *Parser, allocator: std.mem.Allocator, enabled: bool, raw_open: []const u8) !Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); @@ -1106,6 +1190,41 @@ pub const Parser = struct { continue; } + if (std.mem.eql(u8, tag_name, "partialdef")) { + const partial_name = std.mem.trim(u8, node.tag.?.args, " \t\r\n"); + const raw_open = node.tag.?.raw; + + const duped_name = try allocator.dupe(u8, partial_name); + + allocator.free(node.tag.?.name); + allocator.free(node.tag.?.args); + + std.debug.print("3.0 - na real sou um partialdef\n", .{}); + std.debug.print("===================================\n", .{}); + + const partialdef_node = try self.parsePartialDefBlock(allocator, duped_name, raw_open); + try list.append(allocator, partialdef_node); + continue; + } + + if (std.mem.eql(u8, tag_name, "partial")) { + const partial_name = std.mem.trim(u8, node.tag.?.args, " \t\r\n\"'"); + + const duped_name = try allocator.dupe(u8, partial_name); + + allocator.free(node.tag.?.name); + allocator.free(node.tag.?.args); + + std.debug.print("3.0 - na real sou um partial\n", .{}); + std.debug.print("===================================\n", .{}); + + try list.append(allocator, Node{ + .type = .partial, + .partial = .{ .name = duped_name }, + }); + continue; + } + if (std.mem.eql(u8, tag_name, "lorem")) { const args = node.tag.?.args; @@ -1129,9 +1248,8 @@ pub const Parser = struct { method = try allocator.dupe(u8, trimmed); } else if (std.mem.eql(u8, trimmed, "html")) { std.debug.print("trimmed: {s}\n", .{trimmed}); - format = try allocator.dupe(u8, trimmed); + format = try allocator.dupe(u8, trimmed); } - } allocator.free(node.tag.?.name); @@ -1515,6 +1633,25 @@ pub const Parser = struct { continue; } + if (std.mem.eql(u8, tag_name, "debug")) { + // Verifica se tem argumentos (não deve ter) + if (node.tag.?.args.len > 0 and !std.mem.allEqual(u8, node.tag.?.args, ' ')) { + return error.InvalidDebugArgs; + } + + allocator.free(node.tag.?.name); + allocator.free(node.tag.?.args); + + std.debug.print("3.0 - na real sou um debug\n", .{}); + std.debug.print("===================================\n", .{}); + + try list.append(allocator, Node{ + .type = .debug, + .debug = true, + }); + continue; + } + if (std.mem.eql(u8, tag_name, "csrf_token")) { // Verifica se tem argumentos (não deve ter) if (node.tag.?.args.len > 0 and !std.mem.allEqual(u8, node.tag.?.args, ' ')) { diff --git a/src/parser_test.zig b/src/parser_test.zig index 8a47cdb..80a1a2c 100644 --- a/src/parser_test.zig +++ b/src/parser_test.zig @@ -709,3 +709,66 @@ test "parse lorem com argumentos" { try testing.expectEqualStrings("p", l.method.?); try testing.expectEqualStrings("html", l.format.?); } + +test "parse debug simples" { + const allocator = testing.allocator; + const template = "Antes {% debug %} 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 == .debug); + + try testing.expect(nodes[2].type == .text); + try testing.expectEqualStrings(" Depois", nodes[2].text.?.content); +} + +test "parse debug sozinho" { + const allocator = testing.allocator; + const template = "{% debug %}"; + 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 == .debug); +} + +test "parse partialdef simples" { + const allocator = testing.allocator; + const template = "{% partialdef cabecalho %}Cabeçalho{% endpartialdef %}"; + 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 == .partialdef); + const pd = nodes[0].partialdef.?; + try testing.expectEqualStrings("cabecalho", pd.name); + try testing.expectEqual(@as(usize, 1), pd.body.len); + try testing.expectEqualStrings("Cabeçalho", pd.body[0].text.?.content); +} + +test "parse partial uso" { + const allocator = testing.allocator; + const template = "Início {% partial \"cabecalho\" %} 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 == .partial); + try testing.expectEqualStrings("cabecalho", nodes[1].partial.?.name); +} diff --git a/todo.md b/todo.md index 6103185..a071a4c 100644 --- a/todo.md +++ b/todo.md @@ -5,7 +5,7 @@ - [x] comment - [x] csrf_token - [x] cycle -- [ ] debug +- [x] debug - [x] extends - [x] filter - [x] firstof @@ -16,8 +16,8 @@ - [x] load - [x] lorem - [x] now -- [ ] partial -- [ ] partialdef +- [x] partial +- [x] partialdef - [ ] querystring - [ ] regroup - [ ] resetcycle @@ -108,9 +108,9 @@ ___ ## To do 1 - Finalizar o parser — completar as tags que faltam da lista: -- [ ] debug +- [x] debug - [x] lorem -- [ ] partial / partialdef +- [x] partial / partialdef - [ ] querystring - [ ] regroup - [ ] resetcycle