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,11 +1,11 @@
1
1
  import { RubyParser } from '../inline';
2
2
  import { Backtrack } from '../context';
3
- import { eval } from '../../combinator/data/parser';
3
+ import { List, Data, eval } from '../../combinator/data/parser';
4
4
  import { inits, surround, setBacktrack, dup, lazy, bind } from '../../combinator';
5
5
  import { unsafehtmlentity } from './htmlentity';
6
6
  import { txt } from '../source';
7
7
  import { isTightNodeStart } from '../visibility';
8
- import { unshift, push } from 'spica/array';
8
+ import { unwrap } from '../util';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
11
11
  export const ruby: RubyParser = lazy(() => bind(
@@ -14,8 +14,8 @@ export const ruby: RubyParser = lazy(() => bind(
14
14
  '[', text, ']',
15
15
  false,
16
16
  ([, ns]) => {
17
- ns && ns.at(-1) === '' && ns.pop();
18
- return isTightNodeStart(ns) ? [ns] : undefined;
17
+ ns && ns.last?.value === '' && ns.pop();
18
+ return isTightNodeStart(ns) ? ns : undefined;
19
19
  },
20
20
  undefined,
21
21
  [1 | Backtrack.bracket, 3 | Backtrack.ruby])),
@@ -24,48 +24,49 @@ export const ruby: RubyParser = lazy(() => bind(
24
24
  false, undefined, undefined,
25
25
  [1 | Backtrack.bracket, 3 | Backtrack.ruby])),
26
26
  ]),
27
- ([texts, rubies], context) => {
27
+ ([{ value: texts }, { value: rubies = undefined } = {}], context) => {
28
28
  if (rubies === undefined) {
29
29
  const head = context.position - context.range!;
30
30
  return void setBacktrack(context, [2 | Backtrack.ruby], head);
31
31
  }
32
32
  switch (true) {
33
- case rubies.length <= texts.length:
34
- return [[
35
- html('ruby', defrag(texts
36
- .reduce((acc, _, i) =>
37
- push(acc, unshift(
38
- [texts[i]],
39
- i < rubies.length && rubies[i]
40
- ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
41
- : [html('rt')]))
42
- , []))),
43
- ]];
44
- case texts.length === 1 && [...texts[0]].length >= rubies.length:
45
- return [[
46
- html('ruby', defrag([...texts[0]]
47
- .reduce((acc, _, i, texts) =>
48
- push(acc, unshift(
49
- [texts[i]],
50
- i < rubies.length && rubies[i]
51
- ? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
52
- : [html('rt')]))
53
- , []))),
54
- ]];
33
+ case texts.length >= rubies.length:
34
+ return new List([
35
+ new Data(html('ruby', defrag(unwrap([...zip(texts, rubies)]
36
+ .reduce((acc, [{ value: text = '' } = {}, { value: ruby = '' } = {}]) =>
37
+ acc.import(
38
+ ruby
39
+ ? new List([new Data(text), new Data(html('rp', '(')), new Data(html('rt', ruby)), new Data(html('rp', ')'))])
40
+ : new List([new Data(text), new Data(html('rt'))]))
41
+ , new List<Data<string | HTMLElement>>()))))),
42
+ ]);
43
+ case texts.length === 1 && [...texts.head!.value].length >= rubies.length:
44
+ return new List([
45
+ new Data(html('ruby', defrag(unwrap([...zip(new List([...texts.head!.value].map(char => new Data(char))), rubies)]
46
+ .reduce((acc, [{ value: text = '' } = {}, { value: ruby = '' } = {}]) =>
47
+ acc.import(
48
+ ruby
49
+ ? new List([new Data(text), new Data(html('rp', '(')), new Data(html('rt', ruby)), new Data(html('rp', ')'))])
50
+ : new List([new Data(text), new Data(html('rt'))]))
51
+ , new List<Data<string | HTMLElement>>()))))),
52
+ ]);
55
53
  default:
56
54
  assert(rubies.length > 0);
57
- return [[
58
- html('ruby', defrag(unshift(
59
- [texts.join(' ')],
60
- [html('rp', '('), html('rt', rubies.join(' ').trim()), html('rp', ')')]))),
61
- ]];
55
+ return new List([
56
+ new Data(html('ruby', defrag(unwrap(new List<Data<string | HTMLElement>>([
57
+ new Data(texts.foldr(({ value }, acc) => value + ' ' + acc, '').slice(0, -1)),
58
+ new Data(html('rp', '(')),
59
+ new Data(html('rt', rubies.foldr(({ value }, acc) => value + ' ' + acc, '').trim())),
60
+ new Data(html('rp', ')')),
61
+ ]))))),
62
+ ]);
62
63
  }
63
64
  }));
