securemark 0.295.4 → 0.295.6
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 +83 -73
- package/markdown.d.ts +5 -10
- package/package.json +1 -1
- package/src/combinator/data/delimiter.ts +5 -2
- package/src/combinator/data/parser/some.ts +20 -11
- package/src/parser/api/bind.test.ts +2 -2
- package/src/parser/api/parse.test.ts +20 -10
- package/src/parser/block/blockquote.ts +2 -2
- package/src/parser/block/ilist.ts +1 -1
- package/src/parser/block/olist.ts +1 -1
- package/src/parser/block/sidefence.ts +1 -1
- package/src/parser/block/ulist.ts +1 -1
- package/src/parser/inline/autolink/url.ts +1 -1
- package/src/parser/inline/bracket.ts +0 -2
- package/src/parser/inline/deletion.ts +3 -4
- package/src/parser/inline/emphasis.ts +3 -4
- package/src/parser/inline/emstrong.ts +7 -13
- package/src/parser/inline/extension/index.ts +11 -17
- package/src/parser/inline/insertion.ts +3 -4
- package/src/parser/inline/italic.ts +5 -9
- package/src/parser/inline/link.test.ts +1 -1
- package/src/parser/inline/link.ts +8 -7
- package/src/parser/inline/mark.test.ts +6 -6
- package/src/parser/inline/mark.ts +8 -11
- package/src/parser/inline/ruby.ts +2 -2
- package/src/parser/inline/strong.ts +3 -4
- package/src/parser/inline.test.ts +4 -1
- package/src/parser/segment.test.ts +2 -2
- package/src/parser/segment.ts +2 -2
- package/src/parser/source/escapable.ts +2 -2
- package/src/parser/source/text.ts +13 -9
- package/src/parser/source/unescapable.ts +2 -2
- package/src/parser/visibility.ts +18 -14
|
@@ -4,12 +4,25 @@ import { Delimiters } from '../delimiter';
|
|
|
4
4
|
type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
|
|
5
5
|
|
|
6
6
|
export function some<P extends Parser>(parser: P, limit?: number): P;
|
|
7
|
-
export function some<P extends Parser>(parser: P,
|
|
8
|
-
export function some<
|
|
9
|
-
|
|
7
|
+
export function some<P extends Parser>(parser: P, delimiters?: readonly DelimiterOption[]): P;
|
|
8
|
+
export function some<P extends Parser>(parser: P, delimiter: string | RegExp, delimiters?: readonly DelimiterOption[]): P;
|
|
9
|
+
export function some<P extends Parser>(parser: P, delimiter: string | RegExp, after: string | RegExp, delimiters?: readonly DelimiterOption[]): P;
|
|
10
|
+
export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp | readonly DelimiterOption[], after?: string | RegExp | readonly DelimiterOption[], delimiters?: readonly DelimiterOption[], limit = -1): Parser<N> {
|
|
11
|
+
if (typeof delimiter === 'number') {
|
|
12
|
+
limit = delimiter;
|
|
13
|
+
delimiter = undefined;
|
|
14
|
+
}
|
|
15
|
+
else if (Array.isArray(delimiter)) {
|
|
16
|
+
delimiters = delimiter;
|
|
17
|
+
delimiter = undefined;
|
|
18
|
+
}
|
|
19
|
+
else if (after === undefined || Array.isArray(after)) {
|
|
20
|
+
delimiters = after;
|
|
21
|
+
after = undefined;
|
|
22
|
+
}
|
|
10
23
|
assert(parser);
|
|
11
|
-
const match = Delimiters.matcher(
|
|
12
|
-
const delims = delimiters
|
|
24
|
+
const match = Delimiters.matcher(delimiter as string, after as string);
|
|
25
|
+
const delims = delimiters?.map(([delimiter, precedence]) => ({
|
|
13
26
|
signature: Delimiters.signature(delimiter),
|
|
14
27
|
matcher: Delimiters.matcher(delimiter),
|
|
15
28
|
precedence,
|
|
@@ -19,9 +32,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
19
32
|
const { source, position } = context;
|
|
20
33
|
//assert(context.backtracks ??= {});
|
|
21
34
|
let nodes: List<Node<N>> | undefined;
|
|
22
|
-
|
|
23
|
-
context.delimiters.push(delims);
|
|
24
|
-
}
|
|
35
|
+
delims && context.delimiters.push(delims);
|
|
25
36
|
// whileは数倍遅い
|
|
26
37
|
for (const len = source.length; context.position < len;) {
|
|
27
38
|
if (match(input)) break;
|
|
@@ -31,9 +42,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
31
42
|
nodes = nodes?.import(result) ?? result;
|
|
32
43
|
if (limit >= 0 && context.position - position > limit) break;
|
|
33
44
|
}
|
|
34
|
-
|
|
35
|
-
context.delimiters.pop(delims.length);
|
|
36
|
-
}
|
|
45
|
+
delims && context.delimiters.pop(delims.length);
|
|
37
46
|
assert(context.position >= position);
|
|
38
47
|
return context.position > position
|
|
39
48
|
? nodes
|
|
@@ -30,7 +30,7 @@ describe('Unit: parser/api/bind', () => {
|
|
|
30
30
|
const cfgs = { notes: { references: html('ol') } };
|
|
31
31
|
|
|
32
32
|
it('huge input', () => {
|
|
33
|
-
const iter = bind(html('div'), { ...cfgs, id: '' }).parse(`${'\n'.repeat(
|
|
33
|
+
const iter = bind(html('div'), { ...cfgs, id: '' }).parse(`${'\n'.repeat(1e6 + 1)}`);
|
|
34
34
|
assert.deepStrictEqual(
|
|
35
35
|
inspect(iter),
|
|
36
36
|
[
|
|
@@ -42,7 +42,7 @@ describe('Unit: parser/api/bind', () => {
|
|
|
42
42
|
it('huge segment', function () {
|
|
43
43
|
this.timeout(10 * 1000);
|
|
44
44
|
|
|
45
|
-
const iter = bind(html('div'), { ...cfgs, id: '' }).parse(`${'\n'.repeat(
|
|
45
|
+
const iter = bind(html('div'), { ...cfgs, id: '' }).parse(`${'\n'.repeat(1e5 + 1)}`);
|
|
46
46
|
assert.deepStrictEqual(
|
|
47
47
|
inspect(iter, 3),
|
|
48
48
|
[
|
|
@@ -6,7 +6,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
6
6
|
describe('parse', () => {
|
|
7
7
|
it('huge input', () => {
|
|
8
8
|
assert.deepStrictEqual(
|
|
9
|
-
[...parse(`${'\n'.repeat(
|
|
9
|
+
[...parse(`${'\n'.repeat(1e6 + 1)}`, { id: '' }).children].map(el => el.outerHTML),
|
|
10
10
|
[
|
|
11
11
|
'<h1 class="error">Error: Too large input over 1,000,000 bytes.</h1>',
|
|
12
12
|
`<pre class="error" translate="no">${'\n'.repeat(997)}...</pre>`,
|
|
@@ -15,7 +15,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
15
15
|
|
|
16
16
|
it('huge segment', () => {
|
|
17
17
|
assert.deepStrictEqual(
|
|
18
|
-
[...parse(`${'\n'.repeat(
|
|
18
|
+
[...parse(`${'\n'.repeat(1e5 + 1)}`, { id: '' }).children].map(el => el.outerHTML),
|
|
19
19
|
[
|
|
20
20
|
'<h1 class="error">Error: Too large segment over 100,000 bytes.</h1>',
|
|
21
21
|
`<pre class="error" translate="no">${'\n'.repeat(997)}...</pre>`,
|
|
@@ -360,22 +360,32 @@ describe('Unit: parser/api/parse', () => {
|
|
|
360
360
|
]);
|
|
361
361
|
});
|
|
362
362
|
|
|
363
|
-
it('backtrack',
|
|
364
|
-
this.timeout(5000);
|
|
363
|
+
it('backtrack 1', () => {
|
|
365
364
|
// 最悪計算量での実行速度はCommonMarkの公式JS実装の32nに対して50-400%程度。
|
|
366
365
|
// 6n = annotation + reference + link + url/math + ruby + text
|
|
367
|
-
const source = `((([[[[#$[${'.'.repeat(16664)}]]]`;
|
|
368
366
|
assert.deepStrictEqual(
|
|
369
|
-
[...parse(
|
|
367
|
+
[...parse(`((([[[[#$[${'.'.repeat(16665)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
368
|
+
.map(el => el.tagName),
|
|
369
|
+
['P']);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('backtrack 1 error', () => {
|
|
373
|
+
assert.deepStrictEqual(
|
|
374
|
+
[...parse(`((([[[[#$[${'.'.repeat(16665 + 1)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
375
|
+
.map(el => el.tagName),
|
|
376
|
+
['H1', 'PRE']);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('backtrack 2', () => {
|
|
380
|
+
assert.deepStrictEqual(
|
|
381
|
+
[...parse(`((([[[[#$[${'.'.repeat(16664)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
370
382
|
.map(el => el.tagName),
|
|
371
383
|
['P', 'OL']);
|
|
372
384
|
});
|
|
373
385
|
|
|
374
|
-
it('backtrack error',
|
|
375
|
-
this.timeout(5000);
|
|
376
|
-
const source = `((([[[[#$[${'.'.repeat(16664 + 1)}]]]`;
|
|
386
|
+
it('backtrack 2 error', () => {
|
|
377
387
|
assert.deepStrictEqual(
|
|
378
|
-
[...parse(
|
|
388
|
+
[...parse(`((([[[[#$[${'.'.repeat(16664 + 1)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
|
|
379
389
|
.map(el => el.tagName),
|
|
380
390
|
['H1', 'PRE']);
|
|
381
391
|
});
|
|
@@ -22,7 +22,7 @@ const indent = block(open(opener, some(contentline, />(?:$|[ \n])/y)), false);
|
|
|
22
22
|
const unindent = (source: string) => source.replace(/(?<=^|\n)>(?: |(?=>*(?:$|[ \n])))|\n$/g, '');
|
|
23
23
|
|
|
24
24
|
const source: BlockquoteParser.SourceParser = lazy(() => fmap(
|
|
25
|
-
|
|
25
|
+
recursion(Recursion.blockquote, some(union([
|
|
26
26
|
rewrite(
|
|
27
27
|
indent,
|
|
28
28
|
convert(unindent, source, true)),
|
|
@@ -33,7 +33,7 @@ const source: BlockquoteParser.SourceParser = lazy(() => fmap(
|
|
|
33
33
|
ns => new List([new Node(html('blockquote', unwrap(ns)))])));
|
|
34
34
|
|
|
35
35
|
const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
|
|
36
|
-
|
|
36
|
+
recursion(Recursion.blockquote, some(union([
|
|
37
37
|
rewrite(
|
|
38
38
|
indent,
|
|
39
39
|
convert(unindent, markdown, true)),
|
|
@@ -17,7 +17,7 @@ export const ilist: IListParser = lazy(() => block(validate(
|
|
|
17
17
|
|
|
18
18
|
export const ilist_: IListParser = lazy(() => block(fmap(validate(
|
|
19
19
|
/[-+*](?:$|[ \n])/y,
|
|
20
|
-
|
|
20
|
+
recursion(Recursion.listitem, some(union([
|
|
21
21
|
fmap(fallback(
|
|
22
22
|
inits([
|
|
23
23
|
line(open(/[-+*](?:$|[ \n])/y, visualize(trimBlank(some(inline))), true)),
|
|
@@ -32,7 +32,7 @@ export const olist_: OListParser = lazy(() => block(union([
|
|
|
32
32
|
])));
|
|
33
33
|
|
|
34
34
|
const list = (type: string, form: string): OListParser.ListParser => fmap(
|
|
35
|
-
|
|
35
|
+
recursion(Recursion.listitem, some(union([
|
|
36
36
|
indexee(fmap(fallback(
|
|
37
37
|
inits([
|
|
38
38
|
line(open(heads[form], subsequence([
|
|
@@ -21,7 +21,7 @@ const opener = /(?=\|\|+(?:$|[ \n]))/y;
|
|
|
21
21
|
const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?: |(?=\|*(?:$|[ \n])))|\n$/g, '');
|
|
22
22
|
|
|
23
23
|
const source: SidefenceParser.SourceParser = lazy(() => fmap(
|
|
24
|
-
|
|
24
|
+
recursion(Recursion.block, some(union([
|
|
25
25
|
focus(
|
|
26
26
|
/(?:\|\|+(?=$|[ \n])[^\n]*(?:$|\n))+/y,
|
|
27
27
|
convert(unindent, source, true)),
|
|
@@ -15,7 +15,7 @@ export const ulist: UListParser = lazy(() => block(validate(
|
|
|
15
15
|
|
|
16
16
|
export const ulist_: UListParser = lazy(() => block(fmap(validate(
|
|
17
17
|
/-(?=$|[ \n])/y,
|
|
18
|
-
|
|
18
|
+
recursion(Recursion.listitem, some(union([
|
|
19
19
|
indexee(fmap(fallback(
|
|
20
20
|
inits([
|
|
21
21
|
line(open(/-(?:$|[ \n])/y, subsequence([
|
|
@@ -12,7 +12,7 @@ export const url: AutolinkParser.UrlParser = lazy(() => rewrite(
|
|
|
12
12
|
precedence(0, some(union([
|
|
13
13
|
some(unescsource, /(?<![-+*=~^_,.;:!?]|\/{3})(?:[-+*=~^_,.;:!?]|\/{3,}(?!\/))*(?=[\\$"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y),
|
|
14
14
|
precedence(1, verify(bracket, ns => ns.length > 0)),
|
|
15
|
-
]),
|
|
15
|
+
]), [[/[^\x21-\x7E]|\$/y, 9]])),
|
|
16
16
|
false,
|
|
17
17
|
[3 | Backtrack.unescapable]),
|
|
18
18
|
union([
|
|
@@ -100,13 +100,11 @@ const s1 = lazy(() => surround(
|
|
|
100
100
|
setBacktrack(context, 2 | Backtrack.link, head);
|
|
101
101
|
}
|
|
102
102
|
else {
|
|
103
|
-
context.state ^= State.link;
|
|
104
103
|
if (!isBacktrack(context, 1 | Backtrack.link) && !textlink({ context })) {
|
|
105
104
|
setBacktrack(context, 2 | Backtrack.link, head);
|
|
106
105
|
}
|
|
107
106
|
context.position = position;
|
|
108
107
|
context.range = range;
|
|
109
|
-
context.state ^= State.link;
|
|
110
108
|
}
|
|
111
109
|
}
|
|
112
110
|
return as.import(bs as List<Node<string>>).import(cs);
|
|
@@ -8,15 +8,14 @@ import { unwrap, repeat } from '../util';
|
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
10
|
export const deletion: DeletionParser = lazy(() =>
|
|
11
|
-
precedence(0, repeat('~~', surround(
|
|
11
|
+
precedence(0, recursion(Recursion.inline, repeat('~~', surround(
|
|
12
12
|
'',
|
|
13
|
-
recursion(Recursion.inline,
|
|
14
13
|
some(union([
|
|
15
14
|
some(inline, blankWith('\n', '~~')),
|
|
16
15
|
open('\n', some(inline, '~'), true),
|
|
17
|
-
]))
|
|
16
|
+
])),
|
|
18
17
|
'~~',
|
|
19
18
|
false, [],
|
|
20
19
|
([, bs], { buffer }) => buffer.import(bs),
|
|
21
20
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer),
|
|
22
|
-
nodes => new List([new Node(html('del', defrag(unwrap(nodes))))]))));
|
|
21
|
+
nodes => new List([new Node(html('del', defrag(unwrap(nodes))))])))));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { EmphasisParser } from '../inline';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some, recursion, precedence, surround,
|
|
4
|
+
import { union, some, recursion, precedence, surround, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { strong } from './strong';
|
|
7
7
|
import { str } from '../source';
|
|
8
|
-
import { tightStart,
|
|
8
|
+
import { tightStart, afterNonblank } from '../visibility';
|
|
9
9
|
import { unwrap } from '../util';
|
|
10
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
11
11
|
|
|
@@ -13,9 +13,8 @@ export const emphasis: EmphasisParser = lazy(() => surround(
|
|
|
13
13
|
str(/\*(?!\*)/y),
|
|
14
14
|
precedence(0, recursion(Recursion.inline,
|
|
15
15
|
tightStart(some(union([
|
|
16
|
+
some(inline, '*', afterNonblank),
|
|
16
17
|
strong,
|
|
17
|
-
some(inline, blankWith('*')),
|
|
18
|
-
open(some(inline, '*'), inline),
|
|
19
18
|
]))))),
|
|
20
19
|
str('*'),
|
|
21
20
|
false, [],
|
|
@@ -1,37 +1,31 @@
|
|
|
1
1
|
import { EmStrongParser, EmphasisParser, StrongParser } from '../inline';
|
|
2
2
|
import { Recursion, Command } from '../context';
|
|
3
3
|
import { Parser, Result, List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some, recursion, precedence, surround,
|
|
4
|
+
import { union, some, recursion, precedence, surround, lazy, bind } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { strong } from './strong';
|
|
7
7
|
import { emphasis } from './emphasis';
|
|
8
8
|
import { str } from '../source';
|
|
9
|
-
import { tightStart,
|
|
9
|
+
import { tightStart, afterNonblank } from '../visibility';
|
|
10
10
|
import { unwrap, repeat } from '../util';
|
|
11
11
|
import { html, defrag } from 'typed-dom/dom';
|
|
12
12
|
|
|
13
13
|
const substrong: Parser.IntermediateParser<StrongParser> = lazy(() => some(union([
|
|
14
|
+
some(inline, '*', afterNonblank),
|
|
14
15
|
emphasis,
|
|
15
|
-
some(inline, blankWith('*')),
|
|
16
|
-
open(some(inline, '*'), inline),
|
|
17
16
|
])));
|
|
18
17
|
const subemphasis: Parser.IntermediateParser<EmphasisParser> = lazy(() => some(union([
|
|
18
|
+
some(inline, '*', afterNonblank),
|
|
19
19
|
strong,
|
|
20
|
-
some(inline, blankWith('*')),
|
|
21
|
-
open(some(inline, '*'), inline),
|
|
22
20
|
])));
|
|
23
21
|
|
|
24
22
|
// 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず
|
|
25
23
|
// 可能な限り早く閉じるよう解析しなければならない。
|
|
26
24
|
// このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
|
|
27
25
|
export const emstrong: EmStrongParser = lazy(() =>
|
|
28
|
-
precedence(0, repeat('***', surround(
|
|
26
|
+
precedence(0, recursion(Recursion.inline, repeat('***', surround(
|
|
29
27
|
'',
|
|
30
|
-
|
|
31
|
-
tightStart(some(union([
|
|
32
|
-
some(inline, blankWith('*')),
|
|
33
|
-
open(some(inline, '*'), inline),
|
|
34
|
-
])))),
|
|
28
|
+
tightStart(some(union([some(inline, '*', afterNonblank)]))),
|
|
35
29
|
str(/\*{1,3}/y),
|
|
36
30
|
false, [],
|
|
37
31
|
([, bs, cs], context): Result<Parser.Node<EmStrongParser>, Parser.Context<EmStrongParser>> => {
|
|
@@ -143,7 +137,7 @@ export const emstrong: EmStrongParser = lazy(() =>
|
|
|
143
137
|
nodes = prepend('*'.repeat(prefix - postfix), nodes);
|
|
144
138
|
}
|
|
145
139
|
return nodes;
|
|
146
|
-
})));
|
|
140
|
+
}))));
|
|
147
141
|
|
|
148
142
|
function prepend<N>(prefix: string, nodes: List<Node<N>>): List<Node<N>> {
|
|
149
143
|
if (typeof nodes.head?.value === 'string') {
|
|
@@ -29,29 +29,23 @@ export const index: IndexParser = lazy(() => constraint(State.index, fmap(indexe
|
|
|
29
29
|
: undefined,
|
|
30
30
|
undefined)),
|
|
31
31
|
ns => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
assert(ns.last?.value === '');
|
|
44
|
-
ns.pop();
|
|
45
|
-
return ns;
|
|
46
|
-
}
|
|
32
|
+
assert(ns.length === 1);
|
|
33
|
+
const el = ns.head!.value as HTMLAnchorElement;
|
|
34
|
+
return new List([
|
|
35
|
+
new Node(define(el, {
|
|
36
|
+
id: el.id ? null : undefined,
|
|
37
|
+
class: 'index',
|
|
38
|
+
href: el.id ? `#${el.id}` : undefined,
|
|
39
|
+
}))
|
|
40
|
+
]);
|
|
47
41
|
})));
|
|
48
42
|
|
|
49
43
|
export const signature: IndexParser.SignatureParser = lazy(() => validate('|', surround(
|
|
50
44
|
str(/\|(?!\\?\s)/y),
|
|
51
|
-
some(union([
|
|
45
|
+
precedence(9, some(union([
|
|
52
46
|
unsafehtmlentity,
|
|
53
47
|
some(txt, /(?:[$"`\[\](){}<>()[]{}|])/y),
|
|
54
|
-
]), ']'),
|
|
48
|
+
]), ']')),
|
|
55
49
|
/(?=])/y,
|
|
56
50
|
false,
|
|
57
51
|
[3 | Backtrack.escapable],
|
|
@@ -8,15 +8,14 @@ import { unwrap, repeat } from '../util';
|
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
10
|
export const insertion: InsertionParser = lazy(() =>
|
|
11
|
-
precedence(0, repeat('++', surround(
|
|
11
|
+
precedence(0, recursion(Recursion.inline, repeat('++', surround(
|
|
12
12
|
'',
|
|
13
|
-
recursion(Recursion.inline,
|
|
14
13
|
some(union([
|
|
15
14
|
some(inline, blankWith('\n', '++')),
|
|
16
15
|
open('\n', some(inline, '+'), true),
|
|
17
|
-
]))
|
|
16
|
+
])),
|
|
18
17
|
'++',
|
|
19
18
|
false, [],
|
|
20
19
|
([, bs], { buffer }) => buffer.import(bs),
|
|
21
20
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer),
|
|
22
|
-
nodes => new List([new Node(html('ins', defrag(unwrap(nodes))))]))));
|
|
21
|
+
nodes => new List([new Node(html('ins', defrag(unwrap(nodes))))])))));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ItalicParser } from '../inline';
|
|
2
2
|
import { Recursion, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some, recursion, precedence, surround,
|
|
4
|
+
import { union, some, recursion, precedence, surround, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
|
-
import { tightStart,
|
|
6
|
+
import { tightStart, afterNonblank } from '../visibility';
|
|
7
7
|
import { unwrap, repeat } from '../util';
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
@@ -11,15 +11,11 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
11
11
|
// 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
|
|
12
12
|
// ある程度の長さのある文に使うのが望ましい。
|
|
13
13
|
export const italic: ItalicParser = lazy(() =>
|
|
14
|
-
precedence(0, repeat('///', surround(
|
|
14
|
+
precedence(0, recursion(Recursion.inline, repeat('///', surround(
|
|
15
15
|
'',
|
|
16
|
-
|
|
17
|
-
tightStart(some(union([
|
|
18
|
-
some(inline, blankWith('///')),
|
|
19
|
-
open(some(inline, '/'), inline),
|
|
20
|
-
])))),
|
|
16
|
+
tightStart(some(union([inline]), '///', afterNonblank)),
|
|
21
17
|
'///',
|
|
22
18
|
false, [],
|
|
23
19
|
([, bs], { buffer }) => buffer.import(bs),
|
|
24
20
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer),
|
|
25
|
-
nodes => new List([new Node(html('i', defrag(unwrap(nodes))))]))));
|
|
21
|
+
nodes => new List([new Node(html('i', defrag(unwrap(nodes))))])))));
|
|
@@ -183,7 +183,7 @@ describe('Unit: parser/inline/link', () => {
|
|
|
183
183
|
assert.deepStrictEqual(inspect(parser, input('[@a]{b}', new Context())), [['<a class="link" href="b">@a</a>'], '']);
|
|
184
184
|
assert.deepStrictEqual(inspect(parser, input('[@a@b]{c}', new Context())), [['<a class="link" href="c">@a@b</a>'], '']);
|
|
185
185
|
assert.deepStrictEqual(inspect(parser, input('[a@b]{c}', new Context())), [['<a class="link" href="c">a@b</a>'], '']);
|
|
186
|
-
assert.deepStrictEqual(inspect(parser, input('[==a==]{b}', new Context())), [['<a class="link" href="b"
|
|
186
|
+
assert.deepStrictEqual(inspect(parser, input('[==a==]{b}', new Context())), [['<a class="link" href="b"><mark>a</mark></a>'], '']);
|
|
187
187
|
assert.deepStrictEqual(inspect(parser, input('[*a*]{b}', new Context())), [['<a class="link" href="b"><em>a</em></a>'], '']);
|
|
188
188
|
});
|
|
189
189
|
|
|
@@ -15,12 +15,12 @@ const optspec = {
|
|
|
15
15
|
} as const;
|
|
16
16
|
Object.setPrototypeOf(optspec, null);
|
|
17
17
|
|
|
18
|
-
export const textlink: LinkParser.TextLinkParser = lazy(() =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
dup(surround(
|
|
18
|
+
export const textlink: LinkParser.TextLinkParser = lazy(() => bind(
|
|
19
|
+
subsequence([
|
|
20
|
+
constraint(State.link, state(State.linkers, dup(surround(
|
|
22
21
|
'[',
|
|
23
|
-
|
|
22
|
+
precedence(1,
|
|
23
|
+
trimBlankStart(some(union([inline]), ']', [[']', 1]]))),
|
|
24
24
|
']',
|
|
25
25
|
true,
|
|
26
26
|
[3 | Backtrack.common | Backtrack.link, 2 | Backtrack.ruby],
|
|
@@ -30,7 +30,7 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
30
30
|
return void setBacktrack(context, 2 | Backtrack.link | Backtrack.ruby, head);
|
|
31
31
|
}
|
|
32
32
|
return ns.push(new Node(Command.Separator)) && ns;
|
|
33
|
-
})),
|
|
33
|
+
})))),
|
|
34
34
|
// `{ `と`{`で個別にバックトラックが発生し+1nされる。
|
|
35
35
|
// 自己再帰的にパースしてもオプションの不要なパースによる計算量の増加により相殺される。
|
|
36
36
|
dup(surround(
|
|
@@ -43,6 +43,7 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
43
43
|
bs && as.import(bs).push(new Node(Command.Cancel)) && as)),
|
|
44
44
|
]),
|
|
45
45
|
([{ value: content }, { value: params = undefined } = {}], context) => {
|
|
46
|
+
if (context.state & State.link) return new List([new Node(context.source.slice(context.position - context.range, context.position))]);
|
|
46
47
|
if (content.last!.value === Command.Separator) {
|
|
47
48
|
content.pop();
|
|
48
49
|
if (params === undefined) {
|
|
@@ -69,7 +70,7 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
69
70
|
assert(content.head?.value !== '');
|
|
70
71
|
if (content.length !== 0 && trimBlankNodeEnd(content).length === 0) return;
|
|
71
72
|
return new List([new Node(parse(content, params as List<Node<string>>, context))]);
|
|
72
|
-
}))
|
|
73
|
+
}));
|
|
73
74
|
|
|
74
75
|
export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State.link | State.media,
|
|
75
76
|
state(State.linkers,
|
|
@@ -38,12 +38,12 @@ describe('Unit: parser/inline/mark', () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it('nest', () => {
|
|
41
|
-
assert.deepStrictEqual(inspect(parser, input('==a ==b====', new Context())), [['<mark id="mark::a_b">a <mark
|
|
42
|
-
assert.deepStrictEqual(inspect(parser, input('==- ==b====', new Context())), [['<mark id="mark::-_b">- <mark
|
|
43
|
-
assert.deepStrictEqual(inspect(parser, input('==a\\ ==b====', new Context())), [['<mark id="mark::a_b">a <mark
|
|
44
|
-
assert.deepStrictEqual(inspect(parser, input('==a	==b====', new Context())), [['<mark id="mark::a_b=33Mw2l">a\t<mark
|
|
45
|
-
assert.deepStrictEqual(inspect(parser, input('==a<wbr>==b====', new Context())), [['<mark id="mark::ab">a<wbr><mark
|
|
46
|
-
assert.deepStrictEqual(inspect(parser, input('==*==a==*==', new Context())), [['<mark id="mark::a"><em><mark
|
|
41
|
+
assert.deepStrictEqual(inspect(parser, input('==a ==b====', new Context())), [['<mark id="mark::a_b">a <mark>b</mark></mark>', '<a href="#mark::a_b"></a>'], '']);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser, input('==- ==b====', new Context())), [['<mark id="mark::-_b">- <mark>b</mark></mark>', '<a href="#mark::-_b"></a>'], '']);
|
|
43
|
+
assert.deepStrictEqual(inspect(parser, input('==a\\ ==b====', new Context())), [['<mark id="mark::a_b">a <mark>b</mark></mark>', '<a href="#mark::a_b"></a>'], '']);
|
|
44
|
+
assert.deepStrictEqual(inspect(parser, input('==a	==b====', new Context())), [['<mark id="mark::a_b=33Mw2l">a\t<mark>b</mark></mark>', '<a href="#mark::a_b=33Mw2l"></a>'], '']);
|
|
45
|
+
assert.deepStrictEqual(inspect(parser, input('==a<wbr>==b====', new Context())), [['<mark id="mark::ab">a<wbr><mark>b</mark></mark>', '<a href="#mark::ab"></a>'], '']);
|
|
46
|
+
assert.deepStrictEqual(inspect(parser, input('==*==a==*==', new Context())), [['<mark id="mark::a"><em><mark>a</mark></em></mark>', '<a href="#mark::a"></a>'], '']);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
});
|
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
import { MarkParser } from '../inline';
|
|
2
2
|
import { State, Recursion, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some, recursion, precedence, state,
|
|
4
|
+
import { union, some, recursion, precedence, state, surround, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { identity, signature } from './extension/indexee';
|
|
7
|
-
import { tightStart,
|
|
7
|
+
import { tightStart, afterNonblank } from '../visibility';
|
|
8
8
|
import { unwrap, repeat } from '../util';
|
|
9
9
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
|
-
export const mark: MarkParser = lazy(() =>
|
|
12
|
-
precedence(0,
|
|
11
|
+
export const mark: MarkParser = lazy(() =>
|
|
12
|
+
precedence(0, recursion(Recursion.inline, repeat('==', surround(
|
|
13
13
|
'',
|
|
14
|
-
|
|
15
|
-
tightStart(some(union([
|
|
16
|
-
some(inline, blankWith('==')),
|
|
17
|
-
open(some(inline, '='), inline),
|
|
18
|
-
])))),
|
|
14
|
+
tightStart(state(State.mark, some(union([inline]), '==', afterNonblank))),
|
|
19
15
|
'==',
|
|
20
16
|
false, [],
|
|
21
17
|
([, bs], { buffer }) => buffer.import(bs),
|
|
22
18
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer),
|
|
23
|
-
(nodes, { id }) => {
|
|
19
|
+
(nodes, { id, state }) => {
|
|
24
20
|
const el = html('mark', defrag(unwrap(nodes)));
|
|
21
|
+
if (state & State.linkers) return new List([new Node(el)]);
|
|
25
22
|
define(el, { id: identity('mark', id, signature(el)) });
|
|
26
23
|
return el.id
|
|
27
24
|
? new List([new Node(el), new Node(html('a', { href: `#${el.id}` }))])
|
|
28
25
|
: new List([new Node(el)]);
|
|
29
|
-
}))))
|
|
26
|
+
}))));
|
|
@@ -53,9 +53,9 @@ export const ruby: RubyParser = lazy(() => bind(
|
|
|
53
53
|
assert(rubies.length > 0);
|
|
54
54
|
return new List([
|
|
55
55
|
new Node(html('ruby', defrag(unwrap(new List<Node<string | HTMLElement>>([
|
|
56
|
-
new Node(texts.
|
|
56
|
+
new Node(texts.foldl((acc, { value }) => acc ? acc + ' ' + value : value, '')),
|
|
57
57
|
new Node(html('rp', '(')),
|
|
58
|
-
new Node(html('rt', rubies.
|
|
58
|
+
new Node(html('rt', rubies.foldl((acc, { value }) => acc ? acc + ' ' + value : value, '').trim())),
|
|
59
59
|
new Node(html('rp', ')')),
|
|
60
60
|
]))))),
|
|
61
61
|
]);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { StrongParser } from '../inline';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some, recursion, precedence, surround,
|
|
4
|
+
import { union, some, recursion, precedence, surround, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { emphasis } from './emphasis';
|
|
7
7
|
import { str } from '../source';
|
|
8
|
-
import { tightStart,
|
|
8
|
+
import { tightStart, afterNonblank } from '../visibility';
|
|
9
9
|
import { unwrap } from '../util';
|
|
10
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
11
11
|
|
|
@@ -13,9 +13,8 @@ export const strong: StrongParser = lazy(() => surround(
|
|
|
13
13
|
str(/\*\*(?!\*)/y),
|
|
14
14
|
precedence(0, recursion(Recursion.inline,
|
|
15
15
|
tightStart(some(union([
|
|
16
|
+
some(inline, '*', afterNonblank),
|
|
16
17
|
emphasis,
|
|
17
|
-
some(inline, blankWith('*')),
|
|
18
|
-
open(some(inline, '*'), inline),
|
|
19
18
|
]))))),
|
|
20
19
|
str('**'),
|
|
21
20
|
false, [],
|
|
@@ -154,7 +154,7 @@ describe('Unit: parser/inline', () => {
|
|
|
154
154
|
assert.deepStrictEqual(inspect(parser, input('[[a\nb]]', new Context())), [['[', '[', 'a', '<br>', 'b', ']', ']'], '']);
|
|
155
155
|
assert.deepStrictEqual(inspect(parser, input('[[[a\nb]]]', new Context())), [['[', '[', '[', 'a', '<br>', 'b', ']', ']', ']'], '']);
|
|
156
156
|
assert.deepStrictEqual(inspect(parser, input('"[[""]]', new Context())), [['"', '[', '[', '"', '"', ']', ']'], '']);
|
|
157
|
-
assert.deepStrictEqual(inspect(parser, input('[==a==]{b}', new Context())), [['<a class="link" href="b"
|
|
157
|
+
assert.deepStrictEqual(inspect(parser, input('[==a==]{b}', new Context())), [['<a class="link" href="b"><mark>a</mark></a>'], '']);
|
|
158
158
|
assert.deepStrictEqual(inspect(parser, input('[[a](b)]{c}', new Context())), [['<a class="link" href="c"><ruby>a<rp>(</rp><rt>b</rt><rp>)</rp></ruby></a>'], '']);
|
|
159
159
|
assert.deepStrictEqual(inspect(parser, input('[[[[[[[{a}', new Context())), [['[', '[', '[', '[', '[', '[', '[', '<a class="url" href="a">a</a>'], '']);
|
|
160
160
|
assert.deepStrictEqual(inspect(parser, input('<http://host>', new Context())), [['<', '<a class="url" href="http://host" target="_blank">http://host</a>', '>'], '']);
|
|
@@ -170,6 +170,9 @@ describe('Unit: parser/inline', () => {
|
|
|
170
170
|
assert.deepStrictEqual(inspect(parser, input('[#@a/http://host/(<bdi>)]</bdi>', new Context())), [['<a class="index" href="#index::@a/http://host/(<bdi>)">@a/http://host/<span class="paren">(<span class="invalid"><bdi></span>)</span></a>', '</bdi', '>'], '']);
|
|
171
171
|
assert.deepStrictEqual(inspect(parser, input('[#a|<bdi>]</bdi>', new Context())), [['<a class="index" href="#index::a|<bdi>">a|<span class="invalid"><bdi></span></a>', '</bdi', '>'], '']);
|
|
172
172
|
assert.deepStrictEqual(inspect(parser, input('[[#a|<bdi>]</bdi>', new Context())), [['[', '<a class="index" href="#index::a|<bdi>">a|<span class="invalid"><bdi></span></a>', '</bdi', '>'], '']);
|
|
173
|
+
assert.deepStrictEqual(inspect(parser, input('[*==*]{a}', new Context())), [['<a class="link" href="a">*==*</a>'], '']);
|
|
174
|
+
assert.deepStrictEqual(inspect(parser, input('[]{"}[[""]]}]', new Context())), [['<a class="url" href=""">"</a>', '<sup class="reference"><span>""</span></sup>', '}', ']'], '']);
|
|
175
|
+
assert.deepStrictEqual(inspect(parser, input('[ []{"}[[""]]}]', new Context())), [['[', ' ', '<a class="url" href=""">"</a>', '<sup class="reference"><span>""</span></sup>', '}', ']'], '']);
|
|
173
176
|
});
|
|
174
177
|
|
|
175
178
|
it('uri', () => {
|
|
@@ -4,12 +4,12 @@ import { Command } from './context';
|
|
|
4
4
|
describe('Unit: parser/segment', () => {
|
|
5
5
|
describe('segment', () => {
|
|
6
6
|
it('huge input', () => {
|
|
7
|
-
const result = segment(`${'\n'.repeat(
|
|
7
|
+
const result = segment(`${'\n'.repeat(1e6 + 1)}`).next().value?.split('\n', 1)[0];
|
|
8
8
|
assert(result?.startsWith(`${Command.Error}Too large input`));
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('huge segment', () => {
|
|
12
|
-
const result = segment(`${'\n'.repeat(
|
|
12
|
+
const result = segment(`${'\n'.repeat(1e5 + 1)}`).next().value?.split('\n', 1)[0];
|
|
13
13
|
assert(result?.startsWith(`${Command.Error}Too large segment`));
|
|
14
14
|
});
|
|
15
15
|
|
package/src/parser/segment.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const MAX_SEGMENT_SIZE = 100_000; // 100,000 bytes (Max value size of FDB
|
|
|
13
13
|
export const MAX_INPUT_SIZE = MAX_SEGMENT_SIZE * 10;
|
|
14
14
|
|
|
15
15
|
const parser: SegmentParser = union([
|
|
16
|
-
some(emptyline),
|
|
16
|
+
some(emptyline, MAX_SEGMENT_SIZE + 1),
|
|
17
17
|
input => {
|
|
18
18
|
const { context: { source, position } } = input;
|
|
19
19
|
if (position === source.length) return;
|
|
@@ -36,7 +36,7 @@ const parser: SegmentParser = union([
|
|
|
36
36
|
return extension(input);
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
|
-
some(contentline),
|
|
39
|
+
some(contentline, MAX_SEGMENT_SIZE + 1),
|
|
40
40
|
]) as any;
|
|
41
41
|
|
|
42
42
|
export function* segment(source: string): Generator<string, undefined, undefined> {
|