functionalscript 0.10.3 → 0.11.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.
@@ -1,12 +1,34 @@
1
+ /**
2
+ * Provides grammar-to-data conversion and LL(1) dispatch/matching types and helpers
3
+ * for FunctionalScript BNF rules.
4
+ *
5
+ * @module
6
+ */
1
7
  import { type CodePoint } from '../../text/utf16/module.f.ts';
2
8
  import { type RangeMapArray } from '../../types/range_map/module.f.ts';
3
9
  import { type Rule as FRule } from '../module.f.ts';
10
+ /**
11
+ * Encoded terminal range value used by BNF data rules.
12
+ *
13
+ * The same as the functional TerminalRange.
14
+ */
4
15
  export type TerminalRange = number;
16
+ /**
17
+ * Ordered list of grammar rule names.
18
+ */
5
19
  export type Sequence = readonly string[];
6
20
  /** A variant of rule names. */
7
21
  export type Variant = {
8
22
  readonly [k in string]: string;
9
23
  };
24
+ /**
25
+ * Grammar rule definition.
26
+ *
27
+ * It can be one of:
28
+ * - a tagged variant map,
29
+ * - a sequence of referenced rule names,
30
+ * - an encoded terminal range.
31
+ */
10
32
  export type Rule = Variant | Sequence | TerminalRange;
11
33
  /** The full grammar */
12
34
  export type RuleSet = Readonly<Record<string, Rule>>;
