From 3b03764be732f5b393d5d735d0ad34bedaeb1487 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Tue, 15 Nov 2022 19:10:16 -0800 Subject: [PATCH] Start work on template package --- src/template/lib.zig | 121 +++++++++++++++++++++++++++++++++++++ src/template/test.tmp.html | 18 ++++++ 2 files changed, 139 insertions(+) create mode 100644 src/template/lib.zig create mode 100644 src/template/test.tmp.html diff --git a/src/template/lib.zig b/src/template/lib.zig new file mode 100644 index 0000000..fb20ec4 --- /dev/null +++ b/src/template/lib.zig @@ -0,0 +1,121 @@ +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; + } +}; diff --git a/src/template/test.tmp.html b/src/template/test.tmp.html new file mode 100644 index 0000000..9c2ce7b --- /dev/null +++ b/src/template/test.tmp.html @@ -0,0 +1,18 @@ + + + + { .community.name } + + + +

TITLE

+

{{ REAL BRACKETS }}

+ +
+ {{#for args.notes |$note, $i|}} +

Note no. {{$i}}

+ {{#template note_display ($note)}} + {{/for}} +
+ +