securemark 0.296.1 → 0.296.3

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.
@@ -1,50 +1,64 @@
1
1
  import { Parser, Result, List, Node, Context, failsafe } from '../../data/parser';
2
- import { matcher, clear } from '../../../combinator';
2
+ import { tester } from '../../data/delimiter';
3
3
 
4
4
  export function surround<P extends Parser, S = string>(
5
- opener: string | RegExp | Parser<S, Parser.Context<P>>, parser: Parser.IntermediateParser<P>, closer: string | RegExp | Parser<S, Parser.Context<P>>,
5
+ opener: string | RegExp | Parser<S, Parser.Context<P>>,
6
+ parser: Parser.IntermediateParser<P>,
7
+ closer: string | RegExp | Parser<S, Parser.Context<P>>,
6
8
  optional?: false,
7
9
  backtracks?: readonly number[],
8
10
  f?: (rss: [List<Node<S>>, List<Node<Parser.SubNode<P>>>, List<Node<S>>], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
9
11
  g?: (rss: [List<Node<S>>, List<Node<Parser.SubNode<P>>> | undefined], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
10
12
  ): P;
11
13
  export function surround<P extends Parser, S = string>(
12
- opener: string | RegExp | Parser<S, Parser.Context<P>>, parser: Parser.IntermediateParser<P>, closer: string | RegExp | Parser<S, Parser.Context<P>>,
14
+ opener: string | RegExp | Parser<S, Parser.Context<P>>,
15
+ parser: Parser.IntermediateParser<P>,
16
+ closer: string | RegExp | Parser<S, Parser.Context<P>>,
13
17
  optional?: boolean,
14
18
  backtracks?: readonly number[],
15
19
  f?: (rss: [List<Node<S>>, List<Node<Parser.SubNode<P>>> | undefined, List<Node<S>>], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
16
20
  g?: (rss: [List<Node<S>>, List<Node<Parser.SubNode<P>>> | undefined], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
17
21
  ): P;
18
22
  export function surround<P extends Parser, S = string>(
19
- opener: string | RegExp | Parser<S, Parser.Context<P>>, parser: P, closer: string | RegExp | Parser<S, Parser.Context<P>>,
23
+ opener: string | RegExp | Parser<S, Parser.Context<P>>,
24
+ parser: P,
25
+ closer: string | RegExp | Parser<S, Parser.Context<P>>,
20
26
  optional?: false,
21
27
  backtracks?: readonly number[],
22
28
  f?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>>, List<Node<S>>], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
23
29
  g?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>> | undefined], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
24
30
  ): P;
25
31
  export function surround<P extends Parser, S = string>(
26
- opener: string | RegExp | Parser<S, Parser.Context<P>>, parser: P, closer: string | RegExp | Parser<S, Parser.Context<P>>,
32
+ opener: string | RegExp | Parser<S, Parser.Context<P>>,
33
+ parser: P,
34
+ closer: string | RegExp | Parser<S, Parser.Context<P>>,
27
35
  optional?: boolean,
28
36
  backtracks?: readonly number[],
29
37
  f?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>> | undefined, List<Node<S>>], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
30
38
  g?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>> | undefined], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
31
39
  ): P;
32
40
  export function surround<P extends Parser<string>, S = string>(
33
- opener: string | RegExp | Parser<S, Parser.Context<P>>, parser: string | RegExp | P, closer: string | RegExp | Parser<S, Parser.Context<P>>,
41
+ opener: string | RegExp | Parser<S, Parser.Context<P>>,
42
+ parser: string | RegExp | P,
43
+ closer: string | RegExp | Parser<S, Parser.Context<P>>,
34
44
  optional?: false,
35
45
  backtracks?: readonly number[],
36
46
  f?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>>, List<Node<S>>], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
37
47
  g?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>> | undefined], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
38
48
  ): P;
