securemark 0.291.0 → 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 +8 -0
  2. package/design.md +13 -2
  3. package/dist/index.js +1101 -760
  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 +21 -10
  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 +49 -40
  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 +29 -32
  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,32 +1,32 @@
1
- import { Parser, Context, eval, exec, check } from '../../data/parser';
1
+ import { Parser, Context, input, eval, failsafe } from '../../data/parser';
2
2
  import { consume } from '../../../combinator';
3
3
 
4
4
  export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P, cost?: boolean): P;
5
5
  export function focus<N>(scope: string | RegExp, parser: Parser<N>, cost = true): Parser<N> {
6
6
  assert(scope instanceof RegExp ? !scope.flags.match(/[gmy]/) && scope.source.startsWith('^') : scope);
7
7
  assert(parser);
8
- const match: (source: string) => string = typeof scope === 'string'
9
- ? source => source.slice(0, scope.length) === scope ? scope : ''
10
- : source => source.match(scope)?.[0] ?? '';
11
- return ({ source, context }) => {
12
- if (source === '') return;
13
- const src = match(source);
14
- assert(source.startsWith(src));
8
+ const match: (source: string, position: number) => string = typeof scope === 'string'
9
+ ? (source, position) => source.startsWith(scope, position) ? scope : ''
10
+ : (source, position) => source.slice(position).match(scope)?.[0] ?? '';
11
+ return failsafe(({ context }) => {
12
+ const { source, position } = context;
13
+ if (position === source.length) return;
14
+ const src = match(source, position);
15
+ assert(source.startsWith(src, position));
15
16
  if (src === '') return;
16
17
  cost && consume(src.length, context);
17
- const offset = source.length - src.length;
18
- assert(offset >= 0);
18
+ context.range = src.length;
19
19
  context.offset ??= 0;
20
- context.offset += offset;
21
- const result = parser({ source: src, context });
22
- assert(check(src, result));
23
- context.offset -= offset;
20
+ context.offset += position;
21
+ const result = parser(input(src, context));
22
+ context.position += position;
23
+ context.position += result && context.position === position ? src.length : 0;
24
+ assert(context.position > position || !result);
25
+ context.source = source;
26
+ context.offset -= position;
24
27
  if (result === undefined) return;
25
- assert(exec(result).length < src.length);
26
- return exec(result).length < src.length
27
- ? [eval(result), exec(result) + source.slice(src.length)]
28
- : undefined;
29
- };
28
+ return [eval(result)];
29
+ });
30
30
  }
31
31
 
32
32
  //export function rewrite<N, C extends Ctx, D extends Parser<unknown, C>[]>(scope: Parser<unknown, C, D>, parser: Parser<N, C, never>): Parser<N, C, D>;
@@ -34,30 +34,29 @@ export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Contex
34
34
  export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>): Parser<N> {
35
35
  assert(scope);
36
36
  assert(parser);
37
- return input => {
38
- const { source, context } = input;
39
- if (source === '') return;
37
+ return failsafe(({ context }) => {
38
+ const { source, position } = context;
39
+ if (position === source.length) return;
40
40
  // 影響する使用はないはず
41
41
  //const { backtracks } = context;
42
42
  //context.backtracks = {};
43
- const res1 = scope(input);
44
- assert(check(source, res1));
43
+ const res1 = scope({ context });
44
+ assert(context.position > position || !res1);
45
45
  //context.backtracks = backtracks;
46
- if (res1 === undefined || exec(res1).length >= source.length) return;
47
- const src = source.slice(0, source.length - exec(res1).length);
46
+ if (res1 === undefined || context.position < position) return;
47
+ const src = source.slice(position, context.position);
48
48
  assert(src !== '');
49
- assert(source.startsWith(src));
50
- const offset = source.length - src.length;
51
- assert(offset >= 0);
49
+ assert(source.startsWith(src, position));
52
50
  context.offset ??= 0;
53
- context.offset += offset;
54
- const res2 = parser({ source: src, context });
55
- assert(check(src, res2));
56
- context.offset -= offset;
51
+ context.offset += position;
52
+ const res2 = parser(input(src, context));
53
+ context.position += position;
54
+ context.position += res2 && context.position === position ? src.length : 0;
55
+ assert(context.position > position || !res2);
56
+ context.source = source;
57
+ context.offset -= position;
57
58
  if (res2 === undefined) return;
58
- assert(exec(res2) === '');
59
- return exec(res2).length < src.length
60
- ? [eval(res2), exec(res2) + exec(res1)]
61
- : undefined;
62
- };
59
+ assert(context.position === position + src.length);
60
+ return [eval(res2)];
61
+ });
63
62
  }
