functionalscript 0.38.0 → 0.40.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 (137) hide show
  1. package/README.md +1 -1
  2. package/fjs/bnf/descent/module.f.d.ts +43 -2
  3. package/fjs/bnf/descent/module.f.js +37 -12
  4. package/fjs/bnf/descent/proof.f.d.ts +1 -0
  5. package/fjs/bnf/descent/proof.f.js +73 -32
  6. package/fjs/bnf/ll1/module.f.js +2 -2
  7. package/fjs/bnf/token_symbol/module.f.d.ts +38 -0
  8. package/fjs/bnf/token_symbol/module.f.js +48 -0
  9. package/fjs/bnf/token_symbol/proof.f.d.ts +10 -0
  10. package/fjs/bnf/token_symbol/proof.f.js +37 -0
  11. package/fjs/cas/cli/module.f.js +6 -9
  12. package/fjs/cas/evo/module.f.d.ts +77 -13
  13. package/fjs/cas/evo/module.f.js +130 -18
  14. package/fjs/cas/evo/proof.f.d.ts +7 -0
  15. package/fjs/cas/evo/proof.f.js +120 -1
  16. package/fjs/cas/module.f.js +64 -37
  17. package/fjs/cas/proof.f.d.ts +3 -9
  18. package/fjs/cas/proof.f.js +94 -74
  19. package/fjs/ci/bun/module.f.js +3 -3
  20. package/fjs/ci/common/module.f.d.ts +1 -2
  21. package/fjs/ci/common/module.f.js +1 -5
  22. package/fjs/ci/config/module.f.d.ts +11 -8
  23. package/fjs/ci/config/module.f.js +24 -9
  24. package/fjs/ci/deno/module.f.js +3 -3
  25. package/fjs/ci/module.f.js +13 -7
  26. package/fjs/ci/nix/module.f.d.ts +61 -0
  27. package/fjs/ci/nix/module.f.js +92 -0
  28. package/fjs/ci/nix/proof.f.d.ts +23 -0
  29. package/fjs/ci/nix/proof.f.js +109 -0
  30. package/fjs/ci/node/module.f.d.ts +23 -1
  31. package/fjs/ci/node/module.f.js +58 -12
  32. package/fjs/ci/proof.f.d.ts +2 -0
  33. package/fjs/ci/proof.f.js +49 -9
  34. package/fjs/cli/module.f.js +2 -2
  35. package/fjs/common/monoid/module.f.d.ts +46 -2
  36. package/fjs/common/monoid/module.f.js +44 -0
  37. package/fjs/common/monoid/proof.f.d.ts +5 -0
  38. package/fjs/common/monoid/proof.f.js +27 -1
  39. package/fjs/dev/module.f.d.ts +1 -0
  40. package/fjs/dev/module.f.js +38 -27
  41. package/fjs/dev/update/module.f.d.ts +11 -0
  42. package/fjs/dev/update/module.f.js +20 -0
  43. package/fjs/dev/update/proof.f.d.ts +7 -0
  44. package/fjs/dev/update/proof.f.js +35 -0
  45. package/fjs/djs/ast/module.f.d.ts +47 -0
  46. package/fjs/djs/ast/module.f.js +9 -0
  47. package/fjs/djs/module.f.js +5 -8
  48. package/fjs/djs/parser/module.f.d.ts +3 -0
  49. package/fjs/djs/parser/module.f.js +17 -0
  50. package/fjs/djs/tokenizer/module.f.js +2 -2
  51. package/fjs/djs/tokenizer/proof.f.d.ts +1 -0
  52. package/fjs/djs/tokenizer/proof.f.js +70 -16
  53. package/fjs/djs/transpiler/module.f.js +6 -6
  54. package/fjs/effects/eff/module.f.d.ts +72 -0
  55. package/fjs/effects/eff/module.f.js +46 -0
  56. package/fjs/effects/eff/proof.f.d.ts +11 -0
  57. package/fjs/effects/eff/proof.f.js +58 -0
  58. package/fjs/effects/list/module.f.d.ts +11 -5
  59. package/fjs/effects/list/module.f.js +2 -1
  60. package/fjs/effects/memory/proof.f.js +7 -8
  61. package/fjs/effects/module.f.d.ts +334 -52
  62. package/fjs/effects/module.f.js +253 -45
  63. package/fjs/effects/node/memory/proof.js +3 -2
  64. package/fjs/effects/node/module.d.ts +3 -3
  65. package/fjs/effects/node/module.f.d.ts +18 -8
  66. package/fjs/effects/node/module.f.js +31 -11
  67. package/fjs/effects/node/module.js +12 -12
  68. package/fjs/effects/node/proof.f.d.ts +1 -0
  69. package/fjs/effects/node/proof.f.js +32 -11
  70. package/fjs/effects/node/virtual/module.f.js +1 -1
  71. package/fjs/effects/proof.f.d.ts +32 -6
  72. package/fjs/effects/proof.f.js +131 -39
  73. package/fjs/emergent_testing/all.test.js +2 -1
  74. package/fjs/emergent_testing/module.f.d.ts +3 -3
  75. package/fjs/emergent_testing/module.f.js +63 -49
  76. package/fjs/emergent_testing/proof.f.d.ts +4 -0
  77. package/fjs/emergent_testing/proof.f.js +54 -21
  78. package/fjs/emergent_testing/scenarios/thenable.pass.js +1 -1
  79. package/fjs/fsc/module.f.js +4 -4
  80. package/fjs/fsm/module.f.js +1 -1
  81. package/fjs/js/tokenizer/module.f.d.ts +1 -0
  82. package/fjs/js/tokenizer/module.f.js +13 -6
  83. package/fjs/{cas/mcp → mcp/cas}/module.f.d.ts +3 -25
  84. package/fjs/{cas/mcp → mcp/cas}/module.f.js +26 -64
  85. package/fjs/mcp/evo/module.f.d.ts +27 -0
  86. package/fjs/mcp/evo/module.f.js +94 -0
  87. package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.d.ts +2 -0
  88. package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.js +40 -9
  89. package/fjs/mcp/module.f.d.ts +54 -237
  90. package/fjs/mcp/module.f.js +55 -258
  91. package/fjs/mcp/proof.f.d.ts +43 -32
  92. package/fjs/mcp/proof.f.js +509 -200
  93. package/fjs/media/nix/module.f.d.ts +30 -0
  94. package/fjs/media/nix/module.f.js +166 -0
  95. package/fjs/media/nix/proof.f.d.ts +32 -0
  96. package/fjs/media/nix/proof.f.js +127 -0
  97. package/fjs/media/type/module.f.js +2 -2
  98. package/fjs/media/type/proof.f.js +7 -10
  99. package/fjs/module.f.js +5 -9
  100. package/fjs/protocol/json_rpc/module.f.d.ts +114 -0
  101. package/fjs/{media/json/rpc → protocol/json_rpc}/module.f.js +3 -3
  102. package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.js +3 -3
  103. package/fjs/protocol/mcp/module.f.d.ts +239 -0
  104. package/fjs/protocol/mcp/module.f.js +272 -0
  105. package/fjs/protocol/mcp/proof.f.d.ts +34 -0
  106. package/fjs/protocol/mcp/proof.f.js +208 -0
  107. package/fjs/{mcp → protocol/mcp}/stdio/module.f.d.ts +7 -7
  108. package/fjs/{mcp → protocol/mcp}/stdio/module.f.js +22 -22
  109. package/fjs/{mcp → protocol/mcp}/stdio/proof.f.js +9 -9
  110. package/fjs/types/bigint/module.f.js +4 -3
  111. package/fjs/types/bit_vec/module.f.js +5 -15
  112. package/fjs/types/btree/remove/module.f.d.ts +5 -0
  113. package/fjs/types/btree/remove/module.f.js +12 -0
  114. package/fjs/types/list/module.f.d.ts +21 -0
  115. package/fjs/types/list/module.f.js +17 -0
  116. package/fjs/types/list/proof.f.d.ts +1 -0
  117. package/fjs/types/list/proof.f.js +24 -1
  118. package/fjs/types/number/module.f.js +2 -1
  119. package/fjs/types/range_map/module.f.d.ts +14 -13
  120. package/fjs/types/range_map/module.f.js +18 -13
  121. package/fjs/types/range_map/proof.f.js +26 -39
  122. package/fjs/types/range_set/module.f.d.ts +5 -0
  123. package/fjs/types/range_set/module.f.js +16 -0
  124. package/fjs/types/range_set/proof.f.d.ts +1 -0
  125. package/fjs/types/range_set/proof.f.js +19 -0
  126. package/fjs/types/string/module.f.js +10 -2
  127. package/fjs/website/module.f.js +2 -3
  128. package/package.json +5 -5
  129. package/fjs/cas/evo/mcp/module.f.d.ts +0 -19
  130. package/fjs/cas/evo/mcp/module.f.js +0 -56
  131. package/fjs/cas/mcp/proof.f.d.ts +0 -45
  132. package/fjs/cas/mcp/proof.f.js +0 -545
  133. package/fjs/ci/playwright/module.f.d.ts +0 -2
  134. package/fjs/ci/playwright/module.f.js +0 -25
  135. package/fjs/media/json/rpc/module.f.d.ts +0 -114
  136. /package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.d.ts +0 -0
  137. /package/fjs/{mcp → protocol/mcp}/stdio/proof.f.d.ts +0 -0
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Monoids: the `Monoid<T>` algebraic structure (identity plus associative
3
- * binary operation) and `repeat`, which applies the operation `n` times using
4
- * exponentiation by squaring.
3
+ * binary operation), `repeat`, which applies the operation `n` times using
4
+ * exponentiation by squaring, and `fold`, which applies it across every element
5
+ * of a list.
5
6
  *
