securemark 0.291.1 → 0.293.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 (177) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/design.md +13 -2
  3. package/dist/index.js +1353 -908
  4. package/markdown.d.ts +32 -49
  5. package/package.json +1 -1
  6. package/src/combinator/control/constraint/block.test.ts +8 -5
  7. package/src/combinator/control/constraint/block.ts +9 -9
  8. package/src/combinator/control/constraint/contract.ts +21 -38
  9. package/src/combinator/control/constraint/line.test.ts +9 -6
  10. package/src/combinator/control/constraint/line.ts +21 -22
  11. package/src/combinator/control/manipulation/clear.ts +3 -4
  12. package/src/combinator/control/manipulation/convert.ts +29 -13
  13. package/src/combinator/control/manipulation/fence.ts +20 -15
  14. package/src/combinator/control/manipulation/indent.test.ts +17 -14
  15. package/src/combinator/control/manipulation/indent.ts +14 -13
  16. package/src/combinator/control/manipulation/match.ts +13 -11
  17. package/src/combinator/control/manipulation/recovery.ts +7 -3
  18. package/src/combinator/control/manipulation/scope.ts +36 -44
  19. package/src/combinator/control/manipulation/surround.ts +69 -71
  20. package/src/combinator/control/manipulation/trim.test.ts +12 -9
  21. package/src/combinator/control/monad/bind.ts +16 -16
  22. package/src/combinator/control/monad/fmap.ts +6 -6
  23. package/src/combinator/data/parser/context/delimiter.ts +10 -8
  24. package/src/combinator/data/parser/context.test.ts +19 -14
  25. package/src/combinator/data/parser/context.ts +51 -16
  26. package/src/combinator/data/parser/inits.ts +13 -14
  27. package/src/combinator/data/parser/sequence.test.ts +16 -15
  28. package/src/combinator/data/parser/sequence.ts +13 -14
  29. package/src/combinator/data/parser/some.test.ts +20 -19
  30. package/src/combinator/data/parser/some.ts +14 -17
  31. package/src/combinator/data/parser/subsequence.test.ts +22 -21
  32. package/src/combinator/data/parser/subsequence.ts +3 -3
  33. package/src/combinator/data/parser/tails.ts +3 -3
  34. package/src/combinator/data/parser/union.test.ts +16 -15
  35. package/src/combinator/data/parser/union.ts +2 -2
  36. package/src/combinator/data/parser.ts +65 -28
  37. package/src/debug.test.ts +3 -3
  38. package/src/parser/api/bind.ts +3 -3
  39. package/src/parser/api/header.ts +7 -6
  40. package/src/parser/api/normalize.ts +2 -2
  41. package/src/parser/api/parse.test.ts +26 -19
  42. package/src/parser/api/parse.ts +4 -5
  43. package/src/parser/autolink.test.ts +19 -17
  44. package/src/parser/autolink.ts +2 -4
  45. package/src/parser/block/blockquote.test.ts +86 -84
  46. package/src/parser/block/blockquote.ts +10 -8
  47. package/src/parser/block/codeblock.test.ts +58 -56
  48. package/src/parser/block/codeblock.ts +4 -4
  49. package/src/parser/block/dlist.test.ts +58 -56
  50. package/src/parser/block/dlist.ts +6 -6
  51. package/src/parser/block/extension/aside.test.ts +10 -8
  52. package/src/parser/block/extension/aside.ts +2 -2
  53. package/src/parser/block/extension/example.test.ts +20 -18
  54. package/src/parser/block/extension/example.ts +4 -4
  55. package/src/parser/block/extension/fig.test.ts +38 -36
  56. package/src/parser/block/extension/fig.ts +4 -4
  57. package/src/parser/block/extension/figbase.test.ts +17 -15
  58. package/src/parser/block/extension/figbase.ts +1 -1
  59. package/src/parser/block/extension/figure.test.ts +64 -62
  60. package/src/parser/block/extension/figure.ts +7 -6
  61. package/src/parser/block/extension/message.test.ts +15 -13
  62. package/src/parser/block/extension/message.ts +4 -4
  63. package/src/parser/block/extension/placeholder.test.ts +3 -1
  64. package/src/parser/block/extension/placeholder.ts +1 -1
  65. package/src/parser/block/extension/table.test.ts +72 -70
  66. package/src/parser/block/extension/table.ts +19 -19
  67. package/src/parser/block/extension.test.ts +3 -1
  68. package/src/parser/block/extension.ts +2 -2
  69. package/src/parser/block/heading.test.ts +65 -64
  70. package/src/parser/block/heading.ts +5 -5
  71. package/src/parser/block/ilist.test.ts +6 -4
  72. package/src/parser/block/ilist.ts +7 -7
  73. package/src/parser/block/mathblock.test.ts +33 -31
  74. package/src/parser/block/mathblock.ts +2 -2
  75. package/src/parser/block/mediablock.ts +3 -3
  76. package/src/parser/block/olist.test.ts +101 -97
  77. package/src/parser/block/olist.ts +14 -14
  78. package/src/parser/block/pagebreak.test.ts +17 -15
  79. package/src/parser/block/pagebreak.ts +2 -2
  80. package/src/parser/block/paragraph.test.ts +60 -57
  81. package/src/parser/block/paragraph.ts +2 -2
  82. package/src/parser/block/reply/cite.test.ts +41 -39
  83. package/src/parser/block/reply/cite.ts +4 -4
  84. package/src/parser/block/reply/quote.test.ts +52 -50
  85. package/src/parser/block/reply/quote.ts +5 -4
  86. package/src/parser/block/reply.test.ts +21 -19
  87. package/src/parser/block/reply.ts +3 -3
  88. package/src/parser/block/sidefence.test.ts +51 -49
  89. package/src/parser/block/sidefence.ts +3 -3
  90. package/src/parser/block/table.test.ts +51 -50
  91. package/src/parser/block/table.ts +14 -14
  92. package/src/parser/block/ulist.test.ts +53 -50
  93. package/src/parser/block/ulist.ts +8 -8
  94. package/src/parser/block.ts +53 -19
  95. package/src/parser/context.ts +1 -0
  96. package/src/parser/header.test.ts +22 -21
  97. package/src/parser/header.ts +28 -16
  98. package/src/parser/inline/annotation.test.ts +44 -42
  99. package/src/parser/inline/annotation.ts +2 -2
  100. package/src/parser/inline/autolink/account.test.ts +33 -30
  101. package/src/parser/inline/autolink/account.ts +12 -9
  102. package/src/parser/inline/autolink/anchor.test.ts +23 -21
  103. package/src/parser/inline/autolink/anchor.ts +21 -17
  104. package/src/parser/inline/autolink/channel.test.ts +16 -14
  105. package/src/parser/inline/autolink/channel.ts +40 -15
  106. package/src/parser/inline/autolink/email.test.ts +38 -36
  107. package/src/parser/inline/autolink/email.ts +7 -9
  108. package/src/parser/inline/autolink/hashnum.test.ts +34 -37
  109. package/src/parser/inline/autolink/hashnum.ts +9 -5
  110. package/src/parser/inline/autolink/hashtag.test.ts +57 -56
  111. package/src/parser/inline/autolink/hashtag.ts +10 -7
  112. package/src/parser/inline/autolink/url.test.ts +75 -75
  113. package/src/parser/inline/autolink/url.ts +24 -28
  114. package/src/parser/inline/autolink.ts +21 -24
  115. package/src/parser/inline/bracket.test.ts +69 -67
  116. package/src/parser/inline/bracket.ts +35 -35
  117. package/src/parser/inline/code.test.ts +32 -29
  118. package/src/parser/inline/code.ts +20 -13
  119. package/src/parser/inline/deletion.test.ts +29 -27
  120. package/src/parser/inline/deletion.ts +3 -3
  121. package/src/parser/inline/emphasis.test.ts +40 -36
  122. package/src/parser/inline/emphasis.ts +4 -9
  123. package/src/parser/inline/emstrong.test.ts +102 -96
  124. package/src/parser/inline/emstrong.ts +93 -45
  125. package/src/parser/inline/extension/index.test.ts +93 -89
  126. package/src/parser/inline/extension/index.ts +16 -31
  127. package/src/parser/inline/extension/indexee.ts +1 -1
  128. package/src/parser/inline/extension/indexer.test.ts +26 -24
  129. package/src/parser/inline/extension/indexer.ts +3 -3
  130. package/src/parser/inline/extension/label.test.ts +34 -32
  131. package/src/parser/inline/extension/label.ts +1 -1
  132. package/src/parser/inline/extension/placeholder.test.ts +44 -42
  133. package/src/parser/inline/extension/placeholder.ts +12 -9
  134. package/src/parser/inline/html.test.ts +108 -106
  135. package/src/parser/inline/html.ts +38 -37
  136. package/src/parser/inline/htmlentity.test.ts +39 -37
  137. package/src/parser/inline/htmlentity.ts +9 -3
  138. package/src/parser/inline/insertion.test.ts +29 -27
  139. package/src/parser/inline/insertion.ts +3 -3
  140. package/src/parser/inline/italic.test.ts +55 -53
  141. package/src/parser/inline/italic.ts +3 -3
  142. package/src/parser/inline/link.test.ts +187 -185
  143. package/src/parser/inline/link.ts +45 -27
  144. package/src/parser/inline/mark.test.ts +31 -29
  145. package/src/parser/inline/mark.ts +4 -4
  146. package/src/parser/inline/math.test.ts +133 -131
  147. package/src/parser/inline/math.ts +9 -13
  148. package/src/parser/inline/media.test.ts +93 -91
  149. package/src/parser/inline/media.ts +50 -26
  150. package/src/parser/inline/reference.test.ts +110 -108
  151. package/src/parser/inline/reference.ts +50 -41
  152. package/src/parser/inline/remark.test.ts +53 -51
  153. package/src/parser/inline/remark.ts +5 -5
  154. package/src/parser/inline/ruby.test.ts +46 -44
  155. package/src/parser/inline/ruby.ts +26 -27
  156. package/src/parser/inline/shortmedia.test.ts +11 -9
  157. package/src/parser/inline/shortmedia.ts +6 -9
  158. package/src/parser/inline/strong.test.ts +37 -33
  159. package/src/parser/inline/strong.ts +7 -9
  160. package/src/parser/inline/template.test.ts +24 -22
  161. package/src/parser/inline/template.ts +20 -11
  162. package/src/parser/inline.test.ts +223 -220
  163. package/src/parser/inline.ts +13 -8
  164. package/src/parser/segment.ts +13 -8
  165. package/src/parser/source/escapable.test.ts +24 -22
  166. package/src/parser/source/escapable.ts +50 -41
  167. package/src/parser/source/line.test.ts +19 -17
  168. package/src/parser/source/line.ts +28 -4
  169. package/src/parser/source/str.ts +18 -23
  170. package/src/parser/source/text.test.ts +85 -83
  171. package/src/parser/source/text.ts +78 -54
  172. package/src/parser/source/unescapable.test.ts +24 -22
  173. package/src/parser/source/unescapable.ts +42 -33
  174. package/src/parser/source.ts +1 -1
  175. package/src/parser/util.ts +39 -36
  176. package/src/parser/visibility.ts +33 -24
  177. package/src/util/quote.ts +4 -2
