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,14 +1,13 @@
1
1
  import { EmStrongParser, EmphasisParser, StrongParser } from '../inline';
2
2
  import { Recursion, Command } from '../context';
3
- import { Result, Node, Context, IntermediateParser } from '../../combinator/data/parser';
3
+ import { Result, List, Data, Node, Context, IntermediateParser, eval } from '../../combinator/data/parser';
4
4
  import { union, some, recursion, precedence, validate, surround, open, lazy, bind } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { strong } from './strong';
7
7
  import { emphasis } from './emphasis';
8
8
  import { str } from '../source';
9
9
  import { tightStart, blankWith } from '../visibility';
10
- import { repeat } from '../util';
11
- import { unshift, push } from 'spica/array';
10
+ import { unwrap, repeat } from '../util';
12
11
  import { html, defrag } from 'typed-dom/dom';
13
12
 
14
13
  const substrong: IntermediateParser<StrongParser> = lazy(() => some(union([
@@ -36,10 +35,10 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
36
35
  str(/\*{1,3}/y), false,
37
36
  ([, bs, cs], context): Result<Node<EmStrongParser>, Context<EmStrongParser>> => {
38
37
  assert(cs.length === 1);
39
- const { buffer } = context;
40
- switch (cs[0]) {
38
+ const { buffer = new List() } = context;
39
+ switch (cs.head!.value) {
41
40
  case '***':
42
- return [bs];
41
+ return bs;
43
42
  case '**':
44
43
  return bind<EmphasisParser>(
45
44
  subemphasis,
@@ -47,13 +46,18 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
47
46
  const { source } = context;
48
47
  if (source.startsWith('*', context.position)) {
49
48
  context.position += 1;
50
- return [[html('em', push(push(buffer!, [html('strong', defrag(bs))]), defrag(ds))), Command.Separator]];
49
+ buffer.push(new Data(html('strong', defrag(unwrap(bs)))));
50
+ buffer.import(ds);
51
+ return new List([new Data(html('em', defrag(unwrap(buffer)))), new Data(Command.Separator)]);
51
52
  }
52
53
  else {
53
- return [prepend('*', push(push(push(buffer!, [html('strong', defrag(bs))]), defrag(ds)), [Command.Separator]))];
54
+ buffer.push(new Data(html('strong', defrag(unwrap(bs)))));
55
+ buffer.import(ds);
56
+ buffer.push(new Data(Command.Separator));
57
+ return prepend('*', buffer);
54
58
  }
55
59
  })
56
- ({ context }) ?? [prepend('*', push(push(buffer!, [html('strong', defrag(bs))]), [Command.Separator]))];
60
+ ({ context }) ?? prepend('*', buffer.import(new List([new Data(html('strong', defrag(unwrap(bs)))), new Data(Command.Separator)])));
57
61
  case '*':
58
62
  return bind<StrongParser>(
59
63
  substrong,
@@ -61,19 +65,24 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
61
65
  const { source } = context;
62
66
  if (source.startsWith('**', context.position)) {
63
67
  context.position += 2;
64
- return [[html('strong', push(push(buffer!, [html('em', defrag(bs))]), defrag(ds))), Command.Separator]];
68
+ buffer.push(new Data(html('em', defrag(unwrap(bs)))));
69
+ buffer.import(ds);
70
+ return new List([new Data(html('strong', defrag(unwrap(buffer)))), new Data(Command.Separator)]);
65
71
  }
66
72
  else {
67
- return [prepend('**', push(push(push(buffer!, [html('em', defrag(bs))]), defrag(ds)), [Command.Separator]))];
73
+ buffer.push(new Data(html('em', defrag(unwrap(bs)))));
74
+ buffer.import(ds);
75
+ buffer.push(new Data(Command.Separator));
76
+ return prepend('**', buffer);
68
77
  }
69
78
  })
70
- ({ context }) ?? [prepend('**', push(push(buffer!, [html('em', defrag(bs))]), [Command.Separator]))];
79
+ ({ context }) ?? prepend('**', buffer.import(new List([new Data(html('em', defrag(unwrap(bs)))), new Data(Command.Separator)])));
71
80
  }
72
81
  assert(false);
73
82
  },
74
- ([, bs], { buffer }) => bs && [push(push(buffer!, bs), [Command.Cancel])]),
83
+ ([, bs], { buffer }) => bs && buffer!.import(bs) && buffer!.push(new Data(Command.Cancel)) && buffer!),
75
84
  // 3以上の`*`に対してemの適用を保証する
76
- nodes => [html('em', [html('strong', defrag(nodes))])],
85
+ nodes => new List([new Data(html('em', [html('strong', defrag(unwrap(nodes)))]))]),
77
86
  (nodes, context, prefix, postfix, state) => {
78
87
  context.position += postfix;
79
88
  assert(postfix < 3);
@@ -82,10 +91,10 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
82
91
  case 0:
83
92
  break;
84
93
  case 1:
85
- nodes = [html('em', defrag(nodes))];
94
+ nodes = new List([new Data(html('em', defrag(unwrap(nodes))))]);
86
95
  break;
87
96
  case 2:
88
- nodes = [html('strong', defrag(nodes))];
97
+ nodes = new List([new Data(html('strong', defrag(unwrap(nodes))))]);
89
98
  break;
90
99
  default:
91
100
  assert(false);
@@ -96,51 +105,51 @@ export const emstrong: EmStrongParser = lazy(() => validate('***',
96
105
  case 0:
97
106
  break;
98
107
  case 1:
99
- nodes = bind<EmphasisParser>(
108
+ nodes = eval(bind<EmphasisParser>(
100
109
  subemphasis,
101
110
  ds => {
102
111
  const { source } = context;
103
112
  if (source.startsWith('*', context.position)) {
104
113
  context.position += 1;
105
- return [[html('em', push(nodes, defrag(ds)))]];
114
+ return new List([new Data(html('em', defrag(unwrap(nodes.import(ds)))))]);
106
115
  }
107
116
  else {
108
- return [prepend('*', push(nodes, defrag(ds)))];
117
+ return prepend('*', nodes.import(ds));
109
118
  }
110
119
  })
111
- ({ context })?.[0] ?? prepend('*', nodes);
120
+ ({ context })) ?? prepend('*', nodes);
112
121
  prefix -= 1;
113
122
  break;
114
123
  case 2:
115
- nodes = bind<StrongParser>(
124
+ nodes = eval(bind<StrongParser>(
116
125
  substrong,
117
126
  ds => {
118
127
  const { source } = context;
119
128
  if (source.startsWith('**', context.position)) {
120
129
  context.position += 2;
121
- return [[html('strong', push(nodes, defrag(ds)))]];
130
+ return new List([new Data(html('strong', defrag(unwrap(nodes.import(ds)))))]);
122
131
  }
123
132
  else {
124
- return [prepend('**', push(nodes, defrag(ds)))];
133
+ return prepend('**', nodes.import(ds));
125
134
  }
126
135
  })
127
- ({ context })?.[0] ?? prepend('**', nodes);
136
+ ({ context })) ?? prepend('**', nodes);
128
137
  prefix -= 2;
129
138
  break;
130
139
  }
131
140
  }
132
141
  if (prefix > postfix) {
133
- nodes = push(['*'.repeat(prefix - postfix)], nodes);
142
+ nodes = prepend('*'.repeat(prefix - postfix), nodes);
134
143
  }
135
- return [nodes];
144
+ return nodes;
136
145
  }))));
