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
package/markdown.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Parser, Ctx, CtxOptions } from './src/combinator/data/parser';
1
+ import { Parser, List, Data, Ctx, CtxOptions } from './src/combinator/data/parser';
2
2
  import { Dict } from 'spica/dict';
3
3
 
4
4
  declare abstract class Markdown<T> {
@@ -13,7 +13,7 @@ export interface MarkdownParser extends
13
13
  }
14
14
  export namespace MarkdownParser {
15
15
  export interface Context extends Ctx, Options {
16
- buffer?: (string | HTMLElement)[];
16
+ buffer?: List<Data<(string | HTMLElement)>>;
17
17
  sequential?: boolean;
18
18
  }
19
19
  export interface Options extends CtxOptions {
@@ -434,7 +434,7 @@ export namespace MarkdownParser {
434
434
  }
435
435
  export interface RowParser extends
436
436
  Block<'extension/table/row'>,
437
- Parser<[[string[], string[]?], ...HTMLTableCellElement[]], Context, [
437
+ Parser<List<Data<[string[], string[]?] | HTMLTableCellElement>>, Context, [
438
438
  Parser<[string[], string[]?], Context, [
439
439
  AlignParser,
440
440
  ]>,
@@ -448,7 +448,7 @@ export namespace MarkdownParser {
448
448
  }
449
449
  export interface AlignParser extends
450
450
  Block<'extension/table/align'>,
451
- Parser<string[], Context, [
451
+ Parser<[string[], string[]?], Context, [
452
452
  SourceParser.StrParser,
453
453
  ]> {
454
454
  }
@@ -792,7 +792,7 @@ export namespace MarkdownParser {
792
792
  export interface TextLinkParser extends
793
793
  Inline<'link/textlink'>,
794
794
  Parser<HTMLAnchorElement | HTMLSpanElement, Context, [
795
- Parser<(HTMLElement | string)[], Context, [
795
+ Parser<List<Data<string | HTMLElement>>, Context, [
796
796
  InlineParser,
797
797
  ]>,
798
798
  LinkParser.ParameterParser,
@@ -801,7 +801,7 @@ export namespace MarkdownParser {
801
801
  export interface MediaLinkParser extends
802
802
  Inline<'link/medialink'>,
803
803
  Parser<HTMLAnchorElement | HTMLSpanElement, Context, [
804
- Parser<HTMLElement[], Context, [
804
+ Parser<List<Data<HTMLElement>>, Context, [
805
805
  MediaParser,
806
806
  ShortMediaParser,
807
807
  ]>,
@@ -817,7 +817,7 @@ export namespace MarkdownParser {
817
817
  }
818
818
  export interface ContentParser extends
819
819
  Inline<'link/content'>,
820
- Parser<(HTMLElement | string)[], Context, [
820
+ Parser<List<Data<string | HTMLElement>>, Context, [
821
821
  MediaParser,
822
822
  ShortMediaParser,
823
823
  InlineParser,
@@ -825,13 +825,13 @@ export namespace MarkdownParser {
825
825
  }
826
826
  export interface TextParser extends
827
827
  Inline<'link/text'>,
828
- Parser<string[], Context, [
828
+ Parser<List<Data<string>>, Context, [
829
829
  SourceParser.UnescapableSourceParser,
830
830
  ]> {
831
831
  }
832
832
  export interface ParameterParser extends
833
833
  Inline<'link/parameter'>,
834
- Parser<string[], Context, [
834
+ Parser<List<Data<string>>, Context, [
835
835
  LinkParser.ParameterParser.UriParser,
836
836
  LinkParser.ParameterParser.OptionParser,
837
837
  ]> {
@@ -866,7 +866,7 @@ export namespace MarkdownParser {
866
866
  export namespace MediaParser {
867
867
  export interface TextParser extends
868
868
  Inline<'media/text'>,
869
- Parser<string[], Context, [
869
+ Parser<List<Data<string>>, Context, [
870
870
  UnsafeHTMLEntityParser,
871
871
  TextParser.BracketParser,
872
872
  SourceParser.TxtParser,
@@ -900,7 +900,7 @@ export namespace MarkdownParser {
900
900
  }
901
901
  export interface ParameterParser extends
902
902
  Inline<'media/parameter'>,
903
- Parser<string[], Context, [
903
+ Parser<List<Data<string>>, Context, [
904
904
  LinkParser.ParameterParser.UriParser,
905
905
  ParameterParser.OptionParser,
906
906
  ]> {
@@ -919,8 +919,8 @@ export namespace MarkdownParser {
919
919
  // [AB](a b)
920
920
  Inline<'ruby'>,
921
921
  Parser<HTMLElement, Context, [
922
- Parser<string[], Context, []>,
923
- Parser<string[], Context, []>,
922
+ Parser<List<Data<string>>, Context, []>,
923
+ Parser<List<Data<string>>, Context, []>,
924
924
  ]> {
925
925
  }
926
926
  export namespace RubyParser {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.293.4",
3
+ "version": "0.294.0",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -28,7 +28,7 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "dependencies": {
31
- "spica": "0.0.805"
31
+ "spica": "0.0.807"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/dompurify": "3.0.5",
@@ -55,7 +55,7 @@
55
55
  "npm-check-updates": "^17.0.2",
56
56
  "semver": "^7.6.3",
57
57
  "ts-loader": "^9.5.1",
58
- "typed-dom": "0.0.349",
58
+ "typed-dom": "0.0.351",
59
59
  "typescript": "5.4.5",
60
60
  "webpack": "^5.93.0",
61
61
  "webpack-cli": "^5.1.4",
@@ -1,5 +1,5 @@
1
1
  import { block } from './block';
2
- import { input } from '../../data/parser';
2
+ import { List, Data, input } from '../../data/parser';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
5
5
  describe('Unit: combinator/block', () => {
@@ -7,14 +7,14 @@ describe('Unit: combinator/block', () => {
7
7
  const { context: ctx } = input('', {});
8
8
 
9
9
  it('invalid', () => {
10
- assert.throws(() => block(_ => [[]])(input(' \n', ctx)));
10
+ assert.throws(() => block(_ => new List<Data<string>>())(input(' \n', ctx)));
11
11
  });
12
12
 
13
13
  it('valid', () => {
14
- assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length; return [[]]; })(input('\n', ctx)), ctx), [[], '']);
15
- assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length; return [[]]; })(input(' \n', ctx)), ctx), [[], '']);
16
- assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length; return [[]]; })(input('\n\n', ctx)), ctx), [[], '']);
17
- assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length - 1; return [[]]; })(input('\n\n', ctx)), ctx), [[], '\n']);
14
+ assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length; return new List<Data<string>>(); })(input('\n', ctx)), ctx), [[], '']);
15
+ assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length; return new List<Data<string>>(); })(input(' \n', ctx)), ctx), [[], '']);
16
+ assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length; return new List<Data<string>>(); })(input('\n\n', ctx)), ctx), [[], '']);
17
+ assert.deepStrictEqual(inspect(block(({ context }) => { context.position = context.source.length - 1; return new List<Data<string>>(); })(input('\n\n', ctx)), ctx), [[], '\n']);
18
18
  });
19
19
 
20
20
  });