64
65
 
65
66
  const text: RubyParser.TextParser = input => {
66
67
  const { context } = input;
67
68
  const { source } = context;
68
- const acc = [''];
69
+ const acc = new List([new Data('')]);
69
70
  let state = false;
70
71
  context.sequential = true;
71
72
  for (let { position } = context; position < source.length; position = context.position) {
@@ -75,41 +76,37 @@ const text: RubyParser.TextParser = input => {
75
76
  case '&': {
76
77
  const result = unsafehtmlentity(input) ?? txt(input)!;
77
78
  assert(result);
78
- acc[acc.length - 1] += eval(result)[0];
79
+ acc.last!.value += eval(result).head!.value;
79
80
  continue;
80
81
  }
81
82
  default: {
82
83
  if (source[position].trimStart() === '') {
83
- state ||= acc.at(-1)!.trimStart() !== '';
84
- acc.push('');
84
+ state ||= acc.last!.value.trimStart() !== '';
85
+ acc.push(new Data(''));
85
86
  context.position += 1;
86
87
  continue;
87
88
  }
88
89
  const result = txt(input)!;
89
90
  assert(result);
90
- acc[acc.length - 1] += eval(result)[0] ?? source.slice(position, context.position);
91
+ acc.last!.value += eval(result).head?.value ?? '';
91
92
  continue;
92
93
  }
93
94
  }
94
95
  }
95
96
  context.sequential = false;
96
- state ||= acc.at(-1)!.trimStart() !== '';
97
+ state ||= acc.last!.value.trimStart() !== '';
97
98
  return state
98
- ? [acc]
99
+ ? acc
99
100
  : undefined;
100
101
  };
101
102
 
102
- //function attributes(texts: string[], rubies: string[]): Record<string, string> {
103
- // let attrs: Record<string, string> | undefined;
104
- // for (const ss of [texts, rubies]) {
105
- // for (let i = 0; i < ss.length; ++i) {
106
- // if (!ss[i].includes(Command.Error)) continue;
107
- // ss[i] = ss[i].replace(CmdRegExp.Error, '');
108
- // attrs ??= {
109
- // class: 'invalid',
110
- // ...invalid('ruby', ss === texts ? 'content' : 'argument', 'Invalid HTML entity'),
111
- // };
112
- // }
113
- // }
114
- // return attrs ?? {};
115
- //}
103
+ function* zip<N extends List.Node>(a: List<N>, b: List<N>): Iterable<[N | undefined, N | undefined]> {
104
+ const ia = a[Symbol.iterator]();
105
+ const ib = b[Symbol.iterator]();
106
+ while (true) {
107
+ const ra = ia.next();
108
+ const rb = ib.next();
109
+ if (ra.done) break;
110
+ yield [ra.value, rb.value];
111
+ }
112
+ }
@@ -1,11 +1,12 @@
1
1
  import { StrongParser } from '../inline';
2
2
  import { Recursion } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, some, recursion, precedence, surround, open, lazy } from '../../combinator';
4
5
  import { inline } from '../inline';
5
6
  import { emphasis } from './emphasis';
6
7
  import { str } from '../source';
7
8
  import { tightStart, blankWith } from '../visibility';
8
- import { unshift } from 'spica/array';
9
+ import { unwrap } from '../util';
9
10
  import { html, defrag } from 'typed-dom/dom';
10
11
 
11
12
  export const strong: StrongParser = lazy(() => surround(
@@ -17,5 +18,5 @@ export const strong: StrongParser = lazy(() => surround(
17
18
  open(some(inline, '*'), inline),
18
19
  ]))))),
19
20
  str('**'), false,
20
- ([, bs]) => [[html('strong', defrag(bs))]],
21
- ([as, bs]) => bs && [unshift(as, bs)]));
21
+ ([, bs]) => new List([new Data(html('strong', defrag(unwrap(bs))))]),
22
+ ([as, bs]) => bs && as.import(bs as List<Data<string>>)));
@@ -1,9 +1,9 @@
1
1
  import { TemplateParser } 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 { escsource, str } from '../source';
