securemark 0.293.4 → 0.294.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/dist/index.js +868 -564
- package/markdown.d.ts +13 -13
- package/package.json +3 -3
- package/src/combinator/control/constraint/block.test.ts +6 -6
- package/src/combinator/control/constraint/contract.ts +3 -3
- package/src/combinator/control/constraint/line.test.ts +7 -7
- package/src/combinator/control/constraint/line.ts +1 -1
- package/src/combinator/control/manipulation/clear.ts +2 -3
- package/src/combinator/control/manipulation/convert.ts +2 -2
- package/src/combinator/control/manipulation/duplicate.ts +4 -5
- package/src/combinator/control/manipulation/fence.ts +2 -2
- package/src/combinator/control/manipulation/indent.test.ts +2 -2
- package/src/combinator/control/manipulation/indent.ts +4 -4
- package/src/combinator/control/manipulation/reverse.ts +2 -2
- package/src/combinator/control/manipulation/scope.ts +3 -4
- package/src/combinator/control/manipulation/surround.ts +14 -15
- package/src/combinator/control/monad/bind.ts +6 -6
- package/src/combinator/control/monad/fmap.ts +7 -7
- package/src/combinator/data/data.ts +135 -0
- package/src/combinator/data/parser/context.test.ts +16 -15
- package/src/combinator/data/parser/context.ts +5 -4
- package/src/combinator/data/parser/inits.ts +6 -7
- package/src/combinator/data/parser/sequence.test.ts +3 -3
- package/src/combinator/data/parser/sequence.ts +6 -7
- package/src/combinator/data/parser/some.test.ts +3 -3
- package/src/combinator/data/parser/some.ts +4 -4
- package/src/combinator/data/parser/subsequence.test.ts +4 -4
- 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 +3 -3
- package/src/combinator/data/parser.ts +16 -7
- package/src/debug.test.ts +6 -5
- package/src/parser/api/bind.ts +6 -8
- package/src/parser/api/header.ts +1 -1
- package/src/parser/api/normalize.ts +2 -4
- package/src/parser/api/parse.ts +3 -1
- package/src/parser/block/blockquote.ts +6 -4
- package/src/parser/block/codeblock.ts +8 -7
- package/src/parser/block/dlist.ts +9 -8
- package/src/parser/block/extension/aside.ts +27 -21
- package/src/parser/block/extension/example.ts +29 -26
- package/src/parser/block/extension/fig.ts +1 -1
- package/src/parser/block/extension/figbase.ts +6 -5
- package/src/parser/block/extension/figure.ts +23 -19
- package/src/parser/block/extension/message.ts +35 -24
- package/src/parser/block/extension/placeholder.ts +17 -13
- package/src/parser/block/extension/table.ts +47 -40
- package/src/parser/block/heading.test.ts +3 -12
- package/src/parser/block/heading.ts +12 -8
- package/src/parser/block/ilist.ts +13 -12
- package/src/parser/block/mathblock.ts +21 -17
- package/src/parser/block/mediablock.ts +7 -5
- package/src/parser/block/olist.ts +15 -5
- package/src/parser/block/pagebreak.ts +2 -1
- package/src/parser/block/paragraph.ts +3 -1
- package/src/parser/block/reply/cite.ts +20 -15
- package/src/parser/block/reply/quote.ts +9 -7
- package/src/parser/block/reply.ts +7 -3
- package/src/parser/block/sidefence.ts +8 -7
- package/src/parser/block/table.ts +23 -22
- package/src/parser/block/ulist.ts +16 -12
- package/src/parser/block.ts +7 -6
- package/src/parser/header.test.ts +3 -1
- package/src/parser/header.ts +20 -20
- package/src/parser/inline/annotation.ts +3 -1
- package/src/parser/inline/autolink/account.ts +3 -2
- package/src/parser/inline/autolink/anchor.ts +3 -2
- package/src/parser/inline/autolink/channel.ts +5 -4
- package/src/parser/inline/autolink/email.ts +4 -3
- package/src/parser/inline/autolink/hashnum.ts +3 -2
- package/src/parser/inline/autolink/hashtag.ts +4 -3
- package/src/parser/inline/autolink/url.ts +7 -6
- package/src/parser/inline/bracket.ts +16 -15
- package/src/parser/inline/code.ts +5 -4
- package/src/parser/inline/deletion.ts +5 -5
- package/src/parser/inline/emphasis.ts +4 -3
- package/src/parser/inline/emstrong.test.ts +18 -18
- package/src/parser/inline/emstrong.ts +39 -30
- package/src/parser/inline/extension/index.ts +22 -19
- package/src/parser/inline/extension/indexee.ts +2 -2
- package/src/parser/inline/extension/indexer.ts +2 -1
- package/src/parser/inline/extension/label.ts +7 -3
- package/src/parser/inline/extension/placeholder.ts +6 -6
- package/src/parser/inline/html.ts +27 -28
- package/src/parser/inline/htmlentity.ts +9 -8
- package/src/parser/inline/insertion.ts +5 -5
- package/src/parser/inline/italic.ts +5 -5
- package/src/parser/inline/link.ts +36 -38
- package/src/parser/inline/mark.ts +7 -7
- package/src/parser/inline/math.ts +5 -4
- package/src/parser/inline/media.ts +33 -32
- package/src/parser/inline/reference.ts +19 -20
- package/src/parser/inline/remark.ts +11 -11
- package/src/parser/inline/ruby.ts +50 -53
- package/src/parser/inline/strong.ts +4 -3
- package/src/parser/inline/template.ts +16 -15
- package/src/parser/inline.test.ts +3 -3
- package/src/parser/segment.ts +3 -1
- package/src/parser/source/escapable.ts +9 -8
- package/src/parser/source/line.ts +4 -3
- package/src/parser/source/str.ts +2 -2
- package/src/parser/source/text.ts +19 -26
- package/src/parser/source/unescapable.ts +6 -5
- package/src/parser/util.ts +16 -30
- package/src/parser/visibility.ts +19 -20
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Parser, Ctx, CtxOptions, input } from '../parser';
|
|
1
|
+
import { Parser, List, Data, Ctx, CtxOptions, input, eval } from '../parser';
|
|
2
2
|
import { some } from './some';
|
|
3
|
-
import { reset, context } from './context';
|
|
4
|
-
import {
|
|
3
|
+
import { reset, context, creation } from './context';
|
|
4
|
+
import { unwrap } from '../../../parser/util';
|
|
5
5
|
|
|
6
6
|
describe('Unit: combinator/data/parser/context', () => {
|
|
7
7
|
interface Context extends CtxOptions {
|
|
@@ -12,26 +12,26 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
12
12
|
const parser: Parser<number> = some(creation(1,
|
|
13
13
|
({ context }) => {
|
|
14
14
|
context.position += 1;
|
|
15
|
-
return [
|
|
15
|
+
return new List([new Data(context.resources?.clock ?? NaN)]);
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
18
|
it('root', () => {
|
|
19
19
|
const base: Context = { resources: { clock: 3, recursions: [1] } };
|
|
20
20
|
const ctx: Context = {};
|
|
21
|
-
assert.deepStrictEqual(reset(base, parser)(input('123', ctx)), [
|
|
21
|
+
assert.deepStrictEqual([...unwrap(eval(reset(base, parser)(input('123', ctx)))!)], [3, 2, 1]);
|
|
22
22
|
assert(base.resources?.clock === 3);
|
|
23
23
|
assert(ctx.resources?.clock === undefined);
|
|
24
24
|
assert.throws(() => reset(base, parser)(input('1234', ctx)));
|
|
25
|
-
assert(ctx.resources?.clock ===
|
|
25
|
+
assert(ctx.resources?.clock === undefined);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
it('node', () => {
|
|
29
29
|
const base: Context = { resources: { clock: 3, recursions: [1] } };
|
|
30
|
-
const ctx: Context = { resources: { clock:
|
|
31
|
-
assert.deepStrictEqual(reset(base, parser)(input('1', ctx)), [
|
|
30
|
+
const ctx: Context = { resources: { clock: 2, recursions: [1] } };
|
|
31
|
+
assert.deepStrictEqual([...unwrap(eval(reset(base, parser)(input('1', ctx)))!)], [2]);
|
|
32
32
|
assert(base.resources?.clock === 3);
|
|
33
|
-
assert(ctx.resources?.clock ===
|
|
34
|
-
assert.throws(() => reset(base, parser)(input('
|
|
33
|
+
assert(ctx.resources?.clock === 1);
|
|
34
|
+
assert.throws(() => reset(base, parser)(input('12', ctx)));
|
|
35
35
|
assert(ctx.resources?.clock === 0);
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -41,16 +41,17 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
41
41
|
const parser: Parser<boolean, Context & Ctx> = some(creation(1,
|
|
42
42
|
({ context }) => {
|
|
43
43
|
context.position += 1;
|
|
44
|
-
return [
|
|
44
|
+
return new List([new Data(context.status!)]);
|
|
45
45
|
}));
|
|
46
46
|
|
|
47
47
|
it('', () => {
|
|
48
48
|
const base: Context = { status: true };
|
|
49
|
-
const ctx: Context = { resources: { clock:
|
|
50
|
-
assert.deepStrictEqual(context(base, parser)(input('
|
|
51
|
-
assert(
|
|
49
|
+
const ctx: Context = { resources: { clock: 2, recursions: [1] } };
|
|
50
|
+
assert.deepStrictEqual([...unwrap(eval(context(base, parser)(input('1', ctx)))!)], [true]);
|
|
51
|
+
assert(base.resources?.clock === undefined);
|
|
52
|
+
assert(ctx.resources?.clock === 1);
|
|
52
53
|
assert(ctx.status === undefined);
|
|
53
|
-
assert.throws(() =>
|
|
54
|
+
assert.throws(() => context(base, parser)(input('12', ctx)));
|
|
54
55
|
assert(ctx.resources?.clock === 0);
|
|
55
56
|
assert(ctx.status === true);
|
|
56
57
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { min } from 'spica/alias';
|
|
2
|
-
import { Parser, Result, Ctx, CtxOptions, Node, Context } from '../../data/parser';
|
|
2
|
+
import { Parser, Result, List, Data, Ctx, CtxOptions, Node, Context } from '../../data/parser';
|
|
3
3
|
import { clone } from 'spica/assign';
|
|
4
4
|
|
|
5
5
|
export function reset<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
|
|
@@ -9,7 +9,8 @@ export function reset<N>(base: Ctx, parser: Parser<N>): Parser<N> {
|
|
|
9
9
|
const changes = Object.entries(base);
|
|
10
10
|
const values = Array(changes.length);
|
|
11
11
|
return ({ context }) =>
|
|
12
|
-
|
|
12
|
+
// 大域離脱時の汚染回避のため複製
|
|
13
|
+
apply(parser, { ...context }, changes, values, true);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function context<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
|
|
@@ -172,7 +173,7 @@ export function matcher(pattern: string | RegExp, advance: boolean): Parser<stri
|
|
|
172
173
|
if (advance) {
|
|
173
174
|
context.position += pattern.length;
|
|
174
175
|
}
|
|
175
|
-
return [
|
|
176
|
+
return new List([new Data(pattern)]);
|
|
176
177
|
};
|
|
177
178
|
case 'object':
|
|
178
179
|
assert(pattern.sticky);
|
|
@@ -185,7 +186,7 @@ export function matcher(pattern: string | RegExp, advance: boolean): Parser<stri
|
|
|
185
186
|
if (advance) {
|
|
186
187
|
context.position += src.length;
|
|
187
188
|
}
|
|
188
|
-
return [
|
|
189
|
+
return new List([new Data(src)]);
|
|
189
190
|
};
|
|
190
191
|
}
|
|
191
192
|
}
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import { Parser, CtxOptions, Node, Context, SubParsers, SubNode, eval, Ctx } from '../parser';
|
|
2
|
-
import { push } from 'spica/array';
|
|
1
|
+
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode, eval, Ctx } from '../parser';
|
|
3
2
|
|
|
4
|
-
export function inits<P extends Parser<unknown, Ctx>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P
|
|
5
|
-
export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N
|
|
3
|
+
export function inits<P extends Parser<unknown, Ctx>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
4
|
+
export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
|
|
6
5
|
assert(parsers.every(f => f));
|
|
7
6
|
if (parsers.length === 1) return parsers[0];
|
|
8
7
|
return input => {
|
|
9
8
|
const { context } = input;
|
|
10
9
|
const { source, position } = context;
|
|
11
|
-
let nodes: N
|
|
10
|
+
let nodes: List<Data<N>> | undefined;
|
|
12
11
|
for (let len = parsers.length, i = 0; i < len; ++i) {
|
|
13
12
|
if (context.position === source.length) break;
|
|
14
13
|
if (context.delimiters?.match(input)) break;
|
|
15
14
|
const result = parsers[i](input);
|
|
16
15
|
if (result === undefined) break;
|
|
17
16
|
nodes = nodes
|
|
18
|
-
?
|
|
17
|
+
? nodes.import(eval(result))
|
|
19
18
|
: eval(result);
|
|
20
19
|
if (resume?.(eval(result)) === false) break;
|
|
21
20
|
}
|
|
22
21
|
assert(context.position >= position);
|
|
23
22
|
return nodes && context.position > position
|
|
24
|
-
?
|
|
23
|
+
? nodes
|
|
25
24
|
: undefined;
|
|
26
25
|
};
|
|
27
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, input } from '../parser';
|
|
1
|
+
import { Parser, List, Data, input } from '../parser';
|
|
2
2
|
import { sequence } from './sequence';
|
|
3
3
|
import { inspect } from '../../../debug.test';
|
|
4
4
|
|
|
@@ -6,12 +6,12 @@ describe('Unit: combinator/data/parser/sequence', () => {
|
|
|
6
6
|
describe('sequence', () => {
|
|
7
7
|
const a: Parser<string> = ({ context }) => {
|
|
8
8
|
return context.source[context.position] === 'a'
|
|
9
|
-
? void ++context.position || [
|
|
9
|
+
? void ++context.position || new List([new Data('A')])
|
|
10
10
|
: undefined;
|
|
11
11
|
};
|
|
12
12
|
const b: Parser<string> = ({ context }) => {
|
|
13
13
|
return context.source[context.position] === 'b'
|
|
14
|
-
? void ++context.position || [
|
|
14
|
+
? void ++context.position || new List([new Data('B')])
|
|
15
15
|
: undefined;
|
|
16
16
|
};
|
|
17
17
|
const ab = sequence<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import { Parser, CtxOptions, Node, Context, SubParsers, SubNode, eval } from '../parser';
|
|
2
|
-
import { push } from 'spica/array';
|
|
1
|
+
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode, eval } from '../parser';
|
|
3
2
|
|
|
4
|
-
export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P
|
|
5
|
-
export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N
|
|
3
|
+
export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
4
|
+
export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
|
|
6
5
|
assert(parsers.every(f => f));
|
|
7
6
|
if (parsers.length === 1) return parsers[0];
|
|
8
7
|
return input => {
|
|
9
8
|
const { context } = input;
|
|
10
9
|
const { source, position } = context;
|
|
11
|
-
let nodes: N
|
|
10
|
+
let nodes: List<Data<N>> | undefined;
|
|
12
11
|
for (let len = parsers.length, i = 0; i < len; ++i) {
|
|
13
12
|
if (context.position === source.length) return;
|
|
14
13
|
if (context.delimiters?.match(input)) return;
|
|
15
14
|
const result = parsers[i](input);
|
|
16
15
|
if (result === undefined) return;
|
|
17
16
|
nodes = nodes
|
|
18
|
-
?
|
|
17
|
+
? nodes.import(eval(result))
|
|
19
18
|
: eval(result);
|
|
20
19
|
if (resume?.(eval(result)) === false) return;
|
|
21
20
|
}
|
|
22
21
|
assert(context.position >= position);
|
|
23
22
|
return nodes && context.position > position
|
|
24
|
-
?
|
|
23
|
+
? nodes
|
|
25
24
|
: undefined;
|
|
26
25
|
};
|
|
27
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, input } from '../parser';
|
|
1
|
+
import { Parser, List, Data, input } from '../parser';
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { some } from './some';
|
|
4
4
|
import { inspect } from '../../../debug.test';
|
|
@@ -7,12 +7,12 @@ describe('Unit: combinator/data/parser/some', () => {
|
|
|
7
7
|
describe('some', () => {
|
|
8
8
|
const a: Parser<string> = ({ context }) => {
|
|
9
9
|
return context.source[context.position] === 'a'
|
|
10
|
-
? void ++context.position || [
|
|
10
|
+
? void ++context.position || new List([new Data('A')])
|
|
11
11
|
: undefined;
|
|
12
12
|
};
|
|
13
13
|
const b: Parser<string> = ({ context }) => {
|
|
14
14
|
return context.source[context.position] === 'b'
|
|
15
|
-
? void ++context.position || [
|
|
15
|
+
? void ++context.position || new List([new Data('B')])
|
|
16
16
|
: undefined;
|
|
17
17
|
};
|
|
18
18
|
const ab = union<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, eval } from '../parser';
|
|
1
|
+
import { Parser, List, Data, eval } from '../parser';
|
|
2
2
|
import { Delimiters } from './context/delimiter';
|
|
3
3
|
|
|
4
4
|
type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
|
|
@@ -18,7 +18,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
18
18
|
const { context } = input;
|
|
19
19
|
const { source, position } = context;
|
|
20
20
|
//assert(context.backtracks ??= {});
|
|
21
|
-
let nodes: N
|
|
21
|
+
let nodes: List<Data<N>> | undefined;
|
|
22
22
|
if (delims.length > 0) {
|
|
23
23
|
context.delimiters ??= new Delimiters();
|
|
24
24
|
context.delimiters.push(delims);
|
|
@@ -30,7 +30,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
30
30
|
const result = parser(input);
|
|
31
31
|
if (result === undefined) break;
|
|
32
32
|
nodes = nodes
|
|
33
|
-
?
|
|
33
|
+
? nodes.import(eval(result))
|
|
34
34
|
: eval(result);
|
|
35
35
|
if (limit >= 0 && context.position - position > limit) break;
|
|
36
36
|
}
|
|
@@ -39,7 +39,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
39
39
|
}
|
|
40
40
|
assert(context.position >= position);
|
|
41
41
|
return nodes && context.position > position
|
|
42
|
-
?
|
|
42
|
+
? nodes
|
|
43
43
|
: undefined;
|
|
44
44
|
};
|
|
45
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, input } from '../parser';
|
|
1
|
+
import { Parser, List, Data, input } from '../parser';
|
|
2
2
|
import { subsequence } from './subsequence';
|
|
3
3
|
import { inspect } from '../../../debug.test';
|
|
4
4
|
|
|
@@ -6,17 +6,17 @@ describe('Unit: combinator/data/parser/subsequence', () => {
|
|
|
6
6
|
describe('subsequence', () => {
|
|
7
7
|
const a: Parser<string> = ({ context }) => {
|
|
8
8
|
return context.source[context.position] === 'a'
|
|
9
|
-
? void ++context.position || [
|
|
9
|
+
? void ++context.position || new List([new Data('A')])
|
|
10
10
|
: undefined;
|
|
11
11
|
};
|
|
12
12
|
const b: Parser<string> = ({ context }) => {
|
|
13
13
|
return context.source[context.position] === 'b'
|
|
14
|
-
? void ++context.position || [
|
|
14
|
+
? void ++context.position || new List([new Data('B')])
|
|
15
15
|
: undefined;
|
|
16
16
|
};
|
|
17
17
|
const c: Parser<string> = ({ context }) => {
|
|
18
18
|
return context.source[context.position] === 'c'
|
|
19
|
-
? void ++context.position || [
|
|
19
|
+
? void ++context.position || new List([new Data('C')])
|
|
20
20
|
: undefined;
|
|
21
21
|
};
|
|
22
22
|
const abc = subsequence<Parser<string, {}, [typeof a, typeof b, typeof c]>>([a, b, c]);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Parser, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
|
|
1
|
+
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { inits } from './inits';
|
|
4
4
|
|
|
5
|
-
export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P
|
|
6
|
-
export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N
|
|
5
|
+
export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
6
|
+
export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
|
|
7
7
|
assert(parsers.every(f => f));
|
|
8
8
|
return union(
|
|
9
9
|
parsers.map((_, i) =>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Parser, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
|
|
1
|
+
import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { sequence } from './sequence';
|
|
4
4
|
|
|
5
|
-
export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P
|
|
6
|
-
export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N
|
|
5
|
+
export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
6
|
+
export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
|
|
7
7
|
return union(parsers.map((_, i) => sequence(parsers.slice(i), resume)) as D);
|
|
8
8
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Parser, input } from '../parser';
|
|
1
|
+
import { Parser, List, Data, input } from '../parser';
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { inspect } from '../../../debug.test';
|
|
4
4
|
|
|
@@ -6,12 +6,12 @@ describe('Unit: combinator/data/parser/union', () => {
|
|
|
6
6
|
describe('union', () => {
|
|
7
7
|
const a: Parser<string> = ({ context }) => {
|
|
8
8
|
return context.source[context.position] === 'a'
|
|
9
|
-
? void ++context.position || [
|
|
9
|
+
? void ++context.position || new List([new Data('A')])
|
|
10
10
|
: undefined;
|
|
11
11
|
};
|
|
12
12
|
const b: Parser<string> = ({ context }) => {
|
|
13
13
|
return context.source[context.position] === 'b'
|
|
14
|
-
? void ++context.position || [
|
|
14
|
+
? void ++context.position || new List([new Data('B')])
|
|
15
15
|
: undefined;
|
|
16
16
|
};
|
|
17
17
|
const ab = union<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { List } from './data';
|
|
1
2
|
import { Delimiters } from './parser/context/delimiter';
|
|
2
3
|
|
|
3
4
|
export type Parser<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
|
|
@@ -6,9 +7,16 @@ export interface Input<C extends CtxOptions = CtxOptions> {
|
|
|
6
7
|
readonly context: C & Ctx;
|
|
7
8
|
}
|
|
8
9
|
export type Result<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
|
|
9
|
-
= readonly [
|
|
10
|
-
|
|
|
10
|
+
= readonly [never, C, D]
|
|
11
|
+
| List<Data<N>>
|
|
11
12
|
| undefined;
|
|
13
|
+
export { List };
|
|
14
|
+
export class Data<N> implements List.Node {
|
|
15
|
+
constructor(public value: N) {
|
|
16
|
+
}
|
|
17
|
+
public next?: this = undefined;
|
|
18
|
+
public prev?: this = undefined;
|
|
19
|
+
}
|
|
12
20
|
export interface Ctx extends CtxOptions {
|
|
13
21
|
source: string;
|
|
14
22
|
position: number;
|
|
@@ -71,12 +79,13 @@ export function clean<C extends Ctx>(context: C): C {
|
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
export { eval_ as eval };
|
|
74
|
-
function eval_<N>(result: NonNullable<Result<N>>, default_?: N
|
|
75
|
-
function eval_<N>(result: Result<N>, default_: N
|
|
76
|
-
function eval_<N>(result: Result<N>, default_?: undefined): N
|
|
77
|
-
function eval_<N>(result: Result<N>, default_?: N
|
|
82
|
+
function eval_<N>(result: NonNullable<Result<N>>, default_?: List<Data<N>>): List<Data<N>>;
|
|
83
|
+
function eval_<N>(result: Result<N>, default_: List<Data<N>>): List<Data<N>>;
|
|
84
|
+
function eval_<N>(result: Result<N>, default_?: undefined): List<Data<N>> | undefined;
|
|
85
|
+
function eval_<N>(result: Result<N>, default_?: List<Data<N>>): List<Data<N>> | undefined {
|
|
86
|
+
assert(!Array.isArray(result));
|
|
78
87
|
return result
|
|
79
|
-
? result
|
|
88
|
+
? result as List<Data<N>>
|
|
80
89
|
: default_;
|
|
81
90
|
}
|
|
82
91
|
|
package/src/debug.test.ts
CHANGED
|
@@ -2,11 +2,12 @@ import { Result, Ctx, eval } from './combinator/data/parser';
|
|
|
2
2
|
import { html, define } from 'typed-dom/dom';
|
|
3
3
|
import { querySelectorWith, querySelectorAllWith } from 'typed-dom/query';
|
|
4
4
|
|
|
5
|
-
export function inspect(result: Result<HTMLElement | string>, ctx: Ctx, until: number | string = Infinity): [string[], string] | undefined {
|
|
5
|
+
export function inspect(result: Result<DocumentFragment | HTMLElement | string>, ctx: Ctx, until: number | string = Infinity): [string[], string] | undefined {
|
|
6
6
|
return result && [
|
|
7
|
-
eval(result).
|
|
7
|
+
eval(result).foldl<string[]>((acc, { value: node }) => {
|
|
8
8
|
assert(node);
|
|
9
|
-
if (typeof node === 'string') return node;
|
|
9
|
+
if (typeof node === 'string') return acc.push(node), acc;
|
|
10
|
+
if (node instanceof DocumentFragment) return acc.push(html('div', [node]).innerHTML), acc;
|
|
10
11
|
node = node.cloneNode(true);
|
|
11
12
|
assert(!querySelectorWith(node, '.invalid[data-invalid-message$="."]'));
|
|
12
13
|
querySelectorAllWith(node, '.invalid').forEach(el => {
|
|
@@ -31,8 +32,8 @@ export function inspect(result: Result<HTMLElement | string>, ctx: Ctx, until: n
|
|
|
31
32
|
else {
|
|
32
33
|
assert(el.innerHTML.startsWith(node.outerHTML.slice(0, until)));
|
|
33
34
|
}
|
|
34
|
-
return normalize(node.outerHTML.slice(0, until));
|
|
35
|
-
}),
|
|
35
|
+
return acc.push(normalize(node.outerHTML.slice(0, until))), acc;
|
|
36
|
+
}, []),
|
|
36
37
|
ctx.source.slice(ctx.position),
|
|
37
38
|
];
|
|
38
39
|
}
|
package/src/parser/api/bind.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserSettings, Progress } from '../../..';
|
|
2
2
|
import { MarkdownParser } from '../../../markdown';
|
|
3
3
|
import { input, eval } from '../../combinator/data/parser';
|
|
4
|
-
import { segment
|
|
4
|
+
import { segment } from '../segment';
|
|
5
5
|
import { header } from '../header';
|
|
6
6
|
import { block } from '../block';
|
|
7
7
|
import { normalize } from './normalize';
|
|
@@ -44,12 +44,9 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
44
44
|
function* parse(source: string): Generator<Progress, undefined, undefined> {
|
|
45
45
|
if (settings.chunk && revision) throw new Error('Chunks cannot be updated');
|
|
46
46
|
const url = headers(source).find(field => field.toLowerCase().startsWith('url:'))?.slice(4).trim() ?? '';
|
|
47
|
-
source = normalize(
|
|
48
|
-
//
|
|
49
|
-
context =
|
|
50
|
-
...context,
|
|
51
|
-
url: url ? new ReadonlyURL(url as ':') : undefined,
|
|
52
|
-
};
|
|
47
|
+
source = normalize(source);
|
|
48
|
+
// @ts-expect-error
|
|
49
|
+
context.url = url ? new ReadonlyURL(url as ':') : undefined;
|
|
53
50
|
const rev = revision = Symbol();
|
|
54
51
|
const sourceSegments: string[] = [];
|
|
55
52
|
for (const seg of segment(source)) {
|
|
@@ -78,7 +75,8 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
78
75
|
for (; index < sourceSegments.length - last; ++index) {
|
|
79
76
|
assert(rev === revision);
|
|
80
77
|
const seg = sourceSegments[index];
|
|
81
|
-
const es = eval(header(input(seg, { header: index === 0 } as MarkdownParser.
|
|
78
|
+
const es = eval(header(input(seg, { header: index === 0 } as MarkdownParser.Options)) || block(input(seg, context)))
|
|
79
|
+
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? [];
|
|
82
80
|
blocks.splice(index, 0, [seg, es, url]);
|
|
83
81
|
if (es.length === 0) continue;
|
|
84
82
|
// All deletion processes always run after all addition processes have done.
|
package/src/parser/api/header.ts
CHANGED
|
@@ -14,7 +14,7 @@ export function headers(source: string): string[] {
|
|
|
14
14
|
function parse(source: string): [HTMLElement, number] | [] {
|
|
15
15
|
const i = input(source, {});
|
|
16
16
|
const result = h(i);
|
|
17
|
-
const
|
|
17
|
+
const el = eval(result)?.head?.value;
|
|
18
18
|
return el?.tagName === 'ASIDE'
|
|
19
19
|
? [el, i.context.position]
|
|
20
20
|
: [];
|
|
@@ -60,12 +60,10 @@ export const invisibleHTMLEntityNames = [
|
|
|
60
60
|
] as const;
|
|
61
61
|
const unreadableHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames.slice(2);
|
|
62
62
|
const unreadableEscapableCharacters = unreadableHTMLEntityNames
|
|
63
|
-
.map(name => eval(unsafehtmlentity(input(`&${name};`, {})))
|
|
63
|
+
.map(name => eval(unsafehtmlentity(input(`&${name};`, {})))!.head!.value);
|
|
64
64
|
assert(unreadableEscapableCharacters.length === unreadableHTMLEntityNames.length);
|
|
65
65
|
assert(unreadableEscapableCharacters.every(c => c.length === 1));
|
|
66
|
-
const unreadableEscapableCharacter = new RegExp(`[${
|
|
67
|
-
[...new Set<string>(unreadableEscapableCharacters)].join('')
|
|
68
|
-
}]`, 'g');
|
|
66
|
+
const unreadableEscapableCharacter = new RegExp(`[${unreadableEscapableCharacters.join('')}]`, 'g');
|
|
69
67
|
assert(!unreadableEscapableCharacter.source.includes('&'));
|
|
70
68
|
|
|
71
69
|
// https://www.pandanoir.info/entry/2018/03/11/193000
|
package/src/parser/api/parse.ts
CHANGED
|
@@ -34,7 +34,9 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
|
|
|
34
34
|
const node = frag();
|
|
35
35
|
let index = 0;
|
|
36
36
|
for (const seg of segment(source)) {
|
|
37
|
-
node.append(
|
|
37
|
+
node.append(
|
|
38
|
+
...eval(header(input(seg, { header: index++ === 0 } as MarkdownParser.Context)) || block(input(seg, context)))
|
|
39
|
+
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? []);
|
|
38
40
|
}
|
|
39
41
|
assert(opts.id !== '' || !node.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
|
|
40
42
|
if (opts.test) return node;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { BlockquoteParser } from '../block';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
3
4
|
import { union, some, creation, recursion, block, validate, rewrite, open, convert, lazy, fmap } from '../../combinator';
|
|
4
5
|
import { autolink } from '../autolink';
|
|
5
6
|
import { contentline } from '../source';
|
|
7
|
+
import { unwrap } from '../util';
|
|
6
8
|
import { parse } from '../api/parse';
|
|
7
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
10
|
|
|
@@ -26,9 +28,9 @@ const source: BlockquoteParser.SourceParser = lazy(() => fmap(
|
|
|
26
28
|
convert(unindent, source, false, true)),
|
|
27
29
|
rewrite(
|
|
28
30
|
some(contentline, opener),
|
|
29
|
-
convert(unindent, fmap(autolink, ns => [html('pre', defrag(ns))]), false, true)),
|
|
31
|
+
convert(unindent, fmap(autolink, ns => new List([new Data(html('pre', defrag(unwrap(ns))))])), false, true)),
|
|
30
32
|
]))),
|
|
31
|
-
ns => [html('blockquote', ns)]));
|
|
33
|
+
ns => new List([new Data(html('blockquote', unwrap(ns)))])));
|
|
32
34
|
|
|
33
35
|
const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
|
|
34
36
|
some(recursion(Recursion.blockquote, union([
|
|
@@ -48,7 +50,7 @@ const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
|
|
|
48
50
|
},
|
|
49
51
|
}, context);
|
|
50
52
|
context.position = source.length;
|
|
51
|
-
return [
|
|
53
|
+
return new List([new Data(html('section', [document, html('h2', 'References'), references]))]);
|
|
52
54
|
}, false, true))),
|
|
53
55
|
]))),
|
|
54
|
-
ns => [html('blockquote', ns)]));
|
|
56
|
+
ns => new List([new Data(html('blockquote', unwrap(ns)))])));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CodeBlockParser } from '../block';
|
|
2
|
-
import {
|
|
2
|
+
import { List, Data, subinput, eval } from '../../combinator/data/parser';
|
|
3
3
|
import { block, fence, clear, fmap } from '../../combinator';
|
|
4
4
|
import { autolink } from '../autolink';
|
|
5
|
-
import { invalid } from '../util';
|
|
5
|
+
import { unwrap, invalid } from '../util';
|
|
6
6
|
import { html, defrag } from 'typed-dom/dom';
|
|
7
7
|
|
|
8
8
|
const opener = /(`{3,})(?!`)([^\n]*)(?:$|\n)/y;
|
|
@@ -17,7 +17,8 @@ export const segment_: CodeBlockParser.SegmentParser = block(
|
|
|
17
17
|
export const codeblock: CodeBlockParser = block(fmap(
|
|
18
18
|
fence(opener, 300),
|
|
19
19
|
// Bug: Type mismatch between outer and inner.
|
|
20
|
-
(
|
|
20
|
+
(nodes, context) => {
|
|
21
|
+
const [body, overflow, closer, opener, delim, param] = unwrap<string>(nodes);
|
|
21
22
|
const params = param.match(/(?:\\.?|\S)+/g)?.reduce<{
|
|
22
23
|
lang?: string;
|
|
23
24
|
path?: string;
|
|
@@ -49,7 +50,7 @@ export const codeblock: CodeBlockParser = block(fmap(
|
|
|
49
50
|
: params[name] = value;
|
|
50
51
|
return params;
|
|
51
52
|
}, {}) ?? {};
|
|
52
|
-
if (!closer || overflow || params.invalid) return [html('pre', {
|
|
53
|
+
if (!closer || overflow || params.invalid) return new List([new Data(html('pre', {
|
|
53
54
|
class: 'invalid',
|
|
54
55
|
translate: 'no',
|
|
55
56
|
...invalid(
|
|
@@ -60,7 +61,7 @@ export const codeblock: CodeBlockParser = block(fmap(
|
|
|
60
61
|
: overflow
|
|
61
62
|
? `Invalid trailing line after the closing delimiter "${delim}"`
|
|
62
63
|
: params.invalid!),
|
|
63
|
-
}, `${opener}${body}${overflow || closer}`)];
|
|
64
|
+
}, `${opener}${body}${overflow || closer}`))]);
|
|
64
65
|
const el = html('pre',
|
|
65
66
|
{
|
|
66
67
|
class: params.lang ? `code language-${params.lang}` : 'text',
|
|
@@ -72,6 +73,6 @@ export const codeblock: CodeBlockParser = block(fmap(
|
|
|
72
73
|
params.lang
|
|
73
74
|
? context.caches?.code?.get(`${params.lang ?? ''}\n${body.slice(0, -1)}`)?.cloneNode(true).childNodes ||
|
|
74
75
|
body.slice(0, -1) || undefined
|
|
75
|
-
: defrag(eval(autolink(
|
|
76
|
-
return [el];
|
|
76
|
+
: defrag(unwrap(eval(autolink(subinput(body.slice(0, -1), context)), new List()))));
|
|
77
|
+
return new List([new Data(el)]);
|
|
77
78
|
}));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DListParser } from '../block';
|
|
2
2
|
import { State } from '../context';
|
|
3
|
+
import { List, Data } from '../../combinator/data/parser';
|
|
3
4
|
import { union, inits, some, state, block, line, validate, rewrite, open, lazy, fmap } from '../../combinator';
|
|
4
5
|
import { inline, indexee, indexer, dataindex } from '../inline';
|
|
5
6
|
import { anyline } from '../source';
|
|
6
7
|
import { visualize, trimBlank, trimBlankEnd } from '../visibility';
|
|
7
|
-
import {
|
|
8
|
+
import { unwrap } from '../util';
|
|
8
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
10
|
|
|
10
11
|
export const dlist: DListParser = lazy(() => block(fmap(validate(
|
|
@@ -14,13 +15,13 @@ export const dlist: DListParser = lazy(() => block(fmap(validate(
|
|
|
14
15
|
some(term)),
|
|
15
16
|
some(desc),
|
|
16
17
|
]))),
|
|
17
|
-
|
|
18
|
+
ns => new List([new Data(html('dl', unwrap(fillTrailingDescription(ns))))]))));
|
|
18
19
|
|
|
19
20
|
const term: DListParser.TermParser = line(indexee(fmap(open(
|
|
20
21
|
/~[^\S\n]+(?=\S)/y,
|
|
21
22
|
visualize(trimBlank(some(union([indexer, inline])))),
|
|
22
23
|
true),
|
|
23
|
-
ns => [html('dt', { 'data-index': dataindex(ns) }, defrag(ns))])));
|
|
24
|
+
ns => new List([new Data(html('dt', { 'data-index': dataindex(ns) }, defrag(unwrap(ns))))]))));
|
|
24
25
|
|
|
25
26
|
const desc: DListParser.DescriptionParser = block(fmap(open(
|
|
26
27
|
/:[^\S\n]+(?=\S)|/y,
|
|
@@ -28,11 +29,11 @@ const desc: DListParser.DescriptionParser = block(fmap(open(
|
|
|
28
29
|
some(anyline, /[~:][^\S\n]+\S/y),
|
|
29
30
|
visualize(trimBlankEnd(some(union([inline]))))),
|
|
30
31
|
true),
|
|
31
|
-
ns => [html('dd', defrag(ns))]),
|
|
32
|
+
ns => new List([new Data(html('dd', defrag(unwrap(ns))))])),
|
|
32
33
|
false);
|
|
33
34
|
|
|
34
|
-
function fillTrailingDescription(
|
|
35
|
-
return
|
|
36
|
-
? push(
|
|
37
|
-
:
|
|
35
|
+
function fillTrailingDescription(nodes: List<Data<HTMLElement>>): List<Data<HTMLElement>> {
|
|
36
|
+
return nodes.last?.value.tagName === 'DT'
|
|
37
|
+
? nodes.push(new Data(html('dd'))) && nodes
|
|
38
|
+
: nodes;
|
|
38
39
|
}
|