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.
Files changed (53) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/design.md +5 -5
  3. package/dist/index.js +141 -127
  4. package/package.json +1 -1
  5. package/src/combinator/control/constraint/block.ts +1 -1
  6. package/src/combinator/control/constraint/contract.ts +5 -6
  7. package/src/combinator/control/constraint/line.ts +1 -2
  8. package/src/combinator/control/manipulation/convert.ts +1 -1
  9. package/src/combinator/control/manipulation/fallback.ts +1 -1
  10. package/src/combinator/control/manipulation/indent.ts +2 -2
  11. package/src/combinator/control/manipulation/lazy.ts +1 -1
  12. package/src/combinator/control/manipulation/match.ts +2 -5
  13. package/src/combinator/control/manipulation/recovery.ts +1 -1
  14. package/src/combinator/control/manipulation/reverse.ts +1 -1
  15. package/src/combinator/control/manipulation/scope.ts +3 -7
  16. package/src/combinator/control/manipulation/surround.ts +51 -62
  17. package/src/combinator/control/monad/bind.ts +7 -12
  18. package/src/combinator/control/monad/fmap.ts +4 -4
  19. package/src/combinator/data/parser/context.ts +12 -12
  20. package/src/combinator/data/parser/inits.ts +3 -6
  21. package/src/combinator/data/parser/sequence.ts +3 -6
  22. package/src/combinator/data/parser/some.ts +2 -2
  23. package/src/combinator/data/parser/subsequence.ts +1 -1
  24. package/src/combinator/data/parser/tails.ts +1 -1
  25. package/src/combinator/data/parser/union.ts +1 -1
  26. package/src/combinator/data/parser.ts +10 -9
  27. package/src/parser/api/body.test.ts +1 -1
  28. package/src/parser/api/header.test.ts +2 -2
  29. package/src/parser/api/normalize.test.ts +2 -0
  30. package/src/parser/api/normalize.ts +1 -1
  31. package/src/parser/context.ts +9 -6
  32. package/src/parser/header.test.ts +2 -2
  33. package/src/parser/header.ts +3 -3
  34. package/src/parser/inline/annotation.ts +1 -1
  35. package/src/parser/inline/autolink/account.ts +3 -3
  36. package/src/parser/inline/autolink/anchor.ts +1 -1
  37. package/src/parser/inline/autolink/email.ts +1 -1
  38. package/src/parser/inline/autolink/hashnum.ts +1 -1
  39. package/src/parser/inline/autolink/hashtag.ts +1 -1
  40. package/src/parser/inline/autolink/url.ts +9 -10
  41. package/src/parser/inline/bracket.ts +31 -19
  42. package/src/parser/inline/extension/index.ts +3 -3
  43. package/src/parser/inline/extension/indexee.ts +1 -1
  44. package/src/parser/inline/extension/label.ts +1 -1
  45. package/src/parser/inline/extension/placeholder.ts +1 -1
  46. package/src/parser/inline/htmlentity.ts +1 -1
  47. package/src/parser/inline/link.ts +12 -14
  48. package/src/parser/inline/math.ts +2 -2
  49. package/src/parser/inline/media.ts +15 -17
  50. package/src/parser/inline/reference.ts +9 -9
  51. package/src/parser/inline/ruby.ts +4 -4
  52. package/src/parser/inline/template.ts +6 -10
  53. 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<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
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, position } = context;
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
- assert(context.position >= position);
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<unknown>>(parser: P, limit?: number): P;
7
- export function some<P extends Parser<unknown>>(parser: P, end?: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
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<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
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<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
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<unknown>>(parsers: SubParsers<P>): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
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<unknown>> = P extends Parser<infer N> ? N : never;
68
- export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, Ctx, infer D> ? D : never;
69
- export type Context<P extends Parser<unknown>> = P extends Parser<unknown, infer C> ? C : never;
70
- export type SubNode<P extends Parser<unknown>> = ExtractSubNode<SubParsers<P>>;
71
- export type IntermediateParser<P extends Parser<unknown>> = Parser<SubNode<P>, Context<P>, SubParsers<P>>;
72
- type ExtractSubNode<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer N ? N extends Parser<infer U> ? U : never : never;
73
- type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
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<unknown>>(parser: P): P;
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');
@@ -9,7 +9,7 @@ export function normalize(source: string): string {
9
9
  }
10
10
 
11
11
  function format(source: string): string {
12
- return source.replace(/\r\n?/g, '\n');
12
+ return source.replace(/\r\n?|[\u2028\u2029]/g, '\n');
13
13
  }
14
14
 
15
15
  const invalid = new RegExp([
@@ -28,12 +28,15 @@ export const enum Recursion {
28
28
  }
29
29
 
30
30
  export const enum Backtrack {
31
- bracket = 1 << 6,
32
- doublebracket = 1 << 5,
33
- link = 1 << 4,
34
- ruby = 1 << 3,
35
- escbracket = 1 << 2,
36
- autolink = 0 << 2,
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
 
@@ -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
- /---+ *\r?\n(?=\S)/y,
10
+ /---+ *\n(?=\S)/y,
11
11
  inits([
12
12
  block(
13
13
  union([
14
14
  validate(({ context }) => context.header ?? true,
15
- focus(/(---+) *\r?\n(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,100}\1 *(?:$|\r?\n)/y,
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(/ *\r?\n/y)),
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.bracket, 3 | Backtrack.doublebracket],
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.autolink]),
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.autolink])),
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, [2 | Backtrack.autolink], context.position - context.range!);
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.autolink],
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.autolink],
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.autolink],
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.autolink],
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.autolink]),
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, range = 0 } = context;
31
- context.position -= position > 0 && source[position - 1] === '!' ? 1 : 0;
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, context.position))]),
35
+ new List([new Data(source.slice(position))]),
37
36
  context))
