1330 lines
49 KiB
Zig
1330 lines
49 KiB
Zig
const std = @import("std");
|
|
|
|
pub const NodeType = enum {
|
|
text,
|
|
variable,
|
|
tag,
|
|
if_block,
|
|
for_block,
|
|
include,
|
|
with_block,
|
|
now,
|
|
extends,
|
|
block,
|
|
super,
|
|
filter_block,
|
|
autoescape,
|
|
spaceless,
|
|
url,
|
|
};
|
|
|
|
pub const AutoescapeNode = struct {
|
|
body: []Node,
|
|
raw_open: []const u8,
|
|
raw_close: []const u8,
|
|
enabled: bool,
|
|
};
|
|
|
|
pub const UrlNode = struct {
|
|
name: []const u8,
|
|
args: []const []const u8,
|
|
};
|
|
|
|
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 {
|
|
format: []const u8,
|
|
};
|
|
|
|
pub const ExtendsNode = struct {
|
|
parent_name: []const u8,
|
|
};
|
|
|
|
pub const BlockNode = struct {
|
|
name: []const u8,
|
|
body: []Node,
|
|
raw_open: []const u8,
|
|
raw_close: []const u8,
|
|
};
|
|
|
|
pub const Assignment = struct {
|
|
key: []const u8,
|
|
value_expr: []const u8,
|
|
is_literal: bool,
|
|
};
|
|
|
|
pub const WithNode = struct {
|
|
assignments: []const Assignment,
|
|
body: []Node,
|
|
raw_open: []const u8,
|
|
raw_close: []const u8,
|
|
};
|
|
|
|
pub const IncludeNode = struct {
|
|
template_name: []const u8,
|
|
};
|
|
|
|
pub const TextNode = struct {
|
|
content: []const u8,
|
|
};
|
|
|
|
pub const TagNode = struct {
|
|
name: []const u8,
|
|
args: []const u8,
|
|
raw: []const u8,
|
|
};
|
|
|
|
pub const IfNode = struct {
|
|
condition: []const u8, // dupe esse sim
|
|
true_body: []Node,
|
|
false_body: []Node,
|
|
raw_open: []const u8, // slice original, NÃO free
|
|
raw_close: []const u8, // slice original, NÃO free
|
|
};
|
|
|
|
pub const ForNode = struct {
|
|
loop_var: []const u8,
|
|
iterable: []const u8,
|
|
body: []Node,
|
|
empty_body: []Node,
|
|
raw_open: []const u8,
|
|
raw_close: []const u8,
|
|
};
|
|
|
|
pub const Node = struct {
|
|
type: NodeType,
|
|
text: ?TextNode = null,
|
|
variable: ?VariableNode = null,
|
|
tag: ?TagNode = null,
|
|
@"if": ?IfNode = null,
|
|
@"for": ?ForNode = null,
|
|
include: ?IncludeNode = null,
|
|
with: ?WithNode = null,
|
|
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,
|
|
url: ?UrlNode = 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.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);
|
|
const true_copy = ib.true_body;
|
|
for (true_copy) |n| n.deinit(allocator);
|
|
allocator.free(ib.true_body);
|
|
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);
|
|
const body_copy = fb.body;
|
|
for (body_copy) |n| n.deinit(allocator);
|
|
allocator.free(fb.body);
|
|
const empty_copy = fb.empty_body;
|
|
for (empty_copy) |n| n.deinit(allocator);
|
|
allocator.free(fb.empty_body);
|
|
},
|
|
.with_block => if (self.with) |w| {
|
|
for (w.assignments) |a| {
|
|
allocator.free(a.key);
|
|
allocator.free(a.value_expr);
|
|
}
|
|
allocator.free(w.assignments);
|
|
const body_copy = w.body;
|
|
for (body_copy) |n| n.deinit(allocator);
|
|
allocator.free(w.body);
|
|
},
|
|
.block => if (self.block) |b| {
|
|
allocator.free(b.name);
|
|
const body_copy = b.body;
|
|
for (body_copy) |n| n.deinit(allocator);
|
|
allocator.free(b.body);
|
|
},
|
|
.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 => {},
|
|
.url => if (self.url) |u| {
|
|
allocator.free(u.name);
|
|
for (u.args) |a| allocator.free(a);
|
|
allocator.free(u.args);
|
|
},
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const Parser = struct {
|
|
template: []const u8,
|
|
pos: usize = 0,
|
|
|
|
pub fn init(template: []const u8) Parser {
|
|
return .{ .template = template };
|
|
}
|
|
|
|
fn advance(self: *Parser, n: usize) void {
|
|
self.pos += n;
|
|
if (self.pos > self.template.len) self.pos = self.template.len;
|
|
}
|
|
|
|
fn peek(self: Parser, comptime n: usize) ?[]const u8 {
|
|
if (self.pos + n > self.template.len) return null;
|
|
return self.template[self.pos .. self.pos + n];
|
|
}
|
|
|
|
fn skipWhitespace(self: *Parser) void {
|
|
while (self.pos < self.template.len and std.ascii.isWhitespace(self.template[self.pos])) : (self.advance(1)) {}
|
|
}
|
|
|
|
fn parseAssignments(
|
|
self: *Parser,
|
|
allocator: std.mem.Allocator,
|
|
args: []const u8,
|
|
) ![]const Assignment {
|
|
var list = std.ArrayList(Assignment){};
|
|
defer list.deinit(allocator);
|
|
|
|
_ = self;
|
|
var i: usize = 0;
|
|
while (i < args.len) {
|
|
// Pula whitespaces iniciais
|
|
while (i < args.len and std.mem.indexOfScalar(u8, " \t\r\n", args[i]) != null) : (i += 1) {}
|
|
|
|
if (i >= args.len) break;
|
|
|
|
// Parse key (até '=')
|
|
const key_start = i;
|
|
while (i < args.len and args[i] != '=') : (i += 1) {}
|
|
if (i >= args.len or args[i] != '=') return error.InvalidAssignmentSyntax;
|
|
const key = std.mem.trim(u8, args[key_start..i], " \t\r\n");
|
|
if (key.len == 0) return error.InvalidAssignmentSyntax;
|
|
i += 1; // Pula '='
|
|
|
|
// Pula whitespaces após '='
|
|
while (i < args.len and std.mem.indexOfScalar(u8, " \t\r\n", args[i]) != null) : (i += 1) {}
|
|
|
|
// Parse value: se começa com ", parse até próximo " não escapado; senão, até próximo espaço
|
|
const value_start = i;
|
|
var in_quote = false;
|
|
if (i < args.len and args[i] == '"') {
|
|
in_quote = true;
|
|
i += 1; // Pula aspa inicial
|
|
}
|
|
while (i < args.len) {
|
|
if (in_quote) {
|
|
if (args[i] == '"' and (i == 0 or args[i - 1] != '\\')) break; // Fecha aspa não escapada
|
|
} else {
|
|
if (std.mem.indexOfScalar(u8, " \t\r\n", args[i]) != null) break; // Fim sem quote
|
|
}
|
|
i += 1;
|
|
}
|
|
const value_end = i;
|
|
var value = args[value_start..value_end];
|
|
if (in_quote) {
|
|
if (i >= args.len or args[i] != '"') return error.UnclosedQuoteInAssignment;
|
|
i += 1; // Pula aspa final
|
|
value = args[value_start + 1 .. value_end]; // Remove aspas
|
|
// TODO: Se precisar, handle escapes como \" aqui (remova \\ antes de ")
|
|
} else {
|
|
value = std.mem.trim(u8, value, " \t\r\n");
|
|
}
|
|
|
|
try list.append(allocator, .{
|
|
.key = try allocator.dupe(u8, key),
|
|
.value_expr = try allocator.dupe(u8, value),
|
|
.is_literal = in_quote,
|
|
});
|
|
}
|
|
|
|
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 parseVerbatim(self: *Parser, allocator: std.mem.Allocator) !Node {
|
|
const start = self.pos;
|
|
|
|
var depth: usize = 1;
|
|
|
|
while (self.pos < self.template.len and depth > 0) {
|
|
if (self.peek(2)) |p| {
|
|
if (std.mem.eql(u8, p, "{%")) {
|
|
self.advance(2);
|
|
self.skipWhitespace();
|
|
|
|
const content_start = self.pos;
|
|
while (self.pos < self.template.len and !std.mem.eql(u8, self.peek(2) orelse "", "%}")) : (self.advance(1)) {}
|
|
|
|
if (self.pos + 2 > self.template.len or !std.mem.eql(u8, self.template[self.pos .. self.pos + 2], "%}")) {
|
|
return error.UnclosedTag;
|
|
}
|
|
|
|
const inner = std.mem.trim(u8, self.template[content_start..self.pos], " \t\r\n");
|
|
|
|
if (std.mem.eql(u8, inner, "verbatim")) {
|
|
depth += 1;
|
|
} else if (std.mem.eql(u8, inner, "endverbatim")) {
|
|
depth -= 1;
|
|
if (depth == 0) {
|
|
// Copia até o início da tag endverbatim
|
|
const content_end = content_start - 3; // retrocede "{% "
|
|
const content = try allocator.dupe(u8, self.template[start..content_end]);
|
|
self.advance(2); // consome %}
|
|
return Node{
|
|
.type = .text,
|
|
.text = .{ .content = content },
|
|
};
|
|
}
|
|
}
|
|
|
|
self.advance(2);
|
|
continue;
|
|
}
|
|
}
|
|
self.advance(1);
|
|
}
|
|
|
|
return error.UnclosedVerbatim;
|
|
}
|
|
|
|
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);
|
|
|
|
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| {
|
|
if (node.variable) |v| {
|
|
if (std.mem.eql(u8, v.expr, "block.super")) {
|
|
try body.append(allocator, Node{ .type = .super, .super = true });
|
|
allocator.free(v.expr);
|
|
continue;
|
|
}
|
|
}
|
|
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, "block")) {
|
|
depth += 1;
|
|
try body.append(allocator, tag_node);
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "endblock")) {
|
|
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 = .block,
|
|
.block = .{
|
|
.name = 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 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", .{});
|
|
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| {
|
|
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;
|
|
}
|
|
|
|
if (try self.parseTag(allocator)) |tag_node| {
|
|
const tag_name = tag_node.tag.?.name;
|
|
|
|
if (std.mem.eql(u8, tag_name, "with")) {
|
|
depth += 1;
|
|
try body.append(allocator, tag_node);
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "endwith")) {
|
|
depth -= 1;
|
|
const raw_close = tag_node.tag.?.raw;
|
|
|
|
allocator.free(tag_node.tag.?.name);
|
|
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 = .{
|
|
.assignments = assignments,
|
|
.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 parseComment(self: *Parser) !void {
|
|
// Consome a tag open {% comment %}
|
|
// Já estamos após o %}, então só avançamos
|
|
var depth: usize = 1;
|
|
|
|
while (self.pos < self.template.len and depth > 0) {
|
|
if (self.peek(2)) |p| {
|
|
if (std.mem.eql(u8, p, "{%")) {
|
|
self.advance(2);
|
|
self.skipWhitespace();
|
|
|
|
const content_start = self.pos;
|
|
while (self.pos < self.template.len) : (self.advance(1)) {
|
|
if (self.peek(2)) |closing| {
|
|
if (std.mem.eql(u8, closing, "%}")) break;
|
|
}
|
|
}
|
|
|
|
if (self.pos + 2 > self.template.len or !std.mem.eql(u8, self.template[self.pos .. self.pos + 2], "%}")) {
|
|
return error.UnclosedTag;
|
|
}
|
|
|
|
const inner = std.mem.trim(u8, self.template[content_start..self.pos], " \t\r\n");
|
|
|
|
if (std.mem.eql(u8, inner, "comment")) {
|
|
depth += 1;
|
|
} else if (std.mem.eql(u8, inner, "endcomment")) {
|
|
depth -= 1;
|
|
if (depth == 0) {
|
|
self.advance(2); // consome %}
|
|
return;
|
|
}
|
|
}
|
|
|
|
self.advance(2);
|
|
continue;
|
|
}
|
|
}
|
|
self.advance(1);
|
|
}
|
|
|
|
return error.UnclosedComment;
|
|
}
|
|
|
|
fn parseText(self: *Parser, allocator: std.mem.Allocator) !?Node {
|
|
std.debug.print("2.0 - Vou verificar se sou texto\n", .{});
|
|
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;
|
|
}
|
|
}
|
|
self.advance(1);
|
|
}
|
|
|
|
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});
|
|
|
|
if (self.peek(2)) |p| {
|
|
if (!std.mem.eql(u8, p, "{{")) return null;
|
|
} else return null;
|
|
|
|
self.advance(2);
|
|
self.skipWhitespace();
|
|
|
|
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", .{});
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const full_expr = std.mem.trim(u8, self.template[expr_start..self.pos], " \t\r\n");
|
|
|
|
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 = .{
|
|
.expr = duped_expr,
|
|
.filters = try filters.toOwnedSlice(allocator),
|
|
},
|
|
};
|
|
}
|
|
|
|
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});
|
|
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;
|
|
self.advance(2);
|
|
self.skipWhitespace();
|
|
|
|
const content_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.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;
|
|
}
|
|
|
|
const raw_slice = self.template[raw_start .. self.pos + 2];
|
|
const inner = std.mem.trim(u8, self.template[content_start..self.pos], " \t\r\n");
|
|
|
|
const space_idx = std.mem.indexOfScalar(u8, inner, ' ') orelse inner.len;
|
|
const name_raw = inner[0..space_idx];
|
|
const args_raw = if (space_idx < inner.len) std.mem.trim(u8, inner[space_idx + 1 ..], " \t\r\n") else "";
|
|
|
|
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{
|
|
.type = .tag,
|
|
.tag = .{
|
|
.name = name,
|
|
.args = args,
|
|
.raw = raw_slice, // slice original, sem dupe
|
|
},
|
|
};
|
|
}
|
|
|
|
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", .{});
|
|
var true_body = std.ArrayList(Node){};
|
|
defer true_body.deinit(allocator);
|
|
var false_body = std.ArrayList(Node){};
|
|
defer false_body.deinit(allocator);
|
|
|
|
var current_body = &true_body;
|
|
var depth: usize = 1;
|
|
|
|
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;
|
|
}
|
|
|
|
if (try self.parseTag(allocator)) |tag_node| {
|
|
const tag_name = tag_node.tag.?.name;
|
|
|
|
if (std.mem.eql(u8, tag_name, "if")) {
|
|
depth += 1;
|
|
try current_body.append(allocator, tag_node);
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "endif")) {
|
|
depth -= 1;
|
|
const raw_close = tag_node.tag.?.raw;
|
|
|
|
// Libera name e args da tag endif
|
|
allocator.free(tag_node.tag.?.name);
|
|
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" = .{
|
|
.condition = condition,
|
|
.true_body = try true_body.toOwnedSlice(allocator),
|
|
.false_body = try false_body.toOwnedSlice(allocator),
|
|
.raw_open = raw_open,
|
|
.raw_close = raw_close,
|
|
},
|
|
};
|
|
}
|
|
|
|
// Se depth > 0, é endif aninhado — adiciona como tag normal
|
|
try current_body.append(allocator, tag_node);
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "else") and depth == 1) {
|
|
current_body = &false_body;
|
|
allocator.free(tag_node.tag.?.name);
|
|
allocator.free(tag_node.tag.?.args);
|
|
continue;
|
|
}
|
|
|
|
// Qualquer outra tag
|
|
try current_body.append(allocator, tag_node);
|
|
} else {
|
|
self.advance(1);
|
|
}
|
|
}
|
|
|
|
return error.UnclosedBlock;
|
|
}
|
|
|
|
fn parseForBlock(self: *Parser, allocator: std.mem.Allocator, loop_var: []const u8, iterable: []const u8, raw_open: []const u8) !Node {
|
|
var body = std.ArrayList(Node){};
|
|
defer body.deinit(allocator);
|
|
var empty_body = std.ArrayList(Node){};
|
|
defer empty_body.deinit(allocator);
|
|
|
|
var current_body = &body;
|
|
var depth: usize = 1;
|
|
|
|
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;
|
|
}
|
|
|
|
if (try self.parseTag(allocator)) |tag_node| {
|
|
const tag_name = tag_node.tag.?.name;
|
|
|
|
if (std.mem.eql(u8, tag_name, "for")) {
|
|
depth += 1;
|
|
try current_body.append(allocator, tag_node);
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "endfor")) {
|
|
depth -= 1;
|
|
const raw_close = tag_node.tag.?.raw;
|
|
|
|
if (depth == 0) {
|
|
// Libera name e args — essa é a tag de fechamento final
|
|
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" = .{
|
|
.loop_var = loop_var,
|
|
.iterable = iterable,
|
|
.body = try body.toOwnedSlice(allocator),
|
|
.empty_body = try empty_body.toOwnedSlice(allocator),
|
|
.raw_open = raw_open,
|
|
.raw_close = raw_close,
|
|
},
|
|
};
|
|
}
|
|
|
|
// depth > 0: endfor aninhado — adiciona como tag normal
|
|
try current_body.append(allocator, tag_node);
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "empty") and depth == 1) {
|
|
current_body = &empty_body;
|
|
allocator.free(tag_node.tag.?.name);
|
|
allocator.free(tag_node.tag.?.args);
|
|
continue;
|
|
}
|
|
|
|
try current_body.append(allocator, tag_node);
|
|
} else {
|
|
self.advance(1);
|
|
}
|
|
}
|
|
|
|
return error.UnclosedBlock;
|
|
}
|
|
|
|
pub fn parse(self: *Parser, allocator: std.mem.Allocator) ![]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, "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", .{});
|
|
allocator.free(node.tag.?.name);
|
|
allocator.free(node.tag.?.args);
|
|
try self.parseComment();
|
|
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\"");
|
|
|
|
const duped = try allocator.dupe(u8, parent);
|
|
|
|
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 },
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "block")) {
|
|
const block_name_raw = node.tag.?.args;
|
|
const raw_open = 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(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;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "now")) {
|
|
const args = 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(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 },
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "with")) {
|
|
const args = node.tag.?.args;
|
|
const raw_open = node.tag.?.raw;
|
|
|
|
const assignments = try self.parseAssignments(allocator, args);
|
|
|
|
allocator.free(node.tag.?.name);
|
|
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;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "include")) {
|
|
const args = 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(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 },
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "if")) {
|
|
const condition_raw = node.tag.?.args;
|
|
const raw_open = node.tag.?.raw;
|
|
|
|
const condition = try allocator.dupe(u8, condition_raw);
|
|
|
|
// Libera apenas name e args da tag open
|
|
allocator.free(node.tag.?.name);
|
|
allocator.free(node.tag.?.args);
|
|
|
|
// NÃO chame node.deinit aqui — raw_open ainda é usado
|
|
|
|
const if_node = try self.parseIfBlock(allocator, condition, raw_open);
|
|
try list.append(allocator, if_node);
|
|
|
|
std.debug.print("===================================\n", .{});
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "for")) {
|
|
const args = node.tag.?.args;
|
|
const raw_open = 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(node.tag.?.name);
|
|
allocator.free(node.tag.?.args);
|
|
|
|
const for_node = try self.parseForBlock(allocator, loop_var, iterable, raw_open);
|
|
|
|
std.debug.print("===================================\n", .{});
|
|
try list.append(allocator, for_node);
|
|
continue;
|
|
}
|
|
|
|
if (std.mem.eql(u8, tag_name, "verbatim")) {
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
|
|
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", .{});
|
|
return try list.toOwnedSlice(allocator);
|
|
}
|
|
};
|
|
|
|
pub fn parse(allocator: std.mem.Allocator, template: []const u8) ![]Node {
|
|
var p = Parser.init(template);
|
|
return try p.parse(allocator);
|
|
}
|