init
This commit is contained in:
commit
ce835e9f44
10 changed files with 1048 additions and 0 deletions
3
.envrc
Normal file
3
.envrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
use flake
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
zig-out
|
||||
.zig-cache
|
||||
.direnv
|
||||
80
build.zig
Normal file
80
build.zig
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const lazyValues = b.addModule("lazy_evaluation", .{
|
||||
.root_source_file = b.path("src/lazy.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{},
|
||||
});
|
||||
|
||||
const niceties = b.addModule("niceties", .{
|
||||
.root_source_file = b.path("src/niceties.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "lazy_evaluation", .module = lazyValues },
|
||||
},
|
||||
});
|
||||
|
||||
lazyValues.addImport("niceties", niceties);
|
||||
|
||||
const partial_application = b.addModule("partial_application", .{
|
||||
.root_source_file = b.path("src/partial.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "lazy_evaluation", .module = lazyValues },
|
||||
},
|
||||
});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "fzig",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "partial_application", .module = partial_application },
|
||||
.{ .name = "niceties", .module = niceties },
|
||||
.{ .name = "lazy_evaluation", .module = lazyValues },
|
||||
},
|
||||
}),
|
||||
.use_llvm = false,
|
||||
.use_lld = false,
|
||||
.linkage = .static,
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const exe_tests = b.addTest(.{
|
||||
.root_module = exe.root_module,
|
||||
});
|
||||
|
||||
const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&run_exe_tests.step);
|
||||
|
||||
const exe_check = b.addExecutable(.{
|
||||
.name = "foo",
|
||||
.root_module = exe.root_module,
|
||||
});
|
||||
|
||||
const check = b.step("check", "Check if foo compiles");
|
||||
check.dependOn(&exe_check.step);
|
||||
}
|
||||
81
build.zig.zon
Normal file
81
build.zig.zon
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
.{
|
||||
// This is the default name used by packages depending on this one. For
|
||||
// example, when a user runs `zig fetch --save <url>`, this field is used
|
||||
// as the key in the `dependencies` table. Although the user can choose a
|
||||
// different name, most users will stick with this provided value.
|
||||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = .fzig,
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
// Together with name, this represents a globally unique package
|
||||
// identifier. This field is generated by the Zig toolchain when the
|
||||
// package is first created, and then *never changes*. This allows
|
||||
// unambiguous detection of one package being an updated version of
|
||||
// another.
|
||||
//
|
||||
// When forking a Zig project, this id should be regenerated (delete the
|
||||
// field and run `zig build`) if the upstream project is still maintained.
|
||||
// Otherwise, the fork is *hostile*, attempting to take control over the
|
||||
// original project's identity. Thus it is recommended to leave the comment
|
||||
// on the following line intact, so that it shows up in code reviews that
|
||||
// modify the field.
|
||||
.fingerprint = 0x186be502140e04c, // Changing this has security and trust implications.
|
||||
// Tracks the earliest Zig version that the package considers to be a
|
||||
// supported use case.
|
||||
.minimum_zig_version = "0.15.2",
|
||||
// This field is optional.
|
||||
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||
// Once all dependencies are fetched, `zig build` no longer requires
|
||||
// internet connectivity.
|
||||
.dependencies = .{
|
||||
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||
//.example = .{
|
||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
||||
// // the new URL. If the contents of a URL change this will result in a hash mismatch
|
||||
// // which will prevent zig from using it.
|
||||
// .url = "https://example.com/foo.tar.gz",
|
||||
//
|
||||
// // This is computed from the file contents of the directory of files that is
|
||||
// // obtained after fetching `url` and applying the inclusion rules given by
|
||||
// // `paths`.
|
||||
// //
|
||||
// // This field is the source of truth; packages do not come from a `url`; they
|
||||
// // come from a `hash`. `url` is just one of many possible mirrors for how to
|
||||
// // obtain a package matching this `hash`.
|
||||
// //
|
||||
// // Uses the [multihash](https://multiformats.io/multihash/) format.
|
||||
// .hash = "...",
|
||||
//
|
||||
// // When this is provided, the package is found in a directory relative to the
|
||||
// // build root. In this case the package's hash is irrelevant and therefore not
|
||||
// // computed. This field and `url` are mutually exclusive.
|
||||
// .path = "foo",
|
||||
//
|
||||
// // When this is set to `true`, a package is declared to be lazily
|
||||
// // fetched. This makes the dependency only get fetched if it is
|
||||
// // actually used.
|
||||
// .lazy = false,
|
||||
//},
|
||||
},
|
||||
// Specifies the set of files and directories that are included in this package.
|
||||
// Only files and directories listed here are included in the `hash` that
|
||||
// is computed for this package. Only files listed here will remain on disk
|
||||
// when using the zig package manager. As a rule of thumb, one should list
|
||||
// files required for compilation plus any license(s).
|
||||
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
||||
// the build root itself.
|
||||
// A directory listed here means that all files within, recursively, are included.
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
// For example...
|
||||
//"LICENSE",
|
||||
//"README.md",
|
||||
},
|
||||
}
|
||||
140
flake.lock
generated
Normal file
140
flake.lock
generated
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772408722,
|
||||
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1773646010,
|
||||
"narHash": "sha256-iYrs97hS7p5u4lQzuNWzuALGIOdkPXvjz7bviiBjUu8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5b2c2d84341b2afb5647081c1386a80d7a8d8605",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1772328832,
|
||||
"narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"zig-overlay": "zig-overlay",
|
||||
"zls-flake": "zls-flake"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zig-overlay": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773622330,
|
||||
"narHash": "sha256-eaDMObc4Y+b4nIHMf+2pmS+gcQEnRdY7xU62K0G7wzM=",
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"rev": "a20da6ffe5e036abaadc8b0cbe0c21aedf0a1dc5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zls-flake": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"zig-overlay": [
|
||||
"zig-overlay"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773672457,
|
||||
"narHash": "sha256-Xj7XWqD1ZkCtZfb39pkvVn5Ed+GcwbzvuADBslCPFw8=",
|
||||
"owner": "zigtools",
|
||||
"repo": "zls",
|
||||
"rev": "27532e2b3a83215efa2ab4dc6f016feb8478a88e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "zigtools",
|
||||
"repo": "zls",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
46
flake.nix
Normal file
46
flake.nix
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
description = "flake for the fzig library";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
zig-overlay.url = "github:mitchellh/zig-overlay";
|
||||
zig-overlay.inputs.nixpkgs.follows = "nixpkgs";
|
||||
zls-flake.url = "github:zigtools/zls";
|
||||
zls-flake.inputs.zig-overlay.follows = "zig-overlay";
|
||||
zls-flake.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs =
|
||||
inputs@{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-parts,
|
||||
zig-overlay,
|
||||
zls-flake,
|
||||
...
|
||||
}:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
systems = nixpkgs.lib.systems.flakeExposed;
|
||||
imports = [ ];
|
||||
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
let
|
||||
zig = zig-overlay.packages.${system}.master;
|
||||
zls = zls-flake.packages.${system}.default;
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = [
|
||||
zig
|
||||
zls
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
268
src/lazy.zig
Normal file
268
src/lazy.zig
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
pub const allocator = if (@hasDecl(@import("root"), "lazy_allocator")) @import("root").lazy_allocator else @import("std").heap.page_allocator;
|
||||
|
||||
const std = @import("std");
|
||||
const niceties = @import("niceties");
|
||||
|
||||
fn genClears(comptime Self: type) type {
|
||||
return struct {
|
||||
pub fn noop(_: Self) void {}
|
||||
pub fn defaultClear(self: Self) void {
|
||||
allocator.destroy(self._data);
|
||||
}
|
||||
pub fn arrayClear(self: Self) void {
|
||||
allocator.free(self);
|
||||
}
|
||||
};
|
||||
}
|
||||
fn genHMClears(comptime Self: type) type {
|
||||
return struct {
|
||||
pub fn noop(self: *Self) void {
|
||||
self.value.deinit();
|
||||
}
|
||||
pub fn defaultClear(self: *Self) void {
|
||||
allocator.destroy(self._data);
|
||||
self.values.deinit();
|
||||
}
|
||||
pub fn arrayClear(self: *Self) void {
|
||||
allocator.free(self._data);
|
||||
self.values.deinit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn LazyValue(comptime T: type, comptime data: type) type {
|
||||
return struct {
|
||||
pub fn cast(self: @This(), comptime resoult: type) resoult {
|
||||
std.debug.assert(niceties.structCompare(@This(), resoult));
|
||||
|
||||
return @as(*const resoult, @ptrCast(&self)).*;
|
||||
}
|
||||
|
||||
pub fn get(self: @This()) T {
|
||||
return self._retrieve(self);
|
||||
}
|
||||
|
||||
pub fn getClear(self: *@This()) T {
|
||||
defer self.deinit();
|
||||
return self.get();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.references -= 1;
|
||||
if (self.references == 0)
|
||||
self._clear(self);
|
||||
}
|
||||
pub const clears = genClears(@This());
|
||||
|
||||
_retrieve: *const fn (self: @This()) T,
|
||||
_clear: *const fn (self: @This()) void = clears.noop,
|
||||
value: ?T = null,
|
||||
_data: data,
|
||||
references: usize = 1,
|
||||
};
|
||||
}
|
||||
pub fn LazyValue_(comptime T: type) type {
|
||||
return struct {
|
||||
pub fn cast(self: @This(), comptime resoult: type) resoult {
|
||||
std.debug.assert(niceties.structCompare(@This(), resoult));
|
||||
|
||||
return @as(*const resoult, @ptrCast(&self)).*;
|
||||
}
|
||||
|
||||
pub fn get(self: @This()) T {
|
||||
return self._retrieve(self);
|
||||
}
|
||||
pub fn getClear(self: @This()) T {
|
||||
defer self.deinit();
|
||||
return self.get();
|
||||
}
|
||||
|
||||
pub fn deinit(self: @This()) void {
|
||||
self._clear(self);
|
||||
}
|
||||
|
||||
pub const clears = genClears(@This());
|
||||
|
||||
_retrieve: *const fn (@This()) T,
|
||||
_clear: *const fn (@This()) void = clears.defaultClear,
|
||||
value: ?T = null,
|
||||
_data: *anyopaque,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn LazyFunction_(comptime T: type, comptime ret: type, comptime args: type) type {
|
||||
return struct {
|
||||
pub fn cast(self: @This(), comptime resoult: type) resoult {
|
||||
std.debug.assert(niceties.structCompare(@This(), resoult));
|
||||
|
||||
return @as(*const resoult, @ptrCast(&self)).*;
|
||||
}
|
||||
|
||||
pub fn get(self: @This(), arg: args) ret {
|
||||
return self._retrieve(self, arg);
|
||||
}
|
||||
pub fn getClear(self: @This(), arg: args) ret {
|
||||
defer self.deinit();
|
||||
return self.get(arg);
|
||||
}
|
||||
|
||||
pub const clears = genHMClears(@This());
|
||||
|
||||
_retrieve: *const fn (@This(), args) ret,
|
||||
_clear: *const fn (@This()) void = clears.defaultClear,
|
||||
value: std.AutoHashMap(args, T) = .init(allocator),
|
||||
_data: *anyopaque,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn LazyFunction(comptime T: type, comptime err: type, comptime data: type, comptime args: type, comptime threadsafe: bool) type {
|
||||
return struct {
|
||||
pub fn cast(self: @This(), comptime resoult: type) resoult {
|
||||
std.debug.assert(niceties.structCompare(@This(), resoult));
|
||||
|
||||
return @as(*const resoult, @ptrCast(&self)).*;
|
||||
}
|
||||
|
||||
pub fn get(self: *@This(), arg: args) err!T {
|
||||
return self._retrieve(self, arg);
|
||||
}
|
||||
pub fn getClear(self: *@This(), arg: args) err!T {
|
||||
defer self.deinit();
|
||||
return self.get(arg);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self._clear(self);
|
||||
}
|
||||
pub const clears = genHMClears(@This());
|
||||
|
||||
_retrieve: *const fn (*@This(), args) err!T,
|
||||
_clear: *const fn (*@This()) void = clears.noop,
|
||||
value: std.AutoHashMap(args, T) = .init(allocator),
|
||||
_data: data,
|
||||
Io: if (threadsafe) std.Io else void,
|
||||
Mutex: if (threadsafe) std.Io.Mutex else void = if (threadsafe) std.Io.Mutex.init else {},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn LazyArray(comptime T: type, comptime err: type, comptime data: type, comptime threadsafe: bool) type {
|
||||
std.debug.assert(@hasField(data, "arr"));
|
||||
std.debug.assert(@hasField(data, "partial"));
|
||||
return struct {
|
||||
pub fn get(self: *@This(), arg: usize) err!T {
|
||||
std.log.debug("called [ get ]", .{});
|
||||
return self.backing.get(arg);
|
||||
}
|
||||
pub fn getClear(self: *@This(), arg: usize) err!T {
|
||||
defer self.backing.deinit();
|
||||
return self.backing.get(arg);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.backing.deinit();
|
||||
}
|
||||
|
||||
pub fn all(self: *@This()) (error{OutOfMemory} || err)![]T {
|
||||
var tmp: []T = try allocator.alloc(T, self._arrSize(self.*));
|
||||
for (0..self._arrSize(self.*)) |i| {
|
||||
tmp[i] = try self.get(i);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
pub fn allIO(self: *@This(), Io: std.Io) (error{OutOfMemory} || err)![]T {
|
||||
const tmpF: []std.Io.Future(err!T) = try allocator.alloc(std.Io.Future(err!T), self._arrSize(self.*));
|
||||
defer allocator.free(tmpF);
|
||||
const tmp: []T = try allocator.alloc(T, self._arrSize(self.*));
|
||||
for (0..self._arrSize(self.*)) |i| {
|
||||
tmpF[i] = Io.async(@This().get, .{ self, i });
|
||||
}
|
||||
for (tmp, tmpF) |*t, *tf| {
|
||||
t.* = try tf.await(Io);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
pub const BackingType = LazyFunction(T, err, data, usize, threadsafe);
|
||||
backing: BackingType,
|
||||
_arrSize: *const fn (@This()) usize,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn generator(
|
||||
comptime T: type,
|
||||
comptime err: type,
|
||||
comptime data: type,
|
||||
comptime limit: enum(usize) {
|
||||
unlimited = std.math.maxInt(usize),
|
||||
_,
|
||||
pub fn fromNum(comptime num: usize) @This() {
|
||||
return @enumFromInt(num);
|
||||
}
|
||||
pub fn toNum(self: @This()) usize {
|
||||
return @intFromEnum(self);
|
||||
}
|
||||
},
|
||||
) type {
|
||||
return struct {
|
||||
const g = @This();
|
||||
pub fn toLazyArray(self: g) LazyArray(T, err, void, false) {
|
||||
const t = LazyArray(T, err, g, false);
|
||||
return t{
|
||||
._arrSize = limit.toNum(),
|
||||
.backing = .{
|
||||
._retrieve = (struct {
|
||||
pub fn retrieve(s: t, i: usize) err!T {
|
||||
return s.backing._data._retrieve(s.backing._data, i);
|
||||
}
|
||||
}).retrieve,
|
||||
._data = self,
|
||||
._clear = (struct {
|
||||
pub fn clear(s: t) void {
|
||||
s.backing._data._clear;
|
||||
}
|
||||
}).clear,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn next(self: *g) (error{limitReached} || err)!T {
|
||||
defer self.index += 1;
|
||||
switch (limit) {
|
||||
.unlimited => {},
|
||||
_ => if (limit.toNum() < self.index) return error.limitReache,
|
||||
}
|
||||
return self._retrieve(self, self.index);
|
||||
}
|
||||
|
||||
pub fn nextN(self: *g, n: usize) (error{outOfMemory} || err)![]T {
|
||||
const tmp = try allocator.alloc(T, n);
|
||||
for (tmp) |*t| {
|
||||
t.* = self.next();
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
pub fn nextNIO(self: *g, Io: std.Io, n: usize) (error{outOfMemory} || err)![]T {
|
||||
const tmpf = try allocator.alloc(std.Io.Future(err!T), n);
|
||||
defer allocator.free(tmpf);
|
||||
const tmp = try allocator.alloc(T, n);
|
||||
for (tmpf) |*t| {
|
||||
t.* = Io.async(next, .{self});
|
||||
}
|
||||
for (tmp, tmpf) |*t, f| {
|
||||
t.* = try f.await(Io);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
pub fn deinit(self: g) void {
|
||||
self._clear(self);
|
||||
}
|
||||
pub const clears = genClears(g);
|
||||
|
||||
_retrieve: *const fn (self: *g, usize) T,
|
||||
_clear: *const fn (self: g) void = clears.noop,
|
||||
index: usize = 0,
|
||||
_data: data,
|
||||
};
|
||||
}
|
||||
95
src/main.zig
Normal file
95
src/main.zig
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
const std = @import("std");
|
||||
const partial_application = @import("partial_application");
|
||||
const niceties = @import("niceties");
|
||||
|
||||
const generator = @import("lazy_evaluation").generator;
|
||||
|
||||
fn genSlowAdd(time: i64) fn (std.Io, u16, u16) u16 {
|
||||
return struct {
|
||||
pub fn slowAdd(Io: std.Io, a: u16, b: u16) u16 {
|
||||
Io.sleep(.fromMicroseconds(time), .real) catch std.log.err("cancelled sleep", .{});
|
||||
return add(a, b);
|
||||
}
|
||||
}.slowAdd;
|
||||
}
|
||||
|
||||
fn slowAdd(Io: std.Io, a: u16, b: u16) u16 {
|
||||
Io.sleep(.fromMilliseconds(1), .real) catch std.log.err("cancelled sleep", .{});
|
||||
return add(a, b);
|
||||
}
|
||||
|
||||
fn add(a: u16, b: u16) u16 {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
fn getArray(comptime T: type, comptime num: comptime_int, comptime @"fn": fn (comptime index: usize) T) [num]T {
|
||||
@setEvalBranchQuota(100000);
|
||||
var tmp: [num]T = undefined;
|
||||
inline for (0..tmp.len) |i| {
|
||||
tmp[i] = @"fn"(i);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
fn testSpeed(comptime size: comptime_int, Io: std.Io, @"fn": anytype) !void {
|
||||
std.log.info("with size {}: ", .{size});
|
||||
{
|
||||
const genType = generator(u16, void, void, .fromNum(size));
|
||||
const array = genType{
|
||||
._data = {},
|
||||
._retrieve = (struct {
|
||||
pub fn fun(_: *genType, index: usize) u16 {
|
||||
return @intCast(index);
|
||||
}
|
||||
}).fun,
|
||||
};
|
||||
var array2 = try niceties.mapP(u16, &array, &@"fn", .ThreadUnsafe, {});
|
||||
defer array2.deinit();
|
||||
var array3 = try niceties.mapPl(u16, array2, &@"fn", .ThreadUnsafe, {});
|
||||
defer array3.deinit();
|
||||
|
||||
//std.log.info("with all(): ", .{});
|
||||
const start = std.Io.Timestamp.now(Io, .real);
|
||||
//std.log.info("array: {any}, resoult: {any}, result2: {any}", .{ array, array2.all(), array3.all() });
|
||||
_ = try array2.all();
|
||||
_ = try array3.all();
|
||||
const end = std.Io.Timestamp.now(Io, .real);
|
||||
|
||||
std.log.info(" with all(): {f}", .{start.durationTo(end)});
|
||||
}
|
||||
{
|
||||
const array = getArray(u16, size, (struct {
|
||||
pub fn fun(comptime index: usize) u16 {
|
||||
return @intCast(index);
|
||||
}
|
||||
}).fun);
|
||||
var array2 = try niceties.mapP(u16, &array, &@"fn", .ThreadSafe, Io);
|
||||
defer array2.deinit();
|
||||
var array3 = try niceties.mapPl(u16, array2, &@"fn", .ThreadSafe, Io);
|
||||
defer array3.deinit();
|
||||
|
||||
//std.log.info("with allIO(): ", .{});
|
||||
const start = std.Io.Timestamp.now(Io, .real);
|
||||
//std.log.info("array: {any}, resoult: {any}, result2: {any}", .{ array, array2.allIO(Io), array3.allIO(Io) });
|
||||
_ = try array2.allIO(Io);
|
||||
_ = try array3.allIO(Io);
|
||||
const end = std.Io.Timestamp.now(Io, .real);
|
||||
|
||||
std.log.info(" with allIO(): {f}", .{start.durationTo(end)});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
comptime var i = 1;
|
||||
std.log.info(
|
||||
\\fn slowAdd(Io: std.Io, a: u16, b: u16) u16 {{
|
||||
\\ Io.sleep(.fromMicroseconds(1000000/size), .real) catch std.log.err("cancelled sleep", .{{}});
|
||||
\\ return add(a, b);
|
||||
\\}}
|
||||
, .{});
|
||||
inline while (i < (1 << @typeInfo(u16).int.bits)) : (i *= 2) {
|
||||
var addOne = partial_application.genPartial(genSlowAdd(1000000 / i)).new;
|
||||
addOne.init(.{ init.io, 1 });
|
||||
try testSpeed(i, init.io, addOne);
|
||||
}
|
||||
}
|
||||
182
src/niceties.zig
Normal file
182
src/niceties.zig
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
const std = @import("std");
|
||||
const LazyValue = @import("lazy_evaluation").LazyValue;
|
||||
const LazyArray = @import("lazy_evaluation").LazyArray;
|
||||
|
||||
pub fn structCompare(comptime A: type, comptime B: type) bool {
|
||||
std.debug.assert(@typeInfo(A) == .@"struct");
|
||||
std.debug.assert(@typeInfo(B) == .@"struct");
|
||||
|
||||
const fa = @typeInfo(A).@"struct".fields;
|
||||
const fb = @typeInfo(B).@"struct".fields;
|
||||
|
||||
if (fa.len != fb.len) return false;
|
||||
|
||||
outer: inline for (fa) |af| {
|
||||
inline for (fb) |bf| {
|
||||
if (bf.type == af.type and std.mem.eql(u8, bf.name, af.name))
|
||||
continue :outer;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn fnType(@"fn": anytype, input: anytype) type {
|
||||
s: switch (@typeInfo(@TypeOf(@"fn"))) {
|
||||
.@"fn" => return @typeInfo(@TypeOf(@"fn")).@"fn".return_type.? orelse @TypeOf(@"fn"(input[0])),
|
||||
.pointer => continue :s @typeInfo(@TypeOf(@"fn".*)),
|
||||
else => @compileError("must pass a function to map"),
|
||||
}
|
||||
}
|
||||
|
||||
fn mapType(input: anytype, @"fn": anytype) type {
|
||||
s: switch (@typeInfo(@TypeOf(input))) {
|
||||
.array => return fnType(@"fn", input),
|
||||
.pointer => switch (@typeInfo(@TypeOf(input)).pointer.size) {
|
||||
.many, .slice => return fnType(@"fn", input),
|
||||
else => continue :s @typeInfo(@TypeOf(input.*)),
|
||||
},
|
||||
else => @compileError("must pass an array, a slice or a many pointer to map"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(alloc: std.mem.Allocator, array: anytype, fun: anytype) ![]fnType(fun, array) {
|
||||
std.debug.assert(@typeInfo(@TypeOf(array)) == .array);
|
||||
|
||||
var new = try alloc.alloc(fnType(fun, array), array.len);
|
||||
for (array, &new) |a, n| {
|
||||
n.* = if (@typeInfo(@TypeOf(fun)).@"fn".return_type) |_| fun(a) else try fun(a);
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
fn arrayType(T: type) type {
|
||||
s: switch (@typeInfo(T)) {
|
||||
.array => |a| return a.child,
|
||||
.pointer => |p| switch (p.size) {
|
||||
.many, .slice => return p.child,
|
||||
else => continue :s @typeInfo(p.child),
|
||||
},
|
||||
else => @compileError("argoument must either be array, slice or many pointer"),
|
||||
}
|
||||
}
|
||||
|
||||
fn mapPdataType(partial: type, arr: type) type {
|
||||
return struct {
|
||||
partial: partial,
|
||||
arr: arr,
|
||||
};
|
||||
}
|
||||
fn mapParrayType(comptime T: type, comptime p: type, comptime arr: type, comptime threadsafety: bool) type {
|
||||
return LazyArray(T, error{ notEnoughArgs, OutOfMemory }, mapPdataType(p, arr), threadsafety);
|
||||
}
|
||||
pub fn mapP(comptime T: type, array: anytype, partial: anytype, comptime threadSafety: enum { ThreadSafe, ThreadUnsafe }, Io: if (threadSafety == .ThreadSafe) std.Io else void) !mapParrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe) {
|
||||
switch (@typeInfo(@TypeOf(partial))) {
|
||||
.pointer => {
|
||||
std.debug.assert(@typeInfo(@TypeOf(partial.*)) == .@"struct");
|
||||
std.debug.assert(std.meta.hasFn(@TypeOf(partial.*), "callAtomic") and
|
||||
@hasDecl(@TypeOf(partial.*), "returnType"));
|
||||
},
|
||||
.@"struct" => std.debug.assert(std.meta.hasFn(@TypeOf(partial), "callAtomic") and
|
||||
std.meta.hasMethod(@TypeOf(partial), "returnType")),
|
||||
else => std.debug.assert(false),
|
||||
}
|
||||
|
||||
return mapParrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe){
|
||||
._arrSize = (struct {
|
||||
pub fn arrSize(self: mapParrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe)) usize {
|
||||
return self.backing._data.arr.len;
|
||||
}
|
||||
}).arrSize,
|
||||
.backing = .{
|
||||
._retrieve = (struct {
|
||||
pub fn retrieve(self: *mapParrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe).BackingType, index: usize) error{ notEnoughArgs, OutOfMemory }!T {
|
||||
{
|
||||
if (threadSafety == .ThreadSafe)
|
||||
self.Mutex.lockUncancelable(self.Io);
|
||||
defer if (threadSafety == .ThreadSafe) self.Mutex.unlock(self.Io);
|
||||
|
||||
if (self.value.get(index)) |val| return val;
|
||||
}
|
||||
|
||||
const val = (try self._data.partial.callAtomic(.{self._data.arr[index]})).getClear();
|
||||
|
||||
if (threadSafety == .ThreadSafe)
|
||||
self.Mutex.lockUncancelable(self.Io);
|
||||
defer if (threadSafety == .ThreadSafe) self.Mutex.unlock(self.Io);
|
||||
|
||||
try self.value.put(index, val);
|
||||
return val;
|
||||
}
|
||||
}).retrieve,
|
||||
._data = (mapPdataType(@TypeOf(partial), @TypeOf(array))){
|
||||
.partial = partial,
|
||||
.arr = array,
|
||||
},
|
||||
.Io = Io,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn mapPldataType(partial: type, arr: type) type {
|
||||
return struct {
|
||||
partial: partial,
|
||||
arr: arr,
|
||||
};
|
||||
}
|
||||
fn mapPlarrayType(comptime T: type, comptime p: type, comptime arr: type, comptime threadsafety: bool) type {
|
||||
return LazyArray(T, error{ notEnoughArgs, OutOfMemory }, mapPldataType(p, arr), threadsafety);
|
||||
}
|
||||
pub fn mapPl(comptime T: type, array: anytype, partial: anytype, comptime threadSafety: enum { ThreadSafe, ThreadUnsafe }, Io: if (threadSafety == .ThreadSafe) std.Io else void) !mapPlarrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe) {
|
||||
switch (@typeInfo(@TypeOf(partial))) {
|
||||
.pointer => {
|
||||
std.debug.assert(@typeInfo(@TypeOf(partial.*)) == .@"struct");
|
||||
std.debug.assert(std.meta.hasFn(@TypeOf(partial.*), "callAtomic") and
|
||||
@hasDecl(@TypeOf(partial.*), "returnType"));
|
||||
},
|
||||
.@"struct" => std.debug.assert(std.meta.hasFn(@TypeOf(partial), "callAtomic") and
|
||||
std.meta.hasMethod(@TypeOf(partial), "returnType")),
|
||||
else => std.debug.assert(false),
|
||||
}
|
||||
|
||||
return mapPlarrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe){
|
||||
._arrSize = (struct {
|
||||
pub fn arrSize(self: mapPlarrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe)) usize {
|
||||
return self.backing._data.arr._arrSize(self.backing._data.arr);
|
||||
}
|
||||
}).arrSize,
|
||||
.backing = .{
|
||||
._retrieve = (struct {
|
||||
pub fn retrieve(self: *mapPlarrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe).BackingType, index: usize) error{ notEnoughArgs, OutOfMemory }!T {
|
||||
{
|
||||
if (threadSafety == .ThreadSafe)
|
||||
self.Mutex.lockUncancelable(self.Io);
|
||||
defer if (threadSafety == .ThreadSafe) self.Mutex.unlock(self.Io);
|
||||
|
||||
if (self.value.get(index)) |val| return val;
|
||||
}
|
||||
|
||||
const val = (try self._data.partial.callAtomic(.{try self._data.arr.get(index)})).getClear();
|
||||
if (threadSafety == .ThreadSafe)
|
||||
self.Mutex.lockUncancelable(self.Io);
|
||||
defer if (threadSafety == .ThreadSafe) self.Mutex.unlock(self.Io);
|
||||
|
||||
try self.value.put(index, val);
|
||||
return val;
|
||||
}
|
||||
}).retrieve,
|
||||
._clear = (struct {
|
||||
pub fn clear(self: *mapPlarrayType(T, @TypeOf(partial), @TypeOf(array), threadSafety == .ThreadSafe).BackingType) void {
|
||||
self._data.arr.deinit();
|
||||
self.value.deinit();
|
||||
}
|
||||
}).clear,
|
||||
._data = (mapPldataType(@TypeOf(partial), @TypeOf(array))){
|
||||
.partial = partial,
|
||||
.arr = array,
|
||||
},
|
||||
.Io = Io,
|
||||
},
|
||||
};
|
||||
}
|
||||
150
src/partial.zig
Normal file
150
src/partial.zig
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
const std = @import("std");
|
||||
const LazyValue = @import("lazy_evaluation").LazyValue;
|
||||
const allocator = @import("lazy_evaluation").allocator;
|
||||
|
||||
fn intCoerce(a: std.builtin.Type, b: std.builtin.Type) bool {
|
||||
return (a == .int and b == .comptime_int) or
|
||||
(a == .int and b == .int and
|
||||
((a.int.bits >= b.int.bits and a.int.signedness == b.int.signedness) or
|
||||
(a.int.bits >= 2 * b.int.bits and a.int.signedness == .signed and b.int.signedness == .unsigned)));
|
||||
}
|
||||
fn floatCoerce(a: std.builtin.Type, b: std.builtin.Type) bool {
|
||||
return (a == .float and b == .comptime_float) or
|
||||
(a == .float and b == .comptime_int) or
|
||||
(a == .float and b == .int and
|
||||
((a.float.bits == 16 and
|
||||
((b.int.signedness == .signed and b.int.bits == 12) or
|
||||
(b.int.signedness == .unsigned and b.int.bits == 11))) or
|
||||
(a.float.bits == 32 and
|
||||
((b.int.signedness == .signed and b.int.bits == 25) or
|
||||
(b.int.signedness == .unsigned and b.int.bits == 24))) or
|
||||
(a.float.bits == 64 and
|
||||
((b.int.signedness == .signed and b.int.bits == 54) or
|
||||
(b.int.signedness == .unsigned and b.int.bits == 53))) or
|
||||
(a.float.bits == 80 and
|
||||
((b.int.signedness == .signed and b.int.bits == 65) or
|
||||
(b.int.signedness == .unsigned and b.int.bits == 64))) or
|
||||
(a.float.bits == 128 and
|
||||
((b.int.signedness == .signed and b.int.bits == 114) or
|
||||
(b.int.signedness == .unsigned and b.int.bits == 113)))));
|
||||
}
|
||||
|
||||
fn coerces(a: type, b: type) bool {
|
||||
const infoA = @typeInfo(a);
|
||||
const infoB = @typeInfo(b);
|
||||
|
||||
return a == b or
|
||||
b == @TypeOf(undefined) or
|
||||
(infoA == .optional and (b == @TypeOf(null) or b == infoA.optional.child)) or
|
||||
intCoerce(infoA, infoB) or
|
||||
floatCoerce(infoA, infoB);
|
||||
}
|
||||
pub fn genPartial(@"fn": anytype) type {
|
||||
std.debug.assert(@typeInfo(@TypeOf(@"fn")) == .@"fn");
|
||||
const info = @typeInfo(@TypeOf(@"fn")).@"fn";
|
||||
|
||||
var types: [info.params.len]type = undefined;
|
||||
|
||||
for (&types, info.params) |*t, param| {
|
||||
t.* = param.type orelse *anyopaque;
|
||||
}
|
||||
|
||||
const argStruct = @Tuple(
|
||||
&types,
|
||||
);
|
||||
|
||||
return struct {
|
||||
pub const returnType = LazyValue(info.return_type.?, argStruct);
|
||||
const Self = @This();
|
||||
pub inline fn @"type"(comptime _: Self) type {
|
||||
return Self;
|
||||
}
|
||||
fn retrieve(args: returnType) info.return_type.? {
|
||||
if (args.value) |val| return val;
|
||||
|
||||
return @call(.auto, @"fn", args._data);
|
||||
}
|
||||
|
||||
pub fn call(self: *Self, args: anytype) ?returnType {
|
||||
std.log.debug("called [ call ]", .{});
|
||||
const typeinfo = @typeInfo(@TypeOf(args));
|
||||
std.debug.assert(typeinfo == .@"struct");
|
||||
std.debug.assert(typeinfo.@"struct".is_tuple);
|
||||
std.debug.assert(typeinfo.@"struct".fields.len <= @typeInfo(@TypeOf(self.args)).@"struct".fields.len - self.index);
|
||||
|
||||
self.callNoExec(args);
|
||||
|
||||
if (@typeInfo(@TypeOf(self.args)).@"struct".fields.len == self.index)
|
||||
return returnType{
|
||||
._retrieve = Self.retrieve,
|
||||
._data = self.args,
|
||||
.value = null,
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const atomicErr = error{notEnoughArgs};
|
||||
pub fn callAtomic(self: Self, args: anytype) atomicErr!returnType {
|
||||
const typeinfo = @typeInfo(@TypeOf(args));
|
||||
std.debug.assert(typeinfo == .@"struct");
|
||||
std.debug.assert(typeinfo.@"struct".is_tuple);
|
||||
std.debug.assert(typeinfo.@"struct".fields.len == @typeInfo(@TypeOf(self.args)).@"struct".fields.len - self.index);
|
||||
|
||||
var tmp = self;
|
||||
tmp.self = &tmp;
|
||||
return tmp.call(args) orelse atomicErr.notEnoughArgs;
|
||||
}
|
||||
|
||||
pub fn call_(self: *Self, args: anytype) void {
|
||||
self.callNoExec(args);
|
||||
|
||||
_ = @call(.auto, @"fn", self.args);
|
||||
}
|
||||
fn callNoExec(self: *Self, args: anytype) void {
|
||||
std.log.debug("{any}", .{args});
|
||||
const nArgs = @typeInfo(@TypeOf(args)).@"struct".fields.len;
|
||||
const totalFields = @typeInfo(@TypeOf(self.args)).@"struct".fields.len;
|
||||
|
||||
switch (self.index) {
|
||||
inline 0...totalFields - 1 => |base| {
|
||||
// Comptime: does this base index align with the types of args?
|
||||
const valid = comptime blk: {
|
||||
if (base + nArgs > totalFields) break :blk false;
|
||||
for (0..nArgs) |i| {
|
||||
if (!coerces(@TypeOf(self.args[base + i]), @TypeOf(args[i])))
|
||||
break :blk false;
|
||||
}
|
||||
break :blk true;
|
||||
};
|
||||
// Body is NOT compiled for invalid (base, args-type) pairs
|
||||
if (valid) {
|
||||
inline for (0..nArgs) |i| {
|
||||
self.args[base + i] = args[i];
|
||||
}
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
self.index += nArgs;
|
||||
}
|
||||
index: usize,
|
||||
args: argStruct,
|
||||
self: *Self,
|
||||
fn setArg(self: *Self, val: anytype) void {
|
||||
return switch (self.index) {
|
||||
inline 0...@typeInfo(@TypeOf(self.args)).@"struct".fields.len - 1 => |i| self.args[i] = val,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
pub const new: Self = .{
|
||||
.args = undefined,
|
||||
.index = 0,
|
||||
.self = undefined,
|
||||
};
|
||||
pub fn init(self: *Self, args: anytype) void {
|
||||
self.self = self;
|
||||
self.callNoExec(args);
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue