From 934296b38461eb37759b731fdfe0d96362edcf08 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Tue, 15 Nov 2022 23:16:46 -0800 Subject: [PATCH] Parse for loop --- src/template/lib.zig | 121 +++++++++++++++++++++++++++++-------- src/template/test.tmp.html | 3 +- 2 files changed, 98 insertions(+), 26 deletions(-) diff --git a/src/template/lib.zig b/src/template/lib.zig index a35cc20..4ab839c 100644 --- a/src/template/lib.zig +++ b/src/template/lib.zig @@ -7,8 +7,9 @@ pub fn main() !void { const logging = false; pub fn execute(writer: anytype, comptime template: []const u8, args: anytype) !void { - const items = comptime parseTemplate(template); - try executeTemplate(writer, items, args); + @setEvalBranchQuota(@intCast(u32, template.len * 6)); + const tmpl = comptime parseTemplate(TokenIter{ .text = template }, .root); + try executeTemplate(writer, tmpl.item, args); } fn executeTemplate(writer: anytype, comptime items: []const TemplateItem, args: anytype) !void { @@ -23,6 +24,9 @@ fn executeStatement(writer: anytype, comptime stmt: Statement, args: anytype) !v .expression => |expr| switch (expr) { .arg_deref => |fields| try argDeref(writer, fields, args), }, + .for_loop => { + try writer.writeAll("For loop"); + }, else => @compileError("TODO"), } } @@ -37,14 +41,18 @@ fn argDeref(writer: anytype, comptime names: []const []const u8, arg: anytype) ! return argDeref(writer, names[1..], @field(arg, names[0])); } -fn parseTemplate(comptime template: []const u8) []const TemplateItem { +const TemplateType = enum { + root, + subtemplate, +}; + +fn parseTemplate(comptime tokens: TokenIter, comptime template_type: TemplateType) ParseResult([]const TemplateItem) { comptime { - @setEvalBranchQuota(@intCast(u32, template.len * 6)); - var iter = TokenIter{ .text = template }; + var iter = tokens; var items: []const TemplateItem = &.{}; var current_text: []const u8 = ""; - while (iter.next()) |token| { + parse_loop: while (iter.next()) |token| { if (logging) @compileLog(token); switch (token) { .whitespace, .text => |text| current_text = current_text ++ text, @@ -60,6 +68,9 @@ fn parseTemplate(comptime template: []const u8) []const TemplateItem { } const result = parseStatement(iter); iter = result.new_iter; + if (result.item == .end_for) { + if (template_type == .subtemplate) break :parse_loop else @compileError("Unexpected end statement"); + } items = items ++ [_]TemplateItem{.{ .statement = result.item }}; } }, @@ -76,40 +87,94 @@ fn parseTemplate(comptime template: []const u8) []const TemplateItem { items = items ++ [_]TemplateItem{.{ .text = current_text }}; } - return items; + return .{ + .new_iter = iter, + .item = items, + }; } } fn parseStatement(comptime tokens: TokenIter) ParseResult(Statement) { comptime { var iter = tokens; - while (iter.next()) |token| switch (token) { + var stmt: Statement = while (iter.next()) |token| switch (token) { .whitespace => {}, .pound => { const next = iter.next() orelse @compileError("Unexpected end of template"); - if (logging) @compileLog("keyword", next); if (next != .text) @compileError("Expected keyword following '#' character"); const text = next.text; const keyword = std.meta.stringToEnum(Keyword, text) orelse @compileError("Unknown keyword: " ++ text); - _ = keyword; - @panic("todo"); + switch (keyword) { + .end_for => break .{ .end_for = {} }, + .@"for" => { + const result = parseForLoop(iter); + // statemnt already finished so just return + return .{ + .new_iter = result.new_iter, + .item = .{ .for_loop = result.item }, + }; + }, + + //else => @compileError("TODO"), + } }, .period => { const expr = parseArgDeref(iter); iter = expr.new_iter; - while (iter.next()) |it| switch (it) { - .whitespace => {}, - .close_bracket => break, - else => @compileError("TODO"), - }; - return .{ .new_iter = iter, .item = .{ - .expression = .{ .arg_deref = expr.item }, - } }; + break .{ .expression = .{ .arg_deref = expr.item } }; }, else => @compileError(""), }; + // search for end of statement + while (iter.next()) |token| switch (token) { + .whitespace => {}, + .close_bracket => return .{ + .new_iter = iter, + .item = stmt, + }, + else => { + @compileLog(iter.row); + @compileError("TODO" ++ @tagName(token)); + }, + }; + + @compileError("Unexpected end of template"); + } +} + +fn parseForLoop(comptime tokens: TokenIter) ParseResult(ForLoop) { + comptime { + const indexable = parseExpression(tokens); + var iter = indexable.new_iter; + while (iter.next()) |token| switch (token) { + .whitespace => {}, + .close_bracket => break, + else => @compileError("Unexpected token"), + }; + + const subtemplate = parseTemplate(iter, .subtemplate); + + return .{ .new_iter = subtemplate.new_iter, .item = .{ + .indexable = indexable.item, + .subtemplate = subtemplate.item, + } }; + } +} + +fn parseExpression(comptime tokens: TokenIter) ParseResult(Expression) { + comptime { + var iter = tokens; + while (iter.next()) |token| switch (token) { + .whitespace => {}, + .period => { + const deref = parseArgDeref(iter); + return .{ .new_iter = deref.new_iter, .item = .{ .arg_deref = deref.item } }; + }, + else => @compileError("Expected Expression"), + }; + @compileError("Unexpected end of template"); } } @@ -157,14 +222,15 @@ const Expression = union(enum) { arg_deref: []const []const u8, }; +const ForLoop = struct { + subtemplate: []const TemplateItem, + indexable: Expression, +}; + const Statement = union(enum) { expression: Expression, - for_loop: struct { - subtemplate: []const u8, - indexable: Expression, - iteration_capture: []const u8, - index_capture: ?[]const u8, - }, + for_loop: ForLoop, + end_for: void, }; const State = enum { @@ -176,6 +242,7 @@ const State = enum { const Keyword = enum { @"for", + end_for, }; const Token = union(enum) { @@ -192,6 +259,8 @@ const TokenIter = struct { text: []const u8, peeked_token: ?Token = null, + row: usize = 0, + fn next(self: *TokenIter) ?Token { if (self.peeked_token) |token| { self.peeked_token = null; @@ -211,6 +280,8 @@ const TokenIter = struct { ' ', '\t', '\n', '\r' => { var idx: usize = 0; while (idx < remaining.len and std.mem.indexOfScalar(u8, " \t\n\r", remaining[idx]) != null) : (idx += 1) {} + const newline_count = std.mem.count(u8, remaining[0..idx], "\n"); + self.row += newline_count; self.start += idx - 1; return .{ .whitespace = remaining[0..idx] }; diff --git a/src/template/test.tmp.html b/src/template/test.tmp.html index 503b619..30e772a 100644 --- a/src/template/test.tmp.html +++ b/src/template/test.tmp.html @@ -9,7 +9,8 @@

{{ REAL BRACKETS }}

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

Note no. {{$i}}

{{#template note_display ($note)}}