Add @isTag builtin

This commit is contained in:
jaina heartles 2022-12-10 00:32:24 -08:00
parent 4e3450de4e
commit b2007131c8
2 changed files with 90 additions and 1 deletions

View file

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
pub fn main() !void { pub fn main() !void {
const Enum = enum { foo, bar };
try execute( try execute(
std.io.getStdOut().writer(), std.io.getStdOut().writer(),
.{ .test_tmpl = "{.x} {%context_foo}" }, .{ .test_tmpl = "{.x} {%context_foo}" },
@ -18,6 +19,7 @@ pub fn main() !void {
.quxx2 = true, .quxx2 = true,
.maybe_foo = @as(?[]const u8, "foo"), .maybe_foo = @as(?[]const u8, "foo"),
.maybe_bar = @as(?[]const u8, null), .maybe_bar = @as(?[]const u8, null),
.snap = Enum.bar,
.x = "y", .x = "y",
}, },
.{ .{
@ -170,6 +172,9 @@ fn EvaluateExpression(
.capture_deref => |names| Deref(Captures, names), .capture_deref => |names| Deref(Captures, names),
.context_deref => |names| Deref(Context, names), .context_deref => |names| Deref(Context, names),
.equals => bool, .equals => bool,
.builtin => |call| switch (call.*) {
.isTag => bool,
},
}; };
} }
@ -193,6 +198,12 @@ fn evaluateExpression(
return T.eql(lhs, rhs); return T.eql(lhs, rhs);
} else return 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 = "@" }}, .at => items = items ++ [_]TemplateToken{.{ .text = "@" }},
.comma => items = items ++ [_]TemplateToken{.{ .text = "," }}, .comma => items = items ++ [_]TemplateToken{.{ .text = "," }},
.percent => items = items ++ [_]TemplateToken{.{ .text = "%" }}, .percent => items = items ++ [_]TemplateToken{.{ .text = "%" }},
.open_paren => items = items ++ [_]TemplateToken{.{ .text = "(" }},
.close_paren => items = items ++ [_]TemplateToken{.{ .text = ")" }},
}; };
return items; return items;
@ -419,18 +432,21 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt
.period => { .period => {
const names = parseDeref(iter); const names = parseDeref(iter);
iter = names.new_iter; iter = names.new_iter;
if (expr != null) break;
expr = .{ .arg_deref = names.item }; expr = .{ .arg_deref = names.item };
last_valid_iter = iter; last_valid_iter = iter;
}, },
.dollar => { .dollar => {
const names = parseDeref(iter); const names = parseDeref(iter);
iter = names.new_iter; iter = names.new_iter;
if (expr != null) break;
expr = .{ .capture_deref = names.item }; expr = .{ .capture_deref = names.item };
last_valid_iter = iter; last_valid_iter = iter;
}, },
.percent => { .percent => {
const names = parseDeref(iter); const names = parseDeref(iter);
iter = names.new_iter; iter = names.new_iter;
if (expr != null) break;
expr = .{ .context_deref = names.item }; expr = .{ .context_deref = names.item };
last_valid_iter = iter; last_valid_iter = iter;
}, },
@ -450,6 +466,13 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt
last_valid_iter = iter; last_valid_iter = iter;
} else break; } 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, 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) { fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, ControlBlock) {
comptime { comptime {
var iter = tokens; var iter = tokens;
@ -745,6 +816,7 @@ const Expression = union(enum) {
capture_deref: []const []const u8, capture_deref: []const []const u8,
context_deref: []const []const u8, context_deref: []const []const u8,
equals: *const EqualsExpr, equals: *const EqualsExpr,
builtin: *const BuiltinCall,
}; };
const For = struct { const For = struct {
@ -812,6 +884,17 @@ const EndKeyword = enum {
@"if", @"if",
}; };
const Builtin = enum {
isTag,
};
const BuiltinCall = union(Builtin) {
isTag: struct {
tag: []const u8,
expression: Expression,
},
};
const ControlToken = union(enum) { const ControlToken = union(enum) {
text: []const u8, text: []const u8,
open_bracket: void, open_bracket: void,
@ -826,6 +909,8 @@ const ControlToken = union(enum) {
at: void, at: void,
comma: void, comma: void,
percent: void, percent: void,
open_paren: void,
close_paren: void,
}; };
const ControlTokenIter = struct { const ControlTokenIter = struct {
@ -858,6 +943,8 @@ const ControlTokenIter = struct {
'@' => return .{ .at = {} }, '@' => return .{ .at = {} },
',' => return .{ .comma = {} }, ',' => return .{ .comma = {} },
'%' => return .{ .percent = {} }, '%' => return .{ .percent = {} },
'(' => return .{ .open_paren = {} },
')' => return .{ .close_paren = {} },
' ', '\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) {}
@ -869,7 +956,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

@ -16,6 +16,8 @@
{= /for} {= /for}
{#if .quxx == .quxx2}eql{#else}neq{/if} {#if .quxx == .quxx2}eql{#else}neq{/if}
{#if .quxx == .qux}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=} {#if .qux=}
qux qux
{=#elif .quxx=} {=#elif .quxx=}