137
146
 
138
- function prepend<N>(prefix: string, nodes: N[]): N[] {
139
- if (typeof nodes[0] === 'string') {
140
- nodes[0] = prefix + nodes[0] as N;
147
+ function prepend<N>(prefix: string, nodes: List<Data<N>>): List<Data<N>> {
148
+ if (typeof nodes.head?.value === 'string') {
149
+ nodes.head.value = prefix + nodes.head.value as N;
141
150
  }
142
151
  else {
143
- unshift([prefix], nodes);
152
+ nodes.unshift(new Data(prefix as N));
144
153
  }
145
154
  return nodes;
146
155
  }
@@ -1,12 +1,13 @@
1
1
  import { ExtensionParser } from '../../inline';
2
2
  import { State, Backtrack } from '../../context';
3
+ import { List, Data } from '../../../combinator/data/parser';
3
4
  import { union, inits, some, precedence, state, constraint, validate, surround, lazy, fmap } from '../../../combinator';
4
5
  import { inline } from '../../inline';
5
6
  import { indexee, identity } from './indexee';
6
7
  import { unsafehtmlentity } from '../htmlentity';
7
8
  import { txt, str } from '../../source';
8
9
  import { tightStart, trimBlankNodeEnd } from '../../visibility';
9
- import { unshift } from 'spica/array';
10
+ import { unwrap } from '../../util';
10
11
  import { html, define, defrag } from 'typed-dom/dom';
11
12
 
12
13
  import IndexParser = ExtensionParser.IndexParser;
@@ -23,23 +24,23 @@ export const index: IndexParser = lazy(() => constraint(State.index, fmap(indexe
23
24
  false,
24
25
  ([, bs], context) =>
25
26
  context.linebreak === 0 && trimBlankNodeEnd(bs).length > 0
26
- ? [[html('a', { 'data-index': dataindex(bs) }, defrag(bs))]]
27
+ ? new List([new Data(html('a', { 'data-index': dataindex(bs) }, defrag(unwrap(bs))))])
27
28
  : undefined,
28
29
  undefined,
29
30
  [3 | Backtrack.bracket])),
30
31
  ns => {
31
32
  if (ns.length === 1) {
32
- const el = ns[0] as HTMLElement;
33
- return [
34
- define(el, {
33
+ const el = ns.head!.value as HTMLElement;
34
+ return new List([
35
+ new Data(define(el, {
35
36
  id: el.id ? null : undefined,
36
37
  class: 'index',
37
38
  href: el.id ? `#${el.id}` : undefined,
38
- })
39
- ];
39
+ }))
40
+ ]);
40
41
  }
41
42
  else {
42
- assert(ns.at(-1) === '');
43
+ assert(ns.last?.value === '');
43
44
  ns.pop();
44
45
  return ns;
45
46
  }
@@ -54,21 +55,23 @@ export const signature: IndexParser.SignatureParser = lazy(() => validate('|', s
54
55
  /(?=])/y,
55
56
  false,
56
57
  ([, ns], context) => {
57
- const index = identity('index', undefined, ns.join(''))?.slice(7);
58
+ const index = identity('index', undefined, ns.foldl((acc, { value }) => acc + value, ''))?.slice(7);
58
59
  return index && context.linebreak === 0
59
- ? [[html('span', { class: 'indexer', 'data-index': index })]]
60
+ ? new List([new Data(html('span', { class: 'indexer', 'data-index': index }))])
60
61
  : undefined;
61
62
  },
62
- ([as, bs]) => bs && [unshift(as, bs)],
63
+ ([as, bs]) => bs && as.import(bs),
63
64
  [3 | Backtrack.bracket])));
64
65
 
65
- export function dataindex(ns: readonly (string | HTMLElement)[]): string | undefined {
66
- if (ns.length === 0) return;
67
- for (let i = ns.length; i--;) {
68
- const node = ns[i];
69
- if (typeof node === 'string') return;
70
- if (i === ns.length - 1 && ['UL', 'OL'].includes(node.tagName)) continue;
71
- if (!node.classList.contains('indexer')) return;
72
- return node.getAttribute('data-index') ?? undefined;
66
+ export function dataindex(nodes: List<Data<string | HTMLElement>>): string | undefined {
67
+ let node = nodes.last;
68
+ if (typeof node?.value !== 'object') return;
69
+ switch (node.value.tagName) {
70
+ case 'UL':
71
+ case 'OL':
72
+ node = node.prev;
73
+ if (typeof node?.value !== 'object') return;
73
74
  }
75
+ if (!node.value.classList.contains('indexer')) return;
76
+ return node.value.getAttribute('data-index') ?? undefined;
74
77
  }
@@ -1,5 +1,5 @@
1
1
  import { MarkdownParser } from '../../../../markdown';
2
- import { Parser } from '../../../combinator/data/parser';
2
+ import { Parser, List, Data } from '../../../combinator/data/parser';
3
3
  import { fmap } from '../../../combinator';
4
4
  import { define } from 'typed-dom/dom';
5
5
 
@@ -7,7 +7,7 @@ export function indexee<P extends Parser<unknown, MarkdownParser.Context>>(parse
7
7
  export function indexee(parser: Parser<HTMLElement, MarkdownParser.Context>): Parser<HTMLElement> {
8
8
  return fmap(parser, (ns, { id }) =>
9
9
  ns.length === 1
10
- ? [define(ns[0], { id: identity('index', id, ns[0]), 'data-index': null })]
10
+ ? new List([new Data(define(ns.head!.value, { id: identity('index', id, ns.head!.value), 'data-index': null }))])
11
11
  : ns);
12
12
  }
13
13
 
@@ -1,4 +1,5 @@
1
1
  import { ExtensionParser } from '../../inline';
2
+ import { List, Data } from '../../../combinator/data/parser';
2
3
  import { union, focus, surround } from '../../../combinator';
3
4
  import { signature } from './index';
4
5
  import { html } from 'typed-dom/dom';
@@ -13,6 +14,6 @@ export const indexer: ExtensionParser.IndexerParser = surround(
13
14
  /\s\[(?=\|\S)/y,
14
15
  union([
15
16
  signature,
16
- focus(/\|(?=\])/y, () => [[html('span', { class: 'indexer', 'data-index': '' })]]),
17
+ focus(/\|(?=\])/y, () => new List([new Data(html('span', { class: 'indexer', 'data-index': '' }))])),
17
18
  ]),
18
19
  /\]\s*$/y);
@@ -1,5 +1,6 @@
1
1
  import { ExtensionParser } from '../../inline';
2
2
  import { State, Backtrack } from '../../context';
3
+ import { List, Data } from '../../../combinator/data/parser';
3
4
  import { union, constraint, clear, surround, fmap } from '../../../combinator';
4
5
  import { str } from '../../source';
5
6
  import { html } from 'typed-dom/dom';
@@ -16,9 +17,12 @@ export const label: ExtensionParser.LabelParser = constraint(State.label, fmap(
16
17
  surround('[', body, ']', false, undefined, undefined, [1 | Backtrack.bracket, 1]),
17
18
  body,
18
19
  ]),
19
- ([text]) => [
20
- html('a', { class: 'label', 'data-label': text.slice(text[1] === '-' ? 0 : 1).toLowerCase() }, text),
21
- ]));
20
+ ([{ value }]) => new List([
21
+ new Data(html('a', {
22
+ class: 'label',
23
+ 'data-label': value.slice(value[1] === '-' ? 0 : 1).toLowerCase(),
24
+ }, value)),
25
+ ])));
22
26
 