5
- import { invalid } from '../util';
6
- import { unshift, push } from 'spica/array';
6
+ import { unwrap, invalid } from '../util';
7
7
  import { html, defrag } from 'typed-dom/dom';
8
8
 
9
9
  export const template: TemplateParser = lazy(() => surround(
@@ -12,35 +12,36 @@ export const template: TemplateParser = lazy(() => surround(
12
12
  some(union([bracket, escsource]), '}')),
13
13
  str('}}'),
14
14
  true,
15
- ([as, bs = [], cs]) =>
16
- [[html('span', { class: 'template' }, defrag(push(unshift(as, bs), cs)))]],
15
+ ([as, bs = new List(), cs]) => new List([
16
+ new Data(html('span', { class: 'template' }, defrag(unwrap(as.import(bs as List<Data<string>>).import(cs)))))
17
+ ]),
17
18
  ([, bs], context) =>
18
- bs && [[
19
- html('span',
19
+ bs && new List([
20
+ new Data(html('span',
20
21
  {
21
22
  class: 'invalid',
22
23
  ...invalid('template', 'syntax', `Missing the closing symbol "}}"`),
23
24
  },
24
- context.source.slice(context.position - context.range!, context.position))
25
- ]],
25
+ context.source.slice(context.position - context.range!, context.position)))
26
+ ]),
26
27
  [3 | Backtrack.doublebracket, 3 | Backtrack.escbracket]));
27
28
 
28
29
  const bracket: TemplateParser.BracketParser = lazy(() => union([
29
30
  surround(str('('), recursion(Recursion.terminal, some(union([bracket, escsource]), ')')), str(')'), true,
30
- undefined, () => [[]], [3 | Backtrack.escbracket]),
31
+ undefined, () => new List(), [3 | Backtrack.escbracket]),
31
32
  surround(str('['), recursion(Recursion.terminal, some(union([bracket, escsource]), ']')), str(']'), true,
32
- undefined, () => [[]], [3 | Backtrack.escbracket]),
33
+ undefined, () => new List(), [3 | Backtrack.escbracket]),
33
34
  surround(str('{'), recursion(Recursion.terminal, some(union([bracket, escsource]), '}')), str('}'), true,
34
- undefined, () => [[]], [3 | Backtrack.escbracket]),
35
+ undefined, () => new List(), [3 | Backtrack.escbracket]),
35
36
  surround(
36
37
  str('"'),
37
38
  precedence(2, recursion(Recursion.terminal, some(escsource, /["\n]/y, [['"', 2], ['\n', 3]]))),
38
39
  str('"'),
39
40
  true,
40
- ([as, bs = [], cs], context) =>
41
+ ([as, bs = new List(), cs], context) =>
41
42
  context.linebreak === 0
42
- ? [push(unshift(as, bs), cs)]
43
- : (context.position -= 1, [unshift(as, bs)]),
44
- ([as, bs]) => bs && [unshift(as, bs)],
43
+ ? as.import(bs as List<Data<string>>).import(cs)
44
+ : (context.position -= 1, as.import(bs as List<Data<string>>)),
45
+ ([as, bs]) => bs && as.import(bs as List<Data<string>>),
45
46
  [3 | Backtrack.escbracket]),
46
47
  ]));
@@ -56,9 +56,9 @@ describe('Unit: parser/inline', () => {
56
56
  assert.deepStrictEqual(inspect(parser('**a*b***'), ctx), [['<strong>a<em>b</em></strong>'], '']);
57
57
  assert.deepStrictEqual(inspect(parser('**a*b***c'), ctx), [['<strong>a<em>b</em></strong>', 'c'], '']);
58
58
  assert.deepStrictEqual(inspect(parser('**a**b****'), ctx), [['<strong>a</strong>', 'b', '****'], '']);
59
- assert.deepStrictEqual(inspect(parser('**a**b****c'), ctx), [['<strong>a</strong>', 'b', '****', 'c'], '']);
59
+ assert.deepStrictEqual(inspect(parser('**a**b****c'), ctx), [['<strong>a</strong>', 'b', '****c'], '']);
60
60
  assert.deepStrictEqual(inspect(parser('**a***b*****'), ctx), [['<strong>a</strong>', '<em>b</em>', '****'], '']);
61
- assert.deepStrictEqual(inspect(parser('**a***b*****c'), ctx), [['<strong>a</strong>', '<em>b</em>', '****', 'c'], '']);
61
+ assert.deepStrictEqual(inspect(parser('**a***b*****c'), ctx), [['<strong>a</strong>', '<em>b</em>', '****c'], '']);
62
62
  assert.deepStrictEqual(inspect(parser('**a *b***'), ctx), [['<strong>a <em>b</em></strong>'], '']);
63
63
  assert.deepStrictEqual(inspect(parser('**a *b***c'), ctx), [['<strong>a <em>b</em></strong>', 'c'], '']);
64
64
  assert.deepStrictEqual(inspect(parser('**a **b****'), ctx), [['<strong>a <strong>b</strong></strong>'], '']);
@@ -68,7 +68,7 @@ describe('Unit: parser/inline', () => {
68
68
  assert.deepStrictEqual(inspect(parser('***a*'), ctx), [['**', '<em>a</em>'], '']);
69
69
  assert.deepStrictEqual(inspect(parser('***a*b'), ctx), [['**', '<em>a</em>', 'b'], '']);
70
70
  assert.deepStrictEqual(inspect(parser('***a*b*'), ctx), [['**', '<em>a</em>', 'b', '*'], '']);
71
- assert.deepStrictEqual(inspect(parser('***a*b*c'), ctx), [['**', '<em>a</em>', 'b*c'], '']);
71
+ assert.deepStrictEqual(inspect(parser('***a*b*c'), ctx), [['**', '<em>a</em>', 'b', '*', 'c'], '']);
72
72
  assert.deepStrictEqual(inspect(parser('***a*b*c*'), ctx), [['**', '<em>a</em>', 'b', '<em>c</em>'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('***a*b*c**'), ctx), [['**', '<em>a</em>', 'b', '<em>c</em>', '*'], '']);
74
74
  assert.deepStrictEqual(inspect(parser('***a*b*c***'), ctx), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
@@ -53,7 +53,9 @@ export function* segment(source: string): Generator<string, undefined, undefined
53
53
  const result = parser(input)!;
54
54
  assert(result);
55
55
  assert(context.position > position);
56
- const segs = eval(result).length ? eval(result) : [source.slice(position, context.position)];
56
+ const segs = eval(result).length > 0
57
+ ? eval(result).foldl<string[]>((acc, { value }) => void acc.push(value) || acc, [])
58
+ : [source.slice(position, context.position)];
57
59
  assert(segs.join('') === source.slice(position, context.position));
58
60
  for (let i = 0; i < segs.length; ++i) {
59
61
  const seg = segs[i];
@@ -1,5 +1,6 @@
1
1
  import { EscapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { consume } from '../../combinator';
4
5
  import { next } from './text';
5
6
  import { html } from 'typed-dom/dom';
@@ -16,33 +17,33 @@ export const escsource: EscapableSourceParser = ({ context }) => {
16
17
  case '\r':
17
18
  assert(!source.includes('\r', position + 1));
18
19
  consume(-1, context);
19
- return [[]];
20
+ return new List();
20
21
  case Command.Escape:
21
22
  consume(1, context);
22
23
  context.position += 1;
23
- return [[source.slice(position + 1, position + 2)]];
24
+ return new List([new Data(source.slice(position + 1, position + 2))]);
24
25
  case '\\':
25
26
  switch (source[position + 1]) {
26
27
  case undefined:
27
- return [[char]];
28
+ return new List([new Data(char)]);
28
29
  case '\n':
29
- return [[char]];
30
+ return new List([new Data(char)]);
30
31
  default:
31
32
  consume(1, context);
32
33
  context.position += 1;
33
- return [[source.slice(position, position + 2)]];
34
+ return new List([new Data(source.slice(position, position + 2))]);
34
35
  }
35
36
  case '\n':
36
37
  context.linebreak ||= source.length - position;
37
- return [[html('br')]];
38
+ return new List([new Data(html('br'))]);
38
39
  default:
39
40
  assert(char !== '\n');
40
- if (context.sequential) return [[char]];
41
+ if (context.sequential) return new List([new Data(char)]);
41
42
  let i = next(source, position, delimiter);
42
43
  assert(i > position);
43
44
  i -= position;
44
45
  consume(i - 1, context);
45
46
  context.position += i - 1;
46
- return [[source.slice(position, context.position)]];
47
+ return new List([new Data(source.slice(position, context.position))]);
47
48
  }
48
49
  };
@@ -1,10 +1,11 @@
1
1
  import { AnyLineParser, EmptyLineParser, ContentLineParser } from '../source';
2
+ import { List } from '../../combinator/data/parser';
2
3
 
3
4
  export const anyline: AnyLineParser = input => {
4
5
  const { context } = input;
5
6
  const { source, position } = context;
6
7
  context.position = source.indexOf('\n', position) + 1 || source.length;
7
- return [[]];
8
+ return new List();
8
9
  };
9
10
  const regEmptyline = /[^\S\n]*(?:$|\n)/y;
10
11
  export const emptyline: EmptyLineParser = input => {
@@ -15,7 +16,7 @@ export const emptyline: EmptyLineParser = input => {
15
16
  const i = regEmptyline.lastIndex;
16
17
  if (i === 0) return;
17
18
  context.position = i;
18
- return [[]];
19
+ return new List();
19
20
  };
20
21
  const regContentline = /[^\S\n]*\S[^\n]*(?:$|\n)/y;
21
22
  export const contentline: ContentLineParser = input => {
@@ -26,5 +27,5 @@ export const contentline: ContentLineParser = input => {
26
27
  const i = regContentline.lastIndex;
27
28
  if (i === 0) return;
28
29
  context.position = i;
29
- return [[]];
30
+ return new List();
30
31
  }
@@ -1,5 +1,5 @@
1
1
  import { StrParser } from '../source';
2
- import { Parser } from '../../combinator/data/parser';
2
+ import { Parser, List, Data } from '../../combinator/data/parser';
3
3
  import { matcher } from '../../combinator';
4
4
 
5
5
  export function str(pattern: string | RegExp): StrParser;
@@ -17,6 +17,6 @@ export function strs(pattern: string): Parser<string> {
17
17
  acc += pattern;
18
18
  context.position += pattern.length;
19
19
  }
20
- return [[acc]];
20
+ return new List([new Data(acc)]);
21
21
  };
22
22
  }
@@ -1,5 +1,6 @@
1
1
  import { TextParser, TxtParser, LinebreakParser } from '../source';
2
2
  import { Command } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { union, consume, focus } from '../../combinator';
4
5
  import { html } from 'typed-dom/dom';
5
6
 
@@ -17,26 +18,26 @@ export const text: TextParser = input => {
17
18
  case '\r':
18
19
  assert(!source.includes('\r', position + 1));
19
20
  consume(-1, context);
20
- return [[]];
21
+ return new List();
21
22
  case Command.Escape:
22
23
  case '\\':
23
24
  switch (source[position + 1]) {
24
25
  case undefined:
25
- return [[]];
26
+ return new List();
26
27
  case '\n':
27
28
  assert(char !== Command.Escape);
28
- return [[]];
29
+ return new List();
29
30
  default:
30
31
  consume(1, context);
31
32
  context.position += 1;
32
- return [[source.slice(position + 1, context.position)]];
33
+ return new List([new Data(source.slice(position + 1, context.position))]);
33
34
  }
34
35
  case '\n':
35
36
  context.linebreak ||= source.length - position;
36
- return [[html('br')]];
37
+ return new List([new Data(html('br'))]);
37
38
  default:
38
39
  assert(char !== '\n');
39
- if (context.sequential) return [[char]];
40
+ if (context.sequential) return new List([new Data(char)]);
40
41
  nonWhitespace.lastIndex = position + 1;
41
42
  const s = canSkip(source, position);
42
43
  let i = s
@@ -54,8 +55,8 @@ export const text: TextParser = input => {
54
55
  context.position += i - 1;
55
56
  const linestart = position === 0 || source[position - 1] === '\n';
56
57
  return position === context.position || s && !linestart || lineend
57
- ? [[]]
58
- : [[source.slice(position, context.position)]];
58
+ ? new List()
59
+ : new List([new Data(source.slice(position, context.position))]);
59
60
  }
60
61
  };
61
62
 
@@ -136,36 +137,30 @@ export function backToWhitespace(source: string, position: number, index: number
136
137
  export function backToUrlHead(source: string, position: number, index: number): number {
137
138
  const delim = index;
138
139
  let state = false;
139
- let offset = 0;
140
- for (let i = index; --i > position;) {
141
- index = i;
140
+ for (let i = index - 1; i >= position; --i) {
142
141
  const char = source[i];
143
142
  if (state) switch (char) {
144
143
  case '.':
145
144
  case '+':
146
145
  case '-':
147
146
  state = false;
148
- offset = 1;
149
147
  continue;
150
148
  }
151
149
  if (isAlphanumeric(char)) {
152
150
  state = true;
153
- offset = 0;
151
+ index = i;
154
152
  continue;
155
153
  }
156
154
  break;
157
155
  }
158
- if (index === position + 1 && offset === 0 && isAlphanumeric(source[index - 1])) {
159
- return delim;
160
- }
161
- return index + offset;
156
+ return index === position
157
+ ? delim
158
+ : index;
162
159
  }
163
160
  export function backToEmailHead(source: string, position: number, index: number): number {
164
161
  const delim = index;
165
162
  let state = false;
166
- let offset = 0;
167
- for (let i = index; --i > position;) {
168
- index = i;
163
+ for (let i = index - 1; i >= position; --i) {
169
164
  const char = source[i];
170
165
  if (state) switch (char) {
171
166
  case '_':
@@ -173,20 +168,18 @@ export function backToEmailHead(source: string, position: number, index: number)
173
168
  case '+':
174
169
  case '-':
175
170
  state = false;
176
- offset = 1;
177
171
  continue;
178
172
  }
179
173
  if (isAlphanumeric(char)) {
180
174
  state = true;
181
- offset = 0;
175
+ index = i;
182
176
  continue;
183
177
  }
184
178
  break;
185
179
  }
186
- if (index === position + 1 && offset === 0 && isAlphanumeric(source[index - 1])) {
187
- return delim;
188
- }
189
- return index + offset;
180
+ return index === position
181
+ ? delim
182
+ : index;
190
183
  }
191
184
  function isAlphanumeric(char: string): boolean {
192
185
  assert(char.length === 1);
@@ -1,5 +1,6 @@
1
1
  import { UnescapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
+ import { List, Data } from '../../combinator/data/parser';
3
4
  import { consume } from '../../combinator';
4
5
  import { nonWhitespace, canSkip, next } from './text';
5
6
  import { html } from 'typed-dom/dom';
@@ -16,17 +17,17 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
16
17
  case '\r':
17
18
  assert(!source.includes('\r', position + 1));
18
19
  consume(-1, context);
19
- return [[]];
20
+ return new List();
20
21
  case Command.Escape:
21
22
  consume(1, context);
22
23
  context.position += 1;
23
- return [[source.slice(position + 1, position + 2)]];
24
+ return new List([new Data(source.slice(position + 1, position + 2))]);
24
25
  case '\n':
25
26
  context.linebreak ||= source.length - position;
26
- return [[html('br')]];
27
+ return new List([new Data(html('br'))]);
27
28
  default:
28
29
  assert(char !== '\n');
29
- if (context.sequential) return [[char]];
30
+ if (context.sequential) return new List([new Data(char)]);
30
31
  nonWhitespace.lastIndex = position + 1;
31
32
  let i = canSkip(source, position)
32
33
  ? nonWhitespace.test(source)
@@ -37,6 +38,6 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
37
38
  i -= position;
38
39
  consume(i - 1, context);
39
40
  context.position += i - 1;
40
- return [[source.slice(position, context.position)]];
41
+ return new List([new Data(source.slice(position, context.position))]);
41
42
  }
42
43
  };
@@ -1,47 +1,34 @@
1
1
  import { min } from 'spica/alias';
2
2
  import { MarkdownParser } from '../../markdown';
3
3
  import { Command } from './context';
4
- import { Parser, Result, Ctx, Node, Context, eval, failsafe } from '../combinator/data/parser';
5
- import { convert } from '../combinator';
4
+ import { Parser, Result, List, Data, Ctx, Node, Context, eval, failsafe } from '../combinator/data/parser';
6
5
  import { define } from 'typed-dom/dom';
7
6
 
8
- export function linearize<P extends Parser<HTMLElement | string>>(parser: P, trim?: 0 | 1 | -1): P;
9
- export function linearize<N extends HTMLElement | string>(parser: Parser<N>, trim = 0): Parser<N> {
10
- return convert(
11
- source => `${
12
- trim === 0
13
- ? source
14
- : trim > 0
15
- ? source.at(-1) === '\n'
16
- ? source
17
- : source + '\n'
18
- : source.at(-1) === '\n'
19
- ? source.slice(0, -1)
20
- : source
21
- }`,
22
- parser,
23
- trim === 0);
7
+ export function* unwrap<N>(nodes: List<Data<N>>): Iterable<N> {
8
+ for (const node of nodes) {
9
+ yield node.value;
10
+ }
24
11
  }
25
12
 
26
- export function repeat<P extends Parser<HTMLElement | string, MarkdownParser.Context>>(symbol: string, parser: P, cons: (nodes: Node<P>[], context: Context<P>) => Node<P>[], termination?: (acc: Node<P>[], context: Ctx, prefix: number, postfix: number, state: boolean) => Result<string | Node<P>>): P;
27
- export function repeat<N extends HTMLElement | string>(symbol: string, parser: Parser<N>, cons: (nodes: N[], context: MarkdownParser.Context) => N[], termination: (acc: N[], context: Ctx, prefix: number, postfix: number, state: boolean) => Result<string | N, MarkdownParser.Context> = (nodes, context, prefix, postfix) => {
28
- const acc = [];
13
+ export function repeat<P extends Parser<HTMLElement | string, MarkdownParser.Context>>(symbol: string, parser: P, cons: (nodes: List<Data<Node<P>>>, context: Context<P>) => List<Data<Node<P>>>, termination?: (acc: List<Data<Node<P>>>, context: Ctx, prefix: number, postfix: number, state: boolean) => Result<string | Node<P>>): P;
14
+ export function repeat<N extends HTMLElement | string>(symbol: string, parser: Parser<N>, cons: (nodes: List<Data<N>>, context: MarkdownParser.Context) => List<Data<N>>, termination: (acc: List<Data<N>>, context: Ctx, prefix: number, postfix: number, state: boolean) => Result<string | N, MarkdownParser.Context> = (nodes, context, prefix, postfix) => {
15
+ const acc = new List<Data<string | N>>();
29
16
  if (prefix > 0) {
30
- acc.push(symbol[0].repeat(prefix));
17
+ acc.push(new Data(symbol[0].repeat(prefix)));
31
18
  }
32
- acc.push(...nodes);
19
+ acc.import(nodes);
33
20
  if (postfix > 0) {
34
21
  const { source, position } = context;
35
- acc.push(source.slice(position, position + postfix));
22
+ acc.push(new Data(source.slice(position, position + postfix)));
36
23
  context.position += postfix;
37
24
  }
38
- return [acc];
25
+ return acc;
39
26
  }): Parser<string | N, MarkdownParser.Context> {
40
27
  return failsafe(input => {
41
28
  const { context } = input;
42
29
  const { source, position } = context;
43
30
  assert(source.startsWith(symbol, context.position));
44
- let nodes: N[] = [];
31
+ let nodes = new List<Data<N>>();
45
32
  let i = symbol.length;
46
33
  while (source[context.position + i] === source[context.position]) ++i;
47
34
  context.position += i;
@@ -58,7 +45,7 @@ export function repeat<N extends HTMLElement | string>(symbol: string, parser:
58
45
  context.buffer = buf;
59
46
  if (result === undefined) break;
60
47
  nodes = eval(result);
61
- switch (nodes.at(-1)) {
48
+ switch (nodes.last?.value) {
62
49
  case Command.Cancel:
63
50
  assert(!source.startsWith(symbol, context.position));
64
51
  nodes.pop();
@@ -124,10 +111,9 @@ export function unmarkInvalid<N extends HTMLElement>(el: N): N {
124
111
  });
125
112
  }
126
113
 
127
- export function stringify(nodes: readonly (HTMLElement | string)[]): string {
114
+ export function stringify(nodes: Iterable<HTMLElement | string>): string {
128
115
  let acc = '';
129
- for (let i = 0; i < nodes.length; ++i) {
130
- const node = nodes[i];
116
+ for (const node of nodes) {
131
117
  if (typeof node === 'string') {
132
118
  assert(!node.includes('\n'));
133
119
  acc += node;