securemark 0.299.4 → 0.300.1

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 (233) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/design.md +0 -6
  3. package/dist/index.js +2847 -1993
  4. package/index.d.ts +2 -1
  5. package/markdown.d.ts +209 -183
  6. package/package.json +1 -1
  7. package/src/api/bind.test.ts +0 -22
  8. package/src/api/bind.ts +15 -10
  9. package/src/api/header.ts +8 -5
  10. package/src/api/parse.test.ts +118 -122
  11. package/src/api/parse.ts +13 -31
  12. package/src/api/run.ts +6 -0
  13. package/src/api.ts +1 -0
  14. package/src/combinator/control/inits.ts +35 -0
  15. package/src/combinator/control/sequence.test.ts +38 -0
  16. package/src/combinator/control/sequence.ts +19 -0
  17. package/src/combinator/control/some.test.ts +41 -0
  18. package/src/combinator/{data/parser → control}/some.ts +39 -26
  19. package/src/combinator/control/state.ts +33 -0
  20. package/src/combinator/control/subsequence.test.ts +47 -0
  21. package/src/combinator/control/subsequence.ts +16 -0
  22. package/src/combinator/control/tails.ts +8 -0
  23. package/src/combinator/control/union.test.ts +37 -0
  24. package/src/combinator/control/union.ts +31 -0
  25. package/src/combinator/{data/delimiter.ts → delimiter.ts} +40 -60
  26. package/src/combinator/effect/backtrack.ts +64 -0
  27. package/src/combinator/effect/clock.ts +10 -0
  28. package/src/combinator/effect/precedence.ts +50 -0
  29. package/src/combinator/effect/recursion.ts +30 -0
  30. package/src/combinator/effect/scope.ts +100 -0
  31. package/src/combinator/effect/state.ts +72 -0
  32. package/src/combinator/{data/list → parser}/list.ts +35 -10
  33. package/src/combinator/parser.ts +303 -0
  34. package/src/combinator/process/bind.ts +34 -0
  35. package/src/combinator/process/block.test.ts +20 -0
  36. package/src/combinator/process/block.ts +33 -0
  37. package/src/combinator/process/clear.ts +16 -0
  38. package/src/combinator/process/contract.ts +35 -0
  39. package/src/combinator/process/duplicate.ts +7 -0
  40. package/src/combinator/process/error.ts +13 -0
  41. package/src/combinator/{control/manipulation → process}/fallback.ts +3 -3
  42. package/src/combinator/process/fence.ts +59 -0
  43. package/src/combinator/process/fmap.ts +10 -0
  44. package/src/combinator/process/indent.test.ts +31 -0
  45. package/src/combinator/process/indent.ts +51 -0
  46. package/src/combinator/process/lazy.ts +8 -0
  47. package/src/combinator/process/line.test.ts +21 -0
  48. package/src/combinator/process/line.ts +55 -0
  49. package/src/combinator/process/match.ts +37 -0
  50. package/src/combinator/process/reverse.ts +7 -0
  51. package/src/combinator/process/scope.ts +102 -0
  52. package/src/combinator/process/surround.ts +273 -0
  53. package/src/combinator.ts +28 -24
  54. package/src/debug.test.ts +11 -8
  55. package/src/parser/autolink.test.ts +17 -18
  56. package/src/parser/block/blockquote.test.ts +78 -79
  57. package/src/parser/block/blockquote.ts +32 -25
  58. package/src/parser/block/codeblock.test.ts +54 -57
  59. package/src/parser/block/codeblock.ts +44 -26
  60. package/src/parser/block/dlist.test.ts +56 -57
  61. package/src/parser/block/dlist.ts +5 -5
  62. package/src/parser/block/extension/aside.test.ts +7 -9
  63. package/src/parser/block/extension/aside.ts +76 -47
  64. package/src/parser/block/extension/example.test.ts +16 -19
  65. package/src/parser/block/extension/example.ts +88 -48
  66. package/src/parser/block/extension/fig.test.ts +37 -36
  67. package/src/parser/block/extension/fig.ts +20 -25
  68. package/src/parser/block/extension/figbase.test.ts +18 -19
  69. package/src/parser/block/extension/figbase.ts +3 -3
  70. package/src/parser/block/extension/figure.test.ts +58 -63
  71. package/src/parser/block/extension/figure.ts +23 -21
  72. package/src/parser/block/extension/message.test.ts +12 -14
  73. package/src/parser/block/extension/message.ts +52 -39
  74. package/src/parser/block/extension/placeholder.test.ts +13 -13
  75. package/src/parser/block/extension/placeholder.ts +23 -21
  76. package/src/parser/block/extension/table.test.ts +69 -71
  77. package/src/parser/block/extension/table.ts +43 -31
  78. package/src/parser/block/extension.test.ts +24 -24
  79. package/src/parser/block/extension.ts +3 -3
  80. package/src/parser/block/heading.test.ts +58 -59
  81. package/src/parser/block/heading.ts +19 -18
  82. package/src/parser/block/ilist.test.ts +8 -8
  83. package/src/parser/block/ilist.ts +9 -7
  84. package/src/parser/block/mathblock.test.ts +29 -32
  85. package/src/parser/block/mathblock.ts +24 -23
  86. package/src/parser/block/mediablock.ts +7 -7
  87. package/src/parser/block/olist.test.ts +102 -103
  88. package/src/parser/block/olist.ts +11 -12
  89. package/src/parser/block/pagebreak.test.ts +15 -16
  90. package/src/parser/block/pagebreak.ts +5 -5
  91. package/src/parser/block/paragraph.test.ts +57 -58
  92. package/src/parser/block/paragraph.ts +1 -1
  93. package/src/parser/block/reply/cite.test.ts +39 -40
  94. package/src/parser/block/reply/cite.ts +5 -5
  95. package/src/parser/block/reply/quote.test.ts +50 -51
  96. package/src/parser/block/reply/quote.ts +8 -7
  97. package/src/parser/block/reply.test.ts +19 -20
  98. package/src/parser/block/reply.ts +2 -2
  99. package/src/parser/block/sidefence.test.ts +41 -48
  100. package/src/parser/block/sidefence.ts +17 -11
  101. package/src/parser/block/table.test.ts +48 -49
  102. package/src/parser/block/table.ts +10 -9
  103. package/src/parser/block/ulist.test.ts +52 -53
  104. package/src/parser/block/ulist.ts +9 -8
  105. package/src/parser/block.ts +63 -51
  106. package/src/parser/context.ts +40 -40
  107. package/src/parser/document.ts +48 -0
  108. package/src/parser/header.test.ts +19 -20
  109. package/src/parser/header.ts +31 -25
  110. package/src/parser/inline/annotation.test.ts +49 -50
  111. package/src/parser/inline/annotation.ts +14 -16
  112. package/src/parser/inline/autolink/account.test.ts +32 -33
  113. package/src/parser/inline/autolink/account.ts +18 -19
  114. package/src/parser/inline/autolink/anchor.test.ts +21 -22
  115. package/src/parser/inline/autolink/anchor.ts +7 -8
  116. package/src/parser/inline/autolink/channel.test.ts +14 -15
  117. package/src/parser/inline/autolink/email.test.ts +36 -37
  118. package/src/parser/inline/autolink/email.ts +6 -6
  119. package/src/parser/inline/autolink/hashnum.test.ts +32 -33
  120. package/src/parser/inline/autolink/hashnum.ts +7 -8
  121. package/src/parser/inline/autolink/hashtag.test.ts +59 -60
  122. package/src/parser/inline/autolink/hashtag.ts +8 -9
  123. package/src/parser/inline/autolink/url.test.ts +75 -76
  124. package/src/parser/inline/autolink/url.ts +17 -18
  125. package/src/parser/inline/autolink.ts +24 -11
  126. package/src/parser/inline/bracket.test.ts +73 -74
  127. package/src/parser/inline/bracket.ts +88 -63
  128. package/src/parser/inline/code.test.ts +30 -31
  129. package/src/parser/inline/code.ts +6 -6
  130. package/src/parser/inline/deletion.test.ts +27 -28
  131. package/src/parser/inline/deletion.ts +5 -5
  132. package/src/parser/inline/emphasis.test.ts +39 -40
  133. package/src/parser/inline/emphasis.ts +5 -5
  134. package/src/parser/inline/emstrong.test.ts +101 -102
  135. package/src/parser/inline/emstrong.ts +103 -85
  136. package/src/parser/inline/extension/index.test.ts +91 -92
  137. package/src/parser/inline/extension/index.ts +17 -13
  138. package/src/parser/inline/extension/indexee.ts +4 -4
  139. package/src/parser/inline/extension/indexer.test.ts +23 -24
  140. package/src/parser/inline/extension/indexer.ts +6 -5
  141. package/src/parser/inline/extension/label.test.ts +32 -33
  142. package/src/parser/inline/extension/label.ts +14 -5
  143. package/src/parser/inline/extension/placeholder.test.ts +42 -43
  144. package/src/parser/inline/extension/placeholder.ts +8 -9
  145. package/src/parser/inline/html.test.ts +109 -109
  146. package/src/parser/inline/html.ts +27 -27
  147. package/src/parser/inline/htmlentity.test.ts +37 -38
  148. package/src/parser/inline/htmlentity.ts +6 -7
  149. package/src/parser/inline/insertion.test.ts +27 -28
  150. package/src/parser/inline/insertion.ts +5 -5
  151. package/src/parser/inline/italic.test.ts +55 -56
  152. package/src/parser/inline/italic.ts +5 -5
  153. package/src/parser/inline/link.test.ts +186 -187
  154. package/src/parser/inline/link.ts +29 -30
  155. package/src/parser/inline/mark.test.ts +31 -32
  156. package/src/parser/inline/mark.ts +6 -6
  157. package/src/parser/inline/math.test.ts +140 -141
  158. package/src/parser/inline/math.ts +7 -8
  159. package/src/parser/inline/media.test.ts +92 -93
  160. package/src/parser/inline/media.ts +35 -41
  161. package/src/parser/inline/reference.test.ts +111 -112
  162. package/src/parser/inline/reference.ts +61 -32
  163. package/src/parser/inline/remark.test.ts +49 -50
  164. package/src/parser/inline/remark.ts +13 -13
  165. package/src/parser/inline/ruby.test.ts +49 -50
  166. package/src/parser/inline/ruby.ts +100 -52
  167. package/src/parser/inline/shortmedia.test.ts +9 -10
  168. package/src/parser/inline/shortmedia.ts +11 -9
  169. package/src/parser/inline/strong.test.ts +36 -37
  170. package/src/parser/inline/strong.ts +5 -5
  171. package/src/parser/inline/template.test.ts +22 -23
  172. package/src/parser/inline/template.ts +17 -20
  173. package/src/parser/inline.test.ts +225 -226
  174. package/src/parser/inline.ts +68 -34
  175. package/src/parser/parser.ts +51 -0
  176. package/src/parser/repeat.ts +118 -91
  177. package/src/parser/segment.test.ts +0 -11
  178. package/src/parser/segment.ts +25 -28
  179. package/src/parser/source/escapable.test.ts +23 -24
  180. package/src/parser/source/escapable.ts +19 -20
  181. package/src/parser/source/line.test.ts +17 -18
  182. package/src/parser/source/line.ts +19 -24
  183. package/src/parser/source/str.ts +17 -10
  184. package/src/parser/source/text.test.ts +88 -89
  185. package/src/parser/source/text.ts +32 -62
  186. package/src/parser/source/unescapable.test.ts +23 -24
  187. package/src/parser/source/unescapable.ts +15 -16
  188. package/src/parser/source/whitespace.ts +36 -0
  189. package/src/parser/source.ts +1 -0
  190. package/src/parser/util.ts +1 -1
  191. package/src/parser/visibility.ts +37 -15
  192. package/src/processor/figure.test.ts +20 -20
  193. package/src/processor/figure.ts +18 -10
  194. package/src/processor/note.test.ts +13 -13
  195. package/src/processor/note.ts +4 -2
  196. package/src/renderer/render/media/pdf.ts +2 -2
  197. package/src/renderer/render/media/twitter.ts +2 -2
  198. package/src/renderer/render/media.test.ts +12 -12
  199. package/src/renderer/render.test.ts +11 -11
  200. package/src/util/info.test.ts +2 -2
  201. package/src/util/quote.test.ts +3 -3
  202. package/src/util/quote.ts +6 -5
  203. package/src/util/toc.test.ts +12 -12
  204. package/src/combinator/control/constraint/block.test.ts +0 -20
  205. package/src/combinator/control/constraint/block.ts +0 -28
  206. package/src/combinator/control/constraint/contract.ts +0 -27
  207. package/src/combinator/control/constraint/line.test.ts +0 -21
  208. package/src/combinator/control/constraint/line.ts +0 -42
  209. package/src/combinator/control/manipulation/clear.ts +0 -5
  210. package/src/combinator/control/manipulation/convert.ts +0 -22
  211. package/src/combinator/control/manipulation/duplicate.ts +0 -7
  212. package/src/combinator/control/manipulation/fence.ts +0 -54
  213. package/src/combinator/control/manipulation/indent.test.ts +0 -31
  214. package/src/combinator/control/manipulation/indent.ts +0 -39
  215. package/src/combinator/control/manipulation/lazy.ts +0 -8
  216. package/src/combinator/control/manipulation/match.ts +0 -27
  217. package/src/combinator/control/manipulation/recovery.ts +0 -18
  218. package/src/combinator/control/manipulation/reverse.ts +0 -8
  219. package/src/combinator/control/manipulation/scope.ts +0 -61
  220. package/src/combinator/control/manipulation/surround.ts +0 -223
  221. package/src/combinator/control/monad/bind.ts +0 -26
  222. package/src/combinator/control/monad/fmap.ts +0 -10
  223. package/src/combinator/data/parser/context.ts +0 -96
  224. package/src/combinator/data/parser/inits.ts +0 -20
  225. package/src/combinator/data/parser/sequence.test.ts +0 -33
  226. package/src/combinator/data/parser/sequence.ts +0 -20
  227. package/src/combinator/data/parser/some.test.ts +0 -37
  228. package/src/combinator/data/parser/subsequence.test.ts +0 -41
  229. package/src/combinator/data/parser/subsequence.ts +0 -13
  230. package/src/combinator/data/parser/tails.ts +0 -8
  231. package/src/combinator/data/parser/union.test.ts +0 -33
  232. package/src/combinator/data/parser/union.ts +0 -18
  233. package/src/combinator/data/parser.ts +0 -144