23
27
  export function number(label: string, base: string): string {
24
28
  return isFixed(label)
@@ -1,11 +1,11 @@
1
1
  import { ExtensionParser } from '../../inline';
2
2
  import { Recursion, Backtrack } from '../../context';
3
+ import { List, Data } from '../../../combinator/data/parser';
3
4
  import { union, some, recursion, precedence, surround, lazy } from '../../../combinator';
4
5
  import { inline } from '../../inline';
5
6
  import { str } from '../../source';
6
7
  import { tightStart } from '../../visibility';
7
8
  import { invalid } from '../../util';
8
- import { unshift } from 'spica/array';
9
9
  import { html } from 'typed-dom/dom';
10
10
 
11
11
  // Don't use the symbols already used: !#$%@&*+~=|
@@ -18,13 +18,13 @@ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => surroun
18
18
  precedence(1, recursion(Recursion.inline,
19
19
  tightStart(some(union([inline]), ']', [[']', 1]])))),
20
20
  str(']'), false,
21
- (_, context) => [[
22
- html('span',
21
+ (_, context) => new List([
22
+ new Data(html('span',
23
23
  {
24
24
  class: 'invalid',
25
25
  ...invalid('extension', 'syntax', `Invalid start symbol or linebreak`),
26
26
  },
27
- context.source.slice(context.position - context.range!, context.position))
28
- ]],
29
- ([as, bs]) => bs && [unshift(as, bs)],
27
+ context.source.slice(context.position - context.range!, context.position)))
28
+ ]),
29
+ ([as, bs]) => bs && as.import(bs as List<Data<string>>),
30
30
  [3 | Backtrack.bracket]));
