securemark 0.294.10 → 0.295.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 (53) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/design.md +5 -5
  3. package/dist/index.js +141 -127
  4. package/package.json +1 -1
  5. package/src/combinator/control/constraint/block.ts +1 -1
  6. package/src/combinator/control/constraint/contract.ts +5 -6
  7. package/src/combinator/control/constraint/line.ts +1 -2
  8. package/src/combinator/control/manipulation/convert.ts +1 -1
  9. package/src/combinator/control/manipulation/fallback.ts +1 -1
  10. package/src/combinator/control/manipulation/indent.ts +2 -2
  11. package/src/combinator/control/manipulation/lazy.ts +1 -1
  12. package/src/combinator/control/manipulation/match.ts +2 -5
  13. package/src/combinator/control/manipulation/recovery.ts +1 -1
  14. package/src/combinator/control/manipulation/reverse.ts +1 -1
  15. package/src/combinator/control/manipulation/scope.ts +3 -7
  16. package/src/combinator/control/manipulation/surround.ts +51 -62
  17. package/src/combinator/control/monad/bind.ts +7 -12
  18. package/src/combinator/control/monad/fmap.ts +4 -4
  19. package/src/combinator/data/parser/context.ts +12 -12
  20. package/src/combinator/data/parser/inits.ts +3 -6
  21. package/src/combinator/data/parser/sequence.ts +3 -6
  22. package/src/combinator/data/parser/some.ts +2 -2
  23. package/src/combinator/data/parser/subsequence.ts +1 -1
  24. package/src/combinator/data/parser/tails.ts +1 -1
  25. package/src/combinator/data/parser/union.ts +1 -1
  26. package/src/combinator/data/parser.ts +10 -9
  27. package/src/parser/api/body.test.ts +1 -1
  28. package/src/parser/api/header.test.ts +2 -2
  29. package/src/parser/api/normalize.test.ts +2 -0
  30. package/src/parser/api/normalize.ts +1 -1
  31. package/src/parser/context.ts +9 -6
  32. package/src/parser/header.test.ts +2 -2
  33. package/src/parser/header.ts +3 -3
  34. package/src/parser/inline/annotation.ts +1 -1
  35. package/src/parser/inline/autolink/account.ts +3 -3
  36. package/src/parser/inline/autolink/anchor.ts +1 -1
  37. package/src/parser/inline/autolink/email.ts +1 -1
  38. package/src/parser/inline/autolink/hashnum.ts +1 -1
  39. package/src/parser/inline/autolink/hashtag.ts +1 -1
  40. package/src/parser/inline/autolink/url.ts +9 -10
  41. package/src/parser/inline/bracket.ts +31 -19
  42. package/src/parser/inline/extension/index.ts +3 -3
  43. package/src/parser/inline/extension/indexee.ts +1 -1
  44. package/src/parser/inline/extension/label.ts +1 -1
  45. package/src/parser/inline/extension/placeholder.ts +1 -1
  46. package/src/parser/inline/htmlentity.ts +1 -1
  47. package/src/parser/inline/link.ts +12 -14
  48. package/src/parser/inline/math.ts +2 -2
  49. package/src/parser/inline/media.ts +15 -17
  50. package/src/parser/inline/reference.ts +9 -9
  51. package/src/parser/inline/ruby.ts +4 -4
  52. package/src/parser/inline/template.ts +6 -10
  53. package/src/parser/visibility.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.294.10",
3
+ "version": "0.295.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",
@@ -1,7 +1,7 @@
1
1
  import { Parser, failsafe } from '../../data/parser';
2
2
  import { isBlankline } from './line';
3
3
 
