Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

- `jpar-list text` builtin: parse a JSON string and assert the top-level value is an array. Returns `R (L _) t`. `jpar-list! body` unwraps to `L _` directly, so `@x (jpar-list! body){...}` type-checks without a binding or type annotation. `jpar` is unchanged (returns `R _ t`); use `jpar-list` when the response is known to be a JSON array.
- `get-to url timeout-ms > R t t` and `pst-to url body timeout-ms > R t t` builtins. HTTP GET and POST with an explicit per-request timeout in milliseconds. The timeout rounds up to the nearest whole second (minreq granularity). Returns `Err` when the deadline is exceeded, identical to any other connection failure from the caller's view. Both are tree-bridge eligible so VM and Cranelift JIT/AOT inherit them without new opcodes. Closes pending.md item #29.
- Crypto primitives bundle: `sha256 s > t`, `hmac-sha256 key body > t`, `base64-enc s > t`, `base64-dec s > R t t`, `base64url-enc s > t`, `base64url-dec s > R t t`, `hex-enc bytes:L n > t`, `hex-dec s > R (L n) t`, `ct-eq a b > b`. All are tree-bridge eligible (pure text/bytes ops, no FnRef args, no I/O); VM and Cranelift inherit via the existing bridge path with zero new opcodes. Dependencies: `sha2`, `hmac`, `base64`, `hex`, `subtle` crates. `sha256` and `hmac-sha256` return lowercase hex; `ct-eq` is constant-time, safe for comparing secrets (HMAC digests, tokens). `base64-dec`, `base64url-dec`, `hex-dec` return `R t/L n t` and can be auto-unwrapped with `!`. Validated by batch-1 personas needing webhook signature verification, JWT construction, and hex encode/decode workflows. Known vectors: SHA-256 empty/"abc", HMAC RFC 4231 test case 2, RFC 4648 base64 round-trips.
- `rgxall-multi pats:L t s:t > L t` builtin. Apply multiple patterns to a single string and get one flat list of all hits in pattern order. Per-pattern semantics follow `rgxall1`: 0 capture groups returns whole matches; 1 capture group returns capture-1 strings; 2+ capture groups errors with a hint to use `rgxall`. Replaces the verbose `flat (map (p:t>L t;rgxall1 p line) pats)` workaround (~20 tokens per call site saved). Motivated by cron-explainer and historical-archeologist personas, which both needed multi-pattern scan on a single line. Tree-bridge eligible alongside `rgxall1`; no new opcodes.
- `fmod a b` builtin: floor-mod, always non-negative when `b > 0`. Equivalent to Python `a % b` and JS `Math.floor((a % b + b) % b)`. Implemented across VM, JIT, and AOT. Eliminates the `(raw + 7) % 7` workaround that every TZ/weekday persona needed with signed `mod`. `mod` is unchanged (C-style signed remainder).
- `dtparse-rel s now > R n t` builtin. Resolves a natural-language relative-date phrase to a Unix epoch anchored at `now`. Supported: `today`/`yesterday`/`tomorrow`, `N days/weeks/months ago`, `in N days/weeks/months` (singular + plural), `last/next/this <weekday>` (monday-sunday or mon-sun; `last`/`next` never return today), and ISO-8601 `YYYY-MM-DD` passthrough. Month arithmetic clamps to the last valid day (Jan 31 + 1 month = Feb 28/29). Tree-bridge eligible -- VM and Cranelift pick it up automatically. Eliminates ~40 LoC of date-arithmetic helpers per date persona (P1 #8 from the persona feedback log).
Expand Down
92 changes: 92 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ clap = { version = "4", features = ["derive"] }
fastrand = "2"
regex = "1"
chrono = { version = "0.4", default-features = false, features = ["clock"] }
sha2 = "0.10"
hmac = "0.12"
base64 = "0.22"
hex = "0.4"
subtle = "2"

[dev-dependencies]
wiremock = "0.6"
Expand Down
47 changes: 47 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,15 @@ Called like functions, compiled to dedicated opcodes.
| `dtparse-rel s now` | parse relative-date phrase to epoch; `now` is the anchor epoch | `R n t` |
| `dur-parse s` | parse human duration string ("3h 30m", "1 week 2 days", "1.5 hours", "90s") into seconds. Lenient: accepts abbreviations `s`/`m`/`h`/`d`/`w`, full names (singular + plural), decimal quantities, mixed sequences. Err if empty or no unit found | `R n t` |
| `dur-fmt n` | format seconds as human-readable duration ("2h 42m", "1 day", "30s"). Drops zero parts; uses largest applicable units. Zero returns "0s". Negative values format with a leading "-" | `t` |
| `sha256 s` | SHA-256 hex digest (lowercase) of the UTF-8 bytes of `s` | `t` |
| `hmac-sha256 key body` | HMAC-SHA256 of `body` under `key`; returns lowercase hex. Use for webhook signature verification and API request signing | `t` |
| `base64-enc s` | standard base64 encode (RFC 4648 §4, with `=` padding) | `t` |
| `base64-dec s` | standard base64 decode; Err on invalid input (non-base64 chars) | `R t t` |
| `base64url-enc s` | base64url encode (RFC 4648 §5, no padding, URL-safe `-`/`_` alphabet). Use for JWT header/payload segments | `t` |
| `base64url-dec s` | base64url decode; Err on invalid input | `R t t` |
| `hex-enc bytes` | encode a list of integers 0-255 as lowercase hex string | `t` |
| `hex-dec s` | decode a hex string to a list of byte values (each 0-255). Err on odd-length or non-hex input | `R (L n) t` |
| `ct-eq a b` | constant-time text equality - returns `true` iff `a == b` without leaking timing info via short-circuit. Use when comparing secrets (HMAC digests, tokens) | `b` |
| `rdjl path` | read JSONL file as `L (R _ t)`: one parse result per non-empty line | `L (R _ t)` |
| `get-many urls` | concurrent HTTP GET fan-out (max 10 parallel), preserves order | `L (R t t)` |
| `sleep ms` | pause current engine for `ms` milliseconds; returns nil | `_` |
Expand Down Expand Up @@ -714,6 +723,44 @@ emits a single leading minus rather than signing each part.
a fraction (`90.5 -> "1m 30.5s"`). Fractional minutes / hours / days / weeks
are decomposed into smaller units before formatting.

### Crypto primitives

`sha256`, `hmac-sha256`, `base64-enc`, `base64-dec`, `base64url-enc`, `base64url-dec`, `hex-enc`, `hex-dec`, `ct-eq` are tree-bridge eligible: they dispatch through the tree interpreter so VM and Cranelift share identical semantics.

`sha256 s > t` — SHA-256 hex digest (lowercase) of the UTF-8 bytes of `s`. Input is always treated as text; if you need to hash raw bytes, encode them as a string via `hex-enc` first.

`hmac-sha256 key body > t` — HMAC-SHA256 of `body` under `key`, lowercase hex. Both arguments are text. Typical use is webhook signature verification (`ct-eq (hmac-sha256 secret payload) sig`) and API request signing.

`base64-enc s > t` / `base64-dec s > R t t` — standard base64 (RFC 4648 §4) with `=` padding. `base64-dec` returns `R t t`; the Ok branch holds the decoded text string (valid UTF-8), the Err branch holds the reason. Non-UTF-8 decoded bytes always Err.

`base64url-enc s > t` / `base64url-dec s > R t t` — base64url (RFC 4648 §5): URL-safe `-`/`_` alphabet, no padding. Use for JWT header and payload segments.

`hex-enc bytes:L n > t` — encode a list of integers 0-255 as lowercase hex. Each element must be a whole number in `[0, 255]`; ILO-R009 on out-of-range values.

`hex-dec s > R (L n) t` — decode a hex string (upper- or lowercase) to a list of byte values (each 0-255). Err on odd-length input or non-hex characters.

`ct-eq a:t b:t > b` — constant-time text equality. Returns `true` iff `a == b` without short-circuiting on the first differing byte, preventing timing attacks. Always use `ct-eq` rather than `==` when comparing secrets.

```
-- sha256
sha256 "" -- e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
sha256 "abc" -- ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
len (sha256 "x") -- 64 (always)

-- hmac
hmac-sha256 "secret" "payload" -- lowercase hex

-- base64 round-trip
base64-dec! (base64-enc "hello") -- "hello"
base64url-dec! (base64url-enc "hello") -- "hello"

-- hex round-trip
hex-dec! (hex-enc [72, 101, 108, 108, 111]) -- [72, 101, 108, 108, 111]

-- webhook verification pattern
verify sig:t body:t>b;expected=hmac-sha256 "secret" body;ct-eq expected sig
```

### Set operations

`setunion`, `setinter`, `setdiff` operate on lists of `t`, `n`, or `b` (same constraint as `uniqby`). Output is deduped and sorted by a type-prefixed string key, so results are deterministic across runs and engines. Sort is lexicographic on the key, not numeric - re-sort with `srt` afterwards if you need numeric order.
Expand Down
Loading
Loading