@@ -1,13 +1,12 @@
1
1
  import { HTMLParser } from '../inline';
2
2
  import { Recursion } from '../context';
3
- import { Ctx } from '../../combinator/data/parser';
3
+ import { List, Data, Ctx } from '../../combinator/data/parser';
4
4
  import { union, some, recursion, precedence, validate, surround, open, match, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { str } from '../source';
7
7
  import { isLooseNodeStart, blankWith } from '../visibility';
8
- import { invalid } from '../util';
8
+ import { invalid, unwrap } from '../util';
9
9
  import { memoize } from 'spica/memoize';
10
- import { unshift, push } from 'spica/array';
11
10
  import { html as h, defrag } from 'typed-dom/dom';
12
11
 
13
12
  const tags: readonly string[] = ['wbr', 'bdo', 'bdi'];
@@ -28,10 +27,10 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
28
27
  some(union([attribute])),
29
28
  open(str(/ ?/y), str('>'), true),
30
29
  true,
31
- ([as, bs = [], cs], context) =>
32
- [[elem(as[0].slice(1), false, push(unshift(as, bs), cs), [], [], context)]],
33
- ([as, bs = []], context) =>
34
- [[elem(as[0].slice(1), false, unshift(as, bs), [], [], context)]]),
30
+ ([as, bs = new List(), cs], context) =>
31
+ new List([new Data(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs).import(cs))], new List(), new List(), context))]),
32
+ ([as, bs = new List()], context) =>
33
+ new List([new Data(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs))], new List(), new List(), context))])),
35
34
  match(
36
35
  new RegExp(String.raw`<(${TAGS.join('|')})(?=[^\S\n]|>)`, 'y'),
37
36
  memoize(
@@ -40,8 +39,8 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
40
39
  surround(
41
40
  str(`<${tag}`), some(attribute), open(str(/ ?/y), str('>'), true),
42
41
  true,
43
- ([as, bs = [], cs]) => [push(unshift(as, bs), cs)],
44
- ([as, bs = []]) => [unshift(as, bs)]),
42
+ ([as, bs = new List(), cs]) => as.import(bs).import(cs),
43
+ ([as, bs = new List()]) => as.import(bs)),
45
44
  // 不可視のHTML構造が可視構造を変化させるべきでない。
46
45
  // 可視のHTMLは優先度変更を検討する。
47
46
  // このため<>は将来的に共通構造を変化させる可能性があり
@@ -53,10 +52,10 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
53
52
  ])))),