@@ -1,4 +1,4 @@
1
- import { Parser, Input, Ctx, Node, Context, eval, failsafe } from '../../data/parser';
1
+ import { Parser, Input, List, Data, Ctx, Node, Context, eval, failsafe } from '../../data/parser';
2
2
  import { matcher } from '../../../combinator';
3
3
 
4
4
  //export function contract<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], parser: P, cond: (nodes: readonly Data<P>[], rest: string) => boolean): P;
@@ -28,8 +28,8 @@ function guard<N>(f: (input: Input<Ctx>) => boolean, parser: Parser<N>): Parser<
28
28
  : undefined;
29
29
  }
30
30
 
31
- export function verify<P extends Parser<unknown>>(parser: P, cond: (nodes: readonly Node<P>[], context: Context<P>) => boolean): P;
32
- export function verify<N>(parser: Parser<N>, cond: (nodes: readonly N[], context: Ctx) => boolean): Parser<N> {
31
+ export function verify<P extends Parser<unknown>>(parser: P, cond: (nodes: List<Data<Node<P>>>, context: Context<P>) => boolean): P;
32
+ export function verify<N>(parser: Parser<N>, cond: (nodes: List<Data<N>>, context: Ctx) => boolean): Parser<N> {
33
33
  assert(parser);
34
34
  return failsafe(input => {
35
35
  const { context } = input;
@@ -1,4 +1,4 @@
1
- import { input } from '../../data/parser';
1
+ import { List, Data, input } from '../../data/parser';
2
2
  import { line } from './line';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
@@ -7,15 +7,15 @@ describe('Unit: combinator/line', () => {
7
7
  const { context: ctx } = input('', {});
8
8
 
9
9
  it('invalid', () => {
10
- assert.deepStrictEqual(inspect(line(_ => [[]])(input('', ctx)), ctx), undefined);
10
+ assert.deepStrictEqual(inspect(line(_ => new List<Data<string>>())(input('', ctx)), ctx), undefined);
11
11
  });
12
12
 
13
13
  it('valid', () => {
14
- assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return [[]]; })(input(' ', ctx)), ctx), [[], '']);
15
- assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return [[]]; })(input('\n', ctx)), ctx), [[], '']);
16
- assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return [[]]; })(input('\n\n', ctx)), ctx), [[], '\n']);
17
- assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return [[]]; })(input(' \n', ctx)), ctx), [[], '']);
18
- assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length - 1; return [[]]; })(input(' \n', ctx)), ctx), [[], '']);
14
+ assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return new List<Data<string>>(); })(input(' ', ctx)), ctx), [[], '']);
15
+ assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return new List<Data<string>>(); })(input('\n', ctx)), ctx), [[], '']);
16
+ assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return new List<Data<string>>(); })(input('\n\n', ctx)), ctx), [[], '\n']);
17
+ assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length; return new List<Data<string>>(); })(input(' \n', ctx)), ctx), [[], '']);
18
+ assert.deepStrictEqual(inspect(line(({ context }) => { context.position = context.source.length - 1; return new List<Data<string>>(); })(input(' \n', ctx)), ctx), [[], '']);
19
19
  });
