functionalscript 0.33.0 → 0.35.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 (89) hide show
  1. package/README.md +27 -14
  2. package/fs/asserts/module.f.d.ts +1 -1
  3. package/fs/asserts/module.f.js +4 -1
  4. package/fs/base64/proof.f.d.ts +1 -0
  5. package/fs/base64/proof.f.js +25 -56
  6. package/fs/base_n/module.f.js +11 -5
  7. package/fs/base_n/proof.f.d.ts +1 -0
  8. package/fs/base_n/proof.f.js +12 -1
  9. package/fs/bnf/data/module.f.d.ts +0 -103
  10. package/fs/bnf/data/module.f.js +10 -246
  11. package/fs/bnf/data/proof.f.d.ts +0 -10
  12. package/fs/bnf/data/proof.f.js +2 -642
  13. package/fs/bnf/descent/module.f.d.ts +56 -0
  14. package/fs/bnf/descent/module.f.js +119 -0
  15. package/fs/bnf/descent/proof.f.d.ts +5 -0
  16. package/fs/bnf/descent/proof.f.js +296 -0
  17. package/fs/bnf/ll1/module.f.d.ts +72 -0
  18. package/fs/bnf/ll1/module.f.js +153 -0
  19. package/fs/bnf/ll1/proof.f.d.ts +9 -0
  20. package/fs/bnf/ll1/proof.f.js +358 -0
  21. package/fs/cas/cli/module.f.d.ts +6 -0
  22. package/fs/cas/cli/module.f.js +56 -0
  23. package/fs/cas/cli/proof.f.d.ts +14 -0
  24. package/fs/cas/cli/proof.f.js +149 -0
  25. package/fs/cas/mcp/module.f.d.ts +9 -17
  26. package/fs/cas/mcp/module.f.js +123 -96
  27. package/fs/cas/mcp/proof.f.d.ts +12 -1
  28. package/fs/cas/mcp/proof.f.js +199 -32
  29. package/fs/cas/module.f.d.ts +35 -15
  30. package/fs/cas/module.f.js +168 -131
  31. package/fs/cas/proof.f.d.ts +16 -13
  32. package/fs/cas/proof.f.js +255 -121
  33. package/fs/ci/config/module.f.d.ts +14 -14
  34. package/fs/ci/config/module.f.js +14 -14
  35. package/fs/djs/tokenizer-new/module.f.js +1 -1
  36. package/fs/djs/tokenizer-new/proof.f.d.ts +2 -0
  37. package/fs/djs/tokenizer-new/proof.f.js +357 -229
  38. package/fs/effects/list/module.f.d.ts +31 -0
  39. package/fs/effects/list/module.f.js +16 -0
  40. package/fs/effects/module.f.d.ts +11 -5
  41. package/fs/effects/module.f.js +16 -6
  42. package/fs/effects/node/module.f.d.ts +29 -3
  43. package/fs/effects/node/module.f.js +35 -1
  44. package/fs/effects/node/module.js +32 -18
  45. package/fs/effects/node/virtual/module.f.js +57 -0
  46. package/fs/effects/proof.f.d.ts +4 -0
  47. package/fs/effects/proof.f.js +18 -1
  48. package/fs/fjs/module.f.js +2 -8
  49. package/fs/html/module.f.js +6 -8
  50. package/fs/mcp/module.f.d.ts +1 -1
  51. package/fs/mcp/stdio/module.f.d.ts +8 -0
  52. package/fs/mcp/stdio/module.f.js +33 -6
  53. package/fs/mcp/stdio/proof.f.d.ts +3 -0
  54. package/fs/mcp/stdio/proof.f.js +52 -1
  55. package/fs/mime/module.f.d.ts +101 -5
  56. package/fs/mime/module.f.js +159 -6
  57. package/fs/mime/proof.f.d.ts +25 -0
  58. package/fs/mime/proof.f.js +193 -3
  59. package/fs/text/code_point/module.f.d.ts +39 -2
  60. package/fs/text/code_point/module.f.js +74 -2
  61. package/fs/text/code_point/proof.f.d.ts +8 -0
  62. package/fs/text/code_point/proof.f.js +72 -0
  63. package/fs/text/module.f.d.ts +11 -1
  64. package/fs/text/module.f.js +13 -3
  65. package/fs/text/proof.f.d.ts +6 -0
  66. package/fs/text/proof.f.js +25 -2
  67. package/fs/text/utf16/module.f.js +4 -26
  68. package/fs/text/utf8/module.f.d.ts +27 -0
  69. package/fs/text/utf8/module.f.js +8 -14
  70. package/fs/types/bit_vec/module.f.d.ts +20 -2
  71. package/fs/types/bit_vec/module.f.js +77 -27
  72. package/fs/types/bit_vec/proof.f.d.ts +8 -0
  73. package/fs/types/bit_vec/proof.f.js +24 -6
  74. package/fs/types/function/operator/module.f.d.ts +26 -0
  75. package/fs/types/list/module.f.d.ts +3 -0
  76. package/fs/types/nullable/module.f.d.ts +9 -5
  77. package/fs/types/nullable/module.f.js +19 -0
  78. package/fs/types/result/module.d.ts +2 -4
  79. package/fs/types/result/module.js +9 -3
  80. package/package.json +3 -3
  81. package/issues/demo/data/data.f.js +0 -12
  82. package/issues/demo/data/shared.f.js +0 -3
  83. package/issues/demo/fs/app.js +0 -4
  84. package/issues/demo/fs/math.f.js +0 -4
  85. package/issues/demo/sample/proof.f.js +0 -13
  86. /package/{issues → todo}/031-json.f.d.ts +0 -0
  87. /package/{issues → todo}/031-json.f.js +0 -0
  88. /package/{issues → todo}/proof.f.d.ts +0 -0
  89. /package/{issues → todo}/proof.f.js +0 -0