54
53
  str(`</${tag}>`),
55
54
  true,
56
- ([as, bs = [], cs], context) =>
57
- [[elem(tag, true, as, bs, cs, context)]],
58
- ([as, bs = []], context) =>
59
- [[elem(tag, true, as, bs, [], context)]]),
55
+ ([as, bs = new List(), cs], context) =>
56
+ new List([new Data(elem(tag, true, [...unwrap(as)], bs, cs, context))]),
57
+ ([as, bs = new List()], context) =>
58
+ new List([new Data(elem(tag, true, [...unwrap(as)], bs, new List(), context))])),
60
59
  ([, tag]) => tag,
61
60
  new Map())),
62
61
  surround(
@@ -65,10 +64,10 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
65
64
  some(union([attribute])),
66
65
  open(str(/ ?/y), str('>'), true),
67
66
  true,
68
- ([as, bs = [], cs], context) =>
69
- [[elem(as[0].slice(1), false, push(unshift(as, bs), cs), [], [], context)]],
70
- ([as, bs = []], context) =>
71
- [[elem(as[0].slice(1), false, unshift(as, bs), [], [], context)]]),
67
+ ([as, bs = new List(), cs], context) =>
68
+ new List([new Data(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs).import(cs))], new List(), new List(), context))]),
69
+ ([as, bs = new List()], context) =>
70
+ new List([new Data(elem(as.head!.value.slice(1), false, [...unwrap(as.import(bs))], new List(), new List(), context))])),
72
71
  ])));
