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.
- package/bnf/data/module.f.d.ts +67 -1
- package/bnf/data/module.f.js +27 -0
- package/bnf/module.f.js +8 -0
- package/cas/module.f.d.ts +6 -6
- package/cas/module.f.js +22 -32
- package/ci/module.f.d.ts +4 -3
- package/ci/module.f.js +3 -4
- package/dev/module.f.d.ts +3 -0
- package/dev/module.f.js +44 -16
- package/dev/version/module.f.d.ts +3 -2
- package/dev/version/module.f.js +10 -10
- package/dev/version/test.f.js +3 -6
- package/io/module.f.d.ts +4 -2
- package/io/module.f.js +24 -18
- package/package.json +3 -3
- package/types/effects/mock/module.f.d.ts +5 -4
- package/types/effects/mock/module.f.js +6 -4
- package/types/effects/module.d.ts +2 -2
- package/types/effects/module.f.d.ts +17 -19
- package/types/effects/module.f.js +76 -16
- package/types/effects/module.js +4 -3
- package/types/effects/node/module.f.d.ts +30 -35
- package/types/effects/node/module.f.js +18 -7
- package/types/effects/node/test.f.js +23 -25
- package/types/effects/node/virtual/module.f.d.ts +8 -8
- package/types/effects/node/virtual/module.f.js +13 -2
- package/website/module.f.d.ts +3 -1
- package/website/module.f.js +3 -4
- package/ci/module.d.ts +0 -1
- package/ci/module.js +0 -3
- package/website/module.d.ts +0 -1
- package/website/module.js +0 -3
package/bnf/data/module.f.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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 {};
|
package/bnf/data/module.f.js
CHANGED
|
@@ -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
|
|
9
|
-
import { type Fs, type
|
|
10
|
-
export type KvStore<O extends
|
|
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
|
|
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
|
|
23
|
-
export declare const main: (args: readonly string[]) => Effect<
|
|
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 {
|
|
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) =>
|
|
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
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
-
:
|
|
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
|
|
96
|
+
return begin
|
|
105
97
|
.step(() => c.list())
|
|
106
98
|
.step(v => {
|
|
107
99
|
// TODO: make it lazy.
|
|
108
|
-
let i =
|
|
100
|
+
let i = begin;
|
|
109
101
|
for (const j of v) {
|
|
110
102
|
const prev = i;
|
|
111
|
-
i =
|
|
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
|
|
2
|
-
|
|
3
|
-
declare const
|
|
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 {
|
|
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 =
|
|
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 {
|
|
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 =
|
|
88
|
+
const index2 = begin
|
|
62
89
|
.step(() => updateVersion)
|
|
63
90
|
.step(() => readFile(denoJson))
|
|
64
|
-
.step(v => pure(JSON.parse(utf8ToString(unwrap(v)))))
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const list =
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
2
|
-
|
|
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>;
|
package/dev/version/module.f.js
CHANGED
|
@@ -4,24 +4,24 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { utf8, utf8ToString } from "../../text/module.f.js";
|
|
7
|
-
import {
|
|
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) =>
|
|
12
|
+
const readJson = (name) => begin
|
|
13
13
|
.step(() => readFile(jsonFile(name)))
|
|
14
|
-
.step(v => pure(parse(utf8ToString(unwrap(v)))))
|
|
15
|
-
|
|
16
|
-
|
|
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 =
|
|
21
|
+
export const updateVersion = begin
|
|
21
22
|
.step(() => readJson('package'))
|
|
22
23
|
.step(p => {
|
|
23
24
|
const w = writeVersion(p.version);
|
|
24
|
-
return all(
|
|
25
|
+
return all(w('package'), w('deno'));
|
|
25
26
|
})
|
|
26
|
-
.step(() => pure(0))
|
|
27
|
-
.effect;
|
|
27
|
+
.step(() => pure(0));
|
package/dev/version/test.f.js
CHANGED
|
@@ -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 {
|
|
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] =
|
|
90
|
-
const [newState, result] =
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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, }) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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.
|
|
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,
|
|
7
|
-
export type MemOperationMap<O extends
|
|
8
|
-
readonly [K in
|
|
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
|
|
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) =>
|
|
1
|
+
export const run = (o) => state => effect => {
|
|
2
2
|
let s = state;
|
|
3
3
|
let e = effect;
|
|
4
4
|
while (true) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const { value } = e;
|
|
6
|
+
if (value.length === 1) {
|
|
7
|
+
const [v] = value;
|
|
8
|
+
return [s, v];
|
|
7
9
|
}
|
|
8
|
-
const [cmd, payload, cont] =
|
|
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,
|
|
2
|
-
export declare const asyncRun: <O extends
|
|
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
|
|
7
|
-
|
|
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
|
|
11
|
+
export type Value<O extends Operation, T> = Pure<T> | Do<O, T>;
|
|
10
12
|
export type Pure<T> = readonly [T];
|
|
11
|
-
export type
|
|
12
|
-
export type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export declare const pure: <T>(
|
|
16
|
-
export declare const
|
|
17
|
-
export
|
|
18
|
-
|
|
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
|
|
21
|
-
export
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
13
|
-
const
|
|
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
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
export const fluent: Fluent<{}, void> = wrap(pure(undefined))
|
|
78
|
+
|
|
79
|
+
const empty: Effect<{}, readonly never[]> = pure([])
|
|
80
|
+
*/
|
package/types/effects/module.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export const asyncRun = (map) => async (effect) => {
|
|
2
2
|
while (true) {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const { value } = effect;
|
|
4
|
+
if (value.length === 1) {
|
|
5
|
+
return value[0];
|
|
5
6
|
}
|
|
6
|
-
const [command, payload, continuation] =
|
|
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
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
export declare const
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
export
|
|
43
|
-
export
|
|
44
|
-
export type
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export
|
|
48
|
-
export type
|
|
49
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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 =
|
|
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
|
-
|
|
17
|
-
|
|
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] =
|
|
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
|
|
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
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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]] =
|
|
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
|
|
3
|
-
import type {
|
|
4
|
-
export type
|
|
5
|
-
readonly [name in string]?:
|
|
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
|
|
7
|
+
export type State = {
|
|
8
8
|
stdout: string;
|
|
9
9
|
stderr: string;
|
|
10
|
-
root:
|
|
10
|
+
root: Dir;
|
|
11
11
|
internet: {
|
|
12
12
|
readonly [url: string]: Vec;
|
|
13
13
|
};
|
|
14
14
|
};
|
|
15
|
-
export declare const emptyState:
|
|
16
|
-
export declare const virtual:
|
|
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
|
-
|
|
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);
|
package/website/module.f.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
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;
|
package/website/module.f.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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
package/website/module.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/website/module.js
DELETED