securemark 0.291.0 → 0.292.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 +8 -0
- package/design.md +13 -2
- package/dist/index.js +1101 -760
- package/markdown.d.ts +17 -17
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.test.ts +8 -5
- package/src/combinator/control/constraint/block.ts +9 -9
- package/src/combinator/control/constraint/contract.ts +20 -28
- package/src/combinator/control/constraint/line.test.ts +9 -6
- package/src/combinator/control/constraint/line.ts +21 -22
- package/src/combinator/control/manipulation/convert.ts +29 -13
- package/src/combinator/control/manipulation/fence.ts +18 -15
- package/src/combinator/control/manipulation/indent.test.ts +17 -14
- package/src/combinator/control/manipulation/indent.ts +21 -10
- package/src/combinator/control/manipulation/match.ts +11 -10
- package/src/combinator/control/manipulation/recovery.ts +6 -2
- package/src/combinator/control/manipulation/scope.ts +37 -38
- package/src/combinator/control/manipulation/surround.ts +78 -60
- package/src/combinator/control/manipulation/trim.test.ts +12 -9
- package/src/combinator/control/monad/bind.ts +16 -16
- package/src/combinator/control/monad/fmap.ts +6 -6
- package/src/combinator/data/parser/context/delimiter.ts +8 -7
- package/src/combinator/data/parser/context.test.ts +19 -14
- package/src/combinator/data/parser/context.ts +20 -16
- package/src/combinator/data/parser/inits.ts +13 -14
- package/src/combinator/data/parser/sequence.test.ts +16 -15
- package/src/combinator/data/parser/sequence.ts +13 -14
- package/src/combinator/data/parser/some.test.ts +19 -18
- package/src/combinator/data/parser/some.ts +13 -15
- package/src/combinator/data/parser/subsequence.test.ts +22 -21
- package/src/combinator/data/parser/subsequence.ts +3 -3
- package/src/combinator/data/parser/tails.ts +3 -3
- package/src/combinator/data/parser/union.test.ts +16 -15
- package/src/combinator/data/parser/union.ts +2 -2
- package/src/combinator/data/parser.ts +66 -28
- package/src/debug.test.ts +3 -3
- package/src/parser/api/bind.ts +3 -3
- package/src/parser/api/header.ts +7 -6
- package/src/parser/api/normalize.ts +2 -2
- package/src/parser/api/parse.test.ts +14 -15
- package/src/parser/api/parse.ts +3 -3
- package/src/parser/autolink.test.ts +19 -17
- package/src/parser/block/blockquote.test.ts +86 -84
- package/src/parser/block/blockquote.ts +4 -2
- package/src/parser/block/codeblock.test.ts +58 -56
- package/src/parser/block/codeblock.ts +3 -3
- package/src/parser/block/dlist.test.ts +58 -56
- package/src/parser/block/extension/aside.test.ts +10 -8
- package/src/parser/block/extension/aside.ts +1 -1
- package/src/parser/block/extension/example.test.ts +20 -18
- package/src/parser/block/extension/example.ts +3 -3
- package/src/parser/block/extension/fig.test.ts +38 -36
- package/src/parser/block/extension/fig.ts +1 -1
- package/src/parser/block/extension/figbase.test.ts +17 -15
- package/src/parser/block/extension/figure.test.ts +64 -62
- package/src/parser/block/extension/figure.ts +3 -2
- package/src/parser/block/extension/message.test.ts +15 -13
- package/src/parser/block/extension/message.ts +3 -3
- package/src/parser/block/extension/placeholder.test.ts +3 -1
- package/src/parser/block/extension/table.test.ts +73 -71
- package/src/parser/block/extension/table.ts +5 -5
- package/src/parser/block/extension.test.ts +3 -1
- package/src/parser/block/heading.test.ts +65 -64
- package/src/parser/block/heading.ts +3 -3
- package/src/parser/block/ilist.test.ts +3 -1
- package/src/parser/block/ilist.ts +3 -3
- package/src/parser/block/mathblock.test.ts +33 -31
- package/src/parser/block/mathblock.ts +1 -1
- package/src/parser/block/mediablock.ts +2 -2
- package/src/parser/block/olist.test.ts +99 -97
- package/src/parser/block/olist.ts +2 -2
- package/src/parser/block/pagebreak.test.ts +17 -15
- package/src/parser/block/pagebreak.ts +1 -1
- package/src/parser/block/paragraph.test.ts +60 -57
- package/src/parser/block/reply/cite.test.ts +41 -39
- package/src/parser/block/reply/cite.ts +3 -3
- package/src/parser/block/reply/quote.test.ts +52 -50
- package/src/parser/block/reply.test.ts +21 -19
- package/src/parser/block/sidefence.test.ts +51 -49
- package/src/parser/block/table.test.ts +51 -50
- package/src/parser/block/table.ts +6 -6
- package/src/parser/block/ulist.test.ts +52 -50
- package/src/parser/block/ulist.ts +2 -2
- package/src/parser/block.ts +6 -5
- package/src/parser/context.ts +1 -0
- package/src/parser/header.test.ts +22 -21
- package/src/parser/header.ts +25 -13
- package/src/parser/inline/annotation.test.ts +44 -42
- package/src/parser/inline/annotation.ts +2 -2
- package/src/parser/inline/autolink/account.test.ts +32 -30
- package/src/parser/inline/autolink/account.ts +1 -1
- package/src/parser/inline/autolink/anchor.test.ts +23 -21
- package/src/parser/inline/autolink/anchor.ts +1 -1
- package/src/parser/inline/autolink/channel.test.ts +16 -14
- package/src/parser/inline/autolink/channel.ts +2 -2
- package/src/parser/inline/autolink/email.test.ts +38 -36
- package/src/parser/inline/autolink/email.ts +2 -2
- package/src/parser/inline/autolink/hashnum.test.ts +39 -37
- package/src/parser/inline/autolink/hashnum.ts +1 -1
- package/src/parser/inline/autolink/hashtag.test.ts +58 -56
- package/src/parser/inline/autolink/hashtag.ts +1 -1
- package/src/parser/inline/autolink/url.test.ts +76 -74
- package/src/parser/inline/autolink/url.ts +6 -6
- package/src/parser/inline/bracket.test.ts +69 -67
- package/src/parser/inline/bracket.ts +32 -32
- package/src/parser/inline/code.test.ts +32 -29
- package/src/parser/inline/code.ts +20 -13
- package/src/parser/inline/deletion.test.ts +29 -27
- package/src/parser/inline/deletion.ts +2 -2
- package/src/parser/inline/emphasis.test.ts +40 -36
- package/src/parser/inline/emphasis.ts +2 -2
- package/src/parser/inline/emstrong.test.ts +102 -96
- package/src/parser/inline/emstrong.ts +96 -36
- package/src/parser/inline/extension/index.test.ts +91 -89
- package/src/parser/inline/extension/index.ts +18 -30
- package/src/parser/inline/extension/indexee.ts +1 -1
- package/src/parser/inline/extension/indexer.test.ts +26 -24
- package/src/parser/inline/extension/indexer.ts +1 -1
- package/src/parser/inline/extension/label.test.ts +34 -32
- package/src/parser/inline/extension/placeholder.test.ts +44 -42
- package/src/parser/inline/extension/placeholder.ts +11 -8
- package/src/parser/inline/html.test.ts +108 -106
- package/src/parser/inline/html.ts +24 -23
- package/src/parser/inline/htmlentity.test.ts +39 -37
- package/src/parser/inline/htmlentity.ts +9 -3
- package/src/parser/inline/insertion.test.ts +29 -27
- package/src/parser/inline/insertion.ts +2 -2
- package/src/parser/inline/italic.test.ts +55 -53
- package/src/parser/inline/italic.ts +2 -2
- package/src/parser/inline/link.test.ts +187 -185
- package/src/parser/inline/link.ts +30 -12
- package/src/parser/inline/mark.test.ts +31 -29
- package/src/parser/inline/mark.ts +3 -3
- package/src/parser/inline/math.test.ts +133 -131
- package/src/parser/inline/math.ts +2 -2
- package/src/parser/inline/media.test.ts +93 -91
- package/src/parser/inline/media.ts +41 -17
- package/src/parser/inline/reference.test.ts +110 -108
- package/src/parser/inline/reference.ts +49 -40
- package/src/parser/inline/remark.test.ts +53 -51
- package/src/parser/inline/remark.ts +3 -3
- package/src/parser/inline/ruby.test.ts +46 -44
- package/src/parser/inline/ruby.ts +29 -32
- package/src/parser/inline/shortmedia.test.ts +11 -9
- package/src/parser/inline/strong.test.ts +37 -33
- package/src/parser/inline/strong.ts +6 -3
- package/src/parser/inline/template.test.ts +24 -22
- package/src/parser/inline/template.ts +20 -11
- package/src/parser/inline.test.ts +221 -220
- package/src/parser/inline.ts +13 -8
- package/src/parser/segment.ts +13 -8
- package/src/parser/source/escapable.test.ts +24 -22
- package/src/parser/source/escapable.ts +26 -41
- package/src/parser/source/line.test.ts +19 -17
- package/src/parser/source/line.ts +3 -3
- package/src/parser/source/str.ts +28 -11
- package/src/parser/source/text.test.ts +85 -83
- package/src/parser/source/text.ts +26 -54
- package/src/parser/source/unescapable.test.ts +24 -22
- package/src/parser/source/unescapable.ts +18 -33
- package/src/parser/source.ts +1 -1
- package/src/parser/util.ts +36 -33
- package/src/parser/visibility.ts +19 -15
- package/src/util/quote.ts +4 -2
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { Parser, Context,
|
|
1
|
+
import { Parser, Context, input, eval, failsafe } from '../../data/parser';
|
|
2
2
|
import { consume } from '../../../combinator';
|
|
3
3
|
|
|
4
4
|
export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P, cost?: boolean): P;
|
|
5
5
|
export function focus<N>(scope: string | RegExp, parser: Parser<N>, cost = true): Parser<N> {
|
|
6
6
|
assert(scope instanceof RegExp ? !scope.flags.match(/[gmy]/) && scope.source.startsWith('^') : scope);
|
|
7
7
|
assert(parser);
|
|
8
|
-
const match: (source: string) => string = typeof scope === 'string'
|
|
9
|
-
? source => source.
|
|
10
|
-
: source => source.match(scope)?.[0] ?? '';
|
|
11
|
-
return ({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
const match: (source: string, position: number) => string = typeof scope === 'string'
|
|
9
|
+
? (source, position) => source.startsWith(scope, position) ? scope : ''
|
|
10
|
+
: (source, position) => source.slice(position).match(scope)?.[0] ?? '';
|
|
11
|
+
return failsafe(({ context }) => {
|
|
12
|
+
const { source, position } = context;
|
|
13
|
+
if (position === source.length) return;
|
|
14
|
+
const src = match(source, position);
|
|
15
|
+
assert(source.startsWith(src, position));
|
|
15
16
|
if (src === '') return;
|
|
16
17
|
cost && consume(src.length, context);
|
|
17
|
-
|
|
18
|
-
assert(offset >= 0);
|
|
18
|
+
context.range = src.length;
|
|
19
19
|
context.offset ??= 0;
|
|
20
|
-
context.offset +=
|
|
21
|
-
const result = parser(
|
|
22
|
-
|
|
23
|
-
context.
|
|
20
|
+
context.offset += position;
|
|
21
|
+
const result = parser(input(src, context));
|
|
22
|
+
context.position += position;
|
|
23
|
+
context.position += result && context.position === position ? src.length : 0;
|
|
24
|
+
assert(context.position > position || !result);
|
|
25
|
+
context.source = source;
|
|
26
|
+
context.offset -= position;
|
|
24
27
|
if (result === undefined) return;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
? [eval(result), exec(result) + source.slice(src.length)]
|
|
28
|
-
: undefined;
|
|
29
|
-
};
|
|
28
|
+
return [eval(result)];
|
|
29
|
+
});
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
//export function rewrite<N, C extends Ctx, D extends Parser<unknown, C>[]>(scope: Parser<unknown, C, D>, parser: Parser<N, C, never>): Parser<N, C, D>;
|
|
@@ -34,30 +34,29 @@ export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Contex
|
|
|
34
34
|
export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>): Parser<N> {
|
|
35
35
|
assert(scope);
|
|
36
36
|
assert(parser);
|
|
37
|
-
return
|
|
38
|
-
const { source,
|
|
39
|
-
if (
|
|
37
|
+
return failsafe(({ context }) => {
|
|
38
|
+
const { source, position } = context;
|
|
39
|
+
if (position === source.length) return;
|
|
40
40
|
// 影響する使用はないはず
|
|
41
41
|
//const { backtracks } = context;
|
|
42
42
|
//context.backtracks = {};
|
|
43
|
-
const res1 = scope(
|
|
44
|
-
assert(
|
|
43
|
+
const res1 = scope({ context });
|
|
44
|
+
assert(context.position > position || !res1);
|
|
45
45
|
//context.backtracks = backtracks;
|
|
46
|
-
if (res1 === undefined ||
|
|
47
|
-
const src = source.slice(
|
|
46
|
+
if (res1 === undefined || context.position < position) return;
|
|
47
|
+
const src = source.slice(position, context.position);
|
|
48
48
|
assert(src !== '');
|
|
49
|
-
assert(source.startsWith(src));
|
|
50
|
-
const offset = source.length - src.length;
|
|
51
|
-
assert(offset >= 0);
|
|
49
|
+
assert(source.startsWith(src, position));
|
|
52
50
|
context.offset ??= 0;
|
|
53
|
-
context.offset +=
|
|
54
|
-
const res2 = parser(
|
|
55
|
-
|
|
56
|
-
context.
|
|
51
|
+
context.offset += position;
|
|
52
|
+
const res2 = parser(input(src, context));
|
|
53
|
+
context.position += position;
|
|
54
|
+
context.position += res2 && context.position === position ? src.length : 0;
|
|
55
|
+
assert(context.position > position || !res2);
|
|
56
|
+
context.source = source;
|
|
57
|
+
context.offset -= position;
|
|
57
58
|
if (res2 === undefined) return;
|
|
58
|
-
assert(
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
: undefined;
|
|
62
|
-
};
|
|
59
|
+
assert(context.position === position + src.length);
|
|
60
|
+
return [eval(res2)];
|
|
61
|
+
});
|
|
63
62
|
}
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { Parser, Input, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval,
|
|
1
|
+
import { Parser, Input, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
|
|
2
2
|
import { consume } from '../../../combinator';
|
|
3
3
|
import { unshift, push } from 'spica/array';
|
|
4
4
|
|
|
5
5
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
6
6
|
opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
|
|
7
7
|
optional?: false,
|
|
8
|
-
f?: (rss: [S[], SubNode<P>[], S[]],
|
|
9
|
-
g?: (rss: [S[], SubNode<P>[]
|
|
8
|
+
f?: (rss: [S[], SubNode<P>[], S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
9
|
+
g?: (rss: [S[], SubNode<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
10
10
|
backtracks?: readonly number[],
|
|
11
11
|
): P;
|
|
12
12
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
13
13
|
opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
|
|
14
14
|
optional?: boolean,
|
|
15
|
-
f?: (rss: [S[], SubNode<P>[] | undefined, S[]],
|
|
16
|
-
g?: (rss: [S[], SubNode<P>[] | undefined
|
|
15
|
+
f?: (rss: [S[], SubNode<P>[] | undefined, S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
16
|
+
g?: (rss: [S[], SubNode<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
17
17
|
backtracks?: readonly number[],
|
|
18
18
|
): P;
|
|
19
19
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
20
20
|
opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
|
|
21
21
|
optional?: false,
|
|
22
|
-
f?: (rss: [S[], Node<P>[], S[]],
|
|
23
|
-
g?: (rss: [S[], Node<P>[]
|
|
22
|
+
f?: (rss: [S[], Node<P>[], S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
23
|
+
g?: (rss: [S[], Node<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
24
24
|
backtracks?: readonly number[],
|
|
25
25
|
): P;
|
|
26
26
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
27
27
|
opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
|
|
28
28
|
optional?: boolean,
|
|
29
|
-
f?: (rss: [S[], Node<P>[] | undefined, S[]],
|
|
30
|
-
g?: (rss: [S[], Node<P>[] | undefined
|
|
29
|
+
f?: (rss: [S[], Node<P>[] | undefined, S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
30
|
+
g?: (rss: [S[], Node<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
31
31
|
backtracks?: readonly number[],
|
|
32
32
|
): P;
|
|
33
33
|
export function surround<N>(
|
|
34
34
|
opener: string | RegExp | Parser<N>, parser: Parser<N>, closer: string | RegExp | Parser<N>,
|
|
35
35
|
optional: boolean = false,
|
|
36
|
-
f?: (rss: [N[], N[], N[]],
|
|
37
|
-
g?: (rss: [N[], N[]
|
|
36
|
+
f?: (rss: [N[], N[], N[]], context: Ctx) => Result<N>,
|
|
37
|
+
g?: (rss: [N[], N[] | undefined], context: Ctx) => Result<N>,
|
|
38
38
|
backtracks: readonly number[] = [],
|
|
39
39
|
): Parser<N> {
|
|
40
40
|
switch (typeof opener) {
|
|
@@ -47,40 +47,48 @@ export function surround<N>(
|
|
|
47
47
|
case 'object':
|
|
48
48
|
closer = match(closer);
|
|
49
49
|
}
|
|
50
|
-
return (
|
|
51
|
-
const
|
|
52
|
-
|
|
50
|
+
return failsafe(input => {
|
|
51
|
+
const { context } = input;
|
|
52
|
+
const { source, position } = context;
|
|
53
|
+
if (position === source.length) return;
|
|
53
54
|
const { linebreak } = context;
|
|
54
55
|
context.linebreak = 0;
|
|
55
|
-
const
|
|
56
|
-
assert(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
const resultO = opener(input);
|
|
57
|
+
assert(context.position >= position);
|
|
58
|
+
const nodesO = eval(resultO);
|
|
59
|
+
if (!nodesO) {
|
|
60
|
+
return void revert(context, linebreak);
|
|
61
|
+
}
|
|
62
|
+
if (isBacktrack(context, backtracks, position, context.position - position || 1)) {
|
|
63
|
+
return void revert(context, linebreak);
|
|
64
|
+
}
|
|
65
|
+
const resultM = context.position < source.length ? parser(input) : undefined;
|
|
66
|
+
assert(context.position >= position);
|
|
67
|
+
context.range = context.position - position;
|
|
63
68
|
const nodesM = eval(resultM);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
context.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
if (!resultM && !optional) {
|
|
70
|
+
setBacktrack(context, backtracks, position);
|
|
71
|
+
const result = g?.([nodesO, nodesM], context);
|
|
72
|
+
revert(context, linebreak);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
const resultC = resultM || optional ? closer(input) : undefined;
|
|
76
|
+
assert(context.position >= position);
|
|
77
|
+
context.range = context.position - position;
|
|
78
|
+
const nodesC = eval(resultC);
|
|
79
|
+
if (!nodesC) {
|
|
80
|
+
setBacktrack(context, backtracks, position);
|
|
81
|
+
const result = g?.([nodesO, nodesM], context);
|
|
82
|
+
revert(context, linebreak);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
if (context.position === position) {
|
|
86
|
+
return void revert(context, linebreak);
|
|
87
|
+
}
|
|
88
|
+
context.range = context.position - position;
|
|
89
|
+
const result = f
|
|
90
|
+
? f([nodesO, nodesM!, nodesC], context)
|
|
91
|
+
: [push(nodesM ? unshift(nodesO, nodesM) : nodesO, nodesC)] satisfies [N[]];
|
|
84
92
|
if (result) {
|
|
85
93
|
context.linebreak ||= linebreak;
|
|
86
94
|
}
|
|
@@ -88,7 +96,7 @@ export function surround<N>(
|
|
|
88
96
|
revert(context, linebreak);
|
|
89
97
|
}
|
|
90
98
|
return result;
|
|
91
|
-
};
|
|
99
|
+
});
|
|
92
100
|
}
|
|
93
101
|
export function open<P extends Parser<unknown>>(
|
|
94
102
|
opener: string | RegExp | Parser<Node<P>, Context<P>>,
|
|
@@ -97,7 +105,7 @@ export function open<P extends Parser<unknown>>(
|
|
|
97
105
|
backtracks?: readonly number[],
|
|
98
106
|
): P;
|
|
99
107
|
export function open<N>(
|
|
100
|
-
opener: string | RegExp | Parser<N>,
|
|
108
|
+
opener: string | RegExp | Parser<N, Ctx>,
|
|
101
109
|
parser: Parser<N>,
|
|
102
110
|
optional?: boolean,
|
|
103
111
|
backtracks?: readonly number[],
|
|
@@ -112,7 +120,7 @@ export function close<P extends Parser<unknown>>(
|
|
|
112
120
|
): P;
|
|
113
121
|
export function close<N>(
|
|
114
122
|
parser: Parser<N>,
|
|
115
|
-
closer: string | RegExp | Parser<N>,
|
|
123
|
+
closer: string | RegExp | Parser<N, Ctx>,
|
|
116
124
|
optional?: boolean,
|
|
117
125
|
backtracks?: readonly number[],
|
|
118
126
|
): Parser<N> {
|
|
@@ -123,18 +131,19 @@ const statesize = 2;
|
|
|
123
131
|
export function isBacktrack(
|
|
124
132
|
context: Ctx,
|
|
125
133
|
backtracks: readonly number[],
|
|
126
|
-
|
|
134
|
+
position: number = context.position,
|
|
127
135
|
length: number = 1,
|
|
128
136
|
): boolean {
|
|
129
|
-
|
|
137
|
+
const { source } = context;
|
|
138
|
+
if (position === source.length) return false;
|
|
139
|
+
if (length === 0) return false;
|
|
130
140
|
for (const backtrack of backtracks) {
|
|
131
141
|
if (backtrack & 1) {
|
|
132
142
|
const { backtracks = {}, offset = 0 } = context;
|
|
133
143
|
for (let i = 0; i < length; ++i) {
|
|
134
|
-
|
|
135
|
-
if (source[i] !== source[0]) break;
|
|
136
|
-
const pos =
|
|
137
|
-
assert(pos >= 0);
|
|
144
|
+
if (position + i === source.length) break;
|
|
145
|
+
if (source[position + i] !== source[position + 0]) break;
|
|
146
|
+
const pos = position + i + offset;
|
|
138
147
|
if (!(pos in backtracks)) continue;
|
|
139
148
|
if (backtracks[pos] & 1 << size(backtrack >>> statesize)) return true;
|
|
140
149
|
}
|
|
@@ -148,29 +157,38 @@ export function setBacktrack(
|
|
|
148
157
|
position: number,
|
|
149
158
|
length: number = 1,
|
|
150
159
|
): void {
|
|
151
|
-
|
|
160
|
+
const { source } = context;
|
|
161
|
+
if (position === source.length) return;
|
|
162
|
+
if (length === 0) return;
|
|
152
163
|
for (const backtrack of backtracks) {
|
|
153
|
-
if (backtrack & 2
|
|
164
|
+
if (backtrack & 2) {
|
|
154
165
|
const { backtracks = {}, offset = 0 } = context;
|
|
155
166
|
for (let i = 0; i < length; ++i) {
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
if (position + i === source.length) break;
|
|
168
|
+
const pos = position + i + offset;
|
|
158
169
|
backtracks[pos] |= 1 << size(backtrack >>> statesize);
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
}
|
|
162
173
|
}
|
|
163
174
|
|
|
164
|
-
function match(pattern: string | RegExp): (input: Input) =>
|
|
175
|
+
function match(pattern: string | RegExp): (input: Input) => Result<never> {
|
|
165
176
|
switch (typeof pattern) {
|
|
166
177
|
case 'string':
|
|
167
|
-
return ({
|
|
178
|
+
return ({ context }) => {
|
|
179
|
+
const { source, position } = context;
|
|
180
|
+
if (!source.startsWith(pattern, position)) return;
|
|
181
|
+
context.position += pattern.length;
|
|
182
|
+
return [[]];
|
|
183
|
+
};
|
|
168
184
|
case 'object':
|
|
169
|
-
return ({
|
|
170
|
-
const
|
|
185
|
+
return ({ context }) => {
|
|
186
|
+
const { source, position } = context;
|
|
187
|
+
const m = source.slice(position).match(pattern);
|
|
171
188
|
if (m === null) return;
|
|
172
189
|
consume(m[0].length, context);
|
|
173
|
-
|
|
190
|
+
context.position += m[0].length;
|
|
191
|
+
return [[]];
|
|
174
192
|
};
|
|
175
193
|
}
|
|
176
194
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { trim } from './trim';
|
|
2
|
+
import { input } from '../../data/parser';
|
|
2
3
|
import { inspect } from '../../../debug.test';
|
|
3
4
|
|
|
4
5
|
describe('Unit: combinator/trim', () => {
|
|
5
6
|
describe('trim', () => {
|
|
7
|
+
const { context: ctx } = input('', {});
|
|
8
|
+
|
|
6
9
|
it('', () => {
|
|
7
|
-
const parser = trim(({
|
|
8
|
-
assert.deepStrictEqual(inspect(parser(
|
|
9
|
-
assert.deepStrictEqual(inspect(parser(
|
|
10
|
-
assert.deepStrictEqual(inspect(parser(
|
|
11
|
-
assert.deepStrictEqual(inspect(parser(
|
|
12
|
-
assert.deepStrictEqual(inspect(parser(
|
|
13
|
-
assert.deepStrictEqual(inspect(parser(
|
|
14
|
-
assert.deepStrictEqual(inspect(parser(
|
|
15
|
-
assert.deepStrictEqual(inspect(parser(
|
|
10
|
+
const parser = trim(({ context }) => { context.position = context.source.length; return [[context.source]]; });
|
|
11
|
+
assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
|
|
12
|
+
assert.deepStrictEqual(inspect(parser(input('a', ctx)), ctx), [['a'], '']);
|
|
13
|
+
assert.deepStrictEqual(inspect(parser(input('a\n', ctx)), ctx), [['a'], '']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser(input('a ', ctx)), ctx), [['a'], '']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser(input('a \n', ctx)), ctx), [['a'], '']);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
|
|
17
|
+
assert.deepStrictEqual(inspect(parser(input(' a ', ctx)), ctx), [['a'], '']);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser(input(' a \n b \n', ctx)), ctx), [['a \n b'], '']);
|
|
16
19
|
});
|
|
17
20
|
|
|
18
21
|
});
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { Parser, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval,
|
|
1
|
+
import { Parser, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
|
|
2
2
|
|
|
3
|
-
export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[],
|
|
4
|
-
export function bind<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[],
|
|
5
|
-
export function bind<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[],
|
|
6
|
-
export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[],
|
|
7
|
-
export function bind<N, U>(parser: Parser<N>, f: (nodes: N[],
|
|
3
|
+
export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
|
|
4
|
+
export function bind<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
|
|
5
|
+
export function bind<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
|
|
6
|
+
export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
|
|
7
|
+
export function bind<N, U>(parser: Parser<N>, f: (nodes: N[], context: Ctx) => Result<U>): Parser<U> {
|
|
8
8
|
assert(parser);
|
|
9
|
-
return input => {
|
|
10
|
-
const {
|
|
11
|
-
|
|
9
|
+
return failsafe(input => {
|
|
10
|
+
const { context } = input;
|
|
11
|
+
const { source, position } = context;
|
|
12
|
+
if (position === source.length) return;
|
|
12
13
|
const res1 = parser(input);
|
|
13
|
-
assert(
|
|
14
|
+
assert(context.position > position || !res1);
|
|
14
15
|
if (res1 === undefined) return;
|
|
15
|
-
context.
|
|
16
|
-
const res2 = f(eval(res1),
|
|
17
|
-
assert(
|
|
18
|
-
assert(check(exec(res1), res2, false));
|
|
16
|
+
context.range = context.position - position;
|
|
17
|
+
const res2 = f(eval(res1), context);
|
|
18
|
+
assert(context.position > position || !res2);
|
|
19
19
|
if (res2 === undefined) return;
|
|
20
|
-
return
|
|
20
|
+
return context.position > position
|
|
21
21
|
? res2
|
|
22
22
|
: undefined;
|
|
23
|
-
};
|
|
23
|
+
});
|
|
24
24
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Parser, Ctx, SubParsers, Node, Context, IntermediateParser, SubNode } from '../../data/parser';
|
|
2
2
|
import { bind } from './bind';
|
|
3
3
|
|
|
4
|
-
export function fmap<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[],
|
|
5
|
-
export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[],
|
|
6
|
-
export function fmap<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[],
|
|
7
|
-
export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[],
|
|
8
|
-
export function fmap<N, U>(parser: Parser<N>, f: (nodes: N[],
|
|
9
|
-
return bind(parser, (nodes,
|
|
4
|
+
export function fmap<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], context: Context<P>) => Node<P>[]): P;
|
|
5
|
+
export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Node<P>[]): P;
|
|
6
|
+
export function fmap<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], context: Context<P>) => Node<P>[]): P;
|
|
7
|
+
export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => U[]): Parser<U, Context<P>, SubParsers<P>>;
|
|
8
|
+
export function fmap<N, U>(parser: Parser<N>, f: (nodes: N[], context: Ctx) => U[]): Parser<U> {
|
|
9
|
+
return bind(parser, (nodes, context) => [f(nodes, context)]);
|
|
10
10
|
}
|
|
@@ -4,7 +4,7 @@ interface Delimiter {
|
|
|
4
4
|
readonly memory: Delimiter[];
|
|
5
5
|
readonly index: number;
|
|
6
6
|
readonly signature: number | string;
|
|
7
|
-
readonly matcher: (
|
|
7
|
+
readonly matcher: (context: Ctx) => boolean | undefined;
|
|
8
8
|
readonly precedence: number;
|
|
9
9
|
readonly linebreakable: boolean;
|
|
10
10
|
state: boolean;
|
|
@@ -28,14 +28,14 @@ export class Delimiters {
|
|
|
28
28
|
return `r/${pattern.source}/${+linebreakable}`;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
public static matcher(pattern: string | RegExp | undefined): (
|
|
31
|
+
public static matcher(pattern: string | RegExp | undefined): (context: Ctx) => true | undefined {
|
|
32
32
|
switch (typeof pattern) {
|
|
33
33
|
case 'undefined':
|
|
34
34
|
return () => undefined;
|
|
35
35
|
case 'string':
|
|
36
|
-
return source => source.
|
|
36
|
+
return ({ source, position }) => source.startsWith(pattern, position) || undefined;
|
|
37
37
|
case 'object':
|
|
38
|
-
return source => pattern.test(source) || undefined;
|
|
38
|
+
return ({ source, position }) => pattern.test(source.slice(position)) || undefined;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
private readonly tree: Record<number, Delimiter[]> = {};
|
|
@@ -58,7 +58,7 @@ export class Delimiters {
|
|
|
58
58
|
public push(
|
|
59
59
|
delims: readonly {
|
|
60
60
|
readonly signature: number | string;
|
|
61
|
-
readonly matcher: (
|
|
61
|
+
readonly matcher: (context: Ctx) => boolean | undefined;
|
|
62
62
|
readonly precedence: number;
|
|
63
63
|
readonly linebreakable: boolean;
|
|
64
64
|
}[]
|
|
@@ -132,12 +132,13 @@ export class Delimiters {
|
|
|
132
132
|
delimiters[indexes[i]].state = true;
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
public match(
|
|
135
|
+
public match(context: Ctx): boolean {
|
|
136
|
+
const { precedence = 0, linebreak = 0 } = context;
|
|
136
137
|
const { delimiters } = this;
|
|
137
138
|
for (let i = delimiters.length; i--;) {
|
|
138
139
|
const delimiter = delimiters[i];
|
|
139
140
|
if (delimiter.precedence <= precedence || !delimiter.state) continue;
|
|
140
|
-
switch (delimiter.matcher(
|
|
141
|
+
switch (delimiter.matcher(context)) {
|
|
141
142
|
case true:
|
|
142
143
|
if (!delimiter.linebreakable && linebreak > 0) return false;
|
|
143
144
|
return true;
|
|
@@ -1,53 +1,58 @@
|
|
|
1
|
-
import { Parser, Ctx } from '../parser';
|
|
1
|
+
import { Parser, Ctx, CtxOptions, input } from '../parser';
|
|
2
2
|
import { some } from './some';
|
|
3
3
|
import { reset, context } from './context';
|
|
4
4
|
import { creation } from './context';
|
|
5
5
|
|
|
6
6
|
describe('Unit: combinator/data/parser/context', () => {
|
|
7
|
-
interface Context extends
|
|
7
|
+
interface Context extends CtxOptions {
|
|
8
8
|
status?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
describe('reset', () => {
|
|
12
12
|
const parser: Parser<number> = some(creation(1,
|
|
13
|
-
({
|
|
13
|
+
({ context }) => {
|
|
14
|
+
context.position += 1;
|
|
15
|
+
return [[context.resources?.clock ?? NaN]];
|
|
16
|
+
}));
|
|
14
17
|
|
|
15
18
|
it('root', () => {
|
|
16
19
|
const base: Context = { resources: { clock: 3, recursions: [1] } };
|
|
17
20
|
const ctx: Context = {};
|
|
18
|
-
assert.deepStrictEqual(reset(base, parser)(
|
|
21
|
+
assert.deepStrictEqual(reset(base, parser)(input('123', ctx)), [[3, 2, 1]]);
|
|
19
22
|
assert(base.resources?.clock === 3);
|
|
20
23
|
assert(ctx.resources?.clock === undefined);
|
|
21
|
-
assert.throws(() => reset(base, parser)(
|
|
22
|
-
assert(ctx.resources?.clock ===
|
|
23
|
-
assert.deepStrictEqual(reset(base, parser)({ source: '123', context: ctx }), [[3, 2, 1], '']);
|
|
24
|
+
assert.throws(() => reset(base, parser)(input('1234', ctx)));
|
|
25
|
+
assert(ctx.resources?.clock === 0);
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
it('node', () => {
|
|
27
29
|
const base: Context = { resources: { clock: 3, recursions: [1] } };
|
|
28
30
|
const ctx: Context = { resources: { clock: 1, recursions: [1] } };
|
|
29
|
-
assert.deepStrictEqual(reset(base, parser)(
|
|
31
|
+
assert.deepStrictEqual(reset(base, parser)(input('1', ctx)), [[1]]);
|
|
30
32
|
assert(base.resources?.clock === 3);
|
|
31
33
|
assert(ctx.resources?.clock === 0);
|
|
32
|
-
assert.throws(() => reset(base, parser)(
|
|
34
|
+
assert.throws(() => reset(base, parser)(input('1', ctx)));
|
|
33
35
|
assert(ctx.resources?.clock === 0);
|
|
34
36
|
});
|
|
35
37
|
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
describe('context', () => {
|
|
39
|
-
const parser: Parser<boolean, Context> = some(creation(1,
|
|
40
|
-
({
|
|
41
|
+
const parser: Parser<boolean, Context & Ctx> = some(creation(1,
|
|
42
|
+
({ context }) => {
|
|
43
|
+
context.position += 1;
|
|
44
|
+
return [[context.status!]];
|
|
45
|
+
}));
|
|
41
46
|
|
|
42
47
|
it('', () => {
|
|
43
48
|
const base: Context = { status: true };
|
|
44
49
|
const ctx: Context = { resources: { clock: 3, recursions: [1] } };
|
|
45
|
-
assert.deepStrictEqual(context(base, parser)(
|
|
50
|
+
assert.deepStrictEqual(context(base, parser)(input('123', ctx)), [[true, true, true]]);
|
|
46
51
|
assert(ctx.resources?.clock === 0);
|
|
47
52
|
assert(ctx.status === undefined);
|
|
48
|
-
assert.throws(() => reset(base, parser)(
|
|
53
|
+
assert.throws(() => reset(base, parser)(input('1', ctx)));
|
|
49
54
|
assert(ctx.resources?.clock === 0);
|
|
50
|
-
assert(ctx.status ===
|
|
55
|
+
assert(ctx.status === true);
|
|
51
56
|
});
|
|
52
57
|
|
|
53
58
|
});
|
|
@@ -1,57 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Parser, Result, Ctx, Node, Context } from '../../data/parser';
|
|
1
|
+
import { min } from 'spica/alias';
|
|
2
|
+
import { Parser, Result, Ctx, CtxOptions, Node, Context } from '../../data/parser';
|
|
3
3
|
import { clone } from 'spica/assign';
|
|
4
4
|
|
|
5
|
-
export function reset<P extends Parser<unknown>>(base:
|
|
5
|
+
export function reset<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
|
|
6
6
|
export function reset<N>(base: Ctx, parser: Parser<N>): Parser<N> {
|
|
7
7
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
8
8
|
assert(Object.freeze(base));
|
|
9
9
|
const changes = Object.entries(base);
|
|
10
10
|
const values = Array(changes.length);
|
|
11
|
-
return ({
|
|
12
|
-
apply(parser,
|
|
11
|
+
return ({ context }) =>
|
|
12
|
+
apply(parser, context, changes, values, true);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function context<P extends Parser<unknown>>(base:
|
|
15
|
+
export function context<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
|
|
16
16
|
export function context<N>(base: Ctx, parser: Parser<N>): Parser<N> {
|
|
17
17
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
18
18
|
assert(Object.freeze(base));
|
|
19
19
|
const changes = Object.entries(base);
|
|
20
20
|
const values = Array(changes.length);
|
|
21
|
-
return ({
|
|
22
|
-
apply(parser,
|
|
21
|
+
return ({ context }) =>
|
|
22
|
+
apply(parser, context, changes, values);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function apply<P extends Parser<unknown>>(parser: P,
|
|
26
|
-
function apply<N>(parser: Parser<N>,
|
|
27
|
-
if (reset) {
|
|
28
|
-
context.backtracks = {};
|
|
29
|
-
}
|
|
25
|
+
function apply<P extends Parser<unknown>>(parser: P, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Node<P>>;
|
|
26
|
+
function apply<N>(parser: Parser<N>, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<N> {
|
|
30
27
|
for (let i = 0; i < changes.length; ++i) {
|
|
31
28
|
const change = changes[i];
|
|
32
29
|
const prop = change[0];
|
|
33
30
|
switch (prop) {
|
|
31
|
+
case 'source':
|
|
32
|
+
case 'position':
|
|
33
|
+
continue;
|
|
34
34
|
case 'resources':
|
|
35
35
|
assert(reset);
|
|
36
36
|
assert(!context.offset);
|
|
37
37
|
assert(!context.precedence);
|
|
38
38
|
assert(!context.delimiters);
|
|
39
39
|
assert(!context.state);
|
|
40
|
+
values[i] = context[prop];
|
|
40
41
|
context[prop as string] ??= clone({}, change[1] as object);
|
|
41
42
|
continue;
|
|
43
|
+
case 'backtracks':
|
|
44
|
+
change[1] = {};
|
|
42
45
|
}
|
|
43
46
|
values[i] = context[prop];
|
|
44
47
|
context[prop] = change[1];
|
|
45
48
|
}
|
|
46
|
-
const result = parser({
|
|
49
|
+
const result = parser({ context });
|
|
47
50
|
for (let i = 0; i < changes.length; ++i) {
|
|
48
51
|
const change = changes[i];
|
|
49
52
|
const prop = change[0];
|
|
50
53
|
switch (prop) {
|
|
54
|
+
case 'source':
|
|
55
|
+
case 'position':
|
|
56
|
+
continue;
|
|
51
57
|
case 'resources':
|
|
52
58
|
assert(reset);
|
|
53
|
-
// プロトタイプに戻ることで戻す
|
|
54
|
-
continue;
|
|
55
59
|
}
|
|
56
60
|
context[prop] = values[i];
|
|
57
61
|
values[i] = undefined;
|