73
72
 
74
73
  export const attribute: HTMLParser.AttributeParser = union([
@@ -76,7 +75,7 @@ export const attribute: HTMLParser.AttributeParser = union([
76
75
  str(/ [^\s<>]+/y),
77
76
  ]);
78
77
 
79
- function elem(tag: string, content: boolean, as: string[], bs: (HTMLElement | string)[], cs: string[], context: Ctx): HTMLElement {
78
+ function elem(tag: string, content: boolean, as: readonly string[], bs: List<Data<HTMLElement | string>>, cs: List<Data<string>>, context: Ctx): HTMLElement {
80
79
  assert(as.length > 0);
81
80
  assert(as[0][0] === '<');
82
81
  if (!tags.includes(tag)) return ielem('tag', `Invalid HTML tag name "${tag}"`, context);
@@ -88,7 +87,7 @@ function elem(tag: string, content: boolean, as: string[], bs: (HTMLElement | st
88
87
  const [attrs] = attributes('html', attrspecs[tag], as.slice(1, as.at(-1) === '>' ? -1 : as.length));
89
88
  if (/(?<!\S)invalid(?!\S)/.test(attrs['class'] ?? '')) return ielem('attribute', 'Invalid HTML attribute', context)
90
89
  if (as.at(-1) !== '>') return ielem('tag', `Missing the closing symbol ">"`, context);
91
- return h(tag as 'span', attrs, defrag(bs));
90
+ return h(tag as 'span', attrs, defrag(unwrap(bs)));
92
91
  }
93
92
 
94
93
  function ielem(type: string, message: string, context: Ctx): HTMLElement {
@@ -105,7 +104,7 @@ const requiredAttributes = memoize(
105
104
  export function attributes(
106
105
  syntax: string,
107
106
  spec: Readonly<Record<string, readonly (string | undefined)[] | undefined>> | undefined,
108
- params: readonly string[],
107
+ params: Iterable<string>,
109
108
  ): [Record<string, string | undefined>, string[]] {
110
109
  assert(spec instanceof Object === false);
111
110
  assert(!spec?.['__proto__']);
@@ -113,17 +112,17 @@ export function attributes(
113
112
  const remains = [];
114
113
  let invalidation = false;
115
114
  const attrs: Record<string, string | undefined> = {};
116
- for (let i = 0; i < params.length; ++i) {
117
- const param = params[i].trimStart();
118
- if (param === '') continue;
119
- const name = param.split('=', 1)[0];
120
- const value = param !== name
121
- ? param.slice(name.length + 2, -1).replace(/\\(.?)/g, '$1')
115
+ for (const param of params) {
116
+ const attr = param.trimStart();
117
+ if (attr === '') continue;
118
+ const name = attr.split('=', 1)[0];
119
+ const value = attr !== name
120
+ ? attr.slice(name.length + 2, -1).replace(/\\(.?)/g, '$1')
122
121
  : undefined;
123
122
  invalidation ||= name === '' || !spec || name in attrs;
124
- if (name === '')continue;
123
+ if (name === '') continue;
125
124
  if (spec && name in spec && !spec[name]) {
126
- remains.push(params[i]);
125
+ remains.push(param);
127
126
  continue;
128
127
  }
129
128
  if (spec?.[name]?.includes(value) || spec?.[name]?.length === 0 && value !== undefined) {
@@ -1,4 +1,5 @@
1
1
  import { HTMLEntityParser, UnsafeHTMLEntityParser } from '../inline';
2
+ import { List, Data } from '../../combinator/data/parser';
2
3
  import { union, focus, fmap } from '../../combinator';
3
4
  import { invalid } from '../util';
4
5
  import { html } from 'typed-dom/dom';
@@ -10,20 +11,20 @@ export const unsafehtmlentity: UnsafeHTMLEntityParser = focus(
10
11
  const { source } = context;
11
12
  context.position += source.length;
12
13
  return source.length > 1 && source.at(-1) === ';'
13
- ? [[parser(source) ?? source]]
14
- : [[source]];
14
+ ? new List([new Data(parser(source) ?? source)])
15
+ : new List([new Data(source)]);
15
16
  });
16
17
 
17
18
  export const htmlentity: HTMLEntityParser = fmap(
18
19
  union([unsafehtmlentity]),
19
- ([text]) => [
20
- length === 1 || text.at(-1) !== ';'
21
- ? text
22
- : html('span', {
20
+ ([{ value }]) => new List([
21
+ length === 1 || value.at(-1) !== ';'
22
+ ? new Data(value)
23
+ : new Data(html('span', {
23
24
  class: 'invalid',
24
25
  ...invalid('htmlentity', 'syntax', 'Invalid HTML entity'),
25
- }, text)
26
- ]);
26
+ }, value))
27
+ ]));
27
28
 
28
29
  const parser = (el => (entity: string): string | undefined => {
29
30
  if (entity === '&NewLine;') return ' ';
@@ -1,10 +1,10 @@
1
1
  import { InsertionParser } from '../inline';
2
2
  import { Recursion, Command } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, some, recursion, precedence, validate, surround, open, lazy } from '../../combinator';
4
5
  import { inline } from '../inline';
5
6
  import { blankWith } from '../visibility';
6
- import { repeat } from '../util';
7
- import { push } from 'spica/array';
7
+ import { unwrap, repeat } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const insertion: InsertionParser = lazy(() => validate('++',
@@ -16,6 +16,6 @@ export const insertion: InsertionParser = lazy(() => validate('++',
16
16
  open('\n', some(inline, '+'), true),
17
17
  ]))),
18
18
  '++', false,
19
- ([, bs], { buffer }) => [push(buffer!, bs)],
20
- ([, bs], { buffer }) => bs && [push(push(buffer!, bs), [Command.Cancel])]),
21
- nodes => [html('ins', defrag(nodes))]))));
19
+ ([, bs], { buffer }) => buffer!.import(bs),
20
+ ([, bs], { buffer }) => bs && buffer!.import(bs).push(new Data(Command.Cancel)) && buffer!),
21
+ nodes => new List([new Data(html('ins', defrag(unwrap(nodes))))])))));
@@ -1,10 +1,10 @@
1
1
  import { ItalicParser } from '../inline';
2
2
  import { Recursion, Command } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, some, recursion, precedence, validate, surround, open, lazy } from '../../combinator';
4
5
  import { inline } from '../inline';
5
6
  import { tightStart, blankWith } from '../visibility';
6
- import { repeat } from '../util';
7
- import { push } from 'spica/array';
7
+ import { unwrap, repeat } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  // 可読性のため実際にはオブリーク体を指定する。
@@ -19,6 +19,6 @@ export const italic: ItalicParser = lazy(() => validate('///',
19
19
  open(some(inline, '/'), inline),
20
20
  ])))),
21
21
  '///', false,
22
- ([, bs], { buffer }) => [push(buffer!, bs)],
23
- ([, bs], { buffer }) => bs && [push(push(buffer!, bs), [Command.Cancel])]),
24
- nodes => [html('i', defrag(nodes))]))));
22
+ ([, bs], { buffer }) => buffer!.import(bs),
23
+ ([, bs], { buffer }) => bs && buffer!.import(bs).push(new Data(Command.Cancel)) && buffer!),
24
+ nodes => new List([new Data(html('i', defrag(unwrap(nodes))))])))));