4
- export function block<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
4
+ export function block<P extends Parser>(parser: P, separation?: boolean): P;
5
5
  export function block<N>(parser: Parser<N>, separation = true): Parser<N> {
6
6
  assert(parser);
7
7
  return failsafe(input => {
@@ -1,13 +1,13 @@
1
1
  import { Parser, Input, List, Data, Ctx, Node, Context, failsafe } from '../../data/parser';
2
2
  import { matcher } from '../../../combinator';
3
3
 
4
- //export function contract<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], parser: P, cond: (nodes: readonly Data<P>[], rest: string) => boolean): P;
4
+ //export function contract<P extends Parser>(patterns: string | RegExp | (string | RegExp)[], parser: P, cond: (nodes: readonly Data<P>[], rest: string) => boolean): P;
5
5
  //export function contract<N>(patterns: string | RegExp | (string | RegExp)[], parser: Parser<N>, cond: (nodes: readonly N[], rest: string) => boolean): Parser<N> {
6
6
  // return verify(validate(patterns, parser), cond);
7
7
  //}
8
8
 
9
- export function validate<P extends Parser<unknown>>(pattern: string | RegExp, parser: P): P;
10
- export function validate<P extends Parser<unknown>>(cond: ((input: Input<Context<P>>) => boolean), parser: P): P;
9
+ export function validate<P extends Parser>(pattern: string | RegExp, parser: P): P;
10
+ export function validate<P extends Parser>(cond: ((input: Input<Context<P>>) => boolean), parser: P): P;
11
11
  export function validate<N>(pattern: string | RegExp | ((input: Input<Ctx>) => boolean), parser: Parser<N>): Parser<N> {
12
12
  if (typeof pattern === 'function') return guard(pattern, parser);
13
13
  const match = matcher(pattern, false);
@@ -20,7 +20,7 @@ export function validate<N>(pattern: string | RegExp | ((input: Input<Ctx>) => b
20
20
  };
21
21
  }
22
22
 
23
- function guard<P extends Parser<unknown>>(f: (input: Input<Context<P>>) => boolean, parser: P): P;
23
+ function guard<P extends Parser>(f: (input: Input<Context<P>>) => boolean, parser: P): P;
24
24
  function guard<N>(f: (input: Input<Ctx>) => boolean, parser: Parser<N>): Parser<N> {
25
25
  return input =>
26
26
  f(input)
@@ -28,7 +28,7 @@ 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: List<Data<Node<P>>>, context: Context<P>) => boolean): P;
31
+ export function verify<P extends Parser>(parser: P, cond: (nodes: List<Data<Node<P>>>, context: Context<P>) => boolean): P;
32
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 => {
@@ -36,7 +36,6 @@ export function verify<N>(parser: Parser<N>, cond: (nodes: List<Data<N>>, contex
36
36
  const { source, position } = context;
37
37
  if (position === source.length) return;
38
38
  const result = parser(input);
39
- assert(context.position > position || !result);
40
39
  if (result && !cond(result, context)) return;
41
40
  return result;
42
41
  });
@@ -1,6 +1,6 @@
1
1
  import { Parser, input, failsafe } from '../../data/parser';
2
2
 
3
- export function line<P extends Parser<unknown>>(parser: P): P;
3
+ export function line<P extends Parser>(parser: P): P;
4
4
  export function line<N>(parser: Parser<N>): Parser<N> {
5
5
  assert(parser);
6
6
  return failsafe(({ context }) => {
@@ -12,7 +12,6 @@ export function line<N>(parser: Parser<N>): Parser<N> {
12
12
  const result = parser(input(line, context));
13
13
  context.position += position;
14
14
  context.position += result && context.position === position ? line.length : 0;
15
- assert(context.position > position || !result);
16
15
  context.source = source;
17
16
  context.offset -= position;
18
17
  if (result === undefined) return;
@@ -1,6 +1,6 @@
1
1
  import { Parser, List, Ctx, Context, subinput, failsafe } from '../../data/parser';
2
2
 
3
- export function convert<P extends Parser<unknown>>(conv: (source: string, context: Context<P>) => string, parser: P, empty?: boolean): P;
3
+ export function convert<P extends Parser>(conv: (source: string, context: Context<P>) => string, parser: P, empty?: boolean): P;
4
4
  export function convert<N>(conv: (source: string, context: Ctx) => string, parser: Parser<N>, empty = false): Parser<N> {
5
5
  assert(parser);
6
6
  return failsafe(input => {
@@ -1,7 +1,7 @@
1
1
  import { Parser, Node, Context } from '../../data/parser';
2
2
  import { union } from '../../data/parser/union';
3
3
 
4
- export function fallback<P extends Parser<unknown>>(parser: P, otherwise: Parser<Node<P>, Context<P>>): P;
4
+ export function fallback<P extends Parser>(parser: P, otherwise: Parser<Node<P>, Context<P>>): P;
5
5
  export function fallback<N>(parser: Parser<N>, otherwise: Parser<N>): Parser<N> {
6
6
  return union([parser, otherwise]);
7
7
  }
@@ -7,8 +7,8 @@ import { match } from './match';
7
7
  import { open } from './surround';
8
8
  import { memoize } from 'spica/memoize';
9
9
 
10
- export function indent<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
11
- export function indent<P extends Parser<unknown>>(opener: RegExp, parser: P, separation?: boolean): P;
10
+ export function indent<P extends Parser>(parser: P, separation?: boolean): P;
11
+ export function indent<P extends Parser>(opener: RegExp, parser: P, separation?: boolean): P;
12
12
  export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolean = false, separation = false): Parser<N> {
13
13
  if (typeof opener === 'function') {
14
14
  separation = parser as boolean;
@@ -1,6 +1,6 @@
1
1
  import { Parser } from '../../data/parser';
2
2
 
3
- export function lazy<P extends Parser<unknown>>(builder: () => P): P;
3
+ export function lazy<P extends Parser>(builder: () => P): P;
4
4
  export function lazy<N>(builder: () => Parser<N>): Parser<N> {
5
5
  let parser: Parser<N>;
6
6
  return input =>
@@ -1,7 +1,7 @@
1
1
  import { Parser, failsafe } from '../../data/parser';
2
2
  import { consume } from '../../../combinator';
3
3
 
4
- export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
4
+ export function match<P extends Parser>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
5
5
  export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<N>): Parser<N> {
6
6
  assert(!pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^'));
7
7
  const count = typeof pattern === 'object'
@@ -18,9 +18,6 @@ export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Pars
18
18
  count && consume(params[0].length, context);
19
19
  const result = f(params)(input);
20
20
  context.position += result && context.position === position ? params[0].length : 0;
21
- assert(context.position > position || !result);
22
- return context.position > position
23
- ? result
24
- : undefined;
21
+ return result;
25
22
  });
26
23
  }
@@ -1,6 +1,6 @@
1
1
  import { Parser, Input, Result, Ctx, Node, Context } from '../../data/parser';
2
2
 
3
- export function recover<P extends Parser<unknown>>(parser: P, fallback: (input: Input<Context<P>>, reason: unknown) => Result<Node<P>>): P;
3
+ export function recover<P extends Parser>(parser: P, fallback: (input: Input<Context<P>>, reason: unknown) => Result<Node<P>>): P;
4
4
  export function recover<N>(parser: Parser<N>, fallback: (input: Input<Ctx>, reason: unknown) => Result<N>): Parser<N> {
5
5
  return input => {
6
6
  const { context } = input;
@@ -1,7 +1,7 @@
1
1
  import { Parser, List, Data } from '../../data/parser';
2
2
  import { fmap } from '../monad/fmap';
3
3
 
4
- export function reverse<P extends Parser<unknown>>(parser: P): P;
4
+ export function reverse<P extends Parser>(parser: P): P;
5
5
  export function reverse<N>(parser: Parser<N>): Parser<N> {
6
6
  return fmap(parser, nodes => nodes.foldr((node, acc) => acc.push(nodes.delete(node)) && acc, new List<Data<N>>()));
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { Parser, Context, input, failsafe } from '../../data/parser';
2
2
  import { matcher } from '../../../combinator';
3
3
 
4
- export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P, slice?: boolean): P;
4
+ export function focus<P extends Parser>(scope: string | RegExp, parser: P, slice?: boolean): P;
5
5
  export function focus<N>(scope: string | RegExp, parser: Parser<N>, slice = true): Parser<N> {
6
6
  assert(parser);
7
7
  const match = matcher(scope, false);
@@ -16,7 +16,6 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>, slice = true
16
16
  if (!slice) {
17
17
  const result = parser(arg);
18
18
  context.position += result && context.position === position ? range : 0;
19
- assert(context.position > position || !result);
20
19
  return result;
21
20
  }
22
21
  context.offset ??= 0;
@@ -24,7 +23,6 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>, slice = true
24
23
  const result = parser(input(src, context));
25
24
  context.position += position;
26
25
  context.position += result && context.position === position ? src.length : 0;
27
- assert(context.position > position || !result);
28
26
  context.source = source;
29
27
  context.offset -= position;
30
28
  return result;
@@ -32,8 +30,8 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>, slice = true
32
30
  }
33
31
 
34
32
  //export function rewrite<N, C extends Ctx, D extends Parser<unknown, C>[]>(scope: Parser<unknown, C, D>, parser: Parser<N, C, never>): Parser<N, C, D>;
35
- export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Context<P>>, parser: P, slice?: boolean): P;
36
- export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>, slice = true): Parser<N> {
33
+ export function rewrite<P extends Parser>(scope: Parser<unknown, Context<P>>, parser: P, slice?: boolean): P;
34
+ export function rewrite<N>(scope: Parser, parser: Parser<N>, slice = true): Parser<N> {
37
35
  assert(scope);
38
36
  assert(parser);
39
37
  return failsafe(arg => {
@@ -48,7 +46,6 @@ export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>, slice = tr
48
46
  context.position = position;
49
47
  const res2 = parser(arg);
50
48
  context.position += res2 && context.position === position ? range : 0;
51
- assert(context.position > position || !res2);
52
49
  return res2;
53
50
  }
54
51
  const src = source.slice(position, context.position);
@@ -59,7 +56,6 @@ export function rewrite<N>(scope: Parser<unknown>, parser: Parser<N>, slice = tr
59
56
  const res2 = parser(input(src, context));
60
57
  context.position += position;
61
58
  context.position += res2 && context.position === position ? src.length : 0;
62
- assert(context.position > position || !res2);
63
59
  context.source = source;
64
60
  context.offset -= position;
65
61
  return res2;
@@ -1,52 +1,52 @@
1
1
  import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, failsafe } from '../../data/parser';
2
2
  import { matcher, clear } from '../../../combinator';
3
3
 
4
- export function surround<P extends Parser<unknown>, S = string>(
4
+ export function surround<P extends Parser, S = string>(
5
5
  opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
6
6
  optional?: false,
7
- backtracks?: readonly number[],
7
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
8
8
  f?: (rss: [List<Data<S>>, List<Data<SubNode<P>>>, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
9
9
  g?: (rss: [List<Data<S>>, List<Data<SubNode<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
10
10
  ): P;
11
- export function surround<P extends Parser<unknown>, S = string>(
11
+ export function surround<P extends Parser, S = string>(
12
12
  opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
13
13
  optional?: boolean,
14
- backtracks?: readonly number[],
14
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
15
15
  f?: (rss: [List<Data<S>>, List<Data<SubNode<P>>> | undefined, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
16
16
  g?: (rss: [List<Data<S>>, List<Data<SubNode<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
17
17
  ): P;
18
- export function surround<P extends Parser<unknown>, S = string>(
18
+ export function surround<P extends Parser, S = string>(
19
19
  opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
20
20
  optional?: false,
21
- backtracks?: readonly number[],
21
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
22
22
  f?: (rss: [List<Data<S>>, List<Data<Node<P>>>, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
23
23
  g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
24
24
  ): P;
25
- export function surround<P extends Parser<unknown>, S = string>(
25
+ export function surround<P extends Parser, S = string>(
26
26
  opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
27
27
  optional?: boolean,
28
- backtracks?: readonly number[],
28
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
29
29
  f?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
30
30
  g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
31
31
  ): P;
32
32
  export function surround<P extends Parser<string>, S = string>(
33
33
  opener: string | RegExp | Parser<S, Context<P>>, parser: string | RegExp | P, closer: string | RegExp | Parser<S, Context<P>>,
34
34
  optional?: false,
35
- backtracks?: readonly number[],
35
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
36
36
  f?: (rss: [List<Data<S>>, List<Data<Node<P>>>, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
37
37
  g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
38
38
  ): P;
39
39
  export function surround<P extends Parser<string>, S = string>(
40
40
  opener: string | RegExp | Parser<S, Context<P>>, parser: string | RegExp | P, closer: string | RegExp | Parser<S, Context<P>>,
41
41
  optional?: boolean,
42
- backtracks?: readonly number[],
42
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
43
43
  f?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
44
44
  g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
45
45
  ): P;
46
46
  export function surround<N>(
47
47
  opener: string | RegExp | Parser<N>, parser: string | RegExp | Parser<N>, closer: string | RegExp | Parser<N>,
48
48
  optional: boolean = false,
49
- backtracks: readonly number[] = [],
49
+ backtracks: readonly [] | readonly [number] | readonly [number, number] = [],
50
50
  f?: (rss: [List<Data<N>>, List<Data<N>>, List<Data<N>>], context: Ctx) => Result<N>,
51
51
  g?: (rss: [List<Data<N>>, List<Data<N>> | undefined], context: Ctx) => Result<N>,
52
52
  ): Parser<N> {
@@ -68,6 +68,7 @@ export function surround<N>(
68
68
  closer = clear(matcher(closer, true));
69
69
  }
70
70
  assert(closer);
71
+ const [rbs, wbs] = reduce(backtracks);
71
72
  return failsafe(input => {
72
73
  const { context } = input;
73
74
  const { source, position } = context;
@@ -75,26 +76,23 @@ export function surround<N>(
75
76
  const { linebreak } = context;
76
77
  context.linebreak = 0;
77
78
  const nodesO = opener(input);
78
- assert(context.position >= position);
79
79
  if (!nodesO) {
80
80
  return void revert(context, linebreak);
81
81
  }
82
- if (isBacktrack(context, backtracks, position, context.position - position || 1)) {
82
+ if (rbs && isBacktrack(context, rbs, position, context.position - position || 1)) {
83
83
  return void revert(context, linebreak);
84
84
  }
85
85
  const nodesM = context.position < source.length ? parser(input) : undefined;
86
- assert(context.position >= position);
87
86
  context.range = context.position - position;
88
87
  if (!nodesM && !optional) {
89
- setBacktrack(context, backtracks, position);
88
+ wbs && setBacktrack(context, wbs, position);
90
89
  const result = g?.([nodesO, nodesM], context);
91
90
  return result || void revert(context, linebreak);
92
91
  }
93
92
  const nodesC = nodesM || optional ? closer(input) : undefined;
94
- assert(context.position >= position);
95
93
  context.range = context.position - position;
96
94
  if (!nodesC) {
97
- setBacktrack(context, backtracks, position);
95
+ wbs && setBacktrack(context, wbs, position);
98
96
  const result = g?.([nodesO, nodesM], context);
99
97
  return result || void revert(context, linebreak);
100
98
  }
@@ -111,108 +109,99 @@ export function surround<N>(
111
109
  return result || void revert(context, linebreak);
112
110
  });
113
111
  }
114
- export function open<P extends Parser<unknown>>(
112
+ export function open<P extends Parser>(
115
113
  opener: string | RegExp | Parser<Node<P>, Context<P>>,
116
114
  parser: P,
117
115
  optional?: boolean,
118
- backtracks?: readonly number[],
116
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
119
117
  ): P;
120
118
  export function open<P extends Parser<string>>(
121
119
  opener: string | RegExp | Parser<Node<P>, Context<P>>,
122
120
  parser: string | RegExp | P,
123
121
  optional?: boolean,
124
- backtracks?: readonly number[],
122
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
125
123
  ): P;
126
124
  export function open<N>(
127
125
  opener: string | RegExp | Parser<N, Ctx>,
128
126
  parser: string | RegExp | Parser<N>,
129
127
  optional?: boolean,
130
- backtracks?: readonly number[],
128
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
131
129
  ): Parser<N> {
132
130
  return surround(opener, parser as Parser<N>, '', optional, backtracks);
133
131
  }
134
- export function close<P extends Parser<unknown>>(
132
+ export function close<P extends Parser>(
135
133
  parser: P,
136
134
  closer: string | RegExp | Parser<Node<P>, Context<P>>,
137
135
  optional?: boolean,
138
- backtracks?: readonly number[],
136
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
139
137
  ): P;
140
138
  export function close<P extends Parser<string>>(
141
139
  parser: string | RegExp | P,
142
140
  closer: string | RegExp | Parser<Node<P>, Context<P>>,
143
141
  optional?: boolean,
144
- backtracks?: readonly number[],
142
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
145
143
  ): P;
146
144
  export function close<N>(
147
145
  parser: string | RegExp | Parser<N>,
148
146
  closer: string | RegExp | Parser<N, Ctx>,
149
147
  optional?: boolean,
150
- backtracks?: readonly number[],
148
+ backtracks?: readonly [] | readonly [number] | readonly [number, number],
151
149
  ): Parser<N> {
152
150
  return surround('', parser as Parser<N>, closer, optional, backtracks);
153
151
  }
154
152
 
155
- const statesize = 2;
153
+ const commandsize = 2;
156
154
  export function isBacktrack(
157
155
  context: Ctx,
158
- backtracks: readonly number[],
156
+ backtrack: number,
159
157
  position: number = context.position,
160
158
  length: number = 1,
161
159
  ): boolean {
160
+ assert(backtrack & 1);
161
+ if (length === 0) return false;
162
162
  const { source } = context;
163
163
  if (position === source.length) return false;
164
- if (length === 0) return false;
165
- for (const backtrack of backtracks) {
166
- if (backtrack & 1) {
167
- const { backtracks = {}, offset = 0 } = context;
168
- for (let i = 0; i < length; ++i) {
169
- if (position + i === source.length) break;
170
- if (source[position + i] !== source[position + 0]) break;
171
- const pos = position + i + offset;
172
- if (!(pos in backtracks)) continue;
173
- if (backtracks[pos] & 1 << size(backtrack >>> statesize)) return true;
174
- }
175
- }
164
+ const { backtracks = {}, offset = 0 } = context;
165
+ for (let i = 0; i < length; ++i) {
166
+ if (position + i === source.length) break;
167
+ if (i > 0 && source[position + i] !== source[position]) break;
168
+ const pos = position + i + offset;
169
+ if (backtracks[pos] & backtrack >>> commandsize) return true;
176
170
  }
177
171
  return false;
178
172
  }
179
173
  export function setBacktrack(
180
174
  context: Ctx,
181
- backtracks: readonly number[],
175
+ backtrack: number,
182
176
  position: number,
183
177
  length: number = 1,
184
178
  ): void {
185
179
  // バックトラックの可能性がなく記録不要の場合もあるが判別が面倒なので省略
180
+ assert(backtrack & 2);
181
+ if (length === 0) return;
186
182
  const { source } = context;
187
183
  if (position === source.length) return;
188
- if (length === 0) return;
184
+ const { backtracks = {}, offset = 0 } = context;
185
+ for (let i = 0; i < length; ++i) {
186
+ if (position + i === source.length) break;
187
+ const pos = position + i + offset;
188
+ backtracks[pos] |= backtrack >>> commandsize;
189
+ }
190
+ }
191
+ function reduce(backtracks: readonly number[]): readonly [number, number] {
192
+ let rbs = 0;
193
+ let wbs = 0;
189
194
  for (const backtrack of backtracks) {
195
+ if (backtrack & 1) {
196
+ rbs |= backtrack;
197
+ }
190
198
  if (backtrack & 2) {
191
- const { backtracks = {}, offset = 0 } = context;
192
- for (let i = 0; i < length; ++i) {
193
- if (position + i === source.length) break;
194
- const pos = position + i + offset;
195
- backtracks[pos] |= 1 << size(backtrack >>> statesize);
196
- }
199
+ wbs |= backtrack;
197
200
  }
198
201
  }
202
+ return [rbs, wbs];
199
203
  }
200
204
 
201
205
  function revert(context: Ctx, linebreak: number | undefined): void {
202
206
  context.linebreak = linebreak;
203
207
  }
204
- function size(bits: number): number {
205
- if (bits === 0) return 0;
206
- let p = 0;
207
- for (let s = 32 / 2; s > 0; s >>>= 1) {
208
- const q = p + s;
209
- if (bits >>> q === 0) continue;
210
- p = q;
211
- }
212
- return p + 1;
213
- }
214
- assert(size(0 << 0) === 0);
215
- assert(size(1 << 0) === 1);
216
- assert(size(1 << 1) === 2);
217
- assert(size(1 << 30) === 31);
218
- assert(size(1 << 31) === 32);
@@ -1,23 +1,18 @@
1
1
  import { Parser, Result, List, Data, Ctx, Node, Context, SubParsers, SubNode, IntermediateParser, failsafe } from '../../data/parser';
2
2
 
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>>;
3
+ export function bind<P extends Parser>(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>(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>(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>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
7
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;
11
11
  const { source, position } = context;
12
12
  if (position === source.length) return;
13
- const res1 = parser(input);
14
- assert(context.position > position || !res1);
15
- if (res1 === undefined) return;
13
+ const result = parser(input);
14
+ if (result === undefined) return;
16
15
  context.range = context.position - position;
17
- const res2 = f(res1, context);
18
- assert(context.position > position || !res2);
19
- return context.position > position
20
- ? res2
21
- : undefined;
16
+ return f(result, context);
22
17
  });
23
18
  }
@@ -1,10 +1,10 @@
1
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: 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>>;
4
+ export function fmap<P extends Parser>(parser: IntermediateParser<P>, f: (nodes: List<Data<SubNode<P>>>, context: Context<P>) => List<Data<Node<P>>>): P;
5
+ export function fmap<P extends Parser>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => List<Data<Node<P>>>): P;
6
+ export function fmap<N, P extends Parser>(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>(parser: P, f: (nodes: List<Data<Node<P>>>, context: Context<P>) => List<Data<U>>): Parser<U, Context<P>, SubParsers<P>>;
8
8
  export function fmap<N, U>(parser: Parser<N>, f: (nodes: List<Data<N>>, context: Ctx) => List<Data<U>>): Parser<U> {
9
9
  return bind(parser, (nodes, context) => f(nodes, context));
10
10
  }
@@ -2,7 +2,7 @@ import { min } from 'spica/alias';
2
2
  import { Parser, Result, List, Data, Ctx, CtxOptions, Node, Context } from '../../data/parser';
3
3
  import { clone } from 'spica/assign';
4
4
 
5
- export function reset<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
5
+ export function reset<P extends Parser>(base: CtxOptions, parser: P): P;
6
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));
@@ -13,7 +13,7 @@ export function reset<N>(base: Ctx, parser: Parser<N>): Parser<N> {
13
13
  apply(parser, { ...context }, changes, values, true);
14
14
  }
15
15
 
16
- export function context<P extends Parser<unknown>>(base: CtxOptions, parser: P): P;
16
+ export function context<P extends Parser>(base: CtxOptions, parser: P): P;
17
17
  export function context<N>(base: Ctx, parser: Parser<N>): Parser<N> {
18
18
  assert(Object.getPrototypeOf(base) === Object.prototype);
19
19
  assert(Object.freeze(base));
@@ -23,7 +23,7 @@ export function context<N>(base: Ctx, parser: Parser<N>): Parser<N> {
23
23
  apply(parser, context, changes, values);
24
24
  }
25
25
 
26
- function apply<P extends Parser<unknown>>(parser: P, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Node<P>>;
26
+ function apply<P extends Parser>(parser: P, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Node<P>>;
27
27
  function apply<N>(parser: Parser<N>, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<N> {
28
28
  for (let i = 0; i < changes.length; ++i) {
29
29
  const change = changes[i];
@@ -64,8 +64,8 @@ function apply<N>(parser: Parser<N>, context: Ctx, changes: readonly [string, un
64
64
  return result;
65
65
  }
66
66
 
67
- export function creation<P extends Parser<unknown>>(cost: number, parser: P): P;
68
- export function creation(cost: number, parser: Parser<unknown>): Parser<unknown> {
67
+ export function creation<P extends Parser>(cost: number, parser: P): P;
68
+ export function creation(cost: number, parser: Parser): Parser {
69
69
  assert(cost >= 0);
70
70
  return input => {
71
71
  const { context } = input;
@@ -85,8 +85,8 @@ export function consume(cost: number, context: Ctx): void {
85
85
  resources.clock -= cost;
86
86
  }
87
87
 
88
- export function recursion<P extends Parser<unknown>>(recursion: number, parser: P): P;
89
- export function recursion(recursion: number, parser: Parser<unknown>): Parser<unknown> {
88
+ export function recursion<P extends Parser>(recursion: number, parser: P): P;
89
+ export function recursion(recursion: number, parser: Parser): Parser {
90
90
  assert(recursion >= 0);
91
91
  return input => {
92
92
  const { context } = input;
@@ -102,7 +102,7 @@ export function recursion(recursion: number, parser: Parser<unknown>): Parser<un
102
102
  };
103
103
  }
104
104
 
105
- export function precedence<P extends Parser<unknown>>(precedence: number, parser: P): P;
105
+ export function precedence<P extends Parser>(precedence: number, parser: P): P;
106
106
  export function precedence<N>(precedence: number, parser: Parser<N>): Parser<N> {
107
107
  assert(precedence >= 0);
108
108
  return input => {
@@ -119,8 +119,8 @@ export function precedence<N>(precedence: number, parser: Parser<N>): Parser<N>
119
119
  };
120
120
  }
121
121
 
122
- export function state<P extends Parser<unknown>>(state: number, parser: P): P;
123
- export function state<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
122
+ export function state<P extends Parser>(state: number, parser: P): P;
123
+ export function state<P extends Parser>(state: number, positive: boolean, parser: P): P;
124
124
  export function state<N>(state: number, positive: boolean | Parser<N>, parser?: Parser<N>): Parser<N> {
125
125
  if (typeof positive === 'function') {
126
126
  parser = positive;
@@ -140,8 +140,8 @@ export function state<N>(state: number, positive: boolean | Parser<N>, parser?:
140
140
  };
141
141
  }
142
142
 
143
- export function constraint<P extends Parser<unknown>>(state: number, parser: P): P;
144
- //export function constraint<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
143
+ export function constraint<P extends Parser>(state: number, parser: P): P;
144
+ //export function constraint<P extends Parser>(state: number, positive: boolean, parser: P): P;
145
145
  export function constraint<N>(state: number, positive: boolean | Parser<N>, parser?: Parser<N>): Parser<N> {
146
146
  if (typeof positive === 'function') {
147
147
  parser = positive;
@@ -1,12 +1,12 @@
1
1
  import { Parser, List, Data, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
2
2
 
3
- export function inits<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
3
+ export function inits<P extends Parser>(parsers: SubParsers<P>, resume?: (nodes: List<Data<SubNode<P>>>) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
4
4
  export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: List<Data<N>>) => boolean): Parser<N, Ctx, D> {
5
5
  assert(parsers.every(f => f));
6
6
  if (parsers.length === 1) return parsers[0];
7
7
  return input => {
8
8
  const { context } = input;
9
- const { source, position } = context;
9
+ const { source } = context;
10
10
  let nodes: List<Data<N>> | undefined;
11
11
  for (let len = parsers.length, i = 0; i < len; ++i) {
12
12
  if (context.position === source.length) break;
@@ -16,9 +16,6 @@ export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: Lis
16
16
  nodes = nodes?.import(result) ?? result;
17
17
  if (resume?.(result) === false) break;
18
18
  }
19
- assert(context.position >= position);
20
- return context.position > position
21
- ? nodes
22
- : undefined;
19
+ return nodes;
23
20
  };
24
21
  }