From d4538db83a9a922d3e871f6efb054715aec146d1 Mon Sep 17 00:00:00 2001 From: "Lucas F." Date: Wed, 14 Jan 2026 14:29:01 -0300 Subject: [PATCH] update: parseTagContent --- src/parser.zig | 871 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 697 insertions(+), 174 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index 69126ec..d77a292 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -2,7 +2,6 @@ const std = @import("std"); const RenderError = @import("renderer.zig").RenderError; pub const ParserError = error{ - ParseError, InvalidAssignmentSyntax, InvalidAutoescapeArgument, InvalidCsrfTokenArgs, @@ -13,7 +12,7 @@ pub const ParserError = error{ InvalidUrlSyntax, InvalidWidthRatioSyntax, OutOfMemory, - UnexpectedToken, + ParseError, UnclosedBlock, UnclosedComment, UnclosedQuoteInAssignment, @@ -21,37 +20,38 @@ pub const ParserError = error{ UnclosedTag, UnclosedVariable, UnclosedVerbatim, + UnexpectedToken, }; pub const NodeType = enum { - text, - variable, - tag, - if_block, - for_block, - include, - with_block, - now, - extends, - block, - super, - filter_block, autoescape, - spaceless, - url, - cycle, - firstof, - load, + block, csrf_token, - lorem, + cycle, debug, - partialdef, + extends, + filter_block, + firstof, + for_block, + if_block, + include, + load, + lorem, + now, partial, + partialdef, querystring, regroup, resetcycle, - widthratio, + spaceless, + super, + tag, templatetag, + text, + url, + variable, + widthratio, + with_block, }; pub const TemplateTagNode = struct { @@ -226,7 +226,6 @@ pub const Node = struct { now: ?NowNode = null, extends: ?ExtendsNode = null, block: ?BlockNode = null, - super: bool = false, // para {{ block.super }} filter_block: ?FilterBlockNode = null, autoescape: ?AutoescapeNode = null, spaceless: ?SpacelessNode = null, @@ -242,6 +241,7 @@ pub const Node = struct { resetcycle: ?ResetCycleNode = null, widthratio: ?WidthRatioNode = null, templatetag: ?TemplateTagNode = null, + super: bool = false, csrf_token: bool = false, debug: bool = false, @@ -519,10 +519,10 @@ pub const Node = struct { }; }, .with_block => { - const body_copy = try allocator.alloc(Node, self.block.?.body.len); + const body_copy = try allocator.alloc(Node, self.with.?.body.len); errdefer allocator.free(body_copy); - for (self.block.?.body, 0..) |child, j| { + for (self.with.?.body, 0..) |child, j| { body_copy[j] = try child.clone(allocator); } @@ -748,7 +748,7 @@ pub const Parser = struct { self: *Parser, allocator: std.mem.Allocator, args: []const u8, - ) ![]const Assignment { + ) ParserError![]const Assignment { var list = std.ArrayList(Assignment){}; defer list.deinit(allocator); @@ -807,7 +807,7 @@ 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 { + fn parsePartialDefBlock(self: *Parser, allocator: std.mem.Allocator, name: []const u8, raw_open: []const u8) ParserError!Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); @@ -865,7 +865,7 @@ pub const Parser = struct { return error.UnclosedBlock; } - fn parseAutoescapeBlock(self: *Parser, allocator: std.mem.Allocator, enabled: bool, raw_open: []const u8) !Node { + fn parseAutoescapeBlock(self: *Parser, allocator: std.mem.Allocator, enabled: bool, raw_open: []const u8) ParserError!Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); @@ -923,7 +923,7 @@ pub const Parser = struct { return error.UnclosedBlock; } - fn parseVerbatim(self: *Parser, allocator: std.mem.Allocator) !Node { + fn parseVerbatim(self: *Parser, allocator: std.mem.Allocator) ParserError!Node { const start = self.pos; var depth: usize = 1; @@ -969,7 +969,7 @@ pub const Parser = struct { return error.UnclosedVerbatim; } - fn parseSpacelessBlock(self: *Parser, allocator: std.mem.Allocator, raw_open: []const u8) !Node { + fn parseSpacelessBlock(self: *Parser, allocator: std.mem.Allocator, raw_open: []const u8) ParserError!Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); @@ -1030,7 +1030,7 @@ pub const Parser = struct { return error.UnclosedBlock; } - fn parseFilterBlock(self: *Parser, allocator: std.mem.Allocator, filters_raw: []const u8, raw_open: []const u8) !Node { + fn parseFilterBlock(self: *Parser, allocator: std.mem.Allocator, filters_raw: []const u8, raw_open: []const u8) ParserError!Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); @@ -1090,7 +1090,7 @@ pub const Parser = struct { return error.UnclosedBlock; } - fn parseBlockBlock(self: *Parser, allocator: std.mem.Allocator, name: []const u8, raw_open: []const u8) !Node { + fn parseBlockBlock(self: *Parser, allocator: std.mem.Allocator, name: []const u8, raw_open: []const u8) ParserError!Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); @@ -1146,6 +1146,19 @@ pub const Parser = struct { continue; } + if (std.mem.eql(u8, tag_name, "comment")) { + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + try self.parseComment(); + continue; + } else { + const node_parsed: ?Node = self.parseTagContent(allocator, tag_node) catch null; + if (node_parsed) |n| { + try body.append(allocator, n); + continue; + } + } + try body.append(allocator, tag_node); } else { self.advance(1); @@ -1155,8 +1168,7 @@ pub const Parser = struct { return error.UnclosedBlock; } - fn parseWithBlock(self: *Parser, allocator: std.mem.Allocator, assignments: []const Assignment, raw_open: []const u8) !Node { - std.debug.print("Vou verificar se sou bloco with\n", .{}); + fn parseWithBlock(self: *Parser, allocator: std.mem.Allocator, assignments: []const Assignment, raw_open: []const u8) ParserError!Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); @@ -1164,13 +1176,11 @@ pub const Parser = struct { while (self.pos < self.template.len and depth > 0) { if (try self.parseText(allocator)) |node| { - std.debug.print("2.3 - Encontrei um texto: {s}\n", .{node.text.?.content}); try body.append(allocator, node); continue; } if (try self.parseVariable(allocator)) |node| { - std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.expr}); try body.append(allocator, node); continue; } @@ -1192,14 +1202,6 @@ pub const Parser = struct { allocator.free(tag_node.tag.?.args); if (depth == 0) { - // para fins de debug - std.debug.print("2.4 - Encontrei um bloco with:\n - assignments: {any}\n - body: {any}\n - raw_open: {s}\n - raw_close: {s}\n", .{ - assignments, - body.items, - raw_open, - raw_close, - }); - // fim para fins de debug return Node{ .type = .with_block, .with = .{ @@ -1224,7 +1226,7 @@ pub const Parser = struct { return error.UnclosedBlock; } - fn parseComment(self: *Parser) !void { + fn parseComment(self: *Parser) ParserError!void { // Consome a tag open {% comment %} // Já estamos após o %}, então só avançamos var depth: usize = 1; @@ -1268,16 +1270,12 @@ pub const Parser = struct { return error.UnclosedComment; } - fn parseText(self: *Parser, allocator: std.mem.Allocator) !?Node { - std.debug.print("2.0 - Vou verificar se sou texto\n", .{}); + fn parseText(self: *Parser, allocator: std.mem.Allocator) ParserError!?Node { const start = self.pos; - std.debug.print("2.1 - meu start é {d}\n", .{start}); - while (self.pos < self.template.len) { if (self.peek(2)) |p| { if (std.mem.eql(u8, p, "{{") or std.mem.eql(u8, p, "{%")) { - std.debug.print("2.2 - fiz o peek de 2 em 2 até que achei {{{{ ou {{%, então parei\n", .{}); break; } } @@ -1287,17 +1285,13 @@ pub const Parser = struct { if (self.pos == start) return null; const content = try allocator.dupe(u8, self.template[start..self.pos]); - std.debug.print("2.2 - meu content é \'{s}\'\n", .{content}); return Node{ .type = .text, .text = .{ .content = content }, }; } - 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}); - + fn parseVariable(self: *Parser, allocator: std.mem.Allocator) ParserError!?Node { if (self.peek(2)) |p| { if (!std.mem.eql(u8, p, "{{")) return null; } else return null; @@ -1309,14 +1303,10 @@ pub const Parser = struct { 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", .{}); } } - std.debug.print("2.2 - fiz o peek de 2 em 2 até que achei }}}}\n", .{}); if (self.pos + 2 > self.template.len or !std.mem.eql(u8, self.template[self.pos .. self.pos + 2], "}}")) { - std.debug.print("2.3 - deu ruim achei uma variável que não fecha!\n", .{}); return error.UnclosedVariable; } @@ -1352,7 +1342,6 @@ pub const Parser = struct { // 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 = .{ @@ -1362,12 +1351,9 @@ pub const Parser = struct { }; } - fn parseTag(self: *Parser, allocator: std.mem.Allocator) !?Node { - std.debug.print("2.0 - Vou verificar se sou uma tag\n", .{}); - std.debug.print("2.1 - meu start é {d}\n", .{self.pos}); + fn parseTag(self: *Parser, allocator: std.mem.Allocator) ParserError!?Node { 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; const raw_start = self.pos; @@ -1381,7 +1367,6 @@ pub const Parser = struct { } } - std.debug.print("2.2 - fiz o peek de 2 em 2 até que achei %}}\n", .{}); if (self.pos + 2 > self.template.len or !std.mem.eql(u8, self.template[self.pos .. self.pos + 2], "%}")) { return error.UnclosedTag; } @@ -1396,7 +1381,6 @@ pub const Parser = struct { const name = try allocator.dupe(u8, name_raw); const args = try allocator.dupe(u8, args_raw); - std.debug.print("2.3 - meu node:\n - nome: {s}\n - args: {s}\n - raw: {s}\n", .{ name, args, raw_slice }); self.advance(2); return Node{ @@ -1409,8 +1393,7 @@ pub const Parser = struct { }; } - fn parseIfBlock(self: *Parser, allocator: std.mem.Allocator, condition: []const u8, raw_open: []const u8) !Node { - std.debug.print("Vou verificar se sou bloco\n", .{}); + fn parseIfBlock(self: *Parser, allocator: std.mem.Allocator, condition: []const u8, raw_open: []const u8) ParserError!Node { var true_body = std.ArrayList(Node){}; defer true_body.deinit(allocator); var false_body = std.ArrayList(Node){}; @@ -1421,13 +1404,11 @@ pub const Parser = struct { while (self.pos < self.template.len and depth > 0) { if (try self.parseText(allocator)) |node| { - std.debug.print("2.3 - Encontrei um texto: {s}\n", .{node.text.?.content}); try current_body.append(allocator, node); continue; } if (try self.parseVariable(allocator)) |node| { - std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.expr}); try current_body.append(allocator, node); continue; } @@ -1450,13 +1431,6 @@ pub const Parser = struct { allocator.free(tag_node.tag.?.args); if (depth == 0) { - std.debug.print("2.4 - Encontrei um bloco if:\n - condition: {s}\n - true_body: {any}\n - false_body: {any}\n - raw_open: {s}\n - raw_close: {s}\n", .{ - condition, - true_body.items, - false_body.items, - raw_open, - raw_close, - }); return Node{ .type = .if_block, .@"if" = .{ @@ -1491,7 +1465,7 @@ pub const Parser = struct { return error.UnclosedBlock; } - fn parseForBlock(self: *Parser, allocator: std.mem.Allocator, loop_var: []const u8, iterable: []const u8, raw_open: []const u8) !Node { + fn parseForBlock(self: *Parser, allocator: std.mem.Allocator, loop_var: []const u8, iterable: []const u8, raw_open: []const u8) ParserError!Node { var body = std.ArrayList(Node){}; defer body.deinit(allocator); var empty_body = std.ArrayList(Node){}; @@ -1502,13 +1476,11 @@ pub const Parser = struct { while (self.pos < self.template.len and depth > 0) { if (try self.parseText(allocator)) |node| { - std.debug.print("2.3 - Encontrei um texto: {s}\n", .{node.text.?.content}); try current_body.append(allocator, node); continue; } if (try self.parseVariable(allocator)) |node| { - std.debug.print("2.3 - Encontrei uma variável: {s}\n", .{node.variable.?.expr}); try current_body.append(allocator, node); continue; } @@ -1531,14 +1503,6 @@ pub const Parser = struct { allocator.free(tag_node.tag.?.name); allocator.free(tag_node.tag.?.args); - std.debug.print("2.4 - Encontrei um bloco for:\n - loop_var: {s}\n - iterable: {s}\n - body: {any}\n - empty_body: {any}\n - raw_open: {s}\n - raw_close: {s}\n", .{ - loop_var, - iterable, - body.items, - empty_body.items, - raw_open, - raw_close, - }); return Node{ .type = .for_block, .@"for" = .{ @@ -1573,18 +1537,642 @@ pub const Parser = struct { return error.UnclosedBlock; } - pub fn parse(self: *Parser, allocator: std.mem.Allocator) ![]Node { + fn parseTagContent(self: *Parser, allocator: std.mem.Allocator, tag_node: Node) ParserError!?Node { + const tag_name = tag_node.tag.?.name; + if (std.mem.eql(u8, tag_name, "autoescape")) { + const args = std.mem.trim(u8, tag_node.tag.?.args, " \t\r\n"); + const raw_open = tag_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(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const ae_node = try self.parseAutoescapeBlock(allocator, enabled, raw_open); + return ae_node; + } + + if (std.mem.eql(u8, tag_name, "spaceless")) { + const raw_open = tag_node.tag.?.raw; + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const spaceless_node = try self.parseSpacelessBlock(allocator, raw_open); + return spaceless_node; + } + + if (std.mem.eql(u8, tag_name, "partialdef")) { + const partial_name = std.mem.trim(u8, tag_node.tag.?.args, " \t\r\n"); + const raw_open = tag_node.tag.?.raw; + + const duped_name = try allocator.dupe(u8, partial_name); + errdefer allocator.free(duped_name); + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const partialdef_node = try self.parsePartialDefBlock(allocator, duped_name, raw_open); + return partialdef_node; + } + + if (std.mem.eql(u8, tag_name, "partial")) { + const partial_name = std.mem.trim(u8, tag_node.tag.?.args, " \t\r\n\"'"); + + const duped_name = try allocator.dupe(u8, partial_name); + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .partial, + .partial = .{ .name = duped_name }, + }; + } + + if (std.mem.eql(u8, tag_name, "lorem")) { + const args = tag_node.tag.?.args; + + var count: ?[]const u8 = null; + var method: ?[]const u8 = null; + var format: ?[]const u8 = null; + + var parts = std.mem.splitScalar(u8, args, ' '); + while (parts.next()) |part| { + const trimmed = std.mem.trim(u8, part, " \t\r\n"); + if (trimmed.len == 0) continue; + + const num: usize = std.fmt.parseInt(usize, trimmed, 10) catch 0; + if (num > 0) { + count = try allocator.dupe(u8, trimmed); + } + + if (std.mem.eql(u8, trimmed, "p") or std.mem.eql(u8, trimmed, "w")) { + method = try allocator.dupe(u8, trimmed); + } else if (std.mem.eql(u8, trimmed, "html")) { + format = try allocator.dupe(u8, trimmed); + } + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .lorem, + .lorem = .{ + .count = count, + .method = method, + .format = format, + }, + }; + } + + if (std.mem.eql(u8, tag_name, "filter")) { + const filters_raw = tag_node.tag.?.args; + const raw_open = tag_node.tag.?.raw; + + // DUPE O FILTERS IMEDIATAMENTE + const duped_filters = try allocator.dupe(u8, filters_raw); + + // Agora libera a tag open + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const filter_node = try self.parseFilterBlock(allocator, duped_filters, raw_open); + return filter_node; + } + + if (std.mem.eql(u8, tag_name, "extends")) { + const parent = std.mem.trim(u8, tag_node.tag.?.args, " \t\""); + + const duped = try allocator.dupe(u8, parent); + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .extends, + .extends = .{ .parent_name = duped }, + }; + } + + if (std.mem.eql(u8, tag_name, "block")) { + const block_name_raw = tag_node.tag.?.args; + const raw_open = tag_node.tag.?.raw; + + const block_name = std.mem.trim(u8, block_name_raw, " \t\r\n"); + + // DUPE O NOME ANTES DE LIBERAR A TAG + const duped_name = try allocator.dupe(u8, block_name); + + // Agora libera a tag open + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const block_node = try self.parseBlockBlock(allocator, duped_name, raw_open); + return block_node; + } + + if (std.mem.eql(u8, tag_name, "now")) { + const args = tag_node.tag.?.args; + + // Remove aspas se existirem + var format_str = args; + if (format_str.len >= 2 and format_str[0] == '"' and format_str[format_str.len - 1] == '"') { + format_str = format_str[1 .. format_str.len - 1]; + } + + const duped_format = try allocator.dupe(u8, format_str); + + // Libera a tag now + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .now, + .now = .{ .format = duped_format }, + }; + } + + if (std.mem.eql(u8, tag_name, "with")) { + const args = tag_node.tag.?.args; + const raw_open = tag_node.tag.?.raw; + + const assignments = try self.parseAssignments(allocator, args); + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const with_node = try self.parseWithBlock(allocator, assignments, raw_open); + return with_node; + } + + if (std.mem.eql(u8, tag_name, "include")) { + const args = tag_node.tag.?.args; + + // Remove aspas se existirem + var template_name = args; + if (template_name.len >= 2 and template_name[0] == '"' and template_name[template_name.len - 1] == '"') { + template_name = template_name[1 .. template_name.len - 1]; + } + + const duped_name = try allocator.dupe(u8, template_name); + + // Libera a tag include + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .include, + .include = .{ .template_name = duped_name }, + }; + } + + if (std.mem.eql(u8, tag_name, "if")) { + const condition_raw = tag_node.tag.?.args; + const raw_open = tag_node.tag.?.raw; + + const condition = try allocator.dupe(u8, condition_raw); + + // Libera apenas name e args da tag open + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + // NÃO chame node.deinit aqui — raw_open ainda é usado + + const if_node = try self.parseIfBlock(allocator, condition, raw_open); + return if_node; + } + + if (std.mem.eql(u8, tag_name, "for")) { + const args = tag_node.tag.?.args; + const raw_open = tag_node.tag.?.raw; + + const in_pos = std.mem.indexOf(u8, args, " in ") orelse return error.InvalidForSyntax; + const loop_var_raw = std.mem.trim(u8, args[0..in_pos], " \t"); + const iterable_raw = std.mem.trim(u8, args[in_pos + 4 ..], " \t"); + + // DUPE ANTES DE LIBERAR! + const loop_var = try allocator.dupe(u8, loop_var_raw); + const iterable = try allocator.dupe(u8, iterable_raw); + + // Agora sim, libera a tag open + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const for_node = try self.parseForBlock(allocator, loop_var, iterable, raw_open); + return for_node; + } + + if (std.mem.eql(u8, tag_name, "verbatim")) { + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + const verbatim_node = try self.parseVerbatim(allocator); + return verbatim_node; + } + + if (std.mem.eql(u8, tag_name, "url")) { + const args = tag_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(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .url, + .url = .{ + .name = duped_view, + .args = try arg_list.toOwnedSlice(allocator), + }, + }; + } + + if (std.mem.eql(u8, tag_name, "cycle")) { + const args = tag_node.tag.?.args; + + var values = std.ArrayList([]const u8){}; + defer values.deinit(allocator); + + var i: usize = 0; + while (i < args.len) { + while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {} + + if (i >= args.len) break; + + const start = i; + var in_quote = false; + var quote_char: u8 = 0; + if (args[i] == '"' or args[i] == '\'') { + in_quote = true; + quote_char = args[i]; + i += 1; + } + + while (i < args.len) { + if (in_quote) { + if (args[i] == quote_char) { + i += 1; + break; + } + } else { + if (std.ascii.isWhitespace(args[i])) break; + } + i += 1; + } + + const value = std.mem.trim(u8, args[start..i], " \t\r\n\"'"); + try values.append(allocator, try allocator.dupe(u8, value)); + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .cycle, + .cycle = .{ + .values = try values.toOwnedSlice(allocator), + }, + }; + } + + if (std.mem.eql(u8, tag_name, "firstof")) { + const args = tag_node.tag.?.args; + + var values = std.ArrayList([]const u8){}; + defer values.deinit(allocator); + + var i: usize = 0; + while (i < args.len) { + while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {} + + if (i >= args.len) break; + + const start = i; + var in_quote = false; + var quote_char: u8 = 0; + if (args[i] == '"' or args[i] == '\'') { + in_quote = true; + quote_char = args[i]; + i += 1; + } + + while (i < args.len) { + if (in_quote) { + if (args[i] == quote_char) { + i += 1; + break; + } + } else { + if (std.ascii.isWhitespace(args[i])) break; + } + i += 1; + } + + const value = std.mem.trim(u8, args[start..i], " \t\r\n\"'"); + try values.append(allocator, try allocator.dupe(u8, value)); + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .firstof, + .firstof = .{ + .values = try values.toOwnedSlice(allocator), + }, + }; + } + + if (std.mem.eql(u8, tag_name, "load")) { + const args = tag_node.tag.?.args; + + var libraries = std.ArrayList([]const u8){}; + defer libraries.deinit(allocator); + + var i: usize = 0; + while (i < args.len) { + while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {} + + if (i >= args.len) break; + + const start = i; + while (i < args.len and !std.ascii.isWhitespace(args[i])) : (i += 1) {} + + const lib_name = args[start..i]; + try libraries.append(allocator, try allocator.dupe(u8, lib_name)); + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .load, + .load = .{ + .libraries = try libraries.toOwnedSlice(allocator), + }, + }; + } + + if (std.mem.eql(u8, tag_name, "debug")) { + // Verifica se tem argumentos (não deve ter) + if (tag_node.tag.?.args.len > 0 and !std.mem.allEqual(u8, tag_node.tag.?.args, ' ')) { + return error.InvalidDebugArgs; + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .debug, + .debug = true, + }; + } + + if (std.mem.eql(u8, tag_name, "querystring")) { + const args = tag_node.tag.?.args; + + var modifications = std.ArrayList([]const u8){}; + defer modifications.deinit(allocator); + + var i: usize = 0; + while (i < args.len) { + while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {} + + if (i >= args.len) break; + + const start = i; + var in_quote = false; + var quote_char: u8 = 0; + if (args[i] == '"' or args[i] == '\'') { + in_quote = true; + quote_char = args[i]; + i += 1; + } + + while (i < args.len) { + if (in_quote) { + if (args[i] == quote_char) { + i += 1; + break; + } + } else { + if (std.ascii.isWhitespace(args[i])) break; + } + i += 1; + } + + const mod_str = std.mem.trim(u8, args[start..i], " \t\r\n\"'"); + try modifications.append(allocator, try allocator.dupe(u8, mod_str)); + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .querystring, + .querystring = .{ + .modifications = try modifications.toOwnedSlice(allocator), + }, + }; + } + + if (std.mem.eql(u8, tag_name, "regroup")) { + const args = tag_node.tag.?.args; + + // Esperado: lista by atributo as nome_grupo + const by_pos = std.mem.indexOf(u8, args, " by ") orelse return error.InvalidRegroupSyntax; + const as_pos = std.mem.indexOf(u8, args[by_pos + 4 ..], " as ") orelse return error.InvalidRegroupSyntax; + const as_pos_abs = by_pos + 4 + as_pos; + + const source_raw = std.mem.trim(u8, args[0..by_pos], " \t\r\n"); + const by_raw = std.mem.trim(u8, args[by_pos + 4 .. as_pos_abs], " \t\r\n"); + const as_raw = std.mem.trim(u8, args[as_pos_abs + 4 ..], " \t\r\n"); + + const source = try allocator.dupe(u8, source_raw); + const by = try allocator.dupe(u8, by_raw); + const as_var = try allocator.dupe(u8, as_raw); + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .regroup, + .regroup = .{ + .source = source, + .by = by, + .as_var = as_var, + }, + }; + } + + if (std.mem.eql(u8, tag_name, "resetcycle")) { + const args = std.mem.trim(u8, tag_node.tag.?.args, " \t\r\n"); + + var cycle_name: ?[]const u8 = null; + if (args.len > 0) { + cycle_name = try allocator.dupe(u8, args); + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .resetcycle, + .resetcycle = .{ + .cycle_name = cycle_name, + }, + }; + } + + if (std.mem.eql(u8, tag_name, "widthratio")) { + const args = tag_node.tag.?.args; + + var parts = std.mem.splitScalar(u8, args, ' '); + var value_part: ?[]const u8 = null; + var max_part: ?[]const u8 = null; + var divisor_part: ?[]const u8 = null; + + if (parts.next()) |p| value_part = std.mem.trim(u8, p, " \t\r\n"); + if (parts.next()) |p| max_part = std.mem.trim(u8, p, " \t\r\n"); + if (parts.next()) |p| divisor_part = std.mem.trim(u8, p, " \t\r\n"); + + if (value_part == null or max_part == null) return error.InvalidWidthRatioSyntax; + + const value = try allocator.dupe(u8, value_part.?); + const max_value = try allocator.dupe(u8, max_part.?); + const divisor = if (divisor_part) |d| try allocator.dupe(u8, d) else null; + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .widthratio, + .widthratio = .{ + .value = value, + .max_value = max_value, + .divisor = divisor, + }, + }; + } + + if (std.mem.eql(u8, tag_name, "templatetag")) { + const arg = std.mem.trim(u8, tag_node.tag.?.args, " \t\r\n"); + + const templatetag = TemplateTagNode.parse(arg); + if (templatetag == null) return error.InvalidTemplateTag; + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .templatetag, + .templatetag = templatetag, + }; + } + + if (std.mem.eql(u8, tag_name, "csrf_token")) { + // Verifica se tem argumentos (não deve ter) + if (tag_node.tag.?.args.len > 0 and !std.mem.allEqual(u8, tag_node.tag.?.args, ' ')) { + return error.InvalidCsrfTokenArgs; + } + + allocator.free(tag_node.tag.?.name); + allocator.free(tag_node.tag.?.args); + + return Node{ + .type = .csrf_token, + .csrf_token = true, + }; + } + return null; + } + + pub fn parse(self: *Parser, allocator: std.mem.Allocator) ParserError![]Node { var list = std.ArrayList(Node){}; defer list.deinit(allocator); - std.debug.print("O template recebido é:\n\n{s}\n\n", .{self.template}); while (self.pos < self.template.len) { - std.debug.print("1.0 - minha posição ainda é menor que o tamanho do template\n", .{}); if (try self.parseTag(allocator)) |node| { - std.debug.print("3.0 - na real sou uma tag\n", .{}); const tag_name = node.tag.?.name; - std.debug.print("3.1 - meu tag name é: {s}\n", .{tag_name}); + if (std.mem.eql(u8, tag_name, "comment")) { + allocator.free(node.tag.?.name); + allocator.free(node.tag.?.args); + try self.parseComment(); + continue; + } else { + const node_parsed: ?Node = self.parseTagContent(allocator, node) catch null; + if (node_parsed) |n| { + try list.append(allocator, n); + continue; + } + } + } + + if (try self.parseVariable(allocator)) |node| { + try list.append(allocator, node); + continue; + } + + if (try self.parseText(allocator)) |node| { + try list.append(allocator, node); + continue; + } + + self.advance(1); + } + + return try list.toOwnedSlice(allocator); + } + + pub fn parse_(self: *Parser, allocator: std.mem.Allocator) ParserError![]Node { + var list = std.ArrayList(Node){}; + defer list.deinit(allocator); + + while (self.pos < self.template.len) { + if (try self.parseTag(allocator)) |node| { + const tag_name = node.tag.?.name; + try self.parseTagContent(allocator, node); if (std.mem.eql(u8, tag_name, "autoescape")) { const args = std.mem.trim(u8, node.tag.?.args, " \t\r\n"); @@ -1600,9 +2188,6 @@ pub const Parser = struct { 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; @@ -1614,17 +2199,12 @@ pub const Parser = struct { 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", .{}); allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); try self.parseComment(); @@ -1641,9 +2221,6 @@ pub const Parser = struct { 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; @@ -1657,9 +2234,6 @@ pub const Parser = struct { 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 }, @@ -1681,15 +2255,12 @@ pub const Parser = struct { const num: usize = std.fmt.parseInt(usize, trimmed, 10) catch 0; if (num > 0) { - std.debug.print("trimmed: {s}\n", .{trimmed}); count = try allocator.dupe(u8, trimmed); } if (std.mem.eql(u8, trimmed, "p") or std.mem.eql(u8, trimmed, "w")) { - std.debug.print("trimmed: {s}\n", .{trimmed}); 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); } } @@ -1718,8 +2289,6 @@ pub const Parser = struct { // 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); @@ -1734,8 +2303,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um extends\n", .{}); - std.debug.print("===================================\n", .{}); try list.append(allocator, Node{ .type = .extends, .extends = .{ .parent_name = duped }, @@ -1756,9 +2323,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um block\n", .{}); - std.debug.print("===================================\n", .{}); - const block_node = try self.parseBlockBlock(allocator, duped_name, raw_open); try list.append(allocator, block_node); continue; @@ -1779,9 +2343,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou uma tag now\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .now, .now = .{ .format = duped_format }, @@ -1799,8 +2360,6 @@ pub const Parser = struct { allocator.free(node.tag.?.args); const with_node = try self.parseWithBlock(allocator, assignments, raw_open); - std.debug.print("3.0 - na real sou um bloco with\n", .{}); - std.debug.print("===================================\n", .{}); try list.append(allocator, with_node); continue; } @@ -1820,8 +2379,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um include\n", .{}); - std.debug.print("===================================\n", .{}); try list.append(allocator, Node{ .type = .include, .include = .{ .template_name = duped_name }, @@ -1844,7 +2401,6 @@ pub const Parser = struct { const if_node = try self.parseIfBlock(allocator, condition, raw_open); try list.append(allocator, if_node); - std.debug.print("===================================\n", .{}); continue; } @@ -1866,7 +2422,6 @@ pub const Parser = struct { const for_node = try self.parseForBlock(allocator, loop_var, iterable, raw_open); - std.debug.print("===================================\n", .{}); try list.append(allocator, for_node); continue; } @@ -1875,9 +2430,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um verbatim\n", .{}); - std.debug.print("===================================\n", .{}); - const verbatim_node = try self.parseVerbatim(allocator); try list.append(allocator, verbatim_node); continue; @@ -1924,9 +2476,6 @@ pub const Parser = struct { 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 = .{ @@ -1977,9 +2526,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um cycle\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .cycle, .cycle = .{ @@ -2029,9 +2575,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um firstof\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .firstof, .firstof = .{ @@ -2063,9 +2606,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um load\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .load, .load = .{ @@ -2084,9 +2624,6 @@ pub const Parser = struct { 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, @@ -2134,9 +2671,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um querystring\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .querystring, .querystring = .{ @@ -2165,9 +2699,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um regroup\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .regroup, .regroup = .{ @@ -2190,9 +2721,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um resetcycle\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .resetcycle, .resetcycle = .{ @@ -2223,9 +2751,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um widthratio\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .widthratio, .widthratio = .{ @@ -2262,9 +2787,6 @@ pub const Parser = struct { allocator.free(node.tag.?.name); allocator.free(node.tag.?.args); - std.debug.print("3.0 - na real sou um csrf_token\n", .{}); - std.debug.print("===================================\n", .{}); - try list.append(allocator, Node{ .type = .csrf_token, .csrf_token = true, @@ -2273,24 +2795,16 @@ pub const Parser = struct { } // Para tags normais - std.debug.print("===================================\n", .{}); try list.append(allocator, node); continue; } 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.?.expr}); - std.debug.print("4.1 - filters: \'{any}\'\n", .{node.variable.?.filters}); - std.debug.print("===================================\n", .{}); try list.append(allocator, node); continue; } if (try self.parseText(allocator)) |node| { - std.debug.print("3.0 - na real sou texto\n", .{}); - std.debug.print("4.0 - content: \'{s}\'\n", .{node.text.?.content}); - std.debug.print("===================================\n", .{}); try list.append(allocator, node); continue; } @@ -2298,16 +2812,25 @@ pub const Parser = struct { self.advance(1); } - std.debug.print("\nO resultado disso foi esse:\n", .{}); - for (list.items) |item| { - std.debug.print(" -> type: {s}\n", .{@tagName(item.type)}); - } - std.debug.print("\n", .{}); + // std.debug.print("\nO resultado disso foi esse:\n", .{}); + // for (list.items) |item| { + // std.debug.print(" -> type: {s}\n", .{@tagName(item.type)}); + // } + // std.debug.print("\n", .{}); return try list.toOwnedSlice(allocator); } }; -pub fn parse(allocator: std.mem.Allocator, template: []const u8) ![]Node { +pub fn parse_t(allocator: std.mem.Allocator, template: []const u8) ParserError![]Node { var p = Parser.init(template); - return try p.parse(allocator); + return p.parse(allocator); +} + +pub fn parse_template(template: []const u8) ParserError![]Node { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + const alloc = arena.allocator(); + defer arena.deinit(); + + var p = Parser.init(template); + return p.parse(alloc); }