securemark 0.257.3 → 0.258.2

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 (86) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +1235 -615
  3. package/markdown.d.ts +1 -12
  4. package/package.json +9 -9
  5. package/src/combinator/control/manipulation/convert.ts +10 -6
  6. package/src/combinator/control/manipulation/scope.ts +10 -2
  7. package/src/combinator/data/parser/context/delimiter.ts +70 -0
  8. package/src/combinator/data/parser/context/memo.ts +36 -0
  9. package/src/combinator/{control/manipulation → data/parser}/context.test.ts +9 -9
  10. package/src/combinator/data/parser/context.ts +158 -0
  11. package/src/combinator/data/parser/inits.ts +3 -2
  12. package/src/combinator/data/parser/sequence.test.ts +1 -1
  13. package/src/combinator/data/parser/sequence.ts +4 -3
  14. package/src/combinator/data/parser/some.test.ts +1 -1
  15. package/src/combinator/data/parser/some.ts +14 -37
  16. package/src/combinator/data/parser/subsequence.test.ts +1 -1
  17. package/src/combinator/data/parser/subsequence.ts +3 -3
  18. package/src/combinator/data/parser/tails.ts +3 -3
  19. package/src/combinator/data/parser/union.test.ts +1 -1
  20. package/src/combinator/data/parser.ts +6 -47
  21. package/src/combinator.ts +1 -2
  22. package/src/parser/api/bind.ts +5 -5
  23. package/src/parser/api/parse.test.ts +11 -8
  24. package/src/parser/api/parse.ts +3 -1
  25. package/src/parser/block/blockquote.ts +1 -1
  26. package/src/parser/block/dlist.ts +4 -10
  27. package/src/parser/block/extension/figure.ts +4 -3
  28. package/src/parser/block/extension/table.ts +2 -2
  29. package/src/parser/block/heading.ts +5 -13
  30. package/src/parser/block/ilist.ts +3 -2
  31. package/src/parser/block/olist.ts +10 -7
  32. package/src/parser/block/paragraph.ts +1 -1
  33. package/src/parser/block/reply/cite.ts +1 -1
  34. package/src/parser/block/reply/quote.ts +1 -1
  35. package/src/parser/block/reply.ts +1 -1
  36. package/src/parser/block/sidefence.ts +1 -1
  37. package/src/parser/block/table.ts +9 -9
  38. package/src/parser/block/ulist.ts +4 -3
  39. package/src/parser/block.ts +1 -1
  40. package/src/parser/context.ts +32 -0
  41. package/src/parser/header.ts +1 -1
  42. package/src/parser/inline/annotation.test.ts +5 -5
  43. package/src/parser/inline/annotation.ts +9 -17
  44. package/src/parser/inline/autolink/email.ts +1 -1
  45. package/src/parser/inline/autolink/url.ts +1 -1
  46. package/src/parser/inline/autolink.ts +5 -3
  47. package/src/parser/inline/bracket.ts +11 -10
  48. package/src/parser/inline/code.ts +1 -1
  49. package/src/parser/inline/comment.ts +4 -3
  50. package/src/parser/inline/deletion.ts +5 -4
  51. package/src/parser/inline/emphasis.ts +5 -4
  52. package/src/parser/inline/emstrong.ts +5 -4
  53. package/src/parser/inline/extension/index.ts +7 -14
  54. package/src/parser/inline/extension/indexee.ts +8 -10
  55. package/src/parser/inline/extension/indexer.ts +4 -3
  56. package/src/parser/inline/extension/label.ts +3 -2
  57. package/src/parser/inline/extension/placeholder.ts +5 -4
  58. package/src/parser/inline/html.ts +5 -4
  59. package/src/parser/inline/htmlentity.ts +1 -1
  60. package/src/parser/inline/insertion.ts +5 -4
  61. package/src/parser/inline/link.test.ts +2 -1
  62. package/src/parser/inline/link.ts +27 -29
  63. package/src/parser/inline/mark.ts +5 -4
  64. package/src/parser/inline/math.ts +1 -1
  65. package/src/parser/inline/media.test.ts +1 -0
  66. package/src/parser/inline/media.ts +8 -7
  67. package/src/parser/inline/reference.test.ts +5 -5
  68. package/src/parser/inline/reference.ts +10 -16
  69. package/src/parser/inline/ruby.test.ts +1 -0
  70. package/src/parser/inline/ruby.ts +4 -3
  71. package/src/parser/inline/shortmedia.ts +3 -2
  72. package/src/parser/inline/strong.ts +5 -4
  73. package/src/parser/inline/template.test.ts +1 -1
  74. package/src/parser/inline/template.ts +9 -6
  75. package/src/parser/inline.test.ts +2 -1
  76. package/src/parser/locale.ts +6 -7
  77. package/src/parser/processor/footnote.ts +5 -3
  78. package/src/parser/source/text.ts +1 -1
  79. package/src/parser/util.ts +0 -220
  80. package/src/parser/visibility.ts +205 -0
  81. package/src/util/info.ts +4 -2
  82. package/src/util/quote.ts +12 -15
  83. package/src/util/toc.ts +14 -17
  84. package/webpack.config.js +1 -0
  85. package/src/combinator/control/manipulation/context.ts +0 -70
  86. package/src/combinator/control/manipulation/resource.ts +0 -54
