diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
new file mode 100644
index 0000000..b029fa4
--- /dev/null
+++ b/.github/workflows/gh-pages.yml
@@ -0,0 +1,18 @@
+name: CI
+on: push
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Change if you need git info
+
+ - name: Setup Zig
+ uses: mlugg/setup-zig@v1
+ with:
+ version: 0.13.0
+
+ - name: Test
+ run: zig build test
+
diff --git a/README.md b/README.md
index 8f5f0de..6e1e91c 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ General Options:
### Diagnostics
-
+![](.github/vscode.png)
This language server is stricter than the HTML spec whenever it would prevent potential human errors from being reported.
@@ -43,7 +43,7 @@ This will still be reported as an error by SuperHTML because otherwise the follo
```
### Autoformatting
-
+![](.github/vscode-autoformat.gif")
The autoformatter has two main ways of interacting with it in order to request for horizontal / vertical alignment.
diff --git a/build.zig b/build.zig
index 4c7270a..3a217c6 100644
--- a/build.zig
+++ b/build.zig
@@ -136,10 +136,10 @@ pub fn build(b: *std.Build) !void {
wasm.dependOn(&target_output.step);
const afl_fuzz_name = b.fmt("superfuzz-afl{s}", .{target.result.exeFileExt()});
- const afl_fuzz = b.addObject(.{
+ const afl_fuzz = b.addStaticLibrary(.{
.name = afl_fuzz_name,
.root_source_file = b.path("src/fuzz/afl.zig"),
- // .target = b.resolveTargetQuery(.{ .cpu_model = .baseline }),
+ // .target = b.resolveTargetQuery(.{ .ofmt = .c }),
.target = target,
.optimize = .Debug,
.single_threaded = true,
@@ -158,7 +158,7 @@ pub fn build(b: *std.Build) !void {
&.{},
) catch "afl-clang-fast";
- const fuzz = b.step("fuzz", "Generate an executable for AFL++ (persistent mode)");
+ const fuzz = b.step("fuzz", "Generate an executable for AFL++ (persistent mode) plus extra tooling");
const run_afl_clang_fast = b.addSystemCommand(&.{
afl_clang_fast_path,
"-o",
@@ -166,6 +166,8 @@ pub fn build(b: *std.Build) !void {
const prog_exe = run_afl_clang_fast.addOutputFileArg(afl_fuzz_name);
run_afl_clang_fast.addFileArg(b.path("src/fuzz/afl.c"));
+ // run_afl_clang_fast.addFileArg(afl_fuzz.getEmittedBin());
+ // run_afl_clang_fast.addArg("-I/Users/kristoff/zig/0.13.0/files/lib/");
run_afl_clang_fast.addFileArg(afl_fuzz.getEmittedLlvmBc());
fuzz.dependOn(&b.addInstallBinFile(prog_exe, afl_fuzz_name).step);
diff --git a/src/Ast.zig b/src/Ast.zig
index 2ddd421..8e4be95 100644
--- a/src/Ast.zig
+++ b/src/Ast.zig
@@ -26,6 +26,7 @@ const Error = struct {
loop_no_value,
block_cannot_be_inlined,
block_missing_id,
+ duplicate_id_value,
already_branching,
id_under_loop,
extend_without_template_attr,
@@ -518,8 +519,10 @@ pub fn init(
try p.nodes.append(gpa, .{ .kind = .root, .elem_idx = 0, .depth = 0 });
- var cur = p.html.cursor(0);
+ var seen_ids: std.StringHashMapUnmanaged(Span) = .{};
+ defer seen_ids.deinit(gpa);
+ var cur = p.html.cursor(0);
var node_idx: u32 = 0;
var low_mark: u32 = 1;
var seen_non_comment_elems = false;
@@ -556,6 +559,7 @@ pub fn init(
html_node_idx,
depth,
seen_non_comment_elems,
+ &seen_ids,
) orelse continue;
const new_node_idx: u32 = @intCast(p.nodes.items.len - 1);
@@ -700,7 +704,7 @@ const Parser = struct {
elem_idx: u32,
depth: u32,
seen_non_comment_elems: bool,
- // id_map: std.StringHashMapUnmanaged(Span),
+ seen_ids: *std.StringHashMapUnmanaged(Span),
) !?*Node {
const elem = p.html.nodes[elem_idx];
@@ -979,22 +983,14 @@ const Parser = struct {
}
} else {
// we can only statically analyze non-scripted ids
+ const gop = try seen_ids.getOrPut(gpa, maybe_code);
- // TODO: implement this in a way that can account for branching
-
- // const id_str = value.unquote(self.html);
- // const gop = id_map.getOrPut(gpa, id_str) catch oom();
- // if (gop.found_existing) {
- // return errors.report(
- // template_name,
- // template_path,
- // attr.node,
- // self.html,
- // "DUPLICATE ID",
- // \\TODO: explain
- // ,
- // );
- // }
+ if (gop.found_existing) {
+ try p.errors.append(gpa, .{
+ .kind = .duplicate_id_value,
+ .main_location = value.span,
+ });
+ }
}
tmp_result.id_template_parentid = attr;
@@ -1668,10 +1664,17 @@ const Parser = struct {
}
// nodes under a loop can't have ids
+ log.debug("before", .{});
if (node.kind.hasId()) {
+ log.debug("here", .{});
var maybe_parent = p.parent(node);
while (maybe_parent) |par| : (maybe_parent = p.parent(par)) switch (par.kind.branching()) {
- else => continue,
+ else => {
+ log.debug("testing '{s}'", .{
+ par.elem(p.html).open.slice(p.src),
+ });
+ continue;
+ },
.loop, .inloop => {
try p.errors.append(gpa, .{
.kind = .id_under_loop,
diff --git a/src/fuzz/afl.zig b/src/fuzz/afl.zig
index 66bbde0..e4885fe 100644
--- a/src/fuzz/afl.zig
+++ b/src/fuzz/afl.zig
@@ -47,7 +47,7 @@ export fn zig_fuzz_test_astgen(buf: [*]u8, len: isize) void {
const gpa = gpa_impl.allocator();
const astgen_src = buf[0..@intCast(len)];
- const clamp: u32 = @min(2, astgen_src.len);
+ const clamp: u32 = @min(20, astgen_src.len);
const src = astgen.build(gpa, astgen_src[0..clamp]) catch unreachable;
defer gpa.free(src);
diff --git a/src/html/Tokenizer.zig b/src/html/Tokenizer.zig
index 4beb635..30e3f4e 100644
--- a/src/html/Tokenizer.zig
+++ b/src/html/Tokenizer.zig
@@ -108,9 +108,10 @@ pub const Attr = struct {
pub fn span(attr: Attr) Span {
if (attr.value) |v| {
+ const quote: u32 = if (v.quote == .none) 0 else 1;
return .{
.start = attr.name.start,
- .end = v.span.end,
+ .end = v.span.end + quote,
};
}
diff --git a/src/root.zig b/src/root.zig
index 5f48942..5041ff7 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -1,9 +1,32 @@
-const interpreter = @import("interpreter.zig");
+const vm = @import("vm.zig");
const std = @import("std");
pub const html = @import("html.zig");
pub const Ast = @import("Ast.zig");
-pub const SuperVM = interpreter.SuperVM;
-pub const Exception = interpreter.Exception;
+pub const VM = vm.VM;
+pub const Exception = vm.Exception;
+
+pub const utils = struct {
+ pub const ResourceDescriptor = union(enum) { @"if": u32, loop: u32 };
+ pub fn loopUpFunction(comptime Value: type) fn (
+ Value.IterElement,
+ std.mem.Allocator,
+ []const Value,
+ *ResourceDescriptor,
+ ) error{ OutOfMemory, WantResource }!Value {
+ return struct {
+ pub fn up(
+ v: Value.IterElement,
+ _: std.mem.Allocator,
+ args: []const Value,
+ ext: *ResourceDescriptor,
+ ) error{ OutOfMemory, WantResource }!Value {
+ if (args.len != 0) return .{ .err = "'up' wants 0 arguments" };
+ ext.* = .{ .loop = v._up_idx };
+ return error.WantResource;
+ }
+ }.up;
+ }
+};
pub const Language = enum {
html,
diff --git a/src/template.zig b/src/template.zig
index bffc2c8..63839f3 100644
--- a/src/template.zig
+++ b/src/template.zig
@@ -9,7 +9,7 @@ const Node = Ast.Node;
const log = std.log.scoped(.supertemplate);
-pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutWriter: type) type {
+pub fn SuperTemplate(comptime ScriptyVM: type, comptime OutWriter: type) type {
return struct {
arena: std.mem.Allocator,
name: []const u8,
@@ -21,11 +21,15 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
print_end: u32,
role: Role,
- // Template-wide analysis
- eval_frame: std.ArrayListUnmanaged(EvalFrame) = .{},
+ eval_frames: std.ArrayListUnmanaged(EvalFrame) = .{},
+ // index in eval_frame to a IfCondition frame
+ top_if_idx: u32 = 0,
+ // index in eval_frame to a LoopIter frame
+ top_loop_idx: u32 = 0,
- const Self = @This();
- const ScriptyVM = scripty.ScriptyVM(Context, Value);
+ const Template = @This();
+ const Value = ScriptyVM.Value;
+ const Context = ScriptyVM.Context;
const EvalFrame = union(enum) {
if_condition: IfCondition,
loop_condition: LoopCondition,
@@ -35,6 +39,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
const LoopIter = struct {
cursor: Ast.Cursor,
loop: Value.IterElement,
+ // up_idx: u32,
};
const LoopCondition = struct {
@@ -60,20 +65,21 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
cursor: Ast.Cursor,
// eval result
if_result: ?Value,
+ up_idx: u32,
};
};
const Role = enum { layout, template };
- pub fn superBlock(tpl: Self, idx: u32) Ast.Node.Block {
+ pub fn superBlock(tpl: Template, idx: u32) Ast.Node.Block {
return tpl.ast.nodes[idx].superBlock(tpl.src, tpl.html);
}
- pub fn startTag(tpl: Self, idx: u32) Span {
+ pub fn startTag(tpl: Template, idx: u32) Span {
return tpl.ast.nodes[idx].elem(tpl.html).open;
}
- pub fn getName(tpl: Self, idx: u32) Span {
+ pub fn getName(tpl: Template, idx: u32) Span {
const span = tpl.startTag(idx);
return span.getName(tpl.src, tpl.html.language);
}
@@ -86,8 +92,8 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
html_ast: html.Ast,
ast: Ast,
role: Role,
- ) !Self {
- var t: Self = .{
+ ) !Template {
+ var t: Template = .{
.arena = arena,
.path = path,
.name = name,
@@ -97,17 +103,17 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
.role = role,
.print_end = @intCast(src.len),
};
- try t.eval_frame.append(arena, .{
+ try t.eval_frames.append(arena, .{
.default = t.ast.cursor(0),
});
return t;
}
- pub fn finalCheck(tpl: Self) void {
+ pub fn finalCheck(tpl: Template) void {
std.debug.assert(tpl.print_cursor == tpl.print_end);
}
- pub fn showBlocks(tpl: Self, err_writer: errors.ErrWriter) error{ErrIO}!void {
+ pub fn showBlocks(tpl: Template, err_writer: errors.ErrWriter) error{ErrIO}!void {
var found_first = false;
var it = tpl.ast.blocks.iterator();
while (it.next()) |kv| {
@@ -136,7 +142,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
err_writer.print("\n", .{}) catch return error.ErrIO;
}
- pub fn showInterface(tpl: Self, err_writer: errors.ErrWriter) error{ErrIO}!void {
+ pub fn showInterface(tpl: Template, err_writer: errors.ErrWriter) error{ErrIO}!void {
var found_first = false;
var it = tpl.ast.interface.iterator();
while (it.next()) |kv| {
@@ -165,7 +171,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
}
pub fn activateBlock(
- tpl: *Self,
+ tpl: *Template,
script_vm: *ScriptyVM,
script_ctx: *Context,
super_id: []const u8,
@@ -173,7 +179,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
err_writer: errors.ErrWriter,
) errors.FatalOOM!void {
std.debug.assert(tpl.ast.extends_idx != 0);
- std.debug.assert(tpl.eval_frame.items.len == 0);
+ std.debug.assert(tpl.eval_frames.items.len == 0);
const block_idx = tpl.ast.blocks.get(super_id).?;
const block = tpl.ast.nodes[block_idx];
@@ -182,7 +188,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
block_idx,
block.elem(tpl.html).open.slice(tpl.src),
});
- try tpl.eval_frame.append(tpl.arena, .{
+ try tpl.eval_frames.append(tpl.arena, .{
.default = tpl.ast.cursor(block_idx),
});
@@ -221,7 +227,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
},
.element => {
tpl.print_cursor = elem.close.start;
- const frame = &tpl.eval_frame.items[0];
+ const frame = &tpl.eval_frames.items[0];
frame.default.skipChildrenOfCurrentNode();
},
}
@@ -259,31 +265,39 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
pub const Continuation = union(enum) {
// A was found, contains relative id
super_idx: u32,
+ // Done executing the template.
end,
};
pub fn eval(
- tpl: *Self,
+ tpl: *Template,
script_vm: *ScriptyVM,
script_ctx: *Context,
writer: OutWriter,
err_writer: errors.ErrWriter,
) errors.FatalShowOOM!Continuation {
- std.debug.assert(tpl.eval_frame.items.len > 0);
- outer: while (tpl.eval_frame.items.len > 0) {
- const current_context = &tpl.eval_frame.items[tpl.eval_frame.items.len - 1];
- switch (current_context.*) {
+ std.debug.assert(tpl.eval_frames.items.len > 0);
+ outer: while (tpl.eval_frames.items.len > 0) {
+ const cur_frame_idx = tpl.eval_frames.items.len - 1;
+ // necessary to avoid pointer invalidation afterwards
+ try tpl.eval_frames.ensureTotalCapacity(tpl.arena, 1);
+ const cur_frame = &tpl.eval_frames.items[cur_frame_idx];
+ switch (cur_frame.*) {
.default, .loop_iter, .if_condition => {},
.loop_condition => |*l| {
if (l.iter.next(tpl.arena)) |n| {
var cursor_copy = l.cursor_ptr.*;
cursor_copy.depth = 0;
- try tpl.eval_frame.append(tpl.arena, .{
+ var new = n.iter_elem;
+ new._up_idx = tpl.top_loop_idx;
+ tpl.eval_frames.appendAssumeCapacity(.{
.loop_iter = .{
.cursor = cursor_copy,
- .loop = n.iter_elem,
+ .loop = new,
+ // .up_idx = tpl.top_loop_idx,
},
});
+ tpl.top_loop_idx = @intCast(tpl.eval_frames.items.len - 1);
tpl.print_cursor = l.print_loop_body;
tpl.print_end = l.print_loop_body_end;
l.index += 1;
@@ -305,12 +319,12 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
tpl.print_cursor = l.print_loop_body_end;
tpl.print_end = l.print_end;
l.cursor_ptr.skipChildrenOfCurrentNode();
- _ = tpl.eval_frame.pop();
+ _ = tpl.eval_frames.pop();
continue;
}
},
}
- const cur = switch (current_context.*) {
+ const cur = switch (cur_frame.*) {
.default => |*d| d,
.loop_iter => |*li| &li.cursor,
.if_condition => |*ic| &ic.cursor,
@@ -361,7 +375,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
value.span,
);
- try tpl.eval_frame.append(tpl.arena, .{
+ try tpl.eval_frames.append(tpl.arena, .{
.loop_condition = .{
.inloop_idx = cur.current_idx,
.print_loop_body = tpl.print_cursor,
@@ -395,7 +409,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
value.span,
);
- try tpl.eval_frame.append(tpl.arena, .{
+ try tpl.eval_frames.append(tpl.arena, .{
.loop_condition = .{
.print_loop_body = tpl.print_cursor,
.print_loop_body_end = node.elem(tpl.html).close.start,
@@ -444,11 +458,13 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
.if_condition = .{
.cursor = cur.*,
.if_result = Value.from(tpl.arena, o),
+ .up_idx = tpl.top_if_idx,
},
};
+ tpl.top_if_idx = @intCast(tpl.eval_frames.items.len);
new_frame.if_condition.cursor.depth = 0;
- try tpl.eval_frame.append(
+ try tpl.eval_frames.append(
tpl.arena,
new_frame,
);
@@ -563,10 +579,18 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
}
}
- if (tpl.eval_frame.popOrNull()) |ctx| {
- if (ctx == .if_condition) continue;
- // finalization
- std.debug.assert(ctx != .loop_condition);
+ if (tpl.eval_frames.popOrNull()) |frame| {
+ switch (frame) {
+ .default => {},
+ .loop_iter => |li| {
+ tpl.top_loop_idx = li.loop._up_idx;
+ },
+ .if_condition => |ic| {
+ tpl.top_if_idx = ic.up_idx;
+ continue;
+ },
+ .loop_condition => unreachable,
+ }
writer.writeAll(
tpl.src[tpl.print_cursor..tpl.print_end],
) catch return error.OutIO;
@@ -579,45 +603,41 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
}
fn evalVar(
- tpl: *Self,
+ tpl: *Template,
err_writer: errors.ErrWriter,
script_vm: *ScriptyVM,
script_ctx: *Context,
script_attr_name: Span,
code_span: Span,
) errors.Fatal!Value {
- const current_eval_frame = &tpl.eval_frame.items[tpl.eval_frame.items.len - 1];
- const loop = switch (current_eval_frame.*) {
- .loop_iter => |li| li.loop,
- else => null,
- };
+ tpl.setContext(script_ctx);
- const old_loop = script_ctx.loop;
- script_ctx.loop = if (loop) |l| .{ .iterator_element = l } else null;
- defer script_ctx.loop = old_loop;
- // if (loop) |l| switch (l.it) {
- // .string => std.debug.print("loop it.string = `{s}`\n", .{l.it.string}),
- // };
-
- // if
- const if_value = switch (current_eval_frame.*) {
- .if_condition => |ic| ic.if_result,
- else => null,
- };
- const old_if = script_ctx.@"if";
- script_ctx.@"if" = if_value;
- defer script_ctx.@"if" = old_if;
-
- // const diag: script.Interpreter.Diagnostics = .{};
- const result = script_vm.run(
+ const result = while (true) break script_vm.run(
tpl.arena,
script_ctx,
code_span.slice(tpl.src),
.{},
- ) catch |err| {
- std.debug.panic("TODO: handle scripty vm error: {s}", .{
- @errorName(err),
- });
+ ) catch |err| switch (err) {
+ error.WantResource => {
+ const ext = script_vm.getResourceDescriptor();
+ switch (ext) {
+ else => @panic("TODO"),
+ .loop => |frame_idx| {
+ if (frame_idx == 0) {
+ script_vm.insertResource(.{
+ .err = "already at the topmost $loop value",
+ });
+ } else {
+ const iter = tpl.eval_frames.items[frame_idx].loop_iter;
+ script_vm.insertResource(.{
+ .iterator_element = iter.loop,
+ });
+ }
+ },
+ }
+ continue;
+ },
+ else => return error.Fatal,
};
switch (result.value) {
@@ -653,34 +673,12 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
return result.value;
}
fn evalAttr(
- tpl: *Self,
+ tpl: *Template,
script_vm: *ScriptyVM,
script_ctx: *Context,
code_span: Span,
) errors.Fatal!ScriptyVM.Result {
- const current_eval_frame = &tpl.eval_frame.items[tpl.eval_frame.items.len - 1];
- const loop = switch (current_eval_frame.*) {
- .loop_iter => |li| li.loop,
- else => null,
- };
-
- const old_loop = script_ctx.loop;
- script_ctx.loop = if (loop) |l| .{ .iterator_element = l } else null;
- defer script_ctx.loop = old_loop;
- // if (loop) |l| switch (l.it) {
- // .string => std.debug.print("loop it.string = `{s}`\n", .{l.it.string}),
- // };
-
- // if
- const if_value = switch (current_eval_frame.*) {
- .if_condition => |ic| ic.if_result,
- else => null,
- };
- const old_if = script_ctx.@"if";
- script_ctx.@"if" = if_value;
- defer script_ctx.@"if" = old_if;
-
- // const diag: script.Interpreter.Diagnostics = .{};
+ tpl.setContext(script_ctx);
const result = script_vm.run(
tpl.arena,
script_ctx,
@@ -696,34 +694,15 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
}
fn evalIf(
- tpl: *Self,
+ tpl: *Template,
err_writer: errors.ErrWriter,
script_vm: *ScriptyVM,
script_ctx: *Context,
script_attr_name: Span,
code_span: Span,
) errors.Fatal!Value {
- const current_eval_frame = &tpl.eval_frame.items[tpl.eval_frame.items.len - 1];
- // loop
- const loop = switch (current_eval_frame.*) {
- .loop_iter => |li| li.loop,
- else => null,
- };
-
- const old_loop = script_ctx.loop;
- script_ctx.loop = if (loop) |l| .{ .iterator_element = l } else null;
- defer script_ctx.loop = old_loop;
- // if
- const if_value = switch (current_eval_frame.*) {
- .if_condition => |ic| ic.if_result,
- else => null,
- };
+ tpl.setContext(script_ctx);
- const old_if = script_ctx.@"if";
- script_ctx.@"if" = if_value;
- defer script_ctx.@"if" = old_if;
- // std.debug.print("({s}) evalIf if: {any}\n", .{ self.name, script_ctx });
- // const diag: script.Interpreter.Diagnostics = .{};
const result = script_vm.run(
tpl.arena,
script_ctx,
@@ -769,15 +748,15 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
}
fn evalLoop(
- tpl: *Self,
+ tpl: *Template,
err_writer: errors.ErrWriter,
script_vm: *ScriptyVM,
script_ctx: *Context,
script_attr_name: Span,
code_span: Span,
) errors.Fatal!Value.Iterator {
+ tpl.setContext(script_ctx);
- // const diag: script.Interpreter.Diagnostics = .{};
const result = script_vm.run(
tpl.arena,
script_ctx,
@@ -821,7 +800,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
}
pub fn reportError(
- self: Self,
+ self: Template,
err_writer: errors.ErrWriter,
bad_node: Span,
error_code: []const u8,
@@ -841,7 +820,7 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
}
pub fn diagnostic(
- tpl: Self,
+ tpl: Template,
err_writer: errors.ErrWriter,
bracket: bool,
note_line: []const u8,
@@ -857,6 +836,39 @@ pub fn SuperTemplate(comptime Context: type, comptime Value: type, comptime OutW
tpl.src,
);
}
+
+ pub fn setContext(tpl: Template, script_ctx: *Context) void {
+ script_ctx.loop = if (tpl.top_loop_idx == 0) null else blk: {
+ const iter = tpl.eval_frames.items[tpl.top_loop_idx].loop_iter;
+ break :blk .{
+ .iterator_element = iter.loop,
+ };
+ };
+ script_ctx.@"if" = if (tpl.top_if_idx == 0) null else blk: {
+ const cond = tpl.eval_frames.items[tpl.top_if_idx].if_condition;
+ break :blk cond.if_result;
+ };
+
+ // const current_eval_frame = &tpl.eval_frames.items[tpl.eval_frames.items.len - 1];
+ // // loop
+ // const loop = switch (current_eval_frame.*) {
+ // .loop_iter => |li| li.loop,
+ // else => null,
+ // };
+
+ // const old_loop = script_ctx.loop;
+ // script_ctx.loop = if (loop) |l| .{ .iterator_element = l } else null;
+ // defer script_ctx.loop = old_loop;
+ // // if
+ // const if_value = switch (current_eval_frame.*) {
+ // .if_condition => |ic| ic.if_result,
+ // else => null,
+ // };
+
+ // const old_if = script_ctx.@"if";
+ // script_ctx.@"if" = if_value;
+ // defer script_ctx.@"if" = old_if;
+ }
};
}
diff --git a/src/interpreter.zig b/src/vm.zig
similarity index 93%
rename from src/interpreter.zig
rename to src/vm.zig
index ece67bc..a23d83e 100644
--- a/src/interpreter.zig
+++ b/src/vm.zig
@@ -3,12 +3,13 @@ const scripty = @import("scripty");
const errors = @import("errors.zig");
const template = @import("template.zig");
-const Span = @import("root.zig").Span;
+const root = @import("root.zig");
+const utils = root.utils;
+const Span = root.Span;
const html = @import("html.zig");
const Ast = @import("Ast.zig");
const Node = Ast.Node;
const SuperTemplate = template.SuperTemplate;
-const ScriptyVM = scripty.ScriptyVM;
const log = std.log.scoped(.supervm);
@@ -25,7 +26,7 @@ pub const Exception = error{
OutIO,
};
-pub fn SuperVM(comptime Context: type, comptime Value: type) type {
+pub fn VM(comptime Context: type, comptime Value: type) type {
return struct {
arena: std.mem.Allocator,
content_name: []const u8,
@@ -36,7 +37,7 @@ pub fn SuperVM(comptime Context: type, comptime Value: type) type {
quota: usize = 100,
templates: std.ArrayListUnmanaged(Template) = .{},
ctx: *Context,
- scripty_vm: ScriptyVM(Context, Value) = .{},
+ scripty_vm: ScriptyVM = .{},
// discovering templates state
seen_templates: std.StringHashMapUnmanaged(struct {
@@ -44,11 +45,12 @@ pub fn SuperVM(comptime Context: type, comptime Value: type) type {
idx: usize,
}) = .{},
+ const ScriptyVM = scripty.VM(Context, Value, utils.ResourceDescriptor);
const OutWriter = std.io.BufferedWriter(4096, std.fs.File.Writer).Writer;
const ErrWriter = errors.ErrWriter;
const Self = @This();
- pub const Template = SuperTemplate(Context, Value, OutWriter);
+ pub const Template = SuperTemplate(ScriptyVM, OutWriter);
pub const State = union(enum) {
init: TemplateCartridge,
discovering_templates,
@@ -130,7 +132,7 @@ pub fn SuperVM(comptime Context: type, comptime Value: type) type {
// Call this function to report an evaluation trace when the caller
// failed to fetch a requested resource (eg templates, snippets, ...)
- pub fn resourceFetchError(vm: *Self, error_code: []const u8) void {
+ pub fn reportResourceFetchError(vm: *Self, error_code: []const u8) void {
std.debug.assert(vm.state == .want_template);
const wanted = vm.state.want_template;
const t = vm.templates.items[vm.templates.items.len - 1];
@@ -150,8 +152,6 @@ pub fn SuperVM(comptime Context: type, comptime Value: type) type {
pub fn run(vm: *Self) Exception!void {
if (vm.state == .fatal) return error.Fatal;
- // This is where we catch unhandled errors and move the machine
- // state to .fatal
vm.runInternal() catch |err| {
switch (err) {
error.OutOfMemory,
@@ -183,25 +183,38 @@ pub fn SuperVM(comptime Context: type, comptime Value: type) type {
while (vm.quota > 0) : (vm.quota -= 1) {
const t = &vm.templates.items[idx];
- const continuation = t.eval(&vm.scripty_vm, vm.ctx, vm.out, vm.err) catch |err| switch (err) {
+ const continuation = t.eval(
+ &vm.scripty_vm,
+ vm.ctx,
+ vm.out,
+ vm.err,
+ ) catch |err| switch (err) {
error.OutOfMemory,
error.OutIO,
error.ErrIO,
- => |e| return e,
- error.Fatal,
- error.FatalShowInterface,
- => {
- if (err == error.FatalShowInterface) {
- try vm.templates.items[idx + 1].showInterface(vm.err);
- }
- return fatalTrace(vm.content_name, vm.templates.items[0 .. idx + 1], vm.err);
+ => |e| {
+ return e;
+ },
+ error.Fatal => {
+ return fatalTrace(
+ vm.content_name,
+ vm.templates.items[0 .. idx + 1],
+ vm.err,
+ );
+ },
+ error.FatalShowInterface => {
+ try vm.templates.items[idx + 1].showInterface(vm.err);
+ return fatalTrace(
+ vm.content_name,
+ vm.templates.items[0 .. idx + 1],
+ vm.err,
+ );
},
};
switch (continuation) {
.super_idx => |s| {
- //programming error:
- //layout acting like it has in it
+ // loaded layouts have no super tags in them
std.debug.assert(idx != 0);
idx -= 1;
@@ -275,7 +288,7 @@ pub fn SuperVM(comptime Context: type, comptime Value: type) type {
const current = &vm.templates.items[current_idx];
const ext = ¤t.ast.nodes[current.ast.extends_idx];
- _ = current.eval_frame.pop();
+ _ = current.eval_frames.pop();
const template_value = ext.templateValue();
//TODO: unescape
const template_name = template_value.span.slice(current.src);