@@ -23,10 +23,11 @@
23
23
  */
24
24
  import { bitLength, divUp, mask, maxLength, xor } from "../bigint/module.f.js";
25
25
  import { flip, identity } from "../function/module.f.js";
26
- import { fold, iterable, map } from "../list/module.f.js";
26
+ import { iterable, map } from "../list/module.f.js";
27
27
  import { asBase, asNominal } from "../nominal/module.f.js";
28
28
  import { repeat as mRepeat } from "../monoid/module.f.js";
29
29
  import { cmp, max, min } from "../function/compare/module.f.js";
30
+ import { mapUnwrap } from "../nullable/module.f.js";
30
31
  /**
31
32
  * Maximum length of a bit vector in bits (1_048_576 = 0x10_0000).
32
33
  * This limit is enforced by Bun's `bigint` size constraint, the minimal limit
@@ -133,6 +134,64 @@ const op = (norm) => (op) => ap => bp => {
133
134
  const { a, b } = norm(au)(bu)(len);
134
135
  return vec(len)(op(a)(b));
135
136
  };
137
+ const unpackEmpty = { length: 0n, uint: 0n };
138
+ const listToVecOp = (unpackConcat) => ({
139
+ init: { len: 0n, stack: [] },
140
+ update: (v, { len, stack }) => {
141
+ len += v.length;
142
+ if (len > maxLength) {
143
+ return null;
144
+ }
145
+ let i = 0;
146
+ while (true) {
147
+ if (stack.length <= i) {
148
+ stack = [...stack, v];
149
+ break;
150
+ }
151
+ const old = stack[i];
152
+ if (old.length === 0n) {
153
+ stack = stack.toSpliced(i, 1, v);
154
+ break;
155
+ }
156
+ stack = stack.toSpliced(i, 1, unpackEmpty);
157
+ v = unpackConcat(old)(v);
158
+ i++;
159
+ }
160
+ return { len, stack };
161
+ },
162
+ end: ({ stack }) => pack(stack.reduce((p, c) => unpackConcat(c)(p), unpackEmpty))
163
+ });
164
+ /**
165
+ * Concatenates a list of unpacked vectors using a binary-counter accumulator,
166
+ * giving O(n log n) total `bigint` shifting work instead of the O(n²) of a
167
+ * naive left fold.
168
+ *
169
+ * Slot `i` of `result` holds an already-combined run of the most recent
170
+ * `2 ** i` elements. Each arriving element "carries" upward, merging only with
171
+ * runs of comparable size — exactly like incrementing a binary number — so
172
+ * every merge joins two runs of similar length. Left-to-right element order is
173
+ * preserved: `unpackConcat(old)(cur)` keeps the earlier run on the left, and
174
+ * the final reduce prepends higher (earlier) slots in front of accumulated
175
+ * later runs. An empty list yields `unpackEmpty`.
176
+ *
177
+ * This is the bit-vector analogue of a builder that accumulates appended pieces
178
+ * and materializes the combined result on demand, such as `StringBuilder`
179
+ * (Java, C#) or `strings.Builder` (Go).
180
+ */
181
+ const unpackListToVec = (unpackConcat) => {
182
+ const { init, update, end } = listToVecOp(unpackConcat);
183
+ return (list) => {
184
+ let result = init;
185
+ for (const e of iterable(list)) {
186
+ const candidate = update(e, result);
187
+ if (candidate === null) {
188
+ return null;
189
+ }
190
+ result = candidate;
191
+ }
192
+ return end(result);
193
+ };
194
+ };
136
195
  const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }) => {
137
196
  const unpackPopFront = (len) => {
138
197
  const m = mask(len);
@@ -142,8 +201,9 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
142
201
  return [uint & m, { length: v.length - len, uint: rest }];
143
202
  };
144
203
  };