6
7
  * @module
7
8
  */
8
9
  import type { Fold, Reduce } from '../../types/function/operator/module.f.ts';
10
+ import { type List } from '../../types/list/module.f.ts';
9
11
  /**
10
12
  * Represents a monoid, an algebraic structure with a binary operation
11
13
  * and an identity (neutral) element.
@@ -80,3 +82,45 @@ export type Monoid<T> = {
80
82
  * ```
81
83
  */
82
84
  export declare const repeat: <T>({ identity, operation }: Monoid<T>) => Fold<bigint, T>;
85
+ /**
86
+ * Reduces a `List<T>` with the monoid's associative `operation`, seeded at its
87
+ * `identity`. An empty list folds to `identity`.
88
+ *
89
+ * `fold` is the reduction companion of {@link repeat}: where `repeat` applies
90
+ * the operation a fixed number of times to a single element, `fold` applies it
91
+ * across every element of a list. Together they let list reductions such as
92
+ * `sum`, `product`, and string `concat` be expressed directly as monoid folds,
93
+ * so the identity paired with each operation is stated once at the call site
94
+ * instead of hand-seeding a raw `reduce`.
95
+ *
96
+ * Like `repeat`, `fold` applies the operation **accumulator-first**:
97
+ * `operation(accumulator)(element)`, seeded at `identity`, so
98
+ * `[a, b, c]` folds to `((identity op a) op b) op c`. Left-to-right order is
99
+ * therefore preserved and `fold` is correct for non-commutative monoids (e.g.
100
+ * string concatenation as `a => b => a + b`). `list.reduce` calls its reducer
101
+ * element-first, so the operation is `flip`ped before it is handed over.
102
+ *
103
+ * @template T The type of the elements in the monoid.
104
+ * @param monoid The monoid structure, including the identity and binary operation.
105
+ * @returns A function that reduces a `List<T>` to a single `T`.
106
+ *
107
+ * @example
108
+ *
109
+ * ```ts
110
+ * const add: Monoid<number> = {
111
+ * identity: 0,
112
+ * operation: a => b => a + b,
113
+ * };
114
+ *
115
+ * fold(add)([1, 2, 3, 4]) // 10
116
+ * fold(add)([]) // 0
117
+ *
118
+ * const concat: Monoid<string> = {
119
+ * identity: '',
120
+ * operation: a => b => a + b,
121
+ * };
122
+ *
123
+ * fold(concat)(['a', 'b', 'c']) // 'abc' — order preserved
124
+ * ```
125
+ */
126
+ export declare const fold: <T>({ identity, operation }: Monoid<T>) => (list: List<T>) => T;
@@ -1,3 +1,5 @@
1
+ import { reduce } from '../../types/list/module.f.js';
2
+ import { flip } from '../../types/function/module.f.js';
1
3
  /**
2
4
  * Repeats a monoid operation `n` times on the given element `a`.
3
5
  * This function efficiently performs the operation using exponentiation by squaring.
@@ -42,3 +44,45 @@ export const repeat = ({ identity, operation }) => n => a => {
42
44
  ai = operation(ai)(ai);
43
45
  }
44
46
  };
47
+ /**
48
+ * Reduces a `List<T>` with the monoid's associative `operation`, seeded at its
49
+ * `identity`. An empty list folds to `identity`.
50
+ *
51
+ * `fold` is the reduction companion of {@link repeat}: where `repeat` applies
52
+ * the operation a fixed number of times to a single element, `fold` applies it
53
+ * across every element of a list. Together they let list reductions such as
54
+ * `sum`, `product`, and string `concat` be expressed directly as monoid folds,
55
+ * so the identity paired with each operation is stated once at the call site
56
+ * instead of hand-seeding a raw `reduce`.
57
+ *
58
+ * Like `repeat`, `fold` applies the operation **accumulator-first**:
59
+ * `operation(accumulator)(element)`, seeded at `identity`, so
60
+ * `[a, b, c]` folds to `((identity op a) op b) op c`. Left-to-right order is
61
+ * therefore preserved and `fold` is correct for non-commutative monoids (e.g.
62
+ * string concatenation as `a => b => a + b`). `list.reduce` calls its reducer
63
+ * element-first, so the operation is `flip`ped before it is handed over.
64
+ *
65
+ * @template T The type of the elements in the monoid.
66
+ * @param monoid The monoid structure, including the identity and binary operation.
67
+ * @returns A function that reduces a `List<T>` to a single `T`.
68
+ *
69
+ * @example
70
+ *
71
+ * ```ts
72
+ * const add: Monoid<number> = {
73
+ * identity: 0,
74
+ * operation: a => b => a + b,
75
+ * };
76
+ *
77
+ * fold(add)([1, 2, 3, 4]) // 10
78
+ * fold(add)([]) // 0
79
+ *
80
+ * const concat: Monoid<string> = {
81
+ * identity: '',
82
+ * operation: a => b => a + b,
83
+ * };
84
+ *
85
+ * fold(concat)(['a', 'b', 'c']) // 'abc' — order preserved
86
+ * ```
87
+ */
88
+ export const fold = ({ identity, operation }) => reduce(flip(operation))(identity);
@@ -1,4 +1,9 @@
1
1
  export declare const proof: {
2
2
  numberAdd: () => void;
3
3
  stringConcat: () => void;
4
+ fold: {
5
+ nonEmpty: () => void;
6
+ order: () => void;
7
+ empty: () => void;
8
+ };
4
9
  };
