functionalscript 0.27.0 → 0.29.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/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # FunctionalScript
2
2
 
3
3
  [![NPM Version](https://img.shields.io/npm/v/functionalscript)](https://www.npmjs.com/package/functionalscript)
4
- [![JSR Version](https://img.shields.io/jsr/v/%40functionalscript/functionalscript)](https://jsr.io/@functionalscript/functionalscript)
5
4
 
6
5
  FunctionalScript is a safe, purely functional programming language and a strict subset of
7
6
  [ECMAScript](https://en.wikipedia.org/wiki/ECMAScript)/[JavaScript](https://en.wikipedia.org/wiki/JavaScript). It's inspired by
@@ -6,7 +6,7 @@
6
6
  import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
7
7
  import { parse } from "../path/module.f.js";
8
8
  import { cBase32ToVec, vecToCBase32 } from "../cbase32/module.f.js";
9
- import { begin, forEachStep, pure } from "../effects/module.f.js";
9
+ import { forEachStep, pure } from "../effects/module.f.js";
10
10
  import { errorExit, log, mkdir, readdir, readFile, writeFile } from "../effects/node/module.f.js";
11
11
  import { dispatch } from "../cli/module.f.js";
12
12
  import { toOption } from "../types/nullable/module.f.js";
@@ -25,23 +25,20 @@ const toPath = (key) => {
25
25
  * Creates a filesystem-backed key/value store under the provided root path.
26
26
  */
27
27
  export const fileKvStore = (path) => ({
28
- read: (key) => begin
29
- .step(() => readFile(toPath(key)))
28
+ read: (key) => readFile(toPath(key))
30
29
  .step(([status, data]) => pure(status === 'error' ? undefined : data)),
31
30
  write: (key, value) => {
32
31
  const p = toPath(key);
33
32
  const parts = parse(p);
34
33
  const dir = `${path}/${parts.slice(0, -1).join('/')}`;
35
34
  // TODO: error handling
36
- return begin
37
- .step(() => mkdir(dir, { recursive: true }))
35
+ return mkdir(dir, { recursive: true })
38
36
  .step(() => writeFile(`${path}/${p}`, value))
39
37
  .step(() => pure(undefined));
40
38
  },
41
39
  list: () =>
42
40
  // TODO: remove unwrap
43
- begin
44
- .step(() => readdir('.cas', { recursive: true }))
41
+ readdir('.cas', { recursive: true })
45
42
  .step(r => pure(unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile
46
43
  ? cBase32ToVec(parentPath.substring(prefix.length).replaceAll('/', '') + name)
47
44
  : null)))),
@@ -55,8 +52,7 @@ export const cas = (sha2) => {
55
52
  read,
56
53
  write: (value) => {
57
54
  const hash = compute([value]);
58
- return begin
59
- .step(() => write(hash, value))
55
+ return write(hash, value)
60
56
  .step(() => pure(hash));
61
57
  },
62
58
  list,
@@ -72,8 +68,7 @@ export const main = (options) => {
72
68
  if (path === undefined || rest.length !== 0) {
73
69
  return errorExit("'cas add' expects one parameter");
74
70
  }
75
- return begin
76
- .step(() => readFile(path))
71
+ return readFile(path)
77
72
  .step(v => c.write(unwrap(v)))
78
73
  .step(hash => log(vecToCBase32(hash)))
79
74
  .step(() => pure(0));
@@ -90,13 +85,11 @@ export const main = (options) => {
90
85
  if (hash === null) {
91
86
  return errorExit(`invalid hash format: ${hashCBase32}`);
92
87
  }
93
- return begin
94
- .step(() => c.read(hash))
88
+ return c.read(hash)
95
89
  .step(v => {
96
90
  const result = v === undefined
97
91
  ? errorExit(`no such hash: ${hashCBase32}`)
98
- : begin
99
- .step(() => writeFile(path, v))
92
+ : writeFile(path, v)
100
93
  .step(() => pure(0));
101
94
  return result;
102
95
  });
@@ -105,8 +98,7 @@ export const main = (options) => {
105
98
  {
106
99
  names: ['list'],
107
100
  description: 'List all stored content hashes',
108
- handler: () => begin
109
- .step(() => c.list())
101
+ handler: () => c.list()
110
102
  .step(forEachStep(j => log(vecToCBase32(j))))
111
103
  .step(() => pure(0)),
112
104
  },
@@ -16,7 +16,7 @@ const installBun = installOnWindowsArm({
16
16
  });
17
17
  export const bunSteps = (extra) => (v, a) => clean([
18
18
  installBun(v)(a),
19
- test({ run: 'bun install' }),
19
+ test({ run: 'bun install --frozen-lockfile' }),
20
20
  test({ run: 'bun test --timeout 20000' }),
21
21
  ...extra,
22
22
  ]);
@@ -8,7 +8,7 @@ import { deno } from "../config/module.f.js";
8
8
  import { clean, install, test, uses } from "../common/module.f.js";
9
9
  export const denoSteps = (extra) => clean([
10
10
  install(uses('denoland/setup-deno', { 'deno-version': deno })),
11
- test({ run: 'deno install' }),
11
+ test({ run: 'deno install --frozen' }),
12
12
  test({ run: 'deno task test' }),
13
13
  ...extra,
14
14
  ]);
package/fs/ci/module.f.js CHANGED
@@ -76,10 +76,7 @@ const defaultNodeExtra = ({ name, functionalscript }) => (o) => [
76
76
  ];
77
77
  const defaultEffect = (info) => ci({
78
78
  nodeExtra: defaultNodeExtra(info),
79
- denoExtra: [
80
- ...(info.functionalscript ? denoDemoCompile : []),
81
- test({ run: 'deno publish --dry-run --allow-slow-types' }),
82
- ],
79
+ denoExtra: info.functionalscript ? [...denoDemoCompile] : [],
83
80
  bunExtra: info.functionalscript ? bunDemoCompile : [],
84
81
  });
85
82
  export const main = () => readPackageInfo.step(defaultEffect);
@@ -1,4 +1,9 @@
1
- import { type Access, type All, type Env, type Import, type NodeProgram, type Readdir } from '../effects/node/module.f.ts';
1
+ /**
2
+ * Development utilities for indexing modules and loading FunctionalScript files.
3
+ *
4
+ * @module
5
+ */
6
+ import { type Access, type All, type Env, type Import, type Readdir } from '../effects/node/module.f.ts';
2
7
  import { type Effect } from '../effects/module.f.ts';
3
8
  export type Module = {
4
9
  readonly proof?: unknown;
@@ -36,4 +41,3 @@ export type LoadModuleOperations = Access | Import | All | Readdir;
36
41
  * deterministic regardless of filesystem traversal order.
37
42
  */
38
43
  export declare const loadModuleMap: (env: Env) => Effect<LoadModuleOperations, ModuleMap>;
39
- export declare const index4: NodeProgram;
@@ -3,15 +3,10 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { updateVersion } from "./version/module.f.js";
7
- import { all, both, import_, readdir, readFile, writeFile } from "../effects/node/module.f.js";
6
+ import { all, import_, readdir } from "../effects/node/module.f.js";
8
7
  import { cmp as strCmp } from "../types/string/module.f.js";
9
- import { utf8, utf8ToString } from "../text/module.f.js";
10
8
  import { unwrap } from "../types/result/module.f.js";
11
- import { begin, pure } from "../effects/module.f.js";
12
- import { parse as jsonParse } from "../json/module.f.js";
13
- import { record, unknown as rttiUnknown } from "../types/rtti/module.f.js";
14
- import { parse as rttiParse } from "../types/rtti/parse/module.f.js";
9
+ import { pure } from "../effects/module.f.js";
15
10
  import { relativize, toPosix } from "../path/module.f.js";
16
11
  /**
17
12
  * Returns `true` if the file should be loaded for proof discovery.
@@ -29,8 +24,7 @@ export const shouldLoad = (s) => s.endsWith('.f.ts') || s.endsWith('.f.js') ||
29
24
  s.endsWith('proof.mts') || s.endsWith('proof.mjs');
30
25
  const isSourceFile = (path) => path.endsWith('.js') || path.endsWith('.ts') || path.endsWith('.mts') || path.endsWith('.mjs');
31
26
  const allFiles = (s, predicate) => {
32
- const load = (p) => begin
33
- .step(() => readdir(p, {}))
27
+ const load = (p) => readdir(p, {})
34
28
  .step(d => {
35
29
  let result = [];
36
30
  for (const i of unwrap(d)) {
@@ -86,22 +80,3 @@ export const loadModuleMap = (env) => {
86
80
  .map(([k, v]) => [relativize(prefix, k), v])
87
81
  .toSorted(([a], [b]) => strCmp(a)(b)))));
88
82
  };
89
- const denoJson = './deno.json';
90
- const parseDenoJson = rttiParse(record(rttiUnknown));
91
- const index2 = updateVersion
92
- .step(() => readFile(denoJson))
93
- .step(v => pure(unwrap(parseDenoJson(jsonParse(utf8ToString(unwrap(v)))))));
94
- const allFiles2aa = allFiles('.', v => v.endsWith('/module.f.ts') ||
95
- v.endsWith('/module.ts') ||
96
- v.endsWith('/all.test.ts'))
97
- .step(files => {
98
- const exportsA = files.map(v => [v, `./${v.substring(2)}`]);
99
- return pure(Object.fromEntries(exportsA));
100
- });
101
- const index3 = both(index2)(allFiles2aa)
102
- .step(([jsr_json, exports]) => {
103
- const json = JSON.stringify({ ...jsr_json, exports }, null, 2);
104
- return writeFile(denoJson, utf8(json));
105
- })
106
- .step(() => pure(0));
107
- export const index4 = () => index3;
@@ -2,4 +2,8 @@ export declare const proof: {
2
2
  isValid: (() => void)[];
3
3
  tokenizer: (() => void)[];
4
4
  djs: (() => void)[];
5
+ operators: (() => void)[];
6
+ throw: {
7
+ parse: () => void;
8
+ };
5
9
  };
@@ -1,7 +1,9 @@
1
1
  import { descentParser } from "../../bnf/data/module.f.js";
2
+ import { backspace, ht, lf, ff, cr, quotationMark, solidus, reverseSolidus, digitRange, digit0, latinCapitalLetterA, latinSmallLetterA, latinSmallLetterB, latinSmallLetterF, latinSmallLetterN, latinSmallLetterR, latinSmallLetterT, latinSmallLetterU, range, } from "../../text/ascii/module.f.js";
2
3
  import { stringToCodePointList } from "../../text/utf16/module.f.js";
4
+ import { contains } from "../../types/range/module.f.js";
3
5
  import { concat, filter, flat, flatMap, map, stateScan, toArray } from "../../types/list/module.f.js";
4
- import { jsGrammar } from "./module.f.js";
6
+ import { jsGrammar, parse } from "./module.f.js";
5
7
  const mapCodePoint = (cp) => [cp, undefined];
6
8
  const descentParserCpOnly = (m, name, cp) => {
7
9
  const cpm = toArray(map(mapCodePoint)(cp));
@@ -45,16 +47,21 @@ const scanFunc = (input, state) => {
45
47
  }
46
48
  return [null, [state[0], concat(state[1])([input])]];
47
49
  };
50
+ // All operator tag strings produced by the grammar's operator rule
51
+ const operatorTags = new Set([
52
+ '.', '=>', '===', '==', '=', '!==', '!=', '!',
53
+ '>>>=', '>>>', '>>=', '>>', '>=', '>',
54
+ '<<<=', '<<<', '<<=', '<<', '<=', '<',
55
+ '+=', '++', '+', '-=', '--', '-',
56
+ '**=', '**', '*=', '*', '/=', '%=', '%',
57
+ '&&=', '&&', '&=', '&', '||=', '||', '|=', '|',
58
+ '^=', '^', '~', '??=', '??', '?.', '?',
59
+ '[', ']', '{', '}', '(', ')', ',', ':'
60
+ ]);
48
61
  const filterFunc = tk => {
49
62
  if (typeof tk === 'number')
50
63
  return true;
51
64
  switch (tk) {
52
- case '{':
53
- case '}':
54
- case '[':
55
- case ']':
56
- case ':':
57
- case ',':
58
65
  case 'number':
59
66
  case 'string':
60
67
  case '\n':
@@ -63,18 +70,40 @@ const filterFunc = tk => {
63
70
  case '\t':
64
71
  return true;
65
72
  default:
66
- return false;
73
+ return operatorTags.has(tk);
67
74
  }
68
75
  };
76
+ const rangeCapitalAF = range('AF');
77
+ const stringDecodeScan = (cp, state) => {
78
+ switch (state.kind) {
79
+ case 'escape':
80
+ switch (cp) {
81
+ case quotationMark: return [[quotationMark], { kind: 'normal' }]; // \" → "
82
+ case reverseSolidus: return [[reverseSolidus], { kind: 'normal' }]; // \\ → \
83
+ case solidus: return [[solidus], { kind: 'normal' }]; // \/ → /
84
+ case latinSmallLetterB: return [[backspace], { kind: 'normal' }]; // \b → backspace (BS)
85
+ case latinSmallLetterF: return [[ff], { kind: 'normal' }]; // \f → form feed (FF)
86
+ case latinSmallLetterN: return [[lf], { kind: 'normal' }]; // \n → line feed (LF)
87
+ case latinSmallLetterR: return [[cr], { kind: 'normal' }]; // \r → carriage return (CR)
88
+ case latinSmallLetterT: return [[ht], { kind: 'normal' }]; // \t → horizontal tab (HT)
89
+ case latinSmallLetterU: return [null, { kind: 'unicode', acc: 0, count: 0 }]; // \u → start 4 hex digits
90
+ default: return [[cp], { kind: 'normal' }];
91
+ }
92
+ case 'unicode': {
93
+ // convert hex digit char to its numeric value: '0'-'9', 'A'-'F', 'a'-'f'
94
+ const digit = contains(digitRange)(cp) ? cp - digit0
95
+ : contains(rangeCapitalAF)(cp) ? cp - (latinCapitalLetterA - 10)
96
+ : cp - (latinSmallLetterA - 10);
97
+ const acc = (state.acc << 4) | digit;
98
+ return state.count === 3 ? [[acc], { kind: 'normal' }] : [null, { kind: 'unicode', acc, count: state.count + 1 }];
99
+ }
100
+ default:
101
+ return cp === reverseSolidus ? [null, { kind: 'escape' }] : [[cp], { kind: 'normal' }];
102
+ }
103
+ };
104
+ const decodeJsonString = codePoints => String.fromCodePoint(...toArray(flat(stateScan(stringDecodeScan)({ kind: 'normal' })(codePoints.slice(1, -1)))));
69
105
  const toJsToken = tk => {
70
106
  switch (tk[0]) {
71
- case '{':
72
- case '}':
73
- case '[':
74
- case ']':
75
- case ':':
76
- case ',':
77
- return { kind: tk[0] };
78
107
  case '\n':
79
108
  case '\r':
80
109
  return { kind: 'nl' };
@@ -82,9 +111,9 @@ const toJsToken = tk => {
82
111
  case '\t':
83
112
  return { kind: 'ws' };
84
113
  case 'string':
85
- return { kind: 'string', value: String.fromCodePoint(...tk[1].slice(1, -1)) };
114
+ return { kind: 'string', value: decodeJsonString(tk[1]) };
86
115
  default:
87
- return null;
116
+ return { kind: tk[0] };
88
117
  }
89
118
  };
90
119
  const getTokensFromAstRuleOrCodePoint = value => {
@@ -385,18 +414,24 @@ export const proof = {
385
414
  throw result;
386
415
  }
387
416
  },
388
- // () => {
389
- // const result = tokenizeString('"\\\\"')
390
- // if (result !== '[{"kind":"string","value":"\\\\"},{"kind":"eof"}]') { throw result }
391
- // },
392
- // () => {
393
- // const result = tokenizeString('"\\""')
394
- // if (result !== '[{"kind":"string","value":"\\""},{"kind":"eof"}]') { throw result }
395
- // },
396
- // () => {
397
- // const result = tokenizeString('"\\/"')
398
- // if (result !== '[{"kind":"string","value":"/"},{"kind":"eof"}]') { throw result }
399
- // },
417
+ () => {
418
+ const result = tokenizeString('"\\\\"');
419
+ if (result !== '[{"kind":"string","value":"\\\\"},{"kind":"eof"}]') {
420
+ throw result;
421
+ }
422
+ },
423
+ () => {
424
+ const result = tokenizeString('"\\""');
425
+ if (result !== '[{"kind":"string","value":"\\""},{"kind":"eof"}]') {
426
+ throw result;
427
+ }
428
+ },
429
+ () => {
430
+ const result = tokenizeString('"\\/"');
431
+ if (result !== '[{"kind":"string","value":"/"},{"kind":"eof"}]') {
432
+ throw result;
433
+ }
434
+ },
400
435
  () => {
401
436
  const result = tokenizeString('"\\x"');
402
437
  if (result !== 'error') {
@@ -421,18 +456,24 @@ export const proof = {
421
456
  throw result;
422
457
  }
423
458
  },
424
- // () => {
425
- // const result = tokenizeString('"\\b\\f\\n\\r\\t"')
426
- // if (result !== '[{"kind":"string","value":"\\b\\f\\n\\r\\t"},{"kind":"eof"}]') { throw result }
427
- // },
428
- // () => {
429
- // const result = tokenizeString('"\\u1234"')
430
- // if (result !== '[{"kind":"string","value":"ሴ"},{"kind":"eof"}]') { throw result }
431
- // },
432
- // () => {
433
- // const result = tokenizeString('"\\uaBcDEeFf"')
434
- // if (result !== '[{"kind":"string","value":"ꯍEeFf"},{"kind":"eof"}]') { throw result }
435
- // },
459
+ () => {
460
+ const result = tokenizeString('"\\b\\f\\n\\r\\t"');
461
+ if (result !== '[{"kind":"string","value":"\\b\\f\\n\\r\\t"},{"kind":"eof"}]') {
462
+ throw result;
463
+ }
464
+ },
465
+ () => {
466
+ const result = tokenizeString('"\\u1234"');
467
+ if (result !== '[{"kind":"string","value":"ሴ"},{"kind":"eof"}]') {
468
+ throw result;
469
+ }
470
+ },
471
+ () => {
472
+ const result = tokenizeString('"\\uaBcDEeFf"');
473
+ if (result !== '[{"kind":"string","value":"ꯍEeFf"},{"kind":"eof"}]') {
474
+ throw result;
475
+ }
476
+ },
436
477
  () => {
437
478
  const result = tokenizeString('"\\uEeFg"');
438
479
  if (result !== 'error') {
@@ -612,87 +653,88 @@ export const proof = {
612
653
  // if (result !== 'error') { throw result }
613
654
  // },
614
655
  ],
615
- // operators:
616
- // [
617
- // () => {
618
- // const result = tokenizeString('=')
619
- // if (result !== '[{"kind":"="},{"kind":"eof"}]') { throw result }
620
- // },
621
- // () => {
622
- // const result = tokenizeString('=a')
623
- // if (result !== '[{"kind":"="},{"kind":"id","value":"a"},{"kind":"eof"}]') { throw result }
624
- // },
625
- // () => {
626
- // const result = tokenizeString('-')
627
- // if (result !== '[{"kind":"-"},{"kind":"eof"}]') { throw result }
628
- // },
629
- // () => {
630
- // const result = tokenizeString('1*2')
631
- // if (result !== '[{"bf":[1n,0],"kind":"number","value":"1"},{"kind":"*"},{"bf":[2n,0],"kind":"number","value":"2"},{"kind":"eof"}]') { throw result }
632
- // },
633
- // () => {
634
- // const result = tokenizeString('( )')
635
- // if (result !== '[{"kind":"("},{"kind":"ws"},{"kind":")"},{"kind":"eof"}]') { throw result }
636
- // },
637
- // () => {
638
- // const result = tokenizeString('== != === !== > >= < <=')
639
- // if (result !== '[{"kind":"=="},{"kind":"ws"},{"kind":"!="},{"kind":"ws"},{"kind":"==="},{"kind":"ws"},{"kind":"!=="},{"kind":"ws"},{"kind":">"},{"kind":"ws"},{"kind":">="},{"kind":"ws"},{"kind":"<"},{"kind":"ws"},{"kind":"<="},{"kind":"eof"}]') { throw result }
640
- // },
641
- // () => {
642
- // const result = tokenizeString('+ - * / % ++ -- **')
643
- // if (result !== '[{"kind":"+"},{"kind":"ws"},{"kind":"-"},{"kind":"ws"},{"kind":"*"},{"kind":"ws"},{"kind":"/"},{"kind":"ws"},{"kind":"%"},{"kind":"ws"},{"kind":"++"},{"kind":"ws"},{"kind":"--"},{"kind":"ws"},{"kind":"**"},{"kind":"eof"}]') { throw result }
644
- // },
645
- // () => {
646
- // const result = tokenizeString('= += -= *= /= %= **=')
647
- // if (result !== '[{"kind":"="},{"kind":"ws"},{"kind":"+="},{"kind":"ws"},{"kind":"-="},{"kind":"ws"},{"kind":"*="},{"kind":"ws"},{"kind":"/="},{"kind":"ws"},{"kind":"%="},{"kind":"ws"},{"kind":"**="},{"kind":"eof"}]') { throw result }
648
- // },
649
- // () => {
650
- // const result = tokenizeString('& | ^ ~ << >> >>>')
651
- // if (result !== '[{"kind":"&"},{"kind":"ws"},{"kind":"|"},{"kind":"ws"},{"kind":"^"},{"kind":"ws"},{"kind":"~"},{"kind":"ws"},{"kind":"<<"},{"kind":"ws"},{"kind":">>"},{"kind":"ws"},{"kind":">>>"},{"kind":"eof"}]') { throw result }
652
- // },
653
- // () => {
654
- // const result = tokenizeString('&= |= ^= <<= >>= >>>=')
655
- // if (result !== '[{"kind":"&="},{"kind":"ws"},{"kind":"|="},{"kind":"ws"},{"kind":"^="},{"kind":"ws"},{"kind":"<<="},{"kind":"ws"},{"kind":">>="},{"kind":"ws"},{"kind":">>>="},{"kind":"eof"}]') { throw result }
656
- // },
657
- // () => {
658
- // const result = tokenizeString('&& || ! ??')
659
- // if (result !== '[{"kind":"&&"},{"kind":"ws"},{"kind":"||"},{"kind":"ws"},{"kind":"!"},{"kind":"ws"},{"kind":"??"},{"kind":"eof"}]') { throw result }
660
- // },
661
- // () => {
662
- // const result = tokenizeString('&&= ||= ??=')
663
- // if (result !== '[{"kind":"&&="},{"kind":"ws"},{"kind":"||="},{"kind":"ws"},{"kind":"??="},{"kind":"eof"}]') { throw result }
664
- // },
665
- // () => {
666
- // const result = tokenizeString('? ?. . =>')
667
- // if (result !== '[{"kind":"?"},{"kind":"ws"},{"kind":"?."},{"kind":"ws"},{"kind":"."},{"kind":"ws"},{"kind":"=>"},{"kind":"eof"}]') { throw result }
668
- // },
669
- // ],
670
- // ws: [
671
- // () => {
672
- // const result = tokenizeString(' ')
673
- // if (result !== '[{"kind":"ws"},{"kind":"eof"}]') { throw result }
674
- // },
675
- // () => {
676
- // const result = tokenizeString('\t')
677
- // if (result !== '[{"kind":"ws"},{"kind":"eof"}]') { throw result }
678
- // },
679
- // () => {
680
- // const result = tokenizeString(' \t')
681
- // if (result !== '[{"kind":"ws"},{"kind":"eof"}]') { throw result }
682
- // },
683
- // () => {
684
- // const result = tokenizeString('\n')
685
- // if (result !== '[{"kind":"nl"},{"kind":"eof"}]') { throw result }
686
- // },
687
- // () => {
688
- // const result = tokenizeString('\r')
689
- // if (result !== '[{"kind":"nl"},{"kind":"eof"}]') { throw result }
690
- // },
691
- // () => {
692
- // const result = tokenizeString(' \t\n\r ')
693
- // if (result !== '[{"kind":"nl"},{"kind":"eof"}]') { throw result }
694
- // },
695
- // ],
656
+ operators: [
657
+ () => {
658
+ const result = tokenizeString('=');
659
+ if (result !== '[{"kind":"="},{"kind":"eof"}]') {
660
+ throw result;
661
+ }
662
+ },
663
+ // () => {
664
+ // const result = tokenizeString('=a')
665
+ // if (result !== '[{"kind":"="},{"kind":"id","value":"a"},{"kind":"eof"}]') { throw result }
666
+ // },
667
+ // () => {
668
+ // const result = tokenizeString('-')
669
+ // if (result !== '[{"kind":"-"},{"kind":"eof"}]') { throw result }
670
+ // },
671
+ // () => {
672
+ // const result = tokenizeString('1*2')
673
+ // if (result !== '[{"bf":[1n,0],"kind":"number","value":"1"},{"kind":"*"},{"bf":[2n,0],"kind":"number","value":"2"},{"kind":"eof"}]') { throw result }
674
+ // },
675
+ // () => {
676
+ // const result = tokenizeString('( )')
677
+ // if (result !== '[{"kind":"("},{"kind":"ws"},{"kind":")"},{"kind":"eof"}]') { throw result }
678
+ // },
679
+ // () => {
680
+ // const result = tokenizeString('== != === !== > >= < <=')
681
+ // if (result !== '[{"kind":"=="},{"kind":"ws"},{"kind":"!="},{"kind":"ws"},{"kind":"==="},{"kind":"ws"},{"kind":"!=="},{"kind":"ws"},{"kind":">"},{"kind":"ws"},{"kind":">="},{"kind":"ws"},{"kind":"<"},{"kind":"ws"},{"kind":"<="},{"kind":"eof"}]') { throw result }
682
+ // },
683
+ // () => {
684
+ // const result = tokenizeString('+ - * / % ++ -- **')
685
+ // if (result !== '[{"kind":"+"},{"kind":"ws"},{"kind":"-"},{"kind":"ws"},{"kind":"*"},{"kind":"ws"},{"kind":"/"},{"kind":"ws"},{"kind":"%"},{"kind":"ws"},{"kind":"++"},{"kind":"ws"},{"kind":"--"},{"kind":"ws"},{"kind":"**"},{"kind":"eof"}]') { throw result }
686
+ // },
687
+ // () => {
688
+ // const result = tokenizeString('= += -= *= /= %= **=')
689
+ // if (result !== '[{"kind":"="},{"kind":"ws"},{"kind":"+="},{"kind":"ws"},{"kind":"-="},{"kind":"ws"},{"kind":"*="},{"kind":"ws"},{"kind":"/="},{"kind":"ws"},{"kind":"%="},{"kind":"ws"},{"kind":"**="},{"kind":"eof"}]') { throw result }
690
+ // },
691
+ // () => {
692
+ // const result = tokenizeString('& | ^ ~ << >> >>>')
693
+ // if (result !== '[{"kind":"&"},{"kind":"ws"},{"kind":"|"},{"kind":"ws"},{"kind":"^"},{"kind":"ws"},{"kind":"~"},{"kind":"ws"},{"kind":"<<"},{"kind":"ws"},{"kind":">>"},{"kind":"ws"},{"kind":">>>"},{"kind":"eof"}]') { throw result }
694
+ // },
695
+ // () => {
696
+ // const result = tokenizeString('&= |= ^= <<= >>= >>>=')
697
+ // if (result !== '[{"kind":"&="},{"kind":"ws"},{"kind":"|="},{"kind":"ws"},{"kind":"^="},{"kind":"ws"},{"kind":"<<="},{"kind":"ws"},{"kind":">>="},{"kind":"ws"},{"kind":">>>="},{"kind":"eof"}]') { throw result }
698
+ // },
699
+ // () => {
700
+ // const result = tokenizeString('&& || ! ??')
701
+ // if (result !== '[{"kind":"&&"},{"kind":"ws"},{"kind":"||"},{"kind":"ws"},{"kind":"!"},{"kind":"ws"},{"kind":"??"},{"kind":"eof"}]') { throw result }
702
+ // },
703
+ // () => {
704
+ // const result = tokenizeString('&&= ||= ??=')
705
+ // if (result !== '[{"kind":"&&="},{"kind":"ws"},{"kind":"||="},{"kind":"ws"},{"kind":"??="},{"kind":"eof"}]') { throw result }
706
+ // },
707
+ // () => {
708
+ // const result = tokenizeString('? ?. . =>')
709
+ // if (result !== '[{"kind":"?"},{"kind":"ws"},{"kind":"?."},{"kind":"ws"},{"kind":"."},{"kind":"ws"},{"kind":"=>"},{"kind":"eof"}]') { throw result }
710
+ // },
711
+ // ],
712
+ // ws: [
713
+ // () => {
714
+ // const result = tokenizeString(' ')
715
+ // if (result !== '[{"kind":"ws"},{"kind":"eof"}]') { throw result }
716
+ // },
717
+ // () => {
718
+ // const result = tokenizeString('\t')
719
+ // if (result !== '[{"kind":"ws"},{"kind":"eof"}]') { throw result }
720
+ // },
721
+ // () => {
722
+ // const result = tokenizeString(' \t')
723
+ // if (result !== '[{"kind":"ws"},{"kind":"eof"}]') { throw result }
724
+ // },
725
+ // () => {
726
+ // const result = tokenizeString('\n')
727
+ // if (result !== '[{"kind":"nl"},{"kind":"eof"}]') { throw result }
728
+ // },
729
+ // () => {
730
+ // const result = tokenizeString('\r')
731
+ // if (result !== '[{"kind":"nl"},{"kind":"eof"}]') { throw result }
732
+ // },
733
+ // () => {
734
+ // const result = tokenizeString(' \t\n\r ')
735
+ // if (result !== '[{"kind":"nl"},{"kind":"eof"}]') { throw result }
736
+ // },
737
+ ],
696
738
  // id: [
697
739
  // () => {
698
740
  // const result = tokenizeString('err')
@@ -909,6 +951,9 @@ export const proof = {
909
951
  // if (result !== '[{"kind":"yield"},{"kind":"eof"}]') { throw result }
910
952
  // },
911
953
  // ],
954
+ throw: {
955
+ parse: () => { parse(''); }
956
+ },
912
957
  // comments: [
913
958
  // () => {
914
959
  // const result = tokenizeString('//singleline comment')
@@ -20,7 +20,6 @@ export declare const doFull: <O extends Operation, T, K extends O[0]>(cmd: K, pa
20
20
  export type Param<O extends Operation> = F<O>[0];
21
21
  export type Return<O extends Operation> = F<O>[1];
22
22
  export declare const do_: <O extends Operation>(cmd: O[0]) => (...param: Param<O>) => Effect<O, Return<O>>;
23
- export declare const begin: Effect<never, void>;
24
23
  /**
25
24
  * Sequentially threads a state value through an effect for each item in `items`.
26
25
  *
@@ -13,7 +13,6 @@ export const doFull = (cmd, param, cont) => ({
13
13
  step: (f) => doFull(cmd, param, x => cont(x).step(f)),
14
14
  });
15
15
  export const do_ = (cmd) => (...param) => doFull(cmd, param, pure);
16
- export const begin = pure(undefined);
17
16
  /**
18
17
  * Sequentially threads a state value through an effect for each item in `items`.
19
18
  *
@@ -8,7 +8,7 @@
8
8
  * @module
9
9
  */
10
10
  import { utf8 } from "../../text/module.f.js";
11
- import { begin, do_, pure } from "../module.f.js";
11
+ import { do_, pure } from "../module.f.js";
12
12
  const doAll = do_('all');
13
13
  /**
14
14
  * To run the operation `O` should be known by the runner/engine.
@@ -70,6 +70,4 @@ export const test = do_('test');
70
70
  * "fail with a message" program for a `NodeProgram`. For non-`1` exit codes,
71
71
  * compose `error(s).step(() => pure(n))` directly.
72
72
  */
73
- export const errorExit = (s) => begin
74
- .step(() => error(s))
75
- .step(() => pure(1));
73
+ export const errorExit = (s) => error(s).step(() => pure(1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -11,12 +11,11 @@
11
11
  "prepack": "tsc --NoEmit false",
12
12
  "test": "tsc && node ./fs/fjs/module.ts t",
13
13
  "cov": "node --test --experimental-test-coverage --test-coverage-include=**/module.f.ts",
14
- "index": "node ./fs/fjs/module.ts r ./fs/dev/index/module.f.ts",
15
14
  "start": "node ./fs/fjs/module.ts",
16
15
  "ci-update": "node ./fs/fjs/module.ts ci",
17
- "update": "npm install && npm run index && npm run ci-update",
16
+ "update": "npx npm-check-updates -u && npm install && deno install && bun install && npm run ci-update",
18
17
  "index-html": "node ./fs/fjs/module.ts r ./fs/website/module.f.ts",
19
- "website": "npm run prepack &&npm run index-html"
18
+ "website": "npm run prepack && npm run index-html"
20
19
  },
21
20
  "engines": {
22
21
  "node": ">=24"
@@ -44,8 +43,8 @@
44
43
  },
45
44
  "homepage": "https://github.com/functionalscript/functionalscript#readme",
46
45
  "devDependencies": {
47
- "@playwright/test": "1",
48
- "@types/node": "*",
49
- "typescript": "*"
46
+ "@playwright/test": "1.60.0",
47
+ "@types/node": "25.9.2",
48
+ "typescript": "6.0.3"
50
49
  }
51
- }
50
+ }
@@ -1 +0,0 @@
1
- export declare const main: import("../../effects/node/module.f.ts").NodeProgram;
@@ -1,8 +0,0 @@
1
- /**
2
- * Entry point for the `index` developer program: re-exports `index4` from the
3
- * parent `dev` module as the default `NodeProgram`.
4
- *
5
- * @module
6
- */
7
- import { index4 } from "../module.f.js";
8
- export const main = index4;
@@ -1,3 +0,0 @@
1
- import { type Effect } from '../../effects/module.f.ts';
2
- import { type All, type ReadFile, type WriteFile } from '../../effects/node/module.f.ts';
3
- export declare const updateVersion: Effect<ReadFile | WriteFile | All, number>;
@@ -1,33 +0,0 @@
1
- /**
2
- * Version management helpers for updating project package versions.
3
- *
4
- * @module
5
- */
6
- import { utf8, utf8ToString } from "../../text/module.f.js";
7
- import { begin, pure } from "../../effects/module.f.js";
8
- import { all, readFile, writeFile } from "../../effects/node/module.f.js";
9
- import { unwrap } from "../../types/result/module.f.js";
10
- import { validatePackageJson } from "../package_json/module.f.js";
11
- import { assert } from "../../asserts/module.f.js";
12
- const { parse, stringify } = JSON;
13
- const jsonFile = (jsonFile) => `${jsonFile}.json`;
14
- const readJson = (name) => begin
15
- .step(() => readFile(jsonFile(name)))
16
- .step(v => pure(unwrap(validatePackageJson(parse(utf8ToString(unwrap(v)))))));
17
- const writeVersion = (version) => (name) => begin
18
- .step(() => readJson(name))
19
- .step((json) => writeFile(jsonFile(name), utf8(stringify({
20
- ...json,
21
- version,
22
- }, null, 2))));
23
- const version = (p) => {
24
- assert(p.version !== undefined, 'package.json version is missing');
25
- return p.version;
26
- };
27
- export const updateVersion = begin
28
- .step(() => readJson('package'))
29
- .step(p => {
30
- const w = writeVersion(version(p));
31
- return all(w('package'), w('deno'));
32
- })
33
- .step(() => pure(0));
@@ -1,3 +0,0 @@
1
- export declare const proof: {
2
- new: () => void;
3
- };
@@ -1,100 +0,0 @@
1
- import { utf8, utf8ToString } from "../../text/module.f.js";
2
- import { isVec } from "../../types/bit_vec/module.f.js";
3
- import { all, writeFile } from "../../effects/node/module.f.js";
4
- import { emptyState, virtual } from "../../effects/node/virtual/module.f.js";
5
- import { updateVersion } from "./module.f.js";
6
- const version = '0.3.0';
7
- const x = {
8
- 'package.json': {
9
- "name": "functionalscript",
10
- version,
11
- "description": "FunctionalScript is a functional subset of JavaScript",
12
- "main": "module.f.cjs",
13
- "scripts": {
14
- "tsc": "tsc",
15
- "test": "tsc && npm run test-only",
16
- "version": "node ./nodejs/version/main.cjs",
17
- "test-only": "node --trace-uncaught ./test.f.cjs"
18
- },
19
- "repository": {
20
- "type": "git",
21
- "url": "git+https://github.com/functionalscript/functionalscript.git"
22
- },
23
- "author": "NatFoam",
24
- "license": "MIT",
25
- "keywords": [
26
- "lambda",
27
- "functional-programming",
28
- "closure",
29
- "pure-functional",
30
- "typescript",
31
- "programming-language",
32
- "lazy-evaluation"
33
- ],
34
- "bugs": {
35
- "url": "https://github.com/functionalscript/functionalscript/issues"
36
- },
37
- "homepage": "https://github.com/functionalscript/functionalscript#readme",
38
- "devDependencies": {
39
- "@types/node": "^18.6.2",
40
- "typescript": "^4.7.4"
41
- }
42
- },
43
- "deno.json": {}
44
- };
45
- const e = '{\n' +
46
- ' "name": "functionalscript",\n' +
47
- ` "version": "${version}",\n` +
48
- ' "description": "FunctionalScript is a functional subset of JavaScript",\n' +
49
- ' "main": "module.f.cjs",\n' +
50
- ' "scripts": {\n' +
51
- ' "tsc": "tsc",\n' +
52
- ' "test": "tsc && npm run test-only",\n' +
53
- ' "version": "node ./nodejs/version/main.cjs",\n' +
54
- ' "test-only": "node --trace-uncaught ./test.f.cjs"\n' +
55
- ' },\n' +
56
- ' "repository": {\n' +
57
- ' "type": "git",\n' +
58
- ' "url": "git+https://github.com/functionalscript/functionalscript.git"\n' +
59
- ' },\n' +
60
- ' "author": "NatFoam",\n' +
61
- ' "license": "MIT",\n' +
62
- ' "keywords": [\n' +
63
- ' "lambda",\n' +
64
- ' "functional-programming",\n' +
65
- ' "closure",\n' +
66
- ' "pure-functional",\n' +
67
- ' "typescript",\n' +
68
- ' "programming-language",\n' +
69
- ' "lazy-evaluation"\n' +
70
- ' ],\n' +
71
- ' "bugs": {\n' +
72
- ' "url": "https://github.com/functionalscript/functionalscript/issues"\n' +
73
- ' },\n' +
74
- ' "homepage": "https://github.com/functionalscript/functionalscript#readme",\n' +
75
- ' "devDependencies": {\n' +
76
- ' "@types/node": "^18.6.2",\n' +
77
- ' "typescript": "^4.7.4"\n' +
78
- ' }\n' +
79
- '}';
80
- export const proof = {
81
- new: () => {
82
- const w = (name) => {
83
- const fn = `${name}.json`;
84
- return writeFile(fn, utf8(JSON.stringify(x[fn])));
85
- };
86
- const [state] = virtual(emptyState)(all(w('package'), w('deno')));
87
- const [newState, result] = virtual(state)(updateVersion);
88
- if (result !== 0) {
89
- throw result;
90
- }
91
- const vec = newState.root['package.json'];
92
- if (!isVec(vec)) {
93
- throw vec;
94
- }
95
- const n = utf8ToString(vec);
96
- if (n !== e) {
97
- throw [n, e];
98
- }
99
- }
100
- };