@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.
Files changed (115) hide show
  1. package/LICENSE +214 -0
  2. package/LICENSE-ATOMS +38 -0
  3. package/README.md +76 -0
  4. package/dist/bin.js +10884 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/blocks/ascii-char/impl.ts +16 -0
  7. package/dist/blocks/ascii-char/proof/manifest.json +8 -0
  8. package/dist/blocks/ascii-char/proof/tests.fast-check.ts +20 -0
  9. package/dist/blocks/ascii-char/spec.yak +80 -0
  10. package/dist/blocks/ascii-digit-set/impl.ts +9 -0
  11. package/dist/blocks/ascii-digit-set/proof/manifest.json +8 -0
  12. package/dist/blocks/ascii-digit-set/proof/tests.fast-check.ts +20 -0
  13. package/dist/blocks/ascii-digit-set/spec.yak +66 -0
  14. package/dist/blocks/base64-alphabet/impl.ts +98 -0
  15. package/dist/blocks/base64-alphabet/proof/manifest.json +8 -0
  16. package/dist/blocks/base64-alphabet/proof/tests.fast-check.ts +18 -0
  17. package/dist/blocks/base64-alphabet/spec.yak +110 -0
  18. package/dist/blocks/bracket/impl.ts +20 -0
  19. package/dist/blocks/bracket/proof/manifest.json +8 -0
  20. package/dist/blocks/bracket/proof/tests.fast-check.ts +20 -0
  21. package/dist/blocks/bracket/spec.yak +85 -0
  22. package/dist/blocks/char-code/impl.ts +15 -0
  23. package/dist/blocks/char-code/proof/manifest.json +8 -0
  24. package/dist/blocks/char-code/proof/tests.fast-check.ts +20 -0
  25. package/dist/blocks/char-code/spec.yak +80 -0
  26. package/dist/blocks/comma/impl.ts +20 -0
  27. package/dist/blocks/comma/proof/manifest.json +8 -0
  28. package/dist/blocks/comma/proof/tests.fast-check.ts +20 -0
  29. package/dist/blocks/comma/spec.yak +80 -0
  30. package/dist/blocks/comma-separated-integers/impl.ts +85 -0
  31. package/dist/blocks/comma-separated-integers/proof/manifest.json +8 -0
  32. package/dist/blocks/comma-separated-integers/proof/tests.fast-check.ts +20 -0
  33. package/dist/blocks/comma-separated-integers/spec.yak +84 -0
  34. package/dist/blocks/digit/impl.ts +12 -0
  35. package/dist/blocks/digit/proof/manifest.json +8 -0
  36. package/dist/blocks/digit/proof/tests.fast-check.ts +21 -0
  37. package/dist/blocks/digit/spec.yak +79 -0
  38. package/dist/blocks/digit-or-throw/impl.ts +20 -0
  39. package/dist/blocks/digit-or-throw/proof/manifest.json +8 -0
  40. package/dist/blocks/digit-or-throw/proof/tests.fast-check.ts +20 -0
  41. package/dist/blocks/digit-or-throw/spec.yak +80 -0
  42. package/dist/blocks/empty-list-content/impl.ts +20 -0
  43. package/dist/blocks/empty-list-content/proof/manifest.json +8 -0
  44. package/dist/blocks/empty-list-content/proof/tests.fast-check.ts +20 -0
  45. package/dist/blocks/empty-list-content/spec.yak +80 -0
  46. package/dist/blocks/eof-check/impl.ts +16 -0
  47. package/dist/blocks/eof-check/proof/manifest.json +8 -0
  48. package/dist/blocks/eof-check/proof/tests.fast-check.ts +20 -0
  49. package/dist/blocks/eof-check/spec.yak +76 -0
  50. package/dist/blocks/integer/impl.ts +29 -0
  51. package/dist/blocks/integer/proof/manifest.json +8 -0
  52. package/dist/blocks/integer/proof/tests.fast-check.ts +21 -0
  53. package/dist/blocks/integer/spec.yak +84 -0
  54. package/dist/blocks/list-of-ints/impl.ts +168 -0
  55. package/dist/blocks/list-of-ints/proof/manifest.json +8 -0
  56. package/dist/blocks/list-of-ints/proof/tests.fast-check.ts +23 -0
  57. package/dist/blocks/list-of-ints/spec.yak +103 -0
  58. package/dist/blocks/lru-node/impl.ts +50 -0
  59. package/dist/blocks/lru-node/proof/manifest.json +8 -0
  60. package/dist/blocks/lru-node/proof/tests.fast-check.ts +16 -0
  61. package/dist/blocks/lru-node/spec.yak +92 -0
  62. package/dist/blocks/memoize/impl.ts +60 -0
  63. package/dist/blocks/memoize/proof/manifest.json +8 -0
  64. package/dist/blocks/memoize/proof/tests.fast-check.ts +15 -0
  65. package/dist/blocks/memoize/spec.yak +91 -0
  66. package/dist/blocks/non-ascii-rejector/impl.ts +14 -0
  67. package/dist/blocks/non-ascii-rejector/proof/manifest.json +8 -0
  68. package/dist/blocks/non-ascii-rejector/proof/tests.fast-check.ts +20 -0
  69. package/dist/blocks/non-ascii-rejector/spec.yak +67 -0
  70. package/dist/blocks/nonempty-list-content/impl.ts +116 -0
  71. package/dist/blocks/nonempty-list-content/proof/manifest.json +8 -0
  72. package/dist/blocks/nonempty-list-content/proof/tests.fast-check.ts +20 -0
  73. package/dist/blocks/nonempty-list-content/spec.yak +88 -0
  74. package/dist/blocks/optional-whitespace/impl.ts +21 -0
  75. package/dist/blocks/optional-whitespace/proof/manifest.json +8 -0
  76. package/dist/blocks/optional-whitespace/proof/tests.fast-check.ts +20 -0
  77. package/dist/blocks/optional-whitespace/spec.yak +76 -0
  78. package/dist/blocks/peek-char/impl.ts +15 -0
  79. package/dist/blocks/peek-char/proof/manifest.json +8 -0
  80. package/dist/blocks/peek-char/proof/tests.fast-check.ts +20 -0
  81. package/dist/blocks/peek-char/spec.yak +76 -0
  82. package/dist/blocks/position-step/impl.ts +21 -0
  83. package/dist/blocks/position-step/proof/manifest.json +8 -0
  84. package/dist/blocks/position-step/proof/tests.fast-check.ts +20 -0
  85. package/dist/blocks/position-step/spec.yak +85 -0
  86. package/dist/blocks/queue-drain/impl.ts +74 -0
  87. package/dist/blocks/queue-drain/proof/manifest.json +8 -0
  88. package/dist/blocks/queue-drain/proof/tests.fast-check.ts +16 -0
  89. package/dist/blocks/queue-drain/spec.yak +119 -0
  90. package/dist/blocks/semver-component-parser/impl.ts +115 -0
  91. package/dist/blocks/semver-component-parser/proof/manifest.json +8 -0
  92. package/dist/blocks/semver-component-parser/proof/tests.fast-check.ts +19 -0
  93. package/dist/blocks/semver-component-parser/spec.yak +109 -0
  94. package/dist/blocks/signed-integer/impl.ts +40 -0
  95. package/dist/blocks/signed-integer/proof/manifest.json +8 -0
  96. package/dist/blocks/signed-integer/proof/tests.fast-check.ts +21 -0
  97. package/dist/blocks/signed-integer/spec.yak +84 -0
  98. package/dist/blocks/string-from-position/impl.ts +18 -0
  99. package/dist/blocks/string-from-position/proof/manifest.json +8 -0
  100. package/dist/blocks/string-from-position/proof/tests.fast-check.ts +20 -0
  101. package/dist/blocks/string-from-position/spec.yak +85 -0
  102. package/dist/blocks/timer-handle/impl.ts +66 -0
  103. package/dist/blocks/timer-handle/proof/manifest.json +8 -0
  104. package/dist/blocks/timer-handle/proof/tests.fast-check.ts +18 -0
  105. package/dist/blocks/timer-handle/spec.yak +67 -0
  106. package/dist/blocks/whitespace/impl.ts +20 -0
  107. package/dist/blocks/whitespace/proof/manifest.json +8 -0
  108. package/dist/blocks/whitespace/proof/tests.fast-check.ts +21 -0
  109. package/dist/blocks/whitespace/spec.yak +80 -0
  110. package/dist/bootstrap/expected-failures.json +4 -0
  111. package/dist/bootstrap/expected-roots.json +49890 -0
  112. package/dist/bootstrap/yakcc.registry.sqlite +0 -0
  113. package/dist/index.js +10826 -0
  114. package/dist/index.js.map +1 -0
  115. package/package.json +78 -0
