Compare commits

...

3 commits

3 changed files with 77 additions and 12 deletions

View file

@ -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");
try 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] };

View file

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

View file

@ -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 {
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});
}