@@ -29,11 +51,29 @@ type DispatchMap = {
29
51
  type EmptyTagMap = {
30
52
  readonly [id in string]: EmptyTagEntry;
31
53
  };
54
+ /**
55
+ * Recursive descent matcher for a single named rule.
56
+ */
32
57
  export type DescentMatchRule<T> = (name: string, tag: AstTag, s: readonly CodePointMeta<T>[], idx: number) => DescentMatchResult<T>;
58
+ /**
59
+ * Result tuple of a descent match operation: AST node, success flag, and next index.
60
+ */
33
61
  export type DescentMatchResult<T> = readonly [AstRuleMeta<T>, boolean, number];
62
+ /**
63
+ * Entry-point recursive descent matcher.
64
+ */
34
65
  export type DescentMatch<T> = (name: string, s: readonly CodePointMeta<T>[]) => DescentMatchResult<T>;
66
+ /**
67
+ * Code point value paired with metadata.
68
+ */
35
69
  export type CodePointMeta<T> = readonly [CodePoint, T];
70
+ /**
71
+ * AST sequence for the metadata-aware parser.
72
+ */
36
73
  export type AstSequenceMeta<T> = readonly (AstRuleMeta<T> | CodePointMeta<T>)[];
74
+ /**
75
+ * Metadata-aware AST node.
76
+ */
37
77
  export type AstRuleMeta<T> = {
38
78
  readonly tag: AstTag;
39
79
  readonly sequence: AstSequenceMeta<T>;
@@ -55,18 +95,44 @@ type AstRule = {
55
95
  */
56
96
  export type Remainder = readonly CodePoint[] | null;
57
97
  /**
98
+ * Parsing result of {@link parser} and {@link parserRuleSet}.
99
+ *
58
100
  * Represents the result of a match operation, including the parsed AST rule and the remainder of the input.
59
101
  */
60
102
  export type MatchResult = readonly [AstRule, boolean, Remainder];
61
103
  /**
62
- * Represents an LL(1) parser function for matching input against grammar rules.
104
+ * LL(1) parser function for matching by rule name.
63
105
  */
64
106
  export type Match = (name: string, s: readonly CodePoint[]) => MatchResult;
107
+ /**
108
+ * Internal match function signature used by compiled dispatch rules.
109
+ */
65
110
  export type MatchRule = (dr: DispatchRule, s: readonly CodePoint[]) => MatchResult;
111
+ /**
112
+ * Converts a functional grammar rule into serializable BNF data and returns
113
+ * the generated rule set with the entry rule identifier.
114
+ */
66
115
  export declare const toData: (fr: FRule) => readonly [RuleSet, string];
116
+ /**
117
+ * Builds a dispatch map for a {@link RuleSet} to enable predictive parsing.
118
+ */
67
119
  export declare const dispatchMap: (ruleSet: RuleSet) => DispatchMap;
120
+ /**
121
+ * Creates a map that describes whether each rule can consume empty input and,
122
+ * for tagged variants, which tag represents the empty match.
123
+ */
68
124
  export declare const createEmptyTagMap: (data: readonly [RuleSet, string]) => EmptyTagMap;
125
+ /**
126
+ * Creates a recursive descent parser that preserves metadata for each consumed
127
+ * code point.
128
+ */
69
129
  export declare const descentParser: <T>(fr: FRule) => DescentMatch<T>;
130
+ /**
131
+ * Creates an LL(1) parser from a functional grammar rule.
132
+ */
70
133
  export declare const parser: (fr: FRule) => Match;
134
+ /**
135
+ * Creates an LL(1) parser from an already materialized {@link RuleSet}.
136
+ */
71
137
  export declare const parserRuleSet: (ruleSet: RuleSet) => Match;
72
138
  export {};
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Provides grammar-to-data conversion and LL(1) dispatch/matching types and helpers
3
+ * for FunctionalScript BNF rules.
4
+ *
5
+ * @module
6
+ */
1
7
  import { stringToCodePointList } from "../../text/utf16/module.f.js";
2
8
  import { strictEqual } from "../../types/function/operator/module.f.js";
3
9
  import { map, toArray } from "../../types/list/module.f.js";
@@ -74,6 +80,10 @@ const toDataAdd = (map) => (fr) => {
74
80
  const [map2, set, rule] = newRule(map1);
75
81
  return [map2, { ...set, [id]: rule }, id];
76
82
  };
83
+ /**
84
+ * Converts a functional grammar rule into serializable BNF data and returns
85
+ * the generated rule set with the entry rule identifier.
86
+ */
77
87
  export const toData = (fr) => {
78
88
  const [, ruleSet, id] = toDataAdd({})(fr);
79
89
  return [ruleSet, id];
@@ -91,6 +101,9 @@ const dispatchOp = rangeMap({
91
101
  equal: strictEqual,
92
102
  def: null,
93
103
  });
104
+ /**
105
+ * Builds a dispatch map for a {@link RuleSet} to enable predictive parsing.
106
+ */
94
107
  export const dispatchMap = (ruleSet) => {
95
108
  const addRuleToDispatch = (dr, name) => {
96
109
  if (dr === null)
@@ -196,9 +209,17 @@ const emptyTagMapAdd = (ruleSet) => (map) => (name) => {
196
209
  return [ruleSet, { ...map, [name]: emptyTag }, emptyTag];
197
210
  }
198
211
  };
212
+ /**
213
+ * Creates a map that describes whether each rule can consume empty input and,
214
+ * for tagged variants, which tag represents the empty match.
215
+ */
199
216
  export const createEmptyTagMap = (data) => {
200
217
  return emptyTagMapAdd(data[0])({})(data[1])[1];
201
218
  };
219
+ /**
220
+ * Creates a recursive descent parser that preserves metadata for each consumed
221
+ * code point.
222
+ */
202
223
  export const descentParser = (fr) => {
203
224
  const data = toData(fr);
204
225
  const emptyTagMap = createEmptyTagMap(data);
@@ -256,10 +277,16 @@ export const descentParser = (fr) => {
256
277
  };
257
278
  return match;
258
279
  };
280
+ /**
281
+ * Creates an LL(1) parser from a functional grammar rule.
282
+ */
259
283
  export const parser = (fr) => {
260
284
  const data = toData(fr);
261
285
  return parserRuleSet(data[0]);
262
286
  };
287
+ /**
288
+ * Creates an LL(1) parser from an already materialized {@link RuleSet}.
289
+ */
263
290
  export const parserRuleSet = (ruleSet) => {
264
291
  const map = dispatchMap(ruleSet);
265
292
  const f = (rule, cp) => {
package/bnf/module.f.js CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Core BNF grammar primitives and helpers for describing parser rules.
3
+ *
4
+ * The module provides terminal-range encoding utilities, rule composition
5
+ * types, and set/range helpers used by FunctionalScript grammar definitions.
6
+ *
7
+ * @module
8
+ */
1
9
  import { codePointListToString, stringToCodePointList } from "../text/utf16/module.f.js";
2
10
  import { isArray2 } from "../types/array/module.f.js";
3
11
  import { map, toArray, repeat as listRepeat } from "../types/list/module.f.js";
package/cas/module.f.d.ts CHANGED
@@ -5,19 +5,19 @@
5
5
  */
6
6
  import { type Sha2 } from "../crypto/sha2/module.f.ts";
7
7
  import type { Vec } from "../types/bit_vec/module.f.ts";
8
- import { type Effect, type Operations } from "../types/effects/module.f.ts";
9
- import { type Fs, type NodeOperations } from "../types/effects/node/module.f.ts";
10
- export type KvStore<O extends Operations> = {
8
+ import { type Effect, type Operation } from "../types/effects/module.f.ts";
9
+ import { type Fs, type NodeOp } from "../types/effects/node/module.f.ts";
10
+ export type KvStore<O extends Operation> = {
11
11
  readonly read: (key: Vec) => Effect<O, Vec | undefined>;
12
12
  readonly write: (key: Vec, value: Vec) => Effect<O, void>;
13
13
  readonly list: () => Effect<O, readonly Vec[]>;
14
14
  };
15
15
  export type Kv = readonly [Vec, Vec];
16
16
  export declare const fileKvStore: (path: string) => KvStore<Fs>;
17
- export type Cas<O extends Operations> = {
17
+ export type Cas<O extends Operation> = {
18
18
  readonly read: (key: Vec) => Effect<O, Vec | undefined>;
19
19
  readonly write: (value: Vec) => Effect<O, Vec>;
20
20
  readonly list: () => Effect<O, readonly Vec[]>;
21
21
  };
22
- export declare const cas: (sha2: Sha2) => <O extends Operations>(_: KvStore<O>) => Cas<O>;
23
- export declare const main: (args: readonly string[]) => Effect<NodeOperations, number>;
22
+ export declare const cas: (sha2: Sha2) => <O extends Operation>(_: KvStore<O>) => Cas<O>;
23
+ export declare const main: (args: readonly string[]) => Effect<NodeOp, number>;
package/cas/module.f.js CHANGED
@@ -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 "../types/cbase32/module.f.js";
9
- import { fluent, pure } from "../types/effects/module.f.js";
9
+ import { begin, pure } from "../types/effects/module.f.js";
10
10
  import { error, log, mkdir, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
11
11
  import { toOption } from "../types/nullable/module.f.js";
12
12
  import { unwrap } from "../types/result/module.f.js";
@@ -20,29 +20,26 @@ const toPath = (key) => {
20
20
  return `${prefix}/${a}/${b}/${c}`;
21
21
  };
22
22
  export const fileKvStore = (path) => ({
23
- read: (key) => fluent
23
+ read: (key) => begin
24
24
  .step(() => readFile(toPath(key)))
25
- .step(([status, data]) => pure(status === 'error' ? undefined : data))
26
- .effect,
25
+ .step(([status, data]) => pure(status === 'error' ? undefined : data)),
27
26
  write: (key, value) => {
28
27
  const p = toPath(key);
29
28
  const parts = parse(p);
30
29
  const dir = `${path}/${parts.slice(0, -1).join('/')}`;
31
30
  // TODO: error handling
32
- return fluent
31
+ return begin
33
32
  .step(() => mkdir(dir, { recursive: true }))
34
33
  .step(() => writeFile(`${path}/${p}`, value))
35
- .step(() => pure(undefined))
36
- .effect;
34
+ .step(() => pure(undefined));
37
35
  },
38
36
  list: () =>
39
37
  // TODO: remove unwrap
40
- fluent
38
+ begin
41
39
  .step(() => readdir('.cas', { recursive: true }))
42
40
  .step(r => pure(unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile
43
41
  ? cBase32ToVec(parentPath.substring(prefix.length).replaceAll('/', '') + name)
44
- : null))))
45
- .effect,
42
+ : null)))),
46
43
  });
47
44
  export const cas = (sha2) => {
48
45
  const compute = computeSync(sha2);
@@ -50,18 +47,16 @@ export const cas = (sha2) => {
50
47
  read,
51
48
  write: (value) => {
52
49
  const hash = compute([value]);
53
- return fluent
50
+ return begin
54
51
  .step(() => write(hash, value))
55
- .step(() => pure(hash))
56
- .effect;
52
+ .step(() => pure(hash));
57
53
  },
58
54
  list,
59
55
  });
60
56
  };
61
- const e = (s) => fluent
57
+ const e = (s) => begin
62
58
  .step(() => error(s))
63
- .step(() => pure(1))
64
- .effect;
59
+ .step(() => pure(1));
65
60
  export const main = (args) => {
66
61
  const c = cas(sha256)(fileKvStore('.'));
67
62
  const [cmd, ...options] = args;
@@ -71,12 +66,11 @@ export const main = (args) => {
71
66
  return e("'cas add' expects one parameter");
72
67
  }
73
68
  const [path] = options;
74
- return fluent
69
+ return begin
75
70
  .step(() => readFile(path))
76
71
  .step(v => c.write(unwrap(v)))
77
72
  .step(hash => log(vecToCBase32(hash)))
78
- .step(() => pure(0))
79
- .effect;
73
+ .step(() => pure(0));
80
74
  }
81
75
  case 'get': {
82
76
  if (options.length !== 2) {
@@ -87,36 +81,32 @@ export const main = (args) => {
87
81
  if (hash === null) {
88
82
  return e(`invalid hash format: ${hashCBase32}`);
89
83
  }
90
- return fluent
84
+ return begin
91
85
  .step(() => c.read(hash))
92
86
  .step(v => {
93
87
  const result = v === undefined
94
88
  ? e(`no such hash: ${hashCBase32}`)
95
- : fluent
89
+ : begin
96
90
  .step(() => writeFile(path, v))
97
- .step(() => pure(0))
98
- .effect;
91
+ .step(() => pure(0));
99
92
  return result;
100
- })
101
- .effect;
93
+ });
102
94
  }
103
95
  case 'list': {
104
- return fluent
96
+ return begin
105
97
  .step(() => c.list())
106
98
  .step(v => {
107
99
  // TODO: make it lazy.
108
- let i = fluent.effect;
100
+ let i = begin;
109
101
  for (const j of v) {
110
102
  const prev = i;
111
- i = fluent
103
+ i = begin
112
104
  .step(() => prev)
113
- .step(() => log(vecToCBase32(j)))
114
- .effect;
105
+ .step(() => log(vecToCBase32(j)));
115
106
  }
116
107
  return i;
117
108
  })
118
- .step(() => pure(0))
119
- .effect;
109
+ .step(() => pure(0));
120
110
  }
121
111
  case undefined: {
122
112
  return e('Error: CAS command requires subcommand');
package/ci/module.f.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type NodeEffect } from '../types/effects/node/module.f.ts';
2
- export declare const effect: NodeEffect<number>;
3
- declare const _default: () => import("../types/effects/module.f.ts").Pure<number>;
1
+ import { type Effect } from '../types/effects/module.f.ts';
2
+ import { type NodeOp } from '../types/effects/node/module.f.ts';
3
+ export declare const effect: Effect<NodeOp, number>;
4
+ declare const _default: () => Effect<NodeOp, number>;
4
5
  export default _default;
package/ci/module.f.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * @module
5
5
  */
6
6
  import { utf8 } from "../text/module.f.js";
7
- import { fluent, pure } from "../types/effects/module.f.js";
7
+ import { begin, pure } from "../types/effects/module.f.js";
8
8
  import { writeFile } from "../types/effects/node/module.f.js";
9
9
  const os = ['ubuntu', 'macos', 'windows'];
10
10
  const architecture = ['intel', 'arm'];
@@ -177,8 +177,7 @@ const gha = {
177
177
  on: { pull_request: {} },
178
178
  jobs,
179
179
  };
180
- export const effect = fluent
180
+ export const effect = begin
181
181
  .step(() => writeFile('.github/workflows/ci.yml', utf8(JSON.stringify(gha, null, ' '))))
182
- .step(() => pure(0))
183
- .effect;
182
+ .step(() => pure(0));
184
183
  export default () => effect;
package/dev/module.f.d.ts CHANGED
@@ -4,6 +4,8 @@
4
4
  * @module
5
5
  */
6
6
  import { type Io } from '../io/module.f.ts';
7
+ import { type All, type Readdir } from '../types/effects/node/module.f.ts';
8
+ import { type Effect } from '../types/effects/module.f.ts';
7
9
  export declare const todo: () => never;
8
10
  export type Module = {
9
11
  readonly default?: unknown;
@@ -12,6 +14,7 @@ export type ModuleMap = {
12
14
  readonly [k in string]: Module;
13
15
  };
14
16
  export declare const env: (io: Io) => (v: string) => string | undefined;
17
+ export declare const allFiles2: (s: string) => Effect<Readdir | All, readonly string[]>;
15
18
  export declare const allFiles: (io: Io) => (s: string) => Promise<readonly string[]>;
16
19
  export declare const loadModuleMap: (io: Io) => Promise<ModuleMap>;
17
20
  export declare const index: (io: Io) => Promise<number>;
package/dev/module.f.js CHANGED
@@ -6,10 +6,10 @@
6
6
  import { fromIo } from "../io/module.f.js";
7
7
  import { updateVersion } from "./version/module.f.js";
8
8
  import { encodeUtf8 } from "../types/uint8array/module.f.js";
9
- import { readFile } from "../types/effects/node/module.f.js";
10
- import { utf8ToString } from "../text/module.f.js";
9
+ import { all, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
10
+ import { utf8, utf8ToString } from "../text/module.f.js";
11
11
  import { unwrap } from "../types/result/module.f.js";
12
- import { fluent, pure } from "../types/effects/module.f.js";
12
+ import { begin, pure } from "../types/effects/module.f.js";
13
13
  export const todo = () => { throw 'not implemented'; };
14
14
  const cmp = ([a], [b]) => a < b ? -1 : a > b ? 1 : 0;
15
15
  export const env = ({ process: { env } }) => a => {
@@ -18,6 +18,33 @@ export const env = ({ process: { env } }) => a => {
18
18
  typeof r.get === 'function' ? r.get() :
19
19
  r.value;
20
20
  };
21
+ export const allFiles2 = (s) => {
22
+ const load = (p) => begin
23
+ .step(() => readdir(p, {}))
24
+ .step(d => {
25
+ let result = [];
26
+ for (const i of unwrap(d)) {
27
+ const { name } = i;
28
+ if (name.startsWith('.')) {
29
+ continue;
30
+ }
31
+ const file = `${p}/${name}`;
32
+ if (!i.isFile) {
33
+ if (name === 'node_modules') {
34
+ continue;
35
+ }
36
+ result = [...result, load(file)];
37
+ continue;
38
+ }
39
+ if (name.endsWith('.js') || name.endsWith('.ts')) {
40
+ result = [...result, pure([file])];
41
+ }
42
+ }
43
+ return all(...result);
44
+ })
45
+ .step(v => pure(v.flat()));
46
+ return load(s);
47
+ };
21
48
  export const allFiles = (io) => (s) => {
22
49
  const { fs: { promises: { readdir } } } = io;
23
50
  const load = async (p) => {
@@ -58,22 +85,23 @@ export const loadModuleMap = async (io) => {
58
85
  return Object.fromEntries(map.toSorted(cmp));
59
86
  };
60
87
  const denoJson = './deno.json';
61
- const index2 = fluent
88
+ const index2 = begin
62
89
  .step(() => updateVersion)
63
90
  .step(() => readFile(denoJson))
64
- .step(v => pure(JSON.parse(utf8ToString(unwrap(v)))))
65
- .effect;
66
- export const index = async (io) => {
67
- const runner = fromIo(io);
68
- const jsr_json = await runner(index2);
69
- const list = (await allFiles(io)('.')).filter(v => v.endsWith('/module.f.ts') || v.endsWith('/module.ts'));
70
- // console.log(list)
91
+ .step(v => pure(JSON.parse(utf8ToString(unwrap(v)))));
92
+ const allFiles2a = (jsr_json) => begin
93
+ .step(() => allFiles2('.'))
94
+ .step(files => {
95
+ // console.log(files)
96
+ const list = files.filter(v => v.endsWith('/module.f.ts') || v.endsWith('/module.ts'));
71
97
  const exportsA = list.map(v => [v, `./${v.substring(2)}`]);
72
- // console.log(exportsA)
73
98
  const exports = Object.fromEntries(exportsA);
74
- // console.log(exports)
75
99
  const json = JSON.stringify({ ...jsr_json, exports }, null, 2);
76
- // console.log(json)
77
- await io.fs.promises.writeFile(denoJson, encodeUtf8(json));
78
- return 0;
100
+ return writeFile(denoJson, utf8(json));
101
+ })
102
+ .step(() => pure(0));
103
+ export const index = async (io) => {
104
+ const runner = fromIo(io);
105
+ const jsr_json = await runner(index2);
106
+ return await runner(allFiles2a(jsr_json));
79
107
  };
@@ -1,2 +1,3 @@
1
- import { type NodeEffect } from "../../types/effects/node/module.f.ts";
2
- export declare const updateVersion: NodeEffect<number>;
1
+ import { type Effect } from "../../types/effects/module.f.ts";
2
+ import { type All, type ReadFile, type WriteFile } from "../../types/effects/node/module.f.ts";
3
+ export declare const updateVersion: Effect<ReadFile | WriteFile | All, number>;
@@ -4,24 +4,24 @@
4
4
  * @module
5
5
  */
6
6
  import { utf8, utf8ToString } from "../../text/module.f.js";
7
- import { all, fluent, pure, step } from "../../types/effects/module.f.js";
8
- import { readFile, writeFile } from "../../types/effects/node/module.f.js";
7
+ import { begin, pure } from "../../types/effects/module.f.js";
8
+ import { all, readFile, writeFile } from "../../types/effects/node/module.f.js";
9
9
  import { unwrap } from "../../types/result/module.f.js";
10
10
  const { stringify, parse } = JSON;
11
11
  const jsonFile = (jsonFile) => `${jsonFile}.json`;
12
- const readJson = (name) => fluent
12
+ const readJson = (name) => begin
13
13
  .step(() => readFile(jsonFile(name)))
14
- .step(v => pure(parse(utf8ToString(unwrap(v)))))
15
- .effect;
16
- const writeVersion = (version) => (name) => step(readJson(name))(json => writeFile(jsonFile(name), utf8(stringify({
14
+ .step(v => pure(parse(utf8ToString(unwrap(v)))));
15
+ const writeVersion = (version) => (name) => begin
16
+ .step(() => readJson(name))
17
+ .step(json => writeFile(jsonFile(name), utf8(stringify({
17
18
  ...json,
18
19
  version,
19
20
  }, null, 2))));
20
- export const updateVersion = fluent
21
+ export const updateVersion = begin
21
22
  .step(() => readJson('package'))
22
23
  .step(p => {
23
24
  const w = writeVersion(p.version);
24
- return all([w('package'), w('deno')]);
25
+ return all(w('package'), w('deno'));
25
26
  })
26
- .step(() => pure(0))
27
- .effect;
27
+ .step(() => pure(0));
@@ -1,8 +1,6 @@
1
1
  import { utf8, utf8ToString } from "../../text/module.f.js";
2
2
  import { isVec } from "../../types/bit_vec/module.f.js";
3
- import { run } from "../../types/effects/mock/module.f.js";
4
- import { all } from "../../types/effects/module.f.js";
5
- import { writeFile } from "../../types/effects/node/module.f.js";
3
+ import { all, writeFile } from "../../types/effects/node/module.f.js";
6
4
  import { emptyState, virtual } from "../../types/effects/node/virtual/module.f.js";
7
5
  import { updateVersion } from "./module.f.js";
8
6
  const version = '0.3.0';
@@ -81,13 +79,12 @@ const e = '{\n' +
81
79
  '}';
82
80
  export default {
83
81
  new: () => {
84
- const rv = run(virtual);
85
82
  const w = (name) => {
86
83
  const fn = `${name}.json`;
87
84
  return writeFile(fn, utf8(JSON.stringify(x[fn])));
88
85
  };
89
- const [state] = rv(emptyState)(all([w('package'), w('deno')]));
90
- const [newState, result] = rv(state)(updateVersion);
86
+ const [state] = virtual(emptyState)(all(w('package'), w('deno')));
87
+ const [newState, result] = virtual(state)(updateVersion);
91
88
  if (result !== 0) {
92
89
  throw result;
93
90
  }
package/io/module.f.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { NodeEffect } from '../types/effects/node/module.f.ts';
1
+ import { type Effect } from '../types/effects/module.f.ts';
2
+ import type { NodeOp } from '../types/effects/node/module.f.ts';
2
3
  import { type Result } from '../types/result/module.f.ts';
3
4
  /**
4
5
  * Represents a directory entry (file or directory) in the filesystem
@@ -104,4 +105,5 @@ export type Run = (f: App) => Promise<never>;
104
105
  * Handles errors by exiting with code 1
105
106
  */
106
107
  export declare const run: (io: Io) => Run;
107
- export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, fetch, }: Io) => <T>(effect: NodeEffect<T>) => Promise<T>;
108
+ export type EffectToPromise = <T>(effect: Effect<NodeOp, T>) => Promise<T>;
109
+ export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, fetch, }: Io) => EffectToPromise;
package/io/module.f.js CHANGED
@@ -1,4 +1,6 @@
1
+ import { todo } from "../dev/module.f.js";
1
2
  import { normalize } from "../path/module.f.js";
3
+ import { pure } from "../types/effects/module.f.js";
2
4
  import { asyncRun } from "../types/effects/module.js";
3
5
  import { error, ok } from "../types/result/module.f.js";
4
6
  import { fromVec, toVec } from "../types/uint8array/module.f.js";
@@ -7,7 +9,7 @@ import { fromVec, toVec } from "../types/uint8array/module.f.js";
7
9
  * Handles errors by exiting with code 1
8
10
  */
9
11
  export const run = (io) => {
10
- const code = ([x, b]) => {
12
+ const exitCode = ([x, b]) => {
11
13
  if (x === 'error') {
12
14
  io.console.error(b);
13
15
  return 1;
@@ -16,7 +18,7 @@ export const run = (io) => {
16
18
  return b;
17
19
  }
18
20
  };
19
- return async (f) => io.process.exit(code(await io.asyncTryCatch(() => f(io))));
21
+ return async (f) => io.process.exit(exitCode(await io.asyncTryCatch(() => f(io))));
20
22
  };
21
23
  const tc = async (f) => {
22
24
  try {
@@ -26,19 +28,23 @@ const tc = async (f) => {
26
28
  return error(e);
27
29
  }
28
30
  };
29
- export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, fetch, }) => asyncRun({
30
- error: async (message) => error(message),
31
- log: async (message) => log(message),
32
- fetch: async (url) => tc(async () => {
33
- const response = await fetch(url);
34
- if (!response.ok) {
35
- throw new Error(`Fetch error: ${response.status} ${response.statusText}`);
36
- }
37
- return toVec(new Uint8Array(await response.arrayBuffer()));
38
- }),
39
- mkdir: param => tc(async () => { await mkdir(...param); }),
40
- readFile: path => tc(async () => toVec(await readFile(path))),
41
- readdir: ([path, r]) => tc(async () => (await readdir(path, { ...r, withFileTypes: true }))
42
- .map(v => ({ name: v.name, parentPath: normalize(v.parentPath), isFile: v.isFile() }))),
43
- writeFile: ([path, data]) => tc(() => writeFile(path, fromVec(data))),
44
- });
31
+ export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, fetch, }) => {
32
+ const result = asyncRun({
33
+ all: async (effects) => await Promise.all(effects.map(v => result(v))),
34
+ error: async (message) => error(message),
35
+ log: async (message) => log(message),
36
+ fetch: async (url) => tc(async () => {
37
+ const response = await fetch(url);
38
+ if (!response.ok) {
39
+ throw new Error(`Fetch error: ${response.status} ${response.statusText}`);
40
+ }
41
+ return toVec(new Uint8Array(await response.arrayBuffer()));
42
+ }),
43
+ mkdir: param => tc(async () => { await mkdir(...param); }),
44
+ readFile: path => tc(async () => toVec(await readFile(path))),
45
+ readdir: ([path, r]) => tc(async () => (await readdir(path, { ...r, withFileTypes: true }))
46
+ .map(v => ({ name: v.name, parentPath: normalize(v.parentPath), isFile: v.isFile() }))),
47
+ writeFile: ([path, data]) => tc(() => writeFile(path, fromVec(data))),
48
+ });
49
+ return result;
50
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.10.3",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -14,9 +14,9 @@
14
14
  "index": "node ./dev/index/module.ts",
15
15
  "fst": "node ./fjs/module.ts t",
16
16
  "fjs": "node ./fjs/module.ts",
17
- "ci-update": "node ./ci/module.ts",
17
+ "ci-update": "node ./fjs/module.ts r ./ci/module.f.ts",
18
18
  "update": "git clean -fdx && npm install && npm run index && npm run ci-update",
19
- "website": "node --experimental-strip-types ./website/module.ts"
19
+ "website": "node --experimental-strip-types ./fjs/module.ts r ./website/module.f.ts"
20
20
  },
21
21
  "engines": {
22
22
  "node": ">=20"
@@ -3,8 +3,9 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import type { Effect, Operations } from "../module.f.ts";
7
- export type MemOperationMap<O extends Operations, S> = {
8
- readonly [K in keyof O]: (state: S, payload: O[K][0]) => readonly [S, O[K][1]];
6
+ import type { Effect, Operation, Pr } from "../module.f.ts";
7
+ export type MemOperationMap<O extends Operation, S> = {
8
+ readonly [K in O[0]]: (state: S, payload: Pr<O, K>[0]) => readonly [S, Pr<O, K>[1]];
9
9
  };
10
- export declare const run: <O extends Operations, S>(o: MemOperationMap<O, S>) => (state: S) => <O1 extends O, T>(effect: Effect<O1, T>) => readonly [S, T];
10
+ export type RunInstance<O extends Operation, S> = (state: S) => <O1 extends O, T>(effect: Effect<O1, T>) => readonly [S, T];
11
+ export declare const run: <O extends Operation, S>(o: MemOperationMap<O, S>) => RunInstance<O, S>;
@@ -1,11 +1,13 @@
1
- export const run = (o) => (state) => (effect) => {
1
+ export const run = (o) => state => effect => {
2
2
  let s = state;
3
3
  let e = effect;
4
4
  while (true) {
5
- if (e.length === 1) {
6
- return [s, e[0]];
5
+ const { value } = e;
6
+ if (value.length === 1) {
7
+ const [v] = value;
8
+ return [s, v];
7
9
  }
8
- const [cmd, payload, cont] = e;
10
+ const [cmd, payload, cont] = value;
9
11
  const operation = o[cmd];
10
12
  const [ns, m] = operation(s, payload);
11
13
  s = ns;
@@ -1,2 +1,2 @@
1
- import type { Effect, Operations, ToAsyncOperationMap } from "./module.f.ts";
2
- export declare const asyncRun: <O extends Operations>(map: ToAsyncOperationMap<O>) => <T, E extends Effect<O, T>>(effect: Effect<O, T>) => Promise<T>;
1
+ import type { Effect, Operation, ToAsyncOperationMap } from "./module.f.ts";
2
+ export declare const asyncRun: <O extends Operation>(map: ToAsyncOperationMap<O>) => <T, E extends Effect<O, T>>(effect: Effect<O, T>) => Promise<T>;
@@ -3,25 +3,23 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- export type Operations = {
7
- readonly [command in string]: readonly [input: unknown, output: unknown];
6
+ export type Operation = readonly [string, (_: never) => unknown];
7
+ export type Effect<O extends Operation, T> = {
8
+ value: Value<O, T>;
9
+ step: <Q extends Operation, R>(f: (p: T) => Effect<Q, R>) => Effect<O | Q, R>;
8
10
  };
9
- export type Effect<O extends Operations, T> = Pure<T> | Do<O, T>;
11
+ export type Value<O extends Operation, T> = Pure<T> | Do<O, T>;
10
12
  export type Pure<T> = readonly [T];
11
- export type One<O extends Operations, T, K extends keyof O & string> = readonly [K, O[K][0], (input: O[K][1]) => Effect<O, T>];
12
- export type Do<O extends Operations, T> = {
13
- readonly [K in keyof O & string]: One<O, T, K>;
14
- }[keyof O & string];
15
- export declare const pure: <T>(value: T) => Pure<T>;
16
- export declare const do_: <O extends Operations, K extends keyof O & string>(cmd: K, payload: O[K][0]) => Do<O, O[K][1]>;
17
- export type ToAsyncOperationMap<O extends Operations> = {
18
- readonly [K in keyof O]: (payload: O[K][0]) => Promise<O[K][1]>;
13
+ export type DoKPR<O extends Operation, T, K extends string, PR extends readonly [unknown, unknown]> = readonly [K, PR[0], (_: PR[1]) => Effect<O, T>];
14
+ export type Pr<O extends Operation, K extends O[0]> = O extends readonly [K, (_: infer P) => infer R] ? readonly [P, R] : never;
15
+ export type DoK<O extends Operation, T, K extends O[0]> = DoKPR<O, T, K, Pr<O, K>>;
16
+ export type Do<O extends Operation, T> = DoK<O, T, O[0]>;
17
+ export declare const pure: <T>(v: T) => Effect<never, T>;
18
+ export declare const doFull: <O extends Operation, T, K extends O[0]>(cmd: K, param: Pr<O, K>[0], cont: (input: Pr<O, K>[1]) => Effect<O, T>) => Effect<O, T>;
19
+ export declare const do_: <O extends Operation>(cmd: O[0]) => (param: Pr<O, O[0]>[0]) => Effect<O, Pr<O, O[0]>[1]>;
20
+ export declare const begin: Effect<never, void>;
21
+ export type ToAsyncOperationMap<O extends Operation> = {
22
+ readonly [K in O[0]]: (payload: Pr<O, K>[0]) => Promise<Pr<O, K>[1]>;
19
23
  };
20
- export declare const step: <O extends Operations, T>(e: Effect<O, T>) => <O1 extends Operations, R>(f: (_: T) => Effect<O1, R>) => Effect<O | O1, R>;
21
- export declare const map: <O extends Operations, T>(e: Effect<O, T>) => <R>(f: (_: T) => R) => Effect<O, R>;
22
- export type Fluent<O extends Operations, T> = {
23
- readonly effect: Effect<O, T>;
24
- readonly step: <O1 extends Operations, R>(f: (_: T) => Effect<O1, R>) => Fluent<O | O1, R>;
25
- };
26
- export declare const fluent: Fluent<{}, void>;
27
- export declare const all: <O extends Operations, T>(set: readonly Effect<O, T>[]) => Effect<O, readonly T[]>;
24
+ export type F<O extends Operation> = Pr<O, O[0]>;
25
+ export type Func<O extends Operation> = (_: F<O>[0]) => Effect<O, F<O>[1]>;
@@ -1,20 +1,80 @@
1
- export const pure = (value) => [value];
2
- const doFull = (cmd, payload, cont) => [cmd, payload, cont];
3
- export const do_ = (cmd, payload) => doFull(cmd, payload, pure);
4
- export const step = (e) => (f) => {
1
+ /**
2
+ * Core effect type constructors and combinators.
3
+ *
4
+ * @module
5
+ */
6
+ export const pure = (v) => ({
7
+ value: [v],
8
+ step: f => f(v)
9
+ });
10
+ export const doFull = (cmd, param, cont) => ({
11
+ value: [cmd, param, cont],
12
+ step: (f) => doFull(cmd, param, x => cont(x).step(f)),
13
+ });
14
+ export const do_ = (cmd) => (param) => doFull(cmd, param, pure);
15
+ export const begin = pure(undefined);
16
+ //----------------------------------------------------------------------
17
+ /*
18
+ export type Operations = {
19
+ readonly [command in string]: readonly [input: unknown, output: unknown]
20
+ }
21
+
22
+ export type Effect<O extends Operations, T> = Pure<O, T> | Do<O, T>
23
+
24
+ export type Pure<O, T> = readonly [T]
25
+
26
+ export type One<O extends Operations, T, K extends keyof O & string> =
27
+ readonly [K, O[K][0], (input: O[K][1]) => Effect<O, T>]
28
+
29
+ export type Do<O extends Operations, T> = { readonly [K in keyof O & string]: One<O, T, K> }[keyof O & string]
30
+
31
+ export const pure = <O extends Operations, T>(value: T): Pure<O, T> => [value]
32
+
33
+ const doFull = <O extends Operations, K extends keyof O & string, T>(
34
+ cmd: K,
35
+ payload: O[K][0],
36
+ cont: (input: O[K][1]) => Effect<O, T>
37
+ ): Do<O, T> =>
38
+ [cmd, payload, cont]
39
+
40
+ export const do_ = <O extends Operations, K extends keyof O & string>(
41
+ cmd: K,
42
+ payload: O[K][0]
43
+ ): Do<O, O[K][1]> =>
44
+ doFull(cmd, payload, pure)
45
+
46
+ export type ToAsyncOperationMap<O extends Operations> = {
47
+ readonly [K in keyof O]: (payload: O[K][0]) => Promise<O[K][1]>
48
+ }
49
+
50
+ export const step =
51
+ <O extends Operations, T>(e: Effect<O, T>) =>
52
+ <O1 extends Operations, R>(f: (_: T) => Effect<O1, R>): Effect<O | O1, R> =>
53
+ {
5
54
  if (e.length === 1) {
6
- const [value] = e;
7
- return f(value);
55
+ const [value] = e
56
+ return f(value)
8
57
  }
9
- const [cmd, payload, cont] = e;
10
- return doFull(cmd, payload, x => step(cont(x))(f));
11
- };
12
- export const map = (e) => (f) => step(e)(x => pure(f(x)));
13
- const wrap = (effect) => ({
58
+ const [cmd, payload, cont] = e
59
+ return doFull(cmd, payload, x => step(cont(x))(f))
60
+ }
61
+
62
+ export const map =
63
+ <O extends Operations, T>(e: Effect<O, T>) =>
64
+ <R>(f: (_: T) => R): Effect<O, R> =>
65
+ step(e)(x => pure(f(x)))
66
+
67
+ export type Fluent<O extends Operations, T> = {
68
+ readonly effect: Effect<O, T>
69
+ readonly step: <O1 extends Operations, R>(f: (_: T) => Effect<O1, R>) => Fluent<O | O1, R>
70
+ }
71
+
72
+ const wrap = <O extends Operations, T>(effect: Effect<O, T>): Fluent<O, T> => ({
14
73
  effect,
15
74
  step: x => wrap(step(effect)(x)),
16
- });
17
- export const fluent = wrap(pure(undefined));
18
- const empty = pure([]);
19
- // TODO: replace with either a `Do` operation or as an addition to `Pure` and `Do`.
20
- export const all = (set) => set.reduce((previous, current) => step(previous)(previousResult => map(current)(currentResult => [...previousResult, currentResult])), empty);
75
+ })
76
+
77
+ export const fluent: Fluent<{}, void> = wrap(pure(undefined))
78
+
79
+ const empty: Effect<{}, readonly never[]> = pure([])
80
+ */
@@ -1,9 +1,10 @@
1
1
  export const asyncRun = (map) => async (effect) => {
2
2
  while (true) {
3
- if (effect.length === 1) {
4
- return effect[0];
3
+ const { value } = effect;
4
+ if (value.length === 1) {
5
+ return value[0];
5
6
  }
6
- const [command, payload, continuation] = effect;
7
+ const [command, payload, continuation] = value;
7
8
  const operation = map[command];
8
9
  const result = await operation(payload);
9
10
  effect = continuation(result);
@@ -1,23 +1,26 @@
1
1
  import type { Vec } from '../../bit_vec/module.f.ts';
2
2
  import type { Result } from '../../result/module.f.ts';
3
- import { type Do, type Effect, type ToAsyncOperationMap } from '../module.f.ts';
3
+ import { type Effect, type Func, type Operation, type ToAsyncOperationMap } from '../module.f.ts';
4
4
  export type IoResult<T> = Result<T, unknown>;
5
- export type Fetch = {
6
- readonly fetch: readonly [string, IoResult<Vec>];
7
- };
8
- export declare const fetch: (url: string) => Do<Fetch, IoResult<Vec>>;
5
+ export type All = ['all', <T>(_: readonly Effect<never, T>[]) => readonly T[]];
6
+ /**
7
+ * To run the operation `O` should be known by the runner/engine.
8
+ * This is the reason why we merge `O` with `All` in the resulted `Effect`.
9
+ *
10
+ * @param a
11
+ * @returns
12
+ */
13
+ export declare const all: <O extends Operation, T>(...a: readonly Effect<O, T>[]) => Effect<O | All, readonly T[]>;
14
+ export type Fetch = ['fetch', (_: string) => IoResult<Vec>];
15
+ export declare const fetch: Func<Fetch>;
9
16
  export type MakeDirectoryOptions = {
10
17
  readonly recursive: true;
11
18
  };
12
19
  export type MkdirParam = readonly [string, MakeDirectoryOptions?];
13
- export type Mkdir = {
14
- readonly mkdir: readonly [MkdirParam, IoResult<void>];
15
- };
16
- export declare const mkdir: (...p: MkdirParam) => Do<Mkdir, IoResult<void>>;
17
- export type ReadFile = {
18
- readonly readFile: readonly [string, IoResult<Vec>];
19
- };
20
- export declare const readFile: (path: string) => Do<ReadFile, IoResult<Vec>>;
20
+ export type Mkdir = readonly ['mkdir', (_: MkdirParam) => IoResult<void>];
21
+ export declare const mkdir: (...p: MkdirParam) => Effect<Mkdir, IoResult<void>>;
22
+ export type ReadFile = readonly ['readFile', (_: string) => IoResult<Vec>];
23
+ export declare const readFile: Func<ReadFile>;
21
24
  /**
22
25
  * Represents a directory entry (file or directory) in the filesystem
23
26
  * @see https://nodejs.org/api/fs.html#class-fsdirent
@@ -31,26 +34,18 @@ export type ReaddirOptions = {
31
34
  readonly recursive?: true;
32
35
  };
33
36
  export type ReaddirParam = readonly [string, ReaddirOptions];
34
- export type Readdir = {
35
- readonly readdir: readonly [ReaddirParam, IoResult<readonly Dirent[]>];
36
- };
37
- export declare const readdir: (...p: ReaddirParam) => Do<Readdir, IoResult<readonly Dirent[]>>;
37
+ export type Readdir = readonly ['readdir', (_: ReaddirParam) => IoResult<readonly Dirent[]>];
38
+ export declare const readdir: (...p: ReaddirParam) => Effect<Readdir, IoResult<readonly Dirent[]>>;
38
39
  export type WriteFileParam = readonly [string, Vec];
39
- export type WriteFile = {
40
- readonly writeFile: readonly [WriteFileParam, IoResult<void>];
41
- };
42
- export declare const writeFile: (...p: WriteFileParam) => Do<WriteFile, IoResult<void>>;
43
- export type Fs = Mkdir & ReadFile & Readdir & WriteFile;
44
- export type Error = {
45
- readonly error: readonly [string, void];
46
- };
47
- export declare const error: (msg: string) => Do<Error, void>;
48
- export type Log = {
49
- readonly log: readonly [string, void];
50
- };
51
- export declare const log: (msg: string) => Do<Log, void>;
52
- export type Console = Log & Error;
53
- export type NodeOperations = Fetch & Console & Fs;
54
- export type NodeEffect<T> = Effect<NodeOperations, T>;
55
- export type NodeOperationMap = ToAsyncOperationMap<NodeOperations>;
56
- export type NodeProgram = (argv: readonly string[]) => NodeEffect<number>;
40
+ export type WriteFile = readonly ['writeFile', (_: WriteFileParam) => IoResult<void>];
41
+ export declare const writeFile: (...p: WriteFileParam) => Effect<WriteFile, IoResult<void>>;
42
+ export type Fs = Mkdir | ReadFile | Readdir | WriteFile;
43
+ export type Error = ['error', (_: string) => void];
44
+ export declare const error: Func<Error>;
45
+ export type Log = ['log', (_: string) => void];
46
+ export declare const log: Func<Log>;
47
+ export type Console = Log | Error;
48
+ export type NodeOp = All | Fetch | Console | Fs;
49
+ export type NodeEffect<T> = Effect<NodeOp, T>;
50
+ export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
51
+ export type NodeProgram = (argv: readonly string[]) => Effect<NodeOp, number>;
@@ -1,8 +1,19 @@
1
1
  import { do_ } from "../module.f.js";
2
- export const fetch = (url) => do_('fetch', url);
3
- export const mkdir = (...p) => do_('mkdir', p);
4
- export const readFile = (path) => do_('readFile', path);
5
- export const readdir = (...p) => do_('readdir', p);
6
- export const writeFile = (...p) => do_('writeFile', p);
7
- export const error = (msg) => do_('error', msg);
8
- export const log = (msg) => do_('log', msg);
2
+ /**
3
+ * To run the operation `O` should be known by the runner/engine.
4
+ * This is the reason why we merge `O` with `All` in the resulted `Effect`.
5
+ *
6
+ * @param a
7
+ * @returns
8
+ */
9
+ export const all = (...a) => {
10
+ const result = do_('all')(a);
11
+ return result;
12
+ };
13
+ export const fetch = do_('fetch');
14
+ export const mkdir = (...p) => do_('mkdir')(p);
15
+ export const readFile = do_('readFile');
16
+ export const readdir = (...p) => do_('readdir')(p);
17
+ export const writeFile = (...p) => do_('writeFile')(p);
18
+ export const error = do_('error');
19
+ export const log = do_('log');
@@ -1,26 +1,27 @@
1
1
  import { empty, isVec, uint, vec8 } from "../../bit_vec/module.f.js";
2
- import { map } from "../module.f.js";
3
2
  import { run } from "../mock/module.f.js";
3
+ import { pure } from "../module.f.js";
4
4
  import { fetch, mkdir, readdir, readFile, writeFile } from "./module.f.js";
5
5
  import { emptyState, virtual } from "./virtual/module.f.js";
6
6
  export default {
7
7
  map: () => {
8
- let e = map(readFile('hello'))(([k, v]) => {
8
+ let e = readFile('hello').step(([k, v]) => {
9
9
  if (k === 'error') {
10
10
  throw v;
11
11
  }
12
- return uint(v) * 2n;
12
+ return pure(uint(v) * 2n);
13
13
  });
14
14
  //
15
15
  while (true) {
16
- if (e.length === 1) {
17
- const result = e[0];
16
+ const { value } = e;
17
+ if (value.length === 1) {
18
+ const [result] = value;
18
19
  if (result !== 0x2an) {
19
20
  throw result;
20
21
  }
21
22
  return;
22
23
  }
23
- const [cmd, p, cont] = e;
24
+ const [cmd, p, cont] = value;
24
25
  if (cmd !== 'readFile') {
25
26
  throw cmd;
26
27
  }
@@ -31,8 +32,7 @@ export default {
31
32
  }
32
33
  },
33
34
  fetch: () => {
34
- const v = run(virtual);
35
- const [_, [t, result]] = v({
35
+ const [_, [t, result]] = virtual({
36
36
  ...emptyState,
37
37
  internet: {
38
38
  'https://example.com/data': vec8(0x2an),
@@ -50,8 +50,7 @@ export default {
50
50
  },
51
51
  mkdir: {
52
52
  one: () => {
53
- const v = run(virtual);
54
- const [state, [t, result]] = v(emptyState)(mkdir('a'));
53
+ const [state, [t, result]] = virtual(emptyState)(mkdir('a'));
55
54
  if (t === 'error') {
56
55
  throw result;
57
56
  }
@@ -61,7 +60,7 @@ export default {
61
60
  }
62
61
  },
63
62
  rec: () => {
64
- const [state, [t, result]] = run(virtual)(emptyState)(mkdir('tmp/cache', { recursive: true }));
63
+ const [state, [t, result]] = virtual(emptyState)(mkdir('tmp/cache', { recursive: true }));
65
64
  if (t !== 'ok') {
66
65
  throw result;
67
66
  }
@@ -75,7 +74,7 @@ export default {
75
74
  }
76
75
  },
77
76
  nonRec: () => {
78
- const [state, [t, result]] = run(virtual)(emptyState)(mkdir('tmp/cache'));
77
+ const [state, [t, result]] = virtual(emptyState)(mkdir('tmp/cache'));
79
78
  if (t !== 'error') {
80
79
  throw result;
81
80
  }
@@ -86,14 +85,13 @@ export default {
86
85
  },
87
86
  readFile: {
88
87
  one: () => {
89
- const v = run(virtual);
90
88
  const initial = {
91
89
  ...emptyState,
92
90
  root: {
93
91
  hello: vec8(0x2an),
94
92
  },
95
93
  };
96
- const [state, [t, result]] = v(initial)(readFile('hello'));
94
+ const [state, [t, result]] = virtual(initial)(readFile('hello'));
97
95
  if (t === 'error') {
98
96
  throw result;
99
97
  }
@@ -108,7 +106,7 @@ export default {
108
106
  }
109
107
  },
110
108
  nested: () => {
111
- const [_, [tag, result]] = run(virtual)({
109
+ const [_, [tag, result]] = virtual({
112
110
  ...emptyState,
113
111
  root: { tmp: { cache: vec8(0x15n) } }
114
112
  })(readFile('tmp/cache'));
@@ -120,13 +118,13 @@ export default {
120
118
  }
121
119
  },
122
120
  noSuchFile: () => {
123
- const [_, [t, result]] = run(virtual)(emptyState)(readFile('hello'));
121
+ const [_, [t, result]] = virtual(emptyState)(readFile('hello'));
124
122
  if (t !== 'error') {
125
123
  throw result;
126
124
  }
127
125
  },
128
126
  nestedPath: () => {
129
- const [_, [t, result]] = run(virtual)(emptyState)(readFile('tmp/cache'));
127
+ const [_, [t, result]] = virtual(emptyState)(readFile('tmp/cache'));
130
128
  if (t !== 'error') {
131
129
  throw result;
132
130
  }
@@ -137,7 +135,7 @@ export default {
137
135
  },
138
136
  readdir: {
139
137
  one: () => {
140
- const [_, [t, result]] = run(virtual)({
138
+ const [_, [t, result]] = virtual({
141
139
  ...emptyState,
142
140
  root: {
143
141
  file: vec8(0x2an),
@@ -159,7 +157,7 @@ export default {
159
157
  }
160
158
  },
161
159
  nonRecursive: () => {
162
- const [_, [t, result]] = run(virtual)({
160
+ const [_, [t, result]] = virtual({
163
161
  ...emptyState,
164
162
  root: {
165
163
  file: vec8(0x2an),
@@ -184,7 +182,7 @@ export default {
184
182
  }
185
183
  },
186
184
  nested: () => {
187
- const [_, [t, result]] = run(virtual)({
185
+ const [_, [t, result]] = virtual({
188
186
  ...emptyState,
189
187
  root: { tmp: { cache: vec8(0x15n) } }
190
188
  })(readdir('tmp', { recursive: true }));
@@ -203,7 +201,7 @@ export default {
203
201
  }
204
202
  },
205
203
  noSuchDir: () => {
206
- const [_, [t, result]] = run(virtual)(emptyState)(readdir('tmp', { recursive: true }));
204
+ const [_, [t, result]] = virtual(emptyState)(readdir('tmp', { recursive: true }));
207
205
  if (t !== 'error') {
208
206
  throw result;
209
207
  }
@@ -214,7 +212,7 @@ export default {
214
212
  },
215
213
  writeFile: {
216
214
  one: () => {
217
- const [state, [t, result]] = run(virtual)(emptyState)(writeFile('hello', vec8(0x2an)));
215
+ const [state, [t, result]] = virtual(emptyState)(writeFile('hello', vec8(0x2an)));
218
216
  if (t !== 'ok') {
219
217
  throw result;
220
218
  }
@@ -227,7 +225,7 @@ export default {
227
225
  }
228
226
  },
229
227
  overwrite: () => {
230
- const [state, [t, result]] = run(virtual)({
228
+ const [state, [t, result]] = virtual({
231
229
  ...emptyState,
232
230
  root: {
233
231
  hello: vec8(0x15n),
@@ -245,7 +243,7 @@ export default {
245
243
  }
246
244
  },
247
245
  nestedPath: () => {
248
- const [state, [t, result]] = run(virtual)(emptyState)(writeFile('tmp/cache', vec8(0x2an)));
246
+ const [state, [t, result]] = virtual(emptyState)(writeFile('tmp/cache', vec8(0x2an)));
249
247
  if (t !== 'error') {
250
248
  throw result;
251
249
  }
@@ -257,7 +255,7 @@ export default {
257
255
  }
258
256
  },
259
257
  directory: () => {
260
- const [state, [t, result]] = run(virtual)({
258
+ const [state, [t, result]] = virtual({
261
259
  ...emptyState,
262
260
  root: {
263
261
  tmp: {},
@@ -1,16 +1,16 @@
1
1
  import { type Vec } from "../../../bit_vec/module.f.ts";
2
- import type { MemOperationMap } from "../../mock/module.f.ts";
3
- import type { NodeOperations } from "../module.f.ts";
4
- export type VirtualDir = {
5
- readonly [name in string]?: VirtualDir | Vec;
2
+ import { type RunInstance } from "../../mock/module.f.ts";
3
+ import type { NodeOp } from "../module.f.ts";
4
+ export type Dir = {
5
+ readonly [name in string]?: Dir | Vec;
6
6
  };
7
- export type VirtualState = {
7
+ export type State = {
8
8
  stdout: string;
9
9
  stderr: string;
10
- root: VirtualDir;
10
+ root: Dir;
11
11
  internet: {
12
12
  readonly [url: string]: Vec;
13
13
  };
14
14
  };
15
- export declare const emptyState: VirtualState;
16
- export declare const virtual: MemOperationMap<NodeOperations, VirtualState>;
15
+ export declare const emptyState: State;
16
+ export declare const virtual: RunInstance<NodeOp, State>;
@@ -6,6 +6,8 @@
6
6
  import { parse } from "../../../../path/module.f.js";
7
7
  import { isVec } from "../../../bit_vec/module.f.js";
8
8
  import { error, ok } from "../../../result/module.f.js";
9
+ import { run } from "../../mock/module.f.js";
10
+ import { pure } from "../../module.f.js";
9
11
  export const emptyState = {
10
12
  stdout: '',
11
13
  stderr: '',
@@ -30,7 +32,6 @@ const operation = (op) => {
30
32
  return [{ ...state, root }, result];
31
33
  };
32
34
  };
33
- // TODO: we can have a better implementation with some code shared with `operation`.
34
35
  const readOperation = (op) => operation((dir, path) => [dir, op(dir, path)]);
35
36
  const okVoid = ok(undefined);
36
37
  const mkdir = (recursive) => operation((dir, path) => {
@@ -94,7 +95,16 @@ const readdir = (base, recursive) => readOperation((dir, path) => {
94
95
  return ok(f(base, dir));
95
96
  });
96
97
  const console = (name) => (state, payload) => [{ ...state, [name]: `${state[name]}${payload}\n` }, undefined];
97
- export const virtual = {
98
+ const map = {
99
+ all: (state, a) => {
100
+ let e = [];
101
+ for (const i of a) {
102
+ const [ns, ei] = virtual(state)(i);
103
+ state = ns;
104
+ e = [...e, ei];
105
+ }
106
+ return [state, e];
107
+ },
98
108
  error: console('stderr'),
99
109
  log: console('stdout'),
100
110
  fetch: (state, url) => {
@@ -106,3 +116,4 @@ export const virtual = {
106
116
  readdir: (state, [path, { recursive }]) => readdir(path, recursive === true)(state, path),
107
117
  writeFile: (state, [path, payload]) => writeFile(payload)(state, path),
108
118
  };
119
+ export const virtual = run(map);
@@ -1,2 +1,4 @@
1
- declare const _default: () => import("../types/effects/module.f.ts").Pure<number>;
1
+ import { type NodeOp } from '../types/effects/node/module.f.ts';
2
+ import { type Effect } from '../types/effects/module.f.ts';
3
+ declare const _default: () => Effect<NodeOp, number>;
2
4
  export default _default;
@@ -6,12 +6,11 @@
6
6
  import { htmlToString } from "../html/module.f.js";
7
7
  import { writeFile } from "../types/effects/node/module.f.js";
8
8
  import { utf8 } from "../text/module.f.js";
9
- import { fluent, pure } from "../types/effects/module.f.js";
9
+ import { begin, pure } from "../types/effects/module.f.js";
10
10
  const html = ['body',
11
11
  ['a', { href: 'https://github.com/functionalscript/functionalscript' }, 'GitHub Repository']
12
12
  ];
13
- const program = fluent
13
+ const program = begin
14
14
  .step(() => writeFile('index.html', utf8(htmlToString(html))))
15
- .step(() => pure(0))
16
- .effect;
15
+ .step(() => pure(0));
17
16
  export default () => program;
package/ci/module.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/ci/module.js DELETED
@@ -1,3 +0,0 @@
1
- import run from "../io/module.js";
2
- import app from "./module.f.js";
3
- run(app);
@@ -1 +0,0 @@
1
- export {};
package/website/module.js DELETED
@@ -1,3 +0,0 @@
1
- import run from "../io/module.js";
2
- import app from "./module.f.js";
3
- run(app);