We don't need SHA verification or Merkle dags in our transport bundle. Content stores can handle both bundle and term verification and hashing.
115 lines
5.1 KiB
Zig
115 lines
5.1 KiB
Zig
const std = @import("std");
|
|
const tree = @import("tree.zig");
|
|
const Arena = @import("arena.zig").Arena;
|
|
|
|
pub const ReduceError = error{
|
|
FuelExhausted,
|
|
InvalidApply,
|
|
OutOfMemory,
|
|
};
|
|
|
|
/// Reduce a term to weak head normal form.
|
|
pub fn reduce(root: u32, arena: *Arena, fuel: u64) ReduceError!u32 {
|
|
var remaining = fuel;
|
|
return try whnf(root, arena, &remaining);
|
|
}
|
|
|
|
fn whnf(term: u32, arena: *Arena, fuel: *u64) ReduceError!u32 {
|
|
var current = term;
|
|
|
|
while (true) {
|
|
switch (arena.get(current).*) {
|
|
.leaf, .stem, .fork => return current,
|
|
.app => |app| {
|
|
if (fuel.* == 0) return error.FuelExhausted;
|
|
fuel.* -= 1;
|
|
|
|
const orig = current;
|
|
const func_idx = app.func;
|
|
const arg_idx = app.arg;
|
|
|
|
// Reduce function to WHNF
|
|
const f = try whnf(func_idx, arena, fuel);
|
|
|
|
switch (arena.get(f).*) {
|
|
// apply Leaf b = Stem b
|
|
.leaf => {
|
|
arena.get(orig).* = .{ .stem = .{ .child = arg_idx } };
|
|
return orig;
|
|
},
|
|
// apply (Stem a) b = Fork a b
|
|
.stem => |s| {
|
|
const a = s.child;
|
|
arena.get(orig).* = .{ .fork = .{ .left = a, .right = arg_idx } };
|
|
return orig;
|
|
},
|
|
.fork => |fork_f| {
|
|
const left_idx = fork_f.left;
|
|
const right_idx = fork_f.right;
|
|
|
|
// Reduce left child of Fork
|
|
const left = try whnf(left_idx, arena, fuel);
|
|
|
|
switch (arena.get(left).*) {
|
|
// apply (Fork Leaf a) _ = a
|
|
.leaf => {
|
|
const result = try whnf(right_idx, arena, fuel);
|
|
if (orig != result) {
|
|
arena.get(orig).* = arena.get(result).*;
|
|
}
|
|
return orig;
|
|
},
|
|
// apply (Fork (Stem a) b) c = (a c) (b c)
|
|
.stem => |s| {
|
|
const a = s.child;
|
|
const inner1 = try arena.alloc(.{ .app = .{ .func = a, .arg = arg_idx } });
|
|
const inner2 = try arena.alloc(.{ .app = .{ .func = right_idx, .arg = arg_idx } });
|
|
arena.get(orig).* = .{ .app = .{ .func = inner1, .arg = inner2 } };
|
|
current = orig;
|
|
continue;
|
|
},
|
|
.fork => {
|
|
// Reduce argument
|
|
const arg = try whnf(arg_idx, arena, fuel);
|
|
|
|
switch (arena.get(arg).*) {
|
|
// apply (Fork (Fork a b) c) Leaf = a
|
|
.leaf => {
|
|
const a_idx = arena.get(left).fork.left;
|
|
const result = try whnf(a_idx, arena, fuel);
|
|
if (orig != result) {
|
|
arena.get(orig).* = arena.get(result).*;
|
|
}
|
|
return orig;
|
|
},
|
|
// apply (Fork (Fork a b) c) (Stem u) = b u
|
|
.stem => |s| {
|
|
const b_idx = arena.get(left).fork.right;
|
|
const u = s.child;
|
|
arena.get(orig).* = .{ .app = .{ .func = b_idx, .arg = u } };
|
|
current = orig;
|
|
continue;
|
|
},
|
|
// apply (Fork (Fork a b) c) (Fork u v) = (c u) v
|
|
.fork => |arg_fork| {
|
|
const c_idx = right_idx;
|
|
const u = arg_fork.left;
|
|
const v = arg_fork.right;
|
|
const inner = try arena.alloc(.{ .app = .{ .func = c_idx, .arg = u } });
|
|
arena.get(orig).* = .{ .app = .{ .func = inner, .arg = v } };
|
|
current = orig;
|
|
continue;
|
|
},
|
|
.app => return error.InvalidApply,
|
|
}
|
|
},
|
|
.app => return error.InvalidApply,
|
|
}
|
|
},
|
|
.app => return error.InvalidApply,
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|