fediglam/src/template/lib.zig

122 lines
3.8 KiB
Zig
Raw Normal View History

2022-11-16 03:10:16 +00:00
const std = @import("std");
pub fn main() !void {
try execute(std.io.getStdOut().writer(), @embedFile("./test.tmp.html"), .{ .community = .{ .name = "abcd" } });
}
pub fn execute(writer: anytype, comptime template: []const u8, args: anytype) !void {
@setEvalBranchQuota(@intCast(u32, template.len * 6));
comptime var iter = TokenIter{ .text = template };
comptime var state: State = .text;
inline while (comptime iter.next()) |token| {
switch (token) {
.text => |text| switch (state) {
.text => try writer.writeAll(text),
else => @compileError("Text token not allowed in state " ++ @tagName(state) ++ text),
},
.open_bracket => switch (state) {
.text => state = .template_start,
.template_start => {
try writer.writeByte('{');
state = .text;
},
else => @compileError(""),
},
.close_bracket => switch (state) {
.text => state = .text_close_bracket,
.text_close_bracket => {
try writer.writeByte('}');
state = .text;
},
.template_start, .template => state = .text,
//else => @compileError(""),
},
.whitespace => |wsp| switch (state) {
.text => try writer.writeAll(wsp),
else => {},
},
.period => switch (state) {
.text => try writer.writeByte('.'),
.template_start, .template => {
try argDeref(writer, &iter, args);
state = .template;
},
else => @compileError(""),
},
}
}
}
fn argDeref(writer: anytype, comptime iter: *TokenIter, arg: anytype) !void {
inline while (comptime iter.peek()) |token| {
switch (token) {
.period => {},
.text => |text| {
_ = comptime iter.next();
return argDeref(writer, iter, @field(arg, text));
},
else => return try writer.writeAll(arg),
}
_ = comptime iter.next();
}
}
const State = enum {
text,
text_close_bracket,
template_start,
template,
};
const Token = union(enum) {
text: []const u8,
open_bracket: void,
close_bracket: void,
period: void,
whitespace: []const u8,
};
const TokenIter = struct {
start: usize = 0,
text: []const u8,
peeked_token: ?Token = null,
fn next(self: *TokenIter) ?Token {
if (self.peeked_token) |token| {
self.peeked_token = null;
return token;
}
const remaining = self.text[self.start..];
if (remaining.len == 0) return null;
const ch = remaining[0];
self.start += 1;
switch (ch) {
'{' => return .{ .open_bracket = {} },
'}' => return .{ .close_bracket = {} },
'.' => return .{ .period = {} },
' ', '\t', '\n', '\r' => {
var idx: usize = 0;
while (idx < remaining.len and std.mem.indexOfScalar(u8, " \t\n\r", remaining[idx]) != null) : (idx += 1) {}
self.start += idx - 1;
return .{ .whitespace = remaining[0..idx] };
},
else => {
var idx: usize = 0;
while (idx < remaining.len and std.mem.indexOfScalar(u8, "{}. \t\n\r", remaining[idx]) == null) : (idx += 1) {}
self.start += idx - 1;
return .{ .text = remaining[0..idx] };
},
}
}
fn peek(self: *TokenIter) ?Token {
const token = self.next();
self.peeked_token = token;
return token;
}
};