From 091146b617a8516fcab54ba6b1a9f5a4c8d9aefb Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Sun, 11 Dec 2022 22:50:44 -0800 Subject: [PATCH 1/3] Add #format statement --- src/template/lib.zig | 71 +++++++++++++++++++++++++++++++++++--- src/template/test.tmp.html | 2 ++ 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/template/lib.zig b/src/template/lib.zig index 9aa1cf0..100e95b 100644 --- a/src/template/lib.zig +++ b/src/template/lib.zig @@ -180,6 +180,13 @@ fn executeStatement( context, ); }, + .format => |fmt| { + try std.fmt.format( + writer, + "{" ++ fmt.format ++ "}", + .{evaluateExpression(fmt.value, args, captures, context)}, + ); + }, //else => @compileError("TODO"), } } @@ -196,11 +203,10 @@ fn htmlEscape(writer: anytype, str: []const u8) !void { fn print(writer: anytype, arg: anytype) !void { const T = @TypeOf(arg); + if (T == void) return; if (comptime std.meta.trait.isZigString(T)) return htmlEscape(writer, arg); if (comptime std.meta.trait.isNumber(T)) return std.fmt.format(writer, "{}", .{arg}); - @compileLog(@TypeOf(arg)); - - @compileError("TODO"); + std.fmt.format(writer, "{}", .{arg}); } fn Deref(comptime T: type, comptime names: []const []const u8) type { @@ -450,6 +456,9 @@ fn parseTemplate( .call_template => |call| items = items ++ [_]TemplateItem{.{ .statement = .{ .call_template = call }, }}, + .format => |call| items = items ++ [_]TemplateItem{.{ + .statement = .{ .format = call }, + }}, .end_switch, .case_header => if (template_type == .switch_block) break cb else @@ -514,6 +523,7 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken .percent => items = items ++ [_]TemplateToken{.{ .text = "%" }}, .open_paren => items = items ++ [_]TemplateToken{.{ .text = "(" }}, .close_paren => items = items ++ [_]TemplateToken{.{ .text = ")" }}, + .double_quote => items = items ++ [_]TemplateToken{.{ .text = "\"" }}, }; return items; @@ -684,6 +694,11 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken iter = result.new_iter; break .{ .case_header = result.item }; }, + .format => { + const result = parseFormat(iter); + iter = result.new_iter; + break .{ .format = result.item }; + }, //else => @compileError("TODO"), } @@ -706,7 +721,7 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken iter = expr.new_iter; break .{ .expression = expr.item }; }, - else => @compileError("TODO"), + else => @compileError("TODO " ++ @tagName(token)), }; // search for end of statement @@ -957,6 +972,42 @@ fn parseCallTemplate(comptime tokens: ControlTokenIter) ParseResult(ControlToken } } +fn parseFormat(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, FormatStmt) { + comptime { + var iter = skipWhitespace(tokens); + + expectToken(iter.next(), .double_quote); + var fmt_str: []const u8 = ""; + while (true) switch (iter.next() orelse @compileError("Unexpected end of template")) { + .text, .whitespace => |t| fmt_str = fmt_str ++ t, + .open_bracket => fmt_str = fmt_str ++ "{", + .close_bracket => fmt_str = fmt_str ++ "}", + .period => fmt_str = fmt_str ++ ".", + .pound => fmt_str = fmt_str ++ "#", + .pipe => fmt_str = fmt_str ++ "|", + .dollar => fmt_str = fmt_str ++ "$", + .slash => fmt_str = fmt_str ++ "/", + .equals => fmt_str = fmt_str ++ "=", + .at => fmt_str = fmt_str ++ "@", + .comma => fmt_str = fmt_str ++ ",", + .percent => fmt_str = fmt_str ++ "%", + .open_paren => fmt_str = fmt_str ++ "(", + .close_paren => fmt_str = fmt_str ++ ")", + .double_quote => break, + }; + + const expr = parseExpression(iter); + + return .{ + .new_iter = expr.new_iter, + .item = .{ + .format = fmt_str, + .value = expr.item, + }, + }; + } +} + fn ParseResult(comptime It: type, comptime T: type) type { return struct { new_iter: It, @@ -1032,12 +1083,18 @@ const IfHeader = struct { capture: ?[]const u8, }; +const FormatStmt = struct { + format: []const u8, + value: Expression, +}; + const Statement = union(enum) { expression: Expression, @"for": For, @"if": If, @"switch": Switch, call_template: CallTemplate, + format: FormatStmt, }; const ControlBlock = struct { @@ -1053,6 +1110,7 @@ const ControlBlock = struct { switch_header: SwitchHeader, case_header: CaseHeader, end_switch: void, + format: FormatStmt, }; block: Data, strip_before: bool, @@ -1067,6 +1125,7 @@ const Keyword = enum { @"template", @"switch", @"case", + @"format", }; const EndKeyword = enum { @@ -1102,6 +1161,7 @@ const ControlToken = union(enum) { percent: void, open_paren: void, close_paren: void, + double_quote: void, }; const ControlTokenIter = struct { @@ -1136,6 +1196,7 @@ const ControlTokenIter = struct { '%' => return .{ .percent = {} }, '(' => return .{ .open_paren = {} }, ')' => return .{ .close_paren = {} }, + '"' => return .{ .double_quote = {} }, ' ', '\t', '\n', '\r' => { var idx: usize = 0; while (idx < remaining.len and std.mem.indexOfScalar(u8, " \t\n\r", remaining[idx]) != null) : (idx += 1) {} @@ -1147,7 +1208,7 @@ const ControlTokenIter = struct { }, else => { var idx: usize = 0; - while (idx < remaining.len and std.mem.indexOfScalar(u8, "{}.#|$/=@,%() \t\n\r", remaining[idx]) == null) : (idx += 1) {} + 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] }; diff --git a/src/template/test.tmp.html b/src/template/test.tmp.html index de2620d..475581b 100644 --- a/src/template/test.tmp.html +++ b/src/template/test.tmp.html @@ -26,6 +26,8 @@ neither {=/if} + format: {#format "s" .x} + {#switch .snap case foo =} foo {= #case bar =} From 11bdaad6d7c439006fa31b313c7b09a9c2f6e5fe Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Mon, 12 Dec 2022 02:42:24 -0800 Subject: [PATCH 2/3] Support format strings for datetimes --- src/util/DateTime.zig | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/util/DateTime.zig b/src/util/DateTime.zig index 199cdc3..2b096fa 100644 --- a/src/util/DateTime.zig +++ b/src/util/DateTime.zig @@ -117,14 +117,16 @@ pub fn toCharArrayZ(value: DateTime) [array_len + 1:0]u8 { return buf; } -pub fn format(value: DateTime, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - return std.fmt.format( - writer, - "{:0>4}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}Z", - .{ value.year(), value.month().numeric(), value.day(), value.hour(), value.minute(), value.second() }, - ); +pub fn format(value: DateTime, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + if (comptime std.ascii.eqlIgnoreCase(fmt, "rfc3339") or fmt.len == 0) { + return std.fmt.format( + writer, + "{:0>4}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}Z", + .{ value.year(), value.month().numeric(), value.day(), value.hour(), value.minute(), value.second() }, + ); + } else @compileError("Unknown DateTime format " ++ fmt); } pub fn jsonStringify(value: DateTime, _: std.json.StringifyOptions, writer: anytype) !void { - try std.fmt.format(writer, "\"{}\"", .{value}); + try std.fmt.format(writer, "\"{rfc3339}\"", .{value}); } From d504cef8ffa26e5ae5606ed797c47610bba057a0 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Mon, 12 Dec 2022 03:40:51 -0800 Subject: [PATCH 3/3] Fix template formatting --- src/template/lib.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/template/lib.zig b/src/template/lib.zig index 100e95b..4a82a1b 100644 --- a/src/template/lib.zig +++ b/src/template/lib.zig @@ -206,7 +206,7 @@ fn print(writer: anytype, arg: anytype) !void { if (T == void) return; if (comptime std.meta.trait.isZigString(T)) return htmlEscape(writer, arg); if (comptime std.meta.trait.isNumber(T)) return std.fmt.format(writer, "{}", .{arg}); - std.fmt.format(writer, "{}", .{arg}); + try std.fmt.format(writer, "{}", .{arg}); } fn Deref(comptime T: type, comptime names: []const []const u8) type {