securemark 0.285.0 → 0.286.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.
- package/CHANGELOG.md +8 -0
- package/design.md +1 -1
- package/dist/index.js +185 -123
- package/markdown.d.ts +3 -4
- package/package.json +1 -1
- package/src/combinator/control/constraint/contract.ts +4 -4
- package/src/combinator/control/manipulation/convert.ts +3 -3
- package/src/combinator/control/manipulation/indent.ts +4 -4
- package/src/combinator/control/manipulation/match.ts +5 -3
- package/src/combinator/control/manipulation/scope.ts +7 -8
- package/src/combinator/control/manipulation/surround.ts +25 -13
- package/src/combinator/data/parser/context.test.ts +2 -2
- package/src/combinator/data/parser/context.ts +24 -7
- package/src/combinator/data/parser/some.ts +1 -0
- package/src/combinator/data/parser.ts +2 -1
- package/src/parser/api/parse.test.ts +18 -18
- package/src/parser/block/blockquote.ts +4 -4
- package/src/parser/block/extension/aside.ts +2 -2
- package/src/parser/block/extension/example.ts +2 -2
- package/src/parser/block/extension/figure.ts +1 -1
- package/src/parser/block/heading.ts +1 -1
- package/src/parser/block/ilist.ts +2 -2
- package/src/parser/block/olist.ts +6 -6
- package/src/parser/block/pagebreak.ts +1 -1
- package/src/parser/block/reply/cite.ts +3 -3
- package/src/parser/block/sidefence.ts +4 -4
- package/src/parser/block/table.ts +4 -4
- package/src/parser/block/ulist.ts +3 -3
- package/src/parser/block.ts +1 -1
- package/src/parser/context.ts +12 -7
- package/src/parser/header.ts +1 -1
- package/src/parser/inline/annotation.ts +4 -4
- package/src/parser/inline/autolink/email.ts +4 -4
- package/src/parser/inline/autolink/url.ts +15 -8
- package/src/parser/inline/bracket.ts +18 -16
- package/src/parser/inline/code.ts +3 -4
- package/src/parser/inline/deletion.ts +2 -2
- package/src/parser/inline/emphasis.ts +2 -2
- package/src/parser/inline/emstrong.ts +2 -2
- package/src/parser/inline/extension/index.ts +17 -13
- package/src/parser/inline/extension/indexer.ts +2 -3
- package/src/parser/inline/extension/label.ts +5 -5
- package/src/parser/inline/extension/placeholder.ts +2 -2
- package/src/parser/inline/html.ts +2 -2
- package/src/parser/inline/htmlentity.ts +4 -4
- package/src/parser/inline/insertion.ts +2 -2
- package/src/parser/inline/italic.ts +2 -2
- package/src/parser/inline/link.ts +5 -5
- package/src/parser/inline/mark.ts +2 -2
- package/src/parser/inline/math.ts +4 -4
- package/src/parser/inline/media.ts +13 -9
- package/src/parser/inline/reference.ts +6 -6
- package/src/parser/inline/remark.ts +2 -2
- package/src/parser/inline/ruby.ts +6 -6
- package/src/parser/inline/strong.ts +2 -2
- package/src/parser/inline/template.ts +10 -11
- package/src/parser/inline.test.ts +3 -0
- package/src/parser/source/escapable.ts +10 -5
- package/src/parser/source/str.ts +6 -6
- package/src/parser/source/text.ts +9 -5
- package/src/parser/source/unescapable.ts +9 -5
- package/src/parser/util.ts +5 -2
package/markdown.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Parser, Ctx } from './src/combinator/data/parser';
|
|
2
|
+
import { Command } from './src/parser/context';
|
|
2
3
|
import { Dict } from 'spica/dict';
|
|
3
4
|
|
|
4
5
|
declare abstract class Markdown<T> {
|
|
@@ -250,8 +251,6 @@ export namespace MarkdownParser {
|
|
|
250
251
|
Parser<HTMLTableCellElement, Context, [
|
|
251
252
|
SourceParser.StrParser,
|
|
252
253
|
SourceParser.StrParser,
|
|
253
|
-
SourceParser.StrParser,
|
|
254
|
-
SourceParser.StrParser,
|
|
255
254
|
]> {
|
|
256
255
|
}
|
|
257
256
|
export type CellParser
|
|
@@ -1196,8 +1195,8 @@ export namespace MarkdownParser {
|
|
|
1196
1195
|
}
|
|
1197
1196
|
export interface BracketParser extends
|
|
1198
1197
|
Inline<'url/bracket'>,
|
|
1199
|
-
Parser<string, Context, [
|
|
1200
|
-
Parser<string, Context, [
|
|
1198
|
+
Parser<string | Command.Escape, Context, [
|
|
1199
|
+
Parser<string | Command.Escape, Context, [
|
|
1201
1200
|
BracketParser,
|
|
1202
1201
|
SourceParser.UnescapableSourceParser,
|
|
1203
1202
|
]>,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isArray } from 'spica/alias';
|
|
2
2
|
import { Parser, Input, Ctx, Tree, Context, eval, exec, check } from '../../data/parser';
|
|
3
3
|
|
|
4
|
-
//export function contract<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], parser: P, cond: (
|
|
5
|
-
//export function contract<T>(patterns: string | RegExp | (string | RegExp)[], parser: Parser<T>, cond: (
|
|
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
|
+
//export function contract<T>(patterns: string | RegExp | (string | RegExp)[], parser: Parser<T>, cond: (nodes: readonly T[], rest: string) => boolean): Parser<T> {
|
|
6
6
|
// return verify(validate(patterns, parser), cond);
|
|
7
7
|
//}
|
|
8
8
|
|
|
@@ -45,8 +45,8 @@ function guard<T>(f: (input: Input<Ctx>) => boolean, parser: Parser<T>): Parser<
|
|
|
45
45
|
: undefined;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export function verify<P extends Parser<unknown>>(parser: P, cond: (
|
|
49
|
-
export function verify<T>(parser: Parser<T>, cond: (
|
|
48
|
+
export function verify<P extends Parser<unknown>>(parser: P, cond: (nodes: readonly Tree<P>[], rest: string, context: Context<P>) => boolean): P;
|
|
49
|
+
export function verify<T>(parser: Parser<T>, cond: (nodes: readonly T[], rest: string, context: Ctx) => boolean): Parser<T> {
|
|
50
50
|
assert(parser);
|
|
51
51
|
return input => {
|
|
52
52
|
const { source, context } = input;
|
|
@@ -8,11 +8,11 @@ export function convert<T>(conv: (source: string, context: Ctx) => string, parse
|
|
|
8
8
|
const src = conv(source, context);
|
|
9
9
|
if (src === '') return empty ? [[], ''] : undefined;
|
|
10
10
|
const sub = source.endsWith(src);
|
|
11
|
-
const {
|
|
12
|
-
context.
|
|
11
|
+
const { backtracks } = context;
|
|
12
|
+
context.backtracks = sub ? backtracks : {};
|
|
13
13
|
const result = parser({ source: src, context });
|
|
14
14
|
assert(check(src, result));
|
|
15
|
-
context.
|
|
15
|
+
context.backtracks = backtracks;
|
|
16
16
|
return result;
|
|
17
17
|
};
|
|
18
18
|
}
|
|
@@ -17,13 +17,13 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
|
|
|
17
17
|
memoize(
|
|
18
18
|
([indent]) =>
|
|
19
19
|
some(line(open(indent, ({ source }) => [[source], '']))),
|
|
20
|
-
([indent]) => indent.length * 2 + +(indent[0] === ' '), {})), separation),
|
|
20
|
+
([indent]) => indent.length * 2 + +(indent[0] === ' '), {}), false), separation),
|
|
21
21
|
(lines, rest, context) => {
|
|
22
22
|
assert(parser = parser as Parser<T>);
|
|
23
|
-
const {
|
|
24
|
-
context.
|
|
23
|
+
const { backtracks } = context;
|
|
24
|
+
context.backtracks = {};
|
|
25
25
|
const result = parser({ source: trimBlockEnd(lines.join('')), context });
|
|
26
|
-
context.
|
|
26
|
+
context.backtracks = backtracks;
|
|
27
27
|
return result && exec(result) === ''
|
|
28
28
|
? [eval(result), rest]
|
|
29
29
|
: undefined;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Parser, exec, check } from '../../data/parser';
|
|
2
|
+
import { consume } from '../../../combinator';
|
|
2
3
|
|
|
3
|
-
export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
|
|
4
|
-
export function match<T>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<T
|
|
4
|
+
export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P, cost?: boolean): P;
|
|
5
|
+
export function match<T>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<T>, cost = true): Parser<T> {
|
|
5
6
|
assert(!pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^'));
|
|
6
7
|
return input => {
|
|
7
|
-
const { source } = input;
|
|
8
|
+
const { source, context } = input;
|
|
8
9
|
if (source === '') return;
|
|
9
10
|
const param = source.match(pattern);
|
|
10
11
|
if (!param) return;
|
|
11
12
|
assert(source.startsWith(param[0]));
|
|
13
|
+
cost && consume(param.length, context);
|
|
12
14
|
const result = f(param)(input);
|
|
13
15
|
assert(check(source, result, false));
|
|
14
16
|
if (result === undefined) return;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Parser, Context, eval, exec, check } from '../../data/parser';
|
|
2
|
+
import { consume } from '../../../combinator';
|
|
2
3
|
|
|
3
|
-
export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P): P;
|
|
4
|
-
export function focus<T>(scope: string | RegExp, parser: Parser<T
|
|
4
|
+
export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P, cost?: boolean): P;
|
|
5
|
+
export function focus<T>(scope: string | RegExp, parser: Parser<T>, cost = true): Parser<T> {
|
|
5
6
|
assert(scope instanceof RegExp ? !scope.flags.match(/[gmy]/) && scope.source.startsWith('^') : scope);
|
|
6
7
|
assert(parser);
|
|
7
8
|
const match: (source: string) => string = typeof scope === 'string'
|
|
@@ -12,6 +13,7 @@ export function focus<T>(scope: string | RegExp, parser: Parser<T>): Parser<T> {
|
|
|
12
13
|
const src = match(source);
|
|
13
14
|
assert(source.startsWith(src));
|
|
14
15
|
if (src === '') return;
|
|
16
|
+
cost && consume(src.length, context);
|
|
15
17
|
const offset = source.length - src.length;
|
|
16
18
|
assert(offset >= 0);
|
|
17
19
|
context.offset ??= 0;
|
|
@@ -35,14 +37,11 @@ export function rewrite<T>(scope: Parser<unknown>, parser: Parser<T>): Parser<T>
|
|
|
35
37
|
return input => {
|
|
36
38
|
const { source, context } = input;
|
|
37
39
|
if (source === '') return;
|
|
38
|
-
const {
|
|
39
|
-
context.
|
|
40
|
-
//const { resources = { clock: 0 } } = context;
|
|
41
|
-
//const clock = resources.clock;
|
|
40
|
+
const { backtracks } = context;
|
|
41
|
+
context.backtracks = {};
|
|
42
42
|
const res1 = scope(input);
|
|
43
43
|
assert(check(source, res1));
|
|
44
|
-
|
|
45
|
-
context.logger = logger;
|
|
44
|
+
context.backtracks = backtracks;
|
|
46
45
|
if (res1 === undefined || exec(res1).length >= source.length) return;
|
|
47
46
|
const src = source.slice(0, source.length - exec(res1).length);
|
|
48
47
|
assert(src !== '');
|
|
@@ -7,35 +7,40 @@ export function surround<P extends Parser<unknown>, S = string>(
|
|
|
7
7
|
optional?: false,
|
|
8
8
|
f?: (rss: [S[], SubTree<P>[], S[]], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
9
9
|
g?: (rss: [S[], SubTree<P>[], string], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
10
|
-
|
|
10
|
+
backtrack?: number,
|
|
11
|
+
bstate?: number,
|
|
11
12
|
): P;
|
|
12
13
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
13
14
|
opener: string | RegExp | Parser<S, Context<P>>, parser: IntermediateParser<P>, closer: string | RegExp | Parser<S, Context<P>>,
|
|
14
15
|
optional?: boolean,
|
|
15
16
|
f?: (rss: [S[], SubTree<P>[] | undefined, S[]], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
16
17
|
g?: (rss: [S[], SubTree<P>[] | undefined, string], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
17
|
-
|
|
18
|
+
backtrack?: number,
|
|
19
|
+
bstate?: number,
|
|
18
20
|
): P;
|
|
19
21
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
20
22
|
opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
|
|
21
23
|
optional?: false,
|
|
22
24
|
f?: (rss: [S[], Tree<P>[], S[]], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
23
25
|
g?: (rss: [S[], Tree<P>[], string], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
24
|
-
|
|
26
|
+
backtrack?: number,
|
|
27
|
+
bstate?: number,
|
|
25
28
|
): P;
|
|
26
29
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
27
30
|
opener: string | RegExp | Parser<S, Context<P>>, parser: P, closer: string | RegExp | Parser<S, Context<P>>,
|
|
28
31
|
optional?: boolean,
|
|
29
32
|
f?: (rss: [S[], Tree<P>[] | undefined, S[]], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
30
33
|
g?: (rss: [S[], Tree<P>[] | undefined, string], rest: string, context: Context<P>) => Result<Tree<P>, Context<P>, SubParsers<P>>,
|
|
31
|
-
|
|
34
|
+
backtrack?: number,
|
|
35
|
+
bstate?: number,
|
|
32
36
|
): P;
|
|
33
37
|
export function surround<T>(
|
|
34
38
|
opener: string | RegExp | Parser<T>, parser: Parser<T>, closer: string | RegExp | Parser<T>,
|
|
35
39
|
optional: boolean = false,
|
|
36
40
|
f?: (rss: [T[], T[], T[]], rest: string, context: Ctx) => Result<T>,
|
|
37
41
|
g?: (rss: [T[], T[], string], rest: string, context: Ctx) => Result<T>,
|
|
38
|
-
|
|
42
|
+
backtrack: number = 0,
|
|
43
|
+
bstate: number = 0,
|
|
39
44
|
): Parser<T> {
|
|
40
45
|
switch (typeof opener) {
|
|
41
46
|
case 'string':
|
|
@@ -55,18 +60,23 @@ export function surround<T>(
|
|
|
55
60
|
if (res1 === undefined) return;
|
|
56
61
|
const rl = eval(res1);
|
|
57
62
|
const mr_ = exec(res1);
|
|
58
|
-
if (
|
|
59
|
-
const {
|
|
63
|
+
if (backtrack & 1) {
|
|
64
|
+
const { backtracks = {}, backtrack: state = 0, offset = 0 } = context;
|
|
60
65
|
for (let i = 0; i < source.length - mr_.length; ++i) {
|
|
61
66
|
if (source[i] !== source[0]) break;
|
|
62
67
|
const pos = source.length + offset - i - 1;
|
|
63
|
-
if (!(pos in
|
|
64
|
-
assert(
|
|
65
|
-
|
|
68
|
+
if (!(pos in backtracks)) continue;
|
|
69
|
+
assert(backtrack >>> 2);
|
|
70
|
+
// bracket only
|
|
71
|
+
const shift = backtrack >>> 2 === state >>> 2 ? state & 3 : 0;
|
|
72
|
+
if (backtracks[pos] & 1 << (backtrack >>> 2) + shift) return;
|
|
66
73
|
}
|
|
67
74
|
}
|
|
75
|
+
const { backtrack: state = 0 } = context;
|
|
76
|
+
context.backtrack = state | bstate;
|
|
68
77
|
const res2 = mr_ !== '' ? parser({ source: mr_, context }) : undefined;
|
|
69
78
|
assert(check(mr_, res2));
|
|
79
|
+
context.backtrack = state;
|
|
70
80
|
const rm = eval(res2);
|
|
71
81
|
const r_ = exec(res2, mr_);
|
|
72
82
|
if (!rm && !optional) return;
|
|
@@ -75,9 +85,11 @@ export function surround<T>(
|
|
|
75
85
|
const rr = eval(res3);
|
|
76
86
|
const rest = exec(res3, r_);
|
|
77
87
|
if (rest.length === lmr_.length) return;
|
|
78
|
-
if (
|
|
79
|
-
const {
|
|
80
|
-
|
|
88
|
+
if (backtrack & 2 && rr === undefined) {
|
|
89
|
+
const { backtracks = {}, backtrack: state = 0, offset = 0 } = context;
|
|
90
|
+
// bracket only
|
|
91
|
+
const shift = backtrack >>> 2 === state >>> 2 ? state & 3 : 0;
|
|
92
|
+
backtracks[source.length + offset - 1] |= 1 << (backtrack >>> 2) + shift;
|
|
81
93
|
}
|
|
82
94
|
return rr
|
|
83
95
|
? f
|
|
@@ -9,7 +9,7 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
describe('reset', () => {
|
|
12
|
-
const parser: Parser<number> = some(creation(1,
|
|
12
|
+
const parser: Parser<number> = some(creation(1,
|
|
13
13
|
({ source, context }) => [[context.resources?.clock ?? NaN], source.slice(1)]));
|
|
14
14
|
|
|
15
15
|
it('root', () => {
|
|
@@ -36,7 +36,7 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
describe('context', () => {
|
|
39
|
-
const parser: Parser<boolean, Context> = some(creation(1,
|
|
39
|
+
const parser: Parser<boolean, Context> = some(creation(1,
|
|
40
40
|
({ source, context }) => [[context.status!], source.slice(1)]));
|
|
41
41
|
|
|
42
42
|
it('', () => {
|
|
@@ -25,7 +25,7 @@ export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
|
25
25
|
function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Tree<P>>;
|
|
26
26
|
function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<T> {
|
|
27
27
|
if (reset) {
|
|
28
|
-
context.
|
|
28
|
+
context.backtracks = {};
|
|
29
29
|
}
|
|
30
30
|
for (let i = 0; i < changes.length; ++i) {
|
|
31
31
|
const change = changes[i];
|
|
@@ -58,23 +58,40 @@ function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: read
|
|
|
58
58
|
return result;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export function creation<P extends Parser<unknown>>(cost: number,
|
|
62
|
-
export function creation(cost: number,
|
|
61
|
+
export function creation<P extends Parser<unknown>>(cost: number, parser: P): P;
|
|
62
|
+
export function creation(cost: number, parser: Parser<unknown>): Parser<unknown> {
|
|
63
63
|
assert(cost >= 0);
|
|
64
|
-
assert(recursion >= 0);
|
|
65
64
|
return input => {
|
|
66
65
|
const { context } = input;
|
|
67
66
|
const resources = context.resources ?? { clock: cost || 1, recursions: [1] };
|
|
68
67
|
const { recursions } = resources;
|
|
69
68
|
assert(recursions.length > 0);
|
|
69
|
+
const result = parser(input);
|
|
70
|
+
if (result === undefined) return;
|
|
71
|
+
consume(cost, context);
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function consume(cost: number, context: Ctx): void {
|
|
76
|
+
const { resources } = context;
|
|
77
|
+
if (!resources) return;
|
|
78
|
+
if (resources.clock < cost) throw new Error('Too many creations');
|
|
79
|
+
resources.clock -= cost;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function recursion<P extends Parser<unknown>>(recursion: number, parser: P): P;
|
|
83
|
+
export function recursion(recursion: number, parser: Parser<unknown>): Parser<unknown> {
|
|
84
|
+
assert(recursion >= 0);
|
|
85
|
+
return input => {
|
|
86
|
+
const { context } = input;
|
|
87
|
+
const resources = context.resources ?? { clock: 1, recursions: [1] };
|
|
88
|
+
const { recursions } = resources;
|
|
89
|
+
assert(recursions.length > 0);
|
|
70
90
|
const rec = min(recursion, recursions.length);
|
|
71
91
|
if (rec > 0 && recursions[rec - 1] < 1) throw new Error('Too much recursion');
|
|
72
92
|
rec > 0 && --recursions[rec - 1];
|
|
73
93
|
const result = parser(input);
|
|
74
94
|
rec > 0 && ++recursions[rec - 1];
|
|
75
|
-
if (result === undefined) return;
|
|
76
|
-
if (resources.clock < cost) throw new Error('Too many creations');
|
|
77
|
-
resources.clock -= cost;
|
|
78
95
|
return result;
|
|
79
96
|
};
|
|
80
97
|
}
|
|
@@ -18,6 +18,7 @@ export function some<T>(parser: Parser<T>, end?: string | RegExp | number, delim
|
|
|
18
18
|
}));
|
|
19
19
|
return ({ source, context }) => {
|
|
20
20
|
if (source === '') return;
|
|
21
|
+
assert(context.backtracks ??= {});
|
|
21
22
|
let rest = source;
|
|
22
23
|
let nodes: T[] | undefined;
|
|
23
24
|
if (delims.length > 0) {
|
|
@@ -19,7 +19,8 @@ export interface Ctx {
|
|
|
19
19
|
precedence?: number;
|
|
20
20
|
delimiters?: Delimiters;
|
|
21
21
|
state?: number;
|
|
22
|
-
|
|
22
|
+
backtracks?: Record<number, number>;
|
|
23
|
+
backtrack?: number;
|
|
23
24
|
}
|
|
24
25
|
export type Tree<P extends Parser<unknown>> = P extends Parser<infer T> ? T : never;
|
|
25
26
|
export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, Ctx, infer D> ? D : never;
|
|
@@ -297,26 +297,26 @@ describe('Unit: parser/api/parse', () => {
|
|
|
297
297
|
`<pre class="error" translate="no">${'{'.repeat(21)}a</pre>`,
|
|
298
298
|
]);
|
|
299
299
|
assert.deepStrictEqual(
|
|
300
|
-
[...parse(`${'('.repeat(
|
|
301
|
-
[`<p>${'('.repeat(
|
|
300
|
+
[...parse(`${'('.repeat(20)}a`).children].map(el => el.outerHTML),
|
|
301
|
+
[`<p>${'('.repeat(20)}a</p>`]);
|
|
302
302
|
assert.deepStrictEqual(
|
|
303
|
-
[...parse(`${'('.repeat(
|
|
303
|
+
[...parse(`${'('.repeat(21)}a`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
304
304
|
[
|
|
305
305
|
'<h1 id="error:rnd" class="error">Error: Too much recursion</h1>',
|
|
306
|
-
`<pre class="error" translate="no">${'('.repeat(
|
|
306
|
+
`<pre class="error" translate="no">${'('.repeat(21)}a</pre>`,
|
|
307
307
|
]);
|
|
308
308
|
assert.deepStrictEqual(
|
|
309
|
-
[...parse(`${'['.repeat(
|
|
310
|
-
[`<p>${'['.repeat(
|
|
309
|
+
[...parse(`${'['.repeat(20)}a`).children].map(el => el.outerHTML),
|
|
310
|
+
[`<p>${'['.repeat(20)}a</p>`]);
|
|
311
311
|
assert.deepStrictEqual(
|
|
312
|
-
[...parse(`${'['.repeat(
|
|
312
|
+
[...parse(`${'['.repeat(21)}a`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
313
313
|
[
|
|
314
314
|
'<h1 id="error:rnd" class="error">Error: Too much recursion</h1>',
|
|
315
|
-
`<pre class="error" translate="no">${'['.repeat(
|
|
315
|
+
`<pre class="error" translate="no">${'['.repeat(21)}a</pre>`,
|
|
316
316
|
]);
|
|
317
317
|
assert.deepStrictEqual(
|
|
318
|
-
[...parse(`${'['.repeat(
|
|
319
|
-
[`<p>${'['.repeat(
|
|
318
|
+
[...parse(`${'['.repeat(20)}\na`).children].map(el => el.outerHTML),
|
|
319
|
+
[`<p>${'['.repeat(20)}<br>a</p>`]);
|
|
320
320
|
});
|
|
321
321
|
|
|
322
322
|
it('recovery', () => {
|
|
@@ -334,14 +334,14 @@ describe('Unit: parser/api/parse', () => {
|
|
|
334
334
|
it('creation', function () {
|
|
335
335
|
this.timeout(5000);
|
|
336
336
|
assert.deepStrictEqual(
|
|
337
|
-
[...parse('.'.repeat(
|
|
338
|
-
[`<p>${'.'.repeat(
|
|
337
|
+
[...parse('.'.repeat(100000)).children].map(el => el.outerHTML),
|
|
338
|
+
[`<p>${'.'.repeat(100000)}</p>`]);
|
|
339
339
|
});
|
|
340
340
|
|
|
341
|
-
it('creation error', function () {
|
|
341
|
+
it.skip('creation error', function () {
|
|
342
342
|
this.timeout(5000);
|
|
343
343
|
assert.deepStrictEqual(
|
|
344
|
-
[...parse('.'.repeat(
|
|
344
|
+
[...parse('.'.repeat(100001)).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
345
345
|
[
|
|
346
346
|
'<h1 id="error:rnd" class="error">Error: Too many creations</h1>',
|
|
347
347
|
`<pre class="error" translate="no">${'.'.repeat(1000 - 3)}...</pre>`,
|
|
@@ -351,17 +351,17 @@ describe('Unit: parser/api/parse', () => {
|
|
|
351
351
|
it('backtrack', function () {
|
|
352
352
|
this.timeout(5000);
|
|
353
353
|
assert.deepStrictEqual(
|
|
354
|
-
[...parse(
|
|
355
|
-
[`<p
|
|
354
|
+
[...parse(`..((${'['.repeat(16)}http://{{${'.'.repeat(5876)}`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
355
|
+
[`<p>..((${'['.repeat(16)}http://{{${'.'.repeat(5876)}</p>`]);
|
|
356
356
|
});
|
|
357
357
|
|
|
358
358
|
it('backtrack error', function () {
|
|
359
359
|
this.timeout(5000);
|
|
360
360
|
assert.deepStrictEqual(
|
|
361
|
-
[...parse(
|
|
361
|
+
[...parse(`..((${'['.repeat(16)}http://{{${'.'.repeat(5877)}`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
362
362
|
[
|
|
363
363
|
'<h1 id="error:rnd" class="error">Error: Too many creations</h1>',
|
|
364
|
-
`<pre class="error" translate="no"
|
|
364
|
+
`<pre class="error" translate="no">..((${'['.repeat(16)}http://{{${'.'.repeat(1000 - 2 - 2 - 16 - 9 - 3)}...</pre>`,
|
|
365
365
|
]);
|
|
366
366
|
});
|
|
367
367
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BlockquoteParser } from '../block';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
-
import { union, some, creation, block, validate, rewrite, open, convert, lazy, fmap } from '../../combinator';
|
|
3
|
+
import { union, some, creation, recursion, block, validate, rewrite, open, convert, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { autolink } from '../autolink';
|
|
5
5
|
import { contentline } from '../source';
|
|
6
6
|
import { parse } from '../api/parse';
|
|
@@ -20,7 +20,7 @@ const indent = block(open(opener, some(contentline, /^>(?:$|\s)/)), false);
|
|
|
20
20
|
const unindent = (source: string) => source.replace(/(?<=^|\n)>(?:[^\S\n]|(?=>*(?:$|\s)))|\n$/g, '');
|
|
21
21
|
|
|
22
22
|
const source: BlockquoteParser.SourceParser = lazy(() => fmap(
|
|
23
|
-
some(
|
|
23
|
+
some(recursion(Recursion.blockquote, union([
|
|
24
24
|
rewrite(
|
|
25
25
|
indent,
|
|
26
26
|
convert(unindent, source, true)),
|
|
@@ -31,11 +31,11 @@ const source: BlockquoteParser.SourceParser = lazy(() => fmap(
|
|
|
31
31
|
ns => [html('blockquote', ns)]));
|
|
32
32
|
|
|
33
33
|
const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
|
|
34
|
-
some(
|
|
34
|
+
some(recursion(Recursion.blockquote, union([
|
|
35
35
|
rewrite(
|
|
36
36
|
indent,
|
|
37
37
|
convert(unindent, markdown, true)),
|
|
38
|
-
creation(10,
|
|
38
|
+
creation(10,
|
|
39
39
|
rewrite(
|
|
40
40
|
some(contentline, opener),
|
|
41
41
|
convert(unindent, ({ source, context }) => {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
2
|
import { Recursion } from '../../context';
|
|
3
|
-
import {
|
|
3
|
+
import { recursion, block, validate, fence, fmap } from '../../../combinator';
|
|
4
4
|
import { identity } from '../../inline/extension/indexee';
|
|
5
5
|
import { parse } from '../../api/parse';
|
|
6
6
|
import { html } from 'typed-dom/dom';
|
|
7
7
|
|
|
8
|
-
export const aside: ExtensionParser.AsideParser =
|
|
8
|
+
export const aside: ExtensionParser.AsideParser = recursion(Recursion.block, block(validate('~~~', fmap(
|
|
9
9
|
fence(/^(~{3,})aside(?!\S)([^\n]*)(?:$|\n)/, 300),
|
|
10
10
|
// Bug: Type mismatch between outer and inner.
|
|
11
11
|
([body, overflow, closer, opener, delim, param]: string[], _, context) => {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
2
|
import { Recursion } from '../../context';
|
|
3
3
|
import { eval } from '../../../combinator/data/parser';
|
|
4
|
-
import {
|
|
4
|
+
import { recursion, block, validate, fence, fmap } from '../../../combinator';
|
|
5
5
|
import { mathblock } from '../mathblock';
|
|
6
6
|
import { parse } from '../../api/parse';
|
|
7
7
|
import { html } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
9
|
const opener = /^(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/;
|
|
10
10
|
|
|
11
|
-
export const example: ExtensionParser.ExampleParser =
|
|
11
|
+
export const example: ExtensionParser.ExampleParser = recursion(Recursion.block, block(validate('~~~', fmap(
|
|
12
12
|
fence(opener, 300),
|
|
13
13
|
// Bug: Type mismatch between outer and inner.
|
|
14
14
|
([body, overflow, closer, opener, delim, type = 'markdown', param]: string[], _, context) => {
|
|
@@ -42,7 +42,7 @@ export const segment: FigureParser.SegmentParser = block(match(
|
|
|
42
42
|
]),
|
|
43
43
|
]),
|
|
44
44
|
closer),
|
|
45
|
-
([, fence]) => fence.length, {})));
|
|
45
|
+
([, fence]) => fence.length, {}), false));
|
|
46
46
|
|
|
47
47
|
export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
48
48
|
convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
|
|
@@ -8,7 +8,7 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
8
8
|
|
|
9
9
|
export const segment: HeadingParser.SegmentParser = block(validate('#', focus(
|
|
10
10
|
/^#+[^\S\n]+\S[^\n]*(?:\n#+(?!\S)[^\n]*)*(?:$|\n)/,
|
|
11
|
-
some(line(({ source }) => [[source], ''])))));
|
|
11
|
+
some(line(({ source }) => [[source], ''])), false)));
|
|
12
12
|
|
|
13
13
|
export const heading: HeadingParser = block(rewrite(segment,
|
|
14
14
|
// その他の表示制御は各所のCSSで行う。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IListParser } from '../block';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
-
import { union, inits, some,
|
|
3
|
+
import { union, inits, some, recursion, block, line, validate, indent, open, trim, fallback, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { ulist_, invalid, fillFirstLine } from './ulist';
|
|
5
5
|
import { olist_ } from './olist';
|
|
6
6
|
import { inline } from '../inline';
|
|
@@ -14,7 +14,7 @@ export const ilist: IListParser = lazy(() => block(validate(
|
|
|
14
14
|
|
|
15
15
|
export const ilist_: IListParser = lazy(() => block(fmap(validate(
|
|
16
16
|
/^[-+*](?:$|\s)/,
|
|
17
|
-
some(
|
|
17
|
+
some(recursion(Recursion.listitem, union([
|
|
18
18
|
fmap(fallback(
|
|
19
19
|
inits([
|
|
20
20
|
line(open(/^[-+*](?:$|\s)/, trim(visualize(trimBlank(lineable(some(inline))))), true)),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OListParser } from '../block';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
-
import { union, inits, subsequence, some,
|
|
3
|
+
import { union, inits, subsequence, some, recursion, block, line, validate, indent, focus, open, match, trim, fallback, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { ulist_, checkbox, invalid, fillFirstLine } from './ulist';
|
|
5
5
|
import { ilist_ } from './ilist';
|
|
6
6
|
import { inline, indexee, indexer, dataindex } from '../inline';
|
|
@@ -24,14 +24,14 @@ export const olist: OListParser = lazy(() => block(validate(
|
|
|
24
24
|
export const olist_: OListParser = lazy(() => block(union([
|
|
25
25
|
match(
|
|
26
26
|
openers['.'],
|
|
27
|
-
memoize(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), [])),
|
|
27
|
+
memoize(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), []), false),
|
|
28
28
|
match(
|
|
29
29
|
openers['('],
|
|
30
|
-
memoize(ms => list(type(ms[1]), '('), ms => idx(ms[1]), [])),
|
|
30
|
+
memoize(ms => list(type(ms[1]), '('), ms => idx(ms[1]), []), false),
|
|
31
31
|
])));
|
|
32
32
|
|
|
33
33
|
const list = (type: string, form: string): OListParser.ListParser => fmap(
|
|
34
|
-
some(
|
|
34
|
+
some(recursion(Recursion.listitem, union([
|
|
35
35
|
indexee(fmap(fallback(
|
|
36
36
|
inits([
|
|
37
37
|
line(open(heads[form], subsequence([
|
|
@@ -47,10 +47,10 @@ const list = (type: string, form: string): OListParser.ListParser => fmap(
|
|
|
47
47
|
const heads = {
|
|
48
48
|
'.': focus(
|
|
49
49
|
openers['.'],
|
|
50
|
-
({ source }) => [[source.trimEnd().split('.', 1)[0] + '.'], '']),
|
|
50
|
+
({ source }) => [[source.trimEnd().split('.', 1)[0] + '.'], ''], false),
|
|
51
51
|
'(': focus(
|
|
52
52
|
openers['('],
|
|
53
|
-
({ source }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], '']),
|
|
53
|
+
({ source }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], ''], false),
|
|
54
54
|
} as const;
|
|
55
55
|
|
|
56
56
|
function idx(value: string): number {
|
|
@@ -12,9 +12,9 @@ export const cite: ReplyParser.CiteParser = line(fmap(validate(
|
|
|
12
12
|
anchor,
|
|
13
13
|
// Subject page representation.
|
|
14
14
|
// リンクの実装は後で検討
|
|
15
|
-
focus(/^>>\.(?=\s*$)/, () => [[html('a', { class: 'anchor' }, '>>.')], '']),
|
|
16
|
-
focus(/^>>#\S*(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor' }, source)], '']),
|
|
17
|
-
focus(/^>>https?:\/\/(?:[[]|[^\p{C}\p{S}\p{P}\s])\S*(?=\s*$)/u, ({ source }) => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)], '']),
|
|
15
|
+
focus(/^>>\.(?=\s*$)/, () => [[html('a', { class: 'anchor' }, '>>.')], ''], false),
|
|
16
|
+
focus(/^>>#\S*(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor' }, source)], ''], false),
|
|
17
|
+
focus(/^>>https?:\/\/(?:[[]|[^\p{C}\p{S}\p{P}\s])\S*(?=\s*$)/u, ({ source }) => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)], ''], false),
|
|
18
18
|
]),
|
|
19
19
|
]))),
|
|
20
20
|
([el, quotes = '']: [HTMLElement, string?]) => [
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { SidefenceParser } from '../block';
|
|
2
2
|
import { Recursion } from '../context';
|
|
3
|
-
import { union, some,
|
|
3
|
+
import { union, some, recursion, block, focus, rewrite, convert, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { autolink } from '../autolink';
|
|
5
5
|
import { contentline } from '../source';
|
|
6
6
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
7
7
|
|
|
8
8
|
export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
|
|
9
9
|
/^(?=\|+(?:[^\S\n]|\n\|))(?:\|+(?:[^\S\n][^\n]*)?(?:$|\n))+$/,
|
|
10
|
-
union([source])),
|
|
10
|
+
union([source]), false),
|
|
11
11
|
([el]) => [
|
|
12
12
|
define(el, {
|
|
13
13
|
class: 'invalid',
|
|
@@ -21,10 +21,10 @@ const opener = /^(?=\|\|+(?:$|\s))/;
|
|
|
21
21
|
const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?:[^\S\n]|(?=\|*(?:$|\s)))|\n$/g, '');
|
|
22
22
|
|
|
23
23
|
const source: SidefenceParser.SourceParser = lazy(() => fmap(
|
|
24
|
-
some(
|
|
24
|
+
some(recursion(Recursion.block, union([
|
|
25
25
|
focus(
|
|
26
26
|
/^(?:\|\|+(?:[^\S\n][^\n]*)?(?:$|\n))+/,
|
|
27
|
-
convert(unindent, source, true)),
|
|
27
|
+
convert(unindent, source, true), false),
|
|
28
28
|
rewrite(
|
|
29
29
|
some(contentline, opener),
|
|
30
30
|
convert(unindent, fmap(autolink, ns => [html('pre', defrag(ns))]), true)),
|
|
@@ -40,10 +40,10 @@ const row = <P extends CellParser | AlignParser>(parser: P, optional: boolean):
|
|
|
40
40
|
const align: AlignParser = fmap(open(
|
|
41
41
|
'|',
|
|
42
42
|
union([
|
|
43
|
-
focus(
|
|
44
|
-
|
|
45
|
-
focus(
|
|
46
|
-
|
|
43
|
+
focus(/^:-+:?/, ({ source }) =>
|
|
44
|
+
[[source[source.length - 1] === ':' ? 'center' : 'start'], ''], false),
|
|
45
|
+
focus(/^-+:?/, ({ source }) =>
|
|
46
|
+
[[source[source.length - 1] === ':' ? 'end' : ''], ''], false),
|
|
47
47
|
])),
|
|
48
48
|
ns => [html('td', defrag(ns))]);
|
|
49
49
|
|