@@ -1,4 +1,4 @@
1
- import { repeat } from "./module.f.js";
1
+ import { repeat, fold } from "./module.f.js";
2
2
  import { assertEq } from '../../asserts/module.f.js';
3
3
  export const proof = {
4
4
  numberAdd: () => {
@@ -18,5 +18,31 @@ export const proof = {
18
18
  };
19
19
  const resultConcat = repeat(concat)(3n)('ha'); // 'hahaha'
20
20
  assertEq(resultConcat, 'hahaha');
21
+ },
22
+ fold: {
23
+ nonEmpty: () => {
24
+ const add = {
25
+ identity: 0,
26
+ operation: a => b => a + b,
27
+ };
28
+ assertEq(fold(add)([1, 2, 3, 4]), 10);
29
+ },
30
+ order: () => {
31
+ // a non-commutative monoid: `fold` must preserve left-to-right order
32
+ // (`((('' + 'a') + 'b') + 'c')`), not reverse it to `'cba'`.
33
+ const concat = {
34
+ identity: '',
35
+ operation: a => b => a + b,
36
+ };
37
+ assertEq(fold(concat)(['a', 'b', 'c']), 'abc');
38
+ },
39
+ empty: () => {
40
+ const concat = {
41
+ identity: '',
42
+ operation: a => b => a + b,
43
+ };
44
+ // an empty list folds to the identity
45
+ assertEq(fold(concat)([]), '');
46
+ },
21
47
  }
22
48
  };
