@yakcc/cli 0.5.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +214 -0
- package/LICENSE-ATOMS +38 -0
- package/README.md +76 -0
- package/dist/bin.js +10884 -0
- package/dist/bin.js.map +1 -0
- package/dist/blocks/ascii-char/impl.ts +16 -0
- package/dist/blocks/ascii-char/proof/manifest.json +8 -0
- package/dist/blocks/ascii-char/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/ascii-char/spec.yak +80 -0
- package/dist/blocks/ascii-digit-set/impl.ts +9 -0
- package/dist/blocks/ascii-digit-set/proof/manifest.json +8 -0
- package/dist/blocks/ascii-digit-set/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/ascii-digit-set/spec.yak +66 -0
- package/dist/blocks/base64-alphabet/impl.ts +98 -0
- package/dist/blocks/base64-alphabet/proof/manifest.json +8 -0
- package/dist/blocks/base64-alphabet/proof/tests.fast-check.ts +18 -0
- package/dist/blocks/base64-alphabet/spec.yak +110 -0
- package/dist/blocks/bracket/impl.ts +20 -0
- package/dist/blocks/bracket/proof/manifest.json +8 -0
- package/dist/blocks/bracket/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/bracket/spec.yak +85 -0
- package/dist/blocks/char-code/impl.ts +15 -0
- package/dist/blocks/char-code/proof/manifest.json +8 -0
- package/dist/blocks/char-code/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/char-code/spec.yak +80 -0
- package/dist/blocks/comma/impl.ts +20 -0
- package/dist/blocks/comma/proof/manifest.json +8 -0
- package/dist/blocks/comma/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/comma/spec.yak +80 -0
- package/dist/blocks/comma-separated-integers/impl.ts +85 -0
- package/dist/blocks/comma-separated-integers/proof/manifest.json +8 -0
- package/dist/blocks/comma-separated-integers/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/comma-separated-integers/spec.yak +84 -0
- package/dist/blocks/digit/impl.ts +12 -0
- package/dist/blocks/digit/proof/manifest.json +8 -0
- package/dist/blocks/digit/proof/tests.fast-check.ts +21 -0
- package/dist/blocks/digit/spec.yak +79 -0
- package/dist/blocks/digit-or-throw/impl.ts +20 -0
- package/dist/blocks/digit-or-throw/proof/manifest.json +8 -0
- package/dist/blocks/digit-or-throw/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/digit-or-throw/spec.yak +80 -0
- package/dist/blocks/empty-list-content/impl.ts +20 -0
- package/dist/blocks/empty-list-content/proof/manifest.json +8 -0
- package/dist/blocks/empty-list-content/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/empty-list-content/spec.yak +80 -0
- package/dist/blocks/eof-check/impl.ts +16 -0
- package/dist/blocks/eof-check/proof/manifest.json +8 -0
- package/dist/blocks/eof-check/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/eof-check/spec.yak +76 -0
- package/dist/blocks/integer/impl.ts +29 -0
- package/dist/blocks/integer/proof/manifest.json +8 -0
- package/dist/blocks/integer/proof/tests.fast-check.ts +21 -0
- package/dist/blocks/integer/spec.yak +84 -0
- package/dist/blocks/list-of-ints/impl.ts +168 -0
- package/dist/blocks/list-of-ints/proof/manifest.json +8 -0
- package/dist/blocks/list-of-ints/proof/tests.fast-check.ts +23 -0
- package/dist/blocks/list-of-ints/spec.yak +103 -0
- package/dist/blocks/lru-node/impl.ts +50 -0
- package/dist/blocks/lru-node/proof/manifest.json +8 -0
- package/dist/blocks/lru-node/proof/tests.fast-check.ts +16 -0
- package/dist/blocks/lru-node/spec.yak +92 -0
- package/dist/blocks/memoize/impl.ts +60 -0
- package/dist/blocks/memoize/proof/manifest.json +8 -0
- package/dist/blocks/memoize/proof/tests.fast-check.ts +15 -0
- package/dist/blocks/memoize/spec.yak +91 -0
- package/dist/blocks/non-ascii-rejector/impl.ts +14 -0
- package/dist/blocks/non-ascii-rejector/proof/manifest.json +8 -0
- package/dist/blocks/non-ascii-rejector/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/non-ascii-rejector/spec.yak +67 -0
- package/dist/blocks/nonempty-list-content/impl.ts +116 -0
- package/dist/blocks/nonempty-list-content/proof/manifest.json +8 -0
- package/dist/blocks/nonempty-list-content/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/nonempty-list-content/spec.yak +88 -0
- package/dist/blocks/optional-whitespace/impl.ts +21 -0
- package/dist/blocks/optional-whitespace/proof/manifest.json +8 -0
- package/dist/blocks/optional-whitespace/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/optional-whitespace/spec.yak +76 -0
- package/dist/blocks/peek-char/impl.ts +15 -0
- package/dist/blocks/peek-char/proof/manifest.json +8 -0
- package/dist/blocks/peek-char/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/peek-char/spec.yak +76 -0
- package/dist/blocks/position-step/impl.ts +21 -0
- package/dist/blocks/position-step/proof/manifest.json +8 -0
- package/dist/blocks/position-step/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/position-step/spec.yak +85 -0
- package/dist/blocks/queue-drain/impl.ts +74 -0
- package/dist/blocks/queue-drain/proof/manifest.json +8 -0
- package/dist/blocks/queue-drain/proof/tests.fast-check.ts +16 -0
- package/dist/blocks/queue-drain/spec.yak +119 -0
- package/dist/blocks/semver-component-parser/impl.ts +115 -0
- package/dist/blocks/semver-component-parser/proof/manifest.json +8 -0
- package/dist/blocks/semver-component-parser/proof/tests.fast-check.ts +19 -0
- package/dist/blocks/semver-component-parser/spec.yak +109 -0
- package/dist/blocks/signed-integer/impl.ts +40 -0
- package/dist/blocks/signed-integer/proof/manifest.json +8 -0
- package/dist/blocks/signed-integer/proof/tests.fast-check.ts +21 -0
- package/dist/blocks/signed-integer/spec.yak +84 -0
- package/dist/blocks/string-from-position/impl.ts +18 -0
- package/dist/blocks/string-from-position/proof/manifest.json +8 -0
- package/dist/blocks/string-from-position/proof/tests.fast-check.ts +20 -0
- package/dist/blocks/string-from-position/spec.yak +85 -0
- package/dist/blocks/timer-handle/impl.ts +66 -0
- package/dist/blocks/timer-handle/proof/manifest.json +8 -0
- package/dist/blocks/timer-handle/proof/tests.fast-check.ts +18 -0
- package/dist/blocks/timer-handle/spec.yak +67 -0
- package/dist/blocks/whitespace/impl.ts +20 -0
- package/dist/blocks/whitespace/proof/manifest.json +8 -0
- package/dist/blocks/whitespace/proof/tests.fast-check.ts +21 -0
- package/dist/blocks/whitespace/spec.yak +80 -0
- package/dist/bootstrap/expected-failures.json +4 -0
- package/dist/bootstrap/expected-roots.json +49890 -0
- package/dist/bootstrap/yakcc.registry.sqlite +0 -0
- package/dist/index.js +10826 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// @decision DEC-SEEDS-ASCIICHAR-001: ascii-char is the positional consumption primitive.
|
|
3
|
+
// Status: implemented (WI-006)
|
|
4
|
+
// Rationale: Many parsing blocks need to consume a character at a known offset without
|
|
5
|
+
// interpretation. This block is the positional-read utility that others compose.
|
|
6
|
+
|
|
7
|
+
export function asciiChar(input: string, position: number): string {
|
|
8
|
+
if (position < 0 || position >= input.length) {
|
|
9
|
+
throw new RangeError(`Position ${position} out of bounds for input of length ${input.length}`);
|
|
10
|
+
}
|
|
11
|
+
const code = input.charCodeAt(position);
|
|
12
|
+
if (code > 127) {
|
|
13
|
+
throw new RangeError(`Non-ASCII character at position ${position}: code ${code}`);
|
|
14
|
+
}
|
|
15
|
+
return input[position] as string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Property tests for the ascii-char block.
|
|
3
|
+
// These tests exercise the contract declared in ../spec.yak against the
|
|
4
|
+
// implementation in ../impl.ts.
|
|
5
|
+
//
|
|
6
|
+
// The definitive property-test corpus lives in the parent seed package's
|
|
7
|
+
// seed.test.ts (Suite 4: "property-test corpora"). This file satisfies the
|
|
8
|
+
// L0 proof/manifest.json "property_tests" artifact requirement and makes
|
|
9
|
+
// the test IDs declared in spec.yak traceable to this directory.
|
|
10
|
+
//
|
|
11
|
+
// Test IDs declared in spec.yak:
|
|
12
|
+
// ascii-char-first
|
|
13
|
+
// ascii-char-middle
|
|
14
|
+
// ascii-char-oob
|
|
15
|
+
// ascii-char-negative
|
|
16
|
+
// ascii-char-non-ascii
|
|
17
|
+
|
|
18
|
+
// Re-export the implementation so runners importing this artifact directly
|
|
19
|
+
// can access the block function.
|
|
20
|
+
export * from "../impl.js";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ascii-char",
|
|
3
|
+
"inputs": [
|
|
4
|
+
{
|
|
5
|
+
"name": "input",
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "The full input string."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "position",
|
|
11
|
+
"type": "number",
|
|
12
|
+
"description": "Zero-based index to read from."
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"outputs": [
|
|
16
|
+
{
|
|
17
|
+
"name": "char",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Single character at position."
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"preconditions": [],
|
|
23
|
+
"postconditions": [],
|
|
24
|
+
"invariants": [],
|
|
25
|
+
"effects": [],
|
|
26
|
+
"level": "L0",
|
|
27
|
+
"behavior": "Return the single ASCII character at the given zero-based position in the input string. Throws RangeError if position is out of bounds or the character code is above 127.",
|
|
28
|
+
"guarantees": [
|
|
29
|
+
{
|
|
30
|
+
"id": "pure",
|
|
31
|
+
"description": "Referentially transparent; no side effects."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "length-1",
|
|
35
|
+
"description": "Returned string always has length 1."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "ascii",
|
|
39
|
+
"description": "Returned character has char code <= 127."
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"errorConditions": [
|
|
43
|
+
{
|
|
44
|
+
"description": "position < 0 or position >= input.length.",
|
|
45
|
+
"errorType": "RangeError"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"description": "Character at position has code > 127.",
|
|
49
|
+
"errorType": "RangeError"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"nonFunctional": {
|
|
53
|
+
"time": "O(1)",
|
|
54
|
+
"space": "O(1)",
|
|
55
|
+
"purity": "pure",
|
|
56
|
+
"threadSafety": "safe"
|
|
57
|
+
},
|
|
58
|
+
"propertyTests": [
|
|
59
|
+
{
|
|
60
|
+
"id": "ascii-char-first",
|
|
61
|
+
"description": "asciiChar('abc', 0) returns 'a'"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": "ascii-char-middle",
|
|
65
|
+
"description": "asciiChar('abc', 1) returns 'b'"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "ascii-char-oob",
|
|
69
|
+
"description": "asciiChar('abc', 3) throws RangeError"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "ascii-char-negative",
|
|
73
|
+
"description": "asciiChar('abc', -1) throws RangeError"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": "ascii-char-non-ascii",
|
|
77
|
+
"description": "asciiChar('aéb', 1) throws RangeError"
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// @decision DEC-SEEDS-ASCIIDIGITSET-001: constant membership test avoids char comparison chains.
|
|
3
|
+
// Status: implemented (WI-006)
|
|
4
|
+
// Rationale: A Boolean predicate over the digit set is used by multiple blocks that need
|
|
5
|
+
// to check-without-consume. Separating it makes the predicate independently testable.
|
|
6
|
+
|
|
7
|
+
export function isAsciiDigit(c: string): boolean {
|
|
8
|
+
return c.length === 1 && c >= "0" && c <= "9";
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Property tests for the ascii-digit-set block.
|
|
3
|
+
// These tests exercise the contract declared in ../spec.yak against the
|
|
4
|
+
// implementation in ../impl.ts.
|
|
5
|
+
//
|
|
6
|
+
// The definitive property-test corpus lives in the parent seed package's
|
|
7
|
+
// seed.test.ts (Suite 4: "property-test corpora"). This file satisfies the
|
|
8
|
+
// L0 proof/manifest.json "property_tests" artifact requirement and makes
|
|
9
|
+
// the test IDs declared in spec.yak traceable to this directory.
|
|
10
|
+
//
|
|
11
|
+
// Test IDs declared in spec.yak:
|
|
12
|
+
// ascii-digit-set-zero
|
|
13
|
+
// ascii-digit-set-nine
|
|
14
|
+
// ascii-digit-set-letter
|
|
15
|
+
// ascii-digit-set-empty
|
|
16
|
+
// ascii-digit-set-multi
|
|
17
|
+
|
|
18
|
+
// Re-export the implementation so runners importing this artifact directly
|
|
19
|
+
// can access the block function.
|
|
20
|
+
export * from "../impl.js";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ascii-digit-set",
|
|
3
|
+
"inputs": [
|
|
4
|
+
{
|
|
5
|
+
"name": "c",
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "A single character."
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"outputs": [
|
|
11
|
+
{
|
|
12
|
+
"name": "result",
|
|
13
|
+
"type": "boolean",
|
|
14
|
+
"description": "True iff c is an ASCII digit '0'-'9'."
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"preconditions": [],
|
|
18
|
+
"postconditions": [],
|
|
19
|
+
"invariants": [],
|
|
20
|
+
"effects": [],
|
|
21
|
+
"level": "L0",
|
|
22
|
+
"behavior": "Return true if and only if the single character c is in the ASCII digit set '0'-'9'. Returns false for all other characters including empty string or multi-char strings.",
|
|
23
|
+
"guarantees": [
|
|
24
|
+
{
|
|
25
|
+
"id": "pure",
|
|
26
|
+
"description": "Referentially transparent; no side effects."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "total",
|
|
30
|
+
"description": "Never throws; always returns a boolean."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "consistent",
|
|
34
|
+
"description": "isAsciiDigit(c) === (c >= '0' && c <= '9' && c.length === 1)."
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"errorConditions": [],
|
|
38
|
+
"nonFunctional": {
|
|
39
|
+
"time": "O(1)",
|
|
40
|
+
"space": "O(1)",
|
|
41
|
+
"purity": "pure",
|
|
42
|
+
"threadSafety": "safe"
|
|
43
|
+
},
|
|
44
|
+
"propertyTests": [
|
|
45
|
+
{
|
|
46
|
+
"id": "ascii-digit-set-zero",
|
|
47
|
+
"description": "isAsciiDigit('0') returns true"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "ascii-digit-set-nine",
|
|
51
|
+
"description": "isAsciiDigit('9') returns true"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "ascii-digit-set-letter",
|
|
55
|
+
"description": "isAsciiDigit('a') returns false"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "ascii-digit-set-empty",
|
|
59
|
+
"description": "isAsciiDigit('') returns false"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "ascii-digit-set-multi",
|
|
63
|
+
"description": "isAsciiDigit('12') returns false"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// @decision DEC-V0-B4-SEED-base64-alphabet-001
|
|
4
|
+
// @title base64-alphabet: RFC 4648 alphabet map and bit-shift encoder
|
|
5
|
+
// @status accepted
|
|
6
|
+
// @rationale
|
|
7
|
+
// The base64-encode B4 task requires a standard base64 encoder that handles
|
|
8
|
+
// both standard (RFC 4648 Section 4) and URL-safe (Section 5) alphabets.
|
|
9
|
+
// This atom implements the core alphabet lookup and bit-extraction loop.
|
|
10
|
+
//
|
|
11
|
+
// Design decisions:
|
|
12
|
+
// (A) BIT EXTRACTION ORDER: Each 3-byte group (b0, b1, b2 = 24 bits) is
|
|
13
|
+
// split into four 6-bit chunks in MSB-first order:
|
|
14
|
+
// i0 = b0 >> 2
|
|
15
|
+
// i1 = ((b0 & 0x3) << 4) | (b1 >> 4)
|
|
16
|
+
// i2 = ((b1 & 0xf) << 2) | (b2 >> 6)
|
|
17
|
+
// i3 = b2 & 0x3f
|
|
18
|
+
// This is the normative bit ordering from RFC 4648 Section 3.
|
|
19
|
+
//
|
|
20
|
+
// (B) PADDING EXCLUDED: The '=' padding character is not appended by this
|
|
21
|
+
// atom. The B4 task's caller is expected to manage partial-block padding
|
|
22
|
+
// (as per RFC 4648 Section 3.2). Excluding padding keeps the atom pure
|
|
23
|
+
// and free of side-conditions about input length divisibility.
|
|
24
|
+
// The precondition bytes.length % 3 === 0 is enforced with RangeError.
|
|
25
|
+
//
|
|
26
|
+
// (C) CALLER-CONTROLLED URL-SAFE: A boolean flag selects between the two
|
|
27
|
+
// alphabets rather than having two separate atoms, because they share
|
|
28
|
+
// 99% of their logic and the flag is a single conditional per output
|
|
29
|
+
// (actually resolved once at function entry by selecting the alphabet
|
|
30
|
+
// string). This keeps the corpus size down.
|
|
31
|
+
//
|
|
32
|
+
// (D) MODULE-SCOPE CONST ALPHABETS: The two 64-character alphabet strings
|
|
33
|
+
// are module-level constants. They satisfy the strict-subset validator
|
|
34
|
+
// (const at top level is allowed) and are initialised once at module
|
|
35
|
+
// load rather than on every call.
|
|
36
|
+
//
|
|
37
|
+
// (E) noUncheckedIndexedAccess COMPLIANCE: The project tsconfig enables
|
|
38
|
+
// noUncheckedIndexedAccess, which types arr[n] as T | undefined even
|
|
39
|
+
// for loop variables proven safe by the loop bounds. Local variables
|
|
40
|
+
// capture bytes[i], bytes[i+1], bytes[i+2] with ?? 0 fallbacks (the
|
|
41
|
+
// out-of-range guard above makes these unreachable, but TypeScript
|
|
42
|
+
// cannot prove that). RangeError validation precedes the fallback.
|
|
43
|
+
//
|
|
44
|
+
// Reference: RFC 4648 (2006), "The Base16, Base32, and Base64 Data
|
|
45
|
+
// Encodings", Sections 3, 4, and 5. https://www.rfc-editor.org/rfc/rfc4648
|
|
46
|
+
|
|
47
|
+
/** Standard RFC 4648 Section 4 base64 alphabet. */
|
|
48
|
+
const STANDARD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
49
|
+
|
|
50
|
+
/** URL-safe RFC 4648 Section 5 base64 alphabet (+ -> -, / -> _). */
|
|
51
|
+
const URL_SAFE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Encode a byte array to base64 using the RFC 4648 alphabet.
|
|
55
|
+
*
|
|
56
|
+
* Input length must be a multiple of 3. Padding ('=') is NOT appended;
|
|
57
|
+
* the caller is responsible for padding partial input blocks.
|
|
58
|
+
*
|
|
59
|
+
* @param bytes - Byte values, each in [0, 255]. Length must be % 3 === 0.
|
|
60
|
+
* @param urlSafe - Use URL-safe alphabet (RFC 4648 Section 5) when true.
|
|
61
|
+
* @returns Base64 encoded string without '=' padding.
|
|
62
|
+
* @throws RangeError if bytes.length % 3 !== 0 or any byte is outside [0, 255].
|
|
63
|
+
*/
|
|
64
|
+
export function base64Encode(bytes: number[], urlSafe: boolean): string {
|
|
65
|
+
if (bytes.length % 3 !== 0) {
|
|
66
|
+
throw new RangeError(`base64Encode: input length must be a multiple of 3, got ${bytes.length}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const alphabet = urlSafe ? URL_SAFE_ALPHABET : STANDARD_ALPHABET;
|
|
70
|
+
let result = "";
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
73
|
+
// Capture with ?? 0: the loop bound (i < bytes.length) and length % 3 === 0 guarantee
|
|
74
|
+
// bytes[i], bytes[i+1], bytes[i+2] are always defined here; ?? 0 is a TypeScript
|
|
75
|
+
// appeasement for noUncheckedIndexedAccess and is never reached at runtime.
|
|
76
|
+
const b0 = bytes[i] ?? 0;
|
|
77
|
+
const b1 = bytes[i + 1] ?? 0;
|
|
78
|
+
const b2 = bytes[i + 2] ?? 0;
|
|
79
|
+
|
|
80
|
+
if (b0 > 255 || b1 > 255 || b2 > 255) {
|
|
81
|
+
throw new RangeError(`base64Encode: byte value out of range [0, 255] at index ${i}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Extract four 6-bit groups from three 8-bit bytes (MSB-first, RFC 4648 Section 3)
|
|
85
|
+
const i0 = (b0 >> 2) & 0x3f;
|
|
86
|
+
const i1 = ((b0 & 0x03) << 4) | ((b1 >> 4) & 0x0f);
|
|
87
|
+
const i2 = ((b1 & 0x0f) << 2) | ((b2 >> 6) & 0x03);
|
|
88
|
+
const i3 = b2 & 0x3f;
|
|
89
|
+
|
|
90
|
+
// alphabet[n] is string | undefined per noUncheckedIndexedAccess.
|
|
91
|
+
// Indices i0-i3 are in [0, 63] by construction (6-bit values), and alphabet
|
|
92
|
+
// has exactly 64 characters, so these lookups always succeed.
|
|
93
|
+
result +=
|
|
94
|
+
(alphabet[i0] ?? "") + (alphabet[i1] ?? "") + (alphabet[i2] ?? "") + (alphabet[i3] ?? "");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Property tests for the base64-alphabet block.
|
|
3
|
+
// These tests exercise the contract declared in ../spec.yak against the
|
|
4
|
+
// implementation in ../impl.ts.
|
|
5
|
+
//
|
|
6
|
+
// Test IDs declared in spec.yak:
|
|
7
|
+
// base64-empty
|
|
8
|
+
// base64-standard-known
|
|
9
|
+
// base64-url-safe-known
|
|
10
|
+
// base64-output-length
|
|
11
|
+
// base64-invalid-length
|
|
12
|
+
// base64-byte-out-of-range
|
|
13
|
+
// base64-all-zeros
|
|
14
|
+
// base64-all-255
|
|
15
|
+
|
|
16
|
+
// Re-export implementation so runners importing this artifact directly
|
|
17
|
+
// can access the block functions.
|
|
18
|
+
export * from "../impl.js";
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "base64-alphabet",
|
|
3
|
+
"inputs": [
|
|
4
|
+
{
|
|
5
|
+
"name": "bytes",
|
|
6
|
+
"type": "number[]",
|
|
7
|
+
"description": "Array of byte values (each in range 0-255). Length must be a multiple of 3; if not, caller must pad."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "urlSafe",
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"description": "When true, uses URL-safe alphabet (+/ replaced with -_). When false uses standard RFC 4648 alphabet."
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"outputs": [
|
|
16
|
+
{
|
|
17
|
+
"name": "encoded",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Base64 encoded string without padding characters. Caller appends '=' padding if required."
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"preconditions": [
|
|
23
|
+
{
|
|
24
|
+
"description": "Each element of bytes is an integer in [0, 255]."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"description": "bytes.length is a multiple of 3 (padding is caller's responsibility)."
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"postconditions": [
|
|
31
|
+
{
|
|
32
|
+
"description": "encoded.length === (bytes.length / 3) * 4"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"description": "Every character in encoded is from the selected RFC 4648 alphabet."
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"invariants": [],
|
|
39
|
+
"effects": [],
|
|
40
|
+
"level": "L0",
|
|
41
|
+
"behavior": "Encode a byte array to base64 using the RFC 4648 alphabet. Groups bytes in triplets, extracts four 6-bit values via bit-shift, maps each to the alphabet character at that index, and concatenates. When urlSafe is true, replaces '+' with '-' and '/' with '_'. Padding ('=') is not appended; the caller is responsible for padding if the byte count is not a multiple of 3.",
|
|
42
|
+
"guarantees": [
|
|
43
|
+
{
|
|
44
|
+
"id": "pure",
|
|
45
|
+
"description": "Referentially transparent; no side effects."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "rfc4648-standard",
|
|
49
|
+
"description": "Standard alphabet: A-Z, a-z, 0-9, +, / (RFC 4648 Section 4)."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "rfc4648-url-safe",
|
|
53
|
+
"description": "URL-safe alphabet: A-Z, a-z, 0-9, -, _ (RFC 4648 Section 5)."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "output-length",
|
|
57
|
+
"description": "Output length is exactly (bytes.length / 3) * 4 characters."
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"errorConditions": [
|
|
61
|
+
{
|
|
62
|
+
"description": "bytes.length is not a multiple of 3.",
|
|
63
|
+
"errorType": "RangeError"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"description": "A byte value is outside [0, 255].",
|
|
67
|
+
"errorType": "RangeError"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"nonFunctional": {
|
|
71
|
+
"time": "O(n) where n is bytes.length",
|
|
72
|
+
"space": "O(n) for the output string",
|
|
73
|
+
"purity": "pure",
|
|
74
|
+
"threadSafety": "safe"
|
|
75
|
+
},
|
|
76
|
+
"propertyTests": [
|
|
77
|
+
{
|
|
78
|
+
"id": "base64-empty",
|
|
79
|
+
"description": "base64Encode([], false) returns empty string"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "base64-standard-known",
|
|
83
|
+
"description": "base64Encode([77,97,110], false) returns 'TWFu' (RFC 4648 example)"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "base64-url-safe-known",
|
|
87
|
+
"description": "base64Encode bytes that produce + or / in standard mode returns - or _ in url-safe mode"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "base64-output-length",
|
|
91
|
+
"description": "Output length === (bytes.length / 3) * 4 for any valid input"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "base64-invalid-length",
|
|
95
|
+
"description": "bytes.length not a multiple of 3 throws RangeError"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "base64-byte-out-of-range",
|
|
99
|
+
"description": "Byte value 256 throws RangeError"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "base64-all-zeros",
|
|
103
|
+
"description": "base64Encode([0,0,0], false) returns 'AAAA'"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"id": "base64-all-255",
|
|
107
|
+
"description": "base64Encode([255,255,255], false) returns '////' (three max-value bytes)"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// @decision DEC-SEEDS-BRACKET-001: bracket matches a single bracket char and returns next position.
|
|
3
|
+
// Status: implemented (WI-006)
|
|
4
|
+
// Rationale: Bracket matching is a hot path in list parsing. Returning the post-bracket position
|
|
5
|
+
// follows the positional combinator convention used by all other blocks in this corpus.
|
|
6
|
+
|
|
7
|
+
export function bracket(input: string, position: number, kind: "[" | "]"): number {
|
|
8
|
+
if (position < 0) {
|
|
9
|
+
throw new RangeError(`Position ${position} is negative`);
|
|
10
|
+
}
|
|
11
|
+
if (position >= input.length) {
|
|
12
|
+
throw new SyntaxError(`Expected '${kind}' at position ${position} but reached end of input`);
|
|
13
|
+
}
|
|
14
|
+
if (input[position] !== kind) {
|
|
15
|
+
throw new SyntaxError(
|
|
16
|
+
`Expected '${kind}' at position ${position} but found ${JSON.stringify(input[position])}`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return position + 1;
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Property tests for the bracket block.
|
|
3
|
+
// These tests exercise the contract declared in ../spec.yak against the
|
|
4
|
+
// implementation in ../impl.ts.
|
|
5
|
+
//
|
|
6
|
+
// The definitive property-test corpus lives in the parent seed package's
|
|
7
|
+
// seed.test.ts (Suite 4: "property-test corpora"). This file satisfies the
|
|
8
|
+
// L0 proof/manifest.json "property_tests" artifact requirement and makes
|
|
9
|
+
// the test IDs declared in spec.yak traceable to this directory.
|
|
10
|
+
//
|
|
11
|
+
// Test IDs declared in spec.yak:
|
|
12
|
+
// bracket-open
|
|
13
|
+
// bracket-close
|
|
14
|
+
// bracket-mismatch
|
|
15
|
+
// bracket-oob
|
|
16
|
+
// bracket-negative
|
|
17
|
+
|
|
18
|
+
// Re-export the implementation so runners importing this artifact directly
|
|
19
|
+
// can access the block function.
|
|
20
|
+
export * from "../impl.js";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bracket",
|
|
3
|
+
"inputs": [
|
|
4
|
+
{
|
|
5
|
+
"name": "input",
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "The full input string."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "position",
|
|
11
|
+
"type": "number",
|
|
12
|
+
"description": "Zero-based position to match at."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "kind",
|
|
16
|
+
"type": "'[' | ']'",
|
|
17
|
+
"description": "Which bracket to expect."
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"outputs": [
|
|
21
|
+
{
|
|
22
|
+
"name": "newPosition",
|
|
23
|
+
"type": "number",
|
|
24
|
+
"description": "position + 1 after matching the bracket."
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"preconditions": [],
|
|
28
|
+
"postconditions": [],
|
|
29
|
+
"invariants": [],
|
|
30
|
+
"effects": [],
|
|
31
|
+
"level": "L0",
|
|
32
|
+
"behavior": "Assert that the character at position equals kind ('[' or ']'), then return position + 1. Throws SyntaxError if the character does not match or if position is out of bounds.",
|
|
33
|
+
"guarantees": [
|
|
34
|
+
{
|
|
35
|
+
"id": "pure",
|
|
36
|
+
"description": "Referentially transparent; no side effects."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "advance-1",
|
|
40
|
+
"description": "Returns position + 1 on success."
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"errorConditions": [
|
|
44
|
+
{
|
|
45
|
+
"description": "Character at position does not equal kind.",
|
|
46
|
+
"errorType": "SyntaxError"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"description": "position >= input.length (end of input).",
|
|
50
|
+
"errorType": "SyntaxError"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"description": "position < 0.",
|
|
54
|
+
"errorType": "RangeError"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"nonFunctional": {
|
|
58
|
+
"time": "O(1)",
|
|
59
|
+
"space": "O(1)",
|
|
60
|
+
"purity": "pure",
|
|
61
|
+
"threadSafety": "safe"
|
|
62
|
+
},
|
|
63
|
+
"propertyTests": [
|
|
64
|
+
{
|
|
65
|
+
"id": "bracket-open",
|
|
66
|
+
"description": "bracket('[abc', 0, '[') returns 1"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "bracket-close",
|
|
70
|
+
"description": "bracket(']', 0, ']') returns 1"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "bracket-mismatch",
|
|
74
|
+
"description": "bracket('[', 0, ']') throws SyntaxError"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "bracket-oob",
|
|
78
|
+
"description": "bracket('', 0, '[') throws SyntaxError"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "bracket-negative",
|
|
82
|
+
"description": "bracket('[', -1, '[') throws RangeError"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// @decision DEC-SEEDS-CHARCODE-001: char-code exposes char-code lookup as an explicit contract.
|
|
3
|
+
// Status: implemented (WI-006)
|
|
4
|
+
// Rationale: Multiple blocks rely on charCodeAt arithmetic. Making it an explicit block
|
|
5
|
+
// documents the zero-extension property (always non-negative) and enables registry reuse.
|
|
6
|
+
|
|
7
|
+
export function charCode(input: string, position: number): number {
|
|
8
|
+
if (position < 0) {
|
|
9
|
+
throw new RangeError(`Position ${position} is negative`);
|
|
10
|
+
}
|
|
11
|
+
if (position >= input.length) {
|
|
12
|
+
throw new RangeError(`Position ${position} out of bounds for input of length ${input.length}`);
|
|
13
|
+
}
|
|
14
|
+
return input.charCodeAt(position);
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Property tests for the char-code block.
|
|
3
|
+
// These tests exercise the contract declared in ../spec.yak against the
|
|
4
|
+
// implementation in ../impl.ts.
|
|
5
|
+
//
|
|
6
|
+
// The definitive property-test corpus lives in the parent seed package's
|
|
7
|
+
// seed.test.ts (Suite 4: "property-test corpora"). This file satisfies the
|
|
8
|
+
// L0 proof/manifest.json "property_tests" artifact requirement and makes
|
|
9
|
+
// the test IDs declared in spec.yak traceable to this directory.
|
|
10
|
+
//
|
|
11
|
+
// Test IDs declared in spec.yak:
|
|
12
|
+
// char-code-zero
|
|
13
|
+
// char-code-a
|
|
14
|
+
// char-code-oob
|
|
15
|
+
// char-code-negative
|
|
16
|
+
// char-code-bracket
|
|
17
|
+
|
|
18
|
+
// Re-export the implementation so runners importing this artifact directly
|
|
19
|
+
// can access the block function.
|
|
20
|
+
export * from "../impl.js";
|