@@ -1,40 +1,40 @@
1
- import { Parser, Input, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, exec, check } from '../../data/parser';
1
+ import { Parser, Input, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
2
2
  import { consume } from '../../../combinator';
3
3
  import { unshift, push } from 'spica/array';
4
4
 
5
5
  export function surround<P extends Parser<unknown>, S = string>(
6
6
  opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
7
7
  optional?: false,
8
- f?: (rss: [S[], SubNode<P>[], S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
9
- g?: (rss: [S[], SubNode<P>[], string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
8
+ f?: (rss: [S[], SubNode<P>[], S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
9
+ g?: (rss: [S[], SubNode<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
10
10
  backtracks?: readonly number[],
11
11
  ): P;
12
12
  export function surround<P extends Parser<unknown>, S = string>(
13
13
  opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
14
14
  optional?: boolean,
15
- f?: (rss: [S[], SubNode<P>[] | undefined, S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
16
- g?: (rss: [S[], SubNode<P>[] | undefined, string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
15
+ f?: (rss: [S[], SubNode<P>[] | undefined, S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
16
+ g?: (rss: [S[], SubNode<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
17
17
  backtracks?: readonly number[],
18
18
  ): P;
19
19
  export function surround<P extends Parser<unknown>, S = string>(
20
20
  opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
21
21
  optional?: false,
22
- f?: (rss: [S[], Node<P>[], S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
23
- g?: (rss: [S[], Node<P>[], string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
22
+ f?: (rss: [S[], Node<P>[], S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
23
+ g?: (rss: [S[], Node<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
24
24
  backtracks?: readonly number[],
25
25
  ): P;
26
26
  export function surround<P extends Parser<unknown>, S = string>(
27
27
  opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
28
28
  optional?: boolean,
29
- f?: (rss: [S[], Node<P>[] | undefined, S[]], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
30
- g?: (rss: [S[], Node<P>[] | undefined, string], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
29
+ f?: (rss: [S[], Node<P>[] | undefined, S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
30
+ g?: (rss: [S[], Node<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
31
31
  backtracks?: readonly number[],
32
32
  ): P;
33
33
  export function surround<N>(
34
34
  opener: string | RegExp | Parser<N>, parser: Parser<N>, closer: string | RegExp | Parser<N>,
35
35
  optional: boolean = false,
36
- f?: (rss: [N[], N[], N[]], rest: string, context: Ctx) => Result<N>,
37
- g?: (rss: [N[], N[], string], rest: string, context: Ctx) => Result<N>,
36
+ f?: (rss: [N[], N[], N[]], context: Ctx) => Result<N>,
37
+ g?: (rss: [N[], N[] | undefined], context: Ctx) => Result<N>,
38
38
  backtracks: readonly number[] = [],
39
39
  ): Parser<N> {
40
40
  switch (typeof opener) {
@@ -47,40 +47,48 @@ export function surround<N>(
47
47
  case 'object':
48
48
  closer = match(closer);
49
49
  }
50
- return ({ source, context }) => {
51
- const sme_ = source;
52
- if (sme_ === '') return;
50
+ return failsafe(input => {
51
+ const { context } = input;
52
+ const { source, position } = context;
53
+ if (position === source.length) return;
53
54
  const { linebreak } = context;
54
55
  context.linebreak = 0;
55
- const resultS = opener({ source: sme_, context });
56
- assert(check(sme_, resultS, false));
57
- if (resultS === undefined) return void revert(context, linebreak);
58
- const nodesS = eval(resultS);
59
- const me_ = exec(resultS);
60
- if (isBacktrack(context, backtracks, sme_, sme_.length - me_.length)) return void revert(context, linebreak);
61
- const resultM = me_ !== '' ? parser({ source: me_, context }) : undefined;
62
- assert(check(me_, resultM));
56
+ const resultO = opener(input);
57
+ assert(context.position >= position);
58
+ const nodesO = eval(resultO);
59
+ if (!nodesO) {
60
+ return void revert(context, linebreak);
61
+ }
62
+ if (isBacktrack(context, backtracks, position, context.position - position || 1)) {
63
+ return void revert(context, linebreak);
64
+ }
65
+ const resultM = context.position < source.length ? parser(input) : undefined;
66
+ assert(context.position >= position);
67
+ context.range = context.position - position;
63
68
  const nodesM = eval(resultM);
64
- const e_ = exec(resultM) ?? me_;
65
- const resultE = nodesM || optional ? closer({ source: e_, context }) : undefined;
66
- assert(check(e_, resultE, false));
67
- const nodesE = eval(resultE);
68
- const rest = exec(resultE) ?? e_;
69
- nodesE || setBacktrack(context, backtracks, sme_.length);
70
- if (!nodesM && !optional) return void revert(context, linebreak);
71
- if (rest.length === sme_.length) return void revert(context, linebreak);
72
- context.recent = [
73
- sme_.slice(0, sme_.length - me_.length),
74
- me_.slice(0, me_.length - e_.length),
75
- e_.slice(0, e_.length - rest.length),
76
- ];
77
- const result = nodesE
78
- ? f
79
- ? f([nodesS, nodesM!, nodesE], rest, context)
80
- : [push(unshift(nodesS, nodesM ?? []), nodesE), rest] satisfies [N[], string]
81
- : g
82
- ? g([nodesS, nodesM!, me_], rest, context)
83
- : undefined;
69
+ if (!resultM && !optional) {
70
+ setBacktrack(context, backtracks, position);
71
+ const result = g?.([nodesO, nodesM], context);
72
+ revert(context, linebreak);
73
+ return result;
74
+ }
75
+ const resultC = resultM || optional ? closer(input) : undefined;
76
+ assert(context.position >= position);
77
+ context.range = context.position - position;
78
+ const nodesC = eval(resultC);
79
+ if (!nodesC) {
80
+ setBacktrack(context, backtracks, position);
81
+ const result = g?.([nodesO, nodesM], context);
82
+ revert(context, linebreak);
83
+ return result;
84
+ }
85
+ if (context.position === position) {
86
+ return void revert(context, linebreak);
87
+ }
88
+ context.range = context.position - position;
89
+ const result = f
90
+ ? f([nodesO, nodesM!, nodesC], context)
91
+ : [push(nodesM ? unshift(nodesO, nodesM) : nodesO, nodesC)] satisfies [N[]];
84
92
  if (result) {
85
93
  context.linebreak ||= linebreak;
86
94
  }
@@ -88,7 +96,7 @@ export function surround<N>(
88
96
  revert(context, linebreak);
89
97
  }
90
98
  return result;
91
- };
99
+ });
92
100
  }
93
101
  export function open<P extends Parser<unknown>>(
94
102
  opener: string | RegExp | Parser<Node<P>, Context<P>>,
@@ -97,7 +105,7 @@ export function open<P extends Parser<unknown>>(
97
105
  backtracks?: readonly number[],
98
106
  ): P;
99
107
  export function open<N>(
100
- opener: string | RegExp | Parser<N>,
108
+ opener: string | RegExp | Parser<N, Ctx>,
101
109
  parser: Parser<N>,
102
110
  optional?: boolean,
103
111
  backtracks?: readonly number[],
@@ -112,7 +120,7 @@ export function close<P extends Parser<unknown>>(
112
120
  ): P;
113
121
  export function close<N>(
114
122
  parser: Parser<N>,
115
- closer: string | RegExp | Parser<N>,
123
+ closer: string | RegExp | Parser<N, Ctx>,
116
124
  optional?: boolean,
117
125
  backtracks?: readonly number[],
118
126
  ): Parser<N> {
@@ -123,18 +131,19 @@ const statesize = 2;
123
131
  export function isBacktrack(
124
132
  context: Ctx,
125
133
  backtracks: readonly number[],
126
- source: string,
134
+ position: number = context.position,
127
135
  length: number = 1,
128
136
  ): boolean {
129
- if (length === 0 || source.length === 0) return false;
137
+ const { source } = context;
138
+ if (position === source.length) return false;
139
+ if (length === 0) return false;
130
140
  for (const backtrack of backtracks) {
131
141
  if (backtrack & 1) {
132
142
  const { backtracks = {}, offset = 0 } = context;
133
143
  for (let i = 0; i < length; ++i) {
134
- assert(i < source.length);
135
- if (source[i] !== source[0]) break;
136
- const pos = source.length - i + offset - 1;
137
- assert(pos >= 0);
144
+ if (position + i === source.length) break;
145
+ if (source[position + i] !== source[position + 0]) break;
146
+ const pos = position + i + offset;
138
147
  if (!(pos in backtracks)) continue;
139
148
  if (backtracks[pos] & 1 << size(backtrack >>> statesize)) return true;
140
149
  }
@@ -148,29 +157,38 @@ export function setBacktrack(
148
157
  position: number,
149
158
  length: number = 1,
150
159
  ): void {
151
- if (length === 0 || position === 0) return;
160
+ const { source } = context;
161
+ if (position === source.length) return;
162
+ if (length === 0) return;
152
163
  for (const backtrack of backtracks) {
153
- if (backtrack & 2 && position !== 0) {
164
+ if (backtrack & 2) {
154
165
  const { backtracks = {}, offset = 0 } = context;
155
166
  for (let i = 0; i < length; ++i) {
156
- const pos = position - i + offset - 1;
157
- assert(pos >= 0);
167
+ if (position + i === source.length) break;
168
+ const pos = position + i + offset;
158
169
  backtracks[pos] |= 1 << size(backtrack >>> statesize);
159
170
  }
160
171
  }
161
172
  }
162
173
  }
163
174
 
164
- function match(pattern: string | RegExp): (input: Input) => [never[], string] | undefined {
175
+ function match(pattern: string | RegExp): (input: Input) => Result<never> {
165
176
  switch (typeof pattern) {
166
177
  case 'string':
167
- return ({ source }) => source.slice(0, pattern.length) === pattern ? [[], source.slice(pattern.length)] : undefined;
178
+ return ({ context }) => {
179
+ const { source, position } = context;
180
+ if (!source.startsWith(pattern, position)) return;
181
+ context.position += pattern.length;
182
+ return [[]];
183
+ };
168
184
  case 'object':
169
- return ({ source, context }) => {
170
- const m = source.match(pattern);
185
+ return ({ context }) => {
186
+ const { source, position } = context;
187
+ const m = source.slice(position).match(pattern);
171
188
  if (m === null) return;
172
189
  consume(m[0].length, context);
173
- return [[], source.slice(m[0].length)];
190
+ context.position += m[0].length;
191
+ return [[]];
174
192
  };
175
193
  }
176
194
  }
@@ -1,18 +1,21 @@
1
1
  import { trim } from './trim';
2
+ import { input } from '../../data/parser';
2
3
  import { inspect } from '../../../debug.test';
3
4
 
4
5
  describe('Unit: combinator/trim', () => {
5
6
  describe('trim', () => {
7
+ const { context: ctx } = input('', {});
8
+
6
9
  it('', () => {
7
- const parser = trim(({ source }) => [[source], '']);
8
- assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
9
- assert.deepStrictEqual(inspect(parser({ source: 'a', context: {} })), [['a'], '']);
10
- assert.deepStrictEqual(inspect(parser({ source: 'a\n', context: {} })), [['a'], '']);
11
- assert.deepStrictEqual(inspect(parser({ source: 'a ', context: {} })), [['a'], '']);
12
- assert.deepStrictEqual(inspect(parser({ source: 'a \n', context: {} })), [['a'], '']);
13
- assert.deepStrictEqual(inspect(parser({ source: ' a', context: {} })), [['a'], '']);
14
- assert.deepStrictEqual(inspect(parser({ source: ' a ', context: {} })), [['a'], '']);
15
- assert.deepStrictEqual(inspect(parser({ source: ' a \n b \n', context: {} })), [['a \n b'], '']);
10
+ const parser = trim(({ context }) => { context.position = context.source.length; return [[context.source]]; });
11
+ assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
12
+ assert.deepStrictEqual(inspect(parser(input('a', ctx)), ctx), [['a'], '']);
13
+ assert.deepStrictEqual(inspect(parser(input('a\n', ctx)), ctx), [['a'], '']);
14
+ assert.deepStrictEqual(inspect(parser(input('a ', ctx)), ctx), [['a'], '']);
15
+ assert.deepStrictEqual(inspect(parser(input('a \n', ctx)), ctx), [['a'], '']);
16
+ assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
17
+ assert.deepStrictEqual(inspect(parser(input(' a ', ctx)), ctx), [['a'], '']);
18
+ assert.deepStrictEqual(inspect(parser(input(' a \n b \n', ctx)), ctx), [['a \n b'], '']);
16
19
  });
17
20
 
18
21
  });
@@ -1,24 +1,24 @@
1
- import { Parser, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, exec, check } from '../../data/parser';
1
+ import { Parser, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
2
2
 
3
- export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
4
- export function bind<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
5
- export function bind<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], rest: string, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
6
- export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], rest: string, context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
7
- export function bind<N, U>(parser: Parser<N>, f: (nodes: N[], rest: string, context: Ctx) => Result<U>): Parser<U> {
3
+ export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
4
+ export function bind<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
5
+ export function bind<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
6
+ export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
7
+ export function bind<N, U>(parser: Parser<N>, f: (nodes: N[], context: Ctx) => Result<U>): Parser<U> {
8
8
  assert(parser);
9
- return input => {
10
- const { source, context } = input;
11
- if (source === '') return;
9
+ return failsafe(input => {
10
+ const { context } = input;
11
+ const { source, position } = context;
12
+ if (position === source.length) return;
12
13
  const res1 = parser(input);
13
- assert(check(source, res1));
14
+ assert(context.position > position || !res1);
14
15
  if (res1 === undefined) return;
15
- context.recent = [source.slice(0, source.length - exec(res1).length)];
16
- const res2 = f(eval(res1), exec(res1), context);
17
- assert(check(source, res2));
18
- assert(check(exec(res1), res2, false));
16
+ context.range = context.position - position;
17
+ const res2 = f(eval(res1), context);
18
+ assert(context.position > position || !res2);
19
19
  if (res2 === undefined) return;
20
- return exec(res2).length <= exec(res1).length
20
+ return context.position > position
21
21
  ? res2
22
22
  : undefined;
23
- };
23
+ });
24
24
  }
@@ -1,10 +1,10 @@
1
1
  import { Parser, Ctx, SubParsers, Node, Context, IntermediateParser, SubNode } from '../../data/parser';
2
2
  import { bind } from './bind';
3
3
 
4
- export function fmap<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], rest: string, context: Context<P>) => Node<P>[]): P;
5
- export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], rest: string, context: Context<P>) => Node<P>[]): P;
6
- export function fmap<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], rest: string, context: Context<P>) => Node<P>[]): P;
7
- export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], rest: string, context: Context<P>) => U[]): Parser<U, Context<P>, SubParsers<P>>;
8
- export function fmap<N, U>(parser: Parser<N>, f: (nodes: N[], rest: string, context: Ctx) => U[]): Parser<U> {
9
- return bind(parser, (nodes, rest, context) => [f(nodes, rest, context), rest]);
4
+ export function fmap<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], context: Context<P>) => Node<P>[]): P;
5
+ export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Node<P>[]): P;
6
+ export function fmap<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], context: Context<P>) => Node<P>[]): P;
7
+ export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => U[]): Parser<U, Context<P>, SubParsers<P>>;
8
+ export function fmap<N, U>(parser: Parser<N>, f: (nodes: N[], context: Ctx) => U[]): Parser<U> {
9
+ return bind(parser, (nodes, context) => [f(nodes, context)]);
10
10
  }
@@ -4,7 +4,7 @@ interface Delimiter {
4
4
  readonly memory: Delimiter[];
5
5
  readonly index: number;
6
6
  readonly signature: number | string;
7
- readonly matcher: (source: string) => boolean | undefined;
7
+ readonly matcher: (context: Ctx) => boolean | undefined;
8
8
  readonly precedence: number;
9
9
  readonly linebreakable: boolean;
10
10
  state: boolean;
@@ -28,14 +28,14 @@ export class Delimiters {
28
28
  return `r/${pattern.source}/${+linebreakable}`;
29
29
  }
30
30
  }
31
- public static matcher(pattern: string | RegExp | undefined): (source: string) => true | undefined {
31
+ public static matcher(pattern: string | RegExp | undefined): (context: Ctx) => true | undefined {
32
32
  switch (typeof pattern) {
33
33
  case 'undefined':
34
34
  return () => undefined;
35
35
  case 'string':
36
- return source => source.slice(0, pattern.length) === pattern || undefined;
36
+ return ({ source, position }) => source.startsWith(pattern, position) || undefined;
37
37
  case 'object':
38
- return source => pattern.test(source) || undefined;
38
+ return ({ source, position }) => pattern.test(source.slice(position)) || undefined;
39
39
  }
40
40
  }
41
41
  private readonly tree: Record<number, Delimiter[]> = {};
@@ -58,7 +58,7 @@ export class Delimiters {
58
58
  public push(
59
59
  delims: readonly {
60
60
  readonly signature: number | string;
61
- readonly matcher: (source: string) => boolean | undefined;
61
+ readonly matcher: (context: Ctx) => boolean | undefined;
62
62
  readonly precedence: number;
63
63
  readonly linebreakable: boolean;
64
64
  }[]
@@ -132,12 +132,13 @@ export class Delimiters {
132
132
  delimiters[indexes[i]].state = true;
133
133
  }
134
134
  }
135
- public match(source: string, { precedence = 0, linebreak = 0 }: Ctx): boolean {
135
+ public match(context: Ctx): boolean {
136
+ const { precedence = 0, linebreak = 0 } = context;
136
137
  const { delimiters } = this;
137
138
  for (let i = delimiters.length; i--;) {
138
139
  const delimiter = delimiters[i];
139
140
  if (delimiter.precedence <= precedence || !delimiter.state) continue;
140
- switch (delimiter.matcher(source)) {
141
+ switch (delimiter.matcher(context)) {
141
142
  case true:
142
143
  if (!delimiter.linebreakable && linebreak > 0) return false;
143
144
  return true;
@@ -1,53 +1,58 @@
1
- import { Parser, Ctx } from '../parser';
1
+ import { Parser, Ctx, CtxOptions, input } from '../parser';
2
2
  import { some } from './some';
3
3
  import { reset, context } from './context';
4
4
  import { creation } from './context';
5
5
 
6
6
  describe('Unit: combinator/data/parser/context', () => {
7
- interface Context extends Ctx {
7
+ interface Context extends CtxOptions {
8
8
  status?: boolean;
9
9
  }
10
10
 
11
11
  describe('reset', () => {
12
12
  const parser: Parser<number> = some(creation(1,
13
- ({ source, context }) => [[context.resources?.clock ?? NaN], source.slice(1)]));
13
+ ({ context }) => {
14
+ context.position += 1;
15
+ return [[context.resources?.clock ?? NaN]];
16
+ }));
14
17
 
15
18
  it('root', () => {
16
19
  const base: Context = { resources: { clock: 3, recursions: [1] } };
17
20
  const ctx: Context = {};
18
- assert.deepStrictEqual(reset(base, parser)({ source: '123', context: ctx }), [[3, 2, 1], '']);
21
+ assert.deepStrictEqual(reset(base, parser)(input('123', ctx)), [[3, 2, 1]]);
19
22
  assert(base.resources?.clock === 3);
20
23
  assert(ctx.resources?.clock === undefined);
21
- assert.throws(() => reset(base, parser)({ source: '1234', context: ctx }));
22
- assert(ctx.resources?.clock === undefined);
23
- assert.deepStrictEqual(reset(base, parser)({ source: '123', context: ctx }), [[3, 2, 1], '']);
24
+ assert.throws(() => reset(base, parser)(input('1234', ctx)));
25
+ assert(ctx.resources?.clock === 0);
24
26
  });
25
27
 
26
28
  it('node', () => {
27
29
  const base: Context = { resources: { clock: 3, recursions: [1] } };
28
30
  const ctx: Context = { resources: { clock: 1, recursions: [1] } };
29
- assert.deepStrictEqual(reset(base, parser)({ source: '1', context: ctx }), [[1], '']);
31
+ assert.deepStrictEqual(reset(base, parser)(input('1', ctx)), [[1]]);
30
32
  assert(base.resources?.clock === 3);
31
33
  assert(ctx.resources?.clock === 0);
32
- assert.throws(() => reset(base, parser)({ source: '1', context: ctx }));
34
+ assert.throws(() => reset(base, parser)(input('1', ctx)));
33
35
  assert(ctx.resources?.clock === 0);
34
36
  });
35
37
 
36
38
  });
37
39
 
38
40
  describe('context', () => {
39
- const parser: Parser<boolean, Context> = some(creation(1,
40
- ({ source, context }) => [[context.status!], source.slice(1)]));
41
+ const parser: Parser<boolean, Context & Ctx> = some(creation(1,
42
+ ({ context }) => {
43
+ context.position += 1;
44
+ return [[context.status!]];
45
+ }));
41
46
 
42
47
  it('', () => {
43
48
  const base: Context = { status: true };
44
49
  const ctx: Context = { resources: { clock: 3, recursions: [1] } };
45
- assert.deepStrictEqual(context(base, parser)({ source: '123', context: ctx }), [[true, true, true], '']);
50
+ assert.deepStrictEqual(context(base, parser)(input('123', ctx)), [[true, true, true]]);
46
51
  assert(ctx.resources?.clock === 0);
47
52
  assert(ctx.status === undefined);
48
- assert.throws(() => reset(base, parser)({ source: '1', context: ctx }));
53
+ assert.throws(() => reset(base, parser)(input('1', ctx)));
49
54
  assert(ctx.resources?.clock === 0);
50
- assert(ctx.status === undefined);
55
+ assert(ctx.status === true);
51
56
  });
52
57
 
53
58
  });
@@ -1,57 +1,61 @@
1
- import { ObjectCreate, min } from 'spica/alias';
2
- import { Parser, Result, Ctx, Node, Context } from '../../data/parser';
1
+ import { min } from 'spica/alias';
2
+ import { Parser, Result, Ctx, CtxOptions, Node, Context } from '../../data/parser';
3
3
  import { clone } from 'spica/assign';
4
4
 
5
- export function reset<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
5
+ export function reset<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
6
6
  export function reset<N>(base: Ctx, parser: Parser<N>): Parser<N> {
7
7
  assert(Object.getPrototypeOf(base) === Object.prototype);
8
8
  assert(Object.freeze(base));
9
9
  const changes = Object.entries(base);
10
10
  const values = Array(changes.length);
11
- return ({ source, context }) =>
12
- apply(parser, source, ObjectCreate(context), changes, values, true);
11
+ return ({ context }) =>
12
+ apply(parser, context, changes, values, true);
13
13
  }
14
14
 
15
- export function context<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
15
+ export function context<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
16
16
  export function context<N>(base: Ctx, parser: Parser<N>): Parser<N> {
17
17
  assert(Object.getPrototypeOf(base) === Object.prototype);
18
18
  assert(Object.freeze(base));
19
19
  const changes = Object.entries(base);
20
20
  const values = Array(changes.length);
21
- return ({ source, context }) =>
22
- apply(parser, source, context, changes, values);
21
+ return ({ context }) =>
22
+ apply(parser, context, changes, values);
23
23
  }
24
24
 
25
- function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Node<P>>;
26
- function apply<N>(parser: Parser<N>, source: string, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<N> {
27
- if (reset) {
28
- context.backtracks = {};
29
- }
25
+ function apply<P extends Parser<unknown>>(parser: P, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Node<P>>;
26
+ function apply<N>(parser: Parser<N>, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<N> {
30
27
  for (let i = 0; i < changes.length; ++i) {
31
28
  const change = changes[i];
32
29
  const prop = change[0];
33
30
  switch (prop) {
31
+ case 'source':
32
+ case 'position':
33
+ continue;
34
34
  case 'resources':
35
35
  assert(reset);
36
36
  assert(!context.offset);
37
37
  assert(!context.precedence);
38
38
  assert(!context.delimiters);
39
39
  assert(!context.state);
40
+ values[i] = context[prop];
40
41
  context[prop as string] ??= clone({}, change[1] as object);
41
42
  continue;
43
+ case 'backtracks':
44
+ change[1] = {};
42
45
  }
43
46
  values[i] = context[prop];
44
47
  context[prop] = change[1];
45
48
  }
46
- const result = parser({ source, context });
49
+ const result = parser({ context });
47
50
  for (let i = 0; i < changes.length; ++i) {
48
51
  const change = changes[i];
49
52
  const prop = change[0];
50
53
  switch (prop) {
54
+ case 'source':
55
+ case 'position':
56
+ continue;
51
57
  case 'resources':
52
58
  assert(reset);
53
- // プロトタイプに戻ることで戻す
54
- continue;
55
59
  }
56
60
  context[prop] = values[i];
57
61
  values[i] = undefined;