@@ -42,4 +42,5 @@ export type LoadModuleOperations = Access | Import | All | Readdir;
42
42
  export declare const loadModuleMap: (env: Env) => Effect<LoadModuleOperations, ModuleMap>;
43
43
  export declare const proof: {
44
44
  isSourceFile: () => void;
45
+ allFilesSkipsNodeModules: () => void;
45
46
  };
@@ -6,9 +6,10 @@
6
6
  import { all, import_, readdir } from '../effects/node/module.f.js';
7
7
  import { cmp as strCmp } from '../types/string/module.f.js';
8
8
  import { unwrap } from '../types/result/module.f.js';
9
- import { pure } from '../effects/module.f.js';
9
+ import { pure, step } from '../effects/module.f.js';
10
10
  import { join, relativize, toPosix } from '../path/module.f.js';
11
- import { assert } from '../asserts/module.f.js';
11
+ import { assert, assertEq } from '../asserts/module.f.js';
12
+ import { emptyState, virtual } from '../effects/node/virtual/module.f.js';
12
13
  /**
13
14
  * Returns `true` if the file should be loaded for proof discovery.
14
15
  *
@@ -25,32 +26,33 @@ export const shouldLoad = (s) => s.endsWith('.f.ts') || s.endsWith('.f.js') ||
25
26
  s.endsWith('proof.mts') || s.endsWith('proof.mjs');
26
27
  const isSourceFile = (path) => path.endsWith('.js') || path.endsWith('.ts') || path.endsWith('.mts') || path.endsWith('.mjs');
27
28
  const allFiles = (s, predicate) => {
28
- const load = (p) => readdir(p, {})
29
- .step(d => {
30
- let result = [];
31
- for (const i of unwrap(d)) {
32
- const { name } = i;
33
- if (name.startsWith('.')) {
34
- continue;
35
- }
36
- const file = join(p, name);
37
- if (!i.isFile) {
38
- if (name === 'node_modules') {
29
+ const load = (p) => {
30
+ const x0 = step(readdir(p, {}), d => {
31
+ let result = [];
32
+ for (const i of unwrap(d)) {
33
+ const { name } = i;
34
+ if (name.startsWith('.')) {
39
35
  continue;
40
36
  }
41
- result = [...result, load(file)];
42
- continue;
43
- }
44
- if (predicate(file)) {
45
- result = [...result, pure([file])];
37
+ const file = join(p, name);
38
+ if (!i.isFile) {
39
+ if (name === 'node_modules') {
40
+ continue;
41
+ }
42
+ result = [...result, load(file)];
43
+ continue;
44
+ }
45
+ if (predicate(file)) {
46
+ result = [...result, pure([file])];
47
+ }
46
48
  }
47
- }
48
- return all(...result);
49
- })
50
- .step(v => pure(v.flat()));
49
+ return all(...result);
50
+ });
51
+ return step(x0, v => pure(v.flat()));
52
+ };
51
53
  return load(s);
52
54
  };
53
- const loadFile = (f) => import_(f).step(r => pure([[f, unwrap(r)]]));
55
+ const loadFile = (f) => step(import_(f), r => pure([[f, unwrap(r)]]));
54
56
  const { fromEntries } = Object;
55
57
  /**
56
58
  * Discovers all source files under `INIT_CWD` (or `.` if unset) that match
@@ -71,12 +73,11 @@ export const loadModuleMap = (env) => {
71
73
  const s = initCwd === undefined ? '.' : toPosix(initCwd);
72
74
  const prefix = s === '.' ? '' : s;
73
75
  // TODO: there are multiple `all` effects here,
74
- // we should consider optimize them by ALIQ technique or something similar.
76
+ // we should consider optimizing them by ALIQ technique or something similar.
75
77
  // For example, we should be able to write it like `allFiles(s).flatMap(loadFile)`,
76
78
  // then an effect runner can batch all file loading operations together.
77
- return allFiles(s, shouldLoad)
78
- .step(files => all(...files.map(loadFile)))
79
- .step(entries => pure(fromEntries(entries
79
+ const x0 = step(allFiles(s, shouldLoad), files => all(...files.map(loadFile)));
80
+ return step(x0, entries => pure(fromEntries(entries
80
81
  .flat()
81
82
  .map(([k, v]) => [relativize(prefix, k), v])
82
83
  .toSorted(([a], [b]) => strCmp(a)(b)))));
@@ -90,4 +91,14 @@ export const proof = {
90
91
  assert(!isSourceFile('readme.md'));
91
92
  assert(!isSourceFile('module.json'));
92
93
  },
94
+ allFilesSkipsNodeModules: () => {
95
+ // `node_modules` is skipped without descending into it, even though
96
+ // it contains a file that would otherwise match the predicate.
97
+ const root = {
98
+ 'node_modules': { 'pkg.f.ts': [] },
99
+ 'a.f.ts': [],
100
+ };
101
+ const [, result] = virtual({ ...emptyState, root })(allFiles('.', shouldLoad));
102
+ assertEq(result.join(','), './a.f.ts');
103
+ },
93
104
  };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Synchronizes local development configuration generated from canonical repository files.
3
+ *
4
+ * @module
5
+ */
6
+ import { type Effect } from '../../effects/module.f.ts';
7
+ import { type Mkdir, type NodeProgram, type ReadFile, type WriteFile } from '../../effects/node/module.f.ts';
8
+ /** Regenerates VS Code's local MCP configuration from the canonical Copilot configuration. */
9
+ export declare const syncMcp: () => Effect<Mkdir | ReadFile | WriteFile, void>;
10
+ /** Runs all local development configuration generators. */
11
+ export declare const main: NodeProgram;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Synchronizes local development configuration generated from canonical repository files.
3
+ *
4
+ * @module
5
+ */
6
+ import { history, historyStep, mapStep, step } from '../../effects/module.f.js';
7
+ import { mkdir, readUtf8File, writeUtf8File } from '../../effects/node/module.f.js';
8
+ import { unwrap } from '../../types/result/module.f.js';
9
+ const source = '.copilot/mcp.json';
10
+ const targetDirectory = '.vscode';
11
+ const target = '.vscode/mcp.json';
12
+ /** Regenerates VS Code's local MCP configuration from the canonical Copilot configuration. */
13
+ export const syncMcp = () => {
14
+ const sourceText = history(mapStep(readUtf8File(source), unwrap));
15
+ const targetDirectoryReady = historyStep(sourceText, () => mapStep(mkdir(targetDirectory, { recursive: true }), unwrap));
16
+ const targetWritten = step(targetDirectoryReady, ([, text]) => writeUtf8File(target, text));
17
+ return mapStep(targetWritten, unwrap);
18
+ };
19
+ /** Runs all local development configuration generators. */
20
+ export const main = () => mapStep(syncMcp(), () => 0);
@@ -0,0 +1,7 @@
1
+ export declare const proof: {
2
+ syncMcp: () => void;
3
+ main: () => void;
4
+ throw: {
5
+ missingSource: () => readonly [import("../../effects/node/virtual/module.f.ts").State, void];
6
+ };
7
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Proofs for local development configuration generation.
3
+ *
4
+ * @module
5
+ */
6
+ import { assert, assertEq } from '../../asserts/module.f.js';
7
+ import { utf8 } from '../../text/module.f.js';
8
+ import { readUtf8File } from '../../effects/node/module.f.js';
9
+ import { defaultNodeProgramOptions, emptyState, virtual } from '../../effects/node/virtual/module.f.js';
10
+ import { main, syncMcp } from './module.f.js';
11
+ import { step } from '../../effects/module.f.js';
12
+ const mcp = '{"servers":{}}';
13
+ const initial = {
14
+ ...emptyState,
15
+ root: {
16
+ '.copilot': {
17
+ 'mcp.json': [utf8(mcp)],
18
+ },
19
+ },
20
+ };
21
+ export const proof = {
22
+ syncMcp: () => {
23
+ const generatedMcp = step(syncMcp(), () => readUtf8File('.vscode/mcp.json'));
24
+ const [, [tag, result]] = virtual(initial)(generatedMcp);
25
+ assert(tag === 'ok', result);
26
+ assertEq(result, mcp);
27
+ },
28
+ main: () => {
29
+ const [, result] = virtual(initial)(main(defaultNodeProgramOptions));
30
+ assertEq(result, 0);
31
+ },
32
+ throw: {
33
+ missingSource: () => virtual(emptyState)(syncMcp()),
34
+ },
35
+ };
@@ -4,12 +4,59 @@
4
4
  * @module
5
5
  */
6
6
  import type { Primitive, Array, Unknown } from '../module.f.ts';
7
+ /**
8
+ * A parsed DJS module: its imported module specifiers, in source order, and
9
+ * its body.
10
+ *
11
+ * The specifier list indexes `['aref', i]`.
12
+ */
7
13
  export type AstModule = [readonly string[], AstBody];
14
+ /** A value in a module body: a primitive, a reference, an array, or an object. */
8
15
  export type AstConst = Primitive | AstModuleRef | AstArray | AstObject;
16
+ /**
17
+ * A reference to a value defined outside this `AstConst`.
18
+ *
19
+ * - `['aref', i]` — the `i`-th argument of the body, i.e. the `i`-th imported
20
+ * module of the enclosing `AstModule`.
21
+ * - `['cref', i]` — the `i`-th entry of the enclosing `AstBody`.
22
+ *
23
+ * Both indices are absolute and zero-based, **not** offsets from the
24
+ * referencing entry: in the body `[a, b, ['cref', 0]]` the reference resolves
25
+ * to `a`, not to the nearest preceding entry `b`.
26
+ *
27
+ * A `cref` index must be smaller than the index of the entry holding it —
28
+ * `run` evaluates a body left to right, so a reference to the current or a
29
+ * later entry is unsatisfiable. It is not rejected: it resolves to the most
30
+ * recently evaluated entry instead.
31
+ */
9
32
  export type AstModuleRef = ['aref' | 'cref', number];
33
+ /** An array value; its elements are evaluated in order. */
10
34
  export type AstArray = ['array', readonly AstConst[]];
35
+ /** An object value, keyed by property name. */
11
36
  export type AstObject = {
12
37
  readonly [k in string]?: AstConst;
13
38
  };
39
+ /**
40
+ * The constants of a module body, in declaration order. The **last** entry is
41
+ * the value the module yields; the preceding entries exist to be named by
42
+ * `['cref', i]`.
43
+ *
44
+ * A body describes the function
45
+ *
46
+ * ```js
47
+ * (...args) => { const c0 = ...; const c1 = ...; return <last> }
48
+ * ```
49
+ *
50
+ * where `args` are the imported modules.
51
+ */
14
52
  export type AstBody = readonly AstConst[];
53
+ /**
54
+ * Evaluates a module body against its imported modules and returns the value
55
+ * the module yields — the last entry of the body.
56
+ *
57
+ * Entries are evaluated left to right, so a `cref` resolves to an already
58
+ * evaluated entry. A reference is shared, not copied: two properties holding
59
+ * the same `['cref', i]` deserialize to the same object, which is what lets a
60
+ * DJS module denote a graph rather than a tree.
61
+ */
15
62
  export declare const run: (body: AstBody) => (args: Array) => Unknown;
@@ -42,6 +42,15 @@ const toDjs = state => ast => {
42
42
  }
43
43
  }
