@elyukai/utils 0.2.8 → 0.3.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/CHANGELOG.md +26 -0
- package/dist/{array → src/array}/filters.js +4 -4
- package/dist/{array → src/array}/reductions.js +2 -2
- package/dist/src/array/transformations.d.ts +27 -0
- package/dist/src/array/transformations.js +34 -0
- package/dist/{async.js → src/async.js} +2 -2
- package/dist/{classList.js → src/classList.js} +1 -1
- package/dist/{equality.js → src/equality.js} +3 -8
- package/dist/{function.js → src/function.js} +2 -2
- package/dist/{maybe.js → src/maybe.js} +2 -4
- package/dist/{nullable.js → src/nullable.js} +1 -1
- package/dist/{object.js → src/object.js} +1 -1
- package/dist/src/parser.d.ts +146 -0
- package/dist/src/parser.js +219 -0
- package/dist/{reader.js → src/reader.js} +10 -10
- package/dist/{result.js → src/result.js} +3 -3
- package/dist/src/state.d.ts +51 -0
- package/dist/src/state.js +79 -0
- package/dist/src/stateParser.d.ts +143 -0
- package/dist/src/stateParser.js +219 -0
- package/dist/{string → src/string}/number.js +3 -11
- package/dist/{string.js → src/string.js} +6 -13
- package/package.json +6 -6
- package/dist/array/generators.d.ts +0 -13
- package/dist/array/generators.js +0 -17
- /package/dist/{array → src/array}/filters.d.ts +0 -0
- /package/dist/{array → src/array}/fixed.d.ts +0 -0
- /package/dist/{array → src/array}/fixed.js +0 -0
- /package/dist/{array → src/array}/groups.d.ts +0 -0
- /package/dist/{array → src/array}/groups.js +0 -0
- /package/dist/{array → src/array}/modify.d.ts +0 -0
- /package/dist/{array → src/array}/modify.js +0 -0
- /package/dist/{array → src/array}/nonEmpty.d.ts +0 -0
- /package/dist/{array → src/array}/nonEmpty.js +0 -0
- /package/dist/{array → src/array}/reductions.d.ts +0 -0
- /package/dist/{array → src/array}/sets.d.ts +0 -0
- /package/dist/{array → src/array}/sets.js +0 -0
- /package/dist/{async.d.ts → src/async.d.ts} +0 -0
- /package/dist/{classList.d.ts → src/classList.d.ts} +0 -0
- /package/dist/{dictionary → src/dictionary}/native.d.ts +0 -0
- /package/dist/{dictionary → src/dictionary}/native.js +0 -0
- /package/dist/{dictionary.d.ts → src/dictionary.d.ts} +0 -0
- /package/dist/{dictionary.js → src/dictionary.js} +0 -0
- /package/dist/{equality.d.ts → src/equality.d.ts} +0 -0
- /package/dist/{function.d.ts → src/function.d.ts} +0 -0
- /package/dist/{lazy.d.ts → src/lazy.d.ts} +0 -0
- /package/dist/{lazy.js → src/lazy.js} +0 -0
- /package/dist/{maybe.d.ts → src/maybe.d.ts} +0 -0
- /package/dist/{nullable.d.ts → src/nullable.d.ts} +0 -0
- /package/dist/{number.d.ts → src/number.d.ts} +0 -0
- /package/dist/{number.js → src/number.js} +0 -0
- /package/dist/{object.d.ts → src/object.d.ts} +0 -0
- /package/dist/{ordering.d.ts → src/ordering.d.ts} +0 -0
- /package/dist/{ordering.js → src/ordering.js} +0 -0
- /package/dist/{range.d.ts → src/range.d.ts} +0 -0
- /package/dist/{range.js → src/range.js} +0 -0
- /package/dist/{reader.d.ts → src/reader.d.ts} +0 -0
- /package/dist/{result.d.ts → src/result.d.ts} +0 -0
- /package/dist/{roman.d.ts → src/roman.d.ts} +0 -0
- /package/dist/{roman.js → src/roman.js} +0 -0
- /package/dist/{string → src/string}/number.d.ts +0 -0
- /package/dist/{string → src/string}/regex.d.ts +0 -0
- /package/dist/{string → src/string}/regex.js +0 -0
- /package/dist/{string.d.ts → src/string.d.ts} +0 -0
- /package/dist/{typeSafety.d.ts → src/typeSafety.d.ts} +0 -0
- /package/dist/{typeSafety.js → src/typeSafety.js} +0 -0
|
@@ -24,31 +24,31 @@ export class Reader {
|
|
|
24
24
|
* Retrieve the entire environment.
|
|
25
25
|
*/
|
|
26
26
|
static ask() {
|
|
27
|
-
return new Reader(
|
|
27
|
+
return new Reader(env => env);
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Retrieve a transformed value of the environment.
|
|
31
31
|
*/
|
|
32
32
|
static asks(f) {
|
|
33
|
-
return new Reader(
|
|
33
|
+
return new Reader(env => f(env));
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Applies the given function to the value produced by this Reader and returns a new Reader that produces the result.
|
|
37
37
|
*/
|
|
38
38
|
map(f) {
|
|
39
|
-
return new Reader(
|
|
39
|
+
return new Reader(env => f(this.#fn(env)));
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Combines this Reader with another Reader by applying a function to both of their results and returning a new Reader that produces the result.
|
|
43
43
|
*/
|
|
44
44
|
map2(other, f) {
|
|
45
|
-
return new Reader(
|
|
45
|
+
return new Reader(env => f(this.#fn(env), other.#fn(env)));
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* Applies the given function to the value produced by this Reader and returns a new Reader that produces the result. The function must return a Reader, which allows you to chain computations that depend on the same environment.
|
|
49
49
|
*/
|
|
50
50
|
then(f) {
|
|
51
|
-
return new Reader(
|
|
51
|
+
return new Reader(env => f(this.#fn(env)).#fn(env));
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Applies the given function to the value produced by this Reader and returns a new Reader that produces the result. The function must return a Reader, which allows you to chain computations that depend on a shared environment. The environments do not have to be the same, but will need to be combined.
|
|
@@ -56,7 +56,7 @@ export class Reader {
|
|
|
56
56
|
* The suffix ‘W’ stands for ‘widening’, as this method allows you to widen the environment that the Reader depends on.
|
|
57
57
|
*/
|
|
58
58
|
thenW(f) {
|
|
59
|
-
return new Reader(
|
|
59
|
+
return new Reader(env => f(this.#fn(env)).#fn(env));
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Modifies the environment for this Reader by applying the given function to it before running the computation.
|
|
@@ -64,7 +64,7 @@ export class Reader {
|
|
|
64
64
|
* This allows you to adapt the environment for a specific computation without changing the original Reader.
|
|
65
65
|
*/
|
|
66
66
|
with(modifyEnv) {
|
|
67
|
-
return new Reader(
|
|
67
|
+
return new Reader(env => this.#fn(modifyEnv(env)));
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Computes the result by providing the required environment.
|
|
@@ -76,7 +76,7 @@ export class Reader {
|
|
|
76
76
|
* Apply a function to each value in the array, and return a Reader that produces an array of the results.
|
|
77
77
|
*/
|
|
78
78
|
static traverse(values, fn) {
|
|
79
|
-
return new Reader(
|
|
79
|
+
return new Reader(env => values.map(value => fn(value).run(env)));
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* Evaluate each reader in the array, and collect the results in a single reader instance.
|
|
@@ -90,7 +90,7 @@ export class Reader {
|
|
|
90
90
|
* This makes functions producing Readers that depend on more arguments sometimes easier to work with.
|
|
91
91
|
*/
|
|
92
92
|
static defer(fn) {
|
|
93
|
-
return new Reader(
|
|
93
|
+
return new Reader(env => (...args) => fn(...args).run(env));
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
96
|
* Transforms a Reader that produces a function into a function that returns a Reader.
|
|
@@ -98,7 +98,7 @@ export class Reader {
|
|
|
98
98
|
* This is the inverse of {@link defer}.
|
|
99
99
|
*/
|
|
100
100
|
static undefer(reader) {
|
|
101
|
-
return (...args) => new Reader(
|
|
101
|
+
return (...args) => new Reader(env => reader.run(env)(...args));
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
// type Readable<T> =
|
|
@@ -28,15 +28,15 @@ export const reduce = (result, fok, ferror) => (isOk(result) ? fok(result.value)
|
|
|
28
28
|
/**
|
|
29
29
|
* Maps the value of a result to a new value.
|
|
30
30
|
*/
|
|
31
|
-
export const map = (result, f) =>
|
|
31
|
+
export const map = (result, f) => isOk(result) ? ok(f(result.value)) : result;
|
|
32
32
|
/**
|
|
33
33
|
* Maps an error to a new error.
|
|
34
34
|
*/
|
|
35
|
-
export const mapError = (result, f) =>
|
|
35
|
+
export const mapError = (result, f) => isError(result) ? error(f(result.error)) : result;
|
|
36
36
|
/**
|
|
37
37
|
* Chains a result to a new result.
|
|
38
38
|
*/
|
|
39
|
-
export const then = (result, f) =>
|
|
39
|
+
export const then = (result, f) => isOk(result) ? f(result.value) : result;
|
|
40
40
|
/**
|
|
41
41
|
* Combines two results into one.
|
|
42
42
|
*
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The {@link State} class representing a state transformation function and includes methods to chain functions effectively.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A class representing a state transformation function that takes an initial state and returns a tuple containing a value and a new state.
|
|
7
|
+
*/
|
|
8
|
+
export declare class State<S, T> {
|
|
9
|
+
#private;
|
|
10
|
+
private constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Returns a new {@link State} instance that returns the state as the resulting value without modifying the state.
|
|
13
|
+
*/
|
|
14
|
+
static get<S>(): State<S, S>;
|
|
15
|
+
/**
|
|
16
|
+
* Applies the state transformation function to the given state and returns a tuple containing the resulting value and the new state.
|
|
17
|
+
*/
|
|
18
|
+
run(state: S): [T, S];
|
|
19
|
+
/**
|
|
20
|
+
* Applies the state transformation function to the given state and returns the resulting value, discarding the new state.
|
|
21
|
+
*/
|
|
22
|
+
eval(state: S): T;
|
|
23
|
+
/**
|
|
24
|
+
* Applies the state transformation function to the given state and returns the new state, discarding the resulting value.
|
|
25
|
+
*/
|
|
26
|
+
exec(state: S): S;
|
|
27
|
+
/**
|
|
28
|
+
* Returns a new {@link State} instance that returns the given value without modifying the state.
|
|
29
|
+
*/
|
|
30
|
+
static of<S, T>(value: T): State<S, T>;
|
|
31
|
+
/**
|
|
32
|
+
* Transforms the value produced by this state transformation function using the given function, while keeping the state unchanged.
|
|
33
|
+
*/
|
|
34
|
+
map<U>(f: (value: T) => U): State<S, U>;
|
|
35
|
+
/**
|
|
36
|
+
* Transforms the value and state produced by this state transformation function using the given function.
|
|
37
|
+
*/
|
|
38
|
+
mapBoth<U>(f: (result: [T, S]) => [U, S]): State<S, U>;
|
|
39
|
+
/**
|
|
40
|
+
* Chains this state transformation function with another function that takes the value produced by this function and returns a new {@link State} instance, allowing for sequential state transformations.
|
|
41
|
+
*/
|
|
42
|
+
then<U>(f: (value: T) => State<S, U>): State<S, U>;
|
|
43
|
+
/**
|
|
44
|
+
* Returns a new {@link State} instance that applies the given function to the state and returns the resulting value without modifying the state.
|
|
45
|
+
*/
|
|
46
|
+
static gets<S, T>(f: (state: S) => T): State<S, T>;
|
|
47
|
+
/**
|
|
48
|
+
* Modifies the state using the given function before applying this state transformation function.
|
|
49
|
+
*/
|
|
50
|
+
with(f: (state: S) => S): State<S, T>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The {@link State} class representing a state transformation function and includes methods to chain functions effectively.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A class representing a state transformation function that takes an initial state and returns a tuple containing a value and a new state.
|
|
7
|
+
*/
|
|
8
|
+
export class State {
|
|
9
|
+
#fn;
|
|
10
|
+
constructor(fn) {
|
|
11
|
+
this.#fn = fn;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns a new {@link State} instance that returns the state as the resulting value without modifying the state.
|
|
15
|
+
*/
|
|
16
|
+
static get() {
|
|
17
|
+
return new State(state => [state, state]);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Applies the state transformation function to the given state and returns a tuple containing the resulting value and the new state.
|
|
21
|
+
*/
|
|
22
|
+
run(state) {
|
|
23
|
+
return this.#fn(state);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Applies the state transformation function to the given state and returns the resulting value, discarding the new state.
|
|
27
|
+
*/
|
|
28
|
+
eval(state) {
|
|
29
|
+
return this.run(state)[0];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Applies the state transformation function to the given state and returns the new state, discarding the resulting value.
|
|
33
|
+
*/
|
|
34
|
+
exec(state) {
|
|
35
|
+
return this.run(state)[1];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns a new {@link State} instance that returns the given value without modifying the state.
|
|
39
|
+
*/
|
|
40
|
+
static of(value) {
|
|
41
|
+
return new State(state => [value, state]);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Transforms the value produced by this state transformation function using the given function, while keeping the state unchanged.
|
|
45
|
+
*/
|
|
46
|
+
map(f) {
|
|
47
|
+
return new State(state => {
|
|
48
|
+
const [value, newState] = this.run(state);
|
|
49
|
+
return [f(value), newState];
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Transforms the value and state produced by this state transformation function using the given function.
|
|
54
|
+
*/
|
|
55
|
+
mapBoth(f) {
|
|
56
|
+
return new State(state => f(this.run(state)));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Chains this state transformation function with another function that takes the value produced by this function and returns a new {@link State} instance, allowing for sequential state transformations.
|
|
60
|
+
*/
|
|
61
|
+
then(f) {
|
|
62
|
+
return new State(state => {
|
|
63
|
+
const [value, newState] = this.run(state);
|
|
64
|
+
return f(value).run(newState);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Returns a new {@link State} instance that applies the given function to the state and returns the resulting value without modifying the state.
|
|
69
|
+
*/
|
|
70
|
+
static gets(f) {
|
|
71
|
+
return new State(state => [f(state), state]);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Modifies the state using the given function before applying this state transformation function.
|
|
75
|
+
*/
|
|
76
|
+
with(f) {
|
|
77
|
+
return new State(state => this.run(f(state)));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple stateful parser combinator.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import { Parser } from "./parser.js";
|
|
6
|
+
/**
|
|
7
|
+
* A class that combines the functionality of a parser with a state transformation function, allowing for more complex parsing scenarios where the parser can also manipulate some state during the parsing process.
|
|
8
|
+
*/
|
|
9
|
+
export declare class StateTParser<S, T> {
|
|
10
|
+
#private;
|
|
11
|
+
private constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Returns a new {@link StateTParser} instance that always succeeds with the given value while not modifying the state.
|
|
14
|
+
*/
|
|
15
|
+
static of<S, T>(value: T): StateTParser<S, T>;
|
|
16
|
+
/**
|
|
17
|
+
* Chains the state transformation function and the parser with a function that takes the produced value and reutns a new {@link StateTParser} instance, allowing for sequential state transformations and parsing operations.
|
|
18
|
+
*/
|
|
19
|
+
then<U>(f: (result: T) => StateTParser<S, U>): StateTParser<S, U>;
|
|
20
|
+
/**
|
|
21
|
+
* Wraps a parser into a {@link StateTParser}, allowing the parser to be used in the context of state transformations.
|
|
22
|
+
*/
|
|
23
|
+
static lift<S, T>(parser: Parser<T>): StateTParser<S, T>;
|
|
24
|
+
/**
|
|
25
|
+
* Applies the state transformation function to the given state and returns a tuple containing the resulting value and the new state.
|
|
26
|
+
*/
|
|
27
|
+
runT(state: S): Parser<[T, S]>;
|
|
28
|
+
/**
|
|
29
|
+
* Applies the state transformation function to the given state and returns the resulting value, discarding the new state.
|
|
30
|
+
*/
|
|
31
|
+
evalT(state: S): Parser<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Applies the state transformation function to the given state and returns the new state, discarding the resulting value.
|
|
34
|
+
*/
|
|
35
|
+
execT(state: S): Parser<S>;
|
|
36
|
+
/**
|
|
37
|
+
* Transforms the produced value using the given function, while keeping the state unchanged and not consuming any additional input.
|
|
38
|
+
*/
|
|
39
|
+
map<U>(f: (value: T) => U): StateTParser<S, U>;
|
|
40
|
+
/**
|
|
41
|
+
* Transforms the value and state produced by this state transformation function using the given function.
|
|
42
|
+
*/
|
|
43
|
+
mapT<U>(f: (result: Parser<[T, S]>) => Parser<[U, S]>): StateTParser<S, U>;
|
|
44
|
+
/**
|
|
45
|
+
* Returns a new {@link StateTParser} instance that returns the state as the resulting value without modifying the state.
|
|
46
|
+
*/
|
|
47
|
+
static getT<S>(): StateTParser<S, S>;
|
|
48
|
+
/**
|
|
49
|
+
* Returns a new {@link StateTParser} instance that applies the given function to the state and returns the resulting value without modifying the state.
|
|
50
|
+
*/
|
|
51
|
+
static getsT<S, T>(f: (state: S) => T): StateTParser<S, T>;
|
|
52
|
+
/**
|
|
53
|
+
* Modifies the state using the given function before applying this state transformation function.
|
|
54
|
+
*/
|
|
55
|
+
withT(f: (state: S) => S): StateTParser<S, T>;
|
|
56
|
+
/**
|
|
57
|
+
* Combines this parser with another parser, trying this parser first and then the other parser only if the first parser fails. It only returns the first successful result.
|
|
58
|
+
*/
|
|
59
|
+
orFirst(other: StateTParser<S, T>): StateTParser<S, T>;
|
|
60
|
+
/**
|
|
61
|
+
* Combines this parser with another parser, trying this parser first and then the other parser only if the first parser fails. It only returns the first successful result.
|
|
62
|
+
*
|
|
63
|
+
* The suffix ‘W’ stands for ‘widening’, as this method allows you to combine parsers with different result types.
|
|
64
|
+
*/
|
|
65
|
+
orFirstW<U>(other: StateTParser<S, U>): StateTParser<S, T | U>;
|
|
66
|
+
/**
|
|
67
|
+
* Returns a parser that tries this parser and returns its result if it succeeds, but if this parser fails, it returns a parser that always succeeds with `undefined` without consuming any input.
|
|
68
|
+
*/
|
|
69
|
+
optional(): StateTParser<S, T | undefined>;
|
|
70
|
+
/**
|
|
71
|
+
* Returns a parser that parses a specific string.
|
|
72
|
+
*/
|
|
73
|
+
static string<S, T extends string = string>(string: T): StateTParser<S, T>;
|
|
74
|
+
/**
|
|
75
|
+
* Returns a parser that parses zero or more occurrences of this parser, returning an array of the parsed results.
|
|
76
|
+
*/
|
|
77
|
+
many(): StateTParser<S, T[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Returns a parser that parses one or more occurrences of this parser, returning an array of the parsed results.
|
|
80
|
+
*
|
|
81
|
+
* It fails if this parser does not succeed at least once.
|
|
82
|
+
*/
|
|
83
|
+
many1(): StateTParser<S, T[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Returns a parser that parses zero or more occurrences of this parser, separated by another parser, returning an array of the parsed results.
|
|
86
|
+
*/
|
|
87
|
+
separatedBy(separator: StateTParser<S, unknown>): StateTParser<S, T[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Returns a parser that parses one or more occurrences of this parser, separated by another parser, returning an array of the parsed results.
|
|
90
|
+
*
|
|
91
|
+
* It fails if this parser does not succeed at least once.
|
|
92
|
+
*/
|
|
93
|
+
separatedBy1(separator: StateTParser<S, unknown>): StateTParser<S, T[]>;
|
|
94
|
+
/**
|
|
95
|
+
* Returns a parser that parses a string that matches the given regular expression pattern.
|
|
96
|
+
*
|
|
97
|
+
* The pattern must match at the beginning of the string (patterns that are designed like this might perform better), and the parser will return the matched substring as the parsed result.
|
|
98
|
+
*/
|
|
99
|
+
static regex<S>(pattern: RegExp): StateTParser<S, string>;
|
|
100
|
+
/**
|
|
101
|
+
* A parser that parses whitepace characters.
|
|
102
|
+
*/
|
|
103
|
+
static space<S>(): StateTParser<S, string>;
|
|
104
|
+
/**
|
|
105
|
+
* A parser that parses horizontal whitepace characters.
|
|
106
|
+
*
|
|
107
|
+
* This can be useful for parsing a language where newlines are significant.
|
|
108
|
+
*/
|
|
109
|
+
static hspace<S>(): StateTParser<S, string>;
|
|
110
|
+
/**
|
|
111
|
+
* Throws away trailing whitespace characters before applying the given parser.
|
|
112
|
+
*/
|
|
113
|
+
token(): StateTParser<S, T>;
|
|
114
|
+
/**
|
|
115
|
+
* Throws away trailing horizontal whitespace characters before applying the given parser.
|
|
116
|
+
*
|
|
117
|
+
* This can be useful for parsing a language where newlines are significant.
|
|
118
|
+
*/
|
|
119
|
+
htoken(): StateTParser<S, T>;
|
|
120
|
+
/**
|
|
121
|
+
* Returns a parser that parses a specific string, ignoring trailing whitespace characters.
|
|
122
|
+
*/
|
|
123
|
+
static symb<S, T extends string>(symbol: T): StateTParser<S, T>;
|
|
124
|
+
/**
|
|
125
|
+
* Returns a parser that parses a specific string, ignoring trailing horizontal whitespace characters.
|
|
126
|
+
*
|
|
127
|
+
* This can be useful for parsing a language where newlines are significant.
|
|
128
|
+
*/
|
|
129
|
+
static hsymb<S, T extends string>(symbol: T): StateTParser<S, T>;
|
|
130
|
+
/**
|
|
131
|
+
* Creates a parser that parses a value using the given parser, surrounded by the given left and right parsers, and returns the parsed value.
|
|
132
|
+
*/
|
|
133
|
+
between<L, R>(left: StateTParser<S, L>, right: StateTParser<S, R>): StateTParser<S, T>;
|
|
134
|
+
/**
|
|
135
|
+
* Returns a parser that applies the given parser to the input string without consuming any input, returning the result of the parser if it succeeds. If the parser fails, this lookahead parser also fails.
|
|
136
|
+
*/
|
|
137
|
+
static lookahead<S, T>(parser: StateTParser<S, T>): StateTParser<S, T>;
|
|
138
|
+
/**
|
|
139
|
+
* Returns a parser that applies the given parser to the input string without consuming any input, returning `undefined` if it fails. If the parser succeeds, this fails instead.
|
|
140
|
+
*/
|
|
141
|
+
static negativeLookahead<S>(parser: StateTParser<S, unknown>): StateTParser<S, undefined>;
|
|
142
|
+
static debugLog<S, T>(stateParser: StateTParser<S, T>): StateTParser<S, T>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple stateful parser combinator.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import { Parser } from "./parser.js";
|
|
6
|
+
/**
|
|
7
|
+
* A class that combines the functionality of a parser with a state transformation function, allowing for more complex parsing scenarios where the parser can also manipulate some state during the parsing process.
|
|
8
|
+
*/
|
|
9
|
+
export class StateTParser {
|
|
10
|
+
#fn;
|
|
11
|
+
constructor(fn) {
|
|
12
|
+
this.#fn = fn;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns a new {@link StateTParser} instance that always succeeds with the given value while not modifying the state.
|
|
16
|
+
*/
|
|
17
|
+
static of(value) {
|
|
18
|
+
return new StateTParser(state => Parser.of([value, state]));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Chains the state transformation function and the parser with a function that takes the produced value and reutns a new {@link StateTParser} instance, allowing for sequential state transformations and parsing operations.
|
|
22
|
+
*/
|
|
23
|
+
then(f) {
|
|
24
|
+
return new StateTParser(state => this.#fn(state).then(([result, newState]) => f(result).#fn(newState)));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Wraps a parser into a {@link StateTParser}, allowing the parser to be used in the context of state transformations.
|
|
28
|
+
*/
|
|
29
|
+
static lift(parser) {
|
|
30
|
+
return new StateTParser(state => parser.map(result => [result, state]));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Applies the state transformation function to the given state and returns a tuple containing the resulting value and the new state.
|
|
34
|
+
*/
|
|
35
|
+
runT(state) {
|
|
36
|
+
return this.#fn(state);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Applies the state transformation function to the given state and returns the resulting value, discarding the new state.
|
|
40
|
+
*/
|
|
41
|
+
evalT(state) {
|
|
42
|
+
return this.runT(state).map(result => result[0]);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Applies the state transformation function to the given state and returns the new state, discarding the resulting value.
|
|
46
|
+
*/
|
|
47
|
+
execT(state) {
|
|
48
|
+
return this.runT(state).map(result => result[1]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Transforms the produced value using the given function, while keeping the state unchanged and not consuming any additional input.
|
|
52
|
+
*/
|
|
53
|
+
map(f) {
|
|
54
|
+
return new StateTParser(state => this.runT(state).map(([value, newState]) => [f(value), newState]));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Transforms the value and state produced by this state transformation function using the given function.
|
|
58
|
+
*/
|
|
59
|
+
mapT(f) {
|
|
60
|
+
return new StateTParser(state => f(this.runT(state)));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns a new {@link StateTParser} instance that returns the state as the resulting value without modifying the state.
|
|
64
|
+
*/
|
|
65
|
+
static getT() {
|
|
66
|
+
return new StateTParser(state => Parser.of([state, state]));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns a new {@link StateTParser} instance that applies the given function to the state and returns the resulting value without modifying the state.
|
|
70
|
+
*/
|
|
71
|
+
static getsT(f) {
|
|
72
|
+
return new StateTParser(state => Parser.of([f(state), state]));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Modifies the state using the given function before applying this state transformation function.
|
|
76
|
+
*/
|
|
77
|
+
withT(f) {
|
|
78
|
+
return new StateTParser(state => this.runT(f(state)));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Combines this parser with another parser, trying this parser first and then the other parser only if the first parser fails. It only returns the first successful result.
|
|
82
|
+
*/
|
|
83
|
+
orFirst(other) {
|
|
84
|
+
return new StateTParser(state => {
|
|
85
|
+
const parser = this.#fn(state);
|
|
86
|
+
const otherParser = other.#fn(state);
|
|
87
|
+
return parser.orFirst(otherParser);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Combines this parser with another parser, trying this parser first and then the other parser only if the first parser fails. It only returns the first successful result.
|
|
92
|
+
*
|
|
93
|
+
* The suffix ‘W’ stands for ‘widening’, as this method allows you to combine parsers with different result types.
|
|
94
|
+
*/
|
|
95
|
+
orFirstW(other) {
|
|
96
|
+
return new StateTParser((state) => {
|
|
97
|
+
const parser = this.#fn(state);
|
|
98
|
+
const otherParser = other.#fn(state);
|
|
99
|
+
return parser.orFirstW(otherParser);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Returns a parser that tries this parser and returns its result if it succeeds, but if this parser fails, it returns a parser that always succeeds with `undefined` without consuming any input.
|
|
104
|
+
*/
|
|
105
|
+
optional() {
|
|
106
|
+
return this.orFirstW(StateTParser.of(undefined));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Returns a parser that parses a specific string.
|
|
110
|
+
*/
|
|
111
|
+
static string(string) {
|
|
112
|
+
return StateTParser.lift(Parser.string(string));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns a parser that parses zero or more occurrences of this parser, returning an array of the parsed results.
|
|
116
|
+
*/
|
|
117
|
+
many() {
|
|
118
|
+
return this.many1().orFirst(StateTParser.of([]));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Returns a parser that parses one or more occurrences of this parser, returning an array of the parsed results.
|
|
122
|
+
*
|
|
123
|
+
* It fails if this parser does not succeed at least once.
|
|
124
|
+
*/
|
|
125
|
+
many1() {
|
|
126
|
+
return this.then(result => this.many().then(results => StateTParser.of([result, ...results])));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Returns a parser that parses zero or more occurrences of this parser, separated by another parser, returning an array of the parsed results.
|
|
130
|
+
*/
|
|
131
|
+
separatedBy(separator) {
|
|
132
|
+
return this.separatedBy1(separator).orFirst(StateTParser.of([]));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Returns a parser that parses one or more occurrences of this parser, separated by another parser, returning an array of the parsed results.
|
|
136
|
+
*
|
|
137
|
+
* It fails if this parser does not succeed at least once.
|
|
138
|
+
*/
|
|
139
|
+
separatedBy1(separator) {
|
|
140
|
+
return this.then(result => separator
|
|
141
|
+
.then(() => this)
|
|
142
|
+
.many()
|
|
143
|
+
.then(results => StateTParser.of([result, ...results])));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Returns a parser that parses a string that matches the given regular expression pattern.
|
|
147
|
+
*
|
|
148
|
+
* The pattern must match at the beginning of the string (patterns that are designed like this might perform better), and the parser will return the matched substring as the parsed result.
|
|
149
|
+
*/
|
|
150
|
+
static regex(pattern) {
|
|
151
|
+
return StateTParser.lift(Parser.regex(pattern));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* A parser that parses whitepace characters.
|
|
155
|
+
*/
|
|
156
|
+
static space() {
|
|
157
|
+
return StateTParser.lift(Parser.space);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A parser that parses horizontal whitepace characters.
|
|
161
|
+
*
|
|
162
|
+
* This can be useful for parsing a language where newlines are significant.
|
|
163
|
+
*/
|
|
164
|
+
static hspace() {
|
|
165
|
+
return StateTParser.lift(Parser.hspace);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Throws away trailing whitespace characters before applying the given parser.
|
|
169
|
+
*/
|
|
170
|
+
token() {
|
|
171
|
+
return this.then(result => StateTParser.space().then(() => StateTParser.of(result)));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Throws away trailing horizontal whitespace characters before applying the given parser.
|
|
175
|
+
*
|
|
176
|
+
* This can be useful for parsing a language where newlines are significant.
|
|
177
|
+
*/
|
|
178
|
+
htoken() {
|
|
179
|
+
return this.then(result => StateTParser.hspace().then(() => StateTParser.of(result)));
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Returns a parser that parses a specific string, ignoring trailing whitespace characters.
|
|
183
|
+
*/
|
|
184
|
+
static symb(symbol) {
|
|
185
|
+
return StateTParser.string(symbol).token();
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Returns a parser that parses a specific string, ignoring trailing horizontal whitespace characters.
|
|
189
|
+
*
|
|
190
|
+
* This can be useful for parsing a language where newlines are significant.
|
|
191
|
+
*/
|
|
192
|
+
static hsymb(symbol) {
|
|
193
|
+
return StateTParser.string(symbol).htoken();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Creates a parser that parses a value using the given parser, surrounded by the given left and right parsers, and returns the parsed value.
|
|
197
|
+
*/
|
|
198
|
+
between(left, right) {
|
|
199
|
+
return left.then(() => this).then(result => right.then(() => StateTParser.of(result)));
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Returns a parser that applies the given parser to the input string without consuming any input, returning the result of the parser if it succeeds. If the parser fails, this lookahead parser also fails.
|
|
203
|
+
*/
|
|
204
|
+
static lookahead(parser) {
|
|
205
|
+
return new StateTParser(state => Parser.lookahead(parser.runT(state)));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Returns a parser that applies the given parser to the input string without consuming any input, returning `undefined` if it fails. If the parser succeeds, this fails instead.
|
|
209
|
+
*/
|
|
210
|
+
static negativeLookahead(parser) {
|
|
211
|
+
return new StateTParser(state => Parser.negativeLookahead(parser.runT(state)).map(() => [undefined, state]));
|
|
212
|
+
}
|
|
213
|
+
static debugLog(stateParser) {
|
|
214
|
+
return new StateTParser(state => {
|
|
215
|
+
console.log("Parsing with state:", state);
|
|
216
|
+
return Parser.debugLog(stateParser.runT(state));
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -14,24 +14,16 @@ export const plusMinus = "\xB1";
|
|
|
14
14
|
/**
|
|
15
15
|
* Forces signing on the given number, returning `undefined` on zero.
|
|
16
16
|
*/
|
|
17
|
-
export const signIgnoreZero = (x) => x > 0
|
|
18
|
-
? `+${x.toString()}`
|
|
19
|
-
: x < 0
|
|
20
|
-
? `${minus}\u2060${Math.abs(x).toString()}`
|
|
21
|
-
: undefined;
|
|
17
|
+
export const signIgnoreZero = (x) => x > 0 ? `+${x.toString()}` : x < 0 ? `${minus}\u2060${Math.abs(x).toString()}` : undefined;
|
|
22
18
|
/**
|
|
23
19
|
* Forces signing on the given number.
|
|
24
20
|
*/
|
|
25
|
-
export const sign = (x) => x > 0
|
|
26
|
-
? `+${x.toString()}`
|
|
27
|
-
: x < 0
|
|
28
|
-
? `${minus}\u2060${Math.abs(x).toString()}`
|
|
29
|
-
: "0";
|
|
21
|
+
export const sign = (x) => x > 0 ? `+${x.toString()}` : x < 0 ? `${minus}\u2060${Math.abs(x).toString()}` : "0";
|
|
30
22
|
/**
|
|
31
23
|
* Returns the sign of the given number. Returns `undefined` if the number is
|
|
32
24
|
* zero.
|
|
33
25
|
*/
|
|
34
|
-
export const signStr = (x) => x > 0 ? "+" : x < 0 ? minus : undefined;
|
|
26
|
+
export const signStr = (x) => (x > 0 ? "+" : x < 0 ? minus : undefined);
|
|
35
27
|
/**
|
|
36
28
|
* Converts a string to an integer. If the string is not a valid integer, it
|
|
37
29
|
* returns `undefined`.
|
|
@@ -28,8 +28,7 @@ export const splitStringParts = (str) => [...new Intl.Segmenter().segment(str)].
|
|
|
28
28
|
const nextSegment = strArr[i + 1];
|
|
29
29
|
const nextChar = nextSegment?.segment;
|
|
30
30
|
if (lastCharOfLastPart === undefined ||
|
|
31
|
-
(uppercase.test(lastCharOfLastPart) &&
|
|
32
|
-
(nextChar === undefined || separator.test(nextChar)))) {
|
|
31
|
+
(uppercase.test(lastCharOfLastPart) && (nextChar === undefined || separator.test(nextChar)))) {
|
|
33
32
|
return [...acc.slice(0, -1), lastPart + char];
|
|
34
33
|
}
|
|
35
34
|
else {
|
|
@@ -50,9 +49,7 @@ export const splitStringParts = (str) => [...new Intl.Segmenter().segment(str)].
|
|
|
50
49
|
* Converts a string to PascalCase.
|
|
51
50
|
*/
|
|
52
51
|
export const toPascalCase = (str) => splitStringParts(isAllUppercase(str) ? str.toLowerCase() : str)
|
|
53
|
-
.map(
|
|
54
|
-
? part
|
|
55
|
-
: part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
52
|
+
.map(part => isAllUppercase(part) ? part : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
56
53
|
.join("");
|
|
57
54
|
/**
|
|
58
55
|
* Converts a string to camelCase.
|
|
@@ -68,21 +65,19 @@ export const toCamelCase = (str) => splitStringParts(isAllUppercase(str) ? str.t
|
|
|
68
65
|
* Converts a string to kebab-case.
|
|
69
66
|
*/
|
|
70
67
|
export const toKebabCase = (str) => splitStringParts(str)
|
|
71
|
-
.map(
|
|
68
|
+
.map(part => part.toLowerCase())
|
|
72
69
|
.join("-");
|
|
73
70
|
/**
|
|
74
71
|
* Converts a string to snake_case.
|
|
75
72
|
*/
|
|
76
73
|
export const toSnakeCase = (str) => splitStringParts(str)
|
|
77
|
-
.map(
|
|
74
|
+
.map(part => part.toLowerCase())
|
|
78
75
|
.join("_");
|
|
79
76
|
/**
|
|
80
77
|
* Converts a string to Title Case.
|
|
81
78
|
*/
|
|
82
79
|
export const toTitleCase = (str) => splitStringParts(isAllUppercase(str) ? str.toLowerCase() : str)
|
|
83
|
-
.map(
|
|
84
|
-
? part
|
|
85
|
-
: part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
80
|
+
.map(part => isAllUppercase(part) ? part : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
86
81
|
.join(" ");
|
|
87
82
|
/**
|
|
88
83
|
* Finds the longest common prefix among the provided strings.
|
|
@@ -93,8 +88,6 @@ export const commonPrefix = (...strs) => {
|
|
|
93
88
|
}
|
|
94
89
|
return strs.reduce((accPrefix, str) => {
|
|
95
90
|
const indexOfDifference = Array.from(accPrefix).findIndex((char, i) => str[i] !== char);
|
|
96
|
-
return indexOfDifference === -1
|
|
97
|
-
? accPrefix
|
|
98
|
-
: accPrefix.slice(0, indexOfDifference);
|
|
91
|
+
return indexOfDifference === -1 ? accPrefix : accPrefix.slice(0, indexOfDifference);
|
|
99
92
|
});
|
|
100
93
|
};
|