20
20
 
21
21
  });
@@ -18,7 +18,7 @@ export function line<N>(parser: Parser<N>): Parser<N> {
18
18
  if (result === undefined) return;
19
19
  if (!isBlank(source.slice(context.position, position + line.length))) return;
20
20
  context.position = position + line.length;
21
- return [eval(result)];
21
+ return eval(result);
22
22
  });
23
23
  }
24
24
 
@@ -1,6 +1,5 @@
1
- import { Parser, CtxOptions } from '../../data/parser';
2
-
1
+ import { Parser, List, CtxOptions } from '../../data/parser';
3
2
 
4
3
  export function clear<D extends Parser<unknown, C>[], C extends CtxOptions>(parser: Parser<unknown, C, D>): Parser<never, C, D> {
5
- return input => parser(input) && [[]];
4
+ return input => parser(input) && new List();
6
5
  }
@@ -1,4 +1,4 @@
1
- import { Parser, Ctx, Context, subinput, failsafe } from '../../data/parser';
1
+ import { Parser, List, Ctx, Context, subinput, failsafe } from '../../data/parser';
2
2
 
3
3
  export function convert<P extends Parser<unknown>>(conv: (source: string, context: Context<P>) => string, parser: P, continuous: boolean, empty?: boolean): P;
4
4
  export function convert<N>(conv: (source: string, context: Ctx) => string, parser: Parser<N>, continuous: boolean, empty = false): Parser<N> {
@@ -11,7 +11,7 @@ export function convert<N>(conv: (source: string, context: Ctx) => string, parse
11
11
  if (src === '') {
12
12
  if (!empty) return;
13
13
  context.position = source.length;
14
- return [[]];
14
+ return new List();
15
15
  }
16
16
  assert(source.endsWith(src) || src.endsWith(source, position) || !continuous);
17
17
  if (continuous) {
@@ -1,8 +1,7 @@
1
- import { Parser, Ctx, Node, Context, SubParsers } from '../../data/parser';
1
+ import { Parser, List, Data, Ctx } from '../../data/parser';
2
2
  import { fmap } from '../monad/fmap';
3
3
 
4
- export function dup<P extends Parser<unknown[]>>(parser: Parser<Node<P>[number], Context<P>, SubParsers<P>>): P;
5
- export function dup<N, C extends Ctx, D extends Parser<unknown, C>[]>(parser: Parser<N, C, D>): Parser<N[], C, D>;
6
- export function dup<N>(parser: Parser<N>): Parser<N[]> {
7
- return fmap(parser, nodes => [nodes]);
4
+ export function dup<N, C extends Ctx, D extends Parser<unknown, C>[]>(parser: Parser<N, C, D>): Parser<List<Data<N>>, C, D>;
5
+ export function dup<N>(parser: Parser<N>): Parser<List<Data<N>>> {
6
+ return fmap(parser, nodes => new List([new Data(nodes)]));
8
7
  }
@@ -1,4 +1,4 @@
1
- import { Parser, Ctx, failsafe } from '../../data/parser';
1
+ import { Parser, List, Data, Ctx, failsafe } from '../../data/parser';
2
2
  import { firstline, isBlank } from '../constraint/line';
3
3
  import { push } from 'spica/array';
4
4
 
@@ -47,6 +47,6 @@ export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: Reg
47
47
  }
48
48
  context.position += line.length;
49
49
  }
50
- return [push([block, overflow, closer], matches)];
50
+ return new List(push([block, overflow, closer], matches).map(str => new Data(str)));
51
51
  });
