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.
@@ -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, end?: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
8
- export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delimiters: readonly DelimiterOption[] = [], limit = -1): Parser<N> {
9
- if (typeof end === 'number') return some(parser, undefined, delimiters, end);
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(end);
12
- const delims = delimiters.map(([delimiter, precedence]) => ({
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
- if (delims.length > 0) {
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
- if (delims.length > 0) {
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(10 * 1000 ** 2)}`);
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(1000 ** 2 - 1)}`);
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(10 * 1000 ** 2)}`, { id: '' }).children].map(el => el.outerHTML),
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(100 * 1000 + 1)}`, { id: '' }).children].map(el => el.outerHTML),
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', function () {
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(source, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
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', function () {
375
- this.timeout(5000);
376
- const source = `((([[[[#$[${'.'.repeat(16664 + 1)}]]]`;
386
+ it('backtrack 2 error', () => {
377
387
  assert.deepStrictEqual(
378
- [...parse(source, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
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
- some(recursion(Recursion.blockquote, union([
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
- some(recursion(Recursion.blockquote, union([
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
- some(recursion(Recursion.listitem, union([
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
- some(recursion(Recursion.listitem, union([
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
- some(recursion(Recursion.block, union([
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
- some(recursion(Recursion.listitem, union([
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
- ]), undefined, [[/[^\x21-\x7E]|\$/y, 9]])),
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, open, lazy } from '../../combinator';
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, blankWith } from '../visibility';
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, open, lazy, bind } from '../../combinator';
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, blankWith } from '../visibility';
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
- recursion(Recursion.inline,
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
- if (ns.length === 1) {
33
- const el = ns.head!.value as HTMLElement;
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
- ]);
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, open, lazy } from '../../combinator';
4
+ import { union, some, recursion, precedence, surround, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
- import { tightStart, blankWith } from '../visibility';
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
- recursion(Recursion.inline,
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">==a==</a>'], '']);
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(() => constraint(State.link,
19
- precedence(1, state(State.linkers,
20
- bind(subsequence([
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
- trimBlankStart(some(union([inline]), ']', [[']', 1]])),
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 id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
42
- assert.deepStrictEqual(inspect(parser, input('==- ==b====', new Context())), [['<mark id="mark::-_b">- <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::-_b"></a>'], '']);
43
- assert.deepStrictEqual(inspect(parser, input('==a\\ ==b====', new Context())), [['<mark id="mark::a_b">a <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
44
- assert.deepStrictEqual(inspect(parser, input('==a&Tab;==b====', new Context())), [['<mark id="mark::a_b=33Mw2l">a\t<mark id="mark::b">b</mark><a href="#mark::b"></a></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 id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::ab"></a>'], '']);
46
- assert.deepStrictEqual(inspect(parser, input('==*==a==*==', new Context())), [['<mark id="mark::a"><em><mark id="mark::a">a</mark><a href="#mark::a"></a></em></mark>', '<a href="#mark::a"></a>'], '']);
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&Tab;==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, constraint, surround, open, lazy } from '../../combinator';
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, blankWith } from '../visibility';
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(() => constraint(State.linkers & ~State.mark,
12
- precedence(0, state(State.mark, repeat('==', surround(
11
+ export const mark: MarkParser = lazy(() =>
12
+ precedence(0, recursion(Recursion.inline, repeat('==', surround(
13
13
  '',
14
- recursion(Recursion.inline,
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.foldr(({ value }, acc) => value + ' ' + acc, '').slice(0, -1)),
56
+ new Node(texts.foldl((acc, { value }) => acc ? acc + ' ' + value : value, '')),
57
57
  new Node(html('rp', '(')),
58
- new Node(html('rt', rubies.foldr(({ value }, acc) => value + ' ' + acc, '').trim())),
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, open, lazy } from '../../combinator';
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, blankWith } from '../visibility';
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">==a==</a>'], '']);
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/(&lt;bdi&gt;)">@a/http://host/<span class="paren">(<span class="invalid">&lt;bdi&gt;</span>)</span></a>', '</bdi', '>'], '']);
171
171
  assert.deepStrictEqual(inspect(parser, input('[#a|<bdi>]</bdi>', new Context())), [['<a class="index" href="#index::a|&lt;bdi&gt;">a|<span class="invalid">&lt;bdi&gt;</span></a>', '</bdi', '>'], '']);
172
172
  assert.deepStrictEqual(inspect(parser, input('[[#a|<bdi>]</bdi>', new Context())), [['[', '<a class="index" href="#index::a|&lt;bdi&gt;">a|<span class="invalid">&lt;bdi&gt;</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="&quot;">"</a>', '<sup class="reference"><span>""</span></sup>', '}', ']'], '']);
175
+ assert.deepStrictEqual(inspect(parser, input('[ []{"}[[""]]}]', new Context())), [['[', ' ', '<a class="url" href="&quot;">"</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(10 * 1000 ** 2)}`).next().value?.split('\n', 1)[0];
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(1000 ** 2 - 1)}`).next().value?.split('\n', 1)[0];
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
 
@@ -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> {