securemark 0.289.2 → 0.289.4

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 (48) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/design.md +7 -6
  3. package/dist/index.js +141 -88
  4. package/package.json +1 -1
  5. package/src/combinator/control/constraint/block.ts +1 -1
  6. package/src/combinator/control/constraint/contract.ts +7 -7
  7. package/src/combinator/control/constraint/line.ts +1 -1
  8. package/src/combinator/control/manipulation/clear.ts +7 -0
  9. package/src/combinator/control/manipulation/convert.ts +1 -1
  10. package/src/combinator/control/manipulation/duplicate.ts +4 -4
  11. package/src/combinator/control/manipulation/fallback.ts +3 -3
  12. package/src/combinator/control/manipulation/indent.ts +3 -3
  13. package/src/combinator/control/manipulation/lazy.ts +2 -2
  14. package/src/combinator/control/manipulation/match.ts +1 -1
  15. package/src/combinator/control/manipulation/recovery.ts +3 -3
  16. package/src/combinator/control/manipulation/reverse.ts +1 -1
  17. package/src/combinator/control/manipulation/scope.ts +3 -3
  18. package/src/combinator/control/manipulation/surround.ts +59 -61
  19. package/src/combinator/control/manipulation/trim.ts +3 -3
  20. package/src/combinator/control/monad/bind.ts +6 -6
  21. package/src/combinator/control/monad/fmap.ts +6 -6
  22. package/src/combinator/data/parser/context/delimiter.ts +47 -26
  23. package/src/combinator/data/parser/context.ts +8 -8
  24. package/src/combinator/data/parser/inits.ts +4 -4
  25. package/src/combinator/data/parser/sequence.ts +4 -4
  26. package/src/combinator/data/parser/some.ts +3 -3
  27. package/src/combinator/data/parser/subsequence.ts +3 -3
  28. package/src/combinator/data/parser/tails.ts +3 -3
  29. package/src/combinator/data/parser/union.ts +3 -3
  30. package/src/combinator/data/parser.ts +14 -13
  31. package/src/combinator.ts +1 -0
  32. package/src/parser/api/parse.test.ts +2 -2
  33. package/src/parser/block/extension/figure.ts +1 -1
  34. package/src/parser/block/extension/table.ts +3 -3
  35. package/src/parser/block/olist.ts +4 -4
  36. package/src/parser/block/reply/cite.ts +3 -3
  37. package/src/parser/block/ulist.ts +1 -1
  38. package/src/parser/inline/autolink/hashnum.ts +5 -1
  39. package/src/parser/inline/code.ts +2 -1
  40. package/src/parser/inline/emstrong.ts +2 -1
  41. package/src/parser/inline/extension/indexer.ts +6 -0
  42. package/src/parser/inline/extension/label.ts +1 -1
  43. package/src/parser/inline/html.ts +2 -2
  44. package/src/parser/inline.test.ts +1 -0
  45. package/src/parser/inline.ts +19 -4
  46. package/src/parser/util.ts +10 -10
  47. package/src/parser/visibility.ts +11 -11
  48. package/src/util/quote.ts +1 -1
@@ -1,9 +1,9 @@
1
1
  import { ObjectCreate, min } from 'spica/alias';
2
- import { Parser, Result, Ctx, Tree, Context } from '../../data/parser';
2
+ import { Parser, Result, Ctx, Node, Context } from '../../data/parser';
3
3
  import { clone } from 'spica/assign';
4
4
 
5
5
  export function reset<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
6
- export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
6
+ export function reset<N>(base: Ctx, parser: Parser<N>): Parser<N> {
7
7
  assert(Object.getPrototypeOf(base) === Object.prototype);
8
8
  assert(Object.freeze(base));
9
9
  const changes = Object.entries(base);
@@ -13,7 +13,7 @@ export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
13
13
  }
14
14
 
15
15
  export function context<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
16
- export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
16
+ export function context<N>(base: Ctx, parser: Parser<N>): Parser<N> {
17
17
  assert(Object.getPrototypeOf(base) === Object.prototype);
18
18
  assert(Object.freeze(base));
19
19
  const changes = Object.entries(base);
@@ -22,8 +22,8 @@ export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
22
22
  apply(parser, source, context, changes, values);
23
23
  }
24
24
 