52
52
  }
@@ -1,5 +1,5 @@
1
1
  import { indent } from './indent';
2
- import { input } from '../../data/parser';
2
+ import { List, Data, input } from '../../data/parser';
3
3
  import { inspect } from '../../../debug.test';
4
4
 
5
5
  describe('Unit: combinator/indent', () => {
@@ -7,7 +7,7 @@ describe('Unit: combinator/indent', () => {
7
7
  const { context: ctx } = input('', {});
8
8
 
9
9
  it('valid', () => {
10
- const parser = indent(({ context }) => { context.position = context.source.length; return [[context.source]]; });
10
+ const parser = indent(({ context }) => { context.position = context.source.length; return new List([new Data(context.source)]); });
11
11
  assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
12
12
  assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
13
13
  assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
@@ -1,4 +1,4 @@
1
- import { Parser, subinput, eval, failsafe } from '../../data/parser';
1
+ import { Parser, List, Data, subinput, eval, failsafe } from '../../data/parser';
2
2
  import { some } from '../../data/parser/some';
3
3
  import { block } from '../constraint/block';
4
4
  import { line } from '../constraint/line';
@@ -24,15 +24,15 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
24
24
  some(line(open(indent, ({ context }) => {
25
25
  const { source, position } = context;
26
26
  context.position = source.length;
27
- return [[source.slice(position)]];
27
+ return new List([new Data(source.slice(position))]);
28
28
  }))),
29
29
  ([indent]) => indent.length * 2 + +(indent[0] === ' '), {})), separation),
30
30
  (lines, context) => {
31
31
  assert(parser = parser as Parser<N>);
32
- const result = parser(subinput(trimBlockEnd(lines.join('')), context));
32
+ const result = parser(subinput(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context));
33
33
  assert(result);
34
34
  return result
35
- ? [eval(result)]
35
+ ? eval(result)
36
36
  : undefined;
37
37
  }));
38
38
  }
@@ -1,8 +1,8 @@
1
- import { Parser } from '../../data/parser';
1
+ import { Parser, List, Data } from '../../data/parser';
2
2
  import { fmap } from '../monad/fmap';
3
3
 
4
4
  export function reverse<P extends Parser<unknown>>(parser: P): P;
5
5
  export function reverse<N>(parser: Parser<N>): Parser<N> {
6
- return fmap(parser, nodes => nodes.reverse());
6
+ return fmap(parser, nodes => nodes.foldr((node, acc) => acc.push(nodes.delete(node)) && acc, new List<Data<N>>()));
7
7
  }
