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");
|
||||
|
||||
pub fn main() !void {
|
||||
const Enum = enum { foo, bar };
|
||||
const Enum = enum { foo, bar, baz };
|
||||
try execute(
|
||||
std.io.getStdOut().writer(),
|
||||
.{ .test_tmpl = "{.x} {%context_foo}" },
|
||||
|
@ -20,6 +20,11 @@ pub fn main() !void {
|
|||
.maybe_foo = @as(?[]const u8, "foo"),
|
||||
.maybe_bar = @as(?[]const u8, null),
|
||||
.snap = Enum.bar,
|
||||
.crackle = union(Enum) {
|
||||
foo: []const u8,
|
||||
bar: []const u8,
|
||||
baz: []const u8,
|
||||
}{ .foo = "abcd" },
|
||||
.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| {
|
||||
const new_template = @field(templates, call.template_name);
|
||||
try execute(
|
||||
|
@ -237,6 +280,7 @@ const TemplateType = enum {
|
|||
for_block,
|
||||
if_block,
|
||||
if_else_block,
|
||||
switch_block,
|
||||
};
|
||||
|
||||
const TemplateParseResult = struct {
|
||||
|
@ -293,6 +337,7 @@ fn parseTemplate(
|
|||
comptime template_type: TemplateType,
|
||||
) TemplateParseResult {
|
||||
comptime {
|
||||
@setEvalBranchQuota(tokens.len * 100);
|
||||
var i: usize = start;
|
||||
var current_text: []const u8 = "";
|
||||
var items: []const TemplateItem = &.{};
|
||||
|
@ -336,6 +381,41 @@ fn parseTemplate(
|
|||
}};
|
||||
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)
|
||||
break cb
|
||||
else
|
||||
|
@ -348,13 +428,17 @@ fn parseTemplate(
|
|||
break cb
|
||||
else
|
||||
@compileError("Unexpected #elif tag"),
|
||||
.@"else" => if (template_type == .if_block)
|
||||
.@"else" => if (template_type == .if_block or template_type == .switch_block)
|
||||
break cb
|
||||
else
|
||||
@compileError("Unexpected #else tag"),
|
||||
.call_template => |call| items = items ++ [_]TemplateItem{.{
|
||||
.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;
|
||||
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"),
|
||||
}
|
||||
|
@ -588,6 +682,7 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken
|
|||
switch (keyword) {
|
||||
.@"for" => break .{ .end_for = {} },
|
||||
.@"if" => break .{ .end_if = {} },
|
||||
.@"switch" => break .{ .end_switch = {} },
|
||||
}
|
||||
},
|
||||
.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) {
|
||||
comptime {
|
||||
var iter = tokens;
|
||||
|
@ -755,7 +901,7 @@ fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, [
|
|||
switch (token) {
|
||||
.whitespace => {},
|
||||
.text => |text| {
|
||||
if (wants != .text) @compileError("Unexpected token \"" ++ text ++ "\"");
|
||||
if (wants == .period) break;
|
||||
fields = fields ++ [1][]const u8{text};
|
||||
wants = .period;
|
||||
},
|
||||
|
@ -763,13 +909,15 @@ fn parseDeref(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, [
|
|||
if (wants != .period) @compileError("Unexpected token \".\"");
|
||||
wants = .text;
|
||||
},
|
||||
else => if (wants == .period or fields.len == 0) return .{
|
||||
.new_iter = iter,
|
||||
.item = fields,
|
||||
} else @compileError("Unexpected token"),
|
||||
else => if (wants == .period or fields.len == 0) break else @compileError("Unexpected token"),
|
||||
}
|
||||
_ = 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 {
|
||||
template_name: []const u8,
|
||||
args: Expression,
|
||||
|
@ -852,6 +1021,7 @@ const Statement = union(enum) {
|
|||
expression: Expression,
|
||||
@"for": For,
|
||||
@"if": If,
|
||||
@"switch": Switch,
|
||||
call_template: CallTemplate,
|
||||
};
|
||||
|
||||
|
@ -865,6 +1035,9 @@ const ControlBlock = struct {
|
|||
@"else": void,
|
||||
elif_header: IfHeader,
|
||||
call_template: CallTemplate,
|
||||
switch_header: SwitchHeader,
|
||||
case_header: CaseHeader,
|
||||
end_switch: void,
|
||||
};
|
||||
block: Data,
|
||||
strip_before: bool,
|
||||
|
@ -877,11 +1050,14 @@ const Keyword = enum {
|
|||
@"else",
|
||||
@"elif",
|
||||
@"template",
|
||||
@"switch",
|
||||
@"case",
|
||||
};
|
||||
|
||||
const EndKeyword = enum {
|
||||
@"for",
|
||||
@"if",
|
||||
@"switch",
|
||||
};
|
||||
|
||||
const Builtin = enum {
|
||||
|
|
|
@ -26,6 +26,22 @@
|
|||
neither
|
||||
{=/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_bar |$v|}{$v}{#else}null{/if}
|
||||
{#if .maybe_foo |$_|}abcd{#else}null{/if}
|
||||
|
|
Loading…
Reference in a new issue