package/markdown.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { Parser, Ctx } from './src/combinator/data/parser';
2
- import { Command } from './src/parser/context';
1
+ import { Parser, Ctx, CtxOptions } from './src/combinator/data/parser';
3
2
  import { Dict } from 'spica/dict';
4
3
 
5
4
  declare abstract class Markdown<T> {
@@ -13,7 +12,11 @@ export interface MarkdownParser extends
13
12
  ]> {
14
13
  }
15
14
  export namespace MarkdownParser {
16
- export interface Context extends Ctx {
15
+ export interface Context extends Ctx, Options {
16
+ buffer?: (string | HTMLElement)[];
17
+ sequential?: boolean;
18
+ }
19
+ export interface Options extends CtxOptions {
17
20
  readonly host?: URL;
18
21
  readonly url?: URL;
19
22
  readonly id?: string;
@@ -732,18 +735,10 @@ export namespace MarkdownParser {
732
735
  export interface SignatureParser extends
733
736
  Inline<'extension/index/signature'>,
734
737
  Parser<string | HTMLElement, Context, [
735
- InlineParser,
738
+ UnsafeHTMLEntityParser,
739
+ SourceParser.TxtParser,
736
740
  ]> {
737
741
  }
738
- export namespace SignatureParser {
739
- export interface InternalParser extends
740
- Inline<'extension/index/signature/internal'>,
741
- Parser<string, Context, [
742
- UnsafeHTMLEntityParser,
743
- SourceParser.TxtParser,
744
- ]> {
745
- }
746
- }
747
742
  }
748
743
  export interface IndexerParser extends
749
744
  // [|signature]
@@ -784,7 +779,7 @@ export namespace MarkdownParser {
784
779
  // { uri }
785
780
  // [abc]{uri nofollow}
786
781
  Inline<'link'>,
787
- Parser<HTMLAnchorElement, Context, [
782
+ Parser<HTMLAnchorElement | HTMLSpanElement, Context, [
788
783
  LinkParser.MediaLinkParser,
789
784
  LinkParser.TextLinkParser,
790
785
  ]> {
@@ -798,7 +793,7 @@ export namespace MarkdownParser {
798
793
  }
799
794
  export interface TextLinkParser extends
800
795
  Inline<'link/textlink'>,
801
- Parser<HTMLAnchorElement, Context, [
796
+ Parser<HTMLAnchorElement | HTMLSpanElement, Context, [
802
797
  Parser<(HTMLElement | string)[], Context, [
803
798
  InlineParser,
804
799
  ]>,
@@ -807,7 +802,7 @@ export namespace MarkdownParser {
807
802
  }
808
803
  export interface MediaLinkParser extends
809
804
  Inline<'link/medialink'>,
810
- Parser<HTMLAnchorElement, Context, [
805
+ Parser<HTMLAnchorElement | HTMLSpanElement, Context, [
811
806
  Parser<HTMLElement[], Context, [
812
807
  MediaParser,
813
808
  ShortMediaParser,
@@ -979,7 +974,7 @@ export namespace MarkdownParser {
979
974
  Inline<'insertion'>,
980
975
  Parser<HTMLElement | string, Context, [
981
976
  InlineParser,
982
- InsertionParser,
977
+ InlineParser,
983
978
  ]> {
984
979
  }
985
980
  export interface DeletionParser extends
@@ -987,7 +982,7 @@ export namespace MarkdownParser {
987
982
  Inline<'deletion'>,
988
983
  Parser<HTMLElement | string, Context, [
989
984
  InlineParser,
990
- DeletionParser,
985
+ InlineParser,
991
986
  ]> {
992
987
  }
993
988
  export interface MarkParser extends
@@ -995,7 +990,7 @@ export namespace MarkdownParser {
995
990
  Inline<'mark'>,
996
991
  Parser<HTMLElement | string, Context, [
997
992
  InlineParser,
998
- MarkParser,
993
+ InlineParser,
999
994
  ]> {
1000
995
  }
1001
996
  export interface EmStrongParser extends
@@ -1010,11 +1005,9 @@ export namespace MarkdownParser {
1010
1005
  // **abc**
1011
1006
  Inline<'strong'>,
1012
1007
  Parser<HTMLElement | string, Context, [
1008
+ EmphasisParser,
1009
+ InlineParser,
1013
1010
  InlineParser,
1014
- Parser<HTMLElement | string, Context, [
1015
- EmStrongParser,
1016
- StrongParser,
1017
- ]>,
1018
1011
  ]> {
1019
1012
  }
1020
1013
  export interface EmphasisParser extends
@@ -1023,11 +1016,7 @@ export namespace MarkdownParser {
1023
1016
  Parser<HTMLElement | string, Context, [
1024
1017
  StrongParser,
1025
1018
  InlineParser,
1026
- Parser<HTMLElement | string, Context, [
1027
- EmStrongParser,
1028
- StrongParser,
1029
- EmphasisParser,
1030
- ]>,
1019
+ InlineParser,
1031
1020
  ]> {
1032
1021
  }
1033
1022
  export interface ItalicParser extends
@@ -1035,7 +1024,7 @@ export namespace MarkdownParser {
1035
1024
  Inline<'italic'>,
1036
1025
  Parser<HTMLElement | string, Context, [
1037
1026
  InlineParser,
1038
- ItalicParser,
1027
+ InlineParser,
1039
1028
  ]> {
1040
1029
  }
1041
1030
  export interface MathParser extends
@@ -1045,8 +1034,8 @@ export namespace MarkdownParser {
1045
1034
  Parser<HTMLElement, Context, [
1046
1035
  MathParser.BracketParser,
1047
1036
  Parser<string, Context, [
1048
- MathParser.BracketParser,
1049
1037
  SourceParser.EscapableSourceParser,
1038
+ MathParser.BracketParser,
1050
1039
  ]>,
1051
1040
  ]> {
1052
1041
  }
@@ -1112,23 +1101,17 @@ export namespace MarkdownParser {
1112
1101
  export interface AutolinkParser extends
1113
1102
  Inline<'autolink'>,
1114
1103
  Parser<HTMLElement | string, Context, [
1115
- Parser<HTMLElement | string, Context, [
1116
- AutolinkParser.UrlParser.LineUrlParser,
1117
- ]>,
1118
- Parser<HTMLElement | string, Context, [
1119
- AutolinkParser.UrlParser,
1120
- AutolinkParser.EmailParser,
1121
- SourceParser.StrParser,
1122
- AutolinkParser.ChannelParser,
1123
- AutolinkParser.AccountParser,
1124
- SourceParser.StrParser,
1125
- SourceParser.StrParser,
1126
- AutolinkParser.HashtagParser,
1127
- AutolinkParser.HashnumParser,
1128
- SourceParser.StrParser,
1129
- SourceParser.StrParser,
1130
- AutolinkParser.AnchorParser,
1131
- ]>,
1104
+ AutolinkParser.UrlParser.LineUrlParser,
1105
+ AutolinkParser.UrlParser,
1106
+ AutolinkParser.EmailParser,
1107
+ SourceParser.StrParser,
1108
+ AutolinkParser.ChannelParser,
1109
+ AutolinkParser.AccountParser,
1110
+ SourceParser.StrParser,
1111
+ AutolinkParser.HashtagParser,
1112
+ AutolinkParser.HashnumParser,
1113
+ SourceParser.StrParser,
1114
+ AutolinkParser.AnchorParser,
1132
1115
  ]> {
1133
1116
  }
1134
1117
  export namespace AutolinkParser {
@@ -1182,8 +1165,8 @@ export namespace MarkdownParser {
1182
1165
  // @user#tag
1183
1166
  Inline<'channel'>,
1184
1167
  Parser<string | HTMLAnchorElement, Context, [
1185
- InlineParser.AutolinkParser.AccountParser,
1186
- InlineParser.AutolinkParser.HashtagParser,
1168
+ LinkParser.UnsafeLinkParser,
1169
+ Parser<string, Context, []>,
1187
1170
  ]> {
1188
1171
  }
1189
1172
  export interface AccountParser extends
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.291.1",
3
+ "version": "0.293.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,17 +1,20 @@
1
1
  import { block } from './block';
2
+ import { input } from '../../data/parser';
2
3
  import { inspect } from '../../../debug.test';
3
4
 
4
5
  describe('Unit: combinator/block', () => {
5
6
  describe('block', () => {
7
+ const { context: ctx } = input('', {});
8
+
6
9
  it('invalid', () => {
7
- assert.throws(() => block(_ => [[], '\n'])({ source: ' \n', context: {} }));
10
+ assert.throws(() => block(_ => [[]])(input(' \n', ctx)));
8
11
  });
9
12
 
10
13
  it('valid', () => {
11
- assert.deepStrictEqual(inspect(block(_ => [[], ''])({ source: '\n', context: {} })), [[], '']);
12
- assert.deepStrictEqual(inspect(block(_ => [[], ''])({ source: ' \n', context: {} })), [[], '']);
13
- assert.deepStrictEqual(inspect(block(_ => [[], ''])({ source: '\n\n', context: {} })), [[], '']);
14
- assert.deepStrictEqual(inspect(block(_ => [[], '\n'])({ source: '\n\n', context: {} })), [[], '\n']);
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']);
15
18
  });
16
19
 
17
20
  });
@@ -1,19 +1,19 @@
1
- import { Parser, exec } from '../../data/parser';
1
+ import { Parser, failsafe } from '../../data/parser';
2
2
  import { firstline, isBlank } from './line';
3
3
 
4
4
  export function block<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
5
5
  export function block<N>(parser: Parser<N>, separation = true): Parser<N> {
6
6
  assert(parser);
7
- return input => {
8
- const { source } = input;
9
- if (source === '') return;
7
+ return failsafe(input => {
8
+ const { context } = input;
9
+ const { source, position } = context;
10
+ if (position === source.length) return;
10
11
  const result = parser(input);
11
12
  if (result === undefined) return;
12
- const rest = exec(result);
13
- if (separation && !isBlank(firstline(rest))) return;
14
- assert(rest === '' || source[source.length - rest.length - 1] === '\n');
15
- return rest === '' || source[source.length - rest.length - 1] === '\n'
13
+ if (separation && !isBlank(firstline(source, context.position))) return;
14
+ assert(context.position === source.length || source[context.position - 1] === '\n');
15
+ return context.position === source.length || source[context.position - 1] === '\n'
16
16
  ? result
17
17
  : undefined;
18
- };
18
+ });
19
19
  }
@@ -1,36 +1,22 @@
1
- import { isArray } from 'spica/alias';
2
- import { Parser, Input, Ctx, Node, Context, eval, exec, check } from '../../data/parser';
1
+ import { Parser, Input, Ctx, Node, Context, eval, failsafe } from '../../data/parser';
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;
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>>(patterns: string | RegExp | (string | RegExp)[], parser: P): P;
9
+ export function validate<P extends Parser<unknown>>(pattern: string | RegExp, parser: P): P;
10
10
  export function validate<P extends Parser<unknown>>(cond: ((input: Input<Context<P>>) => boolean), parser: P): P;
11
- export function validate<N>(patterns: string | RegExp | (string | RegExp)[] | ((input: Input<Ctx>) => boolean), parser: Parser<N>): Parser<N> {
12
- if (typeof patterns === 'function') return guard(patterns, parser);
13
- if (!isArray(patterns)) return validate([patterns], parser);
14
- assert(patterns.length > 0);
15
- assert(patterns.every(pattern => pattern instanceof RegExp ? !pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^') : true));
16
- const match: (source: string) => boolean = global.eval([
17
- 'source =>',
18
- patterns.map(pattern =>
19
- typeof pattern === 'string'
20
- ? `|| source.slice(0, ${pattern.length}) === '${pattern}'`
21
- : `|| /${pattern.source}/${pattern.flags}.test(source)`).join('').slice(2),
22
- ].join(''));
11
+ export function validate<N>(pattern: string | RegExp | ((input: Input<Ctx>) => boolean), parser: Parser<N>): Parser<N> {
12
+ if (typeof pattern === 'function') return guard(pattern, parser);
13
+ const match = matcher(pattern, false);
23
14
  return input => {
24
- const { source } = input;
25
- if (source === '') return;
26
- if (!match(source)) return;
27
- const result = parser(input);
28
- assert(check(source, result));
29
- if (result === undefined) return;
30
- assert(exec(result).length < source.length);
31
- return exec(result).length < source.length
32
- ? result
33
- : undefined;
15
+ const { context } = input;
16
+ const { source, position } = context;
17
+ if (position === source.length) return;
18
+ if (!match(input)) return;
19
+ return parser(input);
34
20
  };
35
21
  }
36
22
 
@@ -42,19 +28,16 @@ function guard<N>(f: (input: Input<Ctx>) => boolean, parser: Parser<N>): Parser<
42
28
  : undefined;
43
29
  }
44
30
 
45
- export function verify<P extends Parser<unknown>>(parser: P, cond: (nodes: readonly Node<P>[], rest: string, context: Context<P>) => boolean): P;
46
- export function verify<N>(parser: Parser<N>, cond: (nodes: readonly N[], rest: string, context: Ctx) => boolean): Parser<N> {
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> {
47
33
  assert(parser);
48
- return input => {
49
- const { source, context } = input;
50
- if (source === '') return;
34
+ return failsafe(input => {
35
+ const { context } = input;
36
+ const { source, position } = context;
37
+ if (position === source.length) return;
51
38
  const result = parser(input);
52
- assert(check(source, result));
53
- if (result === undefined) return;
54
- if (!cond(eval(result), exec(result), context)) return;
55
- assert(exec(result).length < source.length);
56
- return exec(result).length < source.length
57
- ? result
58
- : undefined;
59
- };
39
+ assert(context.position > position || !result);
40
+ if (result && !cond(eval(result), context)) return;
41
+ return result;
42
+ });
60
43
  }
@@ -1,18 +1,21 @@
1
+ import { input } from '../../data/parser';
1
2
  import { line } from './line';
2
3
  import { inspect } from '../../../debug.test';
3
4
 
4
5
  describe('Unit: combinator/line', () => {
5
6
  describe('line', () => {
7
+ const { context: ctx } = input('', {});
8
+
6
9
  it('invalid', () => {
7
- assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: '', context: {} })), undefined);
10
+ assert.deepStrictEqual(inspect(line(_ => [[]])(input('', ctx)), ctx), undefined);
8
11
  });
9
12
 
10
13
  it('valid', () => {
11
- assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: ' ', context: {} })), [[], '']);
12
- assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: '\n', context: {} })), [[], '']);
13
- assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: '\n\n', context: {} })), [[], '\n']);
14
- assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: ' \n', context: {} })), [[], '']);
15
- assert.deepStrictEqual(inspect(line(_ => [[], '\n'])({ source: ' \n', context: {} })), [[], '']);
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), [[], '']);
16
19
  });
17
20
 
18
21
  });
@@ -1,33 +1,32 @@
1
- import { Parser, eval, exec, check } from '../../data/parser';
1
+ import { Parser, input, eval, failsafe } from '../../data/parser';
2
2
 
3
3
  export function line<P extends Parser<unknown>>(parser: P): P;
4
4
  export function line<N>(parser: Parser<N>): Parser<N> {
5
5
  assert(parser);
6
- return ({ source, context }) => {
7
- if (source === '') return;
8
- const line = firstline(source);
6
+ return failsafe(({ context }) => {
7
+ const { source, position } = context;
8
+ if (position === source.length) return;
9
+ const line = firstline(source, position);
9
10
  context.offset ??= 0;
10
- context.offset += source.length - line.length;
11
- const result = parser({ source: line, context });
12
- assert(check(line, result));
13
- context.offset -= source.length - line.length;
11
+ context.offset += position;
12
+ const result = parser(input(line, context));
13
+ context.position += position;
14
+ context.position += result && context.position === position ? line.length : 0;
15
+ assert(context.position > position || !result);
16
+ context.source = source;
17
+ context.offset -= position;
14
18
  if (result === undefined) return;
15
- return isBlank(exec(result))
16
- ? [eval(result), source.slice(line.length)]
17
- : undefined;
18
- };
19
+ if (!isBlank(source.slice(context.position, position + line.length))) return;
20
+ context.position = position + line.length;
21
+ return [eval(result)];
22
+ });
19
23
  }
20
24
 
21
- export function firstline(source: string): string {
22
- const i = source.indexOf('\n');
23
- switch (i) {
24
- case -1:
25
- return source;
26
- case 0:
27
- return '\n';
28
- default:
29
- return source.slice(0, i + 1);
30
- }
25
+ export function firstline(source: string, position: number): string {
26
+ const i = source.indexOf('\n', position);
27
+ return i === -1
28
+ ? source.slice(position)
29
+ : source.slice(position, i + 1);
31
30
  }
32
31
 
33
32
  export function isBlank(line: string): boolean {
@@ -1,7 +1,6 @@
1
- import { Parser, Ctx } from '../../data/parser';
2
- import { fmap } from '../monad/fmap';
1
+ import { Parser, CtxOptions } from '../../data/parser';
3
2
 
4
3
 
5
- export function clear<D extends Parser<unknown, C>[], C extends Ctx>(parser: Parser<unknown, C, D>): Parser<never, C, D> {
6
- return fmap<never, Parser<unknown, C, D>>(parser, () => []);
4
+ 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) && [[]];
7
6
  }
@@ -1,18 +1,34 @@
1
- import { Parser, Ctx, Context, check } from '../../data/parser';
1
+ import { Parser, 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> {
5
5
  assert(parser);
6
- return ({ source, context }) => {
7
- if (source === '') return;
8
- const src = conv(source, context);
9
- if (src === '') return empty ? [[], ''] : undefined;
10
- const { backtracks } = context;
11
- assert(source.endsWith(src) || src.endsWith(source) || !continuous);
12
- context.backtracks = continuous ? backtracks : {};
13
- const result = parser({ source: src, context });
14
- assert(check(src, result));
15
- context.backtracks = backtracks;
16
- return result;
17
- };
6
+ return failsafe(input => {
7
+ const { context } = input;
8
+ const { source, position } = context;
9
+ if (position === source.length) return;
10
+ const src = conv(source.slice(position), context);
11
+ if (src === '') {
12
+ if (!empty) return;
13
+ context.position = source.length;
14
+ return [[]];
15
+ }
16
+ assert(source.endsWith(src) || src.endsWith(source, position) || !continuous);
17
+ if (continuous) {
18
+ context.position += source.length - position - src.length;
19
+ const result = parser(input);
20
+ assert(context.position > position || !result);
21
+ context.source = source;
22
+ return result;
23
+ }
24
+ else {
25
+ const { offset, backtracks } = context;
26
+ const result = parser(subinput(src, context));
27
+ context.position = context.source.length
28
+ assert(context.offset === offset);
29
+ assert(context.source === source);
30
+ assert(context.backtracks === backtracks);
31
+ return result;
32
+ }
33
+ });
18
34
  }
@@ -1,37 +1,42 @@
1
- import { Parser, Ctx } from '../../data/parser';
1
+ import { Parser, Ctx, failsafe } from '../../data/parser';
2
2
  import { firstline, isBlank } from '../constraint/line';
3
3
  import { push } from 'spica/array';
4
4
 
5
5
  export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: RegExp, limit: number, separation = true): Parser<string, C, D> {
6
- return ({ source }) => {
7
- if (source === '') return;
8
- const matches = source.match(opener);
6
+ assert(!opener.flags.match(/[gm]/) && opener.sticky && !opener.source.startsWith('^'));
7
+ return failsafe(input => {
8
+ const { context } = input;
9
+ const { source, position } = context;
10
+ if (position === source.length) return;
11
+ opener.lastIndex = position;
12
+ const matches = opener.exec(source);
9
13
  if (!matches) return;
10
- assert(matches[0] === firstline(source));
14
+ assert(matches[0] === firstline(source, position));
11
15
  const delim = matches[1];
12
16
  assert(delim && delim === delim.trim());
13
17
  if (matches[0].includes(delim, delim.length)) return;
14
- let rest = source.slice(matches[0].length);
18
+ context.position += matches[0].length;
15
19
  // Prevent annoying parsing in editing.
16
- if (isBlank(firstline(rest)) && firstline(rest.slice(firstline(rest).length)).trimEnd() !== delim) return;
20
+ const secondline = firstline(source, context.position);
21
+ if (isBlank(secondline) && firstline(source, context.position + secondline.length).trimEnd() !== delim) return;
17
22
  let block = '';
18
23
  let closer = '';
19
24
  let overflow = '';
20
25
  for (let count = 1; ; ++count) {
21
- if (rest === '') break;
22
- const line = firstline(rest);
26
+ if (context.position === source.length) break;
27
+ const line = firstline(source, context.position);
23
28
  if ((closer || count > limit + 1) && isBlank(line)) break;
24
29
  if(closer) {
25
30
  overflow += line;
26
31
  }
27
32
  if (!closer && count <= limit + 1 && line.slice(0, delim.length) === delim && line.trimEnd() === delim) {
28
33
  closer = line;
29
- if (isBlank(firstline(rest.slice(line.length)))) {
30
- rest = rest.slice(line.length);
34
+ if (isBlank(firstline(source, context.position + line.length))) {
35
+ context.position += line.length;
31
36
  break;
32
37
  }
33
38
  if (!separation) {
34
- rest = rest.slice(line.length);
39
+ context.position += line.length;
35
40
  break;
36
41
  }
37
42
  assert(!overflow);
@@ -40,8 +45,8 @@ export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: Reg
40
45
  if (!overflow) {
41
46
  block += line;
42
47
  }
43
- rest = rest.slice(line.length);
48
+ context.position += line.length;
44
49
  }
45
- return [push([block, overflow, closer], matches), rest];
46
- };
50
+ return [push([block, overflow, closer], matches)];
51
+ });
47
52
  }
@@ -1,23 +1,26 @@
1
1
  import { indent } from './indent';
2
+ import { input } from '../../data/parser';
2
3
  import { inspect } from '../../../debug.test';
3
4
 
4
5
  describe('Unit: combinator/indent', () => {
5
6
  describe('indent', () => {
7
+ const { context: ctx } = input('', {});
8
+
6
9
  it('valid', () => {
7
- const parser = indent(({ source }) => [[source], '']);
8
- assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
9
- assert.deepStrictEqual(inspect(parser({ source: ' ', context: {} })), undefined);
10
- assert.deepStrictEqual(inspect(parser({ source: ' ', context: {} })), undefined);
11
- assert.deepStrictEqual(inspect(parser({ source: 'a ', context: {} })), undefined);
12
- assert.deepStrictEqual(inspect(parser({ source: ' a\n', context: {} })), [['a'], '']);
13
- assert.deepStrictEqual(inspect(parser({ source: ' a ', context: {} })), [['a '], '']);
14
- assert.deepStrictEqual(inspect(parser({ source: ' a \n', context: {} })), [['a '], '']);
15
- assert.deepStrictEqual(inspect(parser({ source: ' a', context: {} })), [['a'], '']);
16
- assert.deepStrictEqual(inspect(parser({ source: ' a\n a', context: {} })), [['a\na'], '']);
17
- assert.deepStrictEqual(inspect(parser({ source: ' a\n a', context: {} })), [['a\n a'], '']);
18
- assert.deepStrictEqual(inspect(parser({ source: ' a\n a', context: {} })), [['a'], ' a']);
19
- assert.deepStrictEqual(inspect(parser({ source: ' \ta', context: {} })), [['\ta'], '']);
20
- assert.deepStrictEqual(inspect(parser({ source: '\ta', context: {} })), [['a'], '']);
10
+ const parser = indent(({ context }) => { context.position = context.source.length; return [[context.source]]; });
11
+ assert.deepStrictEqual(inspect(parser(input('', ctx)), ctx), undefined);
12
+ assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
13
+ assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
14
+ assert.deepStrictEqual(inspect(parser(input('a ', ctx)), ctx), undefined);
15
+ assert.deepStrictEqual(inspect(parser(input(' a\n', ctx)), ctx), [['a'], '']);
16
+ assert.deepStrictEqual(inspect(parser(input(' a ', ctx)), ctx), [['a '], '']);
17
+ assert.deepStrictEqual(inspect(parser(input(' a \n', ctx)), ctx), [['a '], '']);
18
+ assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
19
+ assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\na'], '']);
20
+ assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\n a'], '']);
21
+ assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a'], ' a']);
22
+ assert.deepStrictEqual(inspect(parser(input(' \ta', ctx)), ctx), [['\ta'], '']);
23
+ assert.deepStrictEqual(inspect(parser(input('\ta', ctx)), ctx), [['a'], '']);
21
24
  });
22
25
 
23
26
  });
@@ -1,4 +1,4 @@
1
- import { Parser, eval, exec } from '../../data/parser';
1
+ import { Parser, 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';
@@ -13,27 +13,28 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
13
13
  if (typeof opener === 'function') {
14
14
  separation = parser as boolean;
15
15
  parser = opener;
16
- opener = /^([ \t])\1*/;
16
+ opener = /([ \t])\1*/y;
17
17
  }
18
+ assert(!opener.flags.match(/[gm]/) && opener.sticky && !opener.source.startsWith('^'));
18
19
  assert(parser);
19
- return bind(block(match(
20
+ return failsafe(bind(block(match(
20
21
  opener,
21
22
  memoize(
22
23
  ([indent]) =>
23
- some(line(open(indent, ({ source }) => [[source], '']))),
24
+ some(line(open(indent, ({ context }) => {
25
+ const { source, position } = context;
26
+ context.position = source.length;
27
+ return [[source.slice(position)]];
28
+ }))),
24
29
  ([indent]) => indent.length * 2 + +(indent[0] === ' '), {})), separation),
25
- (lines, rest, context) => {
30
+ (lines, context) => {
26
31
  assert(parser = parser as Parser<N>);
27
- // 影響する使用はないはず
28
- //const { backtracks } = context;
29
- //context.backtracks = {};
30
- const result = parser({ source: trimBlockEnd(lines.join('')), context });
31
- //context.backtracks = backtracks;
32
+ const result = parser(subinput(trimBlockEnd(lines.join('')), context));
32
33
  assert(result);
33
- return result && exec(result) === ''
34
- ? [eval(result), rest]
34
+ return result
35
+ ? [eval(result)]
35
36
  : undefined;
36
- });
37
+ }));
37
38
  }
38
39
 
39
40
  function trimBlockEnd(block: string): string {