44
44
  };
45
+ /**
46
+ * Evaluates a module body against its imported modules and returns the value
47
+ * the module yields — the last entry of the body.
48
+ *
49
+ * Entries are evaluated left to right, so a `cref` resolves to an already
50
+ * evaluated entry. A reference is shared, not copied: two properties holding
51
+ * the same `['cref', i]` deserialize to the same object, which is what lets a
52
+ * DJS module denote a graph rather than a tree.
53
+ */
45
54
  export const run = body => args => {
46
55
  const state = fold(foldOp)({ body, args, consts: null })(body);
47
56
  return last(null)(state.consts);
@@ -1,25 +1,22 @@
1
1
  import { transpile } from './transpiler/module.f.js';
2
2
  import { stringify, stringifyAsTree } from './serializer/module.f.js';
3
3
  import { sort } from '../types/object/module.f.js';
4
- import { pure } from '../effects/module.f.js';
4
+ import { pure, step } from '../effects/module.f.js';
5
5
  import { writeUtf8File, error, } from '../effects/node/module.f.js';
6
6
  export const compile = args => {
7
7
  if (args.length < 2) {
8
- return error('Error: Requires 2 or more arguments')
9
- .step(() => pure(1));
8
+ return step(error('Error: Requires 2 or more arguments'), () => pure(1));
10
9
  }
11
10
  const inputFileName = args[0];
12
11
  const outputFileName = args[1];
13
- return transpile(inputFileName).step((result) => {
12
+ return step(transpile(inputFileName), (result) => {
14
13
  if (result[0] === 'error') {
15
14
  const metadata = result[1].metadata;
16
- return error(`${metadata?.path}:${metadata?.line}:${metadata?.column} - error: ${result[1].message}`)
17
- .step(() => pure(0));
15
+ return step(error(`${metadata?.path}:${metadata?.line}:${metadata?.column} - error: ${result[1].message}`), () => pure(0));
18
16
  }
19
17
  const content = outputFileName.endsWith('.json')
20
18
  ? stringifyAsTree(sort)(result[1])
21
19
  : stringify(sort)(result[1]);
22
- return writeUtf8File(outputFileName, content)
23
- .step(() => pure(0));
20
+ return step(writeUtf8File(outputFileName, content), () => pure(0));
24
21
  });
25
22
  };
@@ -17,4 +17,7 @@ export declare const proof: {
17
17
  pushKey: {
18
18
  nonObjectTop: () => void;
19
19
  };
20
+ endArray: {
21
+ nonArrayTop: () => void;
22
+ };
20
23
  };
@@ -380,4 +380,21 @@ export const proof = {
380
380
  assertEq(result.state, 'error');
381
381
  },
382
382
  },
383
+ endArray: {
384
+ // `endArray` is only ever invoked while `state.top` is an array (the
385
+ // `'['`/`'[v'`/`'[,'` value-states guarantee it), so its non-array
386
+ // guard is a defensive branch unreachable through `parseFromTokens`.
387
+ // Call it directly to cover that branch.
388
+ nonArrayTop: () => {
389
+ const state = {
390
+ state: 'exportValue',
391
+ module: { refs: null, modules: null, consts: null },
392
+ valueState: '[,',
393
+ top: null,
394
+ stack: null,
395
+ };
396
+ const result = endArray(state);
397
+ assertEq(result.state, 'result');
398
+ },
399
+ },
383
400
  };
@@ -364,7 +364,7 @@ export const tokenizeString = s => {
364
364
  }
365
365
  const m = descentParser(jsGrammar());
366
366
  const cpm = codePointsWithMetadata('')(cp);
367
- const [ast, ok, len] = m('', cpm);
367
+ const { ast, success: ok, idx: len } = m('', cpm);
368
368
  if (!ok || len !== cp.length)
369
369
  return 'error';
370
370
  const flatTokens = toArray(getTokensFromAstRule(ast));
@@ -394,7 +394,7 @@ export const tokenizeJs = input => path => {
394
394
  return [{ token: { kind: 'eof' }, metadata: initial }];
395
395
  const m = descentParser(jsGrammar());
396
396
  const cpm = codePointsWithMetadata(path)(cp);
397
- const [ast, ok, len] = m('', cpm);
397
+ const { ast, success: ok, idx: len } = m('', cpm);
398
398
  const finalMetadata = fold(advanceMetadata)(initial)(cp);
399
399
  if (!ok || len !== cp.length) {
400
400
  const errorMetadata = len < cpm.length ? cpm[len][1] : finalMetadata;
@@ -8,6 +8,7 @@ export declare const proof: {
8
8
  keywords: (() => void)[];
9
9
  comments: (() => void)[];
10
10
  metadata: (() => void)[];
11
+ errorPosition: (() => void)[];
11
12
  djsTokenize: (() => void)[];
12
13
  largeInputs: (() => void)[];
13
14
  };