tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

io_v2_core_probe.zig (5456B)


      1 //! Test-only executable: compiled source against the private core, no native IO
      2 //! facilities. This is not the tricu-zig CLI or a compiler execution backend.
      3 const std = @import("std");
      4 const Arena = @import("arena.zig").Arena;
      5 const P = @import("io_v2_protocol.zig");
      6 const C = @import("codecs.zig");
      7 const Core = @import("io_v2_core.zig");
      8 const bundle = @import("bundle.zig");
      9 const reduce = @import("reduce.zig");
     10 pub fn main(init: std.process.Init) !void {
     11     return mainFor(Core.UnsupportedHost, init);
     12 }
     13 pub fn mainFor(comptime Host: type, init: std.process.Init) !void {
     14     const args = try init.minimal.args.toSlice(init.arena.allocator());
     15     if (args.len < 3) return error.ExpectedBundleAndExport;
     16     const bytes = try std.Io.Dir.cwd().readFileAlloc(init.io, args[1], init.gpa, .limited(10 * 1024 * 1024));
     17     defer init.gpa.free(bytes);
     18     var arena = Arena.init(init.gpa);
     19     defer arena.deinit();
     20     var root = try bundle.loadBundleExport(&arena, bytes, args[2]);
     21     for (args[3..]) |argument| {
     22         const value = if (std.mem.startsWith(u8, argument, "tree-file:")) blk: {
     23             const raw = try std.Io.Dir.cwd().readFileAlloc(init.io, argument[10..], init.gpa, .limited(10 * 1024 * 1024));
     24             defer init.gpa.free(raw);
     25             break :blk try parseRaw(&arena, raw);
     26         } else if (std.mem.startsWith(u8, argument, "text:")) try @import("source_text.zig").fromUtf8(&arena, argument[5..]) else try C.ofNumber(&arena, try std.fmt.parseUnsigned(u64, argument, 10));
     27         root = try arena.allocApp(root, value);
     28     }
     29     try arena.roots.append(init.gpa, root);
     30     arena.gc_enabled = true;
     31     try arena.setGcPolicy(128, 1);
     32     const r: P.Reader = .{ .arena = &arena, .fuel = std.math.maxInt(u64) };
     33     const envelope = try r.pair(root) orelse return error.InvalidEnvelope;
     34     var name = envelope.first;
     35     for ("tricuIO") |ch| {
     36         const cell = try r.pair(name) orelse return error.InvalidEnvelope;
     37         if (try r.nat(cell.first, 7) != @as(?u64, ch)) return error.InvalidEnvelope;
     38         name = cell.second;
     39     }
     40     if (try r.node(name) != .leaf) return error.InvalidEnvelope;
     41     const body = try r.pair(envelope.second) orelse return error.InvalidEnvelope;
     42     if (try r.nat(body.first, 16) != @as(?u64, 2)) return error.UnsupportedVersion;
     43     // Drop the input envelope; private run will root the current computation.
     44     arena.roots.clearRetainingCapacity();
     45     var host: Host = .{};
     46     const outcome = try Core.runWithHost(Host, &host, &arena, body.second, 1, std.math.maxInt(u64));
     47     if (outcome.kind != .completed) {
     48         var error_output = std.Io.File.stderr().writer(init.io, &.{});
     49         try error_output.interface.writeAll(if (outcome.kind == .failed) "failed (payload not forced)\n" else "cancelled (reason not forced)\n");
     50         try error_output.flush();
     51         std.process.exit(1);
     52     }
     53     try arena.roots.append(init.gpa, outcome.value);
     54     var stack: std.ArrayList(u32) = .empty;
     55     defer stack.deinit(init.gpa);
     56     try stack.append(init.gpa, outcome.value);
     57     var buffer: [4096]u8 = undefined;
     58     var output = std.Io.File.stdout().writer(init.io, &buffer);
     59     var count: usize = 0;
     60     while (stack.pop()) |id| {
     61         count += 1;
     62         if (count > 1048576) return error.ResultLimit;
     63         const node = arena.get(try reduce.reduce(id, &arena, 1_000_000)).*;
     64         switch (node) {
     65             .leaf => try output.interface.writeByte('0'),
     66             .stem => |s| {
     67                 try output.interface.writeByte('1');
     68                 try stack.append(init.gpa, s.child);
     69             },
     70             .fork => |f| {
     71                 try output.interface.writeByte('2');
     72                 try stack.appendSlice(init.gpa, &.{ f.right, f.left });
     73             },
     74             .app => unreachable,
     75         }
     76     }
     77     try output.interface.writeByte('\n');
     78     try output.flush();
     79 }
     80 
     81 // Exact raw constructor tree (0/1/2 bytes), parsed without recursion or ignored
     82 // trailing roots. Private source controls can pass NUL and malformed scalar data.
     83 fn parseRaw(arena: *Arena, bytes: []const u8) !u32 {
     84     var stack: std.ArrayList(u32) = .empty;
     85     defer stack.deinit(arena.allocator);
     86     var at = bytes.len;
     87     while (at != 0) {
     88         at -= 1;
     89         const node: @import("tree.zig").Node = switch (bytes[at]) {
     90             0 => .leaf,
     91             1 => .{ .stem = .{ .child = stack.pop() orelse return error.InvalidRawTree } },
     92             2 => .{ .fork = .{ .left = stack.pop() orelse return error.InvalidRawTree, .right = stack.pop() orelse return error.InvalidRawTree } },
     93             else => return error.InvalidRawTree,
     94         };
     95         try stack.append(arena.allocator, try arena.alloc(node));
     96     }
     97     if (stack.items.len != 1) return error.InvalidRawTree;
     98     return stack.items[0];
     99 }
    100 
    101 test "private raw argument parser is exact, iterative and preserves constructor order" {
    102     const t = std.testing;
    103     var a = Arena.init(t.allocator);
    104     defer a.deinit();
    105     const root = try parseRaw(&a, &.{ 2, 1, 0, 0 });
    106     const node = a.get(root).fork;
    107     try t.expect(a.get(node.left).* == .stem and a.get(node.right).* == .leaf);
    108     for ([_][]const u8{ &.{}, &.{ 0, 0 }, &.{1}, &.{ 2, 0 }, &.{3}, "0" }) |raw| try t.expectError(error.InvalidRawTree, parseRaw(&a, raw));
    109     const deep = try t.allocator.alloc(u8, 100001);
    110     defer t.allocator.free(deep);
    111     @memset(deep, 1);
    112     deep[deep.len - 1] = 0;
    113     _ = try parseRaw(&a, deep);
    114 }