Compare commits

...

3 commits

3 changed files with 77 additions and 12 deletions

View file

@ -180,6 +180,13 @@ fn executeStatement(
context, context,
); );
}, },
.format => |fmt| {
try std.fmt.format(
writer,
"{" ++ fmt.format ++ "}",
.{evaluateExpression(fmt.value, args, captures, context)},
);
},
//else => @compileError("TODO"), //else => @compileError("TODO"),
} }
} }
@ -196,11 +203,10 @@ fn htmlEscape(writer: anytype, str: []const u8) !void {
fn print(writer: anytype, arg: anytype) !void { fn print(writer: anytype, arg: anytype) !void {
const T = @TypeOf(arg); const T = @TypeOf(arg);
if (T == void) return;
if (comptime std.meta.trait.isZigString(T)) return htmlEscape(writer, arg); if (comptime std.meta.trait.isZigString(T)) return htmlEscape(writer, arg);
if (comptime std.meta.trait.isNumber(T)) return std.fmt.format(writer, "{}", .{arg}); if (comptime std.meta.trait.isNumber(T)) return std.fmt.format(writer, "{}", .{arg});
@compileLog(@TypeOf(arg)); try std.fmt.format(writer, "{}", .{arg});
@compileError("TODO");
} }
fn Deref(comptime T: type, comptime names: []const []const u8) type { fn Deref(comptime T: type, comptime names: []const []const u8) type {
@ -450,6 +456,9 @@ fn parseTemplate(
.call_template => |call| items = items ++ [_]TemplateItem{.{ .call_template => |call| items = items ++ [_]TemplateItem{.{
.statement = .{ .call_template = call }, .statement = .{ .call_template = call },
}}, }},
.format => |call| items = items ++ [_]TemplateItem{.{
.statement = .{ .format = call },
}},
.end_switch, .case_header => if (template_type == .switch_block) .end_switch, .case_header => if (template_type == .switch_block)
break cb break cb
else else
@ -514,6 +523,7 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken
.percent => items = items ++ [_]TemplateToken{.{ .text = "%" }}, .percent => items = items ++ [_]TemplateToken{.{ .text = "%" }},
.open_paren => items = items ++ [_]TemplateToken{.{ .text = "(" }}, .open_paren => items = items ++ [_]TemplateToken{.{ .text = "(" }},
.close_paren => items = items ++ [_]TemplateToken{.{ .text = ")" }}, .close_paren => items = items ++ [_]TemplateToken{.{ .text = ")" }},
.double_quote => items = items ++ [_]TemplateToken{.{ .text = "\"" }},
}; };
return items; return items;
@ -684,6 +694,11 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken
iter = result.new_iter; iter = result.new_iter;
break .{ .case_header = result.item }; break .{ .case_header = result.item };
}, },
.format => {
const result = parseFormat(iter);
iter = result.new_iter;
break .{ .format = result.item };
},
//else => @compileError("TODO"), //else => @compileError("TODO"),
} }
@ -706,7 +721,7 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken
iter = expr.new_iter; iter = expr.new_iter;
break .{ .expression = expr.item }; break .{ .expression = expr.item };
}, },
else => @compileError("TODO"), else => @compileError("TODO " ++ @tagName(token)),
}; };
// search for end of statement // 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 { fn ParseResult(comptime It: type, comptime T: type) type {
return struct { return struct {
new_iter: It, new_iter: It,
@ -1032,12 +1083,18 @@ const IfHeader = struct {
capture: ?[]const u8, capture: ?[]const u8,
}; };
const FormatStmt = struct {
format: []const u8,
value: Expression,
};
const Statement = union(enum) { const Statement = union(enum) {
expression: Expression, expression: Expression,
@"for": For, @"for": For,
@"if": If, @"if": If,
@"switch": Switch, @"switch": Switch,
call_template: CallTemplate, call_template: CallTemplate,
format: FormatStmt,
}; };
const ControlBlock = struct { const ControlBlock = struct {
@ -1053,6 +1110,7 @@ const ControlBlock = struct {
switch_header: SwitchHeader, switch_header: SwitchHeader,
case_header: CaseHeader, case_header: CaseHeader,
end_switch: void, end_switch: void,
format: FormatStmt,
}; };
block: Data, block: Data,
strip_before: bool, strip_before: bool,
@ -1067,6 +1125,7 @@ const Keyword = enum {
@"template", @"template",
@"switch", @"switch",
@"case", @"case",
@"format",
}; };
const EndKeyword = enum { const EndKeyword = enum {
@ -1102,6 +1161,7 @@ const ControlToken = union(enum) {
percent: void, percent: void,
open_paren: void, open_paren: void,
close_paren: void, close_paren: void,
double_quote: void,
}; };
const ControlTokenIter = struct { const ControlTokenIter = struct {
@ -1136,6 +1196,7 @@ const ControlTokenIter = struct {
'%' => return .{ .percent = {} }, '%' => return .{ .percent = {} },
'(' => return .{ .open_paren = {} }, '(' => return .{ .open_paren = {} },
')' => return .{ .close_paren = {} }, ')' => return .{ .close_paren = {} },
'"' => return .{ .double_quote = {} },
' ', '\t', '\n', '\r' => { ' ', '\t', '\n', '\r' => {
var idx: usize = 0; 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) {}
@ -1147,7 +1208,7 @@ const ControlTokenIter = struct {
}, },
else => { else => {
var idx: usize = 0; 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; self.start += idx - 1;
return .{ .text = remaining[0..idx] }; return .{ .text = remaining[0..idx] };

View file

@ -26,6 +26,8 @@
neither neither
{=/if} {=/if}
format: {#format "s" .x}
{#switch .snap case foo =} {#switch .snap case foo =}
foo foo
{= #case bar =} {= #case bar =}

View file

@ -117,14 +117,16 @@ pub fn toCharArrayZ(value: DateTime) [array_len + 1:0]u8 {
return buf; return buf;
} }
pub fn format(value: DateTime, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { 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( return std.fmt.format(
writer, writer,
"{:0>4}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}Z", "{: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() }, .{ 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 { pub fn jsonStringify(value: DateTime, _: std.json.StringifyOptions, writer: anytype) !void {
try std.fmt.format(writer, "\"{}\"", .{value}); try std.fmt.format(writer, "\"{rfc3339}\"", .{value});
} }