25
- function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Tree<P>>;
26
- function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<T> {
25
+ function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Node<P>>;
26
+ function apply<N>(parser: Parser<N>, source: string, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<N> {
27
27
  if (reset) {
28
28
  context.backtracks = {};
29
29
  }
@@ -97,7 +97,7 @@ export function recursion(recursion: number, parser: Parser<unknown>): Parser<un
97
97
  }
98
98
 
99
99
  export function precedence<P extends Parser<unknown>>(precedence: number, parser: P): P;
100
- export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T> {
100
+ export function precedence<N>(precedence: number, parser: Parser<N>): Parser<N> {
101
101
  assert(precedence >= 0);
102
102
  return input => {
103
103
  const { context } = input;
@@ -115,7 +115,7 @@ export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T>
115
115
 
116
116
  export function state<P extends Parser<unknown>>(state: number, parser: P): P;
117
117
  export function state<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
118
- export function state<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
118
+ export function state<N>(state: number, positive: boolean | Parser<N>, parser?: Parser<N>): Parser<N> {
119
119
  if (typeof positive === 'function') {
120
120
  parser = positive;
121
121
  positive = true;
@@ -136,7 +136,7 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
136
136
 
137
137
  export function constraint<P extends Parser<unknown>>(state: number, parser: P): P;
138
138
  export function constraint<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
139
- export function constraint<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
139
+ export function constraint<N>(state: number, positive: boolean | Parser<N>, parser?: Parser<N>): Parser<N> {
140
140
  if (typeof positive === 'function') {
141
141
  parser = positive;
142
142
  positive = true;
@@ -1,13 +1,13 @@
1
- import { Parser, Ctx, Tree, Context, SubParsers, SubTree, eval, exec, check } from '../parser';
1
+ import { Parser, Ctx, Node, Context, SubParsers, SubNode, eval, exec, check } from '../parser';
2
2
  import { push } from 'spica/array';
3
3
 
4
- export function inits<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubTree<P>[], rest: string) => boolean): SubTree<P> extends Tree<P> ? P : Parser<SubTree<P>, Context<P>, SubParsers<P>>;
5
- export function inits<T, D extends Parser<T>[]>(parsers: D, resume?: (nodes: T[], rest: string) => boolean): Parser<T, Ctx, D> {
4
+ export function inits<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
5
+ export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
6
6
  assert(parsers.every(f => f));
7
7
  if (parsers.length === 1) return parsers[0];
8
8
  return ({ source, context }) => {
9
9
  let rest = source;
10
- let nodes: T[] | undefined;
10
+ let nodes: N[] | undefined;
11
11
  for (let len = parsers.length, i = 0; i < len; ++i) {
12
12
  if (rest === '') break;
13
13
  if (context.delimiters?.match(rest, context)) break;
@@ -1,13 +1,13 @@
1
- import { Parser, Ctx, Tree, Context, SubParsers, SubTree, eval, exec, check } from '../parser';
1
+ import { Parser, Ctx, Node, Context, SubParsers, SubNode, eval, exec, check } from '../parser';
2
2
  import { push } from 'spica/array';
3
3
 
4
- export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubTree<P>[], rest: string) => boolean): SubTree<P> extends Tree<P> ? P : Parser<SubTree<P>, Context<P>, SubParsers<P>>;
5
- export function sequence<T, D extends Parser<T>[]>(parsers: D, resume?: (nodes: T[], rest: string) => boolean): Parser<T, Ctx, D> {
4
+ export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
5
+ export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
6
6
  assert(parsers.every(f => f));
7
7
  if (parsers.length === 1) return parsers[0];
8
8
  return ({ source, context }) => {
9
9
  let rest = source;
10
- let nodes: T[] | undefined;
10
+ let nodes: N[] | undefined;
11
11
  for (let len = parsers.length, i = 0; i < len; ++i) {
12
12
  if (rest === '') return;
13
13
  if (context.delimiters?.match(rest, context)) return;
@@ -6,13 +6,13 @@ type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number,
6
6
 
7
7
  export function some<P extends Parser<unknown>>(parser: P, limit?: number): P;
8
8
  export function some<P extends Parser<unknown>>(parser: P, end?: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
9
- export function some<T>(parser: Parser<T>, end?: string | RegExp | number, delimiters: readonly DelimiterOption[] = [], limit = -1): Parser<T> {
9
+ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delimiters: readonly DelimiterOption[] = [], limit = -1): Parser<N> {
10
10
  if (typeof end === 'number') return some(parser, undefined, delimiters, end);
11
11
  assert(parser);
12
12
  assert([end].concat(delimiters.map(o => o[0])).every(d => d instanceof RegExp ? !d.flags.match(/[gmy]/) && d.source.startsWith('^') : true));
13
13
  const match = Delimiters.matcher(end);
14
14
  const delims = delimiters.map(([delimiter, precedence, linebreakable = true]) => ({
15
- signature: Delimiters.signature(delimiter),
15
+ signature: Delimiters.signature(delimiter, linebreakable),
16
16
  matcher: Delimiters.matcher(delimiter),
17
17
  precedence,
18
18
  linebreakable,
@@ -21,7 +21,7 @@ export function some<T>(parser: Parser<T>, end?: string | RegExp | number, delim
21
21
  if (source === '') return;
22
22
  assert(context.backtracks ??= {});
23
23
  let rest = source;
24
- let nodes: T[] | undefined;
24
+ let nodes: N[] | undefined;
25
25
  if (delims.length > 0) {
26
26
  context.delimiters ??= new Delimiters();
27
27
  context.delimiters.push(delims);
@@ -1,9 +1,9 @@
1
- import { Parser, Ctx, Tree, Context, SubParsers, SubTree } from '../parser';
1
+ import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
2
2
  import { union } from './union';
3
3
  import { inits } from './inits';
4
4
 
5
- export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubTree<P>[], rest: string) => boolean): SubTree<P> extends Tree<P> ? P : Parser<SubTree<P>, Context<P>, SubParsers<P>>;
6
- export function subsequence<T, D extends Parser<T>[]>(parsers: D, resume?: (nodes: T[], rest: string) => boolean): Parser<T, Ctx, D> {
5
+ export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
+ export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
7
7
  assert(parsers.every(f => f));
8
8
  return union(
9
9
  parsers.map((_, i) =>
@@ -1,8 +1,8 @@
1
- import { Parser, Ctx, Tree, Context, SubParsers, SubTree } from '../parser';
1
+ import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
2
2
  import { union } from './union';
3
3
  import { sequence } from './sequence';
4
4
 
5
- export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubTree<P>[], rest: string) => boolean): SubTree<P> extends Tree<P> ? P : Parser<SubTree<P>, Context<P>, SubParsers<P>>;
6
- export function tails<T, D extends Parser<T>[]>(parsers: D, resume?: (nodes: T[], rest: string) => boolean): Parser<T, Ctx, D> {
5
+ export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
6
+ export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
7
7
  return union(parsers.map((_, i) => sequence(parsers.slice(i), resume)) as D);
8
8
  }
@@ -1,7 +1,7 @@
1
- import { Parser, Ctx, Tree, Context, SubParsers, SubTree } from '../parser';
1
+ import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
2
2
 
3
- export function union<P extends Parser<unknown>>(parsers: SubParsers<P>): SubTree<P> extends Tree<P> ? P : Parser<SubTree<P>, Context<P>, SubParsers<P>>;
4
- export function union<T, D extends Parser<T>[]>(parsers: D): Parser<T, Ctx, D> {
3
+ export function union<P extends Parser<unknown>>(parsers: SubParsers<P>): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
4
+ export function union<N, D extends Parser<N>[]>(parsers: D): Parser<N, Ctx, D> {
5
5
  assert(parsers.every(f => f));
6
6
  switch (parsers.length) {
7
7
  case 0:
@@ -1,14 +1,14 @@
1
1
  import { Delimiters } from './parser/context/delimiter';
2
2
 
3
- export type Parser<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
4
- = (input: Input<C>) => Result<T, C, D>;
3
+ export type Parser<N, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
4
+ = (input: Input<C>) => Result<N, C, D>;
5
5
  export interface Input<C extends Ctx = Ctx> {
6
6
  readonly source: string;
7
7
  readonly context: C;
8
8
  }
9
- export type Result<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
10
- = readonly [T[], string, C, D]
11
- | readonly [T[], string]
9
+ export type Result<N, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
10
+ = readonly [N[], string, C, D]
11
+ | readonly [N[], string]
12
12
  | undefined;
13
13
  export interface Ctx {
14
14
  readonly resources?: {
@@ -19,24 +19,25 @@ export interface Ctx {
19
19
  precedence?: number;
20
20
  delimiters?: Delimiters;
21
21
  state?: number;
22
+ depth?: number;
22
23
  backtracks?: Record<number, number>;
23
24
  backtrack?: number;
24
25
  linebreak?: number;
25
26
  recent?: string[];
26
27
  }
27
- export type Tree<P extends Parser<unknown>> = P extends Parser<infer T> ? T : never;
28
+ export type Node<P extends Parser<unknown>> = P extends Parser<infer N> ? N : never;
28
29
  export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, Ctx, infer D> ? D : never;
29
30
  export type Context<P extends Parser<unknown>> = P extends Parser<unknown, infer C> ? C : never;
30
- export type SubTree<P extends Parser<unknown>> = ExtractSubTree<SubParsers<P>>;
31
- export type IntermediateParser<P extends Parser<unknown>> = Parser<SubTree<P>, Context<P>, SubParsers<P>>;
32
- type ExtractSubTree<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer T ? T extends Parser<infer U> ? U : never : never;
31
+ export type SubNode<P extends Parser<unknown>> = ExtractSubNode<SubParsers<P>>;
32
+ export type IntermediateParser<P extends Parser<unknown>> = Parser<SubNode<P>, Context<P>, SubParsers<P>>;
33
+ type ExtractSubNode<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer N ? N extends Parser<infer U> ? U : never : never;
33
34
  type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
34
35
 
35
36
  export { eval_ as eval };
36
- function eval_<T>(result: NonNullable<Result<T>>, default_?: T[]): T[];
37
- function eval_<T>(result: Result<T>, default_: T[]): T[];
38
- function eval_<T>(result: Result<T>, default_?: undefined): T[] | undefined;
39
- function eval_<T>(result: Result<T>, default_?: T[]): T[] | undefined {
37
+ function eval_<N>(result: NonNullable<Result<N>>, default_?: N[]): N[];
38
+ function eval_<N>(result: Result<N>, default_: N[]): N[];
39
+ function eval_<N>(result: Result<N>, default_?: undefined): N[] | undefined;
40
+ function eval_<N>(result: Result<N>, default_?: N[]): N[] | undefined {
40
41
  return result
41
42
  ? result[0]
42
43
  : default_;
package/src/combinator.ts CHANGED
@@ -11,6 +11,7 @@ export * from './combinator/control/constraint/contract';
11
11
  export * from './combinator/control/manipulation/fence';
12
12
  export * from './combinator/control/manipulation/indent';
13
13
  export * from './combinator/control/manipulation/scope';
14
+ export * from './combinator/control/manipulation/clear';
14
15
  export * from './combinator/control/manipulation/surround';
15
16
  export * from './combinator/control/manipulation/match';
16
17
  export * from './combinator/control/manipulation/convert';
@@ -350,7 +350,7 @@ describe('Unit: parser/api/parse', () => {
350
350
 
351
351
  it('backtrack', function () {
352
352
  this.timeout(5000);
353
- const str = `${'.'.repeat(7 + 0)}((${'['.repeat(13)}{{http://[[[${'.'.repeat(8328)}`;
353
+ const str = `${'.'.repeat(5 + 0)}{{(([[[http://${'['.repeat(13)}${'.'.repeat(8323)}`;
354
354
  assert.deepStrictEqual(
355
355
  [...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
356
356
  [`<p>${str}</p>`]);
@@ -358,7 +358,7 @@ describe('Unit: parser/api/parse', () => {
358
358
 
359
359
  it('backtrack error', function () {
360
360
  this.timeout(5000);
361
- const str = `${'.'.repeat(7 + 1)}((${'['.repeat(13)}{{http://[[[${'.'.repeat(8328)}`;
361
+ const str = `${'.'.repeat(5 + 1)}{{(([[[http://${'['.repeat(13)}${'.'.repeat(8323)}`;
362
362
  assert.deepStrictEqual(
363
363
  [...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
364
364
  [
@@ -43,7 +43,7 @@ export const segment: FigureParser.SegmentParser = block(match(
43
43
  ]),
44
44
  ]),
45
45
  closer),
46
- ([, fence]) => fence.length, {}), false));
46
+ ([, fence]) => fence.length, {})));
47
47
 
48
48
  export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
49
49
  convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
@@ -1,7 +1,7 @@
1
1
  import { max, min, isArray } from 'spica/alias';
2
2
  import { ExtensionParser } from '../../block';
3
- import { Tree, eval } from '../../../combinator/data/parser';
4
- import { union, subsequence, inits, some, block, line, validate, fence, rewrite, surround, open, clear, convert, dup, lazy, fmap } from '../../../combinator';
3
+ import { Node, eval } from '../../../combinator/data/parser';
4
+ import { union, subsequence, inits, some, block, line, validate, fence, rewrite, clear, surround, open, convert, dup, lazy, fmap } from '../../../combinator';
5
5
  import { inline, medialink, media, shortmedia } from '../../inline';
6
6
  import { str, anyline, emptyline, contentline } from '../../source';
7
7
  import { lineable, invalid } from '../../util';
@@ -147,7 +147,7 @@ function attributes(source: string): Record<string, string | undefined> {
147
147
  };
148
148
  }
149
149
 
150
- function format(rows: Tree<RowParser>[]): HTMLTableSectionElement[] {
150
+ function format(rows: Node<RowParser>[]): HTMLTableSectionElement[] {
151
151
  const thead = html('thead');
152
152
  const tbody = html('tbody');
153
153
  const tfoot = html('tfoot');
@@ -24,10 +24,10 @@ export const olist: OListParser = lazy(() => block(validate(
24
24
  export const olist_: OListParser = lazy(() => block(union([
25
25
  match(
26
26
  openers['.'],
27
- memoize(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), []), false),
27
+ memoize(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), [])),
28
28
  match(
29
29
  openers['('],
30
- memoize(ms => list(type(ms[1]), '('), ms => idx(ms[1]), []), false),
30
+ memoize(ms => list(type(ms[1]), '('), ms => idx(ms[1]), [])),
31
31
  ])));
32
32
 
33
33
  const list = (type: string, form: string): OListParser.ListParser => fmap(
@@ -47,10 +47,10 @@ const list = (type: string, form: string): OListParser.ListParser => fmap(
47
47
  const heads = {
48
48
  '.': focus(
49
49
  openers['.'],
50
- ({ source }) => [[source.trimEnd().split('.', 1)[0] + '.'], ''], false),
50
+ ({ source }) => [[source.trimEnd().split('.', 1)[0] + '.'], '']),
51
51
  '(': focus(
52
52
  openers['('],
53
- ({ source }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], ''], false),
53
+ ({ source }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], '']),
54
54
  } as const;
55
55
 
56
56
  function idx(value: string): number {
@@ -15,9 +15,9 @@ export const cite: ReplyParser.CiteParser = line(fmap(validate(
15
15
  anchor,
16
16
  // Subject page representation.
17
17
  // リンクの実装は後で検討
18
- focus(/^>>#\S*(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor' }, source)], ''], false),
19
- focus(/^>>https?:\/\/\S+(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)], ''], false),
20
- focus(/^>>.+(?=\s*$)/, ({ source }) => [[source], ''], false),
18
+ focus(/^>>#\S*(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor' }, source)], '']),
19
+ focus(/^>>https?:\/\/\S+(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)], '']),
20
+ focus(/^>>.+(?=\s*$)/, ({ source }) => [[source], '']),
21
21
  ]),
22
22
  )),
23
23
  ([quotes, node]: [string, HTMLElement | string]) => [
@@ -33,7 +33,7 @@ export const checkbox = focus(
33
33
  /^\[[xX ]\](?=$|\s)/,
34
34
  ({ source }) => [[
35
35
  html('span', { class: 'checkbox' }, source[1].trimStart() ? '☑' : '☐'),
36
- ], ''], false);
36
+ ], '']);
37
37
 
38
38
  export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | string)[] {
39
39
  return ns.length === 1
@@ -7,7 +7,11 @@ import { str } from '../../source';
7
7
  import { define } from 'typed-dom/dom';
8
8
 
9
9
  export const hashnum: AutolinkParser.HashnumParser = lazy(() => rewrite(
10
- open('#', str(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji)/u.source.replace(/emoji/, emoji), 'u'))),
10
+ open(
11
+ '#',
12
+ str(new RegExp([
13
+ /^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji)/u.source,
14
+ ].join('').replace(/emoji/, emoji), 'u'))),
11
15
  union([
12
16
  constraint(State.autolink, false, state(State.autolink, fmap(convert(
13
17
  source => `[${source}]{ ${source.slice(1)} }`,
@@ -5,7 +5,8 @@ import { html } from 'typed-dom/dom';
5
5
  export const code: CodeParser = match(
6
6
  /^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/,
7
7
  ([whole, , body]) => ({ source }) =>
8
- [[html('code', { 'data-src': whole }, format(body))], source.slice(whole.length)]);
8
+ [[html('code', { 'data-src': whole }, format(body))], source.slice(whole.length)],
9
+ true);
9
10
 
10
11
  function format(text: string): string {
11
12
  assert(text.length > 0);
@@ -28,7 +28,8 @@ const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
28
28
  ])),
29
29
  ])));
30
30
 
31
- // 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず早く閉じるよう解析しなければならない。
31
+ // 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず
32
+ // 可能な限り早く閉じるよう解析しなければならない。
32
33
  // このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
33
34
  export const emstrong: EmStrongParser = lazy(() => validate('***',
34
35
  precedence(0, repeat('***', surround(
@@ -3,6 +3,12 @@ import { union, focus, surround } from '../../../combinator';
3
3
  import { signature } from './index';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
+ // インデクスの重複解消は不要な重複を削除するのが最もよい。
7
+ // 複合生成インデクスは参照と同期させることが困難であり
8
+ // 複合生成インデクスを手動で同期させるより最初から重複のない
9
+ // テキストまたはインデクスを付けて同期が必要な機会を減らすのが
10
+ // 継続的編集において最も簡便となる。
11
+
6
12
  export const indexer: ExtensionParser.IndexerParser = surround(
7
13
  /^\s+\[(?=\|\S)/,
8
14
  union([
@@ -1,6 +1,6 @@
1
1
  import { ExtensionParser } from '../../inline';
2
2
  import { State, Backtrack } from '../../context';
3
- import { union, constraint, surround, clear, fmap } from '../../../combinator';
3
+ import { union, constraint, clear, surround, fmap } from '../../../combinator';
4
4
  import { str } from '../../source';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
@@ -53,8 +53,8 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i,
53
53
  /^<([a-z]+)(?=[^\S\n]|>)/i,
54
54
  memoize(
55
55
  ([, tag]) =>
56
- surround<HTMLParser.TagParser, string>(surround(
57
- str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
56
+ surround<HTMLParser.TagParser, string>(
57
+ surround(str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
58
58
  precedence(3, recursion(Recursion.inline,
59
59
  subsequence([
60
60
  focus(/^[^\S\n]*\n/, some(inline)),
@@ -70,6 +70,7 @@ describe('Unit: parser/inline', () => {
70
70
  assert.deepStrictEqual(inspect(parser('***a*b*c*')), [['**', '<em>a</em>', 'b', '<em>c</em>'], '']);
71
71
  assert.deepStrictEqual(inspect(parser('***a*b*c**')), [['**', '<em>a</em>', 'b', '<em>c</em>', '*'], '']);
72
72
  assert.deepStrictEqual(inspect(parser('***a*b*c***')), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
73
+ assert.deepStrictEqual(inspect(parser('***a**b**c***')), [['<em><strong>a</strong>b<strong>c</strong></em>'], '']);
73
74
  assert.deepStrictEqual(inspect(parser('*(*a*)*')), [['<em><span class="paren">(<em>a</em>)</span></em>'], '']);
74
75
  assert.deepStrictEqual(inspect(parser('**(**a**)**')), [['<strong><span class="paren">(<strong>a</strong>)</span></strong>'], '']);
75
76
  assert.deepStrictEqual(inspect(parser('*[*]')), [['*', '[', '*', ']'], '']);
@@ -1,5 +1,5 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
- import { union, lazy } from '../combinator';
2
+ import { union, verify, lazy } from '../combinator';
3
3
  import { annotation } from './inline/annotation';
4
4
  import { reference } from './inline/reference';
5
5
  import { template } from './inline/template';
@@ -47,10 +47,12 @@ export import ShortMediaParser = InlineParser.ShortMediaParser;
47
47
  export import BracketParser = InlineParser.BracketParser;
48
48
  export import AutolinkParser = InlineParser.AutolinkParser;
49
49
 
50
- export const inline: InlineParser = lazy(() => union([
50
+ export const inline: InlineParser = lazy(() => verify(union([
51
51
  input => {
52
- const { source } = input;
52
+ const { source, context } = input;
53
53
  if (source === '') return;
54
+ context.depth ??= 0;
55
+ ++context.depth;
54
56
  switch (source.slice(0, 2)) {
55
57
  case '((':
56
58
  return annotation(input);
@@ -104,7 +106,20 @@ export const inline: InlineParser = lazy(() => union([
104
106
  bracket,
105
107
  autolink,
106
108
  text
107
- ])) as any;
109
+ ]), (_, rest, context) => {
110
+ --context.depth!;
111
+ assert([rest]);
112
+ // ヒープを効率的に削除可能な場合は削除する。
113
+ // ヒープサイズは括弧類など特定の構文が完成しなかった場合にしか増加しないため
114
+ // ブロックごとに平均数ノード以下となることから削除せずとも平均的にはあまり影響はない。
115
+ //if (context.depth === 0) {
116
+ // const { backtracks } = context;
117
+ // while (backtracks.peek()?.key! > rest.length) {
118
+ // backtracks.extract();
119
+ // }
120
+ //}
121
+ return true;
122
+ })) as any;
108
123
 
109
124
  export { indexee } from './inline/extension/indexee';
110
125
  export { indexer } from './inline/extension/indexer';
@@ -1,19 +1,19 @@
1
1
  import { min } from 'spica/alias';
2
2
  import { Command } from './context';
3
- import { Parser, Result, Ctx, Tree, Context, eval, exec } from '../combinator/data/parser';
3
+ import { Parser, Result, Ctx, Node, Context, eval, exec } from '../combinator/data/parser';
4
4
  import { convert } from '../combinator';
5
5
  import { define } from 'typed-dom/dom';
6
6
 
7
7
  export function lineable<P extends Parser<HTMLElement | string>>(parser: P, fillTrailingLinebreak?: boolean): P;
8
- export function lineable<T extends HTMLElement | string>(parser: Parser<T>, fillTrailingLinebreak = false): Parser<T> {
8
+ export function lineable<N extends HTMLElement | string>(parser: Parser<N>, fillTrailingLinebreak = false): Parser<N> {
9
9
  return convert(
10
10
  source => `\r${source}${fillTrailingLinebreak && source.at(-1) !== '\n' ? '\n' : ''}`,
11
11
  parser,
12
12
  !fillTrailingLinebreak);
13
13
  }
14
14
 
15
- export function repeat<P extends Parser<HTMLElement | string>>(symbol: string, parser: P, cons: (nodes: Tree<P>[], context: Context<P>) => Tree<P>[], termination?: (acc: Tree<P>[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | Tree<P>>): P;
16
- export function repeat<T extends HTMLElement | string>(symbol: string, parser: Parser<T>, cons: (nodes: T[], context: Ctx) => T[], termination: (acc: T[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | T> = (acc, rest, prefix, postfix) => {
15
+ export function repeat<P extends Parser<HTMLElement | string>>(symbol: string, parser: P, cons: (nodes: Node<P>[], context: Context<P>) => Node<P>[], termination?: (acc: Node<P>[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | Node<P>>): P;
16
+ export function repeat<N extends HTMLElement | string>(symbol: string, parser: Parser<N>, cons: (nodes: N[], context: Ctx) => N[], termination: (acc: N[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | N> = (acc, rest, prefix, postfix) => {
17
17
  const nodes = [];
18
18
  if (prefix > 0) {
19
19
  nodes.push(symbol[0].repeat(prefix));
@@ -26,11 +26,11 @@ export function repeat<T extends HTMLElement | string>(symbol: string, parser: P
26
26
  rest = rest.slice(postfix);
27
27
  }
28
28
  return [nodes, rest];
29
- }): Parser<string | T> {
29
+ }): Parser<string | N> {
30
30
  return input => {
31
31
  const { source, context } = input;
32
32
  assert(source.startsWith(symbol));
33
- let acc: T[][] = [];
33
+ let acc: N[][] = [];
34
34
  let i = symbol.length;
35
35
  while (source[i] === source[0]) ++i;
36
36
  let rest = source.slice(i);
@@ -87,12 +87,12 @@ export function invalid(
87
87
  };
88
88
  }
89
89
 
90
- export function markInvalid<T extends Element>(
91
- el: T,
90
+ export function markInvalid<N extends HTMLElement>(
91
+ el: N,
92
92
  syntax: string,
93
93
  type: string,
94
94
  message: string,
95
- ): T {
95
+ ): N {
96
96
  assert(!message.endsWith('.'));
97
97
  return define(el, {
98
98
  class: void el.classList.add('invalid'),
@@ -102,7 +102,7 @@ export function markInvalid<T extends Element>(
102
102
  });
103
103
  }
104
104
 
105
- export function unmarkInvalid<T extends Element>(el: T): T {
105
+ export function unmarkInvalid<N extends HTMLElement>(el: N): N {
106
106
  return define(el, {
107
107
  class: void el.classList.remove('invalid'),
108
108
  'data-invalid-syntax': null,
@@ -18,7 +18,7 @@ export namespace blank {
18
18
  }
19
19
 
20
20
  export function visualize<P extends Parser<HTMLElement | string>>(parser: P): P;
21
- export function visualize<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
21
+ export function visualize<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
22
22
  return union([
23
23
  convert(
24
24
  source => source.replace(blank.line, line => line.replace(/[\\&<]/g, `${Command.Escape}$&`)),
@@ -60,7 +60,7 @@ export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegEx
60
60
  }
61
61
 
62
62
  //export function looseStart<P extends Parser<HTMLElement | string>>(parser: P, except?: string): P;
63
- //export function looseStart<T extends HTMLElement | string>(parser: Parser<T>, except?: string): Parser<T> {
63
+ //export function looseStart<N extends HTMLElement | string>(parser: Parser<N>, except?: string): Parser<N> {
64
64
  // return input =>
65
65
  // isLooseStart(input, except)
66
66
  // ? parser(input)
@@ -71,7 +71,7 @@ export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegEx
71
71
  //}, ({ source }, except = '') => `${source}${Command.Separator}${except}`);
72
72
 
73
73
  export function tightStart<P extends Parser<unknown>>(parser: P, except?: string): P;
74
- export function tightStart<T>(parser: Parser<T>, except?: string): Parser<T> {
74
+ export function tightStart<N>(parser: Parser<N>, except?: string): Parser<N> {
75
75
  return input =>
76
76
  isTightStart(input, except)
77
77
  ? parser(input)
@@ -155,31 +155,31 @@ function isVisible(node: HTMLElement | string, strpos?: number): boolean {
155
155
 
156
156
  // デフラグ前の非効率な後方トリムを避けるため必要のない限りtrimBlankStart+trimNodeEndで処理する。
157
157
  export function trimBlank<P extends Parser<HTMLElement | string>>(parser: P): P;
158
- export function trimBlank<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
158
+ export function trimBlank<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
159
159
  return trimBlankStart(trimBlankEnd(parser));
160
160
  }
161
161
  export function trimBlankStart<P extends Parser<unknown>>(parser: P): P;
162
- export function trimBlankStart<T>(parser: Parser<T>): Parser<T> {
162
+ export function trimBlankStart<N>(parser: Parser<N>): Parser<N> {
163
163
  return convert(
164
164
  source => source.replace(blank.start, ''),
165
165
  parser,
166
166
  true);
167
167
  }
168
168
  export function trimBlankEnd<P extends Parser<HTMLElement | string>>(parser: P): P;
169
- export function trimBlankEnd<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
169
+ export function trimBlankEnd<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
170
170
  return fmap(
171
171
  parser,
172
172
  trimBlankNodeEnd);
173
173
  }
174
- //export function trimBlankNode<T extends HTMLElement | string>(nodes: T[]): T[] {
174
+ //export function trimBlankNode<N extends HTMLElement | string>(nodes: N[]): N[] {
175
175
  // return trimBlankNodeStart(trimBlankNodeEnd(nodes));
176
176
  //}
177
- //function trimBlankNodeStart<T extends HTMLElement | string>(nodes: T[]): T[] {
177
+ //function trimBlankNodeStart<N extends HTMLElement | string>(nodes: N[]): N[] {
178
178
  // for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
179
179
  // if (typeof node === 'string') {
180
180
  // const pos = node.trimStart().length;
181
181
  // if (pos > 0) {
182
- // nodes[0] = node.slice(-pos) as T;
182
+ // nodes[0] = node.slice(-pos) as N;
183
183
  // break;
184
184
  // }
185
185
  // }
@@ -190,7 +190,7 @@ export function trimBlankEnd<T extends HTMLElement | string>(parser: Parser<T>):
190
190
  // }
191
191
  // return nodes;
192
192
  //}
193
- export function trimBlankNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
193
+ export function trimBlankNodeEnd<N extends HTMLElement | string>(nodes: N[]): N[] {
194
194
  const skip = nodes.length > 0 &&
195
195
  typeof nodes.at(-1) === 'object' &&
196
196
  nodes.at(-1)!['className'] === 'indexer'
@@ -200,7 +200,7 @@ export function trimBlankNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[
200
200
  if (typeof node === 'string') {
201
201
  const len = node.trimEnd().length;
202
202
  if (len > 0) {
203
- nodes[nodes.length - 1] = node.slice(0, len) as T;
203
+ nodes[nodes.length - 1] = node.slice(0, len) as N;
204
204
  break;
205
205
  }
206
206
  }
package/src/util/quote.ts CHANGED
@@ -82,7 +82,7 @@ function fit(range: Range): void {
82
82
  }
83
83
  }
84
84
 
85
- function trim<T extends Node>(node: T): T {
85
+ function trim<N extends Node>(node: N): N {
86
86
  for (let child: ChildNode & Node | null; child = node.firstChild;) {
87
87
  if (child.textContent) break;
88
88
  child.remove();