145
- const unpackConcat = (a) => (b) => ({
146
- length: a.length + b.length, uint: unpackConcatUint(a)(b)
204
+ const unpackConcat = a => b => ({
205
+ length: a.length + b.length,
206
+ uint: unpackConcatUint(a)(b)
147
207
  });
148
208
  const popFront = len => {
149
209
  const f = unpackPopFront(len);
@@ -157,11 +217,13 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
157
217
  const bu = unpack(b);
158
218
  return pack(unpackConcat(au)(bu));
159
219
  };
220
+ const tryListToVec = (list) => unpackListToVec(unpackConcat)(map(unpack)(list));
160
221
  return {
161
222
  front,
162
223
  removeFront,
163
224
  concat,
164
- listToVec: fold(flip(concat))(empty),
225
+ tryListToVec,
226
+ listToVec: mapUnwrap(tryListToVec),
165
227
  xor: op(norm)(xor),
166
228
  unpackPopFront,
167
229
  popFront,
@@ -232,7 +294,16 @@ export const msb = bo({
232
294
  unpackSplit: len => ({ length, uint }) => [uint >> (length - len), uint],
233
295
  unpackConcatUint: flip(lsbUnpackConcatUint),
234
296
  });
235
- const unpackEmpty = { length: 0n, uint: 0n };
297
+ /**
298
+ * Converts a list of unsigned 8-bit integers to a bit vector using the provided
299
+ * bit order, like `u8ListToVec`, but returns `null` instead of throwing when the
300
+ * result would exceed `maxLength`.
301
+ *
302
+ * @param bo The bit order for the conversion
303
+ * @param list The list of unsigned 8-bit integers to be converted.
304
+ * @returns The resulting vector, or `null` if it would exceed `maxLength`.
305
+ */
306
+ export const tryU8ListToVec = ({ unpackConcat }) => (list) => unpackListToVec(unpackConcat)(map((b) => ({ length: 8n, uint: BigInt(b) }))(list));
236
307
  /**
237
308
  * Converts a list of unsigned 8-bit integers to a bit vector using the provided bit order.
238
309
  *
@@ -240,28 +311,7 @@ const unpackEmpty = { length: 0n, uint: 0n };
240
311
  * @param list The list of unsigned 8-bit integers to be converted.
241
312
  * @returns The resulting vector based on the provided bit order.
242
313
  */
243
- export const u8ListToVec = ({ unpackConcat }) => (list) => {
244
- let result = [];
245
- for (const b of iterable(list)) {
246
- let v = { length: 8n, uint: BigInt(b) };
247
- let i = 0;
248
- while (true) {
249
- if (result.length <= i) {
250
- result = [...result, v];
251
- break;
252
- }
253
- const old = result[i];
254
- if (old.length === 0n) {
255
- result = result.toSpliced(i, 1, v);
256
- break;
257
- }
258
- result = result.toSpliced(i, 1, unpackEmpty);
259
- v = unpackConcat(old)(v);
260
- i++;
261
- }
262
- }
263
- return pack(result.reduce((p, c) => unpackConcat(c)(p), unpackEmpty));
264
- };
314
+ export const u8ListToVec = (bo) => mapUnwrap(tryU8ListToVec(bo));
265
315
  const unpackChunkList = ({ unpackSplit }) => (n) => {
266
316
  const divUpN2 = divUp(n << 1n);
267
317
  return u => {
@@ -46,6 +46,14 @@ export declare const proof: {
46
46
  emptyVec: () => void;
47
47
  };
48
48
  u8ListToVec: () => () => void;
49
+ tryListToVecOverflow: () => void;
50
+ listToVecOverflow: {
51
+ throw: () => void;
52
+ };
53
+ u8ListToVecOverflow: {
54
+ try: () => void;
55
+ throw: () => void;
56
+ };
49
57
  u8ListUnaligned: () => void;
50
58
  chunkList: {
51
59
  empty: () => void;
@@ -1,6 +1,7 @@
1
+ import { assertEq } from "../../asserts/module.f.js";
1
2
  import { mask } from "../bigint/module.f.js";
2
3
  import { asBase, asNominal } from "../nominal/module.f.js";
3
- import { length, empty, uint, vec, lsb, msb, repeat, vec8, u8ListToVec, u8List, chunkList, fromSentinel } from "./module.f.js";
4
+ import { length, empty, uint, vec, lsb, msb, repeat, vec8, maxLength, u8ListToVec, tryU8ListToVec, u8List, chunkList, fromSentinel } from "./module.f.js";
4
5
  import { repeat as listRepeat, toArray } from "../list/module.f.js";
5
6
  const unsafeVec = (a) => asNominal(a);
6
7
  // 0x8 = 0b1000 = 0 + 8
@@ -11,11 +12,6 @@ const unsafeVec = (a) => asNominal(a);
11
12
  // 0xD = 0b1101 = 5 + 8
12
13
  // 0xE = 0b1110 = 6 + 8
13
14
  // 0xF = 0b1111 = 7 + 8
14
- const assertEq = (a, b) => {
15
- if (a !== b) {
16
- throw [a, b];
17
- }
18
- };
19
15
  const assertEq2 = ([a0, a1], [b0, b1]) => {
20
16
  assertEq(a0, b0);
21
17
  assertEq(a1, b1);
@@ -504,6 +500,28 @@ export const proof = {
504
500
  }
505
501
  };
506
502
  },
503
+ tryListToVecOverflow: () => {
504
+ const list = [vec(maxLength)(1n), vec(1n)(1n)];
505
+ assertEq(lsb.tryListToVec(list), null);
506
+ assertEq(msb.tryListToVec(list), null);
507
+ },
508
+ listToVecOverflow: {
509
+ // Same oversized input, but through the throwing `listToVec` wrapper.
510
+ throw: () => {
511
+ const list = { first: vec(maxLength + 1n)(1n), tail: null };
512
+ lsb.listToVec(list);
513
+ },
514
+ },
515
+ u8ListToVecOverflow: {
516
+ // 131_073 bytes is 8 bits past `maxLength`; same null/throw split as
517
+ // `tryListToVec`/`listToVec` above, exercised through the byte-list API.
518
+ try: () => {
519
+ assertEq(tryU8ListToVec(msb)(listRepeat(0x12)(131_073)), null);
520
+ },
521
+ throw: () => {
522
+ u8ListToVec(msb)(listRepeat(0x12)(131_073));
523
+ },
524
+ },
507
525
  u8ListUnaligned: () => {
508
526
  const x = vec(9n)(0x83n);
509
527
  const a = toArray(u8List(msb)(x));
@@ -12,6 +12,32 @@ export declare const logicalNot: Unary<boolean, boolean>;
12
12
  export type Equal<T> = Binary<T, T, boolean>;
13
13
  export declare const strictEqual: <T>(a: T) => (b: T) => boolean;
14
14
  export type Scan<I, O> = (input: I) => readonly [O, Scan<I, O>];
15
+ /**
16
+ * One step of a stream transducer: given an `input` symbol and the `prior`
17
+ * state, produce an `output` and the next state. It both maps an input stream
18
+ * to an output stream and threads state, so it models tokenizers, decoders, and
19
+ * other stream-to-stream stages.
20
+ *
21
+ * This is the *shape* of a [Mealy machine](https://en.wikipedia.org/wiki/Mealy_machine)
22
+ * — a [finite-state transducer](https://en.wikipedia.org/wiki/Finite-state_transducer)
23
+ * — but only its signature. The state `S` (and `I`, `O`) is an arbitrary type,
24
+ * not a finite set, so a `StateScan` is strictly more expressive than a Mealy
25
+ * machine — its power is the power of `S`:
26
+ * - a finite `S`/`I`/`O` recovers the classical finite-state machine (e.g. the
27
+ * DFA states in `../../../fsm/module.f.ts`);
28
+ * - an `S` that is a stack makes it a
29
+ * [pushdown / stack machine](https://en.wikipedia.org/wiki/Pushdown_automaton)
30
+ * (context-free power — balanced brackets, nested structure, the AST tier);
31
+ * - an unbounded `S` like `bigint` can count, which no finite automaton can.
32
+ *
33
+ * And `O` may be a list (0+ symbols per input), not the single symbol strict
34
+ * Mealy emits. (Functional/coalgebraic usage still calls this `(input, state)
35
+ * => [output, state]` shape a "Mealy machine", finiteness aside.)
36
+ *
37
+ * A {@link Fold} is the output-less special case (state only); driving a
38
+ * `StateScan` over a `List` is `stateScan` in `../../list/module.f.ts`, and
39
+ * {@link stateScanToScan} hides the state to recover a {@link Scan}.
40
+ */
15
41
  export type StateScan<I, S, O> = (input: I, prior: S) => readonly [O, S];
16
42
  export declare const stateScanToScan: <I, S, O>(op: StateScan<I, S, O>) => (prior: S) => Scan<I, O>;
17
43
  export declare const foldToScan: <I, O>(fold: Fold<I, O>) => (prior: O) => Scan<I, O>;
@@ -4,6 +4,9 @@ type NotLazy<T> = Result<T> | Concat<T> | readonly T[];
4
4
  type Empty = null;
5
5
  export type Result<T> = Empty | NonEmpty<T>;
6
6
  export type Thunk<T> = () => List<T>;
7
+ /**
8
+ * See also https://en.wikipedia.org/wiki/Cons#Lists
9
+ */
7
10
  export type NonEmpty<T> = {
8
11
  readonly first: T;
9
12
  readonly tail: List<T>;
@@ -1,8 +1,3 @@
1
- /**
2
- * Utilities for nullable (`null`/`undefined`) value handling.
3
- *
4
- * @module
5
- */
6
1
  import type { Option } from '../option/module.f.ts';
7
2
  export type Nullable<T> = T | null;
8
3
  export declare const map: <T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>;
@@ -15,3 +10,12 @@ export declare const toOption: <T>(value: Nullable<T>) => Option<T>;
15
10
  * property/index lookups) and FunctionalScript (which uses `null` for absence).
16
11
  */
17
12
  export declare const fromUndefined: <T>(value: T | undefined) => Nullable<T>;
13
+ /**
14
+ * Extracts the value from a `Nullable`, asserting that it is not `null`.
15
+ */
16
+ export declare const unwrap: <T>(value: Nullable<T>) => T;
17
+ /**
18
+ * Lifts a function that signals failure with `null` into one that asserts
19
+ * success instead, unwrapping the result.
20
+ */
21
+ export declare const mapUnwrap: <I, T>(f: (i: I) => Nullable<T>) => import("../function/module.f.ts").Func<I, T>;
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Utilities for nullable (`null`/`undefined`) value handling.
3
+ *
4
+ * @module
5
+ */
6
+ import { assert } from "../../asserts/module.f.js";
7
+ import { fn } from "../function/module.f.js";
1
8
  export const map = f => value => value === null ? null : f(value);
2
9
  export const match = f => none => value => value === null ? none() : f(value);
3
10
  export const toOption = (value) => value === null ? [] : [value];
@@ -8,3 +15,15 @@ export const toOption = (value) => value === null ? [] : [value];
8
15
  * property/index lookups) and FunctionalScript (which uses `null` for absence).
9
16
  */
10
17
  export const fromUndefined = (value) => value === undefined ? null : value;
18
+ /**
19
+ * Extracts the value from a `Nullable`, asserting that it is not `null`.
20
+ */
21
+ export const unwrap = (value) => {
22
+ assert(value !== null);
23
+ return value;
24
+ };
25
+ /**
26
+ * Lifts a function that signals failure with `null` into one that asserts
27
+ * success instead, unwrapping the result.
28
+ */
29
+ export const mapUnwrap = (f) => fn(f).map(unwrap).result;
@@ -1,5 +1,3 @@
1
1
  import { type Result } from './module.f.ts';
2
- declare const _default: {
3
- readonly tryCatch: <T>(f: () => T) => Result<T, unknown>;
4
- };
5
- export default _default;
2
+ export declare const tryCatch: <T>(f: () => T) => Result<T, unknown>;
3
+ export declare const asyncTryCatch: <T>(f: () => Promise<T>) => Promise<Result<T, unknown>>;
@@ -1,5 +1,5 @@
1
1
  import { ok, error } from "./module.f.js";
2
- const tryCatch = f => {
2
+ export const tryCatch = f => {
3
3
  // Side effect: `try catch` is not allowed in FunctionalScript.
4
4
  try {
5
5
  return ok(f());
@@ -8,6 +8,12 @@ const tryCatch = f => {
8
8
  return error(e);
9
9
  }
10
10
  };
11
- export default {
12
- tryCatch,
11
+ export const asyncTryCatch = async (f) => {
12
+ // Side effect: `try catch` is not allowed in FunctionalScript.
13
+ try {
14
+ return ok(await f());
15
+ }
16
+ catch (e) {
17
+ return error(e);
18
+ }
13
19
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -43,8 +43,8 @@
43
43
  },
44
44
  "homepage": "https://github.com/functionalscript/functionalscript#readme",
45
45
  "devDependencies": {
46
- "@playwright/test": "1.61.0",
47
- "@types/node": "26.0.0",
46
+ "@playwright/test": "1.61.1",
47
+ "@types/node": "26.1.0",
48
48
  "typescript": "6.0.3"
49
49
  }
50
50
  }
@@ -1,12 +0,0 @@
1
- import shared from "./shared.f.js"
2
-
3
- // comment
4
- const s = "Hello, world!"
5
-
6
- export default {
7
- "a": shared,
8
- "b": shared,
9
- "s": s,
10
- d: -24n,
11
- x: undefined,
12
- }
@@ -1,3 +0,0 @@
1
- export default {
2
- "name": "shared object"
3
- }
@@ -1,4 +0,0 @@
1
- import math from "./math.f.js"
2
-
3
- const add2 = math.add(2)
4
- console.log("5 ===", add2(3))
@@ -1,4 +0,0 @@
1
- export default {
2
- add: a => b => a + b,
3
- mul: a => b => a * b,
4
- }
@@ -1,13 +0,0 @@
1
- const arrayOfTests = [
2
- () => {
3
- if (2 + 2 !== 4) throw "It's the end of the world as we know it!"
4
- },
5
- () => {
6
- if (2n + 2n !== 4n) throw "It's the end of the world as we know it!"
7
- }
8
- ]
9
-
10
- export const proof = {
11
- arrayOfTests,
12
- generatingTests: () => arrayOfTests,
13
- }
File without changes
File without changes
File without changes
File without changes