Add optional unwrap syntax
This commit is contained in:
parent
a230a26151
commit
bf0d2f6642
2 changed files with 40 additions and 15 deletions
|
@ -40,7 +40,7 @@ pub fn execute(
|
||||||
args: anytype,
|
args: anytype,
|
||||||
context: anytype,
|
context: anytype,
|
||||||
) !void {
|
) !void {
|
||||||
@setEvalBranchQuota(@intCast(u32, template.len * 8));
|
@setEvalBranchQuota(@intCast(u32, template.len * 12));
|
||||||
|
|
||||||
const tokens = comptime parseTemplateTokens(ControlTokenIter{ .text = template });
|
const tokens = comptime parseTemplateTokens(ControlTokenIter{ .text = template });
|
||||||
const tmpl = comptime parseTemplate(tokens, 0, .root);
|
const tmpl = comptime parseTemplate(tokens, 0, .root);
|
||||||
|
@ -209,20 +209,30 @@ fn print(writer: anytype, arg: anytype) !void {
|
||||||
try std.fmt.format(writer, "{}", .{arg});
|
try std.fmt.format(writer, "{}", .{arg});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Deref(comptime T: type, comptime names: []const []const u8) type {
|
fn Deref(comptime T: type, comptime names: []const DerefDecl) type {
|
||||||
if (names.len == 0) return T;
|
if (names.len == 0) return T;
|
||||||
|
|
||||||
// Compiler segfaults when I use std.meta to get this info so we search it manually
|
// Compiler segfaults when I use std.meta to get this info so we search it manually
|
||||||
|
const F = switch (names[0]) {
|
||||||
|
.field => |name| blk: {
|
||||||
const field = for (@typeInfo(T).Struct.fields) |f| {
|
const field = for (@typeInfo(T).Struct.fields) |f| {
|
||||||
if (std.mem.eql(u8, f.name, names[0])) break f;
|
if (std.mem.eql(u8, f.name, name)) break f;
|
||||||
} else @compileError("Unknown field " ++ names[0] ++ " in type " ++ @typeName(T));
|
} else @compileError("Unknown field " ++ name ++ " in type " ++ @typeName(T));
|
||||||
|
break :blk field.field_type;
|
||||||
|
},
|
||||||
|
.optional_unwrap => std.meta.Child(T),
|
||||||
|
};
|
||||||
|
|
||||||
return Deref(field.field_type, names[1..]);
|
return Deref(F, names[1..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deref(arg: anytype, comptime names: []const []const u8) Deref(@TypeOf(arg), names) {
|
fn deref(arg: anytype, comptime names: []const DerefDecl) Deref(@TypeOf(arg), names) {
|
||||||
if (names.len == 0) return arg;
|
if (names.len == 0) return arg;
|
||||||
return deref(@field(arg, names[0]), names[1..]);
|
|
||||||
|
switch (names[0]) {
|
||||||
|
.field => |name| return deref(@field(arg, name), names[1..]),
|
||||||
|
.optional_unwrap => return arg.?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn EvaluateExpression(
|
fn EvaluateExpression(
|
||||||
|
@ -524,6 +534,7 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken
|
||||||
.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 = "\"" }},
|
.double_quote => items = items ++ [_]TemplateToken{.{ .text = "\"" }},
|
||||||
|
.question_mark => items = items ++ [_]TemplateToken{.{ .text = "?" }},
|
||||||
};
|
};
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
@ -922,23 +933,28 @@ fn parseSwitchHeader(comptime tokens: ControlTokenIter) ParseResult(ControlToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, []const []const u8) {
|
fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, []const DerefDecl) {
|
||||||
comptime {
|
comptime {
|
||||||
var iter = tokens;
|
var iter = tokens;
|
||||||
var fields: []const []const u8 = &.{};
|
var fields: []const DerefDecl = &.{};
|
||||||
var wants = .text;
|
var wants = .text;
|
||||||
while (iter.peek()) |token| {
|
while (iter.peek()) |token| {
|
||||||
switch (token) {
|
switch (token) {
|
||||||
.whitespace => {},
|
.whitespace => {},
|
||||||
.text => |text| {
|
.text => |text| {
|
||||||
if (wants == .period) break;
|
if (wants == .period) break;
|
||||||
fields = fields ++ [1][]const u8{text};
|
fields = fields ++ [1]DerefDecl{.{ .field = text }};
|
||||||
wants = .period;
|
wants = .period;
|
||||||
},
|
},
|
||||||
.period => {
|
.period => {
|
||||||
if (wants != .period) @compileError("Unexpected token \".\"");
|
if (wants != .period) @compileError("Unexpected token \".\"");
|
||||||
wants = .text;
|
wants = .text;
|
||||||
},
|
},
|
||||||
|
.question_mark => {
|
||||||
|
if (wants == .period) break;
|
||||||
|
fields = fields ++ [1]DerefDecl{.{ .optional_unwrap = {} }};
|
||||||
|
wants = .period;
|
||||||
|
},
|
||||||
else => if (wants == .period or fields.len == 0) break else @compileError("Unexpected token"),
|
else => if (wants == .period or fields.len == 0) break else @compileError("Unexpected token"),
|
||||||
}
|
}
|
||||||
_ = iter.next();
|
_ = iter.next();
|
||||||
|
@ -993,6 +1009,7 @@ fn parseFormat(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter,
|
||||||
.percent => fmt_str = fmt_str ++ "%",
|
.percent => fmt_str = fmt_str ++ "%",
|
||||||
.open_paren => fmt_str = fmt_str ++ "(",
|
.open_paren => fmt_str = fmt_str ++ "(",
|
||||||
.close_paren => fmt_str = fmt_str ++ ")",
|
.close_paren => fmt_str = fmt_str ++ ")",
|
||||||
|
.question_mark => fmt_str = fmt_str ++ "?",
|
||||||
.double_quote => break,
|
.double_quote => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1020,15 +1037,20 @@ const TemplateItem = union(enum) {
|
||||||
statement: Statement,
|
statement: Statement,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DerefDecl = union(enum) {
|
||||||
|
field: []const u8,
|
||||||
|
optional_unwrap: void,
|
||||||
|
};
|
||||||
|
|
||||||
const EqualsExpr = struct {
|
const EqualsExpr = struct {
|
||||||
lhs: Expression,
|
lhs: Expression,
|
||||||
rhs: Expression,
|
rhs: Expression,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Expression = union(enum) {
|
const Expression = union(enum) {
|
||||||
arg_deref: []const []const u8,
|
arg_deref: []const DerefDecl,
|
||||||
capture_deref: []const []const u8,
|
capture_deref: []const DerefDecl,
|
||||||
context_deref: []const []const u8,
|
context_deref: []const DerefDecl,
|
||||||
equals: *const EqualsExpr,
|
equals: *const EqualsExpr,
|
||||||
builtin: *const BuiltinCall,
|
builtin: *const BuiltinCall,
|
||||||
};
|
};
|
||||||
|
@ -1162,6 +1184,7 @@ const ControlToken = union(enum) {
|
||||||
open_paren: void,
|
open_paren: void,
|
||||||
close_paren: void,
|
close_paren: void,
|
||||||
double_quote: void,
|
double_quote: void,
|
||||||
|
question_mark: void,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ControlTokenIter = struct {
|
const ControlTokenIter = struct {
|
||||||
|
@ -1197,6 +1220,7 @@ const ControlTokenIter = struct {
|
||||||
'(' => return .{ .open_paren = {} },
|
'(' => return .{ .open_paren = {} },
|
||||||
')' => return .{ .close_paren = {} },
|
')' => return .{ .close_paren = {} },
|
||||||
'"' => return .{ .double_quote = {} },
|
'"' => return .{ .double_quote = {} },
|
||||||
|
'?' => return .{ .question_mark = {} },
|
||||||
' ', '\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) {}
|
||||||
|
@ -1208,7 +1232,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] };
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
{#if .maybe_foo |$v|}{$v}{#else}null{/if}
|
{#if .maybe_foo |$v|}{$v}{#else}null{/if}
|
||||||
{#if .maybe_bar |$v|}{$v}{#else}null{/if}
|
{#if .maybe_bar |$v|}{$v}{#else}null{/if}
|
||||||
{#if .maybe_foo |$_|}abcd{#else}null{/if}
|
{#if .maybe_foo |$_|}abcd{#else}null{/if}
|
||||||
|
{.maybe_foo.?}
|
||||||
|
|
||||||
<template>{#template test_tmpl .bar}</template>
|
<template>{#template test_tmpl .bar}</template>
|
||||||
<template>{#template test_tmpl .}</template>
|
<template>{#template test_tmpl .}</template>
|
||||||
|
|
Loading…
Reference in a new issue