parent
8c0548fc74
commit
86e203730c
3 changed files with 71 additions and 25 deletions
|
@ -227,7 +227,7 @@ pub const Response = struct {
|
||||||
defer stream.close();
|
defer stream.close();
|
||||||
|
|
||||||
const writer = stream.writer();
|
const writer = stream.writer();
|
||||||
try @import("template").execute(writer, .{}, templ, data);
|
try @import("template").execute(writer, .{}, templ, data, .{});
|
||||||
|
|
||||||
try stream.finish();
|
try stream.finish();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ const std = @import("std");
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
try execute(
|
try execute(
|
||||||
std.io.getStdOut().writer(),
|
std.io.getStdOut().writer(),
|
||||||
.{ .test_tmpl = "{.x}" },
|
.{ .test_tmpl = "{.x} {@context_foo}" },
|
||||||
@embedFile("./test.tmp.html"),
|
@embedFile("./test.tmp.html"),
|
||||||
.{
|
.{
|
||||||
.community = .{ .name = "abcd" },
|
.community = .{ .name = "abcd" },
|
||||||
|
@ -16,34 +16,57 @@ pub fn main() !void {
|
||||||
.qux = false,
|
.qux = false,
|
||||||
.quxx = true,
|
.quxx = true,
|
||||||
},
|
},
|
||||||
|
.{
|
||||||
|
.context_foo = "foo",
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(writer: anytype, comptime other_templates: anytype, comptime template: []const u8, args: anytype) !void {
|
pub fn execute(
|
||||||
|
writer: anytype,
|
||||||
|
comptime other_templates: anytype,
|
||||||
|
comptime template: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
context: anytype,
|
||||||
|
) !void {
|
||||||
@setEvalBranchQuota(@intCast(u32, template.len * 8));
|
@setEvalBranchQuota(@intCast(u32, template.len * 8));
|
||||||
|
|
||||||
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);
|
||||||
try executeTemplate(writer, other_templates, tmpl.items, args, .{});
|
try executeTemplate(writer, other_templates, tmpl.items, args, .{}, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executeTemplate(writer: anytype, comptime templates: anytype, comptime items: []const TemplateItem, args: anytype, captures: anytype) !void {
|
fn executeTemplate(
|
||||||
|
writer: anytype,
|
||||||
|
comptime templates: anytype,
|
||||||
|
comptime items: []const TemplateItem,
|
||||||
|
args: anytype,
|
||||||
|
captures: anytype,
|
||||||
|
context: anytype,
|
||||||
|
) !void {
|
||||||
inline for (items) |it| {
|
inline for (items) |it| {
|
||||||
switch (it) {
|
switch (it) {
|
||||||
.text => |text| try writer.writeAll(text),
|
.text => |text| try writer.writeAll(text),
|
||||||
.statement => |stmt| try executeStatement(writer, templates, stmt, args, captures),
|
.statement => |stmt| try executeStatement(writer, templates, stmt, args, captures, context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executeStatement(writer: anytype, comptime templates: anytype, comptime stmt: Statement, args: anytype, captures: anytype) !void {
|
fn executeStatement(
|
||||||
|
writer: anytype,
|
||||||
|
comptime templates: anytype,
|
||||||
|
comptime stmt: Statement,
|
||||||
|
args: anytype,
|
||||||
|
captures: anytype,
|
||||||
|
context: anytype,
|
||||||
|
) !void {
|
||||||
switch (stmt) {
|
switch (stmt) {
|
||||||
.expression => |expr| {
|
.expression => |expr| {
|
||||||
const val = evaluateExpression(expr, args, captures);
|
const val = evaluateExpression(expr, args, captures, context);
|
||||||
try print(writer, val);
|
try print(writer, val);
|
||||||
},
|
},
|
||||||
.@"for" => |loop| {
|
.@"for" => |loop| {
|
||||||
const iterable = evaluateExpression(loop.header.iterable, args, captures);
|
const iterable = evaluateExpression(loop.header.iterable, args, captures, context);
|
||||||
const subtemplate = loop.subtemplate;
|
const subtemplate = loop.subtemplate;
|
||||||
//std.log.debug("{any}", .{subtemplate});
|
//std.log.debug("{any}", .{subtemplate});
|
||||||
for (iterable) |v| {
|
for (iterable) |v| {
|
||||||
|
@ -53,25 +76,31 @@ fn executeStatement(writer: anytype, comptime templates: anytype, comptime stmt:
|
||||||
subtemplate,
|
subtemplate,
|
||||||
args,
|
args,
|
||||||
addCapture(captures, loop.header.capture, v),
|
addCapture(captures, loop.header.capture, v),
|
||||||
|
context,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.@"if" => |if_stmt| {
|
.@"if" => |if_stmt| {
|
||||||
const condition = evaluateExpression(if_stmt.header.condition, args, captures);
|
const condition = evaluateExpression(if_stmt.header.condition, args, captures, context);
|
||||||
const subtemplate = if_stmt.subtemplate;
|
const subtemplate = if_stmt.subtemplate;
|
||||||
if (condition) {
|
if (condition) {
|
||||||
try executeTemplate(writer, templates, subtemplate, args, captures);
|
try executeTemplate(writer, templates, subtemplate, args, captures, context);
|
||||||
} else {
|
} else {
|
||||||
if (if_stmt.else_branch) |branch| switch (branch) {
|
if (if_stmt.else_branch) |branch| switch (branch) {
|
||||||
.@"else" => |subtmpl| try executeTemplate(writer, templates, subtmpl, args, captures),
|
.@"else" => |subtmpl| try executeTemplate(writer, templates, subtmpl, args, captures, context),
|
||||||
.elif => |elif| try executeStatement(writer, templates, .{ .@"if" = elif.* }, args, captures),
|
.elif => |elif| try executeStatement(writer, templates, .{ .@"if" = elif.* }, args, captures, context),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.call_template => |call| {
|
.call_template => |call| {
|
||||||
const new_template = @field(templates, call.template_name);
|
const new_template = @field(templates, call.template_name);
|
||||||
try execute(writer, templates, new_template, evaluateExpression(call.args, args, captures));
|
try execute(
|
||||||
//std.log.debug("calling template {s} with arg {any}", .{ call.template_name, call.args });
|
writer,
|
||||||
|
templates,
|
||||||
|
new_template,
|
||||||
|
evaluateExpression(call.args, args, captures, context),
|
||||||
|
context,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
//else => @compileError("TODO"),
|
//else => @compileError("TODO"),
|
||||||
}
|
}
|
||||||
|
@ -102,10 +131,16 @@ fn deref(arg: anytype, comptime names: []const []const u8) Deref(@TypeOf(arg), n
|
||||||
return deref(@field(arg, names[0]), names[1..]);
|
return deref(@field(arg, names[0]), names[1..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn EvaluateExpression(comptime expression: Expression, comptime Args: type, comptime Captures: type) type {
|
fn EvaluateExpression(
|
||||||
|
comptime expression: Expression,
|
||||||
|
comptime Args: type,
|
||||||
|
comptime Captures: type,
|
||||||
|
comptime Context: type,
|
||||||
|
) type {
|
||||||
return switch (expression) {
|
return switch (expression) {
|
||||||
.arg_deref => |names| Deref(Args, names),
|
.arg_deref => |names| Deref(Args, names),
|
||||||
.capture_deref => |names| Deref(Captures, names),
|
.capture_deref => |names| Deref(Captures, names),
|
||||||
|
.context_deref => |names| Deref(Context, names),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +148,12 @@ fn evaluateExpression(
|
||||||
comptime expression: Expression,
|
comptime expression: Expression,
|
||||||
args: anytype,
|
args: anytype,
|
||||||
captures: anytype,
|
captures: anytype,
|
||||||
) EvaluateExpression(expression, @TypeOf(args), @TypeOf(captures)) {
|
context: anytype,
|
||||||
|
) EvaluateExpression(expression, @TypeOf(args), @TypeOf(captures), @TypeOf(context)) {
|
||||||
return switch (expression) {
|
return switch (expression) {
|
||||||
.arg_deref => |names| deref(args, names),
|
.arg_deref => |names| deref(args, names),
|
||||||
.capture_deref => |names| deref(captures, names),
|
.capture_deref => |names| deref(captures, names),
|
||||||
|
.context_deref => |names| deref(context, names),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,11 +300,9 @@ fn parseTemplate(
|
||||||
break cb
|
break cb
|
||||||
else
|
else
|
||||||
@compileError("Unexpected #else tag"),
|
@compileError("Unexpected #else tag"),
|
||||||
.call_template => |call| {
|
.call_template => |call| items = items ++ [_]TemplateItem{.{
|
||||||
items = items ++ [_]TemplateItem{.{
|
.statement = .{ .call_template = call },
|
||||||
.statement = .{ .call_template = call },
|
}},
|
||||||
}};
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -323,6 +358,7 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken
|
||||||
.dollar => items = items ++ [_]TemplateToken{.{ .text = "$" }},
|
.dollar => items = items ++ [_]TemplateToken{.{ .text = "$" }},
|
||||||
.slash => items = items ++ [_]TemplateToken{.{ .text = "/" }},
|
.slash => items = items ++ [_]TemplateToken{.{ .text = "/" }},
|
||||||
.equals => items = items ++ [_]TemplateToken{.{ .text = "=" }},
|
.equals => items = items ++ [_]TemplateToken{.{ .text = "=" }},
|
||||||
|
.at => items = items ++ [_]TemplateToken{.{ .text = "@" }},
|
||||||
};
|
};
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
@ -345,6 +381,11 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt
|
||||||
iter = names.new_iter;
|
iter = names.new_iter;
|
||||||
break .{ .capture_deref = names.item };
|
break .{ .capture_deref = names.item };
|
||||||
},
|
},
|
||||||
|
.at => {
|
||||||
|
const names = parseDeref(iter);
|
||||||
|
iter = names.new_iter;
|
||||||
|
break .{ .context_deref = names.item };
|
||||||
|
},
|
||||||
else => @compileError("TODO"),
|
else => @compileError("TODO"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -414,7 +455,7 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken
|
||||||
.@"if" => break .{ .end_if = {} },
|
.@"if" => break .{ .end_if = {} },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.period, .dollar => {
|
.period, .dollar, .at => {
|
||||||
iter.putBack(token);
|
iter.putBack(token);
|
||||||
const expr = parseExpression(iter);
|
const expr = parseExpression(iter);
|
||||||
iter = expr.new_iter;
|
iter = expr.new_iter;
|
||||||
|
@ -587,6 +628,7 @@ const TemplateItem = union(enum) {
|
||||||
const Expression = union(enum) {
|
const Expression = union(enum) {
|
||||||
arg_deref: []const []const u8,
|
arg_deref: []const []const u8,
|
||||||
capture_deref: []const []const u8,
|
capture_deref: []const []const u8,
|
||||||
|
context_deref: []const []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const For = struct {
|
const For = struct {
|
||||||
|
@ -664,6 +706,7 @@ const ControlToken = union(enum) {
|
||||||
dollar: void,
|
dollar: void,
|
||||||
slash: void,
|
slash: void,
|
||||||
equals: void,
|
equals: void,
|
||||||
|
at: void,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ControlTokenIter = struct {
|
const ControlTokenIter = struct {
|
||||||
|
@ -693,6 +736,7 @@ const ControlTokenIter = struct {
|
||||||
'$' => return .{ .dollar = {} },
|
'$' => return .{ .dollar = {} },
|
||||||
'/' => return .{ .slash = {} },
|
'/' => return .{ .slash = {} },
|
||||||
'=' => return .{ .equals = {} },
|
'=' => return .{ .equals = {} },
|
||||||
|
'@' => return .{ .at = {} },
|
||||||
' ', '\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) {}
|
||||||
|
@ -704,7 +748,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] };
|
||||||
|
@ -728,7 +772,7 @@ test "template" {
|
||||||
const testCase = struct {
|
const testCase = struct {
|
||||||
fn case(comptime tmpl: []const u8, args: anytype, expected: []const u8) !void {
|
fn case(comptime tmpl: []const u8, args: anytype, expected: []const u8) !void {
|
||||||
var stream = std.io.changeDetectionStream(expected, std.io.null_writer);
|
var stream = std.io.changeDetectionStream(expected, std.io.null_writer);
|
||||||
try execute(stream.writer(), tmpl, args);
|
try execute(stream.writer(), .{}, tmpl, args, .{});
|
||||||
try std.testing.expect(!stream.changeDetected());
|
try std.testing.expect(!stream.changeDetected());
|
||||||
}
|
}
|
||||||
}.case;
|
}.case;
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
{=/if}
|
{=/if}
|
||||||
|
|
||||||
<template>{#template test_tmpl .bar}</template>
|
<template>{#template test_tmpl .bar}</template>
|
||||||
|
|
||||||
|
{@context_foo}
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue