securemark 0.289.2 → 0.289.4
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 +7 -6
- package/dist/index.js +141 -88
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +1 -1
- package/src/combinator/control/constraint/contract.ts +7 -7
- package/src/combinator/control/constraint/line.ts +1 -1
- package/src/combinator/control/manipulation/clear.ts +7 -0
- package/src/combinator/control/manipulation/convert.ts +1 -1
- package/src/combinator/control/manipulation/duplicate.ts +4 -4
- package/src/combinator/control/manipulation/fallback.ts +3 -3
- package/src/combinator/control/manipulation/indent.ts +3 -3
- package/src/combinator/control/manipulation/lazy.ts +2 -2
- package/src/combinator/control/manipulation/match.ts +1 -1
- package/src/combinator/control/manipulation/recovery.ts +3 -3
- package/src/combinator/control/manipulation/reverse.ts +1 -1
- package/src/combinator/control/manipulation/scope.ts +3 -3
- package/src/combinator/control/manipulation/surround.ts +59 -61
- package/src/combinator/control/manipulation/trim.ts +3 -3
- package/src/combinator/control/monad/bind.ts +6 -6
- package/src/combinator/control/monad/fmap.ts +6 -6
- package/src/combinator/data/parser/context/delimiter.ts +47 -26
- package/src/combinator/data/parser/context.ts +8 -8
- package/src/combinator/data/parser/inits.ts +4 -4
- package/src/combinator/data/parser/sequence.ts +4 -4
- package/src/combinator/data/parser/some.ts +3 -3
- package/src/combinator/data/parser/subsequence.ts +3 -3
- package/src/combinator/data/parser/tails.ts +3 -3
- package/src/combinator/data/parser/union.ts +3 -3
- package/src/combinator/data/parser.ts +14 -13
- package/src/combinator.ts +1 -0
- package/src/parser/api/parse.test.ts +2 -2
- package/src/parser/block/extension/figure.ts +1 -1
- package/src/parser/block/extension/table.ts +3 -3
- package/src/parser/block/olist.ts +4 -4
- package/src/parser/block/reply/cite.ts +3 -3
- package/src/parser/block/ulist.ts +1 -1
- package/src/parser/inline/autolink/hashnum.ts +5 -1
- package/src/parser/inline/code.ts +2 -1
- package/src/parser/inline/emstrong.ts +2 -1
- package/src/parser/inline/extension/indexer.ts +6 -0
- package/src/parser/inline/extension/label.ts +1 -1
- package/src/parser/inline/html.ts +2 -2
- package/src/parser/inline.test.ts +1 -0
- package/src/parser/inline.ts +19 -4
- package/src/parser/util.ts +10 -10
- package/src/parser/visibility.ts +11 -11
- package/src/util/quote.ts +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { isArray } from 'spica/alias';
|
|
2
|
-
import { Parser, Input, Ctx,
|
|
2
|
+
import { Parser, Input, Ctx, Node, Context, eval, exec, check } from '../../data/parser';
|
|
3
3
|
|
|
4
4
|
//export function contract<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], parser: P, cond: (nodes: readonly Data<P>[], rest: string) => boolean): P;
|
|
5
|
-
//export function contract<
|
|
5
|
+
//export function contract<N>(patterns: string | RegExp | (string | RegExp)[], parser: Parser<N>, cond: (nodes: readonly N[], rest: string) => boolean): Parser<N> {
|
|
6
6
|
// return verify(validate(patterns, parser), cond);
|
|
7
7
|
//}
|
|
8
8
|
|
|
9
9
|
export function validate<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], parser: P): P;
|
|
10
10
|
export function validate<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], has: string, parser: P): P;
|
|
11
11
|
export function validate<P extends Parser<unknown>>(cond: ((input: Input<Context<P>>) => boolean), parser: P): P;
|
|
12
|
-
export function validate<
|
|
13
|
-
if (typeof patterns === 'function') return guard(patterns, has as Parser<
|
|
12
|
+
export function validate<N>(patterns: string | RegExp | (string | RegExp)[] | ((input: Input<Ctx>) => boolean), has: string | Parser<N>, parser?: Parser<N>): Parser<N> {
|
|
13
|
+
if (typeof patterns === 'function') return guard(patterns, has as Parser<N>);
|
|
14
14
|
if (typeof has === 'function') return validate(patterns, '', has);
|
|
15
15
|
if (!isArray(patterns)) return validate([patterns], has, parser!);
|
|
16
16
|
assert(patterns.length > 0);
|
|
@@ -38,15 +38,15 @@ export function validate<T>(patterns: string | RegExp | (string | RegExp)[] | ((
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function guard<P extends Parser<unknown>>(f: (input: Input<Context<P>>) => boolean, parser: P): P;
|
|
41
|
-
function guard<
|
|
41
|
+
function guard<N>(f: (input: Input<Ctx>) => boolean, parser: Parser<N>): Parser<N> {
|
|
42
42
|
return input =>
|
|
43
43
|
f(input)
|
|
44
44
|
? parser(input)
|
|
45
45
|
: undefined;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export function verify<P extends Parser<unknown>>(parser: P, cond: (nodes: readonly
|
|
49
|
-
export function verify<
|
|
48
|
+
export function verify<P extends Parser<unknown>>(parser: P, cond: (nodes: readonly Node<P>[], rest: string, context: Context<P>) => boolean): P;
|
|
49
|
+
export function verify<N>(parser: Parser<N>, cond: (nodes: readonly N[], rest: string, context: Ctx) => boolean): Parser<N> {
|
|
50
50
|
assert(parser);
|
|
51
51
|
return input => {
|
|
52
52
|
const { source, context } = input;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Parser, eval, exec, check } from '../../data/parser';
|
|
2
2
|
|
|
3
3
|
export function line<P extends Parser<unknown>>(parser: P): P;
|
|
4
|
-
export function line<
|
|
4
|
+
export function line<N>(parser: Parser<N>): Parser<N> {
|
|
5
5
|
assert(parser);
|
|
6
6
|
return ({ source, context }) => {
|
|
7
7
|
if (source === '') return;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Parser, Ctx } from '../../data/parser';
|
|
2
|
+
import { fmap } from '../monad/fmap';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function clear<D extends Parser<unknown, C>[], C extends Ctx>(parser: Parser<unknown, C, D>): Parser<never, C, D> {
|
|
6
|
+
return fmap<never, Parser<unknown, C, D>>(parser, () => []);
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Parser, Ctx, Context, check } from '../../data/parser';
|
|
2
2
|
|
|
3
3
|
export function convert<P extends Parser<unknown>>(conv: (source: string, context: Context<P>) => string, parser: P, continuous: boolean, empty?: boolean): P;
|
|
4
|
-
export function convert<
|
|
4
|
+
export function convert<N>(conv: (source: string, context: Ctx) => string, parser: Parser<N>, continuous: boolean, empty = false): Parser<N> {
|
|
5
5
|
assert(parser);
|
|
6
6
|
return ({ source, context }) => {
|
|
7
7
|
if (source === '') return;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Parser, Ctx,
|
|
1
|
+
import { Parser, Ctx, Node, Context, SubParsers } from '../../data/parser';
|
|
2
2
|
import { fmap } from '../monad/fmap';
|
|
3
3
|
|
|
4
|
-
export function dup<P extends Parser<unknown[]>>(parser: Parser<
|
|
5
|
-
export function dup<
|
|
6
|
-
export function dup<
|
|
4
|
+
export function dup<P extends Parser<unknown[]>>(parser: Parser<Node<P>[number], Context<P>, SubParsers<P>>): P;
|
|
5
|
+
export function dup<N, C extends Ctx, D extends Parser<unknown, C>[]>(parser: Parser<N, C, D>): Parser<N[], C, D>;
|
|
6
|
+
export function dup<N>(parser: Parser<N>): Parser<N[]> {
|
|
7
7
|
return fmap(parser, nodes => [nodes]);
|
|
8
8
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Parser,
|
|
1
|
+
import { Parser, Node, Context } from '../../data/parser';
|
|
2
2
|
import { union } from '../../data/parser/union';
|
|
3
3
|
|
|
4
|
-
export function fallback<P extends Parser<unknown>>(parser: P, otherwise: Parser<
|
|
5
|
-
export function fallback<
|
|
4
|
+
export function fallback<P extends Parser<unknown>>(parser: P, otherwise: Parser<Node<P>, Context<P>>): P;
|
|
5
|
+
export function fallback<N>(parser: Parser<N>, otherwise: Parser<N>): Parser<N> {
|
|
6
6
|
return union([parser, otherwise]);
|
|
7
7
|
}
|
|
@@ -9,7 +9,7 @@ import { memoize } from 'spica/memoize';
|
|
|
9
9
|
|
|
10
10
|
export function indent<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
|
|
11
11
|
export function indent<P extends Parser<unknown>>(opener: RegExp, parser: P, separation?: boolean): P;
|
|
12
|
-
export function indent<
|
|
12
|
+
export function indent<N>(opener: RegExp | Parser<N>, parser?: Parser<N> | boolean, separation = false): Parser<N> {
|
|
13
13
|
if (typeof opener === 'function') return indent(/^([ \t])\1*/, opener, parser as boolean);
|
|
14
14
|
assert(parser);
|
|
15
15
|
return bind(block(match(
|
|
@@ -17,9 +17,9 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
|
|
|
17
17
|
memoize(
|
|
18
18
|
([indent]) =>
|
|
19
19
|
some(line(open(indent, ({ source }) => [[source], '']))),
|
|
20
|
-
([indent]) => indent.length * 2 + +(indent[0] === ' '), {})
|
|
20
|
+
([indent]) => indent.length * 2 + +(indent[0] === ' '), {})), separation),
|
|
21
21
|
(lines, rest, context) => {
|
|
22
|
-
assert(parser = parser as Parser<
|
|
22
|
+
assert(parser = parser as Parser<N>);
|
|
23
23
|
// 影響する使用はないはず
|
|
24
24
|
//const { backtracks } = context;
|
|
25
25
|
//context.backtracks = {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Parser } from '../../data/parser';
|
|
2
2
|
|
|
3
3
|
export function lazy<P extends Parser<unknown>>(builder: () => P): P;
|
|
4
|
-
export function lazy<
|
|
5
|
-
let parser: Parser<
|
|
4
|
+
export function lazy<N>(builder: () => Parser<N>): Parser<N> {
|
|
5
|
+
let parser: Parser<N>;
|
|
6
6
|
return input =>
|
|
7
7
|
parser !== undefined
|
|
8
8
|
? parser(input)
|
|
@@ -2,7 +2,7 @@ import { Parser, exec, check } from '../../data/parser';
|
|
|
2
2
|
import { consume } from '../../../combinator';
|
|
3
3
|
|
|
4
4
|
export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P, cost?: boolean): P;
|
|
5
|
-
export function match<
|
|
5
|
+
export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<N>, cost = false): Parser<N> {
|
|
6
6
|
assert(!pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^'));
|
|
7
7
|
return input => {
|
|
8
8
|
const { source, context } = input;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Parser, Input, Result,
|
|
1
|
+
import { Parser, Input, Result, Node, Context } from '../../data/parser';
|
|
2
2
|
|
|
3
|
-
export function recover<P extends Parser<unknown>>(parser: P, fallback: (input: Input<Context<P>>, reason: unknown) => Result<
|
|
4
|
-
export function recover<
|
|
3
|
+
export function recover<P extends Parser<unknown>>(parser: P, fallback: (input: Input<Context<P>>, reason: unknown) => Result<Node<P>>): P;
|
|
4
|
+
export function recover<N>(parser: Parser<N>, fallback: (input: Input, reason: unknown) => Result<N>): Parser<N> {
|
|
5
5
|
return input => {
|
|
6
6
|
try {
|
|
7
7
|
return parser(input);
|
|
@@ -2,7 +2,7 @@ import { Parser } from '../../data/parser';
|
|
|
2
2
|
import { fmap } from '../monad/fmap';
|
|
3
3
|
|
|
4
4
|
export function reverse<P extends Parser<unknown>>(parser: P): P;
|
|
5
|
-
export function reverse<
|
|
5
|
+
export function reverse<N>(parser: Parser<N>): Parser<N> {
|
|
6
6
|
return fmap(parser, nodes => nodes.reverse());
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import { Parser, Context, eval, exec, check } 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
|
-
export function focus<
|
|
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
8
|
const match: (source: string) => string = typeof scope === 'string'
|
|
@@ -29,9 +29,9 @@ export function focus<T>(scope: string | RegExp, parser: Parser<T>, cost = true)
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
//export function rewrite<
|
|
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>;
|
|
33
33
|
export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Context<P>>, parser: P): P;
|
|
34
|
-
export function rewrite<
|
|
34
|
+
export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>): Parser<N> {
|
|
35
35
|
assert(scope);
|
|
36
36
|
assert(parser);
|
|
37
37
|
return input => {
|
|
@@ -1,47 +1,46 @@
|
|
|
1
|
-
import { Parser, Input, Result, Ctx,
|
|
2
|
-
import { fmap } from '../monad/fmap';
|
|
1
|
+
import { Parser, Input, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, exec, check } from '../../data/parser';
|
|
3
2
|
import { unshift, push } from 'spica/array';
|
|
4
3
|
|
|
5
4
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
6
5
|
opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
|
|
7
6
|
optional?: false,
|
|
8
|
-
f?: (rss: [S[],
|
|
9
|
-
g?: (rss: [S[],
|
|
7
|
+
f?: (rss: [S[], SubNode<P>[], S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
8
|
+
g?: (rss: [S[], SubNode<P>[], string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
10
9
|
backtracks?: readonly number[],
|
|
11
10
|
backtrackstate?: number,
|
|
12
11
|
): P;
|
|
13
12
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
14
13
|
opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
|
|
15
14
|
optional?: boolean,
|
|
16
|
-
f?: (rss: [S[],
|
|
17
|
-
g?: (rss: [S[],
|
|
15
|
+
f?: (rss: [S[], SubNode<P>[] | undefined, S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
16
|
+
g?: (rss: [S[], SubNode<P>[] | undefined, string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
18
17
|
backtracks?: readonly number[],
|
|
19
18
|
backtrackstate?: number,
|
|
20
19
|
): P;
|
|
21
20
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
22
21
|
opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
|
|
23
22
|
optional?: false,
|
|
24
|
-
f?: (rss: [S[],
|
|
25
|
-
g?: (rss: [S[],
|
|
23
|
+
f?: (rss: [S[], Node<P>[], S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
24
|
+
g?: (rss: [S[], Node<P>[], string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
26
25
|
backtracks?: readonly number[],
|
|
27
26
|
backtrackstate?: number,
|
|
28
27
|
): P;
|
|
29
28
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
30
29
|
opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
|
|
31
30
|
optional?: boolean,
|
|
32
|
-
f?: (rss: [S[],
|
|
33
|
-
g?: (rss: [S[],
|
|
31
|
+
f?: (rss: [S[], Node<P>[] | undefined, S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
32
|
+
g?: (rss: [S[], Node<P>[] | undefined, string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
|
|
34
33
|
backtracks?: readonly number[],
|
|
35
34
|
backtrackstate?: number,
|
|
36
35
|
): P;
|
|
37
|
-
export function surround<
|
|
38
|
-
opener: string | RegExp | Parser<
|
|
36
|
+
export function surround<N>(
|
|
37
|
+
opener: string | RegExp | Parser<N>, parser: Parser<N>, closer: string | RegExp | Parser<N>,
|
|
39
38
|
optional: boolean = false,
|
|
40
|
-
f?: (rss: [
|
|
41
|
-
g?: (rss: [
|
|
39
|
+
f?: (rss: [N[], N[], N[]], rest: string, context: Ctx) => Result<N>,
|
|
40
|
+
g?: (rss: [N[], N[], string], rest: string, context: Ctx) => Result<N>,
|
|
42
41
|
backtracks: readonly number[] = [],
|
|
43
42
|
backtrackstate: number = 0,
|
|
44
|
-
): Parser<
|
|
43
|
+
): Parser<N> {
|
|
45
44
|
switch (typeof opener) {
|
|
46
45
|
case 'string':
|
|
47
46
|
case 'object':
|
|
@@ -54,21 +53,22 @@ export function surround<T>(
|
|
|
54
53
|
}
|
|
55
54
|
const statesize = 2;
|
|
56
55
|
return ({ source, context }) => {
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
56
|
+
const sme_ = source;
|
|
57
|
+
if (sme_ === '') return;
|
|
59
58
|
const { linebreak } = context;
|
|
60
59
|
context.linebreak = undefined;
|
|
61
|
-
const
|
|
62
|
-
assert(check(
|
|
63
|
-
if (
|
|
64
|
-
const
|
|
65
|
-
const
|
|
60
|
+
const resultS = opener({ source: sme_, context });
|
|
61
|
+
assert(check(sme_, resultS, false));
|
|
62
|
+
if (resultS === undefined) return void revert(context, linebreak);
|
|
63
|
+
const nodesS = eval(resultS);
|
|
64
|
+
const me_ = exec(resultS);
|
|
66
65
|
for (const backtrack of backtracks) {
|
|
67
66
|
if (backtrack & 1) {
|
|
68
67
|
const { backtracks = {}, backtrack: state = 0, offset = 0 } = context;
|
|
69
|
-
for (let i = 0; i < source.length -
|
|
68
|
+
for (let i = 0; i < source.length - me_.length; ++i) {
|
|
70
69
|
if (source[i] !== source[0]) break;
|
|
71
|
-
const pos = source.length + offset -
|
|
70
|
+
const pos = source.length - i + offset - 1;
|
|
71
|
+
assert(pos >= 0);
|
|
72
72
|
if (!(pos in backtracks)) continue;
|
|
73
73
|
const shift = backtrack >>> statesize & state >>> statesize ? state & (1 << statesize) - 1 : 0;
|
|
74
74
|
if (backtracks[pos] & 1 << size(backtrack >>> statesize) + shift) return void revert(context, linebreak);
|
|
@@ -77,35 +77,37 @@ export function surround<T>(
|
|
|
77
77
|
}
|
|
78
78
|
const { backtrack = 0 } = context;
|
|
79
79
|
context.backtrack = backtrack | backtrackstate;
|
|
80
|
-
const
|
|
81
|
-
assert(check(
|
|
80
|
+
const resultM = me_ !== '' ? parser({ source: me_, context }) : undefined;
|
|
81
|
+
assert(check(me_, resultM));
|
|
82
82
|
context.backtrack = backtrack;
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
86
|
-
const
|
|
87
|
-
assert(check(
|
|
88
|
-
const
|
|
89
|
-
const rest = exec(
|
|
90
|
-
if (rest.length ===
|
|
83
|
+
const nodesM = eval(resultM);
|
|
84
|
+
const e_ = exec(resultM, me_);
|
|
85
|
+
if (!nodesM && !optional) return void revert(context, linebreak);
|
|
86
|
+
const resultE = closer({ source: e_, context });
|
|
87
|
+
assert(check(e_, resultE, false));
|
|
88
|
+
const nodesE = eval(resultE);
|
|
89
|
+
const rest = exec(resultE, e_);
|
|
90
|
+
if (rest.length === sme_.length) return void revert(context, linebreak);
|
|
91
91
|
for (const backtrack of backtracks) {
|
|
92
|
-
if (backtrack & 2 &&
|
|
92
|
+
if (backtrack & 2 && nodesE === undefined) {
|
|
93
93
|
const { backtracks = {}, backtrack: state = 0, offset = 0 } = context;
|
|
94
|
+
const pos = source.length + offset - 1;
|
|
95
|
+
assert(pos >= 0);
|
|
94
96
|
const shift = backtrack >>> statesize & state >>> statesize ? state & (1 << statesize) - 1 : 0;
|
|
95
|
-
backtracks[
|
|
97
|
+
backtracks[pos] |= 1 << size(backtrack >>> statesize) + shift;
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
context.recent = [
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
sme_.slice(0, sme_.length - me_.length),
|
|
102
|
+
me_.slice(0, me_.length - e_.length),
|
|
103
|
+
e_.slice(0, e_.length - rest.length),
|
|
102
104
|
];
|
|
103
|
-
const result =
|
|
105
|
+
const result = nodesE
|
|
104
106
|
? f
|
|
105
|
-
? f([
|
|
106
|
-
: [push(unshift(
|
|
107
|
+
? f([nodesS, nodesM!, nodesE], rest, context)
|
|
108
|
+
: [push(unshift(nodesS, nodesM ?? []), nodesE), rest] satisfies [N[], string]
|
|
107
109
|
: g
|
|
108
|
-
? g([
|
|
110
|
+
? g([nodesS, nodesM!, me_], rest, context)
|
|
109
111
|
: undefined;
|
|
110
112
|
if (result) {
|
|
111
113
|
context.linebreak ??= linebreak;
|
|
@@ -116,6 +118,14 @@ export function surround<T>(
|
|
|
116
118
|
return result;
|
|
117
119
|
};
|
|
118
120
|
}
|
|
121
|
+
export function open<P extends Parser<unknown>>(opener: string | RegExp | Parser<Node<P>, Context<P>>, parser: P, optional?: boolean): P;
|
|
122
|
+
export function open<N>(opener: string | RegExp | Parser<N>, parser: Parser<N>, optional = false): Parser<N> {
|
|
123
|
+
return surround(opener, parser, '', optional);
|
|
124
|
+
}
|
|
125
|
+
export function close<P extends Parser<unknown>>(parser: P, closer: string | RegExp | Parser<Node<P>, Context<P>>, optional?: boolean): P;
|
|
126
|
+
export function close<N>(parser: Parser<N>, closer: string | RegExp | Parser<N>, optional: boolean = false): Parser<N> {
|
|
127
|
+
return surround('', parser, closer, optional);
|
|
128
|
+
}
|
|
119
129
|
|
|
120
130
|
function match(pattern: string | RegExp): (input: Input) => [never[], string] | undefined {
|
|
121
131
|
switch (typeof pattern) {
|
|
@@ -130,12 +140,16 @@ function match(pattern: string | RegExp): (input: Input) => [never[], string] |
|
|
|
130
140
|
};
|
|
131
141
|
}
|
|
132
142
|
}
|
|
143
|
+
function revert(context: Ctx, linebreak: number | undefined): void {
|
|
144
|
+
context.linebreak = linebreak;
|
|
145
|
+
}
|
|
133
146
|
function size(bits: number): number {
|
|
134
147
|
if (bits === 0) return 0;
|
|
135
148
|
let p = 0;
|
|
136
149
|
for (let s = 32 / 2; s > 0; s >>>= 1) {
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
const q = p + s;
|
|
151
|
+
if (bits >>> q === 0) continue;
|
|
152
|
+
p = q;
|
|
139
153
|
}
|
|
140
154
|
return p + 1;
|
|
141
155
|
}
|
|
@@ -144,19 +158,3 @@ assert(size(1 << 0) === 1);
|
|
|
144
158
|
assert(size(1 << 1) === 2);
|
|
145
159
|
assert(size(1 << 30) === 31);
|
|
146
160
|
assert(size(1 << 31) === 32);
|
|
147
|
-
function revert(context: Ctx, linebreak: number | undefined): void {
|
|
148
|
-
context.linebreak = linebreak;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export function open<P extends Parser<unknown>>(opener: string | RegExp | Parser<Tree<P>, Context<P>>, parser: P, optional?: boolean): P;
|
|
152
|
-
export function open<T>(opener: string | RegExp | Parser<T>, parser: Parser<T>, optional = false): Parser<T> {
|
|
153
|
-
return surround(opener, parser, '', optional);
|
|
154
|
-
}
|
|
155
|
-
export function close<P extends Parser<unknown>>(parser: P, closer: string | RegExp | Parser<Tree<P>, Context<P>>, optional?: boolean): P;
|
|
156
|
-
export function close<T>(parser: Parser<T>, closer: string | RegExp | Parser<T>, optional: boolean = false): Parser<T> {
|
|
157
|
-
return surround('', parser, closer, optional);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function clear<D extends Parser<unknown, C>[], C extends Ctx>(parser: Parser<unknown, C, D>): Parser<never, C, D> {
|
|
161
|
-
return fmap<never, Parser<unknown, C, D>>(parser, () => []);
|
|
162
|
-
}
|
|
@@ -2,16 +2,16 @@ import { Parser } from '../../data/parser';
|
|
|
2
2
|
import { convert } from './convert';
|
|
3
3
|
|
|
4
4
|
export function trim<P extends Parser<unknown>>(parser: P): P;
|
|
5
|
-
export function trim<
|
|
5
|
+
export function trim<N>(parser: Parser<N>): Parser<N> {
|
|
6
6
|
return convert(source => source.trim(), parser, false);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function trimStart<P extends Parser<unknown>>(parser: P): P;
|
|
10
|
-
export function trimStart<
|
|
10
|
+
export function trimStart<N>(parser: Parser<N>): Parser<N> {
|
|
11
11
|
return convert(source => source.trimStart(), parser, true);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function trimEnd<P extends Parser<unknown>>(parser: P): P;
|
|
15
|
-
export function trimEnd<
|
|
15
|
+
export function trimEnd<N>(parser: Parser<N>): Parser<N> {
|
|
16
16
|
return convert(source => source.trimEnd(), parser, false);
|
|
17
17
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Parser, Result, Ctx,
|
|
1
|
+
import { Parser, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, exec, check } from '../../data/parser';
|
|
2
2
|
|
|
3
|
-
export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes:
|
|
4
|
-
export function bind<P extends Parser<unknown>>(parser: P, f: (nodes:
|
|
5
|
-
export function bind<
|
|
6
|
-
export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes:
|
|
7
|
-
export function bind<
|
|
3
|
+
export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], rest: string, 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>[], rest: string, 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[], rest: string, 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>[], rest: string, 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[], rest: string, context: Ctx) => Result<U>): Parser<U> {
|
|
8
8
|
assert(parser);
|
|
9
9
|
return input => {
|
|
10
10
|
const { source, context } = input;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Parser, Ctx, SubParsers,
|
|
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:
|
|
5
|
-
export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes:
|
|
6
|
-
export function fmap<
|
|
7
|
-
export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes:
|
|
8
|
-
export function fmap<
|
|
4
|
+
export function fmap<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], rest: string, context: Context<P>) => Node<P>[]): P;
|
|
5
|
+
export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], rest: string, 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[], rest: string, context: Context<P>) => Node<P>[]): P;
|
|
7
|
+
export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], rest: string, context: Context<P>) => U[]): Parser<U, Context<P>, SubParsers<P>>;
|
|
8
|
+
export function fmap<N, U>(parser: Parser<N>, f: (nodes: N[], rest: string, context: Ctx) => U[]): Parser<U> {
|
|
9
9
|
return bind(parser, (nodes, rest, context) => [f(nodes, rest, context), rest]);
|
|
10
10
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Ctx } from '../../parser';
|
|
2
|
-
import { memoize } from 'spica/memoize';
|
|
3
2
|
|
|
4
3
|
interface Delimiter {
|
|
4
|
+
readonly memory: Delimiter[];
|
|
5
5
|
readonly index: number;
|
|
6
|
-
readonly signature: string;
|
|
6
|
+
readonly signature: number | string;
|
|
7
7
|
readonly matcher: (source: string) => boolean | undefined;
|
|
8
8
|
readonly precedence: number;
|
|
9
9
|
readonly linebreakable: boolean;
|
|
@@ -11,54 +11,74 @@ interface Delimiter {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export class Delimiters {
|
|
14
|
-
|
|
14
|
+
// 手間を惜しまなければ規定のパターンはすべて配列のインデクスに変換可能。
|
|
15
|
+
public static signature(pattern: string | RegExp | undefined, linebreakable: boolean): number | string {
|
|
15
16
|
switch (typeof pattern) {
|
|
16
17
|
case 'undefined':
|
|
17
|
-
return
|
|
18
|
+
return +linebreakable;
|
|
18
19
|
case 'string':
|
|
19
|
-
|
|
20
|
+
assert(pattern !== '\x00');
|
|
21
|
+
if (pattern.length === 1) {
|
|
22
|
+
const code = pattern.charCodeAt(0);
|
|
23
|
+
// 使用中のパターンの8ビット目が空いてるのでひとまずこうしとく
|
|
24
|
+
if ((code & 1 << 7) === 0) return code | +linebreakable << 7;
|
|
25
|
+
}
|
|
26
|
+
return `s:${pattern}:${+linebreakable}`;
|
|
20
27
|
case 'object':
|
|
21
|
-
return `r/${pattern.source}/${
|
|
28
|
+
return `r/${pattern.source}/${+linebreakable}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
public static matcher(pattern: string | RegExp | undefined): (source: string) => true | undefined {
|
|
32
|
+
switch (typeof pattern) {
|
|
33
|
+
case 'undefined':
|
|
34
|
+
return () => undefined;
|
|
35
|
+
case 'string':
|
|
36
|
+
return source => source.slice(0, pattern.length) === pattern || undefined;
|
|
37
|
+
case 'object':
|
|
38
|
+
return source => pattern.test(source) || undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
private readonly heap: Record<number, Delimiter[]> = {};
|
|
42
|
+
private readonly map: Map<string, Delimiter[]> = new Map();
|
|
43
|
+
private registry(signature: number | string): Delimiter[] {
|
|
44
|
+
if (typeof signature === 'number') {
|
|
45
|
+
return this.heap[signature] ??= [];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const ds = this.map.get(signature);
|
|
49
|
+
if (ds) return ds;
|
|
50
|
+
const blank: Delimiter[] = [];
|
|
51
|
+
this.map.set(signature, blank);
|
|
52
|
+
return blank;
|
|
22
53
|
}
|
|
23
54
|
}
|
|
24
|
-
public static matcher = memoize(
|
|
25
|
-
(pattern: string | RegExp | undefined): (source: string) => true | undefined => {
|
|
26
|
-
switch (typeof pattern) {
|
|
27
|
-
case 'undefined':
|
|
28
|
-
return () => undefined;
|
|
29
|
-
case 'string':
|
|
30
|
-
return source => source.slice(0, pattern.length) === pattern || undefined;
|
|
31
|
-
case 'object':
|
|
32
|
-
return source => pattern.test(source) || undefined;
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
this.signature);
|
|
36
|
-
private readonly registry = memoize<(signature: string) => Delimiter[]>(() => []);
|
|
37
55
|
private readonly delimiters: Delimiter[] = [];
|
|
38
56
|
private readonly stack: number[] = [];
|
|
39
57
|
private readonly states: (readonly number[])[] = [];
|
|
40
58
|
public push(
|
|
41
59
|
delims: readonly {
|
|
42
|
-
readonly signature: string;
|
|
60
|
+
readonly signature: number | string;
|
|
43
61
|
readonly matcher: (source: string) => boolean | undefined;
|
|
44
62
|
readonly precedence: number;
|
|
45
63
|
readonly linebreakable: boolean;
|
|
46
64
|
}[]
|
|
47
65
|
): void {
|
|
48
|
-
const {
|
|
66
|
+
const { delimiters, stack } = this;
|
|
49
67
|
// シグネチャ数以下
|
|
50
68
|
assert(delimiters.length < 100);
|
|
51
69
|
for (let i = 0; i < delims.length; ++i) {
|
|
52
70
|
const { signature, matcher, precedence, linebreakable } = delims[i];
|
|
53
|
-
const memory = registry(signature);
|
|
71
|
+
const memory = this.registry(signature);
|
|
54
72
|
const index = memory[0]?.index ?? delimiters.length;
|
|
55
|
-
|
|
73
|
+
assert(memory.length === 0 || precedence === delimiters[index].precedence);
|
|
74
|
+
if (memory.length === 0) {
|
|
56
75
|
const delimiter: Delimiter = {
|
|
76
|
+
memory,
|
|
57
77
|
index,
|
|
58
78
|
signature,
|
|
59
79
|
matcher,
|
|
60
80
|
precedence,
|
|
61
|
-
linebreakable
|
|
81
|
+
linebreakable,
|
|
62
82
|
state: true,
|
|
63
83
|
};
|
|
64
84
|
delimiters[index] = delimiter;
|
|
@@ -74,12 +94,12 @@ export class Delimiters {
|
|
|
74
94
|
}
|
|
75
95
|
public pop(count: number): void {
|
|
76
96
|
assert(count > 0);
|
|
77
|
-
const {
|
|
97
|
+
const { delimiters, stack } = this;
|
|
78
98
|
for (let i = 0; i < count; ++i) {
|
|
79
99
|
assert(this.stack.length > 0);
|
|
80
100
|
const index = stack.pop()!;
|
|
81
101
|
if (index === -1) continue;
|
|
82
|
-
const memory =
|
|
102
|
+
const { memory } = delimiters[index];
|
|
83
103
|
assert(memory.length > 0);
|
|
84
104
|
if (memory.length === 1) {
|
|
85
105
|
assert(index === delimiters.length - 1);
|
|
@@ -88,6 +108,7 @@ export class Delimiters {
|
|
|
88
108
|
delimiters.pop();
|
|
89
109
|
}
|
|
90
110
|
else {
|
|
111
|
+
assert(false);
|
|
91
112
|
memory.pop();
|
|
92
113
|
delimiters[index] = memory.at(-1)!;
|
|
93
114
|
}
|