38
37
  ]);
39
38
  })),
40
39
  str(/[^:]+/y),
41
40
  ]),
42
- ]), false));
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.autolink], undefined, () => new List()),
45
+ true, [3 | Backtrack.unescapable], undefined, () => new List()),
47
46
  surround(str('['), recursion(Recursion.terminal, some(union([bracket, unescsource]), ']')), str(']'),
48
- true, [3 | Backtrack.autolink], undefined, () => new List()),
47
+ true, [3 | Backtrack.unescapable], undefined, () => new List()),
49
48
  surround(str('{'), recursion(Recursion.terminal, some(union([bracket, unescsource]), '}')), str('}'),
50
- true, [3 | Backtrack.autolink], undefined, () => new List()),
49
+ true, [3 | Backtrack.unescapable], undefined, () => new List()),
51
50
  surround(str('"'), precedence(2, recursion(Recursion.terminal, some(unescsource, '"'))), str('"'),
52
- true, [3 | Backtrack.autolink], undefined, () => new List()),
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
- [2 | Backtrack.bracket],
43
- ([as, bs = new List(), cs], { source, position, range = 0 }) => {
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()]) => as.import(bs as List<Data<string>>)));
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.bracket],
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, [2 | Backtrack.link], head);
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, [1 | Backtrack.link])
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, [2 | Backtrack.link], head);
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.bracket],
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, /(?:[$"`\[\](){}<>()[]{}])/y),
53
+ some(txt, /(?:[$"`\[\](){}<>()[]{}|])/y),
54
54
  ]), ']'),
55
55
  /(?=])/y,
56
56
  false,
57
- [3 | Backtrack.bracket],
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<unknown, MarkdownParser.Context>>(parser: P): P;
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.bracket, 1]),
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.bracket],
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.bracket],
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.bracket, 3 | Backtrack.link, 2 | Backtrack.ruby],
28
- ([, ns = new List()], context) =>
29
- context.linebreak === 0
30
- ? ns.push(new Data(Command.Separator)) && ns
31
- : undefined)),
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], context) => {
42
- if (!bs) return;
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, [2 | Backtrack.link], head);
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.bracket]),
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.bracket]),
27
+ [3 | Backtrack.escapable]),
28
28
  ]),
29
29
  ({ context: { source, caches: { math: cache } = {} } }) => new List([
30
30
  new Data(cache?.get(source)?.cloneNode(true) ||