-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Origins + more test suite passing (#7)
# Description This PR gets all the testsuite tests passing. However, that is currently using an arena allocator, and if we instead use a GPA we find that we're leaking memory. Need to fix this (and in doing so figure out a good lifetime model)
- Loading branch information
1 parent
227fbd8
commit 432d4b6
Showing
75 changed files
with
4,729 additions
and
555 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Tour | ||
|
||
`biscuit-zig` is split into a number of modules. The following description should provide helpful orientation: | ||
|
||
- `biscuit-schema` | ||
- Contains the `schema.proto` from the official biscuit repo | ||
- Contains `schema.pb.zig` which is generated from `schema.proto` using https://github.com/Arwalk/zig-protobuf. This powers the biscuit deserialization. | ||
- `biscuit-format` | ||
- Provides an intermediate `SerializedBiscuit` type that deserializes the protobuf format and verifies biscuit. | ||
- `biscuit` | ||
- Provides runtime representation of biscuit, this is the main interface a consumer of the library will use. | ||
- A `Biscuit` can be initialized |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# biscuit-parser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
const std = @import("std"); | ||
|
||
// Although this function looks imperative, note that its job is to | ||
// declaratively construct a build graph that will be executed by an external | ||
// runner. | ||
pub fn build(b: *std.Build) void { | ||
// Standard target options allows the person running `zig build` to choose | ||
// what target to build for. Here we do not override the defaults, which | ||
// means any target is allowed, and the default is native. Other options | ||
// for restricting supported target set are available. | ||
const target = b.standardTargetOptions(.{}); | ||
|
||
// Standard optimization options allow the person running `zig build` to select | ||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not | ||
// set a preferred release mode, allowing the user to decide how to optimize. | ||
const optimize = b.standardOptimizeOption(.{}); | ||
|
||
const schema = b.dependency("biscuit-schema", .{ .target = target, .optimize = optimize }); | ||
const format = b.dependency("biscuit-format", .{ .target = target, .optimize = optimize }); | ||
const datalog = b.dependency("biscuit-datalog", .{ .target = target, .optimize = optimize }); | ||
|
||
_ = b.addModule("biscuit-builder", .{ | ||
.root_source_file = .{ .path = "src/root.zig" }, | ||
.imports = &.{ | ||
.{ .name = "biscuit-schema", .module = schema.module("biscuit-schema") }, | ||
.{ .name = "biscuit-format", .module = format.module("biscuit-format") }, | ||
.{ .name = "biscuit-datalog", .module = datalog.module("biscuit-datalog") }, | ||
}, | ||
}); | ||
|
||
// Creates a step for unit testing. This only builds the test executable | ||
// but does not run it. | ||
const lib_unit_tests = b.addTest(.{ | ||
.root_source_file = .{ .path = "src/main.zig" }, | ||
.target = target, | ||
.optimize = optimize, | ||
}); | ||
lib_unit_tests.root_module.addImport("biscuit-schema", schema.module("biscuit-schema")); | ||
lib_unit_tests.root_module.addImport("biscuit-format", format.module("biscuit-format")); | ||
|
||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); | ||
|
||
// Similar to creating the run step earlier, this exposes a `test` step to | ||
// the `zig build --help` menu, providing a way for the user to request | ||
// running the unit tests. | ||
const test_step = b.step("test", "Run unit tests"); | ||
test_step.dependOn(&run_lib_unit_tests.step); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
.{ | ||
.name = "biscuit-builder", | ||
// 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", | ||
|
||
// This field is optional. | ||
// This is currently advisory only; Zig does not yet do anything | ||
// with this value. | ||
//.minimum_zig_version = "0.11.0", | ||
|
||
// 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. | ||
// .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", | ||
//}, | ||
.@"biscuit-schema" = .{ .path = "../biscuit-schema" }, | ||
.@"biscuit-format" = .{ .path = "../biscuit-format" }, | ||
.@"biscuit-datalog" = .{ .path = "../biscuit-datalog" }, | ||
}, | ||
|
||
// 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. | ||
// 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 = .{ | ||
// This makes *all* files, recursively, included in this package. It is generally | ||
// better to explicitly list the files and directories instead, to insure that | ||
// fetching from tarballs, file system paths, and version control all result | ||
// in the same contents hash. | ||
"", | ||
// For example... | ||
//"build.zig", | ||
//"build.zig.zon", | ||
//"src", | ||
//"LICENSE", | ||
//"README.md", | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
const std = @import("std"); | ||
const datalog = @import("biscuit-datalog"); | ||
const Predicate = @import("predicate.zig").Predicate; | ||
const Term = @import("term.zig").Term; | ||
const Rule = @import("rule.zig").Rule; | ||
|
||
pub const Check = struct { | ||
kind: datalog.Check.Kind, | ||
queries: std.ArrayList(Rule), | ||
|
||
pub fn deinit(check: Check) void { | ||
for (check.queries.items) |query| { | ||
query.deinit(); | ||
} | ||
|
||
check.queries.deinit(); | ||
} | ||
|
||
pub fn convert(check: Check, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Check { | ||
var queries = std.ArrayList(datalog.Rule).init(allocator); | ||
|
||
for (check.queries.items) |query| { | ||
try queries.append(try query.convert(allocator, symbols)); | ||
} | ||
|
||
return .{ .kind = check.kind, .queries = queries }; | ||
} | ||
|
||
pub fn format(check: Check, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { | ||
try writer.print("check ", .{}); | ||
|
||
switch (check.kind) { | ||
.one => try writer.print("if", .{}), | ||
.all => try writer.print("all", .{}), | ||
} | ||
|
||
for (check.queries.items) |query| { | ||
try writer.print(" {any}", .{query}); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
const std = @import("std"); | ||
|
||
pub const Date = struct { | ||
year: i32, | ||
month: u8, | ||
day: u8, | ||
hour: u8, | ||
minute: u8, | ||
second: u8, | ||
nanosecond: u32, | ||
// Timezone offset in minutes from UTC; can be negative | ||
utc_offset: i32, | ||
|
||
pub fn eql(left: Date, right: Date) bool { | ||
return left.year == right.year and | ||
left.month == right.month and | ||
left.day == right.day and | ||
left.hour == right.hour and | ||
left.minute == right.minute and | ||
left.second == right.second and | ||
left.nanosecond == right.nanosecond; | ||
} | ||
|
||
pub fn lt(left: Date, right: Date) bool { | ||
if (left.year < right.year) return true; | ||
if (left.year > right.year) return false; | ||
|
||
std.debug.assert(left.year == right.year); | ||
|
||
if (left.month < right.month) return true; | ||
if (left.month > right.month) return false; | ||
|
||
std.debug.assert(left.month == right.month); | ||
|
||
if (left.day < right.day) return true; | ||
if (left.day > right.day) return false; | ||
|
||
std.debug.assert(left.day == right.day); | ||
|
||
if (left.hour < right.hour) return true; | ||
if (left.hour > right.hour) return false; | ||
|
||
std.debug.assert(left.hour == right.hour); | ||
|
||
if (left.minute < right.minute) return true; | ||
if (left.minute > right.minute) return false; | ||
|
||
std.debug.assert(left.minute == right.minute); | ||
|
||
if (left.second < right.second) return true; | ||
if (left.second > right.second) return false; | ||
|
||
std.debug.assert(left.second == right.second); | ||
|
||
if (left.nanosecond < right.nanosecond) return true; | ||
if (left.nanosecond > right.nanosecond) return false; | ||
|
||
std.debug.assert(left.nanosecond == right.nanosecond); | ||
|
||
return false; | ||
} | ||
|
||
pub fn gt(left: Date, right: Date) bool { | ||
if (left.year > right.year) return true; | ||
if (left.year < right.year) return false; | ||
|
||
std.debug.assert(left.year == right.year); | ||
|
||
if (left.month > right.month) return true; | ||
if (left.month < right.month) return false; | ||
|
||
std.debug.assert(left.month == right.month); | ||
|
||
if (left.day > right.day) return true; | ||
if (left.day < right.day) return false; | ||
|
||
std.debug.assert(left.day == right.day); | ||
|
||
if (left.hour > right.hour) return true; | ||
if (left.hour < right.hour) return false; | ||
|
||
std.debug.assert(left.hour == right.hour); | ||
|
||
if (left.minute > right.minute) return true; | ||
if (left.minute < right.minute) return false; | ||
|
||
std.debug.assert(left.minute == right.minute); | ||
|
||
if (left.second > right.second) return true; | ||
if (left.second < right.second) return false; | ||
|
||
std.debug.assert(left.second == right.second); | ||
|
||
if (left.nanosecond > right.nanosecond) return true; | ||
if (left.nanosecond < right.nanosecond) return false; | ||
|
||
std.debug.assert(left.nanosecond == right.nanosecond); | ||
|
||
return false; | ||
} | ||
|
||
pub fn lteq(left: Date, right: Date) bool { | ||
return left.eq(right) or left.lt(right); | ||
} | ||
|
||
pub fn gteq(left: Date, right: Date) bool { | ||
return left.eq(right) or left.gt(right); | ||
} | ||
|
||
pub fn format(date: Date, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { | ||
return writer.print("{}-{}-{}T{}:{}:{}Z", .{ date.year, date.month, date.day, date.hour, date.minute, date.second }); | ||
} | ||
|
||
// FIXME: leap seconds? | ||
pub fn unixEpoch(date: Date) u64 { | ||
var total_days: usize = 0; | ||
|
||
const date_year = @as(usize, @intCast(date.year)); | ||
|
||
for (1970..date_year) |year| { | ||
if (isLeapYear(usize, year)) { | ||
total_days += 366; | ||
} else { | ||
total_days += 365; | ||
} | ||
} | ||
|
||
for (1..date.month) |month| { | ||
total_days += daysInMonth(usize, date_year, @as(u8, @intCast(month))); | ||
} | ||
|
||
for (1..date.day) |_| { | ||
total_days += 1; | ||
} | ||
|
||
var total_seconds: u64 = total_days * 24 * 60 * 60; | ||
|
||
total_seconds += @as(u64, date.hour) * 60 * 60; | ||
total_seconds += @as(u64, date.minute) * 60; | ||
total_seconds += date.second; | ||
|
||
return total_seconds; | ||
} | ||
|
||
pub fn isDayMonthYearValid(comptime T: type, year: T, month: u8, day: u8) bool { | ||
return switch (month) { | ||
// 30 days has september, april june and november | ||
4, 6, 9, 11 => day <= 30, | ||
1, 3, 5, 7, 8, 10, 12 => day <= 31, | ||
2 => if (isLeapYear(T, year)) day <= 29 else day <= 28, | ||
else => false, | ||
}; | ||
} | ||
}; | ||
|
||
pub fn daysInMonth(comptime T: type, year: T, month: u8) u8 { | ||
return switch (month) { | ||
4, 6, 9, 11 => 30, | ||
1, 3, 5, 7, 8, 10, 12 => 31, | ||
2 => if (isLeapYear(T, year)) 29 else 28, | ||
else => unreachable, | ||
}; | ||
} | ||
|
||
fn isLeapYear(comptime T: type, year: T) bool { | ||
if (@mod(year, 400) == 0) return true; | ||
if (@mod(year, 100) == 0) return false; | ||
if (@mod(year, 4) == 0) return true; | ||
|
||
return false; | ||
} |
Oops, something went wrong.