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);