securemark 0.293.4 → 0.294.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 (106) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +868 -564
  3. package/markdown.d.ts +13 -13
  4. package/package.json +3 -3
  5. package/src/combinator/control/constraint/block.test.ts +6 -6
  6. package/src/combinator/control/constraint/contract.ts +3 -3
  7. package/src/combinator/control/constraint/line.test.ts +7 -7
  8. package/src/combinator/control/constraint/line.ts +1 -1
  9. package/src/combinator/control/manipulation/clear.ts +2 -3
  10. package/src/combinator/control/manipulation/convert.ts +2 -2
  11. package/src/combinator/control/manipulation/duplicate.ts +4 -5
  12. package/src/combinator/control/manipulation/fence.ts +2 -2
  13. package/src/combinator/control/manipulation/indent.test.ts +2 -2
  14. package/src/combinator/control/manipulation/indent.ts +4 -4
  15. package/src/combinator/control/manipulation/reverse.ts +2 -2
  16. package/src/combinator/control/manipulation/scope.ts +3 -4
  17. package/src/combinator/control/manipulation/surround.ts +14 -15
  18. package/src/combinator/control/monad/bind.ts +6 -6
  19. package/src/combinator/control/monad/fmap.ts +7 -7
  20. package/src/combinator/data/data.ts +135 -0
  21. package/src/combinator/data/parser/context.test.ts +16 -15
  22. package/src/combinator/data/parser/context.ts +5 -4
  23. package/src/combinator/data/parser/inits.ts +6 -7
  24. package/src/combinator/data/parser/sequence.test.ts +3 -3
  25. package/src/combinator/data/parser/sequence.ts +6 -7
  26. package/src/combinator/data/parser/some.test.ts +3 -3
  27. package/src/combinator/data/parser/some.ts +4 -4
  28. package/src/combinator/data/parser/subsequence.test.ts +4 -4
  29. package/src/combinator/data/parser/subsequence.ts +3 -3
  30. package/src/combinator/data/parser/tails.ts +3 -3
  31. package/src/combinator/data/parser/union.test.ts +3 -3
  32. package/src/combinator/data/parser.ts +16 -7
  33. package/src/debug.test.ts +6 -5
  34. package/src/parser/api/bind.ts +6 -8
  35. package/src/parser/api/header.ts +1 -1
  36. package/src/parser/api/normalize.ts +2 -4
  37. package/src/parser/api/parse.ts +3 -1
  38. package/src/parser/block/blockquote.ts +6 -4
  39. package/src/parser/block/codeblock.ts +8 -7
  40. package/src/parser/block/dlist.ts +9 -8
  41. package/src/parser/block/extension/aside.ts +27 -21
  42. package/src/parser/block/extension/example.ts +29 -26
  43. package/src/parser/block/extension/fig.ts +1 -1
  44. package/src/parser/block/extension/figbase.ts +6 -5
  45. package/src/parser/block/extension/figure.ts +23 -19
  46. package/src/parser/block/extension/message.ts +35 -24
  47. package/src/parser/block/extension/placeholder.ts +17 -13
  48. package/src/parser/block/extension/table.ts +47 -40
  49. package/src/parser/block/heading.test.ts +3 -12
  50. package/src/parser/block/heading.ts +12 -8
  51. package/src/parser/block/ilist.ts +13 -12
  52. package/src/parser/block/mathblock.ts +21 -17
  53. package/src/parser/block/mediablock.ts +7 -5
  54. package/src/parser/block/olist.ts +15 -5
  55. package/src/parser/block/pagebreak.ts +2 -1
  56. package/src/parser/block/paragraph.ts +3 -1
  57. package/src/parser/block/reply/cite.ts +20 -15
  58. package/src/parser/block/reply/quote.ts +9 -7
  59. package/src/parser/block/reply.ts +7 -3
  60. package/src/parser/block/sidefence.ts +8 -7
  61. package/src/parser/block/table.ts +23 -22
  62. package/src/parser/block/ulist.ts +16 -12
  63. package/src/parser/block.ts +7 -6
  64. package/src/parser/header.test.ts +3 -1
  65. package/src/parser/header.ts +20 -20
  66. package/src/parser/inline/annotation.ts +3 -1
  67. package/src/parser/inline/autolink/account.ts +3 -2
  68. package/src/parser/inline/autolink/anchor.ts +3 -2
  69. package/src/parser/inline/autolink/channel.ts +5 -4
  70. package/src/parser/inline/autolink/email.ts +4 -3
  71. package/src/parser/inline/autolink/hashnum.ts +3 -2
  72. package/src/parser/inline/autolink/hashtag.ts +4 -3
  73. package/src/parser/inline/autolink/url.ts +7 -6
  74. package/src/parser/inline/bracket.ts +16 -15
  75. package/src/parser/inline/code.ts +5 -4
  76. package/src/parser/inline/deletion.ts +5 -5
  77. package/src/parser/inline/emphasis.ts +4 -3
  78. package/src/parser/inline/emstrong.test.ts +18 -18
  79. package/src/parser/inline/emstrong.ts +39 -30
  80. package/src/parser/inline/extension/index.ts +22 -19
  81. package/src/parser/inline/extension/indexee.ts +2 -2
  82. package/src/parser/inline/extension/indexer.ts +2 -1
  83. package/src/parser/inline/extension/label.ts +7 -3
  84. package/src/parser/inline/extension/placeholder.ts +6 -6
  85. package/src/parser/inline/html.ts +27 -28
  86. package/src/parser/inline/htmlentity.ts +9 -8
  87. package/src/parser/inline/insertion.ts +5 -5
  88. package/src/parser/inline/italic.ts +5 -5
  89. package/src/parser/inline/link.ts +36 -38
  90. package/src/parser/inline/mark.ts +7 -7
  91. package/src/parser/inline/math.ts +5 -4
  92. package/src/parser/inline/media.ts +33 -32
  93. package/src/parser/inline/reference.ts +19 -20
  94. package/src/parser/inline/remark.ts +11 -11
  95. package/src/parser/inline/ruby.ts +50 -53
  96. package/src/parser/inline/strong.ts +4 -3
  97. package/src/parser/inline/template.ts +16 -15
  98. package/src/parser/inline.test.ts +3 -3
  99. package/src/parser/segment.ts +3 -1
  100. package/src/parser/source/escapable.ts +9 -8
  101. package/src/parser/source/line.ts +4 -3
  102. package/src/parser/source/str.ts +2 -2
  103. package/src/parser/source/text.ts +19 -26
  104. package/src/parser/source/unescapable.ts +6 -5
  105. package/src/parser/util.ts +16 -30
  106. package/src/parser/visibility.ts +19 -20
