securemark 0.294.10 → 0.295.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 +5 -5
- package/dist/index.js +141 -127
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +1 -1
- package/src/combinator/control/constraint/contract.ts +5 -6
- package/src/combinator/control/constraint/line.ts +1 -2
- package/src/combinator/control/manipulation/convert.ts +1 -1
- package/src/combinator/control/manipulation/fallback.ts +1 -1
- package/src/combinator/control/manipulation/indent.ts +2 -2
- package/src/combinator/control/manipulation/lazy.ts +1 -1
- package/src/combinator/control/manipulation/match.ts +2 -5
- package/src/combinator/control/manipulation/recovery.ts +1 -1
- package/src/combinator/control/manipulation/reverse.ts +1 -1
- package/src/combinator/control/manipulation/scope.ts +3 -7
- package/src/combinator/control/manipulation/surround.ts +51 -62
- package/src/combinator/control/monad/bind.ts +7 -12
- package/src/combinator/control/monad/fmap.ts +4 -4
- package/src/combinator/data/parser/context.ts +12 -12
- package/src/combinator/data/parser/inits.ts +3 -6
- package/src/combinator/data/parser/sequence.ts +3 -6
- package/src/combinator/data/parser/some.ts +2 -2
- package/src/combinator/data/parser/subsequence.ts +1 -1
- package/src/combinator/data/parser/tails.ts +1 -1
- package/src/combinator/data/parser/union.ts +1 -1
- package/src/combinator/data/parser.ts +10 -9
- package/src/parser/api/body.test.ts +1 -1
- package/src/parser/api/header.test.ts +2 -2
- package/src/parser/api/normalize.test.ts +2 -0
- package/src/parser/api/normalize.ts +1 -1
- package/src/parser/context.ts +9 -6
- package/src/parser/header.test.ts +2 -2
- package/src/parser/header.ts +3 -3
- package/src/parser/inline/annotation.ts +1 -1
- package/src/parser/inline/autolink/account.ts +3 -3
- package/src/parser/inline/autolink/anchor.ts +1 -1
- package/src/parser/inline/autolink/email.ts +1 -1
- package/src/parser/inline/autolink/hashnum.ts +1 -1
- package/src/parser/inline/autolink/hashtag.ts +1 -1
- package/src/parser/inline/autolink/url.ts +9 -10
- package/src/parser/inline/bracket.ts +31 -19
- package/src/parser/inline/extension/index.ts +3 -3
- package/src/parser/inline/extension/indexee.ts +1 -1
- package/src/parser/inline/extension/label.ts +1 -1
- package/src/parser/inline/extension/placeholder.ts +1 -1
- package/src/parser/inline/htmlentity.ts +1 -1
- package/src/parser/inline/link.ts +12 -14
- package/src/parser/inline/math.ts +2 -2
- package/src/parser/inline/media.ts +15 -17
- package/src/parser/inline/reference.ts +9 -9
- package/src/parser/inline/ruby.ts +4 -4
- package/src/parser/inline/template.ts +6 -10
- package/src/parser/visibility.ts +2 -2
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Parser, List, Data, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
|
|
3
|
-
export function sequence<P extends Parser
|
|
3
|
+
export function sequence<P extends Parser>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
4
4
|
export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, Ctx, D> {
|
|
5
5
|
assert(parsers.every(f => f));
|
|
6
6
|
if (parsers.length === 1) return parsers[0];
|
|
7
7
|
return input => {
|
|
8
8
|
const { context } = input;
|
|
9
|
-
const { source
|
|
9
|
+
const { source } = context;
|
|
10
10
|
let nodes: List<Data<N>> | undefined;
|
|
11
11
|
for (let len = parsers.length, i = 0; i < len; ++i) {
|
|
12
12
|
if (context.position === source.length) return;
|
|
@@ -16,9 +16,6 @@ export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes:
|
|
|
16
16
|
nodes = nodes?.import(result) ?? result;
|
|
17
17
|
if (resume?.(result) === false) return;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
return context.position > position
|
|
21
|
-
? nodes
|
|
22
|
-
: undefined;
|
|
19
|
+
return nodes;
|
|
23
20
|
};
|
|
24
21
|
}
|
|
@@ -3,8 +3,8 @@ import { Delimiters } from './context/delimiter';
|
|
|
3
3
|
|
|
4
4
|
type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
|
|
5
5
|
|
|
6
|
-
export function some<P extends Parser
|
|
7
|
-
export function some<P extends Parser
|
|
6
|
+
export function some<P extends Parser>(parser: P, limit?: number): P;
|
|
7
|
+
export function some<P extends Parser>(parser: P, end?: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
|
|
8
8
|
export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delimiters: readonly DelimiterOption[] = [], limit = -1): Parser<N> {
|
|
9
9
|
if (typeof end === 'number') return some(parser, undefined, delimiters, end);
|
|
10
10
|
assert(parser);
|
|
@@ -2,7 +2,7 @@ import { Parser, List, Data, Ctx, Node, Context, SubParsers, SubNode } from '../
|
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { inits } from './inits';
|
|
4
4
|
|
|
5
|
-
export function subsequence<P extends Parser
|
|
5
|
+
export function subsequence<P extends Parser>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
6
6
|
export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, Ctx, D> {
|
|
7
7
|
assert(parsers.every(f => f));
|
|
8
8
|
return union(
|
|
@@ -2,7 +2,7 @@ import { Parser, List, Data, Ctx, Node, Context, SubParsers, SubNode } from '../
|
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { sequence } from './sequence';
|
|
4
4
|
|
|
5
|
-
export function tails<P extends Parser
|
|
5
|
+
export function tails<P extends Parser>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
6
6
|
export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, Ctx, D> {
|
|
7
7
|
return union(parsers.map((_, i) => sequence(parsers.slice(i), resume)) as D);
|
|
8
8
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
|
|
3
|
-
export function union<P extends Parser
|
|
3
|
+
export function union<P extends Parser>(parsers: SubParsers<P>): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
4
4
|
export function union<N, D extends Parser<N>[]>(parsers: D): Parser<N, Ctx, D> {
|
|
5
5
|
assert(parsers.every(f => f));
|
|
6
6
|
switch (parsers.length) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { List } from './data';
|
|
2
2
|
import { Delimiters } from './parser/context/delimiter';
|
|
3
3
|
|
|
4
|
-
export type Parser<N, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
|
|
4
|
+
export type Parser<N = unknown, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
|
|
5
5
|
= (input: Input<C>) => Result<N, C, D>;
|
|
6
6
|
export interface Input<C extends Ctx = Ctx> {
|
|
7
7
|
readonly context: C;
|
|
@@ -35,6 +35,7 @@ export interface CtxOptions {
|
|
|
35
35
|
// 最大セグメントサイズ10KB内で探索コストが平均実行性能を圧迫するほど大きくなるとは考えにくいが
|
|
36
36
|
// 探索コストを減らすにはバックトラック位置数が規定数を超えた場合一定区間ごとに探索木を分割する方法が考えられる。
|
|
37
37
|
// 10KBの入力すべてを保持する探索木を1024文字ごとに分割するために必要なテーブルサイズは64bit*98=784byteとなる。
|
|
38
|
+
// 128文字ごとでもテーブルサイズは入力全体の1%未満であるため無視でき16文字ごとでさえ6.25%に過ぎない。
|
|
38
39
|
// 探索木のポインタによるオーバーヘッドを考慮すれば一定サイズ以上ではテーブルのほうが効率的となる。
|
|
39
40
|
// 区間別テーブルは固定サイズであるためプールして再使用できる。
|
|
40
41
|
// 従って分割時のデータ構造は区間ごとに探索木を動的に生成しデータ数に応じてテーブルに移行するのが最も効率的である。
|
|
@@ -64,13 +65,13 @@ export interface CtxOptions {
|
|
|
64
65
|
linebreak?: number;
|
|
65
66
|
range?: number;
|
|
66
67
|
}
|
|
67
|
-
export type Node<P extends Parser
|
|
68
|
-
export type SubParsers<P extends Parser
|
|
69
|
-
export type Context<P extends Parser
|
|
70
|
-
export type SubNode<P extends Parser
|
|
71
|
-
export type IntermediateParser<P extends Parser
|
|
72
|
-
type ExtractSubNode<D extends Parser
|
|
73
|
-
type ExtractSubParser<D extends Parser
|
|
68
|
+
export type Node<P extends Parser> = P extends Parser<infer N> ? N : never;
|
|
69
|
+
export type SubParsers<P extends Parser> = P extends Parser<unknown, Ctx, infer D> ? D : never;
|
|
70
|
+
export type Context<P extends Parser> = P extends Parser<unknown, infer C> ? C : never;
|
|
71
|
+
export type SubNode<P extends Parser> = ExtractSubNode<SubParsers<P>>;
|
|
72
|
+
export type IntermediateParser<P extends Parser> = Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
73
|
+
type ExtractSubNode<D extends Parser[]> = ExtractSubParser<D> extends infer N ? N extends Parser<infer U> ? U : never : never;
|
|
74
|
+
type ExtractSubParser<D extends Parser[]> = D extends (infer P)[] ? P extends Parser ? P : never : never;
|
|
74
75
|
|
|
75
76
|
export function input<C extends CtxOptions>(source: string, context: C): Input<C & Ctx> {
|
|
76
77
|
// @ts-expect-error
|
|
@@ -95,7 +96,7 @@ export function subinput<C extends Ctx>(source: string, context: C): Input<C> {
|
|
|
95
96
|
};
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
export function failsafe<P extends Parser
|
|
99
|
+
export function failsafe<P extends Parser>(parser: P): P;
|
|
99
100
|
export function failsafe<N>(parser: Parser<N>): Parser<N> {
|
|
100
101
|
assert(parser);
|
|
101
102
|
return input => {
|
|
@@ -13,7 +13,7 @@ describe('Unit: parser/api/body', () => {
|
|
|
13
13
|
assert.deepStrictEqual(body('---\na: b\n---\n\n'), '');
|
|
14
14
|
assert.deepStrictEqual(body('---\na: b\n---\n\n\n'), '\n');
|
|
15
15
|
assert.deepStrictEqual(body('---\na: b\n---\n\n\na'), '\na');
|
|
16
|
-
assert.deepStrictEqual(body('--- \r\na: b \r\n--- \r\n \r\n \r\na'), ' \r\na');
|
|
16
|
+
//assert.deepStrictEqual(body('--- \r\na: b \r\n--- \r\n \r\n \r\na'), ' \r\na');
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
});
|
|
@@ -13,13 +13,13 @@ describe('Unit: parser/api/header', () => {
|
|
|
13
13
|
assert.deepStrictEqual(headers('---\na: b\n---\nc'), []);
|
|
14
14
|
assert.deepStrictEqual(headers('---\r \na: b\n---'), []);
|
|
15
15
|
assert.deepStrictEqual(headers('---\na:\rb\n---'), []);
|
|
16
|
-
assert.deepStrictEqual(headers('---\na: b\r \n---'), []);
|
|
16
|
+
//assert.deepStrictEqual(headers('---\na: b\r \n---'), []);
|
|
17
17
|
assert.deepStrictEqual(headers('---\n\n---'), []);
|
|
18
18
|
assert.deepStrictEqual(headers('---\n \n---'), []);
|
|
19
19
|
assert.deepStrictEqual(headers('---\n-\n---'), []);
|
|
20
20
|
assert.deepStrictEqual(headers('---\na: b\n---'), ['a: b']);
|
|
21
21
|
assert.deepStrictEqual(headers('---\na: b\nC: D e\n---\n'), ['a: b', 'C: D e']);
|
|
22
|
-
assert.deepStrictEqual(headers('--- \r\na: b \r\n--- \r\n \r\n'), ['a: b']);
|
|
22
|
+
//assert.deepStrictEqual(headers('--- \r\na: b \r\n--- \r\n \r\n'), ['a: b']);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
});
|
|
@@ -6,6 +6,8 @@ describe('Unit: parser/normalize', () => {
|
|
|
6
6
|
assert(normalize('\r') === '\n');
|
|
7
7
|
assert(normalize('\r\n') === '\n');
|
|
8
8
|
assert(normalize('\n\r') === '\n\n');
|
|
9
|
+
assert(normalize('\u2028') === '\n');
|
|
10
|
+
assert(normalize('\u2029') === '\n');
|
|
9
11
|
assert(normalize('\x00') === '\uFFFD');
|
|
10
12
|
assert(normalize('\x01') === '\uFFFD');
|
|
11
13
|
assert(normalize('\x02') === '\uFFFD');
|
package/src/parser/context.ts
CHANGED
|
@@ -28,12 +28,15 @@ export const enum Recursion {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export const enum Backtrack {
|
|
31
|
-
|
|
32
|
-
doublebracket = 1 <<
|
|
33
|
-
link = 1 <<
|
|
34
|
-
ruby = 1 <<
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
// 構文
|
|
32
|
+
doublebracket = 1 << 7,
|
|
33
|
+
link = 1 << 6,
|
|
34
|
+
ruby = 1 << 5,
|
|
35
|
+
// 特殊構造
|
|
36
|
+
escapable = 1 << 4,
|
|
37
|
+
unescapable = 1 << 3,
|
|
38
|
+
// 共通構造
|
|
39
|
+
common = 1 << 2,
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
export const enum Command {
|
|
@@ -17,7 +17,7 @@ describe('Unit: parser/header', () => {
|
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('---\na: b\n---\nc'), ctx), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('---\r \na: b\n---'), ctx), undefined);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('---\na:\rb\n---'), ctx), [['<pre class="invalid" translate="no">---\na:\nb\n---</pre>'], '']);
|
|
20
|
-
assert.deepStrictEqual(inspect(parser('---\na: b\r \n---'), ctx), [['<pre class="invalid" translate="no">---\na: b\n \n---</pre>'], '']);
|
|
20
|
+
//assert.deepStrictEqual(inspect(parser('---\na: b\r \n---'), ctx), [['<pre class="invalid" translate="no">---\na: b\n \n---</pre>'], '']);
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('---\n\n---'), ctx), undefined);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('---\n \n---'), ctx), undefined);
|
|
23
23
|
assert.deepStrictEqual(inspect(parser('---\n-\n---'), ctx), [['<pre class="invalid" translate="no">---\n-\n---</pre>'], '']);
|
|
@@ -30,7 +30,7 @@ describe('Unit: parser/header', () => {
|
|
|
30
30
|
assert.deepStrictEqual(inspect(parser('---\na: b\n---'), ctx), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], '']);
|
|
31
31
|
assert.deepStrictEqual(inspect(parser('---\na: b\n---\n'), ctx), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], '']);
|
|
32
32
|
assert.deepStrictEqual(inspect(parser('---\na: b\nC: D e\n---\n'), ctx), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span><span class="field" data-name="c" data-value="D e"><span class="field-name">C</span>: <span class="field-value">D e</span>\n</span></details></aside>'], '']);
|
|
33
|
-
assert.deepStrictEqual(inspect(parser('--- \r\na: b \r\n--- \r\n \r\n \r\na'), ctx), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], ' \r\na']);
|
|
33
|
+
//assert.deepStrictEqual(inspect(parser('--- \r\na: b \r\n--- \r\n \r\n \r\na'), ctx), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], ' \r\na']);
|
|
34
34
|
assert.deepStrictEqual(inspect(parser('----\na: b\n----'), ctx), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], '']);
|
|
35
35
|
});
|
|
36
36
|
|
package/src/parser/header.ts
CHANGED
|
@@ -7,12 +7,12 @@ import { normalize } from './api/normalize';
|
|
|
7
7
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
9
|
export const header: MarkdownParser.HeaderParser = lazy(() => validate(
|
|
10
|
-
/---+ *\
|
|
10
|
+
/---+ *\n(?=\S)/y,
|
|
11
11
|
inits([
|
|
12
12
|
block(
|
|
13
13
|
union([
|
|
14
14
|
validate(({ context }) => context.header ?? true,
|
|
15
|
-
focus(/(---+) *\
|
|
15
|
+
focus(/(---+) *\n(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*:[ \t]+\S[^\n]*\n){1,100}\1 *(?:$|\n)/y,
|
|
16
16
|
convert(source =>
|
|
17
17
|
normalize(source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n'))),
|
|
18
18
|
fmap(
|
|
@@ -36,7 +36,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
|
|
|
36
36
|
]);
|
|
37
37
|
},
|
|
38
38
|
])),
|
|
39
|
-
clear(str(/ *\
|
|
39
|
+
clear(str(/ *\n/y)),
|
|
40
40
|
])));
|
|
41
41
|
|
|
42
42
|
const field: MarkdownParser.HeaderParser.FieldParser = line(({ context: { source, position } }) => {
|
|
@@ -13,7 +13,7 @@ export const annotation: AnnotationParser = lazy(() => constraint(State.annotati
|
|
|
13
13
|
trimBlankStart(some(union([inline]), ')', [[')', 1]])))),
|
|
14
14
|
'))',
|
|
15
15
|
false,
|
|
16
|
-
[1 | Backtrack.
|
|
16
|
+
[1 | Backtrack.common, 3 | Backtrack.doublebracket],
|
|
17
17
|
([, ns], context) =>
|
|
18
18
|
context.linebreak === 0
|
|
19
19
|
? new List([new Data(html('sup', { class: 'annotation' }, [html('span', defrag(unwrap(trimBlankNodeEnd(ns))))]))])
|
|
@@ -17,7 +17,7 @@ export const account: AutolinkParser.AccountParser = lazy(() => constraint(State
|
|
|
17
17
|
str(/[0-9a-z](?:[.-](?=[0-9a-z])|[0-9a-z]){0,254}\/|/yi),
|
|
18
18
|
str(/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@]|>>|:\S)/yi),
|
|
19
19
|
false,
|
|
20
|
-
[3 | Backtrack.
|
|
20
|
+
[3 | Backtrack.unescapable]),
|
|
21
21
|
some(surround(
|
|
22
22
|
'#',
|
|
23
23
|
verify(
|
|
@@ -29,7 +29,7 @@ export const account: AutolinkParser.AccountParser = lazy(() => constraint(State
|
|
|
29
29
|
/(?![0-9a-z@]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
|
|
30
30
|
].join('|').replace(/emoji/g, emoji.source), 'yu'),
|
|
31
31
|
false,
|
|
32
|
-
[3 | Backtrack.
|
|
32
|
+
[3 | Backtrack.unescapable])),
|
|
33
33
|
'',
|
|
34
34
|
false, [],
|
|
35
35
|
([[{ value: host }, { value: account }], nodes], context) => {
|
|
@@ -47,7 +47,7 @@ export const account: AutolinkParser.AccountParser = lazy(() => constraint(State
|
|
|
47
47
|
([[{ value: host }, { value: account }]], context) => {
|
|
48
48
|
if (context.source[context.position] === '#') {
|
|
49
49
|
assert(context.source[context.position - context.range!] === '@');
|
|
50
|
-
return void setBacktrack(context,
|
|
50
|
+
return void setBacktrack(context, 2 | Backtrack.unescapable, context.position - context.range!);
|
|
51
51
|
}
|
|
52
52
|
return new List([
|
|
53
53
|
new Data(define(
|
|
@@ -22,7 +22,7 @@ export const anchor: AutolinkParser.AnchorParser = lazy(() => constraint(State.a
|
|
|
22
22
|
str(/[0-9a-z]+(?:-[0-9a-z]+)*(?!-?[0-9a-z@#]|>>|:\S)/yi),
|
|
23
23
|
'',
|
|
24
24
|
false,
|
|
25
|
-
[3 | Backtrack.
|
|
25
|
+
[3 | Backtrack.unescapable],
|
|
26
26
|
([, [{ value }]], context) =>
|
|
27
27
|
new List([
|
|
28
28
|
new Data(define(parse(
|
|
@@ -15,6 +15,6 @@ export const email: AutolinkParser.EmailParser = constraint(State.autolink, stat
|
|
|
15
15
|
([{ value }]) => value.length <= 254),
|
|
16
16
|
'',
|
|
17
17
|
false,
|
|
18
|
-
[3 | Backtrack.
|
|
18
|
+
[3 | Backtrack.unescapable],
|
|
19
19
|
([, [{ value }]]) =>
|
|
20
20
|
new List([new Data(html('a', { class: 'email', href: `mailto:${value}` }, value))]))));
|
|
@@ -17,7 +17,7 @@ export const hashnum: AutolinkParser.HashnumParser = lazy(() => constraint(State
|
|
|
17
17
|
].join('|').replace(/emoji/g, emoji.source), 'yu')),
|
|
18
18
|
'',
|
|
19
19
|
false,
|
|
20
|
-
[1 | Backtrack.
|
|
20
|
+
[1 | Backtrack.unescapable],
|
|
21
21
|
([, [{ value }]], context) =>
|
|
22
22
|
new List([
|
|
23
23
|
new Data(define(parse(
|
|
@@ -25,7 +25,7 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => constraint(State
|
|
|
25
25
|
/(?![0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
|
|
26
26
|
].join('|').replace(/emoji/g, emoji.source), 'yu'),
|
|
27
27
|
false,
|
|
28
|
-
[3 | Backtrack.
|
|
28
|
+
[3 | Backtrack.unescapable],
|
|
29
29
|
([, [{ value }]], context) =>
|
|
30
30
|
new List([
|
|
31
31
|
new Data(define(parse(
|
|
@@ -14,7 +14,7 @@ export const url: AutolinkParser.UrlParser = lazy(() => rewrite(
|
|
|
14
14
|
precedence(1, verify(bracket, ns => ns.length > 0)),
|
|
15
15
|
]), undefined, [[/[^\x21-\x7E]|\$/y, 9]])),
|
|
16
16
|
false,
|
|
17
|
-
[3 | Backtrack.
|
|
17
|
+
[3 | Backtrack.unescapable]),
|
|
18
18
|
union([
|
|
19
19
|
constraint(State.autolink, state(State.autolink, ({ context }) =>
|
|
20
20
|
new List([new Data(parse(new List(), new List([new Data(context.source)]), context))]))),
|
|
@@ -27,27 +27,26 @@ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => focus(
|
|
|
27
27
|
str('!'),
|
|
28
28
|
union([
|
|
29
29
|
constraint(State.autolink, state(State.autolink, ({ context }) => {
|
|
30
|
-
const { source, position
|
|
31
|
-
context.position -=
|
|
32
|
-
context.position += range;
|
|
30
|
+
const { source, position } = context;
|
|
31
|
+
context.position -= source[0] === '!' ? 1 : 0;
|
|
33
32
|
return new List([
|
|
34
33
|
new Data(parse(
|
|
35
34
|
new List(),
|
|
36
|
-
new List([new Data(source.slice(position
|
|
35
|
+
new List([new Data(source.slice(position))]),
|
|
37
36
|
context))
|
|
38
37
|
]);
|
|
39
38
|
})),
|
|
40
39
|
str(/[^:]+/y),
|
|
41
40
|
]),
|
|
42
|
-
])
|
|
41
|
+
])));
|
|
43
42
|
|
|
44
43
|
const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => union([
|
|
45
44
|
surround(str('('), recursion(Recursion.terminal, some(union([bracket, unescsource]), ')')), str(')'),
|
|
46
|
-
true, [3 | Backtrack.
|
|
45
|
+
true, [3 | Backtrack.unescapable], undefined, () => new List()),
|
|
47
46
|
surround(str('['), recursion(Recursion.terminal, some(union([bracket, unescsource]), ']')), str(']'),
|
|
48
|
-
true, [3 | Backtrack.
|
|
47
|
+
true, [3 | Backtrack.unescapable], undefined, () => new List()),
|
|
49
48
|
surround(str('{'), recursion(Recursion.terminal, some(union([bracket, unescsource]), '}')), str('}'),
|
|
50
|
-
true, [3 | Backtrack.
|
|
49
|
+
true, [3 | Backtrack.unescapable], undefined, () => new List()),
|
|
51
50
|
surround(str('"'), precedence(2, recursion(Recursion.terminal, some(unescsource, '"'))), str('"'),
|
|
52
|
-
true, [3 | Backtrack.
|
|
51
|
+
true, [3 | Backtrack.unescapable], undefined, () => new List()),
|
|
53
52
|
]));
|
|
@@ -38,22 +38,32 @@ const p1 = lazy(() => surround(
|
|
|
38
38
|
str('('),
|
|
39
39
|
precedence(1, recursion(Recursion.bracket, some(inline, ')', [[')', 1]]))),
|
|
40
40
|
str(')'),
|
|
41
|
-
true,
|
|
42
|
-
[
|
|
43
|
-
|
|
41
|
+
true, [],
|
|
42
|
+
([as, bs = new List(), cs], context) => {
|
|
43
|
+
const { source, position, range = 0 } = context;
|
|
44
|
+
const head = position - range;
|
|
45
|
+
if (context.linebreak !== 0 || source[position - 2] !== ')' || source[head + 1] !== '(') {
|
|
46
|
+
setBacktrack(context, 2 | Backtrack.doublebracket, head);
|
|
47
|
+
}
|
|
44
48
|
const str = source.slice(position - range + 1, position - 1);
|
|
45
49
|
return indexA.test(str)
|
|
46
50
|
? new List([new Data(as.head!.value), new Data(str), new Data(cs.head!.value)])
|
|
47
51
|
: new List([new Data(html('span', { class: 'paren' }, defrag(unwrap(as.import(bs as List<Data<string>>).import(cs)))))]);
|
|
48
52
|
},
|
|
49
|
-
([as, bs = new List()]) =>
|
|
53
|
+
([as, bs = new List()], context) => {
|
|
54
|
+
const { source, position, range = 0 } = context;
|
|
55
|
+
const head = position - range;
|
|
56
|
+
if (context.linebreak !== 0 || source[head + 1] !== '(') {
|
|
57
|
+
setBacktrack(context, 2 | Backtrack.doublebracket, head);
|
|
58
|
+
}
|
|
59
|
+
return as.import(bs as List<Data<string>>);
|
|
60
|
+
}));
|
|
50
61
|
|
|
51
62
|
const p2 = lazy(() => surround(
|
|
52
63
|
str('('),
|
|
53
64
|
precedence(1, recursion(Recursion.bracket, some(inline, ')', [[')', 1]]))),
|
|
54
65
|
str(')'),
|
|
55
|
-
true,
|
|
56
|
-
[2 | Backtrack.bracket],
|
|
66
|
+
true, [],
|
|
57
67
|
([as, bs = [], cs], { source, position, range = 0 }) => {
|
|
58
68
|
const str = source.slice(position - range + 1, position - 1);
|
|
59
69
|
return indexF.test(str)
|
|
@@ -67,22 +77,28 @@ const s1 = lazy(() => surround(
|
|
|
67
77
|
precedence(1, recursion(Recursion.bracket, some(inline, ']', [[']', 1]]))),
|
|
68
78
|
str(']'),
|
|
69
79
|
true,
|
|
70
|
-
[2 | Backtrack.
|
|
80
|
+
[2 | Backtrack.common],
|
|
71
81
|
([as, bs = new List(), cs], context) => {
|
|
72
82
|
if (context.state! & State.link) {
|
|
73
83
|
const { source, position, range = 0 } = context;
|
|
74
84
|
const head = position - range;
|
|
75
|
-
if (context.linebreak !== 0 || source[position] !== '
|
|
76
|
-
setBacktrack(context,
|
|
85
|
+
if (context.linebreak !== 0 || source[position - 2] !== ']' || source[head + 1] !== '[') {
|
|
86
|
+
setBacktrack(context, 2 | Backtrack.doublebracket, head);
|
|
87
|
+
}
|
|
88
|
+
if (context.linebreak !== 0) {
|
|
89
|
+
setBacktrack(context, 2 | Backtrack.doublebracket | Backtrack.link | Backtrack.ruby, head);
|
|
90
|
+
}
|
|
91
|
+
else if (source[position] !== '{') {
|
|
92
|
+
setBacktrack(context, 2 | Backtrack.link, head);
|
|
77
93
|
}
|
|
78
94
|
else {
|
|
79
95
|
context.state! ^= State.link;
|
|
80
|
-
const result = !isBacktrack(context,
|
|
96
|
+
const result = !isBacktrack(context, 1 | Backtrack.link)
|
|
81
97
|
? textlink({ context })
|
|
82
98
|
: undefined;
|
|
83
99
|
context.position = position;
|
|
84
100
|
if (!result) {
|
|
85
|
-
setBacktrack(context,
|
|
101
|
+
setBacktrack(context, 2 | Backtrack.link, head);
|
|
86
102
|
}
|
|
87
103
|
context.state! ^= State.link;
|
|
88
104
|
context.range = range;
|
|
@@ -96,8 +112,7 @@ const s2 = lazy(() => surround(
|
|
|
96
112
|
str('['),
|
|
97
113
|
precedence(1, recursion(Recursion.bracket, some(inline, ']', [[']', 1]]))),
|
|
98
114
|
str(']'),
|
|
99
|
-
true,
|
|
100
|
-
[2 | Backtrack.bracket],
|
|
115
|
+
true, [],
|
|
101
116
|
undefined,
|
|
102
117
|
([as, bs = new List()]) => as.import(bs as List<Data<string>>)));
|
|
103
118
|
|
|
@@ -105,8 +120,7 @@ const c1 = lazy(() => surround(
|
|
|
105
120
|
str('{'),
|
|
106
121
|
precedence(1, recursion(Recursion.bracket, some(inline, '}', [['}', 1]]))),
|
|
107
122
|
str('}'),
|
|
108
|
-
true,
|
|
109
|
-
[2 | Backtrack.bracket],
|
|
123
|
+
true, [],
|
|
110
124
|
undefined,
|
|
111
125
|
([as, bs = new List()]) => as.import(bs as List<Data<string>>)));
|
|
112
126
|
|
|
@@ -114,8 +128,7 @@ const c2 = lazy(() => surround(
|
|
|
114
128
|
str('{'),
|
|
115
129
|
precedence(1, recursion(Recursion.bracket, some(inline, '}', [['}', 1]]))),
|
|
116
130
|
str('}'),
|
|
117
|
-
true,
|
|
118
|
-
[2 | Backtrack.bracket],
|
|
131
|
+
true, [],
|
|
119
132
|
undefined,
|
|
120
133
|
([as, bs = new List()]) => as.import(bs as List<Data<string>>)));
|
|
121
134
|
|
|
@@ -124,7 +137,6 @@ const d1 = lazy(() => surround(
|
|
|
124
137
|
// 改行の優先度を構文ごとに変える場合シグネチャの優先度対応が必要
|
|
125
138
|
precedence(2, recursion(Recursion.bracket, some(inline, /["\n]/y, [['"', 2], ['\n', 3]]))),
|
|
126
139
|
str('"'),
|
|
127
|
-
true,
|
|
128
|
-
[2 | Backtrack.bracket],
|
|
140
|
+
true, [],
|
|
129
141
|
undefined,
|
|
130
142
|
([as, bs = new List()]) => as.import(bs as List<Data<string>>)));
|
|
@@ -22,7 +22,7 @@ export const index: IndexParser = lazy(() => constraint(State.index, fmap(indexe
|
|
|
22
22
|
]), ']', [[']', 1]])))),
|
|
23
23
|
str(']'),
|
|
24
24
|
false,
|
|
25
|
-
[3 | Backtrack.
|
|
25
|
+
[3 | Backtrack.common],
|
|
26
26
|
([, bs], context) =>
|
|
27
27
|
context.linebreak === 0 && trimBlankNodeEnd(bs).length > 0
|
|
28
28
|
? new List([new Data(html('a', { 'data-index': dataindex(bs) }, defrag(unwrap(bs))))])
|
|
@@ -50,11 +50,11 @@ export const signature: IndexParser.SignatureParser = lazy(() => validate('|', s
|
|
|
50
50
|
str(/\|(?!\\?\s)/y),
|
|
51
51
|
some(union([
|
|
52
52
|
unsafehtmlentity,
|
|
53
|
-
some(txt, /(?:[$"`\[\](){}
|
|
53
|
+
some(txt, /(?:[$"`\[\](){}<>()[]{}|])/y),
|
|
54
54
|
]), ']'),
|
|
55
55
|
/(?=])/y,
|
|
56
56
|
false,
|
|
57
|
-
[3 | Backtrack.
|
|
57
|
+
[3 | Backtrack.escapable],
|
|
58
58
|
([, ns], context) => {
|
|
59
59
|
const index = identity('index', undefined, ns.foldl((acc, { value }) => acc + value, ''))?.slice(7);
|
|
60
60
|
return index && context.linebreak === 0
|
|
@@ -3,7 +3,7 @@ import { Parser, List, Data } from '../../../combinator/data/parser';
|
|
|
3
3
|
import { fmap } from '../../../combinator';
|
|
4
4
|
import { define } from 'typed-dom/dom';
|
|
5
5
|
|
|
6
|
-
export function indexee<P extends Parser<
|
|
6
|
+
export function indexee<P extends Parser<HTMLElement, MarkdownParser.Context>>(parser: P): P;
|
|
7
7
|
export function indexee(parser: Parser<HTMLElement, MarkdownParser.Context>): Parser<HTMLElement> {
|
|
8
8
|
return fmap(parser, (ns, { id }) =>
|
|
9
9
|
ns.length === 1
|
|
@@ -14,7 +14,7 @@ export const segment: ExtensionParser.LabelParser.SegmentParser = clear(union([
|
|
|
14
14
|
|
|
15
15
|
export const label: ExtensionParser.LabelParser = constraint(State.label, fmap(
|
|
16
16
|
union([
|
|
17
|
-
surround('[', body, ']', false, [1 | Backtrack.
|
|
17
|
+
surround('[', body, ']', false, [1 | Backtrack.common]),
|
|
18
18
|
body,
|
|
19
19
|
]),
|
|
20
20
|
([{ value }]) => new List([
|
|
@@ -19,7 +19,7 @@ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => surroun
|
|
|
19
19
|
tightStart(some(union([inline]), ']', [[']', 1]])))),
|
|
20
20
|
str(']'),
|
|
21
21
|
false,
|
|
22
|
-
[3 | Backtrack.
|
|
22
|
+
[3 | Backtrack.common],
|
|
23
23
|
(_, context) => new List([
|
|
24
24
|
new Data(html('span',
|
|
25
25
|
{
|
|
@@ -9,7 +9,7 @@ import { html } from 'typed-dom/dom';
|
|
|
9
9
|
export const unsafehtmlentity: UnsafeHTMLEntityParser = surround(
|
|
10
10
|
str('&'), str(/[0-9A-Za-z]+/y), str(';'),
|
|
11
11
|
false,
|
|
12
|
-
[3 | Backtrack.
|
|
12
|
+
[3 | Backtrack.unescapable],
|
|
13
13
|
([as, bs, cs]) =>
|
|
14
14
|
new List([new Data(parser(as.head!.value + bs.head!.value + cs.head!.value))]),
|
|
15
15
|
([as, bs]) =>
|
|
@@ -24,33 +24,31 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
24
24
|
trimBlankStart(some(union([inline]), ']', [[']', 1]])),
|
|
25
25
|
']',
|
|
26
26
|
true,
|
|
27
|
-
[3 | Backtrack.
|
|
28
|
-
([, ns = new List()], context) =>
|
|
29
|
-
context.linebreak
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
[3 | Backtrack.common | Backtrack.link, 2 | Backtrack.ruby],
|
|
28
|
+
([, ns = new List()], context) => {
|
|
29
|
+
if (context.linebreak !== 0) {
|
|
30
|
+
const head = context.position - context.range!;
|
|
31
|
+
return void setBacktrack(context, 2 | Backtrack.link | Backtrack.ruby, head);
|
|
32
|
+
}
|
|
33
|
+
return ns.push(new Data(Command.Separator)) && ns;
|
|
34
|
+
})),
|
|
32
35
|
// `{ `と`{`で個別にバックトラックが発生し+1nされる。
|
|
33
36
|
// 自己再帰的にパースしてもオプションの不要なパースによる計算量の増加により相殺される。
|
|
34
37
|
dup(surround(
|
|
35
38
|
/{(?![{}])/y,
|
|
36
39
|
inits([uri, some(option)]),
|
|
37
40
|
/ ?}/y,
|
|
38
|
-
false,
|
|
39
|
-
[3 | Backtrack.link],
|
|
41
|
+
false, [],
|
|
40
42
|
undefined,
|
|
41
|
-
([as, bs]
|
|
42
|
-
|
|
43
|
-
const head = context.position - context.range!;
|
|
44
|
-
setBacktrack(context, [2 | Backtrack.link], head);
|
|
45
|
-
return as.import(bs).push(new Data(Command.Cancel)) && as;
|
|
46
|
-
})),
|
|
43
|
+
([as, bs]) =>
|
|
44
|
+
bs && as.import(bs).push(new Data(Command.Cancel)) && as)),
|
|
47
45
|
]),
|
|
48
46
|
([{ value: content }, { value: params = undefined } = {}], context) => {
|
|
49
47
|
if (content.last!.value === Command.Separator) {
|
|
50
48
|
content.pop();
|
|
51
49
|
if (params === undefined) {
|
|
52
50
|
const head = context.position - context.range!;
|
|
53
|
-
return void setBacktrack(context,
|
|
51
|
+
return void setBacktrack(context, 2 | Backtrack.link, head);
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
else {
|
|
@@ -15,7 +15,7 @@ export const math: MathParser = lazy(() => rewrite(
|
|
|
15
15
|
precedence(4, bracket),
|
|
16
16
|
'$',
|
|
17
17
|
false,
|
|
18
|
-
[3 | Backtrack.
|
|
18
|
+
[3 | Backtrack.escapable]),
|
|
19
19
|
surround(
|
|
20
20
|
/\$(?![\s{}])/y,
|
|
21
21
|
precedence(2, some(union([
|
|
@@ -24,7 +24,7 @@ export const math: MathParser = lazy(() => rewrite(
|
|
|
24
24
|
]))),
|
|
25
25
|
/\$(?![-0-9A-Za-z])/y,
|
|
26
26
|
false,
|
|
27
|
-
[3 | Backtrack.
|
|
27
|
+
[3 | Backtrack.escapable]),
|
|
28
28
|
]),
|
|
29
29
|
({ context: { source, caches: { math: cache } = {} } }) => new List([
|
|
30
30
|
new Data(cache?.get(source)?.cloneNode(true) ||
|