8
8
 
@@ -8,7 +8,7 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>): Parser<N> {
8
8
  return failsafe(({ context }) => {
9
9
  const { source, position } = context;
10
10
  if (position === source.length) return;
11
- const src = eval(match({ context }))?.[0] ?? '';
11
+ const src = eval(match({ context }))?.head?.value ?? '';
12
12
  assert(source.startsWith(src, position));
13
13
  if (src === '') return;
14
14
  context.range = src.length;
@@ -21,7 +21,7 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>): Parser<N> {
21
21
  context.source = source;
22
22
  context.offset -= position;
23
23
  if (result === undefined) return;
24
- return [eval(result)];
24
+ return eval(result);
25
25
  });
26
26
  }
27
27
 
@@ -48,7 +48,6 @@ export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>): Parser<N>
48
48
  context.source = source;
49
49
  context.offset -= position;
50
50
  if (res2 === undefined) return;
51
- assert(context.position === position + src.length);
52
- return [eval(res2)];
51
+ return eval(res2);
53
52
  });
54
53
  }
@@ -1,40 +1,39 @@
1
- import { Parser, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
1
+ import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
2
2
  import { matcher, clear } from '../../../combinator';
3
- import { unshift, push } from 'spica/array';
4
3
 
5
4
  export function surround<P extends Parser<unknown>, S = string>(
6
5
  opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
7
6
  optional?: false,
8
- f?: (rss: [S[], SubNode<P>[], S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
9
- g?: (rss: [S[], SubNode<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
7
+ f?: (rss: [List<Data<S>>, List<Data<SubNode<P>>>, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
8
+ g?: (rss: [List<Data<S>>, List<Data<SubNode<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
10
9
  backtracks?: readonly number[],
11
10
  ): P;
12
11
  export function surround<P extends Parser<unknown>, S = string>(
13
12
  opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
14
13
  optional?: boolean,
15
- f?: (rss: [S[], SubNode<P>[] | undefined, S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
16
- g?: (rss: [S[], SubNode<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
14
+ f?: (rss: [List<Data<S>>, List<Data<SubNode<P>>> | undefined, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
15
+ g?: (rss: [List<Data<S>>, List<Data<SubNode<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
17
16
  backtracks?: readonly number[],
18
17
  ): P;
19
18
  export function surround<P extends Parser<unknown>, S = string>(
20
19
  opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
21
20
  optional?: false,
22
- f?: (rss: [S[], Node<P>[], S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
23
- g?: (rss: [S[], Node<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
21
+ f?: (rss: [List<Data<S>>, List<Data<Node<P>>>, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
22
+ g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
24
23
  backtracks?: readonly number[],
25
24
  ): P;
26
25
  export function surround<P extends Parser<unknown>, S = string>(
27
26
  opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
28
27
  optional?: boolean,
29
- f?: (rss: [S[], Node<P>[] | undefined, S[]], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
30
- g?: (rss: [S[], Node<P>[] | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
28
+ f?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
29
+ g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
31
30
  backtracks?: readonly number[],
32
31
  ): P;
33
32
  export function surround<N>(
34
33
  opener: string | RegExp | Parser<N>, parser: Parser<N>, closer: string | RegExp | Parser<N>,
35
34
  optional: boolean = false,
36
- f?: (rss: [N[], N[], N[]], context: Ctx) => Result<N>,
37
- g?: (rss: [N[], N[] | undefined], context: Ctx) => Result<N>,
35
+ f?: (rss: [List<Data<N>>, List<Data<N>>, List<Data<N>>], context: Ctx) => Result<N>,
36
+ g?: (rss: [List<Data<N>>, List<Data<N>> | undefined], context: Ctx) => Result<N>,
38
37
  backtracks: readonly number[] = [],
39
38
  ): Parser<N> {
40
39
  switch (typeof opener) {
@@ -88,7 +87,7 @@ export function surround<N>(
88
87
  context.range = context.position - position;
89
88
  const result = f
90
89
  ? f([nodesO, nodesM!, nodesC], context)
91
- : [push(nodesM ? unshift(nodesO, nodesM) : nodesO, nodesC)] satisfies [N[]];
90
+ : nodesO.import(nodesM ?? new List()).import(nodesC);
92
91
  if (result) {
93
92
  context.linebreak ||= linebreak;
94
93
  }
@@ -157,8 +156,8 @@ export function setBacktrack(
157
156
  position: number,
158
157
  length: number = 1,
159
158
  ): void {
160
- const { source, state = 0 } = context;
161
- if (state === 0) return;
159
+ // 以降バックトラックの可能性がなく記録不要の場合もあるが判別が面倒なので省略
160
+ const { source } = context;
162
161
  if (position === source.length) return;
163
162
  if (length === 0) return;
164
163
  for (const backtrack of backtracks) {
@@ -1,10 +1,10 @@
1
- import { Parser, Result, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
1
+ import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, eval, failsafe } from '../../data/parser';
2
2
 
3
- export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
4
- export function bind<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
5
- export function bind<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
6
- export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
7
- export function bind<N, U>(parser: Parser<N>, f: (nodes: N[], context: Ctx) => Result<U>): Parser<U> {
3
+ export function bind<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: List<Data<SubNode<P>>>, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
4
+ export function bind<P extends Parser<unknown>>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
5
+ export function bind<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: List<Data<N>>, context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>): P;
6
+ export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
7
+ export function bind<N, U>(parser: Parser<N>, f: (nodes: List<Data<N>>, context: Ctx) => Result<U>): Parser<U> {
8
8
  assert(parser);
9
9
  return failsafe(input => {
10
10
  const { context } = input;
@@ -1,10 +1,10 @@
1
- import { Parser, Ctx, SubParsers, Node, Context, IntermediateParser, SubNode } from '../../data/parser';
1
+ import { Parser, List, Data, Ctx, SubParsers, Node, Context, IntermediateParser, SubNode } from '../../data/parser';
2
2
  import { bind } from './bind';
3
3
 
4
- export function fmap<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: SubNode<P>[], context: Context<P>) => Node<P>[]): P;
5
- export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => Node<P>[]): P;
6
- export function fmap<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: N[], context: Context<P>) => Node<P>[]): P;
7
- export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes: Node<P>[], context: Context<P>) => U[]): Parser<U, Context<P>, SubParsers<P>>;
8
- export function fmap<N, U>(parser: Parser<N>, f: (nodes: N[], context: Ctx) => U[]): Parser<U> {
9
- return bind(parser, (nodes, context) => [f(nodes, context)]);
4
+ export function fmap<P extends Parser<unknown>>(parser: IntermediateParser<P>, f: (nodes: List<Data<SubNode<P>>>, context: Context<P>) => List<Data<Node<P>>>): P;
5
+ export function fmap<P extends Parser<unknown>>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => List<Data<Node<P>>>): P;
6
+ export function fmap<N, P extends Parser<unknown>>(parser: Parser<N, Context<P>, SubParsers<P>>, f: (nodes: List<Data<N>>, context: Context<P>) => List<Data<Node<P>>>): P;
7
+ export function fmap<U, P extends Parser<unknown>>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => List<Data<U>>): Parser<U, Context<P>, SubParsers<P>>;
8
+ export function fmap<N, U>(parser: Parser<N>, f: (nodes: List<Data<N>>, context: Ctx) => List<Data<U>>): Parser<U> {
9
+ return bind(parser, (nodes, context) => f(nodes, context));
10
10
  }
@@ -0,0 +1,135 @@
1
+ // Memory-efficient flexible list.
2
+
3
+ export class List<N extends List.Node = List.Node> {
4
+ constructor(nodes?: ArrayLike<N>) {
5
+ if (nodes === undefined) return;
6
+ for (let i = 0; i < nodes.length; ++i) {
7
+ this.push(nodes[i]);
8
+ }
9
+ }
10
+ public length = 0;
11
+ public head?: N = undefined;
12
+ public get tail(): N | undefined {
13
+ return this.head?.next;
14
+ }
15
+ public get last(): N | undefined {
16
+ return this.head?.prev;
17
+ }
18
+ public insert(node: N, before?: N): N {
19
+ assert(!node.next);
20
+ if (++this.length === 1) {
21
+ return this.head = node.next = node.prev = node;
22
+ }
23
+ assert(node !== before);
24
+ const next = node.next = before ?? this.head!;
25
+ const prev = node.prev = next.prev!;
26
+ return next.prev = prev.next = node;
27
+ }
28
+ public delete(node: N): N {
29
+ assert(node.next);
30
+ assert(this.length > 0);
31
+ if (--this.length === 0) {
32
+ this.head = undefined;
33
+ }
34
+ else {
35
+ const { next, prev } = node;
36
+ if (node === this.head) {
37
+ this.head = next;
38
+ }
39
+ // Error if not used.
40
+ prev!.next = next;
41
+ next!.prev = prev;
42
+ }
43
+ node.next = node.prev = undefined;
44
+ return node;
45
+ }
46
+ public unshift(node: N): N {
47
+ assert(!node.next);
48
+ return this.head = this.insert(node, this.head);
49
+ }
50
+ public push(node: N): N {
51
+ assert(!node.next);
52
+ return this.insert(node, this.head);
53
+ }
54
+ public shift(): N | undefined {
55
+ if (this.length === 0) return;
56
+ return this.delete(this.head!);
57
+ }
58
+ public pop(): N | undefined {
59
+ if (this.length === 0) return;
60
+ return this.delete(this.head!.prev!);
61
+ }
62
+ public import(list: List<N>, before?: N): this {
63
+ assert(list !== this);
64
+ if (list.length === 0) return this;
65
+ if (this.length === 0) {
66
+ this.head = list.head;
67
+ this.length += list.length;
68
+ list.clear();
69
+ return this;
70
+ }
71
+ const head = list.head!;
72
+ const last = list.last!;
73
+ const next = last.next = before ?? this.head!;
74
+ const prev = head.prev = next.prev!;
75
+ next.prev = last;
76
+ prev.next = head;
77
+ this.length += list.length;
78
+ list.clear();
79
+ return this;
80
+ }
81
+ public clear(): void {
82
+ this.length = 0;
83
+ this.head = undefined;
84
+ }
85
+ public *[Symbol.iterator](): Iterator<N, undefined, undefined> {
86
+ for (let node = this.head; node && this.head;) {
87
+ const next = node.next;
88
+ yield node;
89
+ node = next;
90
+ if (node === this.head) break;
91
+ }
92
+ }
93
+ public flatMap<T extends List.Node>(f: (node: N) => List<T>): List<T> {
94
+ const acc = new List<T>();
95
+ for (let node = this.head; node && this.head;) {
96
+ const next = node.next;
97
+ acc.import(f(node));
98
+ node = next;
99
+ if (node === this.head) break;
100
+ }
101
+ return acc;
102
+ }
103
+ public foldl<T>(f: (acc: T, node: N) => T, acc: T): T {
104
+ for (let node = this.head; node && this.head;) {
105
+ const next = node.next;
106
+ acc = f(acc, node);
107
+ node = next;
108
+ if (node === this.head) break;
109
+ }
110
+ return acc;
111
+ }
112
+ public foldr<T>(f: (node: N, acc: T) => T, acc: T): T {
113
+ for (let node = this.head?.prev; node && this.head;) {
114
+ const prev = node.prev;
115
+ acc = f(node, acc);
116
+ if (node === this.head) break;
117
+ node = prev;
118
+ }
119
+ return acc;
120
+ }
121
+ public find(f: (node: N) => unknown): N | undefined {
122
+ for (let node = this.head; node && this.head;) {
123
+ const next = node.next;
124
+ if (f(node)) return node;
125
+ node = next;
126
+ if (node === this.head) break;
127
+ }
128
+ }
129
+ }
130
+ export namespace List {
131
+ export class Node {
132
+ public next?: this = undefined;
133
+ public prev?: this = undefined;
134
+ }
135
+ }