@@ -1,7 +1,7 @@
1
- import { Parser, Ctx, CtxOptions, input } from '../parser';
1
+ import { Parser, List, Data, Ctx, CtxOptions, input, eval } from '../parser';
2
2
  import { some } from './some';
3
- import { reset, context } from './context';
4
- import { creation } from './context';
3
+ import { reset, context, creation } from './context';
4
+ import { unwrap } from '../../../parser/util';
5
5
 
6
6
  describe('Unit: combinator/data/parser/context', () => {
7
7
  interface Context extends CtxOptions {
@@ -12,26 +12,26 @@ describe('Unit: combinator/data/parser/context', () => {
12
12
  const parser: Parser<number> = some(creation(1,
13
13
  ({ context }) => {
14
14
  context.position += 1;
15
- return [[context.resources?.clock ?? NaN]];
15
+ return new List([new Data(context.resources?.clock ?? NaN)]);
16
16
  }));
17
17
 
18
18
  it('root', () => {
19
19
  const base: Context = { resources: { clock: 3, recursions: [1] } };
20
20
  const ctx: Context = {};
21
- assert.deepStrictEqual(reset(base, parser)(input('123', ctx)), [[3, 2, 1]]);
21
+ assert.deepStrictEqual([...unwrap(eval(reset(base, parser)(input('123', ctx)))!)], [3, 2, 1]);
22
22
  assert(base.resources?.clock === 3);
23
23
  assert(ctx.resources?.clock === undefined);
24
24
  assert.throws(() => reset(base, parser)(input('1234', ctx)));
25
- assert(ctx.resources?.clock === 0);
25
+ assert(ctx.resources?.clock === undefined);
26
26
  });
27
27
 
28
28
  it('node', () => {
29
29
  const base: Context = { resources: { clock: 3, recursions: [1] } };
30
- const ctx: Context = { resources: { clock: 1, recursions: [1] } };
31
- assert.deepStrictEqual(reset(base, parser)(input('1', ctx)), [[1]]);
30
+ const ctx: Context = { resources: { clock: 2, recursions: [1] } };
31
+ assert.deepStrictEqual([...unwrap(eval(reset(base, parser)(input('1', ctx)))!)], [2]);
32
32
  assert(base.resources?.clock === 3);
33
- assert(ctx.resources?.clock === 0);
34
- assert.throws(() => reset(base, parser)(input('1', ctx)));
33
+ assert(ctx.resources?.clock === 1);
34
+ assert.throws(() => reset(base, parser)(input('12', ctx)));
35
35
  assert(ctx.resources?.clock === 0);
36
36
  });
37
37
 
@@ -41,16 +41,17 @@ describe('Unit: combinator/data/parser/context', () => {
41
41
  const parser: Parser<boolean, Context & Ctx> = some(creation(1,
42
42
  ({ context }) => {
43
43
  context.position += 1;
44
- return [[context.status!]];
44
+ return new List([new Data(context.status!)]);
45
45
  }));
46
46
 
47
47
  it('', () => {
48
48
  const base: Context = { status: true };
49
- const ctx: Context = { resources: { clock: 3, recursions: [1] } };
50
- assert.deepStrictEqual(context(base, parser)(input('123', ctx)), [[true, true, true]]);
51
- assert(ctx.resources?.clock === 0);
49
+ const ctx: Context = { resources: { clock: 2, recursions: [1] } };
50
+ assert.deepStrictEqual([...unwrap(eval(context(base, parser)(input('1', ctx)))!)], [true]);
51
+ assert(base.resources?.clock === undefined);
52
+ assert(ctx.resources?.clock === 1);
52
53
  assert(ctx.status === undefined);
53
- assert.throws(() => reset(base, parser)(input('1', ctx)));
54
+ assert.throws(() => context(base, parser)(input('12', ctx)));
54
55
  assert(ctx.resources?.clock === 0);
55
56
  assert(ctx.status === true);
56
57
  });
@@ -1,5 +1,5 @@
1
1
  import { min } from 'spica/alias';
2
- import { Parser, Result, Ctx, CtxOptions, Node, Context } from '../../data/parser';
2
+ import { Parser, Result, List, Data, Ctx, CtxOptions, Node, Context } from '../../data/parser';
3
3
  import { clone } from 'spica/assign';
4
4
 
5
5
  export function reset<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
@@ -9,7 +9,8 @@ export function reset<N>(base: Ctx, parser: Parser<N>): Parser<N> {
9
9
  const changes = Object.entries(base);
10
10
  const values = Array(changes.length);
11
11
  return ({ context }) =>
12
- apply(parser, context, changes, values, true);
12
+ // 大域離脱時の汚染回避のため複製
13
+ apply(parser, { ...context }, changes, values, true);
13
14
  }
14
15
 
15
16
  export function context<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
@@ -172,7 +173,7 @@ export function matcher(pattern: string | RegExp, advance: boolean): Parser<stri
172
173
  if (advance) {
173
174
  context.position += pattern.length;
174
175
  }
175
- return [[pattern]];
176
+ return new List([new Data(pattern)]);
176
177
  };
177
178
  case 'object':
178
179
  assert(pattern.sticky);
@@ -185,7 +186,7 @@ export function matcher(pattern: string | RegExp, advance: boolean): Parser<stri
185
186
  if (advance) {
186
187
  context.position += src.length;
187
188
  }
188
- return [[src]];
189
+ return new List([new Data(src)]);
189
190
  };
190
191
  }
191
192
  }
@@ -1,27 +1,26 @@
1
- import { Parser, CtxOptions, Node, Context, SubParsers, SubNode, eval, Ctx } from '../parser';
2
- import { push } from 'spica/array';
1
+ import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode, eval, Ctx } from '../parser';
3
2
 
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> {
3
+ export function inits<P extends Parser<unknown, Ctx>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
4
+ export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
6
5
  assert(parsers.every(f => f));
7
6
  if (parsers.length === 1) return parsers[0];
8
7
  return input => {
9
8
  const { context } = input;
10
9
  const { source, position } = context;
11
- let nodes: N[] | undefined;
10
+ let nodes: List<Data<N>> | undefined;
12
11
  for (let len = parsers.length, i = 0; i < len; ++i) {
13
12
  if (context.position === source.length) break;
14
13
  if (context.delimiters?.match(input)) break;
15
14
  const result = parsers[i](input);
16
15
  if (result === undefined) break;
17
16
  nodes = nodes
18
- ? push(nodes, eval(result))
17
+ ? nodes.import(eval(result))
19
18
  : eval(result);
20
19
  if (resume?.(eval(result)) === false) break;
21
20
  }
22
21
  assert(context.position >= position);
23
22
  return nodes && context.position > position
24
- ? [nodes]
23
+ ? nodes
25
24
  : undefined;
26
25
  };
27
26
  }
@@ -1,4 +1,4 @@
1
- import { Parser, input } from '../parser';
1
+ import { Parser, List, Data, input } from '../parser';
2
2
  import { sequence } from './sequence';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
@@ -6,12 +6,12 @@ describe('Unit: combinator/data/parser/sequence', () => {
6
6
  describe('sequence', () => {
7
7
  const a: Parser<string> = ({ context }) => {
8
8
  return context.source[context.position] === 'a'
9
- ? void ++context.position || [['A']]
9
+ ? void ++context.position || new List([new Data('A')])
10
10
  : undefined;
11
11
  };
12
12
  const b: Parser<string> = ({ context }) => {
13
13
  return context.source[context.position] === 'b'
14
- ? void ++context.position || [['B']]
14
+ ? void ++context.position || new List([new Data('B')])
15
15
  : undefined;
16
16
  };
17
17
  const ab = sequence<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
@@ -1,27 +1,26 @@
1
- import { Parser, CtxOptions, Node, Context, SubParsers, SubNode, eval } from '../parser';
2
- import { push } from 'spica/array';
1
+ import { Parser, List, Data, CtxOptions, Node, Context, SubParsers, SubNode, eval } from '../parser';
3
2
 
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> {
3
+ export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
4
+ export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
6
5
  assert(parsers.every(f => f));
7
6
  if (parsers.length === 1) return parsers[0];
8
7
  return input => {
9
8
  const { context } = input;
10
9
  const { source, position } = context;
11
- let nodes: N[] | undefined;
10
+ let nodes: List<Data<N>> | undefined;
12
11
  for (let len = parsers.length, i = 0; i < len; ++i) {
13
12
  if (context.position === source.length) return;
14
13
  if (context.delimiters?.match(input)) return;
15
14
  const result = parsers[i](input);
16
15
  if (result === undefined) return;
17
16
  nodes = nodes
18
- ? push(nodes, eval(result))
17
+ ? nodes.import(eval(result))
19
18
  : eval(result);
20
19
  if (resume?.(eval(result)) === false) return;
21
20
  }
22
21
  assert(context.position >= position);
23
22
  return nodes && context.position > position
24
- ? [nodes]
23
+ ? nodes
25
24
  : undefined;
26
25
  };
27
26
  }
@@ -1,4 +1,4 @@
1
- import { Parser, input } from '../parser';
1
+ import { Parser, List, Data, input } from '../parser';
2
2
  import { union } from './union';
3
3
  import { some } from './some';
4
4
  import { inspect } from '../../../debug.test';
@@ -7,12 +7,12 @@ describe('Unit: combinator/data/parser/some', () => {
7
7
  describe('some', () => {
8
8
  const a: Parser<string> = ({ context }) => {
9
9
  return context.source[context.position] === 'a'
10
- ? void ++context.position || [['A']]
10
+ ? void ++context.position || new List([new Data('A')])
11
11
  : undefined;
12
12
  };
13
13
  const b: Parser<string> = ({ context }) => {
14
14
  return context.source[context.position] === 'b'
15
- ? void ++context.position || [['B']]
15
+ ? void ++context.position || new List([new Data('B')])
16
16
  : undefined;
17
17
  };
18
18
  const ab = union<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
@@ -1,4 +1,4 @@
1
- import { Parser, eval } from '../parser';
1
+ import { Parser, List, Data, eval } from '../parser';
2
2
  import { Delimiters } from './context/delimiter';
3
3
 
4
4
  type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
@@ -18,7 +18,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
18
18
  const { context } = input;
19
19
  const { source, position } = context;
20
20
  //assert(context.backtracks ??= {});
21
- let nodes: N[] | undefined;
21
+ let nodes: List<Data<N>> | undefined;
22
22
  if (delims.length > 0) {
23
23
  context.delimiters ??= new Delimiters();
24
24
  context.delimiters.push(delims);
@@ -30,7 +30,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
30
30
  const result = parser(input);
31
31
  if (result === undefined) break;
32
32
  nodes = nodes
33
- ? (nodes.push(...eval(result)), nodes)
33
+ ? nodes.import(eval(result))
34
34
  : eval(result);
35
35
  if (limit >= 0 && context.position - position > limit) break;
36
36
  }
@@ -39,7 +39,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
39
39
  }
40
40
  assert(context.position >= position);
41
41
  return nodes && context.position > position
42
- ? [nodes]
42
+ ? nodes
43
43
  : undefined;
44
44
  };
45
45
  }
@@ -1,4 +1,4 @@
1
- import { Parser, input } from '../parser';
1
+ import { Parser, List, Data, input } from '../parser';
2
2
  import { subsequence } from './subsequence';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
@@ -6,17 +6,17 @@ describe('Unit: combinator/data/parser/subsequence', () => {
6
6
  describe('subsequence', () => {
7
7
  const a: Parser<string> = ({ context }) => {
8
8
  return context.source[context.position] === 'a'
9
- ? void ++context.position || [['A']]
9
+ ? void ++context.position || new List([new Data('A')])
10
10
  : undefined;
11
11
  };
12
12
  const b: Parser<string> = ({ context }) => {
13
13
  return context.source[context.position] === 'b'
14
- ? void ++context.position || [['B']]
14
+ ? void ++context.position || new List([new Data('B')])
15
15
  : undefined;
16
16
  };
17
17
  const c: Parser<string> = ({ context }) => {
18
18
  return context.source[context.position] === 'c'
19
- ? void ++context.position || [['C']]
19
+ ? void ++context.position || new List([new Data('C')])
20
20
  : undefined;
21
21
  };
22
22
  const abc = subsequence<Parser<string, {}, [typeof a, typeof b, typeof c]>>([a, b, c]);
@@ -1,9 +1,9 @@
1
- import { Parser, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
1
+ import { Parser, List, Data, 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>[]) => 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> {
5
+ export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
+ export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<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, CtxOptions, Node, Context, SubParsers, SubNode } from '../parser';
1
+ import { Parser, List, Data, 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>[]) => 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> {
5
+ export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
+ export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, CtxOptions, D> {
7
7
  return union(parsers.map((_, i) => sequence(parsers.slice(i), resume)) as D);
8
8
  }
@@ -1,4 +1,4 @@
1
- import { Parser, input } from '../parser';
1
+ import { Parser, List, Data, input } from '../parser';
2
2
  import { union } from './union';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
@@ -6,12 +6,12 @@ describe('Unit: combinator/data/parser/union', () => {
6
6
  describe('union', () => {
7
7
  const a: Parser<string> = ({ context }) => {
8
8
  return context.source[context.position] === 'a'
9
- ? void ++context.position || [['A']]
9
+ ? void ++context.position || new List([new Data('A')])
10
10
  : undefined;
11
11
  };
12
12
  const b: Parser<string> = ({ context }) => {
13
13
  return context.source[context.position] === 'b'
14
- ? void ++context.position || [['B']]
14
+ ? void ++context.position || new List([new Data('B')])
15
15
  : undefined;
16
16
  };
17
17
  const ab = union<Parser<string, {}, [typeof a, typeof b]>>([a, b]);
@@ -1,3 +1,4 @@
1
+ import { List } from './data';
1
2
  import { Delimiters } from './parser/context/delimiter';
2
3
 
3
4
  export type Parser<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
@@ -6,9 +7,16 @@ export interface Input<C extends CtxOptions = CtxOptions> {
6
7
  readonly context: C & Ctx;
7
8
  }
8
9
  export type Result<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
9
- = readonly [N[], C, D]
10
- | readonly [N[]]
10
+ = readonly [never, C, D]
11
+ | List<Data<N>>
11
12
  | undefined;
13
+ export { List };
14
+ export class Data<N> implements List.Node {
15
+ constructor(public value: N) {
16
+ }
17
+ public next?: this = undefined;
18
+ public prev?: this = undefined;
19
+ }
12
20
  export interface Ctx extends CtxOptions {
13
21
  source: string;
14
22
  position: number;
@@ -71,12 +79,13 @@ export function clean<C extends Ctx>(context: C): C {
71
79
  }
72
80
 
73
81
  export { eval_ as eval };
74
- function eval_<N>(result: NonNullable<Result<N>>, default_?: N[]): N[];
75
- function eval_<N>(result: Result<N>, default_: N[]): N[];
76
- function eval_<N>(result: Result<N>, default_?: undefined): N[] | undefined;
77
- function eval_<N>(result: Result<N>, default_?: N[]): N[] | undefined {
82
+ function eval_<N>(result: NonNullable<Result<N>>, default_?: List<Data<N>>): List<Data<N>>;
83
+ function eval_<N>(result: Result<N>, default_: List<Data<N>>): List<Data<N>>;
84
+ function eval_<N>(result: Result<N>, default_?: undefined): List<Data<N>> | undefined;
85
+ function eval_<N>(result: Result<N>, default_?: List<Data<N>>): List<Data<N>> | undefined {
86
+ assert(!Array.isArray(result));
78
87
  return result
79
- ? result[0]
88
+ ? result as List<Data<N>>
80
89
  : default_;
81
90
  }
82
91
 
package/src/debug.test.ts CHANGED
@@ -2,11 +2,12 @@ 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>, ctx: Ctx, until: number | string = Infinity): [string[], string] | undefined {
5
+ export function inspect(result: Result<DocumentFragment | HTMLElement | string>, ctx: Ctx, until: number | string = Infinity): [string[], string] | undefined {
6
6
  return result && [
7
- eval(result).map(node => {
7
+ eval(result).foldl<string[]>((acc, { value: node }) => {
8
8
  assert(node);
9
- if (typeof node === 'string') return node;
9
+ if (typeof node === 'string') return acc.push(node), acc;
10
+ if (node instanceof DocumentFragment) return acc.push(html('div', [node]).innerHTML), acc;
10
11
  node = node.cloneNode(true);
11
12
  assert(!querySelectorWith(node, '.invalid[data-invalid-message$="."]'));
12
13
  querySelectorAllWith(node, '.invalid').forEach(el => {
@@ -31,8 +32,8 @@ export function inspect(result: Result<HTMLElement | string>, ctx: Ctx, until: n
31
32
  else {
32
33
  assert(el.innerHTML.startsWith(node.outerHTML.slice(0, until)));
33
34
  }
34
- return normalize(node.outerHTML.slice(0, until));
35
- }),
35
+ return acc.push(normalize(node.outerHTML.slice(0, until))), acc;
36
+ }, []),
36
37
  ctx.source.slice(ctx.position),
37
38
  ];
38
39
  }
@@ -1,7 +1,7 @@
1
1
  import { ParserSettings, Progress } from '../../..';
2
2
  import { MarkdownParser } from '../../../markdown';
3
3
  import { input, eval } from '../../combinator/data/parser';
4
- import { segment, validate, MAX_INPUT_SIZE } from '../segment';
4
+ import { segment } from '../segment';
5
5
  import { header } from '../header';
6
6
  import { block } from '../block';
7
7
  import { normalize } from './normalize';
@@ -44,12 +44,9 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
44
44
  function* parse(source: string): Generator<Progress, undefined, undefined> {
45
45
  if (settings.chunk && revision) throw new Error('Chunks cannot be updated');
46
46
  const url = headers(source).find(field => field.toLowerCase().startsWith('url:'))?.slice(4).trim() ?? '';
47
- source = normalize(validate(source, MAX_INPUT_SIZE) ? source : source.slice(0, MAX_INPUT_SIZE + 1));
48
- // Change the object identity.
49
- context = {
50
- ...context,
51
- url: url ? new ReadonlyURL(url as ':') : undefined,
52
- };
47
+ source = normalize(source);
48
+ // @ts-expect-error
49
+ context.url = url ? new ReadonlyURL(url as ':') : undefined;
53
50
  const rev = revision = Symbol();
54
51
  const sourceSegments: string[] = [];
55
52
  for (const seg of segment(source)) {
@@ -78,7 +75,8 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
78
75
  for (; index < sourceSegments.length - last; ++index) {
79
76
  assert(rev === revision);
80
77
  const seg = sourceSegments[index];
81
- const es = eval(header(input(seg, { header: index === 0 } as MarkdownParser.Context)) || block(input(seg, context)), []);
78
+ const es = eval(header(input(seg, { header: index === 0 } as MarkdownParser.Options)) || block(input(seg, context)))
79
+ ?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? [];
82
80
  blocks.splice(index, 0, [seg, es, url]);
83
81
  if (es.length === 0) continue;
84
82
  // All deletion processes always run after all addition processes have done.
@@ -14,7 +14,7 @@ export function headers(source: string): string[] {
14
14
  function parse(source: string): [HTMLElement, number] | [] {
15
15
  const i = input(source, {});
16
16
  const result = h(i);
17
- const [el] = eval(result, []);
17
+ const el = eval(result)?.head?.value;
18
18
  return el?.tagName === 'ASIDE'
19
19
  ? [el, i.context.position]
20
20
  : [];
@@ -60,12 +60,10 @@ 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(input(`&${name};`, {})))![0]);
63
+ .map(name => eval(unsafehtmlentity(input(`&${name};`, {})))!.head!.value);
64
64
  assert(unreadableEscapableCharacters.length === unreadableHTMLEntityNames.length);
65
65
  assert(unreadableEscapableCharacters.every(c => c.length === 1));
66
- const unreadableEscapableCharacter = new RegExp(`[${
67
- [...new Set<string>(unreadableEscapableCharacters)].join('')
68
- }]`, 'g');
66
+ const unreadableEscapableCharacter = new RegExp(`[${unreadableEscapableCharacters.join('')}]`, 'g');
69
67
  assert(!unreadableEscapableCharacter.source.includes('&'));
70
68
 
71
69
  // https://www.pandanoir.info/entry/2018/03/11/193000
@@ -34,7 +34,9 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
34
34
  const node = frag();
35
35
  let index = 0;
36
36
  for (const seg of segment(source)) {
37
- node.append(...eval(header(input(seg, { header: index++ === 0 } as MarkdownParser.Context)) || block(input(seg, context)), []));
37
+ node.append(
38
+ ...eval(header(input(seg, { header: index++ === 0 } as MarkdownParser.Context)) || block(input(seg, context)))
39
+ ?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? []);
38
40
  }
39
41
  assert(opts.id !== '' || !node.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
40
42
  if (opts.test) return node;
@@ -1,8 +1,10 @@
1
1
  import { BlockquoteParser } from '../block';
2
2
  import { Recursion } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, some, creation, recursion, block, validate, rewrite, open, convert, lazy, fmap } from '../../combinator';
4
5
  import { autolink } from '../autolink';
5
6
  import { contentline } from '../source';
7
+ import { unwrap } from '../util';
6
8
  import { parse } from '../api/parse';
7
9
  import { html, defrag } from 'typed-dom/dom';
8
10
 
@@ -26,9 +28,9 @@ const source: BlockquoteParser.SourceParser = lazy(() => fmap(
26
28
  convert(unindent, source, false, true)),
27
29
  rewrite(
28
30
  some(contentline, opener),
29
- convert(unindent, fmap(autolink, ns => [html('pre', defrag(ns))]), false, true)),
31
+ convert(unindent, fmap(autolink, ns => new List([new Data(html('pre', defrag(unwrap(ns))))])), false, true)),
30
32
  ]))),
31
- ns => [html('blockquote', ns)]));
33
+ ns => new List([new Data(html('blockquote', unwrap(ns)))])));
32
34
 
33
35
  const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
34
36
  some(recursion(Recursion.blockquote, union([
@@ -48,7 +50,7 @@ const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
48
50
  },
49
51
  }, context);
50
52
  context.position = source.length;
51
- return [[html('section', [document, html('h2', 'References'), references])]];
53
+ return new List([new Data(html('section', [document, html('h2', 'References'), references]))]);
52
54
  }, false, true))),
53
55
  ]))),
54
- ns => [html('blockquote', ns)]));
56
+ ns => new List([new Data(html('blockquote', unwrap(ns)))])));
@@ -1,8 +1,8 @@
1
1
  import { CodeBlockParser } from '../block';
2
- import { input, eval } from '../../combinator/data/parser';
2
+ import { List, Data, subinput, eval } from '../../combinator/data/parser';
3
3
  import { block, fence, clear, fmap } from '../../combinator';
4
4
  import { autolink } from '../autolink';
5
- import { invalid } from '../util';
5
+ import { unwrap, invalid } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
 
8
8
  const opener = /(`{3,})(?!`)([^\n]*)(?:$|\n)/y;
@@ -17,7 +17,8 @@ export const segment_: CodeBlockParser.SegmentParser = block(
17
17
  export const codeblock: CodeBlockParser = block(fmap(
18
18
  fence(opener, 300),
19
19
  // Bug: Type mismatch between outer and inner.
20
- ([body, overflow, closer, opener, delim, param]: string[], context) => {
20
+ (nodes, context) => {
21
+ const [body, overflow, closer, opener, delim, param] = unwrap<string>(nodes);
21
22
  const params = param.match(/(?:\\.?|\S)+/g)?.reduce<{
22
23
  lang?: string;
23
24
  path?: string;
@@ -49,7 +50,7 @@ export const codeblock: CodeBlockParser = block(fmap(
49
50
  : params[name] = value;
50
51
  return params;
51
52
  }, {}) ?? {};
52
- if (!closer || overflow || params.invalid) return [html('pre', {
53
+ if (!closer || overflow || params.invalid) return new List([new Data(html('pre', {
53
54
  class: 'invalid',
54
55
  translate: 'no',
55
56
  ...invalid(
@@ -60,7 +61,7 @@ export const codeblock: CodeBlockParser = block(fmap(
60
61
  : overflow
61
62
  ? `Invalid trailing line after the closing delimiter "${delim}"`
62
63
  : params.invalid!),
63
- }, `${opener}${body}${overflow || closer}`)];
64
+ }, `${opener}${body}${overflow || closer}`))]);
64
65
  const el = html('pre',
65
66
  {
66
67
  class: params.lang ? `code language-${params.lang}` : 'text',
@@ -72,6 +73,6 @@ export const codeblock: CodeBlockParser = block(fmap(
72
73
  params.lang
73
74
  ? context.caches?.code?.get(`${params.lang ?? ''}\n${body.slice(0, -1)}`)?.cloneNode(true).childNodes ||
74
75
  body.slice(0, -1) || undefined
75
- : defrag(eval(autolink(input(body.slice(0, -1), { ...context })), [])));
76
- return [el];
76
+ : defrag(unwrap(eval(autolink(subinput(body.slice(0, -1), context)), new List()))));
77
+ return new List([new Data(el)]);
77
78
  }));
@@ -1,10 +1,11 @@
1
1
  import { DListParser } from '../block';
2
2
  import { State } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, inits, some, state, block, line, validate, rewrite, open, lazy, fmap } from '../../combinator';
4
5
  import { inline, indexee, indexer, dataindex } from '../inline';
5
6
  import { anyline } from '../source';
6
7
  import { visualize, trimBlank, trimBlankEnd } from '../visibility';
7
- import { push } from 'spica/array';
8
+ import { unwrap } from '../util';
8
9
  import { html, defrag } from 'typed-dom/dom';
9
10
 
10
11
  export const dlist: DListParser = lazy(() => block(fmap(validate(
@@ -14,13 +15,13 @@ export const dlist: DListParser = lazy(() => block(fmap(validate(
14
15
  some(term)),
15
16
  some(desc),
16
17
  ]))),
17
- es => [html('dl', fillTrailingDescription(es))])));
18
+ ns => new List([new Data(html('dl', unwrap(fillTrailingDescription(ns))))]))));
18
19
 
19
20
  const term: DListParser.TermParser = line(indexee(fmap(open(
20
21
  /~[^\S\n]+(?=\S)/y,
21
22
  visualize(trimBlank(some(union([indexer, inline])))),
22
23
  true),
23
- ns => [html('dt', { 'data-index': dataindex(ns) }, defrag(ns))])));
24
+ ns => new List([new Data(html('dt', { 'data-index': dataindex(ns) }, defrag(unwrap(ns))))]))));
24
25
 
25
26
  const desc: DListParser.DescriptionParser = block(fmap(open(
26
27
  /:[^\S\n]+(?=\S)|/y,
@@ -28,11 +29,11 @@ const desc: DListParser.DescriptionParser = block(fmap(open(
28
29
  some(anyline, /[~:][^\S\n]+\S/y),
29
30
  visualize(trimBlankEnd(some(union([inline]))))),
30
31
  true),
31
- ns => [html('dd', defrag(ns))]),
32
+ ns => new List([new Data(html('dd', defrag(unwrap(ns))))])),
32
33
  false);
33
34
 
34
- function fillTrailingDescription(es: HTMLElement[]): HTMLElement[] {
35
- return es.length > 0 && es.at(-1)!.tagName === 'DT'
36
- ? push(es, [html('dd')])
37
- : es;
35
+ function fillTrailingDescription(nodes: List<Data<HTMLElement>>): List<Data<HTMLElement>> {
36
+ return nodes.last?.value.tagName === 'DT'
37
+ ? nodes.push(new Data(html('dd'))) && nodes
38
+ : nodes;
38
39
  }