@@ -1,3 +1,6 @@
1
+ import { Delimiters } from './parser/context/delimiter';
2
+ import { Memo } from './parser/context/memo';
3
+
1
4
  export type Parser<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
2
5
  = (source: string, context: C) => Result<T, C, D>;
3
6
  export type Result<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
@@ -11,6 +14,9 @@ export interface Ctx {
11
14
  };
12
15
  precedence?: number;
13
16
  delimiters?: Delimiters;
17
+ state?: number;
18
+ backtrackable?: number;
19
+ memo?: Memo;
14
20
  }
15
21
  export type Tree<P extends Parser<unknown>> = P extends Parser<infer T> ? T : never;
16
22
  export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, Ctx, infer D> ? D : never;
@@ -20,53 +26,6 @@ export type IntermediateParser<P extends Parser<unknown>> = Parser<SubTree<P>, C
20
26
  type ExtractSubTree<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer T ? T extends Parser<infer U> ? U : never : never;
21
27
  type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
22
28
 
23
- export class Delimiters {
24
- private readonly matchers: [number, string, number, (source: string) => boolean | undefined][] = [];
25
- private readonly registry: Record<string, boolean> = {};
26
- private length = 0;
27
- public push(
28
- ...delimiters: readonly {
29
- readonly signature: string;
30
- readonly matcher: (source: string) => boolean | undefined;
31
- readonly precedence?: number;
32
- }[]
33
- ): void {
34
- for (let i = 0; i < delimiters.length; ++i) {
35
- const delimiter = delimiters[i];
36
- assert(this.length >= this.matchers.length);
37
- const { signature, matcher, precedence = 1 } = delimiter;
38
- if (!this.registry[signature]) {
39
- this.matchers.unshift([this.length, signature, precedence, matcher]);
40
- this.registry[signature] = true;
41
- }
42
- ++this.length;
43
- }
44
- }
45
- public pop(count = 1): void {
46
- assert(count > 0);
47
- for (let i = 0; i < count; ++i) {
48
- assert(this.matchers.length > 0);
49
- assert(this.length >= this.matchers.length);
50
- if (--this.length === this.matchers[0][0]) {
51
- this.registry[this.matchers.shift()![1]] = false;
52
- }
53
- }
54
- }
55
- public match(source: string, precedence = 1): boolean {
56
- const { matchers } = this;
57
- for (let i = 0; i < matchers.length; ++i) {
58
- switch (matchers[i][3](source)) {
59
- case true:
60
- if (precedence < matchers[i][2]) return true;
61
- continue;
62
- case false:
63
- return false;
64
- }
65
- }
66
- return false;
67
- }
68
- }
69
-
70
29
  export { eval_ as eval };
71
30
  function eval_<T>(result: NonNullable<Result<T>>, default_?: T[]): T[];
72
31
  function eval_<T>(result: Result<T>, default_: T[]): T[];
package/src/combinator.ts CHANGED
@@ -4,14 +4,13 @@ export * from './combinator/data/parser/tails';
4
4
  export * from './combinator/data/parser/sequence';
5
5
  export * from './combinator/data/parser/subsequence';
6
6
  export * from './combinator/data/parser/some';
7
+ export * from './combinator/data/parser/context';
7
8
  export * from './combinator/control/constraint/block';
8
9
  export * from './combinator/control/constraint/line';
9
10
  export * from './combinator/control/constraint/contract';
10
11
  export * from './combinator/control/manipulation/fence';
11
12
  export * from './combinator/control/manipulation/indent';
12
13
  export * from './combinator/control/manipulation/scope';
13
- export * from './combinator/control/manipulation/context';
14
- export * from './combinator/control/manipulation/resource';
15
14
  export * from './combinator/control/manipulation/surround';