package/src/api.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { run } from './api/run';
1
2
  export { parse } from './api/parse';
2
3
  export { bind } from './api/bind';
3
4
  export { caches } from './api/cache';
@@ -0,0 +1,35 @@
1
+ import { Parser, SubParsers, Result } from '../parser';
2
+ import { union } from './union';
3
+ import { sequence } from './sequence';
4
+
5
+ export function inits<P extends Parser>(parsers: Parser.SubParsers<P>): P;
6
+ export function inits<T>(parsers: SubParsers<T>): Parser<T> {
7
+ assert(parsers.every(f => f));
8
+ switch (parsers.length) {
9
+ case 0:
10
+ assert(false);
11
+ return (_, output) => output.context;
12
+ case 1:
13
+ return parsers[0];
14
+ default:
15
+ return parsers.reduceRight((acc, parser, i) =>
16
+ sequence([
17
+ parser,
18
+ i !== 0
19
+ ? acc
20
+ : union([
21
+ acc,
22
+ recovery,
23
+ ]),
24
+ ]));
25
+ }
26
+ }
27
+
28
+ const recovery: Parser<never> = (_, output) => {
29
+ if (output.state) {
30
+ output.state = true;
31
+ // @ts-expect-error
32
+ output.context ??= Result.succ;
33
+ }
34
+ return output.context;
35
+ };
@@ -0,0 +1,38 @@
1
+ import { Parser, Result, Input, Node, input } from '../parser';
2
+ import { sequence } from './sequence';
3
+ import { backtrack } from '../effect/backtrack';
4
+ import { inspect } from '../../debug.test';
5
+
6
+ describe('Unit: lib/parser/control/sequence', () => {
7
+ describe('sequence', () => {
8
+ const A: Parser<string> = (input, output) => {
9
+ if(input.source[input.position] === 'A'){
10
+ ++input.position;
11
+ output.append(new Node('a'));
12
+ return Result.succ;
13
+ }
14
+ };
15
+ const B: Parser<string> = (input, output) => {
16
+ if(input.source[input.position] === 'B'){
17
+ ++input.position;
18
+ output.append(new Node('b'));
19
+ return Result.succ;
20
+ }
21
+ };
22
+ const AB = backtrack(sequence<Parser<string, Input, [typeof A, typeof B]>>([A, B]));
23
+
24
+ it('basic', () => {
25
+ const parser = AB;
26
+ assert.deepStrictEqual(inspect(parser, input('')), undefined);
27
+ assert.deepStrictEqual(inspect(parser, input('A')), undefined);
28
+ assert.deepStrictEqual(inspect(parser, input('B')), undefined);
29
+ assert.deepStrictEqual(inspect(parser, input('AB')), [['a', 'b'], '']);
30
+ assert.deepStrictEqual(inspect(parser, input('BA')), undefined);
31
+ assert.deepStrictEqual(inspect(parser, input('AAB')), undefined);
32
+ assert.deepStrictEqual(inspect(parser, input('ABB')), [['a', 'b'], 'B']);
33
+ assert.deepStrictEqual(inspect(parser, input('BBA')), undefined);
34
+ });
35
+
36
+ });
37
+
38
+ });
@@ -0,0 +1,19 @@
1
+ import { Parser, SubParsers } from '../parser';
2
+ import { success, always } from './state';
3
+
4
+ export function sequence<P extends Parser>(parsers: Parser.SubParsers<P>): P;
5
+ export function sequence<T>(parsers: SubParsers<T>): Parser<T> {
6
+ assert(parsers.every(f => f));
7
+ switch (parsers.length) {
8
+ case 0:
9
+ assert(false);
10
+ return (_, output) => output.context;
11
+ case 1:
12
+ return parsers[0];
13
+ default:
14
+ return parsers.reduceRight((acc, parser) => always([
15
+ parser,
16
+ success(acc),
17
+ ]));
18
+ }
19
+ }
@@ -0,0 +1,41 @@
1
+ import { Parser, Result, Input, Node, input } from '../parser';
2
+ import { union } from './union';
3
+ import { some } from './some';
4
+ import { inspect } from '../../debug.test';
5
+
6
+ describe('Unit: lib/parser/control/some', () => {
7
+ describe('some', () => {
8
+ const A: Parser<string> = (input, output) => {
9
+ if(input.source[input.position] === 'A'){
10
+ ++input.position;
11
+ output.append(new Node('a'));
12
+ return Result.succ;
13
+ }
14
+ };
15
+ const B: Parser<string> = (input, output) => {
16
+ if(input.source[input.position] === 'B'){
17
+ ++input.position;
18
+ output.append(new Node('b'));
19
+ return Result.succ;
20
+ }
21
+ };
22
+ const AB = union<Parser<string, Input, [typeof A, typeof B]>>([A, B]);
23
+
24
+ it('basic', () => {
25
+ const parser = some(AB, /AAA/y);
26
+ assert.deepStrictEqual(inspect(parser, input('')), undefined);
27
+ assert.deepStrictEqual(inspect(parser, input('A')), [['a'], '']);
28
+ assert.deepStrictEqual(inspect(parser, input('B')), [['b'], '']);
29
+ assert.deepStrictEqual(inspect(parser, input('AB')), [['a', 'b'], '']);
30
+ assert.deepStrictEqual(inspect(parser, input('BA')), [['b', 'a'], '']);
31
+ assert.deepStrictEqual(inspect(parser, input('AAB')), [['a', 'a', 'b'], '']);
32
+ assert.deepStrictEqual(inspect(parser, input('BBA')), [['b', 'b', 'a'], '']);
33
+ assert.deepStrictEqual(inspect(parser, input('AAA')), undefined);
34
+ assert.deepStrictEqual(inspect(parser, input('BBB')), [['b', 'b', 'b'], '']);
35
+ assert.deepStrictEqual(inspect(parser, input('AAAB')), undefined);
36
+ assert.deepStrictEqual(inspect(parser, input('BAAA')), [['b'], 'AAA']);
37
+ });
38
+
39
+ });
40
+
41
+ });
@@ -1,5 +1,6 @@
1
- import { Parser, List, Node } from '../parser';
1
+ import { Parser, Result, Input } from '../parser';
2
2
  import { Delimiters } from '../delimiter';