39
49
  export function surround<P extends Parser<string>, S = string>(
40
- opener: string | RegExp | Parser<S, Parser.Context<P>>, parser: string | RegExp | P, closer: string | RegExp | Parser<S, Parser.Context<P>>,
50
+ opener: string | RegExp | Parser<S, Parser.Context<P>>,
51
+ parser: string | RegExp | P,
52
+ closer: string | RegExp | Parser<S, Parser.Context<P>>,
41
53
  optional?: boolean,
42
54
  backtracks?: readonly number[],
43
55
  f?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>> | undefined, List<Node<S>>], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
44
56
  g?: (rss: [List<Node<S>>, List<Node<Parser.Node<P>>> | undefined], context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>,
45
57
  ): P;
46
58
  export function surround<N>(
47
- opener: string | RegExp | Parser<N>, parser: string | RegExp | Parser<N>, closer: string | RegExp | Parser<N>,
59
+ opener: string | RegExp | Parser<N>,
60
+ parser: string | RegExp | Parser<N>,
61
+ closer: string | RegExp | Parser<N>,
48
62
  optional: boolean = false,
49
63
  backtracks: readonly number[] = [],
50
64
  f?: (rss: [List<Node<N>>, List<Node<N>>, List<Node<N>>], context: Context) => Result<N>,
@@ -53,19 +67,19 @@ export function surround<N>(
53
67
  switch (typeof opener) {
54
68
  case 'string':
55
69
  case 'object':
56
- opener = clear(matcher(opener, true));
70
+ opener = tester(opener, true);
57
71
  }
58
72
  assert(opener);
59
73
  switch (typeof parser) {
60
74
  case 'string':
61
75
  case 'object':
62
- parser = clear(matcher(parser, true));
76
+ parser = tester(parser, true);
63
77
  }
64
78
  assert(parser);
65
79
  switch (typeof closer) {
66
80
  case 'string':
67
81
  case 'object':
68
- closer = clear(matcher(closer, true));
82
+ closer = tester(closer, true);
69
83
  }
70
84
  assert(closer);
71
85
  const [blen, rbs, wbs] = reduce(backtracks);
@@ -76,7 +90,7 @@ export function surround<N>(
76
90
  const { linebreak } = context;
77
91
  context.linebreak = 0;
78
92
  const nodesO = opener(input);
79
- if (!nodesO) {
93
+ if (nodesO === undefined) {
80
94
  return void revert(context, linebreak);
81
95
  }
82
96
  if (rbs && isBacktrack(context, rbs, position, blen)) {
@@ -84,14 +98,14 @@ export function surround<N>(
84
98
  }
85
99
  const nodesM = context.position < source.length ? parser(input) : undefined;
86
100
  context.range = context.position - position;
87
- if (!nodesM && !optional) {
101
+ if (nodesM === undefined && !optional) {
88
102
  wbs && setBacktrack(context, wbs, position);
89
103
  const result = g?.([nodesO, nodesM], context);
90
104
  return result || void revert(context, linebreak);
91
105
  }
92
- const nodesC = nodesM || optional ? closer(input) : undefined;
106
+ const nodesC = optional || nodesM ? closer(input) : undefined;
93
107
  context.range = context.position - position;
94
- if (!nodesC) {
108
+ if (nodesC === undefined) {
95
109
  wbs && setBacktrack(context, wbs, position);
96
110
  const result = g?.([nodesO, nodesM], context);
97
111
  return result || void revert(context, linebreak);
@@ -102,7 +116,9 @@ export function surround<N>(
102
116
  context.range = context.position - position;
103
117
  const result = f
104
118
  ? f([nodesO, nodesM!, nodesC], context)
105
- : nodesO.import(nodesM ?? new List()).import(nodesC);
119
+ : nodesM
120
+ ? nodesO.import(nodesM).import(nodesC)
121
+ : nodesO.import(nodesC);
106
122
  if (result) {
107
123
  context.linebreak ||= linebreak;
108
124
  }
@@ -1,5 +1,5 @@
1
- import { Input, Context } from './parser';
2
- import { matcher } from '../../combinator';
1
+ import { Parser, Input, List, Node, Context } from './parser';
2
+ import { consume } from './parser/context';
3
3
 
4
4
  interface Delimiter {
5
5
  readonly memory: Delimiter[];
@@ -33,11 +33,11 @@ export class Delimiters {
33
33
  return () => undefined;
34
34
  case 'string':
35
35
  case 'object':
36
- const match = matcher(pattern, false);
37
- const verify = after ? matcher(after, false) : undefined;
36
+ const test = tester(pattern, false);
37
+ const verify = after ? tester(after, false) : undefined;
38
38
  return verify
39
- ? input => match(input) !== undefined && verify(input) !== undefined || undefined
40
- : input => match(input) !== undefined || undefined;
39
+ ? input => test(input) !== undefined && verify(input) !== undefined || undefined
40
+ : input => test(input) !== undefined || undefined;
41
41
  }
42
42
  }
43
43
  private readonly tree: Record<number, Delimiter[]> = {};
@@ -150,3 +150,79 @@ export class Delimiters {
150
150
  return false;
151
151
  }
152
152
  }
153
+
154
+ export function matcher(pattern: string | RegExp, advance: boolean, after?: Parser<string>): Parser<string> {
155
+ assert(pattern instanceof RegExp ? !pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^') : true);
156
+ const count = typeof pattern === 'object'
157
+ ? /[^^\\*+][*+]/.test(pattern.source)
158
+ : false;
159
+ switch (typeof pattern) {
160
+ case 'string':
161
+ if (pattern === '') return () => new List([new Node(pattern)]);
162
+ return input => {
163
+ const { context } = input;
164
+ const { source, position } = context;
165
+ if (!source.startsWith(pattern, position)) return;
166
+ if (advance) {
167
+ context.position += pattern.length;
168
+ }
169
+ const next = after?.(input);
170
+ return after
171
+ ? next && new List([new Node(pattern)]).import(next)
172
+ : new List([new Node(pattern)]);
173
+ };
174
+ case 'object':
175
+ assert(pattern.sticky);
176
+ return input => {
177
+ const { context } = input;
178
+ const { source, position } = context;
179
+ pattern.lastIndex = position;
180
+ if (!pattern.test(source)) return;
181
+ const src = source.slice(position, pattern.lastIndex);
182
+ count && consume(src.length, context);
183
+ if (advance) {
184
+ context.position += src.length;
185
+ }
186
+ const next = after?.(input);
187
+ return after
188
+ ? next && new List([new Node(src)]).import(next)
189
+ : new List([new Node(src)]);
190
+ };
191
+ }
192
+ }
193
+
194
+ export function tester(pattern: string | RegExp, advance: boolean, after?: Parser<unknown>): Parser<never> {
195
+ assert(pattern instanceof RegExp ? !pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^') : true);
196
+ const count = typeof pattern === 'object'
197
+ ? /[^^\\*+][*+]/.test(pattern.source)
198
+ : false;
199
+ switch (typeof pattern) {
200
+ case 'string':
201
+ if (pattern === '') return () => new List();
202
+ return input => {
203
+ const { context } = input;
204
+ const { source, position } = context;
205
+ if (!source.startsWith(pattern, position)) return;
206
+ if (advance) {
207
+ context.position += pattern.length;
208
+ }
209
+ if (after && after(input) === undefined) return;
210
+ return new List();
211
+ };
212
+ case 'object':
213
+ assert(pattern.sticky);
214
+ return input => {
215
+ const { context } = input;
216
+ const { source, position } = context;
217
+ pattern.lastIndex = position;
218
+ if (!pattern.test(source)) return;
219
+ const len = pattern.lastIndex - position;
220
+ count && consume(len, context);
221
+ if (advance) {
222
+ context.position += len;
223
+ }
224
+ if (after && after(input) === undefined) return;
225
+ return new List();
226
+ };
227
+ }
228
+ }
@@ -21,9 +21,9 @@ describe('Unit: combinator/data/parser/context', () => {
21
21
  const ctx: Ctx = new Context();
22
22
  assert.deepStrictEqual([...unwrap(reset(base, parser)(input('123', ctx))!)], [3, 2, 1]);
23
23
  assert(base.resources?.clock === 3);
24
- assert(ctx.resources?.clock === undefined);
24
+ assert(ctx.resources?.clock === 0);
25
25
  assert.throws(() => reset(base, parser)(input('1234', ctx)));
26
- assert(ctx.resources?.clock === undefined);
26
+ assert(ctx.resources?.clock === 0);
27
27
  });
28
28
 
29
29
  it('node', () => {
@@ -1,16 +1,25 @@
1
- import { Parser, Result, List, Node, Context, Options } from '../../data/parser';
1
+ import { Parser, Result, Context, Options } from '../../data/parser';
2
2
  import { min } from 'spica/alias';
3
3
  import { clone } from 'spica/assign';
4
4
 
5
5
  export function reset<P extends Parser>(base: Options, parser: P): P;
6
6
  export function reset<N>(base: Context, parser: Parser<N>): Parser<N> {
7
+ return input => {
8
+ const { context } = input;
9
+ // @ts-expect-error
10
+ context.resources ??= {
11
+ clock: base.resources?.clock,
12
+ recursions: base.resources?.recursions.slice(),
13
+ };
14
+ context.backtracks = {};
15
+ return parser(input);
16
+ };
7
17
  assert(Object.getPrototypeOf(base) === Object.prototype);
8
18
  assert(Object.freeze(base));
9
19
  const changes = Object.entries(base);
10
20
  const values = Array(changes.length);
11
21
  return ({ context }) =>
12
- // 大域離脱時の汚染回避のため複製
13
- apply(parser, { ...context }, changes, values, true);
22
+ apply(parser, context, changes, values, true);
14
23
  }
15
24
 
16
25
  export function context<P extends Parser>(base: Options, parser: P): P;
@@ -158,36 +167,3 @@ export function constraint<N>(state: number, positive: boolean | Parser<N>, pars
158
167
  : undefined;
159
168
  };
160
169
  }
161
-
162
- export function matcher(pattern: string | RegExp, advance: boolean, verify?: (source: string, position: number, range: number) => boolean): Parser<string> {
163
- assert(pattern instanceof RegExp ? !pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^') : true);
164
- const count = typeof pattern === 'object'
165
- ? /[^^\\*+][*+]/.test(pattern.source)
166
- : false;
167
- switch (typeof pattern) {
168
- case 'string':
169
- return ({ context }) => {
170
- const { source, position } = context;
171
- if (!source.startsWith(pattern, position)) return;
172
- if (verify?.(source, position, pattern.length) === false) return;
173
- if (advance) {
174
- context.position += pattern.length;
175
- }
176
- return new List([new Node(pattern)]);
177
- };
178
- case 'object':
179
- assert(pattern.sticky);
180
- return ({ context }) => {
181
- const { source, position } = context;
182
- pattern.lastIndex = position;
183
- if (!pattern.test(source)) return;
184
- const src = source.slice(position, pattern.lastIndex);
185
- count && consume(src.length, context);
186
- if (verify?.(source, position, src.length) === false) return;
187
- if (advance) {
188
- context.position += src.length;
189
- }
190
- return new List([new Node(src)]);
191
- };
192
- }
193
- }
@@ -8,7 +8,7 @@ import { headers } from './header';
8
8
  import { figure } from '../processor/figure';
9
9
  import { note } from '../processor/note';
10
10
  import { ReadonlyURL } from 'spica/url';
11
- import { push, splice } from 'spica/array';
11
+ import { splice } from 'spica/array';
12
12
 
13
13
  interface Settings extends ParserSettings {
14
14
  }
@@ -29,8 +29,8 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
29
29
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
30
30
  type Block = readonly [segment: string, blocks: readonly HTMLElement[], url: string];
31
31
  const blocks: Block[] = [];
32
- const adds: [HTMLElement, Node | null][] = [];
33
- const dels: [HTMLElement][] = [];
32
+ const adds: (readonly [HTMLElement, Node | null])[] = [];
33
+ const dels: (readonly [HTMLElement])[] = [];
34
34
  const bottom = target.firstChild;
35
35
  let revision: symbol | undefined;
36
36
  return {
@@ -75,7 +75,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
75
75
  for (; index < sourceSegments.length - last; ++index) {
76
76
  assert(rev === revision);
77
77
  const seg = sourceSegments[index];
78
- const es = block(input(seg, context))
78
+ const es = block(input(seg, new Context(context)))
79
79
  ?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? [];
80
80
  // @ts-expect-error
81
81
  context.header = false;
@@ -83,7 +83,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
83
83
  if (es.length === 0) continue;
84
84
  // All deletion processes always run after all addition processes have done.
85
85
  // Therefore any `base` node will never be unavailable by deletions until all the dependent `el` nodes are added.
86
- push(adds, es.map(el => [el, base] as const));
86
+ adds.push(...es.map(el => [el, base] as const));
87
87
  adds.reverse();
88
88
  for (; adds.length > 0;) {
89
89
  assert(rev === revision);
@@ -98,7 +98,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
98
98
  assert(rev === revision);
99
99
  const es = refuse[i][1];
100
100
  if (es.length === 0) continue;
101
- push(dels, es.map(el => [el]));
101
+ dels.push(...es.map(el => [el] as const));
102
102
  }
103
103
  assert(blocks.length === sourceSegments.length);
104
104
  adds.reverse();
@@ -34,7 +34,7 @@ export function parse(source: string, options: Options = {}, context?: Context):
34
34
  context.header = true;
35
35
  for (const seg of segment(source)) {
36
36
  node.append(
37
- ...block(input(seg, context))
37
+ ...block(input(seg, new Context(context)))
38
38
  ?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? []);
39
39
  // @ts-expect-error
40
40
  context.header = false;
@@ -1,16 +1,16 @@
1
1
  import { AnnotationParser } from '../inline';
2
2
  import { State, Backtrack } from '../context';
3
3
  import { List, Node } from '../../combinator/data/parser';
4
- import { union, some, precedence, state, constraint, surround, setBacktrack, lazy } from '../../combinator';
4
+ import { union, some, precedence, state, constraint, surround, close, setBacktrack, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { beforeNonblank, trimBlankNodeEnd } from '../visibility';
7
7
  import { unwrap } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const annotation: AnnotationParser = lazy(() => constraint(State.annotation, surround(
11
- '((',
11
+ close('((', beforeNonblank),
12
12
  precedence(1, state(State.annotation,
13
- beforeNonblank(some(union([inline]), ')', [[')', 1]])))),
13
+ some(union([inline]), ')', [[')', 1]]))),
14
14
  '))',
15
15
  false,
16
16
  [2, 1 | Backtrack.common, 3 | Backtrack.doublebracket],
@@ -8,7 +8,7 @@ import { unwrap, repeat } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const deletion: DeletionParser = lazy(() =>
11
- precedence(0, recursion(Recursion.inline, repeat('~~', surround(
11
+ precedence(0, recursion(Recursion.inline, repeat('~~', '', surround(
12
12
  '',
13
13
  some(union([
14
14
  some(inline, blankWith('\n', '~~')),
@@ -5,17 +5,17 @@ import { union, some, recursion, precedence, surround, lazy } from '../../combin
5
5
  import { inline } from '../inline';
6
6
  import { strong } from './strong';
7
7
  import { str } from '../source';
8
- import { beforeNonblank, afterNonblank } from '../visibility';
8
+ import { beforeNonblankWith, afterNonblank } from '../visibility';
9
9
  import { unwrap } from '../util';
10
10
  import { html, defrag } from 'typed-dom/dom';
11
11
 
12
12
  export const emphasis: EmphasisParser = lazy(() => surround(
13
- str('*', (source, position, range) => !source.startsWith('*', position + range)),
13
+ str('*', beforeNonblankWith(/(?!\*)/)),
14
14
  precedence(0, recursion(Recursion.inline,
15
- beforeNonblank(some(union([
15
+ some(union([
16
16
  some(inline, '*', afterNonblank),
17
17
  strong,
18
- ]))))),
18
+ ])))),
19
19
  str('*'),
20
20
  false, [],
21
21
  ([, bs]) => new List([new Node(html('em', defrag(unwrap(bs))))]),
@@ -114,6 +114,7 @@ describe('Unit: parser/inline/emstrong', () => {
114
114
  assert.deepStrictEqual(inspect(parser, input('******a*b ***', new Context())), [['*****', '<em>a</em>', 'b ', '***'], '']);
115
115
  assert.deepStrictEqual(inspect(parser, input('******a*b ****', new Context())), [['*****', '<em>a</em>', 'b ', '****'], '']);
116
116
  assert.deepStrictEqual(inspect(parser, input('******a*b *****', new Context())), [['*****', '<em>a</em>', 'b ', '*****'], '']);
117
+ assert.deepStrictEqual(inspect(parser, input('******a*** b***', new Context())), [['<em><strong><em><strong>a</strong></em> b</strong></em>'], '']);
117
118
  });
118
119
 
119
120
  });
@@ -23,9 +23,9 @@ const subemphasis: Parser.IntermediateParser<EmphasisParser> = lazy(() => some(u
23
23
  // 可能な限り早く閉じるよう解析しなければならない。
24
24
  // このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
25
25
  export const emstrong: EmStrongParser = lazy(() =>
26
- precedence(0, recursion(Recursion.inline, repeat('***', surround(
26
+ precedence(0, recursion(Recursion.inline, repeat('***', beforeNonblank, surround(
27
27
  '',
28
- beforeNonblank(some(union([some(inline, '*', afterNonblank)]))),
28
+ some(union([some(inline, '*', afterNonblank)])),
29
29
  strs('*', 3),
30
30
  false, [],
31
31
  ([, bs, cs], context): Result<Parser.Node<EmStrongParser>, Parser.Context<EmStrongParser>> => {
@@ -33,7 +33,7 @@ export const emstrong: EmStrongParser = lazy(() =>
33
33
  const { buffer } = context;
34
34
  switch (cs.head!.value) {
35
35
  case '***':
36
- return bs;
36
+ return buffer.import(bs);
37
37
  case '**':
38
38
  return bind<EmphasisParser>(
39
39
  subemphasis,
@@ -13,13 +13,12 @@ import { html, define, defrag } from 'typed-dom/dom';
13
13
  import IndexParser = ExtensionParser.IndexParser;
14
14
 
15
15
  export const index: IndexParser = lazy(() => constraint(State.index, fmap(indexee(surround(
16
- str('[#'),
16
+ str('[#', beforeNonblank),
17
17
  precedence(1, state(State.linkers,
18
- beforeNonblank(
19
18
  some(inits([
20
19
  inline,
21
20
  signature,
22
- ]), ']', [[']', 1]])))),
21
+ ]), ']', [[']', 1]]))),
23
22
  str(']'),
24
23
  false,
25
24
  [3 | Backtrack.common],
@@ -14,9 +14,9 @@ import { html } from 'typed-dom/dom';
14
14
 
15
15
  export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => surround(
16
16
  // ^はabbrで使用済みだが^:などのようにして分離使用可能
17
- str(/\[[:^|]/y),
17
+ str(/\[[:^|]/y, beforeNonblank),
18
18
  precedence(1, recursion(Recursion.inline,
19
- beforeNonblank(some(union([inline]), ']', [[']', 1]])))),
19
+ some(union([inline]), ']', [[']', 1]]))),
20
20
  str(']'),
21
21
  false,
22
22
  [3 | Backtrack.common],
@@ -8,7 +8,7 @@ import { unwrap, repeat } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const insertion: InsertionParser = lazy(() =>
11
- precedence(0, recursion(Recursion.inline, repeat('++', surround(
11
+ precedence(0, recursion(Recursion.inline, repeat('++', '', surround(
12
12
  '',
13
13
  some(union([
14
14
  some(inline, blankWith('\n', '++')),
@@ -57,6 +57,7 @@ describe('Unit: parser/inline/italic', () => {
57
57
  assert.deepStrictEqual(inspect(parser, input('//////a/////b', new Context())), [['///', '<i>a</i>', '//b'], '']);
58
58
  assert.deepStrictEqual(inspect(parser, input('//////a//////', new Context())), [['<i><i>a</i></i>'], '']);
59
59
  assert.deepStrictEqual(inspect(parser, input('//////a///b///', new Context())), [['<i><i>a</i>b</i>'], '']);
60
+ assert.deepStrictEqual(inspect(parser, input('//////a/// b///', new Context())), [['<i><i>a</i> b</i>'], '']);
60
61
  assert.deepStrictEqual(inspect(parser, input('///a ///b//////', new Context())), [['<i>a <i>b</i></i>'], '']);
61
62
  assert.deepStrictEqual(inspect(parser, input('///- ///b//////', new Context())), [['<i>- <i>b</i></i>'], '']);
62
63
  assert.deepStrictEqual(inspect(parser, input('///a\\ ///b//////', new Context())), [['<i>a <i>b</i></i>'], '']);
@@ -11,9 +11,9 @@ import { html, defrag } from 'typed-dom/dom';
11
11
  // 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
12
12
  // ある程度の長さのある文に使うのが望ましい。
13
13
  export const italic: ItalicParser = lazy(() =>
14
- precedence(0, recursion(Recursion.inline, repeat('///', surround(
14
+ precedence(0, recursion(Recursion.inline, repeat('///', beforeNonblank, surround(
15
15
  '',
16
- beforeNonblank(some(union([inline]), '///', afterNonblank)),
16
+ some(union([inline]), '///', afterNonblank),
17
17
  '///',
18
18
  false, [],
19
19
  ([, bs], { buffer }) => buffer.import(bs),
@@ -1,7 +1,7 @@
1
1
  import { LinkParser } from '../inline';
2
2
  import { Context, State, Backtrack, Command } from '../context';
3
3
  import { List, Node } from '../../combinator/data/parser';
4
- import { union, inits, sequence, subsequence, some, consume, precedence, state, constraint, surround, open, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
4
+ import { union, inits, sequence, subsequence, some, consume, precedence, state, constraint, surround, open, close, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
5
5
  import { inline, media, shortmedia } from '../inline';
6
6
  import { attributes } from './html';
7
7
  import { str } from '../source';
@@ -18,9 +18,9 @@ Object.setPrototypeOf(optspec, null);
18
18
  export const textlink: LinkParser.TextLinkParser = lazy(() => bind(
19
19
  subsequence([
20
20
  constraint(State.link, state(State.linkers, dup(surround(
21
- '[',
21
+ close('[', beforeNonblank),
22
22
  precedence(1,
23
- beforeNonblank(some(union([inline]), ']', [[']', 1]]))),
23
+ some(union([inline]), ']', [[']', 1]])),
24
24
  ']',
25
25
  true,
26
26
  [3 | Backtrack.common | Backtrack.link, 2 | Backtrack.ruby],
@@ -44,6 +44,7 @@ describe('Unit: parser/inline/mark', () => {
44
44
  assert.deepStrictEqual(inspect(parser, input('==a&Tab;==b====', new Context())), [['<mark id="mark::a_b=33Mw2l">a\t<mark>b</mark></mark>', '<a href="#mark::a_b=33Mw2l"></a>'], '']);
45
45
  assert.deepStrictEqual(inspect(parser, input('==a<wbr>==b====', new Context())), [['<mark id="mark::ab">a<wbr><mark>b</mark></mark>', '<a href="#mark::ab"></a>'], '']);
46
46
  assert.deepStrictEqual(inspect(parser, input('==*==a==*==', new Context())), [['<mark id="mark::a"><em><mark>a</mark></em></mark>', '<a href="#mark::a"></a>'], '']);
47
+ assert.deepStrictEqual(inspect(parser, input('====a== b==', new Context())), [['<mark id="mark::a_b"><mark>a</mark> b</mark>', '<a href="#mark::a_b"></a>'], '']);
47
48
  });
48
49
 
49
50
  });
@@ -9,16 +9,16 @@ import { unwrap, repeat } from '../util';
9
9
  import { html, define, defrag } from 'typed-dom/dom';
10
10
 
11
11
  export const mark: MarkParser = lazy(() =>
12
- precedence(0, recursion(Recursion.inline, repeat('==', surround(
12
+ precedence(0, recursion(Recursion.inline, repeat('==', beforeNonblank, surround(
13
13
  '',
14
- beforeNonblank(state(State.mark, some(union([inline]), '==', afterNonblank))),
14
+ state(State.mark, some(union([inline]), '==', afterNonblank)),
15
15
  '==',
16
16
  false, [],
17
17
  ([, bs], { buffer }) => buffer.import(bs),
18
18
  ([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer),
19
- (nodes, { id, state }) => {
19
+ (nodes, { id, state }, nest) => {
20
20
  const el = html('mark', defrag(unwrap(nodes)));
21
- if (state & State.linkers) return new List([new Node(el)]);
21
+ if (state & State.linkers || nest) return new List([new Node(el)]);
22
22
  define(el, { id: identity('mark', id, signature(el)) });
23
23
  return el.id
24
24
  ? new List([new Node(el), new Node(html('a', { href: `#${el.id}` }))])
@@ -1,7 +1,7 @@
1
1
  import { ReferenceParser } from '../inline';
2
2
  import { State, Backtrack, Command } from '../context';
3
3
  import { List, Node } from '../../combinator/data/parser';
4
- import { union, subsequence, some, precedence, state, constraint, surround, isBacktrack, setBacktrack, lazy } from '../../combinator';
4
+ import { union, subsequence, some, precedence, state, constraint, surround, open, isBacktrack, setBacktrack, lazy } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { textlink } from './link';
7
7
  import { str } from '../source';
@@ -10,11 +10,11 @@ import { unwrap, invalid } from '../util';
10
10
  import { html, defrag } from 'typed-dom/dom';
11
11
 
12
12
  export const reference: ReferenceParser = lazy(() => constraint(State.reference, surround(
13
- str('[['),
13
+ '[[',
14
14
  precedence(1, state(State.annotation | State.reference,
15
15
  subsequence([
16
16
  abbr,
17
- beforeNonblank(some(inline, ']', [[']', 1]])),
17
+ open(beforeNonblank, some(inline, ']', [[']', 1]])),
18
18
  ]))),
19
19
  ']]',
20
20
  false,
@@ -5,17 +5,17 @@ import { union, some, recursion, precedence, surround, lazy } from '../../combin
5
5
  import { inline } from '../inline';
6
6
  import { emphasis } from './emphasis';
7
7
  import { str } from '../source';
8
- import { beforeNonblank, afterNonblank } from '../visibility';
8
+ import { beforeNonblankWith, afterNonblank } from '../visibility';
9
9
  import { unwrap } from '../util';
10
10
  import { html, defrag } from 'typed-dom/dom';
11
11
 
12
12
  export const strong: StrongParser = lazy(() => surround(
13
- str('**', (source, position, range) => !source.startsWith('*', position + range)),
13
+ str('**', beforeNonblankWith(/(?!\*)/)),
14
14
  precedence(0, recursion(Recursion.inline,
15
- beforeNonblank(some(union([
15
+ some(union([
16
16
  some(inline, '*', afterNonblank),
17
17
  emphasis,
18
- ]))))),
18
+ ])))),
19
19
  str('**'),
20
20
  false, [],
21
21
  ([, bs]) => new List([new Node(html('strong', defrag(unwrap(bs))))]),
@@ -1,10 +1,10 @@
1
1
  import { StrParser } from '../source';
2
2
  import { Parser, List, Node } from '../../combinator/data/parser';
3
- import { matcher } from '../../combinator';
3
+ import { matcher, tester } from '../../combinator/data/delimiter';
4
4
 
5
- export function str(pattern: string | RegExp, verify?: (source: string, position: number, range: number) => boolean): StrParser;
6
- export function str(pattern: string | RegExp, verify?: (source: string, position: number, range: number) => boolean): Parser<string> {
7
- return matcher(pattern, true, verify);
5
+ export function str(pattern: string | RegExp, after?: string | RegExp): StrParser;
6
+ export function str(pattern: string | RegExp, after?: string | RegExp): Parser<string> {
7
+ return matcher(pattern, true, after ? tester(after, false) : undefined);
8
8
  }
9
9
 
10
10
  export function strs(pattern: string, limit?: number): StrParser;