update: renderer initial idea

This commit is contained in:
Lucas F. 2026-01-11 09:42:02 -03:00
parent d1c63cddda
commit 0e74ddb278
2 changed files with 225 additions and 0 deletions

105
src/renderer.zig Normal file
View file

@ -0,0 +1,105 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
const Context = @import("context.zig").Context;
const Value = @import("context.zig").Value;
const builtin_filters = @import("filters.zig").builtin_filters;
const FilterError = @import("filters.zig").FilterError;
const parser = @import("parser.zig");
pub const RenderError = FilterError || error{
OutOfMemory,
InvalidSyntax,
UnknownVariable,
UnknownFilter,
InvalidTemplate,
BlockNotFound,
CircularExtends,
FileNotFound,
AccessDenied,
FileTooBig,
NoSpaceLeft,
Unexpected,
UnclosedTag,
InvalidAutoescapeArgument,
UnclosedVariable,
UnclosedBlock,
UnclosedComment,
InvalidAssignmentSyntax,
UnclosedQuoteInAssignment,
InvalidForSyntax,
UnclosedVerbatim,
InvalidUrlSyntax,
UnclosedQuoteInUrl,
InvalidDebugArgs,
InvalidRegroupSyntax,
InvalidWidthRatioSyntax,
InvalidTemplateTag,
InvalidCsrfTokenArgs,
};
pub const Renderer = struct {
context: *Context,
allocator: Allocator,
pub fn init(context: *Context) Renderer {
return .{
.context = context,
.allocator = context.allocator(),
};
}
pub fn renderString(self: *const Renderer, template: []const u8, writer: anytype) RenderError!void {
var p = parser.Parser.init(template);
const nodes = try p.parse(self.allocator);
for (nodes) |node| {
try self.renderNode(node, writer);
}
}
fn renderNode(self: *const Renderer, node: parser.Node, writer: anytype) RenderError!void {
switch (node.type) {
.text => try writer.writeAll(node.text.?.content),
.variable => {
const var_name = node.variable.?.expr;
var value: Value = self.context.get(var_name) orelse Value.null;
var buf = ArrayListUnmanaged(u8){};
defer buf.deinit(self.allocator);
var is_safe = false;
for (node.variable.?.filters) |f| {
const filter_fn = builtin_filters.get(f.name) orelse continue;
value = try filter_fn(self.allocator, value, Value{.string = f.arg orelse ""});
if (std.mem.eql(u8, f.name, "safe")) is_safe = true;
}
if (!is_safe) {
if (builtin_filters.get("escape")) |escape_fn| {
value = try escape_fn(self.allocator, value, null);
}
}
try self.valueToString(&buf, value);
try writer.writeAll(buf.items);
},
else => unreachable,
}
}
fn valueToString(self: *const Renderer, buf: *ArrayListUnmanaged(u8), value: Value) !void {
var w = buf.writer(self.allocator);
switch (value) {
.null => try w.writeAll("null"),
.bool => |b| try w.print("{}", .{b}),
.int => |n| try w.print("{d}", .{n}),
.float => |f| try w.print("{d}", .{f}),
.string => |s| try w.writeAll(s),
.list => try w.writeAll("[list]"),
.dict => try w.writeAll("{dict}"),
}
}
};