16
15
  export * from './combinator/control/manipulation/match';
17
16
  export * from './combinator/control/manipulation/convert';
@@ -2,9 +2,10 @@ import { undefined, location } from 'spica/global';
2
2
  import { ParserSettings, Progress } from '../../..';
3
3
  import { MarkdownParser } from '../../../markdown';
4
4
  import { eval } from '../../combinator/data/parser';
5
+ import { segment, validate, MAX_INPUT_SIZE } from '../segment';
5
6
  import { header } from '../header';
6
7
  import { block } from '../block';
7
- import { segment, validate, MAX_INPUT_SIZE } from '../segment';
8
+ import { backtrackable } from '../context';
8
9
  import { normalize } from './normalize';
9
10
  import { headers } from './header';
10
11
  import { figure } from '../processor/figure';
@@ -23,6 +24,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
23
24
  let context: MarkdownParser.Context = {
24
25
  ...settings,
25
26
  host: settings.host ?? new ReadonlyURL(location.pathname, location.origin),
27
+ backtrackable,
26
28
  };
27
29
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
28
30
  assert(!settings.id);
@@ -141,8 +143,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
141
143
 
142
144
  function nearest(index: number): HTMLElement | undefined {
143
145
  let el: HTMLElement | undefined;
144
- let len = 0;
145
- for (let i = 0; i < blocks.length; ++i) {
146
+ for (let i = 0, len = 0; i < blocks.length; ++i) {
146
147
  const block = blocks[i];
147
148
  len += block[0].length;
148
149
  el = block[1][0] ?? el;
@@ -152,8 +153,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
152
153
  }
153
154
 
154
155
  function index(source: HTMLElement): number {
155
- let len = 0;
156
- for (let i = 0; i < blocks.length; ++i) {
156
+ for (let i = 0, len = 0; i < blocks.length; ++i) {
157
157
  const block = blocks[i];
158
158
  if (block[1].includes(source)) return len;
159
159
  len += block[0].length;
@@ -234,6 +234,15 @@ describe('Unit: parser/api/parse', () => {
234
234
  });
235
235
 
236
236
  it('recursion', () => {
237
+ assert.deepStrictEqual(
238
+ [...parse('{'.repeat(20)).children].map(el => el.outerHTML),
239
+ [`<p>${'{'.repeat(20)}</p>`]);
240
+ assert.deepStrictEqual(
241
+ [...parse('{'.repeat(21)).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
242
+ [
243
+ '<h1 id="error:rnd" class="error">Error: Too much recursion</h1>',
244
+ `<pre class="error" translate="no">${'{'.repeat(21)}</pre>`,
245
+ ]);
237
246
  assert.deepStrictEqual(
238
247
  [...parse('('.repeat(20)).children].map(el => el.outerHTML),
239
248
  [`<p>${'('.repeat(20)}</p>`]);
@@ -253,14 +262,8 @@ describe('Unit: parser/api/parse', () => {
253
262
  `<pre class="error" translate="no">${'['.repeat(21)}</pre>`,
254
263
  ]);
255
264
  assert.deepStrictEqual(
256
- [...parse('{'.repeat(20)).children].map(el => el.outerHTML),
257
- [`<p>${'{'.repeat(20)}</p>`]);
258
- assert.deepStrictEqual(
259
- [...parse('{'.repeat(21)).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
260
- [
261
- '<h1 id="error:rnd" class="error">Error: Too much recursion</h1>',
262
- `<pre class="error" translate="no">${'{'.repeat(21)}</pre>`,
263
- ]);
265
+ [...parse('['.repeat(17) + '\na').children].map(el => el.outerHTML),
266
+ [`<p>${'['.repeat(17)}<br>a</p>`]);
264
267
  });
265
268
 
266
269
  if (!navigator.userAgent.includes('Chrome')) return;
@@ -2,9 +2,10 @@ import { location } from 'spica/global';
2
2
  import { ParserOptions } from '../../..';
3
3
  import { MarkdownParser } from '../../../markdown';
4
4
  import { eval } from '../../combinator/data/parser';
5
+ import { segment, validate, MAX_SEGMENT_SIZE } from '../segment';
5
6
  import { header } from '../header';
6
7
  import { block } from '../block';
7
- import { segment, validate, MAX_SEGMENT_SIZE } from '../segment';
8
+ import { backtrackable } from '../context';
8
9
  import { normalize } from './normalize';
9
10
  import { headers } from './header';
10
11
  import { figure } from '../processor/figure';
@@ -29,6 +30,7 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
29
30
  ...context?.resources && {
30
31
  resources: context.resources,
31
32
  },
33
+ backtrackable,
32
34
  };
33
35
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
34
36
  const node = frag();
@@ -1,5 +1,5 @@
1
1
  import { BlockquoteParser } from '../block';
2
- import { union, some, block, validate, rewrite, creator, open, convert, lazy, fmap } from '../../combinator';
2
+ import { union, some, creator, block, validate, rewrite, open, convert, lazy, fmap } from '../../combinator';
3
3
  import { autolink } from '../autolink';
4
4
  import { contentline } from '../source';
5
5
  import { parse } from '../api/parse';
@@ -1,23 +1,17 @@
1
1
  import { DListParser } from '../block';
2
- import { union, inits, some, block, line, validate, rewrite, context, creator, open, trimEnd, lazy, fmap } from '../../combinator';
2
+ import { union, inits, some, creator, state, block, line, validate, rewrite, open, trimEnd, lazy, fmap } from '../../combinator';
3
3
  import { inline, indexee, indexer } from '../inline';
4
4
  import { anyline } from '../source';
5
+ import { State } from '../context';
5
6
  import { localize } from '../locale';
6
- import { visualize, trimBlank } from '../util';
7
+ import { visualize, trimBlank } from '../visibility';
7
8
  import { html, defrag } from 'typed-dom/dom';
8
9
  import { push } from 'spica/array';
9
10
 
10
11
  export const dlist: DListParser = lazy(() => block(localize(fmap(validate(
11
12
  /^~[^\S\n]+(?=\S)/,
12
13
  some(inits([
13
- context({ syntax: { inline: {
14
- annotation: false,
15
- reference: false,
16
- index: false,
17
- label: false,
18
- link: false,
19
- media: false,
20
- }}},
14
+ state(State.annotation | State.reference | State.index | State.label | State.link | State.media,
21
15
  some(term)),
22
16
  some(desc),
23
17
  ]))),
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ExtensionParser } from '../../block';
3
- import { union, inits, sequence, some, block, line, fence, rewrite, context, close, match, convert, trimEnd, fallback, fmap } from '../../../combinator';
3
+ import { union, inits, sequence, some, state, block, line, fence, rewrite, close, match, convert, trimEnd, fallback, fmap } from '../../../combinator';
4
4
  import { str, contentline, emptyline } from '../../source';
5
5
  import { label, segment as seg_label } from '../../inline/extension/label';
6
6
  import { ulist } from '../ulist';
@@ -13,8 +13,9 @@ import { table, segment_ as seg_table } from './table';
13
13
  import { blockquote, segment as seg_blockquote } from '../blockquote';
14
14
  import { placeholder, segment_ as seg_placeholder } from './placeholder';
15
15
  import { inline, media, shortmedia } from '../../inline';
16
+ import { State } from '../../context';
16
17
  import { localize } from '../../locale';
17
- import { visualize, trimBlank } from '../../util';
18
+ import { visualize, trimBlank } from '../../visibility';
18
19
  import { html, defrag } from 'typed-dom/dom';
19
20
  import { memoize } from 'spica/memoize';
20
21
 
@@ -66,7 +67,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
66
67
  ])),
67
68
  emptyline,
68
69
  block(localize(
69
- context({ syntax: { inline: { media: false } } },
70
+ state(State.media,
70
71
  visualize(trimBlank(trimEnd(some(inline))))))),
71
72
  ]),
72
73
  ])),
@@ -2,11 +2,11 @@ import { undefined, BigInt, Array } from 'spica/global';
2
2
  import { max, min, isArray } from 'spica/alias';
3
3
  import { ExtensionParser } from '../../block';
4
4
  import { Tree, eval } from '../../../combinator/data/parser';
5
- import { union, subsequence, inits, some, block, line, validate, fence, rewrite, creator, open, clear, convert, trim, dup, lazy, fmap } from '../../../combinator';
5
+ import { union, subsequence, inits, some, creator, block, line, validate, fence, rewrite, open, clear, convert, trim, dup, lazy, fmap } from '../../../combinator';
6
6
  import { inline } from '../../inline';
7
7
  import { str, anyline, emptyline, contentline } from '../../source';
8
8
  import { localize } from '../../locale';
9
- import { visualize } from '../../util';
9
+ import { visualize } from '../../visibility';
10
10
  import { html, define, defrag } from 'typed-dom/dom';
11
11
  import { unshift, splice } from 'spica/array';
12
12
 
@@ -1,8 +1,9 @@
1
1
  import { HeadingParser } from '../block';
2
- import { union, some, block, line, validate, focus, rewrite, context, open, fmap } from '../../combinator';
2
+ import { union, some, state, block, line, validate, focus, rewrite, open, fmap } from '../../combinator';
3
3
  import { inline, indexee, indexer } from '../inline';
4
4
  import { str } from '../source';
5
- import { visualize, trimBlank } from '../util';
5
+ import { State } from '../context';
6
+ import { visualize, trimBlank } from '../visibility';
6
7
  import { html, defrag } from 'typed-dom/dom';
7
8
 
8
9
  export const segment: HeadingParser.SegmentParser = block(validate('#', focus(
@@ -10,23 +11,14 @@ export const segment: HeadingParser.SegmentParser = block(validate('#', focus(
10
11
  some(line(source => [[source], ''])))));
11
12
 
12
13
  export const heading: HeadingParser = block(rewrite(segment,
13
- context({ syntax: { inline: {
14
- annotation: false,
15
- reference: false,
16
- index: false,
17
- label: false,
18
- link: false,
19
- media: false,
20
- }}},
14
+ state(State.annotation | State.reference | State.index | State.label | State.link | State.media,
21
15
  line(indexee(fmap(union([
22
16
  open(
23
17
  str(/^##+/),
24
18
  visualize(trimBlank(some(union([indexer, inline])))), true),
25
19
  open(
26
20
  str('#'),
27
- context({ syntax: { inline: {
28
- autolink: false,
29
- }}},
21
+ state(State.autolink,
30
22
  visualize(trimBlank(some(union([indexer, inline]))))), true),
31
23
  ]),
32
24
  ([h, ...ns]: [string, ...(HTMLElement | string)[]]) => [
@@ -1,13 +1,14 @@
1
1
  import { IListParser } from '../block';
2
- import { union, inits, some, block, line, validate, indent, context, creator, open, fallback, lazy, fmap } from '../../combinator';
2
+ import { union, inits, some, creator, state, block, line, validate, indent, open, fallback, lazy, fmap } from '../../combinator';
3
3
  import { ulist_, fillFirstLine } from './ulist';
4
4
  import { olist_, invalid } from './olist';
5
5
  import { inline } from '../inline';
6
+ import { State } from '../context';
6
7
  import { html, defrag } from 'typed-dom/dom';
7
8
 
8
9
  export const ilist: IListParser = lazy(() => block(validate(
9
10
  /^[-+*](?=[^\S\n]|\n[^\S\n]*\S)/,
10
- context({ syntax: { inline: { media: false } } },
11
+ state(State.media,
11
12
  ilist_))));
12
13
 
13
14
  export const ilist_: IListParser = lazy(() => block(fmap(validate(
@@ -1,13 +1,15 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { OListParser } from '../block';
3
- import { union, inits, subsequence, some, block, line, validate, indent, focus, rewrite, context, creator, open, match, fallback, lazy, fmap } from '../../combinator';
3
+ import { union, inits, subsequence, some, creator, state, block, line, validate, indent, focus, rewrite, open, match, fallback, lazy, fmap } from '../../combinator';
4
4
  import { checkbox, ulist_, fillFirstLine } from './ulist';
5
5
  import { ilist_ } from './ilist';
6
6
  import { inline, indexee, indexer } from '../inline';
7
7
  import { contentline } from '../source';
8
- import { trimBlank } from '../util';
8
+ import { State } from '../context';
9
+ import { trimBlank } from '../visibility';
9
10
  import { html, define, defrag } from 'typed-dom/dom';
10
11
  import { memoize } from 'spica/memoize';
12
+ import { duffbk } from 'spica/duff';
11
13
  import { shift } from 'spica/array';
12
14
  import { tuple } from 'spica/tuple';
13
15
 
@@ -21,7 +23,7 @@ export const olist: OListParser = lazy(() => block(validate(
21
23
  /^([0-9]+|[a-z]+|[A-Z]+)(?:-[0-9]+)*\.(?=[^\S\n]|\n[^\S\n]*\S)/.source,
22
24
  /^\(([0-9]+|[a-z]+)\)(?:-[0-9]+)*(?=[^\S\n]|\n[^\S\n]*\S)/.source,
23
25
  ].join('|')),
24
- context({ syntax: { inline: { media: false } } },
26
+ state(State.media,
25
27
  olist_))));
26
28
 
27
29
  export const olist_: OListParser = lazy(() => block(union([
@@ -122,15 +124,16 @@ function format(el: HTMLOListElement, type: string, form: string): HTMLOListElem
122
124
  'data-type': style(type) || undefined,
123
125
  });
124
126
  const marker = el.firstElementChild?.getAttribute('data-marker')!.match(initial(type))?.[0] ?? '';
125
- for (let es = el.children, len = es.length, i = 0; i < len; ++i) {
127
+ const es = el.children;
128
+ duffbk(es.length, i => {
126
129
  const el = es[i];
127
130
  switch (el.getAttribute('data-marker')) {
128
131
  case '':
129
132
  case marker:
130
133
  el.removeAttribute('data-marker');
131
- continue;
134
+ return;
132
135
  }
133
- break;
134
- }
136
+ return false;
137
+ });
135
138
  return el;
136
139
  }
@@ -2,7 +2,7 @@ import { ParagraphParser } from '../block';
2
2
  import { union, some, block, trimEnd, fmap } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { localize } from '../locale';
5
- import { visualize } from '../util';
5
+ import { visualize } from '../visibility';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
 
8
8
  export const paragraph: ParagraphParser = block(localize(fmap(
@@ -1,5 +1,5 @@
1
1
  import { ReplyParser } from '../../block';
2
- import { union, tails, line, validate, focus, creator, reverse, fmap } from '../../../combinator';
2
+ import { union, tails, creator, line, validate, focus, reverse, fmap } from '../../../combinator';
3
3
  import { anchor } from '../../inline/autolink/anchor';
4
4
  import { str } from '../../source';
5
5
  import { html, define, defrag } from 'typed-dom/dom';
@@ -1,6 +1,6 @@
1
1
  import { ReplyParser } from '../../block';
2
2
  import { eval } from '../../../combinator/data/parser';
3
- import { union, some, block, line, validate, rewrite, creator, lazy, fmap } from '../../../combinator';
3
+ import { union, some, creator, block, line, validate, rewrite, lazy, fmap } from '../../../combinator';
4
4
  import { math } from '../../inline/math';
5
5
  import { str, anyline } from '../../source';
6
6
  import { autolink } from '../../autolink';
@@ -5,7 +5,7 @@ import { quote, syntax as delimiter } from './reply/quote';
5
5
  import { inline } from '../inline';
6
6
  import { anyline } from '../source';
7
7
  import { localize } from '../locale';
8
- import { visualize } from '../util';
8
+ import { visualize } from '../visibility';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
  import { push, pop } from 'spica/array';
11
11
 
@@ -1,5 +1,5 @@
1
1
  import { SidefenceParser } from '../block';
2
- import { union, some, block, focus, rewrite, creator, convert, lazy, fmap } from '../../combinator';
2
+ import { union, some, creator, block, focus, rewrite, convert, lazy, fmap } from '../../combinator';
3
3
  import { autolink } from '../autolink';
4
4
  import { contentline } from '../source';
5
5
  import { html, define, defrag } from 'typed-dom/dom';
@@ -1,9 +1,10 @@
1
1
  import { TableParser } from '../block';
2
- import { union, sequence, some, block, line, validate, focus, rewrite, creator, surround, open, fallback, lazy, fmap } from '../../combinator';
2
+ import { union, sequence, some, creator, block, line, validate, focus, rewrite, surround, open, fallback, lazy, fmap } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { contentline } from '../source';
5
- import { trimNode } from '../util';
5
+ import { trimNode } from '../visibility';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
+ import { duffEach, duffReduce } from 'spica/duff';
7
8
  import { push } from 'spica/array';
8
9
 
9
10
  import RowParser = TableParser.RowParser;
@@ -62,16 +63,15 @@ const data: CellParser.DataParser = creator(fmap(
62
63
  function format(rows: HTMLTableRowElement[]): HTMLTableRowElement[] {
63
64
  const aligns = rows[0].classList.contains('invalid')
64
65
  ? []
65
- : push([], rows.shift()!.children).map(el => el.textContent!);
66
- for (let i = 0, len = rows.length; i < len; ++i) {
67
- const cols = rows[i].children;
68
- for (let i = 0, len = cols.length; i < len; ++i) {
66
+ : duffReduce(rows.shift()!.children, (acc, el) => push(acc, [el.textContent!]), [] as string[]);
67
+ for (let i = 0; i < rows.length; ++i) {
68
+ duffEach(rows[i].children, (col, i) => {
69
69
  if (i > 0 && !aligns[i]) {
70
70
  aligns[i] = aligns[i - 1];
71
71
  }
72
- if (!aligns[i]) continue;
73
- cols[i].setAttribute('align', aligns[i]);
74
- }
72
+ if (!aligns[i]) return;
73
+ col.setAttribute('align', aligns[i]);
74
+ });
75
75
  }
76
76
  return rows;
77
77
  }
@@ -1,15 +1,16 @@
1
1
  import { UListParser } from '../block';
2
- import { union, inits, subsequence, some, block, line, validate, indent, focus, context, creator, open, fallback, lazy, fmap } from '../../combinator';
2
+ import { union, inits, subsequence, some, creator, state, block, line, validate, indent, focus, open, fallback, lazy, fmap } from '../../combinator';
3
3
  import { olist_, invalid } from './olist';
4
4
  import { ilist_ } from './ilist';
5
5
  import { inline, indexer, indexee } from '../inline';
6
- import { trimBlank } from '../util';
6
+ import { State } from '../context';
7
+ import { trimBlank } from '../visibility';
7
8
  import { html, defrag } from 'typed-dom/dom';
8
9
  import { unshift } from 'spica/array';
9
10
 
10
11
  export const ulist: UListParser = lazy(() => block(validate(
11
12
  /^-(?=[^\S\n]|\n[^\S\n]*\S)/,
12
- context({ syntax: { inline: { media: false } } },
13
+ state(State.media,
13
14
  ulist_))));
14
15
 
15
16
  export const ulist_: UListParser = lazy(() => block(fmap(validate(
@@ -36,7 +36,7 @@ export import ReplyParser = BlockParser.ReplyParser;
36
36
  export import ParagraphParser = BlockParser.ParagraphParser;
37
37
 
38
38
  export const block: BlockParser = creator(error(
39
- reset({ resources: { budget: 50 * 1000, recursion: 20 + 1 } },
39
+ reset({ resources: { budget: 50 * 1000, recursion: 20 } },
40
40
  union([
41
41
  emptyline,
42
42
  horizontalrule,
@@ -0,0 +1,32 @@
1
+ export const enum Rule {
2
+ reference = 1 << 12,
3
+ comment = 1 << 11,
4
+ index = 1 << 10,
5
+ placeholder = 1 << 9,
6
+ link = 1 << 8,
7
+ bracket = 1 << 7,
8
+ media = 1 << 6,
9
+ annotation = 1 << 5,
10
+ mathbracket = 1 << 4,
11
+ html = 1 << 3,
12
+ math = 1 << 2,
13
+ autolink = 1 << 1,
14
+ quote = 1 << 0,
15
+ none = 0,
16
+ }
17
+
18
+ export const enum State {
19
+ annotation = 1 << 6,
20
+ reference = 1 << 5,
21
+ index = 1 << 4,
22
+ label = 1 << 3,
23
+ link = 1 << 2,
24
+ media = 1 << 1,
25
+ autolink = 1 << 0,
26
+ }
27
+ export const backtrackable = 0
28
+ | State.annotation
29
+ | State.reference
30
+ | State.index
31
+ | State.link
32
+ | State.media;
@@ -1,5 +1,5 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
- import { union, inits, some, block, line, validate, focus, rewrite, guard, clear, convert, lazy, fmap } from '../combinator';
2
+ import { union, inits, some, guard, block, line, validate, focus, rewrite, clear, convert, lazy, fmap } from '../combinator';
3
3
  import { segment } from './segment';
4
4
  import { str } from './source';
5
5
  import { normalize } from './api/normalize';
@@ -18,11 +18,11 @@ describe('Unit: parser/inline/annotation', () => {
18
18
  assert.deepStrictEqual(inspect(parser('((\n))')), undefined);
19
19
  assert.deepStrictEqual(inspect(parser('((\na))')), undefined);
20
20
  assert.deepStrictEqual(inspect(parser('((\\\na))')), undefined);
21
- assert.deepStrictEqual(inspect(parser('((a\n))')), [['', '(('], 'a\n))']);
22
- assert.deepStrictEqual(inspect(parser('((a\\\n))')), [['', '(('], 'a\\\n))']);
23
- assert.deepStrictEqual(inspect(parser('((a\nb))')), [['', '(('], 'a\nb))']);
24
- assert.deepStrictEqual(inspect(parser('((a\\\nb))')), [['', '(('], 'a\\\nb))']);
25
- assert.deepStrictEqual(inspect(parser('((*a\nb*))')), [['', '(('], '*a\nb*))']);
21
+ assert.deepStrictEqual(inspect(parser('((a\n))')), undefined);
22
+ assert.deepStrictEqual(inspect(parser('((a\\\n))')), undefined);
23
+ assert.deepStrictEqual(inspect(parser('((a\nb))')), undefined);
24
+ assert.deepStrictEqual(inspect(parser('((a\\\nb))')), undefined);
25
+ assert.deepStrictEqual(inspect(parser('((*a\nb*))')), undefined);
26
26
  assert.deepStrictEqual(inspect(parser('((\\))')), undefined);
27
27
  assert.deepStrictEqual(inspect(parser('((a)b))')), undefined);
28
28
  assert.deepStrictEqual(inspect(parser('(((a))')), undefined);
@@ -1,28 +1,20 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { AnnotationParser } from '../inline';
3
- import { union, some, validate, guard, context, precedence, creator, recursion, surround, lazy } from '../../combinator';
3
+ import { union, some, guard, context, syntax, state, validate, surround, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { optimize } from './link';
6
- import { startLoose, trimNode } from '../util';
6
+ import { Rule, State } from '../context';
7
+ import { startLoose, trimNode } from '../visibility';
7
8
  import { html, defrag } from 'typed-dom/dom';
8
9
 
9
- export const annotation: AnnotationParser = lazy(() => creator(recursion(precedence(6, validate('((', surround(
10
+ export const annotation: AnnotationParser = lazy(() => validate('((', syntax(Rule.annotation, 6, surround(
10
11
  '((',
11
- guard(context => context.syntax?.inline?.annotation ?? true,
12
+ guard(context => ~context.state! & State.annotation,
13
+ state(State.annotation | State.media,
12
14
  startLoose(
13
- context({ syntax: { inline: {
14
- annotation: false,
15
- // Redundant
16
- //reference: true,
17
- media: false,
18
- // Redundant
19
- //index: true,
20
- //label: true,
21
- //link: true,
22
- //autolink: true,
23
- }}, delimiters: undefined },
24
- some(union([inline]), ')', [[/^\\?\n/, 9], [')', 2], ['))', 6]])), ')')),
15
+ context({ delimiters: undefined },
16
+ some(union([inline]), ')', [[/^\\?\n/, 9], [')', 2], ['))', 6]])), ')'))),
25
17
  '))',
26
18
  false,
27
19
  ([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNode(defrag(ns)))])], rest],
28
- ([, ns, rest], next) => next[0] === ')' ? undefined : optimize('((', ns, rest)))))));
20
+ ([, ns, rest], next) => next[0] === ')' ? undefined : optimize('((', ns, rest, next)))));
@@ -1,5 +1,5 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { verify, rewrite, creator } from '../../../combinator';
2
+ import { creator, verify, rewrite } from '../../../combinator';
3
3
  import { str } from '../../source';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, some, validate, focus, rewrite, precedence, creator, convert, surround, open, lazy } from '../../../combinator';
2
+ import { union, some, creator, precedence, validate, focus, rewrite, convert, surround, open, lazy } from '../../../combinator';
3
3
  import { textlink } from '../link';
4
4
  import { unescsource } from '../../source';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { AutolinkParser } from '../inline';
2
- import { union, some, validate, guard, fmap } from '../../combinator';
2
+ import { union, some, syntax, guard, validate, fmap } from '../../combinator';
3
3
  import { url } from './autolink/url';
4
4
  import { email } from './autolink/email';
5
5
  import { channel } from './autolink/channel';
@@ -8,11 +8,13 @@ import { hashtag, emoji } from './autolink/hashtag';
8
8
  import { hashnum } from './autolink/hashnum';
9
9
  import { anchor } from './autolink/anchor';
10
10
  import { str } from '../source';
11
+ import { Rule, State } from '../context';
11
12
  import { stringify } from '../util';
12
13
 
13
14
  export const autolink: AutolinkParser = fmap(
14
15
  validate(/^(?:[@#>0-9A-Za-z]|\S#)/,
15
- guard(context => context.syntax?.inline?.autolink ?? true,
16
+ guard(context => ~context.state! & State.autolink,
17
+ syntax(Rule.autolink, 1,
16
18
  some(union([
17
19
  url,
18
20
  email,
@@ -29,5 +31,5 @@ export const autolink: AutolinkParser = fmap(
29
31
  // Escape unmatched hashtag-like strings.
30
32
  str(new RegExp(/^#+(?:[^\p{C}\p{S}\p{P}\s]|emoji|['_])*/u.source.replace('emoji', emoji), 'u')),
31
33
  anchor,
32
- ])))),
34
+ ]))))),
33
35
  ns => ns.length === 1 ? ns : [stringify(ns)]);