@@ -0,0 +1,116 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // @decision DEC-SEEDS-NONEMPTYLIST-001: nonempty-list-content parses interior with at least one element.
3
+ // Status: implemented (WI-006)
4
+ // Rationale: The import-type declarations below declare the composition graph. Relative "./"
5
+ // imports are used so TypeScript can resolve the sibling block declarations; seedRegistry
6
+ // passes blockPatterns: ["./"] to parseBlock so extractComposition captures these as
7
+ // sub-block references in the provenance manifest. `import type` is used because
8
+ // strict-subset validates each block in an isolated single-file ts-morph project where
9
+ // sibling value imports resolve to `any` and fail no-untyped-imports. Type-only imports
10
+ // are unconditionally skipped by that rule.
11
+ // Composition graph — captured by extractComposition via the @yakcc/seeds/ builtin pattern.
12
+ // WI-T05-fix: import paths use "@yakcc/seeds/blocks/<name>" so the compile resolver's
13
+ // SUB_BLOCK_IMPORT_RE (which matches "@yakcc/seeds/" prefix) can extract sub-block deps.
14
+ // All imports are "import type" so the strict-subset validator skips them unconditionally
15
+ // and vitest/Vite erases them at transpile time (no runtime module resolution needed).
16
+ import type { bracket } from "@yakcc/seeds/blocks/bracket";
17
+ import type { comma } from "@yakcc/seeds/blocks/comma";
18
+ import type { integer } from "@yakcc/seeds/blocks/integer";
19
+ import type { optionalWhitespace } from "@yakcc/seeds/blocks/optional-whitespace";
20
+ import type { peekChar } from "@yakcc/seeds/blocks/peek-char";
21
+
22
+ // Suppress "imported but never used as a value" by surfacing type aliases.
23
+ type _Bracket = typeof bracket;
24
+ type _Comma = typeof comma;
25
+ type _Integer = typeof integer;
26
+ type _OptionalWhitespace = typeof optionalWhitespace;
27
+ type _PeekChar = typeof peekChar;
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Implementation
31
+ //
32
+ // Each section mirrors one sub-block's contract boundary exactly:
33
+ // optionalWhitespace(input, pos) → skip spaces/tabs; return new pos
34
+ // integer(input, pos) → parse digits; return [value, newPos]
35
+ // peekChar(input, pos) → return char at pos or null (no advance)
36
+ // comma(input, pos) → assert ','; return pos+1
37
+ // bracket(input, pos, ']') → assert ']'; return pos+1
38
+ // ---------------------------------------------------------------------------
39
+
40
+ export function nonemptyListContent(
41
+ input: string,
42
+ position: number,
43
+ ): readonly [ReadonlyArray<number>, number] {
44
+ if (position < 0) {
45
+ throw new RangeError(`Position ${position} is negative`);
46
+ }
47
+
48
+ const values: number[] = [];
49
+ let pos = position;
50
+
51
+ // optionalWhitespace: skip leading spaces/tabs before first integer.
52
+ while (pos < input.length && (input[pos] === " " || input[pos] === "\t")) {
53
+ pos++;
54
+ }
55
+
56
+ // integer: parse first integer (one or more digits required).
57
+ if (pos >= input.length || (input[pos] as string) < "0" || (input[pos] as string) > "9") {
58
+ throw new SyntaxError(
59
+ `Expected digit at position ${pos} but found ${JSON.stringify(input[pos] ?? "EOF")}`,
60
+ );
61
+ }
62
+ let first = 0;
63
+ while (pos < input.length) {
64
+ const c = input[pos] as string;
65
+ if (c < "0" || c > "9") break;
66
+ first = first * 10 + (c.charCodeAt(0) - 48);
67
+ pos++;
68
+ }
69
+ values.push(first);
70
+
71
+ // optionalWhitespace: skip whitespace after first integer.
72
+ while (pos < input.length && (input[pos] === " " || input[pos] === "\t")) {
73
+ pos++;
74
+ }
75
+
76
+ // peekChar + comma loop: consume each ", integer" pair while ',' is next.
77
+ while (pos < input.length && input[pos] === ",") {
78
+ // comma: assert ',' and advance.
79
+ pos++;
80
+
81
+ // optionalWhitespace: skip whitespace after comma.
82
+ while (pos < input.length && (input[pos] === " " || input[pos] === "\t")) {
83
+ pos++;
84
+ }
85
+
86
+ // integer: parse next integer.
87
+ if (pos >= input.length || (input[pos] as string) < "0" || (input[pos] as string) > "9") {
88
+ throw new SyntaxError(
89
+ `Expected digit after ',' at position ${pos} but found ${JSON.stringify(input[pos] ?? "EOF")}`,
90
+ );
91
+ }
92
+ let val = 0;
93
+ while (pos < input.length) {
94
+ const c = input[pos] as string;
95
+ if (c < "0" || c > "9") break;
96
+ val = val * 10 + (c.charCodeAt(0) - 48);
97
+ pos++;
98
+ }
99
+ values.push(val);
100
+
101
+ // optionalWhitespace: skip whitespace after integer.
102
+ while (pos < input.length && (input[pos] === " " || input[pos] === "\t")) {
103
+ pos++;
104
+ }
105
+ }
106
+
107
+ // bracket(input, pos, ']'): expect closing ']'.
108
+ if (pos >= input.length || input[pos] !== "]") {
109
+ throw new SyntaxError(
110
+ `Expected ']' at position ${pos} but found ${JSON.stringify(input[pos] ?? "EOF")}`,
111
+ );
112
+ }
113
+ pos++;
114
+
115
+ return [values, pos] as const;
116
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "artifacts": [
3
+ {
4
+ "kind": "property_tests",
5
+ "path": "tests.fast-check.ts"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Property tests for the nonempty-list-content 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
+ // nonempty-single
13
+ // nonempty-multiple
14
+ // nonempty-spaces
15
+ // nonempty-no-digit
16
+ // nonempty-no-close
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,88 @@
1
+ {
2
+ "name": "nonempty-list-content",
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": "Position immediately after '['."
13
+ }
14
+ ],
15
+ "outputs": [
16
+ {
17
+ "name": "result",
18
+ "type": "readonly [ReadonlyArray<number>, number]",
19
+ "description": "[parsedValues, newPosition] where newPosition is after the closing ']'."
20
+ }
21
+ ],
22
+ "preconditions": [],
23
+ "postconditions": [],
24
+ "invariants": [],
25
+ "effects": [],
26
+ "level": "L0",
27
+ "behavior": "Parse one or more comma-separated integers followed by ']'. Skips optional whitespace around each integer. Returns [values, newPosition] where newPosition is after the closing ']'. Throws SyntaxError if the first character is not a digit or if ']' is not found after all elements.",
28
+ "guarantees": [
29
+ {
30
+ "id": "pure",
31
+ "description": "Referentially transparent; no side effects."
32
+ },
33
+ {
34
+ "id": "nonempty",
35
+ "description": "Result array always contains at least one element."
36
+ },
37
+ {
38
+ "id": "closes",
39
+ "description": "newPosition is always after the closing ']' on success."
40
+ },
41
+ {
42
+ "id": "composition",
43
+ "description": "Implemented by composing optionalWhitespace, integer, peekChar, comma, and bracket."
44
+ }
45
+ ],
46
+ "errorConditions": [
47
+ {
48
+ "description": "No digit found where first integer is expected.",
49
+ "errorType": "SyntaxError"
50
+ },
51
+ {
52
+ "description": "Missing ']' after last integer.",
53
+ "errorType": "SyntaxError"
54
+ },
55
+ {
56
+ "description": "position < 0.",
57
+ "errorType": "RangeError"
58
+ }
59
+ ],
60
+ "nonFunctional": {
61
+ "time": "O(n)",
62
+ "space": "O(n)",
63
+ "purity": "pure",
64
+ "threadSafety": "safe"
65
+ },
66
+ "propertyTests": [
67
+ {
68
+ "id": "nonempty-single",
69
+ "description": "nonemptyListContent('1]', 0) returns [[1], 2]"
70
+ },
71
+ {
72
+ "id": "nonempty-multiple",
73
+ "description": "nonemptyListContent('1,2,3]', 0) returns [[1, 2, 3], 6]"
74
+ },
75
+ {
76
+ "id": "nonempty-spaces",
77
+ "description": "nonemptyListContent(' 42 ]', 0) returns [[42], 5]"
78
+ },
79
+ {
80
+ "id": "nonempty-no-digit",
81
+ "description": "nonemptyListContent('x]', 0) throws SyntaxError"
82
+ },
83
+ {
84
+ "id": "nonempty-no-close",
85
+ "description": "nonemptyListContent('1,2', 0) throws SyntaxError"
86
+ }
87
+ ]
88
+ }
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // @decision DEC-SEEDS-OPTWS-001: optional-whitespace is an explicit alias for whitespace.
3
+ // Status: implemented (WI-006)
4
+ // Rationale: Naming clarity for composition sites that want to communicate intent.
5
+ // "optional-whitespace" at a call site signals "whitespace may or may not be present here"
6
+ // vs a plain "whitespace" call which is ambiguous about whether whitespace is required.
7
+
8
+ export function optionalWhitespace(input: string, position: number): number {
9
+ if (position < 0) {
10
+ throw new RangeError(`Position ${position} is negative`);
11
+ }
12
+ let pos = position;
13
+ while (pos < input.length) {
14
+ const c = input[pos];
15
+ if (c !== " " && c !== "\t") {
16
+ break;
17
+ }
18
+ pos++;
19
+ }
20
+ return pos;
21
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "artifacts": [
3
+ {
4
+ "kind": "property_tests",
5
+ "path": "tests.fast-check.ts"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Property tests for the optional-whitespace 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
+ // optws-none
13
+ // optws-space
14
+ // optws-tab
15
+ // optws-eof
16
+ // optws-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,76 @@
1
+ {
2
+ "name": "optional-whitespace",
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 start position."
13
+ }
14
+ ],
15
+ "outputs": [
16
+ {
17
+ "name": "newPosition",
18
+ "type": "number",
19
+ "description": "Position after skipping any leading spaces or tabs."
20
+ }
21
+ ],
22
+ "preconditions": [],
23
+ "postconditions": [],
24
+ "invariants": [],
25
+ "effects": [],
26
+ "level": "L0",
27
+ "behavior": "Skip zero or more space (U+0020) or tab (U+0009) characters at position. Returns position unchanged if no whitespace is present. This is the same contract as whitespace but with an explicit name communicating optionality.",
28
+ "guarantees": [
29
+ {
30
+ "id": "pure",
31
+ "description": "Referentially transparent; no side effects."
32
+ },
33
+ {
34
+ "id": "monotonic",
35
+ "description": "Result is always >= position."
36
+ },
37
+ {
38
+ "id": "no-throw-on-eof",
39
+ "description": "Returns position unchanged when position >= input.length."
40
+ }
41
+ ],
42
+ "errorConditions": [
43
+ {
44
+ "description": "position < 0.",
45
+ "errorType": "RangeError"
46
+ }
47
+ ],
48
+ "nonFunctional": {
49
+ "time": "O(n)",
50
+ "space": "O(1)",
51
+ "purity": "pure",
52
+ "threadSafety": "safe"
53
+ },
54
+ "propertyTests": [
55
+ {
56
+ "id": "optws-none",
57
+ "description": "optionalWhitespace('abc', 0) returns 0"
58
+ },
59
+ {
60
+ "id": "optws-space",
61
+ "description": "optionalWhitespace(' x', 0) returns 2"
62
+ },
63
+ {
64
+ "id": "optws-tab",
65
+ "description": "optionalWhitespace('\\tx', 0) returns 1"
66
+ },
67
+ {
68
+ "id": "optws-eof",
69
+ "description": "optionalWhitespace('', 0) returns 0"
70
+ },
71
+ {
72
+ "id": "optws-negative",
73
+ "description": "optionalWhitespace('x', -1) throws RangeError"
74
+ }
75
+ ]
76
+ }
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // @decision DEC-SEEDS-PEEKCHAR-001: peek-without-advance is a fundamental parser combinator primitive.
3
+ // Status: implemented (WI-006)
4
+ // Rationale: Many parsing decisions are made by inspecting the next character without consuming it.
5
+ // Separating peek from consume avoids coupling lookahead to position advancement.
6
+
7
+ export function peekChar(input: string, position: number): string | null {
8
+ if (position < 0) {
9
+ throw new RangeError(`Position ${position} is negative`);
10
+ }
11
+ if (position >= input.length) {
12
+ return null;
13
+ }
14
+ return input[position] as string;
15
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "artifacts": [
3
+ {
4
+ "kind": "property_tests",
5
+ "path": "tests.fast-check.ts"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Property tests for the peek-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
+ // peek-char-first
13
+ // peek-char-eof
14
+ // peek-char-negative
15
+ // peek-char-empty-input
16
+ // peek-char-last
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,76 @@
1
+ {
2
+ "name": "peek-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 position to peek at."
13
+ }
14
+ ],
15
+ "outputs": [
16
+ {
17
+ "name": "char",
18
+ "type": "string | null",
19
+ "description": "Character at position, or null if at end of input."
20
+ }
21
+ ],
22
+ "preconditions": [],
23
+ "postconditions": [],
24
+ "invariants": [],
25
+ "effects": [],
26
+ "level": "L0",
27
+ "behavior": "Return the character at the given position without advancing. Returns null if position is at or beyond the end of input. Throws RangeError if position is negative.",
28
+ "guarantees": [
29
+ {
30
+ "id": "pure",
31
+ "description": "Referentially transparent; no side effects."
32
+ },
33
+ {
34
+ "id": "no-advance",
35
+ "description": "Does not modify position; caller position is unchanged."
36
+ },
37
+ {
38
+ "id": "null-at-eof",
39
+ "description": "Returns null exactly when position >= input.length."
40
+ }
41
+ ],
42
+ "errorConditions": [
43
+ {
44
+ "description": "position < 0.",
45
+ "errorType": "RangeError"
46
+ }
47
+ ],
48
+ "nonFunctional": {
49
+ "time": "O(1)",
50
+ "space": "O(1)",
51
+ "purity": "pure",
52
+ "threadSafety": "safe"
53
+ },
54
+ "propertyTests": [
55
+ {
56
+ "id": "peek-char-first",
57
+ "description": "peekChar('abc', 0) returns 'a'"
58
+ },
59
+ {
60
+ "id": "peek-char-eof",
61
+ "description": "peekChar('abc', 3) returns null"
62
+ },
63
+ {
64
+ "id": "peek-char-negative",
65
+ "description": "peekChar('abc', -1) throws RangeError"
66
+ },
67
+ {
68
+ "id": "peek-char-empty-input",
69
+ "description": "peekChar('', 0) returns null"
70
+ },
71
+ {
72
+ "id": "peek-char-last",
73
+ "description": "peekChar('abc', 2) returns 'c'"
74
+ }
75
+ ]
76
+ }
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // @decision DEC-SEEDS-POSSTEP-001: position advancement is an explicit operation, not implicit mutation.
3
+ // Status: implemented (WI-006)
4
+ // Rationale: Parser blocks work with immutable position values. positionStep returns
5
+ // the new position rather than mutating any state, keeping all blocks pure.
6
+
7
+ export function positionStep(position: number, n: number, inputLength: number): number {
8
+ if (position < 0) {
9
+ throw new RangeError(`position ${position} is negative`);
10
+ }
11
+ if (n < 0) {
12
+ throw new RangeError(`step ${n} is negative`);
13
+ }
14
+ const next = position + n;
15
+ if (next > inputLength) {
16
+ throw new RangeError(
17
+ `position ${position} + step ${n} = ${next} exceeds input length ${inputLength}`,
18
+ );
19
+ }
20
+ return next;
21
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "artifacts": [
3
+ {
4
+ "kind": "property_tests",
5
+ "path": "tests.fast-check.ts"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Property tests for the position-step 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
+ // pos-step-basic
13
+ // pos-step-to-end
14
+ // pos-step-overrun
15
+ // pos-step-negative-n
16
+ // pos-step-zero
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": "position-step",
3
+ "inputs": [
4
+ {
5
+ "name": "position",
6
+ "type": "number",
7
+ "description": "Current zero-based position."
8
+ },
9
+ {
10
+ "name": "n",
11
+ "type": "number",
12
+ "description": "Number of characters to advance."
13
+ },
14
+ {
15
+ "name": "inputLength",
16
+ "type": "number",
17
+ "description": "Total input length for bounds check."
18
+ }
19
+ ],
20
+ "outputs": [
21
+ {
22
+ "name": "newPosition",
23
+ "type": "number",
24
+ "description": "position + n, validated against inputLength."
25
+ }
26
+ ],
27
+ "preconditions": [],
28
+ "postconditions": [],
29
+ "invariants": [],
30
+ "effects": [],
31
+ "level": "L0",
32
+ "behavior": "Return position + n after validating that the result does not exceed inputLength. Throws RangeError if n < 0, position < 0, or position + n > inputLength.",
33
+ "guarantees": [
34
+ {
35
+ "id": "pure",
36
+ "description": "Referentially transparent; no side effects."
37
+ },
38
+ {
39
+ "id": "monotonic",
40
+ "description": "Result is always >= position when n >= 0."
41
+ }
42
+ ],
43
+ "errorConditions": [
44
+ {
45
+ "description": "n < 0.",
46
+ "errorType": "RangeError"
47
+ },
48
+ {
49
+ "description": "position < 0.",
50
+ "errorType": "RangeError"
51
+ },
52
+ {
53
+ "description": "position + n > inputLength.",
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": "pos-step-basic",
66
+ "description": "positionStep(0, 3, 5) returns 3"
67
+ },
68
+ {
69
+ "id": "pos-step-to-end",
70
+ "description": "positionStep(2, 3, 5) returns 5"
71
+ },
72
+ {
73
+ "id": "pos-step-overrun",
74
+ "description": "positionStep(3, 3, 5) throws RangeError"
75
+ },
76
+ {
77
+ "id": "pos-step-negative-n",
78
+ "description": "positionStep(0, -1, 5) throws RangeError"
79
+ },
80
+ {
81
+ "id": "pos-step-zero",
82
+ "description": "positionStep(2, 0, 5) returns 2"
83
+ }
84
+ ]
85
+ }