Expression refactor
This commit is contained in:
parent
1269aeeac1
commit
54748b4c07
2 changed files with 100 additions and 129 deletions
|
@ -211,34 +211,7 @@ fn print(writer: anytype, arg: anytype) !void {
|
||||||
try std.fmt.format(writer, "{}", .{arg});
|
try std.fmt.format(writer, "{}", .{arg});
|
||||||
}
|
}
|
||||||
|
|
||||||
const DerefError = error{NullOptional};
|
const ExpressionError = error{ IndexOutOfBounds, NullOptional };
|
||||||
fn Deref(comptime T: type, comptime names: []const DerefDecl) type {
|
|
||||||
if (names.len == 0) return T;
|
|
||||||
|
|
||||||
// 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| {
|
|
||||||
if (std.mem.eql(u8, f.name, name)) break f;
|
|
||||||
} else @compileError("Unknown field " ++ name ++ " in type " ++ @typeName(T));
|
|
||||||
break :blk field.field_type;
|
|
||||||
},
|
|
||||||
.optional_unwrap => std.meta.Child(T),
|
|
||||||
};
|
|
||||||
|
|
||||||
return Deref(F, names[1..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deref(arg: anytype, comptime names: []const DerefDecl) DerefError!Deref(@TypeOf(arg), names) {
|
|
||||||
if (names.len == 0) return arg;
|
|
||||||
|
|
||||||
switch (names[0]) {
|
|
||||||
.field => |name| return deref(@field(arg, name), names[1..]),
|
|
||||||
.optional_unwrap => return arg orelse error.NullOptional,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ExpressionError = error{IndexOutOfBounds} || DerefError;
|
|
||||||
|
|
||||||
fn EvaluateExpression(
|
fn EvaluateExpression(
|
||||||
comptime expression: Expression,
|
comptime expression: Expression,
|
||||||
|
@ -247,14 +220,21 @@ fn EvaluateExpression(
|
||||||
comptime Context: type,
|
comptime Context: type,
|
||||||
) type {
|
) type {
|
||||||
return switch (expression) {
|
return switch (expression) {
|
||||||
.arg_deref => |names| Deref(Args, names),
|
.args => Args,
|
||||||
.capture_deref => |names| Deref(Captures, names),
|
.captures => Captures,
|
||||||
.context_deref => |names| Deref(Context, names),
|
.context => Context,
|
||||||
|
.deref => |expr| {
|
||||||
|
const T = EvaluateExpression(expr.container, Args, Captures, Context);
|
||||||
|
for (@typeInfo(T).Struct.fields) |f| {
|
||||||
|
if (std.mem.eql(u8, expr.field, f.name)) return f.field_type;
|
||||||
|
}
|
||||||
|
},
|
||||||
.equals => bool,
|
.equals => bool,
|
||||||
.builtin => |call| switch (call.*) {
|
.builtin => |call| switch (call.*) {
|
||||||
.isTag => bool,
|
.isTag => bool,
|
||||||
.slice => |sl| []const std.meta.Elem(EvaluateExpression(sl.iterable, Args, Captures, Context)),
|
.slice => |sl| []const std.meta.Elem(EvaluateExpression(sl.iterable, Args, Captures, Context)),
|
||||||
},
|
},
|
||||||
|
.optional_unwrap => |expr| std.meta.Child(EvaluateExpression(expr, Args, Captures, Context)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,9 +245,15 @@ fn evaluateExpression(
|
||||||
context: anytype,
|
context: anytype,
|
||||||
) ExpressionError!EvaluateExpression(expression, @TypeOf(args), @TypeOf(captures), @TypeOf(context)) {
|
) ExpressionError!EvaluateExpression(expression, @TypeOf(args), @TypeOf(captures), @TypeOf(context)) {
|
||||||
return switch (expression) {
|
return switch (expression) {
|
||||||
.arg_deref => |names| try deref(args, names),
|
.args => args,
|
||||||
.capture_deref => |names| try deref(captures, names),
|
.captures => captures,
|
||||||
.context_deref => |names| try deref(context, names),
|
.context => context,
|
||||||
|
.deref => |expr| {
|
||||||
|
return @field(
|
||||||
|
try evaluateExpression(expr.container, args, captures, context),
|
||||||
|
expr.field,
|
||||||
|
);
|
||||||
|
},
|
||||||
.equals => |eql| {
|
.equals => |eql| {
|
||||||
const lhs = try evaluateExpression(eql.lhs, args, captures, context);
|
const lhs = try evaluateExpression(eql.lhs, args, captures, context);
|
||||||
const rhs = try evaluateExpression(eql.rhs, args, captures, context);
|
const rhs = try evaluateExpression(eql.rhs, args, captures, context);
|
||||||
|
@ -293,6 +279,7 @@ fn evaluateExpression(
|
||||||
return iterable[start..end];
|
return iterable[start..end];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.optional_unwrap => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,27 +553,38 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt
|
||||||
|
|
||||||
var last_valid_iter: ?ControlTokenIter = null;
|
var last_valid_iter: ?ControlTokenIter = null;
|
||||||
var expr: ?Expression = null;
|
var expr: ?Expression = null;
|
||||||
while (iter.next()) |token| switch (token) {
|
while (iter.next()) |token| {
|
||||||
|
switch (token) {
|
||||||
.whitespace => {},
|
.whitespace => {},
|
||||||
.period => {
|
.period => {
|
||||||
const names = parseDeref(iter);
|
iter = skipWhitespace(iter);
|
||||||
iter = names.new_iter;
|
if (expr == null) {
|
||||||
if (expr != null) break;
|
expr = .{ .args = {} };
|
||||||
expr = .{ .arg_deref = names.item };
|
if (iter.peek()) |n| if (n == .text) iter.putBack(.{ .period = {} });
|
||||||
|
} else {
|
||||||
|
const field = iter.next();
|
||||||
|
expectToken(field, .text);
|
||||||
|
expr = .{
|
||||||
|
.deref = &.{
|
||||||
|
.container = expr.?,
|
||||||
|
.field = field.?.text,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
last_valid_iter = iter;
|
last_valid_iter = iter;
|
||||||
},
|
},
|
||||||
.dollar => {
|
.dollar => {
|
||||||
const names = parseDeref(iter);
|
|
||||||
iter = names.new_iter;
|
|
||||||
if (expr != null) break;
|
if (expr != null) break;
|
||||||
expr = .{ .capture_deref = names.item };
|
iter = skipWhitespace(iter);
|
||||||
|
expr = .{ .captures = {} };
|
||||||
|
if (iter.peek()) |n| if (n == .text) iter.putBack(.{ .period = {} });
|
||||||
last_valid_iter = iter;
|
last_valid_iter = iter;
|
||||||
},
|
},
|
||||||
.percent => {
|
.percent => {
|
||||||
const names = parseDeref(iter);
|
|
||||||
iter = names.new_iter;
|
|
||||||
if (expr != null) break;
|
if (expr != null) break;
|
||||||
expr = .{ .context_deref = names.item };
|
iter = skipWhitespace(iter);
|
||||||
|
expr = .{ .context = {} };
|
||||||
|
if (iter.peek()) |n| if (n == .text) iter.putBack(.{ .period = {} });
|
||||||
last_valid_iter = iter;
|
last_valid_iter = iter;
|
||||||
},
|
},
|
||||||
.equals => {
|
.equals => {
|
||||||
|
@ -613,7 +611,8 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt
|
||||||
last_valid_iter = iter;
|
last_valid_iter = iter;
|
||||||
},
|
},
|
||||||
else => break,
|
else => break,
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.new_iter = last_valid_iter orelse @compileError("Invalid Expression"),
|
.new_iter = last_valid_iter orelse @compileError("Invalid Expression"),
|
||||||
|
@ -972,40 +971,6 @@ fn parseSwitchHeader(comptime tokens: ControlTokenIter) ParseResult(ControlToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, []const DerefDecl) {
|
|
||||||
comptime {
|
|
||||||
var iter = tokens;
|
|
||||||
var fields: []const DerefDecl = &.{};
|
|
||||||
var wants = .text;
|
|
||||||
while (iter.peek()) |token| {
|
|
||||||
switch (token) {
|
|
||||||
.whitespace => {},
|
|
||||||
.text => |text| {
|
|
||||||
if (wants == .period) break;
|
|
||||||
fields = fields ++ [1]DerefDecl{.{ .field = text }};
|
|
||||||
wants = .period;
|
|
||||||
},
|
|
||||||
.period => {
|
|
||||||
if (wants != .period) @compileError("Unexpected token \".\"");
|
|
||||||
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"),
|
|
||||||
}
|
|
||||||
_ = iter.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.new_iter = iter,
|
|
||||||
.item = fields,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseCallTemplate(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, CallTemplate) {
|
fn parseCallTemplate(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, CallTemplate) {
|
||||||
comptime {
|
comptime {
|
||||||
var iter = tokens;
|
var iter = tokens;
|
||||||
|
@ -1076,9 +1041,9 @@ const TemplateItem = union(enum) {
|
||||||
statement: Statement,
|
statement: Statement,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DerefDecl = union(enum) {
|
const DerefExpr = struct {
|
||||||
|
container: Expression,
|
||||||
field: []const u8,
|
field: []const u8,
|
||||||
optional_unwrap: void,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const EqualsExpr = struct {
|
const EqualsExpr = struct {
|
||||||
|
@ -1087,11 +1052,13 @@ const EqualsExpr = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Expression = union(enum) {
|
const Expression = union(enum) {
|
||||||
arg_deref: []const DerefDecl,
|
args: void,
|
||||||
capture_deref: []const DerefDecl,
|
captures: void,
|
||||||
context_deref: []const DerefDecl,
|
context: void,
|
||||||
|
deref: *const DerefExpr,
|
||||||
equals: *const EqualsExpr,
|
equals: *const EqualsExpr,
|
||||||
builtin: *const BuiltinCall,
|
builtin: *const BuiltinCall,
|
||||||
|
optional_unwrap: *const Expression,
|
||||||
};
|
};
|
||||||
|
|
||||||
const For = struct {
|
const For = struct {
|
||||||
|
@ -1235,14 +1202,17 @@ const ControlToken = union(enum) {
|
||||||
const ControlTokenIter = struct {
|
const ControlTokenIter = struct {
|
||||||
start: usize = 0,
|
start: usize = 0,
|
||||||
text: []const u8,
|
text: []const u8,
|
||||||
peeked_token: ?ControlToken = null,
|
peeked_tokens: [2]?ControlToken = [2]?ControlToken{ null, null },
|
||||||
|
peeked_token_count: usize = 0,
|
||||||
|
|
||||||
row: usize = 0,
|
row: usize = 0,
|
||||||
|
|
||||||
fn next(self: *ControlTokenIter) ?ControlToken {
|
fn next(self: *ControlTokenIter) ?ControlToken {
|
||||||
if (self.peeked_token) |token| {
|
if (self.peeked_token_count != 0) {
|
||||||
self.peeked_token = null;
|
const t = self.peeked_tokens[self.peeked_token_count - 1].?;
|
||||||
return token;
|
self.peeked_tokens[self.peeked_token_count - 1] = null;
|
||||||
|
self.peeked_token_count -= 1;
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
const remaining = self.text[self.start..];
|
const remaining = self.text[self.start..];
|
||||||
|
@ -1287,13 +1257,14 @@ const ControlTokenIter = struct {
|
||||||
|
|
||||||
fn peek(self: *ControlTokenIter) ?ControlToken {
|
fn peek(self: *ControlTokenIter) ?ControlToken {
|
||||||
const token = self.next();
|
const token = self.next();
|
||||||
self.peeked_token = token;
|
if (token) |t| self.putBack(t);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn putBack(self: *ControlTokenIter, token: ControlToken) void {
|
fn putBack(self: *ControlTokenIter, token: ControlToken) void {
|
||||||
std.debug.assert(self.peeked_token == null);
|
std.debug.assert(self.peeked_token_count < self.peeked_tokens.len);
|
||||||
self.peeked_token = token;
|
self.peeked_tokens[self.peeked_token_count] = token;
|
||||||
|
self.peeked_token_count += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1310,8 +1281,8 @@ test "template" {
|
||||||
try testCase("abcd", .{}, "abcd");
|
try testCase("abcd", .{}, "abcd");
|
||||||
try testCase("{.val}", .{ .val = 3 }, "3");
|
try testCase("{.val}", .{ .val = 3 }, "3");
|
||||||
try testCase("{#if .val}1{/if}", .{ .val = true }, "1");
|
try testCase("{#if .val}1{/if}", .{ .val = true }, "1");
|
||||||
try testCase("{#for .vals |$v|}{$v}{/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
|
||||||
try testCase("{#for .vals |$v|=} {$v} {=/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
try testCase("{#for .vals |$v|=} {$v} {=/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
||||||
|
try testCase("{#for .vals |$val|}{$val}{/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
||||||
try testCase("{#if .val}1{#else}0{/if}", .{ .val = true }, "1");
|
try testCase("{#if .val}1{#else}0{/if}", .{ .val = true }, "1");
|
||||||
try testCase("{#if .val}1{#else}0{/if}", .{ .val = false }, "0");
|
try testCase("{#if .val}1{#else}0{/if}", .{ .val = false }, "0");
|
||||||
try testCase("{#if .val}1{#elif .foo}2{/if}", .{ .val = false, .foo = true }, "2");
|
try testCase("{#if .val}1{#elif .foo}2{/if}", .{ .val = false, .foo = true }, "2");
|
||||||
|
|
|
@ -49,7 +49,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.?}
|
{{.maybe_foo.?}}
|
||||||
This causes an error: {{.maybe_bar.?}}
|
This causes an error: {{.maybe_bar.?}}
|
||||||
|
|
||||||
<template>{#template test_tmpl .bar}</template>
|
<template>{#template test_tmpl .bar}</template>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue