securemark 0.291.1 → 0.292.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 (164) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/design.md +13 -2
  3. package/dist/index.js +1093 -756
  4. package/markdown.d.ts +17 -17
  5. package/package.json +1 -1
  6. package/src/combinator/control/constraint/block.test.ts +8 -5
  7. package/src/combinator/control/constraint/block.ts +9 -9
  8. package/src/combinator/control/constraint/contract.ts +20 -28
  9. package/src/combinator/control/constraint/line.test.ts +9 -6
  10. package/src/combinator/control/constraint/line.ts +21 -22
  11. package/src/combinator/control/manipulation/convert.ts +29 -13
  12. package/src/combinator/control/manipulation/fence.ts +18 -15
  13. package/src/combinator/control/manipulation/indent.test.ts +17 -14
  14. package/src/combinator/control/manipulation/indent.ts +15 -8
  15. package/src/combinator/control/manipulation/match.ts +11 -10
  16. package/src/combinator/control/manipulation/recovery.ts +6 -2
  17. package/src/combinator/control/manipulation/scope.ts +37 -38
  18. package/src/combinator/control/manipulation/surround.ts +78 -60
  19. package/src/combinator/control/manipulation/trim.test.ts +12 -9
  20. package/src/combinator/control/monad/bind.ts +16 -16
  21. package/src/combinator/control/monad/fmap.ts +6 -6
  22. package/src/combinator/data/parser/context/delimiter.ts +8 -7
  23. package/src/combinator/data/parser/context.test.ts +19 -14
  24. package/src/combinator/data/parser/context.ts +20 -16
  25. package/src/combinator/data/parser/inits.ts +13 -14
  26. package/src/combinator/data/parser/sequence.test.ts +16 -15
  27. package/src/combinator/data/parser/sequence.ts +13 -14
  28. package/src/combinator/data/parser/some.test.ts +19 -18
  29. package/src/combinator/data/parser/some.ts +13 -15
  30. package/src/combinator/data/parser/subsequence.test.ts +22 -21
  31. package/src/combinator/data/parser/subsequence.ts +3 -3
  32. package/src/combinator/data/parser/tails.ts +3 -3
  33. package/src/combinator/data/parser/union.test.ts +16 -15
  34. package/src/combinator/data/parser/union.ts +2 -2
  35. package/src/combinator/data/parser.ts +66 -28
  36. package/src/debug.test.ts +3 -3
  37. package/src/parser/api/bind.ts +3 -3
  38. package/src/parser/api/header.ts +7 -6
  39. package/src/parser/api/normalize.ts +2 -2
  40. package/src/parser/api/parse.test.ts +14 -15
  41. package/src/parser/api/parse.ts +3 -3
  42. package/src/parser/autolink.test.ts +19 -17
  43. package/src/parser/block/blockquote.test.ts +86 -84
  44. package/src/parser/block/blockquote.ts +4 -2
  45. package/src/parser/block/codeblock.test.ts +58 -56
  46. package/src/parser/block/codeblock.ts +3 -3
  47. package/src/parser/block/dlist.test.ts +58 -56
  48. package/src/parser/block/extension/aside.test.ts +10 -8
  49. package/src/parser/block/extension/aside.ts +1 -1
  50. package/src/parser/block/extension/example.test.ts +20 -18
  51. package/src/parser/block/extension/example.ts +3 -3
  52. package/src/parser/block/extension/fig.test.ts +38 -36
  53. package/src/parser/block/extension/fig.ts +1 -1
  54. package/src/parser/block/extension/figbase.test.ts +17 -15
  55. package/src/parser/block/extension/figure.test.ts +64 -62
  56. package/src/parser/block/extension/figure.ts +3 -2
  57. package/src/parser/block/extension/message.test.ts +15 -13
  58. package/src/parser/block/extension/message.ts +3 -3
  59. package/src/parser/block/extension/placeholder.test.ts +3 -1
  60. package/src/parser/block/extension/table.test.ts +73 -71
  61. package/src/parser/block/extension/table.ts +5 -5
  62. package/src/parser/block/extension.test.ts +3 -1
  63. package/src/parser/block/heading.test.ts +65 -64
  64. package/src/parser/block/heading.ts +3 -3
  65. package/src/parser/block/ilist.test.ts +3 -1
  66. package/src/parser/block/ilist.ts +3 -3
  67. package/src/parser/block/mathblock.test.ts +33 -31
  68. package/src/parser/block/mathblock.ts +1 -1
  69. package/src/parser/block/mediablock.ts +2 -2
  70. package/src/parser/block/olist.test.ts +99 -97
  71. package/src/parser/block/olist.ts +2 -2
  72. package/src/parser/block/pagebreak.test.ts +17 -15
  73. package/src/parser/block/pagebreak.ts +1 -1
  74. package/src/parser/block/paragraph.test.ts +60 -57
  75. package/src/parser/block/reply/cite.test.ts +41 -39
  76. package/src/parser/block/reply/cite.ts +3 -3
  77. package/src/parser/block/reply/quote.test.ts +52 -50
  78. package/src/parser/block/reply.test.ts +21 -19
  79. package/src/parser/block/sidefence.test.ts +51 -49
  80. package/src/parser/block/table.test.ts +51 -50
  81. package/src/parser/block/table.ts +6 -6
  82. package/src/parser/block/ulist.test.ts +52 -50
  83. package/src/parser/block/ulist.ts +2 -2
  84. package/src/parser/block.ts +6 -5
  85. package/src/parser/context.ts +1 -0
  86. package/src/parser/header.test.ts +22 -21
  87. package/src/parser/header.ts +25 -13
  88. package/src/parser/inline/annotation.test.ts +44 -42
  89. package/src/parser/inline/annotation.ts +2 -2
  90. package/src/parser/inline/autolink/account.test.ts +32 -30
  91. package/src/parser/inline/autolink/account.ts +1 -1
  92. package/src/parser/inline/autolink/anchor.test.ts +23 -21
  93. package/src/parser/inline/autolink/anchor.ts +1 -1
  94. package/src/parser/inline/autolink/channel.test.ts +16 -14
  95. package/src/parser/inline/autolink/channel.ts +2 -2
  96. package/src/parser/inline/autolink/email.test.ts +38 -36
  97. package/src/parser/inline/autolink/email.ts +2 -2
  98. package/src/parser/inline/autolink/hashnum.test.ts +39 -37
  99. package/src/parser/inline/autolink/hashnum.ts +1 -1
  100. package/src/parser/inline/autolink/hashtag.test.ts +58 -56
  101. package/src/parser/inline/autolink/hashtag.ts +1 -1
  102. package/src/parser/inline/autolink/url.test.ts +76 -74
  103. package/src/parser/inline/autolink/url.ts +6 -6
  104. package/src/parser/inline/bracket.test.ts +69 -67
  105. package/src/parser/inline/bracket.ts +32 -32
  106. package/src/parser/inline/code.test.ts +32 -29
  107. package/src/parser/inline/code.ts +20 -13
  108. package/src/parser/inline/deletion.test.ts +29 -27
  109. package/src/parser/inline/deletion.ts +2 -2
  110. package/src/parser/inline/emphasis.test.ts +40 -36
  111. package/src/parser/inline/emphasis.ts +2 -2
  112. package/src/parser/inline/emstrong.test.ts +102 -96
  113. package/src/parser/inline/emstrong.ts +96 -36
  114. package/src/parser/inline/extension/index.test.ts +91 -89
  115. package/src/parser/inline/extension/index.ts +18 -30
  116. package/src/parser/inline/extension/indexee.ts +1 -1
  117. package/src/parser/inline/extension/indexer.test.ts +26 -24
  118. package/src/parser/inline/extension/indexer.ts +1 -1
  119. package/src/parser/inline/extension/label.test.ts +34 -32
  120. package/src/parser/inline/extension/placeholder.test.ts +44 -42
  121. package/src/parser/inline/extension/placeholder.ts +11 -8
  122. package/src/parser/inline/html.test.ts +108 -106
  123. package/src/parser/inline/html.ts +24 -23
  124. package/src/parser/inline/htmlentity.test.ts +39 -37
  125. package/src/parser/inline/htmlentity.ts +9 -3
  126. package/src/parser/inline/insertion.test.ts +29 -27
  127. package/src/parser/inline/insertion.ts +2 -2
  128. package/src/parser/inline/italic.test.ts +55 -53
  129. package/src/parser/inline/italic.ts +2 -2
  130. package/src/parser/inline/link.test.ts +187 -185
  131. package/src/parser/inline/link.ts +30 -12
  132. package/src/parser/inline/mark.test.ts +31 -29
  133. package/src/parser/inline/mark.ts +3 -3
  134. package/src/parser/inline/math.test.ts +133 -131
  135. package/src/parser/inline/math.ts +2 -2
  136. package/src/parser/inline/media.test.ts +93 -91
  137. package/src/parser/inline/media.ts +41 -17
  138. package/src/parser/inline/reference.test.ts +110 -108
  139. package/src/parser/inline/reference.ts +48 -39
  140. package/src/parser/inline/remark.test.ts +53 -51
  141. package/src/parser/inline/remark.ts +3 -3
  142. package/src/parser/inline/ruby.test.ts +46 -44
  143. package/src/parser/inline/ruby.ts +24 -27
  144. package/src/parser/inline/shortmedia.test.ts +11 -9
  145. package/src/parser/inline/strong.test.ts +37 -33
  146. package/src/parser/inline/strong.ts +6 -3
  147. package/src/parser/inline/template.test.ts +24 -22
  148. package/src/parser/inline/template.ts +20 -11
  149. package/src/parser/inline.test.ts +221 -220
  150. package/src/parser/inline.ts +13 -8
  151. package/src/parser/segment.ts +13 -8
  152. package/src/parser/source/escapable.test.ts +24 -22
  153. package/src/parser/source/escapable.ts +26 -41
  154. package/src/parser/source/line.test.ts +19 -17
  155. package/src/parser/source/line.ts +3 -3
  156. package/src/parser/source/str.ts +28 -11
  157. package/src/parser/source/text.test.ts +85 -83
  158. package/src/parser/source/text.ts +26 -54
  159. package/src/parser/source/unescapable.test.ts +24 -22
  160. package/src/parser/source/unescapable.ts +18 -33
  161. package/src/parser/source.ts +1 -1
  162. package/src/parser/util.ts +36 -33
  163. package/src/parser/visibility.ts +19 -15
  164. package/src/util/quote.ts +4 -2
@@ -1,28 +1,27 @@
1
- import { Parser, Ctx, Node, Context, SubParsers, SubNode, eval, exec, check } from '../parser';
1
+ import { Parser, CtxOptions, Node, Context, SubParsers, SubNode, eval, Ctx } from '../parser';
2
2
  import { push } from 'spica/array';
3
3
 
4
- export function inits<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
5
- export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
4
+ export function inits<P extends Parser<unknown, Ctx>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[]) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
5
+ export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[]) => boolean): Parser<N, CtxOptions, D> {
6
6
  assert(parsers.every(f => f));
7
7
  if (parsers.length === 1) return parsers[0];
8
- return ({ source, context }) => {
9
- let rest = source;
8
+ return input => {
9
+ const { context } = input;
10
+ const { source, position } = context;
10
11
  let nodes: N[] | undefined;
11
12
  for (let len = parsers.length, i = 0; i < len; ++i) {
12
- if (rest === '') break;
13
- if (context.delimiters?.match(rest, context)) break;
14
- const result = parsers[i]({ source: rest, context });
15
- assert(check(rest, result, false));
13
+ if (context.position === source.length) break;
14
+ if (context.delimiters?.match(context)) break;
15
+ const result = parsers[i](input);
16
16
  if (result === undefined) break;
17
17
  nodes = nodes
18
18
  ? push(nodes, eval(result))
19
19
  : eval(result);
20
- rest = exec(result);
21
- if (resume?.(eval(result), exec(result)) === false) break;
20
+ if (resume?.(eval(result)) === false) break;
22
21
  }
23
- assert(rest.length <= source.length);
24
- return nodes && rest.length < source.length
25
- ? [nodes, rest]
22
+ assert(context.position >= position);
23
+ return nodes && context.position > position
24
+ ? [nodes]
26
25
  : undefined;
27
26
  };
28
27
  }
@@ -1,31 +1,32 @@
1
- import { Parser } from '../parser';
1
+ import { Parser, input } from '../parser';
2
2
  import { sequence } from './sequence';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
5
5
  describe('Unit: combinator/data/parser/sequence', () => {
6
6
  describe('sequence', () => {
7
- const a: Parser<string, never> = ({ source }) => {
8
- return source && source[0] === 'a'
9
- ? [['A'], source.slice(1)]
7
+ const a: Parser<string> = ({ context }) => {
8
+ return context.source[context.position] === 'a'
9
+ ? void ++context.position || [['A']]
10
10
  : undefined;
11
11
  };
12
- const b: Parser<string, never> = ({ source }) => {
13
- return source && source[0] === 'b'
14
- ? [['B'], source.slice(1)]
12
+ const b: Parser<string> = ({ context }) => {
13
+ return context.source[context.position] === 'b'
14
+ ? void ++context.position || [['B']]
15
15
  : undefined;
16
16
  };
17
17
  const ab = sequence<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
18
+ const { context: ctx } = input('', {});
18
19
 
19
20
  it('basic', () => {
20
21
  const parser = ab;
21
- assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
22
- assert.deepStrictEqual(inspect(parser({ source: 'a', context: {} })), undefined);
23
- assert.deepStrictEqual(inspect(parser({ source: 'b', context: {} })), undefined);
24
- assert.deepStrictEqual(inspect(parser({ source: 'ab', context: {} })), [['A', 'B'], '']);
25
- assert.deepStrictEqual(inspect(parser({ source: 'ba', context: {} })), undefined);
26
- assert.deepStrictEqual(inspect(parser({ source: 'aab', context: {} })), undefined);
27
- assert.deepStrictEqual(inspect(parser({ source: 'abb', context: {} })), [['A', 'B'], 'b']);
28
- assert.deepStrictEqual(inspect(parser({ source: 'bba', context: {} })), undefined);
22
+ assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
23
+ assert.deepStrictEqual(inspect(parser(input('a', ctx)), ctx), undefined);
24
+ assert.deepStrictEqual(inspect(parser(input('b', ctx)), ctx), undefined);
25
+ assert.deepStrictEqual(inspect(parser(input('ab', ctx)), ctx), [['A', 'B'], '']);
26
+ assert.deepStrictEqual(inspect(parser(input('ba', ctx)), ctx), undefined);
27
+ assert.deepStrictEqual(inspect(parser(input('aab', ctx)), ctx), undefined);
28
+ assert.deepStrictEqual(inspect(parser(input('abb', ctx)), ctx), [['A', 'B'], 'b']);
29
+ assert.deepStrictEqual(inspect(parser(input('bba', ctx)), ctx), undefined);
29
30
  });
30
31
 
31
32
  });
@@ -1,28 +1,27 @@
1
- import { Parser, Ctx, Node, Context, SubParsers, SubNode, eval, exec, check } from '../parser';
1
+ import { Parser, CtxOptions, Node, Context, SubParsers, SubNode, eval } from '../parser';
2
2
  import { push } from 'spica/array';
3
3
 
4
- export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
5
- export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
4
+ export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[]) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
5
+ export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[]) => boolean): Parser<N, CtxOptions, D> {
6
6
  assert(parsers.every(f => f));
7
7
  if (parsers.length === 1) return parsers[0];
8
- return ({ source, context }) => {
9
- let rest = source;
8
+ return input => {
9
+ const { context } = input;
10
+ const { source, position } = context;
10
11
  let nodes: N[] | undefined;
11
12
  for (let len = parsers.length, i = 0; i < len; ++i) {
12
- if (rest === '') return;
13
- if (context.delimiters?.match(rest, context)) return;
14
- const result = parsers[i]({ source: rest, context });
15
- assert(check(rest, result, false));
13
+ if (context.position === source.length) return;
14
+ if (context.delimiters?.match(context)) return;
15
+ const result = parsers[i](input);
16
16
  if (result === undefined) return;
17
17
  nodes = nodes
18
18
  ? push(nodes, eval(result))
19
19
  : eval(result);
20
- rest = exec(result);
21
- if (resume?.(eval(result), exec(result)) === false) return;
20
+ if (resume?.(eval(result)) === false) return;
22
21
  }
23
- assert(rest.length <= source.length);
24
- return nodes && rest.length < source.length
25
- ? [nodes, rest]
22
+ assert(context.position >= position);
23
+ return nodes && context.position > position
24
+ ? [nodes]
26
25
  : undefined;
27
26
  };
28
27
  }
@@ -1,35 +1,36 @@
1
- import { Parser } from '../parser';
1
+ import { Parser, input } from '../parser';
2
2
  import { union } from './union';
3
3
  import { some } from './some';
4
4
  import { inspect } from '../../../debug.test';
5
5
 
6
6
  describe('Unit: combinator/data/parser/some', () => {
7
7
  describe('some', () => {
8
- const a: Parser<string, never> = ({ source }) => {
9
- return source && source[0] === 'a'
10
- ? [['A'], source.slice(1)]
8
+ const a: Parser<string> = ({ context }) => {
9
+ return context.source[context.position] === 'a'
10
+ ? void ++context.position || [['A']]
11
11
  : undefined;
12
12
  };
13
- const b: Parser<string, never> = ({ source }) => {
14
- return source && source[0] === 'b'
15
- ? [['B'], source.slice(1)]
13
+ const b: Parser<string> = ({ context }) => {
14
+ return context.source[context.position] === 'b'
15
+ ? void ++context.position || [['B']]
16
16
  : undefined;
17
17
  };
18
18
  const ab = union<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
19
+ const { context: ctx } = input('', {});
19
20
 
20
21
  it('basic', () => {
21
22
  const parser = some(ab, /^aaa/);
22
- assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
23
- assert.deepStrictEqual(inspect(parser({ source: 'a', context: {} })), [['A'], '']);
24
- assert.deepStrictEqual(inspect(parser({ source: 'b', context: {} })), [['B'], '']);
25
- assert.deepStrictEqual(inspect(parser({ source: 'ab', context: {} })), [['A', 'B'], '']);
26
- assert.deepStrictEqual(inspect(parser({ source: 'ba', context: {} })), [['B', 'A'], '']);
27
- assert.deepStrictEqual(inspect(parser({ source: 'aab', context: {} })), [['A', 'A', 'B'], '']);
28
- assert.deepStrictEqual(inspect(parser({ source: 'bba', context: {} })), [['B', 'B', 'A'], '']);
29
- assert.deepStrictEqual(inspect(parser({ source: 'aaa', context: {} })), undefined);
30
- assert.deepStrictEqual(inspect(parser({ source: 'bbb', context: {} })), [['B', 'B', 'B'], '']);
31
- assert.deepStrictEqual(inspect(parser({ source: 'aaab', context: {} })), undefined);
32
- assert.deepStrictEqual(inspect(parser({ source: 'baaa', context: {} })), [['B'], 'aaa']);
23
+ assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
24
+ assert.deepStrictEqual(inspect(parser(input('a', ctx)), ctx), [['A'], '']);
25
+ assert.deepStrictEqual(inspect(parser(input('b', ctx)), ctx), [['B'], '']);
26
+ assert.deepStrictEqual(inspect(parser(input('ab', ctx)), ctx), [['A', 'B'], '']);
27
+ assert.deepStrictEqual(inspect(parser(input('ba', ctx)), ctx), [['B', 'A'], '']);
28
+ assert.deepStrictEqual(inspect(parser(input('aab', ctx)), ctx), [['A', 'A', 'B'], '']);
29
+ assert.deepStrictEqual(inspect(parser(input('bba', ctx)), ctx), [['B', 'B', 'A'], '']);
30
+ assert.deepStrictEqual(inspect(parser(input('aaa', ctx)), ctx), undefined);
31
+ assert.deepStrictEqual(inspect(parser(input('bbb', ctx)), ctx), [['B', 'B', 'B'], '']);
32
+ assert.deepStrictEqual(inspect(parser(input('aaab', ctx)), ctx), undefined);
33
+ assert.deepStrictEqual(inspect(parser(input('baaa', ctx)), ctx), [['B'], 'aaa']);
33
34
  });
34
35
 
35
36
  });
@@ -1,4 +1,4 @@
1
- import { Parser, eval, exec, check } from '../parser';
1
+ import { Parser, eval } from '../parser';
2
2
  import { Delimiters } from './context/delimiter';
3
3
  import { unshift, push } from 'spica/array';
4
4
 
@@ -17,36 +17,34 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
17
17
  precedence,
18
18
  linebreakable,
19
19
  }));
20
- return ({ source, context }) => {
21
- if (source === '') return;
22
- assert(context.backtracks ??= {});
23
- let rest = source;
20
+ return input => {
21
+ const { context } = input;
22
+ const { source, position } = context;
23
+ //assert(context.backtracks ??= {});
24
24
  let nodes: N[] | undefined;
25
25
  if (delims.length > 0) {
26
26
  context.delimiters ??= new Delimiters();
27
27
  context.delimiters.push(delims);
28
28
  }
29
29
  while (true) {
30
- if (rest === '') break;
31
- if (match(rest)) break;
32
- if (context.delimiters?.match(rest, context)) break;
33
- const result = parser({ source: rest, context });
34
- assert.doesNotThrow(() => limit === 0 && check(rest, result));
30
+ if (context.position === source.length) break;
31
+ if (match(context)) break;
32
+ if (context.delimiters?.match(context)) break;
33
+ const result = parser(input);
35
34
  if (result === undefined) break;
36
35
  nodes = nodes
37
36
  ? nodes.length < eval(result).length / 8
38
37
  ? unshift(nodes, eval(result))
39
38
  : push(nodes, eval(result))
40
39
  : eval(result);
41
- rest = exec(result);
42
- if (limit > 0 && source.length - rest.length > limit) break;
40
+ if (limit > 0 && context.position - position > limit) break;
43
41
  }
44
42
  if (delims.length > 0) {
45
43
  context.delimiters!.pop(delims.length);
46
44
  }
47
- assert(rest.length <= source.length);
48
- return nodes && rest.length < source.length
49
- ? [nodes, rest]
45
+ assert(context.position >= position);
46
+ return nodes && context.position > position
47
+ ? [nodes]
50
48
  : undefined;
51
49
  };
52
50
  }
@@ -1,39 +1,40 @@
1
- import { Parser } from '../parser';
1
+ import { Parser, input } from '../parser';
2
2
  import { subsequence } from './subsequence';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
5
5
  describe('Unit: combinator/data/parser/subsequence', () => {
6
6
  describe('subsequence', () => {
7
- const a: Parser<string, never> = ({ source }) => {
8
- return source && source[0] === 'a'
9
- ? [['A'], source.slice(1)]
7
+ const a: Parser<string> = ({ context }) => {
8
+ return context.source[context.position] === 'a'
9
+ ? void ++context.position || [['A']]
10
10
  : undefined;
11
11
  };
12
- const b: Parser<string, never> = ({ source }) => {
13
- return source && source[0] === 'b'
14
- ? [['B'], source.slice(1)]
12
+ const b: Parser<string> = ({ context }) => {
13
+ return context.source[context.position] === 'b'
14
+ ? void ++context.position || [['B']]
15
15
  : undefined;
16
16
  };
17
- const c: Parser<string, never> = ({ source }) => {
18
- return source && source[0] === 'c'
19
- ? [['C'], source.slice(1)]
17
+ const c: Parser<string> = ({ context }) => {
18
+ return context.source[context.position] === 'c'
19
+ ? void ++context.position || [['C']]
20
20
  : undefined;
21
21
  };
22
22
  const abc = subsequence<Parser<string, {}, [typeof a, typeof b, typeof c]>>([a, b, c]);
23
+ const { context: ctx } = input('', {});
23
24
 
24
25
  it('basic', () => {
25
26
  const parser = abc;
26
- assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
27
- assert.deepStrictEqual(inspect(parser({ source: 'a', context: {} })), [['A'], '']);
28
- assert.deepStrictEqual(inspect(parser({ source: 'b', context: {} })), [['B'], '']);
29
- assert.deepStrictEqual(inspect(parser({ source: 'c', context: {} })), [['C'], '']);
30
- assert.deepStrictEqual(inspect(parser({ source: 'ab', context: {} })), [['A', 'B'], '']);
31
- assert.deepStrictEqual(inspect(parser({ source: 'ba', context: {} })), [['B'], 'a']);
32
- assert.deepStrictEqual(inspect(parser({ source: 'aab', context: {} })), [['A'], 'ab']);
33
- assert.deepStrictEqual(inspect(parser({ source: 'abb', context: {} })), [['A', 'B'], 'b']);
34
- assert.deepStrictEqual(inspect(parser({ source: 'bba', context: {} })), [['B'], 'ba']);
35
- assert.deepStrictEqual(inspect(parser({ source: 'ac', context: {} })), [['A', 'C'], '']);
36
- assert.deepStrictEqual(inspect(parser({ source: 'bc', context: {} })), [['B', 'C'], '']);
27
+ assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
28
+ assert.deepStrictEqual(inspect(parser(input('a', ctx)), ctx), [['A'], '']);
29
+ assert.deepStrictEqual(inspect(parser(input('b', ctx)), ctx), [['B'], '']);
30
+ assert.deepStrictEqual(inspect(parser(input('c', ctx)), ctx), [['C'], '']);
31
+ assert.deepStrictEqual(inspect(parser(input('ab', ctx)), ctx), [['A', 'B'], '']);
32
+ assert.deepStrictEqual(inspect(parser(input('ba', ctx)), ctx), [['B'], 'a']);
33
+ assert.deepStrictEqual(inspect(parser(input('aab', ctx)), ctx), [['A'], 'ab']);
34
+ assert.deepStrictEqual(inspect(parser(input('abb', ctx)), ctx), [['A', 'B'], 'b']);
35
+ assert.deepStrictEqual(inspect(parser(input('bba', ctx)), ctx), [['B'], 'ba']);
36
+ assert.deepStrictEqual(inspect(parser(input('ac', ctx)), ctx), [['A', 'C'], '']);
37
+ assert.deepStrictEqual(inspect(parser(input('bc', ctx)), ctx), [['B', 'C'], '']);
37
38
  });
38
39
 
39
40
  });
@@ -1,9 +1,9 @@
1
- import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
1
+ import { Parser, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
2
2
  import { union } from './union';
3
3
  import { inits } from './inits';
4
4
 
5
- export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
- export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
5
+ export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[]) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
+ export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[]) => boolean): Parser<N, CtxOptions, D> {
7
7
  assert(parsers.every(f => f));
8
8
  return union(
9
9
  parsers.map((_, i) =>
@@ -1,8 +1,8 @@
1
- import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
1
+ import { Parser, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
2
2
  import { union } from './union';
3
3
  import { sequence } from './sequence';
4
4
 
5
- export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
- export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
5
+ export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[]) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
+ export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[]) => boolean): Parser<N, CtxOptions, D> {
7
7
  return union(parsers.map((_, i) => sequence(parsers.slice(i), resume)) as D);
8
8
  }
@@ -1,31 +1,32 @@
1
- import { Parser } from '../parser';
1
+ import { Parser, input } from '../parser';
2
2
  import { union } from './union';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
5
5
  describe('Unit: combinator/data/parser/union', () => {
6
6
  describe('union', () => {
7
- const a: Parser<string, never> = ({ source }) => {
8
- return source && source[0] === 'a'
9
- ? [['A'], source.slice(1)]
7
+ const a: Parser<string> = ({ context }) => {
8
+ return context.source[context.position] === 'a'
9
+ ? void ++context.position || [['A']]
10
10
  : undefined;
11
11
  };
12
- const b: Parser<string, never> = ({ source }) => {
13
- return source && source[0] === 'b'
14
- ? [['B'], source.slice(1)]
12
+ const b: Parser<string> = ({ context }) => {
13
+ return context.source[context.position] === 'b'
14
+ ? void ++context.position || [['B']]
15
15
  : undefined;
16
16
  };
17
17
  const ab = union<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
18
+ const { context: ctx } = input('', {});
18
19
 
19
20
  it('basic', () => {
20
21
  const parser = ab;
21
- assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
22
- assert.deepStrictEqual(inspect(parser({ source: 'a', context: {} })), [['A'], '']);
23
- assert.deepStrictEqual(inspect(parser({ source: 'b', context: {} })), [['B'], '']);
24
- assert.deepStrictEqual(inspect(parser({ source: 'ab', context: {} })), [['A'], 'b']);
25
- assert.deepStrictEqual(inspect(parser({ source: 'ba', context: {} })), [['B'], 'a']);
26
- assert.deepStrictEqual(inspect(parser({ source: 'aab', context: {} })), [['A'], 'ab']);
27
- assert.deepStrictEqual(inspect(parser({ source: 'abb', context: {} })), [['A'], 'bb']);
28
- assert.deepStrictEqual(inspect(parser({ source: 'bba', context: {} })), [['B'], 'ba']);
22
+ assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
23
+ assert.deepStrictEqual(inspect(parser(input('a', ctx)), ctx), [['A'], '']);
24
+ assert.deepStrictEqual(inspect(parser(input('b', ctx)), ctx), [['B'], '']);
25
+ assert.deepStrictEqual(inspect(parser(input('ab', ctx)), ctx), [['A'], 'b']);
26
+ assert.deepStrictEqual(inspect(parser(input('ba', ctx)), ctx), [['B'], 'a']);
27
+ assert.deepStrictEqual(inspect(parser(input('aab', ctx)), ctx), [['A'], 'ab']);
28
+ assert.deepStrictEqual(inspect(parser(input('abb', ctx)), ctx), [['A'], 'bb']);
29
+ assert.deepStrictEqual(inspect(parser(input('bba', ctx)), ctx), [['B'], 'ba']);
29
30
  });
30
31
 
31
32
  });
@@ -1,7 +1,7 @@
1
- import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
1
+ import { Parser, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
2
2
 
3
3
  export function union<P extends Parser<unknown>>(parsers: SubParsers<P>): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
4
- export function union<N, D extends Parser<N>[]>(parsers: D): Parser<N, Ctx, D> {
4
+ export function union<N, D extends Parser<N>[]>(parsers: D): Parser<N, CtxOptions, D> {
5
5
  assert(parsers.every(f => f));
6
6
  switch (parsers.length) {
7
7
  case 0:
@@ -1,16 +1,20 @@
1
1
  import { Delimiters } from './parser/context/delimiter';
2
+ import { MarkdownParser } from '../../../markdown';
2
3
 
3
- export type Parser<N, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
4
- = (input: Input<C>) => Result<N, C, D>;
5
- export interface Input<C extends Ctx = Ctx> {
6
- readonly source: string;
7
- readonly context: C;
8
- }
9
- export type Result<N, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
10
- = readonly [N[], string, C, D]
11
- | readonly [N[], string]
4
+ export type Parser<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
5
+ = (input: Input<C & Ctx>) => Result<N, C, D>;
6
+ export interface Input<C extends CtxOptions = CtxOptions> {
7
+ readonly context: C & Ctx;
8
+ }
9
+ export type Result<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
10
+ = readonly [N[], C, D]
11
+ | readonly [N[]]
12
12
  | undefined;
13
- export interface Ctx {
13
+ export interface Ctx extends CtxOptions {
14
+ source: string;
15
+ position: number;
16
+ }
17
+ export interface CtxOptions {
14
18
  readonly resources?: {
15
19
  clock: number;
16
20
  recursions: number[];
@@ -24,16 +28,52 @@ export interface Ctx {
24
28
  // AVL木が適当と思われる。
25
29
  backtracks?: Record<number, number>;
26
30
  linebreak?: number;
27
- recent?: string[];
31
+ range?: number;
28
32
  }
29
33
  export type Node<P extends Parser<unknown>> = P extends Parser<infer N> ? N : never;
30
- export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, Ctx, infer D> ? D : never;
31
- export type Context<P extends Parser<unknown>> = P extends Parser<unknown, infer C> ? C : never;
34
+ export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, CtxOptions, infer D> ? D : never;
35
+ export type Context<P extends Parser<unknown>> = P extends Parser<unknown, infer C> ? C & Ctx : never;
32
36
  export type SubNode<P extends Parser<unknown>> = ExtractSubNode<SubParsers<P>>;
33
37
  export type IntermediateParser<P extends Parser<unknown>> = Parser<SubNode<P>, Context<P>, SubParsers<P>>;
34
38
  type ExtractSubNode<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer N ? N extends Parser<infer U> ? U : never : never;
35
39
  type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
36
40
 
41
+ export function input(source: string, context: CtxOptions): Input<Ctx>;
42
+ export function input(source: string, context: MarkdownParser.Options): Input<MarkdownParser.Context>;
43
+ export function input(source: string, context: CtxOptions): Input<Ctx> {
44
+ // @ts-expect-error
45
+ context.source = source;
46
+ // @ts-expect-error
47
+ context.position = 0;
48
+ return {
49
+ source,
50
+ // @ts-expect-error
51
+ context,
52
+ };
53
+ }
54
+
55
+ export function subinput<C extends Ctx>(source: string, context: C): Input<C> {
56
+ return {
57
+ context: {
58
+ ...context,
59
+ source,
60
+ position: 0,
61
+ offset: undefined,
62
+ backtracks: {},
63
+ }
64
+ };
65
+ }
66
+
67
+ export function clean<C extends Ctx>(context: C): C {
68
+ const { source, position } = context;
69
+ for (const p of Object.keys(context)) {
70
+ context[p] = undefined;
71
+ }
72
+ context.source = source;
73
+ context.position = position;
74
+ return context;
75
+ }
76
+
37
77
  export { eval_ as eval };
38
78
  function eval_<N>(result: NonNullable<Result<N>>, default_?: N[]): N[];
39
79
  function eval_<N>(result: Result<N>, default_: N[]): N[];
@@ -44,19 +84,17 @@ function eval_<N>(result: Result<N>, default_?: N[]): N[] | undefined {
44
84
  : default_;
45
85
  }
46
86
 
47
- export function exec(result: NonNullable<Result<unknown>>, default_?: ''): string;
48
- export function exec(result: Result<unknown>, default_: ''): string
49
- export function exec(result: Result<unknown>, default_?: undefined): string | undefined;
50
- export function exec(result: Result<unknown>, default_?: string): string | undefined {
51
- return result
52
- ? result[1]
53
- : default_;
54
- }
55
-
56
- export function check(source: string, result: Result<unknown>, mustConsume = true): true {
57
- assert.doesNotThrow(() => {
58
- if (source.length > 1000) return;
59
- if (source.slice(+mustConsume).slice(-exec(result, '').length || source.length) !== exec(result, '')) throw new Error();
60
- });
61
- return true;
87
+ export function failsafe<P extends Parser<unknown>>(parser: P): P;
88
+ export function failsafe<N>(parser: Parser<N>): Parser<N> {
89
+ assert(parser);
90
+ return input => {
91
+ const { context } = input;
92
+ const { source, position } = context;
93
+ const result = parser(input);
94
+ if (result === undefined) {
95
+ context.source = source;
96
+ context.position = position;
97
+ }
98
+ return result;
99
+ };
62
100
  }
package/src/debug.test.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Result, eval, exec } from './combinator/data/parser';
1
+ import { Result, Ctx, eval } from './combinator/data/parser';
2
2
  import { html, define } from 'typed-dom/dom';
3
3
  import { querySelectorWith, querySelectorAllWith } from 'typed-dom/query';
4
4
 
5
- export function inspect(result: Result<HTMLElement | string>, until: number | string = Infinity): Result<string> {
5
+ export function inspect(result: Result<HTMLElement | string>, ctx: Ctx, until: number | string = Infinity): [string[], string] | undefined {
6
6
  return result && [
7
7
  eval(result).map(node => {
8
8
  assert(node);
@@ -33,7 +33,7 @@ export function inspect(result: Result<HTMLElement | string>, until: number | st
33
33
  }
34
34
  return normalize(node.outerHTML.slice(0, until));
35
35
  }),
36
- exec(result)
36
+ ctx.source.slice(ctx.position),
37
37
  ];
38
38
  }
39
39
 
@@ -1,6 +1,6 @@
1
1
  import { ParserSettings, Progress } from '../../..';
2
2
  import { MarkdownParser } from '../../../markdown';
3
- import { eval } from '../../combinator/data/parser';
3
+ import { input, eval } from '../../combinator/data/parser';
4
4
  import { segment, validate, MAX_INPUT_SIZE } from '../segment';
5
5
  import { header } from '../header';
6
6
  import { block } from '../block';
@@ -19,7 +19,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
19
19
  nearest: (position: number) => HTMLElement | undefined;
20
20
  index: (block: HTMLElement) => number;
21
21
  } {
22
- let context: MarkdownParser.Context = {
22
+ let context: MarkdownParser.Options = {
23
23
  ...settings,
24
24
  host: settings.host ?? new ReadonlyURL(location.pathname, location.origin),
25
25
  };
@@ -78,7 +78,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
78
78
  for (; index < sourceSegments.length - last; ++index) {
79
79
  assert(rev === revision);
80
80
  const seg = sourceSegments[index];
81
- const es = eval(header({ source: seg, context: { header: index === 0 } }) || block({ source: seg, context }), []);
81
+ const es = eval(header(input(seg, { header: index === 0 })) || block(input(seg, context)), []);
82
82
  blocks.splice(index, 0, [seg, es, url]);
83
83
  if (es.length === 0) continue;
84
84
  // All deletion processes always run after all addition processes have done.
@@ -1,9 +1,9 @@
1
- import { eval, exec } from '../../combinator/data/parser';
1
+ import { input, eval } from '../../combinator/data/parser';
2
2
  import { header as h } from '../header';
3
3
 
4
4
  export function header(source: string): string {
5
- const [, rest = source] = parse(source);
6
- return source.slice(0, source.length - rest.length);
5
+ const [, pos = 0] = parse(source);
6
+ return source.slice(0, pos);
7
7
  }
8
8
 
9
9
  export function headers(source: string): string[] {
@@ -11,10 +11,11 @@ export function headers(source: string): string[] {
11
11
  return el?.textContent!.trimEnd().slice(el.firstChild!.firstChild!.textContent!.length).split('\n') ?? [];
12
12
  }
13
13
 
14
- function parse(source: string): [HTMLElement, string] | [] {
15
- const result = h({ source, context: {} });
14
+ function parse(source: string): [HTMLElement, number] | [] {
15
+ const i = input(source, {});
16
+ const result = h(i);
16
17
  const [el] = eval(result, []);
17
18
  return el?.tagName === 'ASIDE'
18
- ? [el, exec(result!)]
19
+ ? [el, i.context.position]
19
20
  : [];
20
21
  }
@@ -1,4 +1,4 @@
1
- import { eval } from '../../combinator/data/parser';
1
+ import { input, eval } from '../../combinator/data/parser';
2
2
  import { unsafehtmlentity } from '../inline/htmlentity';
3
3
 
4
4
  const UNICODE_REPLACEMENT_CHARACTER = '\uFFFD';
@@ -60,7 +60,7 @@ export const invisibleHTMLEntityNames = [
60
60
  ] as const;
61
61
  const unreadableHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames.slice(2);
62
62
  const unreadableEscapableCharacters = unreadableHTMLEntityNames
63
- .map(name => eval(unsafehtmlentity({ source: `&${name};`, context: {} }))![0]);
63
+ .map(name => eval(unsafehtmlentity(input(`&${name};`, {})))![0]);
64
64
  assert(unreadableEscapableCharacters.length === unreadableHTMLEntityNames.length);
65
65
  assert(unreadableEscapableCharacters.every(c => c.length === 1));
66
66
  const unreadableEscapableCharacter = new RegExp(`[${