3
+ import { always } from './state';
3
4
 
4
5
  type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
5
6
 
@@ -7,7 +8,7 @@ export function some<P extends Parser>(parser: P, limit?: number): P;
7
8
  export function some<P extends Parser>(parser: P, delimiters?: readonly DelimiterOption[], limit?: number): P;
8
9
  export function some<P extends Parser>(parser: P, delimiter: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
9
10
  export function some<P extends Parser>(parser: P, delimiter: string | RegExp, after: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
10
- export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp | readonly DelimiterOption[], after?: number | string | RegExp | readonly DelimiterOption[], delimiters?: number | readonly DelimiterOption[], limit = 0): Parser<N> {
11
+ export function some<T>(parser: Parser<T>, delimiter?: number | string | RegExp | readonly DelimiterOption[], after?: number | string | RegExp | readonly DelimiterOption[], delimiters?: number | readonly DelimiterOption[], limit = 0): Parser<T> {
11
12
  if (typeof delimiter === 'number') {
12
13
  limit = delimiter;
13
14
  delimiters = undefined;
@@ -29,33 +30,45 @@ export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp
29
30
  assert(parser);
30
31
  assert(delimiter !== '');
31
32
  assert(delimiters === undefined || Array.isArray(delimiters));
32
- const match = Delimiters.tester(delimiter as string, after as string);
33
+ const test = Delimiters.tester(delimiter as string, after as string);
33
34
  const delims = delimiters?.map(([delimiter, precedence]) => ({
34
35
  signature: Delimiters.signature(delimiter),
35
36
  tester: Delimiters.tester(delimiter),
36
37
  precedence,
37
38
  }));
38
- return input => {
39
- const context = input;
40
- const { source, position } = context;
41
- let nodes: List<Node<N>> | undefined;
42
- delims && context.delimiters.push(delims);
43
- // whileは数倍遅い
44
- for (const len = source.length; context.position < len;) {
45
- if (match(input)) break;
46
- if (context.delimiters.test(input)) break;
47
- const pos = context.position;
48
- const result = parser(input);
49
- if (result === undefined) break;
50
- if (context.position === pos) break;
51
- nodes = nodes?.import(result) ?? result;
52
- // 次にパースに成功すれば確実に制限値を超えるので制限値ちょうどでも中止する
53
- if (limit > 0 && context.position - position >= limit) break;
54
- }
55
- delims && context.delimiters.pop(delims.length);
56
- assert(context.position >= position);
57
- return context.position > position
58
- ? nodes
59
- : undefined;
60
- };
39
+ interface Memory {
40
+ readonly position: number;
41
+ }
42
+ // 末尾再帰
43
+ const loop: Result<T, Input<Memory>> = [
44
+ (input, output) =>
45
+ test(input, output) || input.delimiters.test(input, output)
46
+ ? Result.skip
47
+ : Result.succ,
48
+ parser,
49
+ (input, output) =>
50
+ output.state
51
+ // 次にパースに成功すれば確実に制限値を超えるので制限値ちょうどでも中止する
52
+ ? input.position === input.source.length || limit !== 0 && input.position - input.memory.position >= limit
53
+ ? Result.succ
54
+ : loop
55
+ : Result.fail,
56
+ ];
57
+ return always<Parser<T, Input<Memory>>>([
58
+ input => {
59
+ const { source, position } = input;
60
+ if (position === source.length) return Result.skip;
61
+ input.memory = {
62
+ position,
63
+ };
64
+ delims && input.delimiters.push(delims);
65
+ return loop;
66
+ },
67
+ input => {
68
+ delims && input.delimiters.pop(delims.length);
69
+ return input.position > input.memory.position
70
+ ? Result.succ
71
+ : Result.fail;
72
+ },
73
+ ]);
61
74
  }
@@ -0,0 +1,33 @@
1
+ import { Parser, SubParsers } from '../parser';
2
+
3
+ export function then<P extends Parser>(success: P, failure: P): P;
4
+ export function then<T>(success: Parser<T>, failure: Parser<T>): Parser<T> {
5
+ assert(success);
6
+ assert(failure);
7
+ return (input, output) => output.state ? success(input, output) : failure(input, output);
8
+ }
9
+
10
+ export function success<P extends Parser>(parser: P): P;
11
+ export function success<T>(parser: Parser<T>): Parser<T> {
12
+ assert(parser);
13
+ return (input, output) => output.context && parser(input, output);
14
+ }
15
+
16
+ export function failure<P extends Parser>(parser: P): P;
17
+ export function failure<T>(parser: Parser<T>): Parser<T> {
18
+ assert(parser);
19
+ return (input, output) => output.context ?? parser(input, output);
20
+ }
21
+
22
+ export function always<P extends Parser>(parsers: readonly P[]): P;
23
+ export function always<T, S extends SubParsers<T>>(parsers: S): Parser<T> {
24
+ return () => parsers;
25
+ }
26
+
27
+ export function force<P extends Parser>(parser: P): P;
28
+ export function force<T>(parser: Parser<T>): Parser<T> {
29
+ return (input, output) =>
30
+ input.position === input.source.length
31
+ ? output.context
32
+ : parser(input, output);
33
+ }
@@ -0,0 +1,47 @@
1
+ import { Parser, Result, Input, Node, input } from '../parser';
2
+ import { subsequence } from './subsequence';
3
+ import { inspect } from '../../debug.test';
4
+
5
+ describe('Unit: lib/parser/control/subsequence', () => {
6
+ describe('subsequence', () => {
7
+ const A: Parser<string> = (input, output) => {
8
+ if(input.source[input.position] === 'A'){
9
+ ++input.position;
10
+ output.append(new Node('a'));
11
+ return Result.succ;
12
+ }
13
+ };
14
+ const B: Parser<string> = (input, output) => {
15
+ if(input.source[input.position] === 'B'){
16
+ ++input.position;
17
+ output.append(new Node('b'));
18
+ return Result.succ;
19
+ }
20
+ };
21
+ const C: Parser<string> = (input, output) => {
22
+ if(input.source[input.position] === 'C'){
23
+ ++input.position;
24
+ output.append(new Node('c'));
25
+ return Result.succ;
26
+ }
27
+ };
28
+ const ABC = subsequence<Parser<string, Input, [typeof A, typeof B, typeof C]>>([A, B, C]);
29
+
30
+ it('basic', () => {
31
+ const parser = ABC;
32
+ assert.deepStrictEqual(inspect(parser, input('')), undefined);
33
+ assert.deepStrictEqual(inspect(parser, input('A')), [['a'], '']);
34
+ assert.deepStrictEqual(inspect(parser, input('B')), [['b'], '']);
35
+ assert.deepStrictEqual(inspect(parser, input('C')), [['c'], '']);
36
+ assert.deepStrictEqual(inspect(parser, input('AB')), [['a', 'b'], '']);
37
+ assert.deepStrictEqual(inspect(parser, input('BA')), [['b'], 'A']);
38
+ assert.deepStrictEqual(inspect(parser, input('AAB')), [['a'], 'AB']);
39
+ assert.deepStrictEqual(inspect(parser, input('ABB')), [['a', 'b'], 'B']);
40
+ assert.deepStrictEqual(inspect(parser, input('BBA')), [['b'], 'BA']);
41
+ assert.deepStrictEqual(inspect(parser, input('AC')), [['a', 'c'], '']);
42
+ assert.deepStrictEqual(inspect(parser, input('BC')), [['b', 'c'], '']);
43
+ });
44
+
45
+ });
46
+
47
+ });
@@ -0,0 +1,16 @@
1
+ import { Parser, SubParsers } from '../parser';
2
+ import { union } from './union';
3
+ import { inits } from './inits';
4
+
5
+ export function subsequence<P extends Parser>(parsers: Parser.SubParsers<P>): P;
6
+ export function subsequence<T>(parsers: SubParsers<T>): Parser<T> {
7
+ assert(parsers.every(f => f));
8
+ return union(
9
+ parsers.map((_, i) =>
10
+ i + 1 !== parsers.length
11
+ ? inits([
12
+ parsers[i],
13
+ subsequence(parsers.slice(i + 1)),
14
+ ])
15
+ : parsers[i]));
16
+ }
@@ -0,0 +1,8 @@
1
+ import { Parser, SubParsers } from '../parser';
2
+ import { union } from './union';
3
+ import { sequence } from './sequence';
4
+
5
+ export function tails<P extends Parser>(parsers: Parser.SubParsers<P>): P;
6
+ export function tails<T>(parsers: SubParsers<T>): Parser<T> {
7
+ return union(parsers.map((_, i) => sequence(parsers.slice(i))));
8
+ }
@@ -0,0 +1,37 @@
1
+ import { Parser, Result, Input, Node, input } from '../parser';
2
+ import { union } from './union';
3
+ import { inspect } from '../../debug.test';
4
+
5
+ describe('Unit: lib/parser/control/union', () => {
6
+ describe('union', () => {
7
+ const A: Parser<string> = (input, output) => {
8
+ if(input.source[input.position] === 'A'){
9
+ ++input.position;
10
+ output.append(new Node('a'));
11
+ return Result.succ;
12
+ }
13
+ };
14
+ const B: Parser<string> = (input, output) => {
15
+ if(input.source[input.position] === 'B'){
16
+ ++input.position;
17
+ output.append(new Node('b'));
18
+ return Result.succ;
19
+ }
20
+ };
21
+ const AB = union<Parser<string, Input, [typeof A, typeof B]>>([A, B]);
22
+
23
+ it('basic', () => {
24
+ const parser = AB;
25
+ assert.deepStrictEqual(inspect(parser, input('')), undefined);
26
+ assert.deepStrictEqual(inspect(parser, input('A')), [['a'], '']);
27
+ assert.deepStrictEqual(inspect(parser, input('B')), [['b'], '']);
28
+ assert.deepStrictEqual(inspect(parser, input('AB')), [['a'], 'B']);
29
+ assert.deepStrictEqual(inspect(parser, input('BA')), [['b'], 'A']);
30
+ assert.deepStrictEqual(inspect(parser, input('AAB')), [['a'], 'AB']);
31
+ assert.deepStrictEqual(inspect(parser, input('ABB')), [['a'], 'BB']);
32
+ assert.deepStrictEqual(inspect(parser, input('BBA')), [['b'], 'BA']);
33
+ });
34
+
35
+ });
36
+
37
+ });
@@ -0,0 +1,31 @@
1
+ import { Parser, SubParsers, Result } from '../parser';
2
+ import { failure, always } from './state';
3
+
4
+ export function union<P extends Parser>(parsers: Parser.SubParsers<P>): P;
5
+ export function union<T>(parsers: SubParsers<T>): Parser<T> {
6
+ assert(parsers.every(f => f));
7
+ switch (parsers.length) {
8
+ case 0:
9
+ assert(false);
10
+ return (_, output) => output.context;
11
+ case 1:
12
+ return parsers[0];
13
+ default:
14
+ return parsers.reduceRight((acc, parser) => always([
15
+ parser,
16
+ failure(recovery(acc)),
17
+ ]));
18
+ }
19
+ }
20
+
21
+ function recovery<P extends Parser>(parser: P): P;
22
+ function recovery<T>(parser: Parser<T>): Parser<T> {
23
+ return (input, output) => {
24
+ if (!output.state) {
25
+ output.state = true;
26
+ // @ts-expect-error
27
+ output.context ??= Result.succ;
28
+ }
29
+ return parser(input, output);
30
+ };
31
+ }
@@ -1,10 +1,10 @@
1
- import { Parser, Input, List, Node, Context } from './parser';
2
- import { spend } from './parser/context';
1
+ import { Input, Output } from './parser';
2
+ import { spend } from './effect/clock';
3
3
 
4
4
  interface Delimiter {
5
5
  readonly memory: Delimiter[];
6
6
  readonly index: number;
7
- readonly tester: (input: Input) => boolean | undefined;
7
+ readonly tester: (input: Input, output: Output<unknown>) => boolean;
8
8
  readonly precedence: number;
9
9
  state: boolean;
10
10
  }
@@ -42,17 +42,17 @@ export class Delimiters {
42
42
  return index(`/${pattern.source}`);
43
43
  }
44
44
  }
45
- public static tester(pattern: string | RegExp | undefined, after?: string | RegExp): (input: Input<Context>) => true | undefined {
45
+ public static tester(pattern: string | RegExp | undefined, after?: string | RegExp): (input: Input, output: Output<unknown>) => boolean {
46
46
  switch (typeof pattern) {
47
47
  case 'undefined':
48
- return () => undefined;
48
+ return () => false;
49
49
  case 'string':
50
50
  case 'object':
51
51
  const test = tester(pattern, false);
52
- const verify = after ? tester(after, false) : undefined;
52
+ const verify = after && tester(after, false);
53
53
  return verify
54
- ? input => test(input) !== undefined && verify(input) !== undefined || undefined
55
- : input => test(input) !== undefined || undefined;
54
+ ? (input, output) => test(input, output) && verify(input, output)
55
+ : (input, output) => test(input, output);
56
56
  }
57
57
  }
58
58
  private readonly memories: Delimiter[][] = [];
@@ -68,7 +68,7 @@ export class Delimiters {
68
68
  public push(
69
69
  delims: readonly {
70
70
  readonly signature: number;
71
- readonly tester: (input: Input) => boolean | undefined;
71
+ readonly tester: (input: Input, output: Output<unknown>) => boolean;
72
72
  readonly precedence: number;
73
73
  }[]
74
74
  ): void {
@@ -139,37 +139,25 @@ export class Delimiters {
139
139
  delimiters[indexes[i]].state = true;
140
140
  }
141
141
  }
142
- public test(input: Input): boolean {
142
+ public test(input: Input, output: Output<unknown>): boolean {
143
143
  const { precedence } = input;
144
144
  const { delimiters } = this;
145
145
  for (let i = delimiters.length; i--;) {
146
146
  const delimiter = delimiters[i];
147
147
  if (delimiter.precedence <= precedence || !delimiter.state) continue;
148
- switch (delimiter.tester(input)) {
149
- case true:
150
- return true;
151
- case false:
152
- return false;
153
- default:
154
- continue;
155
- }
148
+ if (delimiter.tester(input, output)) return true;
156
149
  }
157
150
  return false;
158
151
  }
159
152
  }
160
153
 
161
- export function matcher(pattern: string | RegExp, advance: boolean, after?: Parser<string>): Parser<string> {
154
+ export function matcher(pattern: string | RegExp, advance: boolean, after?: (input: Input, output: Output<unknown>) => boolean): (input: Input, output: Output<unknown>) => string | undefined {
162
155
  assert(pattern instanceof RegExp ? !pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^') : true);
163
- const count = typeof pattern === 'object'
164
- ? /[^^\\*+][*+]|{\d+,}/.test(pattern.source)
165
- : false;
166
156
  let sid = 0, pos = 0, index = -1;
167
157
  switch (typeof pattern) {
168
158
  case 'string':
169
- if (pattern === '') return () => new List([new Node(pattern)]);
170
- return input => {
171
- const context = input;
172
- const { SID, source, position } = context;
159
+ return (input, output) => {
160
+ const { SID, source, position } = input;
173
161
  const hit = SID === sid && position === pos;
174
162
  index = hit
175
163
  ? index
@@ -177,19 +165,18 @@ export function matcher(pattern: string | RegExp, advance: boolean, after?: Pars
177
165
  sid = SID;
178
166
  pos = position;
179
167
  if (index === -1) return;
168
+ !hit && spend(input, output, pattern.length);
180
169
  if (advance) {
181
- context.position += pattern.length;
170
+ input.position += pattern.length;
182
171
  }
183
- const next = after?.(input);
184
- return after
185
- ? next && new List([new Node(pattern)]).import(next)
186
- : new List([new Node(pattern)]);
172
+ return after?.(input, output) ?? true
173
+ ? pattern
174
+ : undefined;
187
175
  };
188
176
  case 'object':
189
177
  assert(pattern.sticky);
190
- return input => {
191
- const context = input;
192
- const { SID, source, position } = context;
178
+ return (input, output) => {
179
+ const { SID, source, position } = input;
193
180
  const hit = SID === sid && position === pos;
194
181
  pattern.lastIndex = position;
195
182
  index = hit
@@ -199,48 +186,42 @@ export function matcher(pattern: string | RegExp, advance: boolean, after?: Pars
199
186
  pos = position;
200
187
  if (index === -1) return;
201
188
  const src = source.slice(position, index);
202
- count && !hit && spend(context, src.length);
189
+ !hit && spend(input, output, src.length);
203
190
  if (advance) {
204
- context.position = index;
191
+ input.position = index;
205
192
  }
206
- const next = after?.(input);
207
- return after
208
- ? next && new List([new Node(src)]).import(next)
209
- : new List([new Node(src)]);
193
+ return after?.(input, output) ?? true
194
+ ? src
195
+ : undefined;
210
196
  };
211
197
  }
212
198
  }
213
199
 
214
- export function tester(pattern: string | RegExp, advance: boolean, after?: Parser<unknown>): Parser<never> {
200
+ export function tester(pattern: string | RegExp, advance: boolean, after?: (input: Input, output: Output<unknown>) => boolean): (input: Input, output: Output<unknown>) => boolean {
215
201
  assert(pattern instanceof RegExp ? !pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^') : true);
216
- const count = typeof pattern === 'object'
217
- ? /[^^\\*+][*+]|{\d+,}/.test(pattern.source)
218
- : false;
219
202
  let sid = 0, pos = 0, index = -1;
220
203
  switch (typeof pattern) {
221
204
  case 'string':
222
- if (pattern === '') return () => new List();
223
- return input => {
224
- const context = input;
225
- const { SID, source, position } = context;
205
+ if (pattern === '') return () => true;
206
+ return (input, output) => {
207
+ const { SID, source, position } = input;
226
208
  const hit = SID === sid && position === pos;
227
209
  index = hit
228
210
  ? index
229
211
  : source.startsWith(pattern, position) ? position : -1;
230
212
  sid = SID;
231
213
  pos = position;
232
- if (index === -1) return;
214
+ if (index === -1) return false;
215
+ !hit && spend(input, output, pattern.length);
233
216
  if (advance) {
234
- context.position += pattern.length;
217
+ input.position += pattern.length;
235
218
  }
236
- if (after && after(input) === undefined) return;
237
- return new List();
219
+ return after?.(input, output) ?? true;
238
220
  };
239
221
  case 'object':
240
222
  assert(pattern.sticky);
241
- return input => {
242
- const context = input;
243
- const { SID, source, position } = context;
223
+ return (input, output) => {
224
+ const { SID, source, position } = input;
244
225
  const hit = SID === sid && position === pos;
245
226
  pattern.lastIndex = position;
246
227
  index = hit
@@ -248,14 +229,13 @@ export function tester(pattern: string | RegExp, advance: boolean, after?: Parse
248
229
  : pattern.test(source) ? pattern.lastIndex : -1;
249
230
  sid = SID;
250
231
  pos = position;
251
- if (index === -1) return;
232
+ if (index === -1) return false;
252
233
  const len = index - position;
253
- count && !hit && spend(context, len);
234
+ !hit && spend(input, output, len);
254
235
  if (advance) {
255
- context.position = index;
236
+ input.position = index;
256
237
  }
257
- if (after && after(input) === undefined) return;
258
- return new List();
238
+ return after?.(input, output) ?? true;
259
239
  };
260
240
  }
261
241
  }