diff --git a/src/template/lib.zig b/src/template/lib.zig index 4c13fc8..da0fef8 100644 --- a/src/template/lib.zig +++ b/src/template/lib.zig @@ -1,6 +1,7 @@ const std = @import("std"); pub fn main() !void { + const Enum = enum { foo, bar }; try execute( std.io.getStdOut().writer(), .{ .test_tmpl = "{.x} {%context_foo}" }, @@ -18,6 +19,7 @@ pub fn main() !void { .quxx2 = true, .maybe_foo = @as(?[]const u8, "foo"), .maybe_bar = @as(?[]const u8, null), + .snap = Enum.bar, .x = "y", }, .{ @@ -170,6 +172,9 @@ fn EvaluateExpression( .capture_deref => |names| Deref(Captures, names), .context_deref => |names| Deref(Context, names), .equals => bool, + .builtin => |call| switch (call.*) { + .isTag => bool, + }, }; } @@ -193,6 +198,12 @@ fn evaluateExpression( return T.eql(lhs, rhs); } else return lhs == rhs; }, + .builtin => |call| switch (call.*) { + .isTag => |hdr| { + const val = evaluateExpression(hdr.expression, args, captures, context); + return std.meta.isTag(val, hdr.tag); + }, + }, }; } @@ -402,6 +413,8 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken .at => items = items ++ [_]TemplateToken{.{ .text = "@" }}, .comma => items = items ++ [_]TemplateToken{.{ .text = "," }}, .percent => items = items ++ [_]TemplateToken{.{ .text = "%" }}, + .open_paren => items = items ++ [_]TemplateToken{.{ .text = "(" }}, + .close_paren => items = items ++ [_]TemplateToken{.{ .text = ")" }}, }; return items; @@ -419,18 +432,21 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt .period => { const names = parseDeref(iter); iter = names.new_iter; + if (expr != null) break; expr = .{ .arg_deref = names.item }; last_valid_iter = iter; }, .dollar => { const names = parseDeref(iter); iter = names.new_iter; + if (expr != null) break; expr = .{ .capture_deref = names.item }; last_valid_iter = iter; }, .percent => { const names = parseDeref(iter); iter = names.new_iter; + if (expr != null) break; expr = .{ .context_deref = names.item }; last_valid_iter = iter; }, @@ -450,6 +466,13 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt last_valid_iter = iter; } else break; }, + .at => { + if (expr != null) break; + const builtin = parseBuiltin(iter); + iter = builtin.new_iter; + expr = .{ .builtin = &builtin.item }; + last_valid_iter = iter; + }, else => break, }; @@ -460,6 +483,54 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt } } +fn expectToken(comptime token: ?ControlToken, comptime exp: std.meta.Tag(ControlToken)) void { + comptime { + if (token == null) @compileError("Unexpected End Of Template"); + const token_tag = std.meta.activeTag(token.?); + + if (token_tag != exp) + @compileError("Expected " ++ @tagName(exp) ++ ", got " ++ @tagName(token_tag)); + } +} + +fn parseBuiltin(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, BuiltinCall) { + comptime { + var iter = tokens; + const builtin = blk: { + const next = iter.next() orelse @compileError("Invalid Builtin"); + if (next != .text) @compileError("Invalid Builtin"); + break :blk std.meta.stringToEnum(Builtin, next.text) orelse @compileError("Invalid Builtin"); + }; + + iter = skipWhitespace(iter); + expectToken(iter.next(), .open_paren); + iter = skipWhitespace(iter); + const call = switch (builtin) { + .isTag => blk: { + const expr = parseExpression(iter); + iter = expr.new_iter; + expectToken(iter.next(), .comma); + iter = skipWhitespace(iter); + const tag = iter.next(); + expectToken(tag, .text); + break :blk .{ + .isTag = .{ + .tag = tag.?.text, + .expression = expr.item, + }, + }; + }, + }; + iter = skipWhitespace(iter); + expectToken(iter.next(), .close_paren); + + return .{ + .new_iter = iter, + .item = call, + }; + } +} + fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, ControlBlock) { comptime { var iter = tokens; @@ -745,6 +816,7 @@ const Expression = union(enum) { capture_deref: []const []const u8, context_deref: []const []const u8, equals: *const EqualsExpr, + builtin: *const BuiltinCall, }; const For = struct { @@ -812,6 +884,17 @@ const EndKeyword = enum { @"if", }; +const Builtin = enum { + isTag, +}; + +const BuiltinCall = union(Builtin) { + isTag: struct { + tag: []const u8, + expression: Expression, + }, +}; + const ControlToken = union(enum) { text: []const u8, open_bracket: void, @@ -826,6 +909,8 @@ const ControlToken = union(enum) { at: void, comma: void, percent: void, + open_paren: void, + close_paren: void, }; const ControlTokenIter = struct { @@ -858,6 +943,8 @@ const ControlTokenIter = struct { '@' => return .{ .at = {} }, ',' => return .{ .comma = {} }, '%' => return .{ .percent = {} }, + '(' => return .{ .open_paren = {} }, + ')' => return .{ .close_paren = {} }, ' ', '\t', '\n', '\r' => { var idx: usize = 0; while (idx < remaining.len and std.mem.indexOfScalar(u8, " \t\n\r", remaining[idx]) != null) : (idx += 1) {} @@ -869,7 +956,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 2dc07ff..36ad2b4 100644 --- a/src/template/test.tmp.html +++ b/src/template/test.tmp.html @@ -16,6 +16,8 @@ {= /for} {#if .quxx == .quxx2}eql{#else}neq{/if} {#if .quxx == .qux}eql{#else}neq{/if} + {#if @isTag(.snap, foo)}foo{/if} + {#if @isTag(.snap, bar)}bar{/if} {#if .qux=} qux {=#elif .quxx=}