Compare commits
4 commits
86e203730c
...
466a15f3cb
Author | SHA1 | Date | |
---|---|---|---|
466a15f3cb | |||
ef27bb5c21 | |||
5924c257e0 | |||
06b8483a8a |
2 changed files with 95 additions and 8 deletions
|
@ -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} {@context_foo}" },
|
.{ .test_tmpl = "{.x} {%context_foo}" },
|
||||||
@embedFile("./test.tmp.html"),
|
@embedFile("./test.tmp.html"),
|
||||||
.{
|
.{
|
||||||
.community = .{ .name = "abcd" },
|
.community = .{ .name = "abcd" },
|
||||||
|
@ -15,6 +15,9 @@ pub fn main() !void {
|
||||||
.bar = .{ .x = "x" },
|
.bar = .{ .x = "x" },
|
||||||
.qux = false,
|
.qux = false,
|
||||||
.quxx = true,
|
.quxx = true,
|
||||||
|
.maybe_foo = @as(?[]const u8, "foo"),
|
||||||
|
.maybe_bar = @as(?[]const u8, null),
|
||||||
|
.x = "y",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.context_foo = "foo",
|
.context_foo = "foo",
|
||||||
|
@ -83,9 +86,33 @@ fn executeStatement(
|
||||||
.@"if" => |if_stmt| {
|
.@"if" => |if_stmt| {
|
||||||
const condition = evaluateExpression(if_stmt.header.condition, args, captures, context);
|
const condition = evaluateExpression(if_stmt.header.condition, args, captures, context);
|
||||||
const subtemplate = if_stmt.subtemplate;
|
const subtemplate = if_stmt.subtemplate;
|
||||||
if (condition) {
|
var was_true: bool = false;
|
||||||
try executeTemplate(writer, templates, subtemplate, args, captures, context);
|
if (if_stmt.header.capture) |capture| {
|
||||||
|
if (condition) |val| {
|
||||||
|
was_true = true;
|
||||||
|
try executeTemplate(
|
||||||
|
writer,
|
||||||
|
templates,
|
||||||
|
subtemplate,
|
||||||
|
args,
|
||||||
|
addCapture(captures, capture, val),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (condition) {
|
||||||
|
was_true = true;
|
||||||
|
try executeTemplate(
|
||||||
|
writer,
|
||||||
|
templates,
|
||||||
|
subtemplate,
|
||||||
|
args,
|
||||||
|
captures,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!was_true) {
|
||||||
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, context),
|
.@"else" => |subtmpl| try executeTemplate(writer, templates, subtmpl, args, captures, context),
|
||||||
.elif => |elif| try executeStatement(writer, templates, .{ .@"if" = elif.* }, args, captures, context),
|
.elif => |elif| try executeStatement(writer, templates, .{ .@"if" = elif.* }, args, captures, context),
|
||||||
|
@ -158,6 +185,7 @@ fn evaluateExpression(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn AddCapture(comptime Root: type, comptime name: []const u8, comptime Val: type) type {
|
fn AddCapture(comptime Root: type, comptime name: []const u8, comptime Val: type) type {
|
||||||
|
if (std.mem.eql(u8, name, "_")) return Root;
|
||||||
var fields = std.meta.fields(Root) ++ [_]std.builtin.Type.StructField{.{
|
var fields = std.meta.fields(Root) ++ [_]std.builtin.Type.StructField{.{
|
||||||
.name = name,
|
.name = name,
|
||||||
.field_type = Val,
|
.field_type = Val,
|
||||||
|
@ -175,6 +203,7 @@ fn AddCapture(comptime Root: type, comptime name: []const u8, comptime Val: type
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addCapture(root: anytype, comptime name: []const u8, val: anytype) AddCapture(@TypeOf(root), name, @TypeOf(val)) {
|
fn addCapture(root: anytype, comptime name: []const u8, val: anytype) AddCapture(@TypeOf(root), name, @TypeOf(val)) {
|
||||||
|
if (comptime std.mem.eql(u8, name, "_")) return root;
|
||||||
var result = std.mem.zeroInit(AddCapture(@TypeOf(root), name, @TypeOf(val)), root);
|
var result = std.mem.zeroInit(AddCapture(@TypeOf(root), name, @TypeOf(val)), root);
|
||||||
@field(result, name) = val;
|
@field(result, name) = val;
|
||||||
return result;
|
return result;
|
||||||
|
@ -359,6 +388,8 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken
|
||||||
.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 = "@" }},
|
.at => items = items ++ [_]TemplateToken{.{ .text = "@" }},
|
||||||
|
.comma => items = items ++ [_]TemplateToken{.{ .text = "," }},
|
||||||
|
.percent => items = items ++ [_]TemplateToken{.{ .text = "%" }},
|
||||||
};
|
};
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
@ -381,7 +412,7 @@ 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 => {
|
.percent => {
|
||||||
const names = parseDeref(iter);
|
const names = parseDeref(iter);
|
||||||
iter = names.new_iter;
|
iter = names.new_iter;
|
||||||
break .{ .context_deref = names.item };
|
break .{ .context_deref = names.item };
|
||||||
|
@ -455,7 +486,7 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken
|
||||||
.@"if" => break .{ .end_if = {} },
|
.@"if" => break .{ .end_if = {} },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.period, .dollar, .at => {
|
.period, .dollar, .percent => {
|
||||||
iter.putBack(token);
|
iter.putBack(token);
|
||||||
const expr = parseExpression(iter);
|
const expr = parseExpression(iter);
|
||||||
iter = expr.new_iter;
|
iter = expr.new_iter;
|
||||||
|
@ -551,15 +582,61 @@ fn parseForHeader(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tryParseCapture(comptime tokens: ControlTokenIter) ?ParseResult(ControlTokenIter, []const []const u8) {
|
||||||
|
comptime {
|
||||||
|
var iter = tokens;
|
||||||
|
|
||||||
|
iter = skipWhitespace(iter);
|
||||||
|
if ((iter.next() orelse return null) != .pipe) return null;
|
||||||
|
var captures: []const []const u8 = &.{};
|
||||||
|
while (true) {
|
||||||
|
iter = skipWhitespace(iter);
|
||||||
|
if ((iter.next() orelse return null) != .dollar) return null;
|
||||||
|
iter = skipWhitespace(iter);
|
||||||
|
const name = switch (iter.next() orelse return null) {
|
||||||
|
.text => |text| text,
|
||||||
|
else => return null,
|
||||||
|
};
|
||||||
|
iter = skipWhitespace(iter);
|
||||||
|
captures = captures ++ &[_][]const u8{name};
|
||||||
|
|
||||||
|
switch (iter.next() orelse return null) {
|
||||||
|
.pipe => break,
|
||||||
|
.comma => {},
|
||||||
|
else => return null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.new_iter = iter,
|
||||||
|
.item = captures,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parseIfHeader(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, IfHeader) {
|
fn parseIfHeader(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, IfHeader) {
|
||||||
comptime {
|
comptime {
|
||||||
const condition = parseExpression(tokens);
|
const condition = parseExpression(tokens);
|
||||||
var iter = condition.new_iter;
|
var iter = condition.new_iter;
|
||||||
|
|
||||||
|
const captures = tryParseCapture(iter);
|
||||||
|
if (captures) |cap| {
|
||||||
|
if (cap.item.len == 1) {
|
||||||
|
return .{
|
||||||
|
.new_iter = cap.new_iter,
|
||||||
|
.item = IfHeader{
|
||||||
|
.condition = condition.item,
|
||||||
|
.capture = cap.item[0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else @compileError("Only one capture allowed for if statements");
|
||||||
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.new_iter = iter,
|
.new_iter = iter,
|
||||||
.item = .{
|
.item = .{
|
||||||
.condition = condition.item,
|
.condition = condition.item,
|
||||||
|
.capture = null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -582,7 +659,7 @@ fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, [
|
||||||
if (wants != .period) @compileError("Unexpected token \".\"");
|
if (wants != .period) @compileError("Unexpected token \".\"");
|
||||||
wants = .text;
|
wants = .text;
|
||||||
},
|
},
|
||||||
else => if (wants == .period) return .{
|
else => if (wants == .period or fields.len == 0) return .{
|
||||||
.new_iter = iter,
|
.new_iter = iter,
|
||||||
.item = fields,
|
.item = fields,
|
||||||
} else @compileError("Unexpected token"),
|
} else @compileError("Unexpected token"),
|
||||||
|
@ -657,6 +734,7 @@ const CallTemplate = struct {
|
||||||
|
|
||||||
const IfHeader = struct {
|
const IfHeader = struct {
|
||||||
condition: Expression,
|
condition: Expression,
|
||||||
|
capture: ?[]const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Statement = union(enum) {
|
const Statement = union(enum) {
|
||||||
|
@ -707,6 +785,8 @@ const ControlToken = union(enum) {
|
||||||
slash: void,
|
slash: void,
|
||||||
equals: void,
|
equals: void,
|
||||||
at: void,
|
at: void,
|
||||||
|
comma: void,
|
||||||
|
percent: void,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ControlTokenIter = struct {
|
const ControlTokenIter = struct {
|
||||||
|
@ -737,6 +817,8 @@ const ControlTokenIter = struct {
|
||||||
'/' => return .{ .slash = {} },
|
'/' => return .{ .slash = {} },
|
||||||
'=' => return .{ .equals = {} },
|
'=' => return .{ .equals = {} },
|
||||||
'@' => return .{ .at = {} },
|
'@' => return .{ .at = {} },
|
||||||
|
',' => return .{ .comma = {} },
|
||||||
|
'%' => return .{ .percent = {} },
|
||||||
' ', '\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) {}
|
||||||
|
|
|
@ -22,9 +22,14 @@
|
||||||
neither
|
neither
|
||||||
{=/if}
|
{=/if}
|
||||||
|
|
||||||
<template>{#template test_tmpl .bar}</template>
|
{#if .maybe_foo |$v|}{$v}{#else}null{/if}
|
||||||
|
{#if .maybe_bar |$v|}{$v}{#else}null{/if}
|
||||||
|
{#if .maybe_foo |$_|}abcd{#else}null{/if}
|
||||||
|
|
||||||
{@context_foo}
|
<template>{#template test_tmpl .bar}</template>
|
||||||
|
<template>{#template test_tmpl .}</template>
|
||||||
|
|
||||||
|
{%context_foo}
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue