Add switch statements
This commit is contained in:
parent
b2007131c8
commit
ad513d70e2
2 changed files with 199 additions and 7 deletions
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const Enum = enum { foo, bar };
|
const Enum = enum { foo, bar, baz };
|
||||||
try execute(
|
try execute(
|
||||||
std.io.getStdOut().writer(),
|
std.io.getStdOut().writer(),
|
||||||
.{ .test_tmpl = "{.x} {%context_foo}" },
|
.{ .test_tmpl = "{.x} {%context_foo}" },
|
||||||
|
@ -20,6 +20,11 @@ pub fn main() !void {
|
||||||
.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,
|
.snap = Enum.bar,
|
||||||
|
.crackle = union(Enum) {
|
||||||
|
foo: []const u8,
|
||||||
|
bar: []const u8,
|
||||||
|
baz: []const u8,
|
||||||
|
}{ .foo = "abcd" },
|
||||||
.x = "y",
|
.x = "y",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
|
@ -122,6 +127,44 @@ fn executeStatement(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.@"switch" => |switch_stmt| {
|
||||||
|
const expr = evaluateExpression(switch_stmt.expression, args, captures, context);
|
||||||
|
|
||||||
|
var found = false;
|
||||||
|
inline for (switch_stmt.cases) |case| {
|
||||||
|
if (std.meta.isTag(expr, case.header.tag)) {
|
||||||
|
found = true;
|
||||||
|
if (case.header.capture) |capture| {
|
||||||
|
try executeTemplate(
|
||||||
|
writer,
|
||||||
|
templates,
|
||||||
|
case.subtemplate,
|
||||||
|
args,
|
||||||
|
addCapture(captures, capture, @field(expr, case.header.tag)),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try executeTemplate(
|
||||||
|
writer,
|
||||||
|
templates,
|
||||||
|
case.subtemplate,
|
||||||
|
args,
|
||||||
|
captures,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!found) if (switch_stmt.else_branch) |subtemplate| {
|
||||||
|
try executeTemplate(
|
||||||
|
writer,
|
||||||
|
templates,
|
||||||
|
subtemplate,
|
||||||
|
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(
|
try execute(
|
||||||
|
@ -237,6 +280,7 @@ const TemplateType = enum {
|
||||||
for_block,
|
for_block,
|
||||||
if_block,
|
if_block,
|
||||||
if_else_block,
|
if_else_block,
|
||||||
|
switch_block,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TemplateParseResult = struct {
|
const TemplateParseResult = struct {
|
||||||
|
@ -293,6 +337,7 @@ fn parseTemplate(
|
||||||
comptime template_type: TemplateType,
|
comptime template_type: TemplateType,
|
||||||
) TemplateParseResult {
|
) TemplateParseResult {
|
||||||
comptime {
|
comptime {
|
||||||
|
@setEvalBranchQuota(tokens.len * 100);
|
||||||
var i: usize = start;
|
var i: usize = start;
|
||||||
var current_text: []const u8 = "";
|
var current_text: []const u8 = "";
|
||||||
var items: []const TemplateItem = &.{};
|
var items: []const TemplateItem = &.{};
|
||||||
|
@ -336,6 +381,41 @@ fn parseTemplate(
|
||||||
}};
|
}};
|
||||||
i = subtemplate.new_idx;
|
i = subtemplate.new_idx;
|
||||||
},
|
},
|
||||||
|
.switch_header => |header| {
|
||||||
|
var cases: []const Case = &.{};
|
||||||
|
var else_branch: ?[]const TemplateItem = null;
|
||||||
|
var last_header: CaseHeader = header.first_case;
|
||||||
|
var is_else = false;
|
||||||
|
while (true) {
|
||||||
|
const case = parseTemplate(tokens, i + 1, .switch_block);
|
||||||
|
i = case.new_idx;
|
||||||
|
|
||||||
|
if (!is_else) {
|
||||||
|
cases = cases ++ [_]Case{.{
|
||||||
|
.header = last_header,
|
||||||
|
.subtemplate = case.items,
|
||||||
|
}};
|
||||||
|
} else {
|
||||||
|
else_branch = case.items;
|
||||||
|
}
|
||||||
|
switch (case.closing_block.?.block) {
|
||||||
|
.end_switch => break,
|
||||||
|
.@"else" => is_else = true,
|
||||||
|
.case_header => |case_header| last_header = case_header,
|
||||||
|
else => @compileError("Unexpected token"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items = items ++ [_]TemplateItem{.{
|
||||||
|
.statement = .{
|
||||||
|
.@"switch" = .{
|
||||||
|
.expression = header.expression,
|
||||||
|
.cases = cases,
|
||||||
|
.else_branch = else_branch,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
},
|
||||||
.end_for => if (template_type == .for_block)
|
.end_for => if (template_type == .for_block)
|
||||||
break cb
|
break cb
|
||||||
else
|
else
|
||||||
|
@ -348,13 +428,17 @@ fn parseTemplate(
|
||||||
break cb
|
break cb
|
||||||
else
|
else
|
||||||
@compileError("Unexpected #elif tag"),
|
@compileError("Unexpected #elif tag"),
|
||||||
.@"else" => if (template_type == .if_block)
|
.@"else" => if (template_type == .if_block or template_type == .switch_block)
|
||||||
break cb
|
break cb
|
||||||
else
|
else
|
||||||
@compileError("Unexpected #else tag"),
|
@compileError("Unexpected #else tag"),
|
||||||
.call_template => |call| items = items ++ [_]TemplateItem{.{
|
.call_template => |call| items = items ++ [_]TemplateItem{.{
|
||||||
.statement = .{ .call_template = call },
|
.statement = .{ .call_template = call },
|
||||||
}},
|
}},
|
||||||
|
.end_switch, .case_header => if (template_type == .switch_block)
|
||||||
|
break cb
|
||||||
|
else
|
||||||
|
@compileError("Unexpected /switch tag"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -575,6 +659,16 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken
|
||||||
iter = result.new_iter;
|
iter = result.new_iter;
|
||||||
break .{ .call_template = result.item };
|
break .{ .call_template = result.item };
|
||||||
},
|
},
|
||||||
|
.@"switch" => {
|
||||||
|
const result = parseSwitchHeader(iter);
|
||||||
|
iter = result.new_iter;
|
||||||
|
break .{ .switch_header = result.item };
|
||||||
|
},
|
||||||
|
.case => {
|
||||||
|
const result = parseCaseHeader(iter);
|
||||||
|
iter = result.new_iter;
|
||||||
|
break .{ .case_header = result.item };
|
||||||
|
},
|
||||||
|
|
||||||
//else => @compileError("TODO"),
|
//else => @compileError("TODO"),
|
||||||
}
|
}
|
||||||
|
@ -588,6 +682,7 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken
|
||||||
switch (keyword) {
|
switch (keyword) {
|
||||||
.@"for" => break .{ .end_for = {} },
|
.@"for" => break .{ .end_for = {} },
|
||||||
.@"if" => break .{ .end_if = {} },
|
.@"if" => break .{ .end_if = {} },
|
||||||
|
.@"switch" => break .{ .end_switch = {} },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.period, .dollar, .percent => {
|
.period, .dollar, .percent => {
|
||||||
|
@ -746,6 +841,57 @@ fn parseIfHeader(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parseCaseHeader(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, CaseHeader) {
|
||||||
|
comptime {
|
||||||
|
var iter = skipWhitespace(tokens);
|
||||||
|
const tag = iter.next();
|
||||||
|
expectToken(tag, .text);
|
||||||
|
|
||||||
|
const captures = tryParseCapture(iter);
|
||||||
|
if (captures) |cap| {
|
||||||
|
if (cap.item.len == 1) {
|
||||||
|
return .{
|
||||||
|
.new_iter = cap.new_iter,
|
||||||
|
.item = CaseHeader{
|
||||||
|
.tag = tag.?.text,
|
||||||
|
.capture = cap.item[0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else @compileError("Only one capture allowed for case statements");
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.new_iter = iter,
|
||||||
|
.item = .{
|
||||||
|
.tag = tag.?.text,
|
||||||
|
.capture = null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parseSwitchHeader(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, SwitchHeader) {
|
||||||
|
comptime {
|
||||||
|
const condition = parseExpression(tokens);
|
||||||
|
var iter = skipWhitespace(condition.new_iter);
|
||||||
|
|
||||||
|
const next = iter.next();
|
||||||
|
expectToken(next, .text);
|
||||||
|
if (!std.mem.eql(u8, next.?.text, "case")) @compileError("Expected case following switch condition");
|
||||||
|
|
||||||
|
iter = skipWhitespace(iter);
|
||||||
|
const first = parseCaseHeader(iter);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.new_iter = first.new_iter,
|
||||||
|
.item = .{
|
||||||
|
.expression = condition.item,
|
||||||
|
.first_case = first.item,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, []const []const u8) {
|
fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, []const []const u8) {
|
||||||
comptime {
|
comptime {
|
||||||
var iter = tokens;
|
var iter = tokens;
|
||||||
|
@ -755,7 +901,7 @@ fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, [
|
||||||
switch (token) {
|
switch (token) {
|
||||||
.whitespace => {},
|
.whitespace => {},
|
||||||
.text => |text| {
|
.text => |text| {
|
||||||
if (wants != .text) @compileError("Unexpected token \"" ++ text ++ "\"");
|
if (wants == .period) break;
|
||||||
fields = fields ++ [1][]const u8{text};
|
fields = fields ++ [1][]const u8{text};
|
||||||
wants = .period;
|
wants = .period;
|
||||||
},
|
},
|
||||||
|
@ -763,13 +909,15 @@ 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 or fields.len == 0) return .{
|
else => if (wants == .period or fields.len == 0) break else @compileError("Unexpected token"),
|
||||||
.new_iter = iter,
|
|
||||||
.item = fields,
|
|
||||||
} else @compileError("Unexpected token"),
|
|
||||||
}
|
}
|
||||||
_ = iter.next();
|
_ = iter.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.new_iter = iter,
|
||||||
|
.item = fields,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,6 +986,27 @@ const If = struct {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Case = struct {
|
||||||
|
header: CaseHeader,
|
||||||
|
subtemplate: []const TemplateItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SwitchHeader = struct {
|
||||||
|
expression: Expression,
|
||||||
|
first_case: CaseHeader,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CaseHeader = struct {
|
||||||
|
tag: []const u8,
|
||||||
|
capture: ?[]const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Switch = struct {
|
||||||
|
expression: Expression,
|
||||||
|
cases: []const Case,
|
||||||
|
else_branch: ?[]const TemplateItem,
|
||||||
|
};
|
||||||
|
|
||||||
const CallTemplate = struct {
|
const CallTemplate = struct {
|
||||||
template_name: []const u8,
|
template_name: []const u8,
|
||||||
args: Expression,
|
args: Expression,
|
||||||
|
@ -852,6 +1021,7 @@ const Statement = union(enum) {
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
@"for": For,
|
@"for": For,
|
||||||
@"if": If,
|
@"if": If,
|
||||||
|
@"switch": Switch,
|
||||||
call_template: CallTemplate,
|
call_template: CallTemplate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -865,6 +1035,9 @@ const ControlBlock = struct {
|
||||||
@"else": void,
|
@"else": void,
|
||||||
elif_header: IfHeader,
|
elif_header: IfHeader,
|
||||||
call_template: CallTemplate,
|
call_template: CallTemplate,
|
||||||
|
switch_header: SwitchHeader,
|
||||||
|
case_header: CaseHeader,
|
||||||
|
end_switch: void,
|
||||||
};
|
};
|
||||||
block: Data,
|
block: Data,
|
||||||
strip_before: bool,
|
strip_before: bool,
|
||||||
|
@ -877,11 +1050,14 @@ const Keyword = enum {
|
||||||
@"else",
|
@"else",
|
||||||
@"elif",
|
@"elif",
|
||||||
@"template",
|
@"template",
|
||||||
|
@"switch",
|
||||||
|
@"case",
|
||||||
};
|
};
|
||||||
|
|
||||||
const EndKeyword = enum {
|
const EndKeyword = enum {
|
||||||
@"for",
|
@"for",
|
||||||
@"if",
|
@"if",
|
||||||
|
@"switch",
|
||||||
};
|
};
|
||||||
|
|
||||||
const Builtin = enum {
|
const Builtin = enum {
|
||||||
|
|
|
@ -26,6 +26,22 @@
|
||||||
neither
|
neither
|
||||||
{=/if}
|
{=/if}
|
||||||
|
|
||||||
|
{#switch .snap case foo =}
|
||||||
|
foo
|
||||||
|
{= #case bar =}
|
||||||
|
bar
|
||||||
|
{= #else =}
|
||||||
|
other
|
||||||
|
{= /switch}
|
||||||
|
|
||||||
|
crackle: {#switch .crackle case foo |$foo|=}
|
||||||
|
foo:{$foo}
|
||||||
|
{= #case bar |$bar|=}
|
||||||
|
bar:{$bar}
|
||||||
|
{= #else =}
|
||||||
|
other
|
||||||
|
{= /switch}
|
||||||
|
|
||||||
{#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}
|
||||||
|
|
Loading…
Reference in a new issue