securemark 0.296.3 → 0.296.4
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 +4 -0
- package/design.md +2 -3
- package/dist/index.js +106 -53
- package/markdown.d.ts +8 -4
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +12 -6
- package/src/combinator/control/manipulation/recovery.ts +3 -3
- package/src/combinator/data/parser.ts +7 -0
- package/src/parser/api/bind.ts +10 -5
- package/src/parser/api/parse.ts +3 -2
- package/src/parser/block/extension/fig.ts +3 -1
- package/src/parser/block/extension/figure.ts +2 -1
- package/src/parser/block/extension/message.ts +1 -1
- package/src/parser/block/extension.test.ts +3 -3
- package/src/parser/block/extension.ts +3 -4
- package/src/parser/block/heading.ts +4 -4
- package/src/parser/block.ts +22 -12
- package/src/parser/context.ts +13 -0
- package/src/parser/inline/emstrong.ts +1 -1
- package/src/parser/segment.test.ts +73 -73
- package/src/parser/segment.ts +9 -11
- package/src/parser/source/line.ts +25 -6
- package/src/parser/source/str.ts +9 -10
- package/src/parser/source.ts +2 -1
|
@@ -33,6 +33,7 @@ export class Context {
|
|
|
33
33
|
{
|
|
34
34
|
source,
|
|
35
35
|
position,
|
|
36
|
+
segment,
|
|
36
37
|
resources,
|
|
37
38
|
delimiters,
|
|
38
39
|
precedence,
|
|
@@ -45,6 +46,7 @@ export class Context {
|
|
|
45
46
|
) {
|
|
46
47
|
this.source = source ?? '';
|
|
47
48
|
this.position = position ?? 0;
|
|
49
|
+
this.segment = segment ?? 0;
|
|
48
50
|
this.resources = resources;
|
|
49
51
|
this.precedence = precedence ?? 0;
|
|
50
52
|
this.delimiters = delimiters ?? new Delimiters();
|
|
@@ -56,6 +58,7 @@ export class Context {
|
|
|
56
58
|
}
|
|
57
59
|
public source: string;
|
|
58
60
|
public position: number;
|
|
61
|
+
public segment: number;
|
|
59
62
|
public readonly resources?: {
|
|
60
63
|
clock: number;
|
|
61
64
|
recursions: number[];
|
|
@@ -103,6 +106,10 @@ export class Context {
|
|
|
103
106
|
public backtracks: Record<number, number>;
|
|
104
107
|
}
|
|
105
108
|
export type Options = Partial<Context>;
|
|
109
|
+
export const enum Segment {
|
|
110
|
+
unknown = 0,
|
|
111
|
+
write = 1,
|
|
112
|
+
}
|
|
106
113
|
|
|
107
114
|
export function input<C extends Context>(source: string, context: C): Input<C> {
|
|
108
115
|
context.source = source;
|
package/src/parser/api/bind.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ParserSettings, Progress } from '../../..';
|
|
2
|
-
import { Context } from '../context';
|
|
2
|
+
import { Context, Segment } from '../context';
|
|
3
3
|
import { input } from '../../combinator/data/parser';
|
|
4
4
|
import { segment } from '../segment';
|
|
5
5
|
import { block } from '../block';
|
|
@@ -47,8 +47,10 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
47
47
|
context.url = url ? new ReadonlyURL(url as ':') : undefined;
|
|
48
48
|
const rev = revision = Symbol();
|
|
49
49
|
const sourceSegments: string[] = [];
|
|
50
|
-
|
|
50
|
+
const sourceSegmentAttrs: Segment[] = [];
|
|
51
|
+
for (const [seg, attr] of segment(source)) {
|
|
51
52
|
sourceSegments.push(seg);
|
|
53
|
+
sourceSegmentAttrs.push(attr);
|
|
52
54
|
yield { type: 'segment', value: seg };
|
|
53
55
|
}
|
|
54
56
|
const targetSegments = blocks.map(([seg]) => seg);
|
|
@@ -74,12 +76,15 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
74
76
|
context.header = true;
|
|
75
77
|
for (; index < sourceSegments.length - last; ++index) {
|
|
76
78
|
assert(rev === revision);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
+
const src = sourceSegments[index];
|
|
80
|
+
context.segment = sourceSegmentAttrs[index] | Segment.write;
|
|
81
|
+
const es = block(input(src, new Context(context)))
|
|
79
82
|
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? [];
|
|
80
83
|
// @ts-expect-error
|
|
81
84
|
context.header = false;
|
|
82
|
-
blocks.
|
|
85
|
+
blocks.length === index
|
|
86
|
+
? blocks.push([src, es, url])
|
|
87
|
+
: blocks.splice(index, 0, [src, es, url]);
|
|
83
88
|
if (es.length === 0) continue;
|
|
84
89
|
// All deletion processes always run after all addition processes have done.
|
|
85
90
|
// Therefore any `base` node will never be unavailable by deletions until all the dependent `el` nodes are added.
|
package/src/parser/api/parse.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParserOptions } from '../../..';
|
|
2
2
|
import { input } from '../../combinator/data/parser';
|
|
3
|
-
import { Context } from '../context';
|
|
3
|
+
import { Context, Segment } from '../context';
|
|
4
4
|
import { segment } from '../segment';
|
|
5
5
|
import { block } from '../block';
|
|
6
6
|
import { normalize } from './normalize';
|
|
@@ -32,7 +32,8 @@ export function parse(source: string, options: Options = {}, context?: Context):
|
|
|
32
32
|
const node = frag();
|
|
33
33
|
// @ts-expect-error
|
|
34
34
|
context.header = true;
|
|
35
|
-
for (const seg of segment(source)) {
|
|
35
|
+
for (const [seg, attr] of segment(source)) {
|
|
36
|
+
context.segment = attr | Segment.write;
|
|
36
37
|
node.append(
|
|
37
38
|
...block(input(seg, new Context(context)))
|
|
38
39
|
?.foldl<HTMLElement[]>((acc, { value }) => void acc.push(value) || acc, []) ?? []);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
|
+
import { Segment } from '../../context';
|
|
2
3
|
import { union, sequence, some, block, line, verify, rewrite, close, convert } from '../../../combinator';
|
|
3
4
|
import { contentline } from '../../source';
|
|
4
5
|
import { figure } from './figure';
|
|
@@ -23,7 +24,7 @@ export const segment: FigParser.SegmentParser = block(
|
|
|
23
24
|
seg_placeholder,
|
|
24
25
|
some(contentline),
|
|
25
26
|
]),
|
|
26
|
-
]));
|
|
27
|
+
]), true, Segment.fig);
|
|
27
28
|
|
|
28
29
|
export const fig: FigParser = block(rewrite(segment, verify(convert(
|
|
29
30
|
(source, context) => {
|
|
@@ -33,6 +34,7 @@ export const fig: FigParser = block(rewrite(segment, verify(convert(
|
|
|
33
34
|
const { position } = context;
|
|
34
35
|
const result = parser({ context });
|
|
35
36
|
context.position = position;
|
|
37
|
+
context.segment = Segment.figure | Segment.write;
|
|
36
38
|
return result
|
|
37
39
|
? `${fence}figure ${source.replace(/^(.+\n.+\n)([\S\s]+?)\n?$/, '$1\n$2')}\n${fence}`
|
|
38
40
|
: `${fence}figure ${source}\n\n${fence}`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
|
+
import { Segment } from '../../context';
|
|
2
3
|
import { List, Node, subinput } from '../../../combinator/data/parser';
|
|
3
4
|
import { union, inits, sequence, some, block, line, fence, rewrite, close, match, convert, fallback, fmap } from '../../../combinator';
|
|
4
5
|
import { str, contentline, emptyline } from '../../source';
|
|
@@ -44,7 +45,7 @@ export const segment: FigureParser.SegmentParser = block(match(
|
|
|
44
45
|
]),
|
|
45
46
|
]),
|
|
46
47
|
closer),
|
|
47
|
-
([, fence]) => fence.length - 1, [], 2 ** 4 - 1)));
|
|
48
|
+
([, fence]) => fence.length - 1, [], 2 ** 4 - 1)), true, Segment.figure);
|
|
48
49
|
|
|
49
50
|
export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
50
51
|
convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
|
|
@@ -30,9 +30,9 @@ describe('Unit: parser/block/extension', () => {
|
|
|
30
30
|
assert(parser(input('~~~\na\n~~~', new Context())));
|
|
31
31
|
assert(parser(input('~~~a\n~~~', new Context())));
|
|
32
32
|
assert(parser(input('~~~a\nb\n~~~', new Context())));
|
|
33
|
-
assert(parser(input('$-0', new Context())));
|
|
34
|
-
assert(parser(input('$-0\n', new Context())));
|
|
35
|
-
assert(parser(input('$-0\n\n', new Context())));
|
|
33
|
+
//assert(parser(input('$-0', new Context())));
|
|
34
|
+
//assert(parser(input('$-0\n', new Context())));
|
|
35
|
+
//assert(parser(input('$-0\n\n', new Context())));
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ExtensionParser } from '../block';
|
|
2
2
|
import { union } from '../../combinator';
|
|
3
|
-
import {
|
|
4
|
-
import { fig, segment as seg_fig } from './extension/fig';
|
|
3
|
+
import { segment as seg_fig } from './extension/fig';
|
|
5
4
|
import { figure, segment as seg_figure } from './extension/figure';
|
|
6
5
|
import { table, segment as seg_table } from './extension/table';
|
|
7
6
|
import { message } from './extension/message';
|
|
@@ -17,8 +16,8 @@ export const segment: ExtensionParser.SegmentParser = union([
|
|
|
17
16
|
]);
|
|
18
17
|
|
|
19
18
|
export const extension: ExtensionParser = union([
|
|
20
|
-
figbase,
|
|
21
|
-
fig,
|
|
19
|
+
//figbase,
|
|
20
|
+
//fig,
|
|
22
21
|
figure,
|
|
23
22
|
table,
|
|
24
23
|
message,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { HeadingParser } from '../block';
|
|
2
|
-
import { State } from '../context';
|
|
2
|
+
import { Segment, State } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
4
|
import { union, some, state, block, line, focus, rewrite, open, fmap, firstline } from '../../combinator';
|
|
5
5
|
import { inline, indexee, indexer, dataindex } from '../inline';
|
|
6
|
-
import { str } from '../source';
|
|
6
|
+
import { str, strs } from '../source';
|
|
7
7
|
import { visualize, trimBlank } from '../visibility';
|
|
8
8
|
import { unwrap, invalid } from '../util';
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
@@ -20,14 +20,14 @@ export const segment: HeadingParser.SegmentParser = block(focus(
|
|
|
20
20
|
context.position += line.length;
|
|
21
21
|
}
|
|
22
22
|
return acc;
|
|
23
|
-
}, false));
|
|
23
|
+
}, false), true, Segment.heading);
|
|
24
24
|
|
|
25
25
|
export const heading: HeadingParser = block(rewrite(segment,
|
|
26
26
|
// その他の表示制御は各所のCSSで行う。
|
|
27
27
|
state(State.annotation | State.reference | State.index | State.label | State.link,
|
|
28
28
|
line(indexee(fmap(union([
|
|
29
29
|
open(
|
|
30
|
-
|
|
30
|
+
strs('#', 2),
|
|
31
31
|
visualize(trimBlank(some(union([indexer, inline])))), true),
|
|
32
32
|
open(
|
|
33
33
|
str('#'),
|
package/src/parser/block.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { MarkdownParser } from '../../markdown';
|
|
2
|
-
import { Recursion, Command } from './context';
|
|
2
|
+
import { Segment, Recursion, Command } from './context';
|
|
3
3
|
import { List, Node } from '../combinator/data/parser';
|
|
4
|
-
import { union, reset,
|
|
4
|
+
import { union, reset, firstline, recover } from '../combinator';
|
|
5
5
|
import { MAX_SEGMENT_SIZE } from './segment';
|
|
6
6
|
import { header } from './header';
|
|
7
|
-
import {
|
|
7
|
+
import { emptysegment } from './source';
|
|
8
8
|
import { pagebreak } from './block/pagebreak';
|
|
9
9
|
import { heading } from './block/heading';
|
|
10
10
|
import { ulist } from './block/ulist';
|
|
@@ -15,6 +15,9 @@ import { table } from './block/table';
|
|
|
15
15
|
import { codeblock } from './block/codeblock';
|
|
16
16
|
import { mathblock } from './block/mathblock';
|
|
17
17
|
import { extension } from './block/extension';
|
|
18
|
+
import { figbase } from './block/extension/figbase';
|
|
19
|
+
import { fig } from './block/extension/fig';
|
|
20
|
+
import { figure } from './block/extension/figure';
|
|
18
21
|
import { sidefence } from './block/sidefence';
|
|
19
22
|
import { blockquote } from './block/blockquote';
|
|
20
23
|
import { mediablock } from './block/mediablock';
|
|
@@ -57,12 +60,22 @@ export const block: BlockParser = reset(
|
|
|
57
60
|
backtracks: {},
|
|
58
61
|
},
|
|
59
62
|
error(union([
|
|
60
|
-
|
|
63
|
+
emptysegment,
|
|
61
64
|
input => {
|
|
62
|
-
const { context: { source, position } } = input;
|
|
65
|
+
const { context: { source, position, segment } } = input;
|
|
63
66
|
if (position === source.length) return;
|
|
67
|
+
switch (segment ^ Segment.write) {
|
|
68
|
+
case Segment.heading:
|
|
69
|
+
return heading(input);
|
|
70
|
+
case Segment.fig:
|
|
71
|
+
return fig(input);
|
|
72
|
+
case Segment.figure:
|
|
73
|
+
return figure(input);
|
|
74
|
+
}
|
|
64
75
|
const fst = source[position];
|
|
65
76
|
switch (fst) {
|
|
77
|
+
case Command.Error:
|
|
78
|
+
throw new Error(firstline(source, position + 1).trimEnd());
|
|
66
79
|
case '=':
|
|
67
80
|
if (source.startsWith('===', position)) return pagebreak(input);
|
|
68
81
|
break;
|
|
@@ -84,7 +97,7 @@ export const block: BlockParser = reset(
|
|
|
84
97
|
case '[':
|
|
85
98
|
switch (source[position + 1]) {
|
|
86
99
|
case '$':
|
|
87
|
-
return
|
|
100
|
+
return figbase(input);
|
|
88
101
|
case '!':
|
|
89
102
|
return mediablock(input);
|
|
90
103
|
}
|
|
@@ -95,11 +108,9 @@ export const block: BlockParser = reset(
|
|
|
95
108
|
case '>':
|
|
96
109
|
if (source[position + 1] === '>') return blockquote(input) || reply(input);
|
|
97
110
|
return blockquote(input);
|
|
98
|
-
case '#':
|
|
99
|
-
return heading(input);
|
|
100
111
|
case '$':
|
|
101
112
|
if (source[position + 1] === '$') return mathblock(input);
|
|
102
|
-
return
|
|
113
|
+
return figbase(input);
|
|
103
114
|
case '|':
|
|
104
115
|
return table(input) || sidefence(input);
|
|
105
116
|
case '(':
|
|
@@ -113,9 +124,8 @@ export const block: BlockParser = reset(
|
|
|
113
124
|
|
|
114
125
|
function error(parser: BlockParser): BlockParser {
|
|
115
126
|
const reg = new RegExp(String.raw`^${Command.Error}[^\n]*\n`)
|
|
116
|
-
return recover<BlockParser>(
|
|
117
|
-
|
|
118
|
-
parser),
|
|
127
|
+
return recover<BlockParser>(
|
|
128
|
+
parser,
|
|
119
129
|
({ context: { source, position, id } }, reason) => new List([
|
|
120
130
|
new Node(html('h1',
|
|
121
131
|
{
|
package/src/parser/context.ts
CHANGED
|
@@ -7,6 +7,7 @@ export class Context extends Ctx {
|
|
|
7
7
|
) {
|
|
8
8
|
super(options);
|
|
9
9
|
const {
|
|
10
|
+
segment,
|
|
10
11
|
buffer,
|
|
11
12
|
sequential,
|
|
12
13
|
header,
|
|
@@ -15,6 +16,7 @@ export class Context extends Ctx {
|
|
|
15
16
|
id,
|
|
16
17
|
caches,
|
|
17
18
|
} = options;
|
|
19
|
+
this.segment = segment ?? Segment.unknown;
|
|
18
20
|
this.buffer = buffer ?? new List();
|
|
19
21
|
this.sequential = sequential ?? false;
|
|
20
22
|
this.header = header ?? true;
|
|
@@ -23,6 +25,7 @@ export class Context extends Ctx {
|
|
|
23
25
|
this.id = id;
|
|
24
26
|
this.caches = caches;
|
|
25
27
|
}
|
|
28
|
+
public override segment: Segment;
|
|
26
29
|
public buffer: List<Node<(string | HTMLElement)>>;
|
|
27
30
|
public sequential: boolean;
|
|
28
31
|
public readonly header: boolean;
|
|
@@ -37,6 +40,16 @@ export class Context extends Ctx {
|
|
|
37
40
|
}
|
|
38
41
|
export type Options = Partial<Context>;
|
|
39
42
|
|
|
43
|
+
export const enum Segment {
|
|
44
|
+
unknown = 0,
|
|
45
|
+
write = 1,
|
|
46
|
+
nonempty = 0,
|
|
47
|
+
empty = 1 << 1,
|
|
48
|
+
heading = 3 << 1,
|
|
49
|
+
fig = 4 << 1,
|
|
50
|
+
figure = 5 << 1,
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
export const enum State {
|
|
41
54
|
annotation = 1 << 7,
|
|
42
55
|
reference = 1 << 6,
|
|
@@ -26,7 +26,7 @@ export const emstrong: EmStrongParser = lazy(() =>
|
|
|
26
26
|
precedence(0, recursion(Recursion.inline, repeat('***', beforeNonblank, surround(
|
|
27
27
|
'',
|
|
28
28
|
some(union([some(inline, '*', afterNonblank)])),
|
|
29
|
-
strs('*', 3),
|
|
29
|
+
strs('*', 1, 3),
|
|
30
30
|
false, [],
|
|
31
31
|
([, bs, cs], context): Result<Parser.Node<EmStrongParser>, Parser.Context<EmStrongParser>> => {
|
|
32
32
|
assert(cs.length === 1);
|
|
@@ -4,108 +4,108 @@ import { Command } from './context';
|
|
|
4
4
|
describe('Unit: parser/segment', () => {
|
|
5
5
|
describe('segment', () => {
|
|
6
6
|
it('huge input', () => {
|
|
7
|
-
const result = segment(`${'\n'.repeat(1e6 + 1)}`).next().value?.split('\n', 1)[0];
|
|
7
|
+
const result = segment(`${'\n'.repeat(1e6 + 1)}`).next().value?.[0].split('\n', 1)[0];
|
|
8
8
|
assert(result?.startsWith(`${Command.Error}Too large input`));
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('huge segment', () => {
|
|
12
|
-
const result = segment(`${'\n'.repeat(1e5 + 1)}`).next().value?.split('\n', 1)[0];
|
|
12
|
+
const result = segment(`${'\n'.repeat(1e5 + 1)}`).next().value?.[0].split('\n', 1)[0];
|
|
13
13
|
assert(result?.startsWith(`${Command.Error}Too large segment`));
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
it('basic', () => {
|
|
17
|
-
assert.deepStrictEqual([...segment('')], []);
|
|
18
|
-
assert.deepStrictEqual([...segment('a')], ['a']);
|
|
19
|
-
assert.deepStrictEqual([...segment('a\n')], ['a\n']);
|
|
20
|
-
assert.deepStrictEqual([...segment('a\n\n')], ['a\n', '\n']);
|
|
21
|
-
assert.deepStrictEqual([...segment('a\n\n\n')], ['a\n', '\n\n']);
|
|
22
|
-
assert.deepStrictEqual([...segment('a\nb')], ['a\nb']);
|
|
23
|
-
assert.deepStrictEqual([...segment('a\nb\n')], ['a\nb\n']);
|
|
24
|
-
assert.deepStrictEqual([...segment('a\nb\n\n')], ['a\nb\n', '\n']);
|
|
25
|
-
assert.deepStrictEqual([...segment('a\nb\n\n\n')], ['a\nb\n', '\n\n']);
|
|
26
|
-
assert.deepStrictEqual([...segment('a\nb\n\nc\n\nd')], ['a\nb\n', '\n', 'c\n', '\n', 'd']);
|
|
27
|
-
assert.deepStrictEqual([...segment('a\n\\\nb')], ['a\n\\\nb']);
|
|
28
|
-
assert.deepStrictEqual([...segment('a ')], ['a ']);
|
|
29
|
-
assert.deepStrictEqual([...segment(' a')], [' a']);
|
|
30
|
-
assert.deepStrictEqual([...segment(' a ')], [' a ']);
|
|
17
|
+
assert.deepStrictEqual([...segment('')].map(t => t[0]), []);
|
|
18
|
+
assert.deepStrictEqual([...segment('a')].map(t => t[0]), ['a']);
|
|
19
|
+
assert.deepStrictEqual([...segment('a\n')].map(t => t[0]), ['a\n']);
|
|
20
|
+
assert.deepStrictEqual([...segment('a\n\n')].map(t => t[0]), ['a\n', '\n']);
|
|
21
|
+
assert.deepStrictEqual([...segment('a\n\n\n')].map(t => t[0]), ['a\n', '\n\n']);
|
|
22
|
+
assert.deepStrictEqual([...segment('a\nb')].map(t => t[0]), ['a\nb']);
|
|
23
|
+
assert.deepStrictEqual([...segment('a\nb\n')].map(t => t[0]), ['a\nb\n']);
|
|
24
|
+
assert.deepStrictEqual([...segment('a\nb\n\n')].map(t => t[0]), ['a\nb\n', '\n']);
|
|
25
|
+
assert.deepStrictEqual([...segment('a\nb\n\n\n')].map(t => t[0]), ['a\nb\n', '\n\n']);
|
|
26
|
+
assert.deepStrictEqual([...segment('a\nb\n\nc\n\nd')].map(t => t[0]), ['a\nb\n', '\n', 'c\n', '\n', 'd']);
|
|
27
|
+
assert.deepStrictEqual([...segment('a\n\\\nb')].map(t => t[0]), ['a\n\\\nb']);
|
|
28
|
+
assert.deepStrictEqual([...segment('a ')].map(t => t[0]), ['a ']);
|
|
29
|
+
assert.deepStrictEqual([...segment(' a')].map(t => t[0]), [' a']);
|
|
30
|
+
assert.deepStrictEqual([...segment(' a ')].map(t => t[0]), [' a ']);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
it('linebreak', () => {
|
|
34
|
-
assert.deepStrictEqual([...segment('\n')], ['\n']);
|
|
35
|
-
assert.deepStrictEqual([...segment('\n\n')], ['\n\n']);
|
|
36
|
-
assert.deepStrictEqual([...segment('\n\n\n')], ['\n\n\n']);
|
|
37
|
-
assert.deepStrictEqual([...segment('\n\n\n\n')], ['\n\n\n\n']);
|
|
38
|
-
assert.deepStrictEqual([...segment(' ')], [' ']);
|
|
39
|
-
assert.deepStrictEqual([...segment(' \n')], [' \n']);
|
|
40
|
-
assert.deepStrictEqual([...segment(' \n \n \n ')], [' \n \n \n ']);
|
|
41
|
-
assert.deepStrictEqual([...segment('a\n')], ['a\n']);
|
|
42
|
-
assert.deepStrictEqual([...segment('a\n ')], ['a\n', ' ']);
|
|
43
|
-
assert.deepStrictEqual([...segment('a\n\n ')], ['a\n', '\n ']);
|
|
44
|
-
assert.deepStrictEqual([...segment('a\n\n\n ')], ['a\n', '\n\n ']);
|
|
45
|
-
assert.deepStrictEqual([...segment('a\n\n\n\n ')], ['a\n', '\n\n\n ']);
|
|
46
|
-
assert.deepStrictEqual([...segment('a\n\n\n\n\n ')], ['a\n', '\n\n\n\n ']);
|
|
47
|
-
assert.deepStrictEqual([...segment('a\n\n\n\n\n\n ')], ['a\n', '\n\n\n\n\n ']);
|
|
34
|
+
assert.deepStrictEqual([...segment('\n')].map(t => t[0]), ['\n']);
|
|
35
|
+
assert.deepStrictEqual([...segment('\n\n')].map(t => t[0]), ['\n\n']);
|
|
36
|
+
assert.deepStrictEqual([...segment('\n\n\n')].map(t => t[0]), ['\n\n\n']);
|
|
37
|
+
assert.deepStrictEqual([...segment('\n\n\n\n')].map(t => t[0]), ['\n\n\n\n']);
|
|
38
|
+
assert.deepStrictEqual([...segment(' ')].map(t => t[0]), [' ']);
|
|
39
|
+
assert.deepStrictEqual([...segment(' \n')].map(t => t[0]), [' \n']);
|
|
40
|
+
assert.deepStrictEqual([...segment(' \n \n \n ')].map(t => t[0]), [' \n \n \n ']);
|
|
41
|
+
assert.deepStrictEqual([...segment('a\n')].map(t => t[0]), ['a\n']);
|
|
42
|
+
assert.deepStrictEqual([...segment('a\n ')].map(t => t[0]), ['a\n', ' ']);
|
|
43
|
+
assert.deepStrictEqual([...segment('a\n\n ')].map(t => t[0]), ['a\n', '\n ']);
|
|
44
|
+
assert.deepStrictEqual([...segment('a\n\n\n ')].map(t => t[0]), ['a\n', '\n\n ']);
|
|
45
|
+
assert.deepStrictEqual([...segment('a\n\n\n\n ')].map(t => t[0]), ['a\n', '\n\n\n ']);
|
|
46
|
+
assert.deepStrictEqual([...segment('a\n\n\n\n\n ')].map(t => t[0]), ['a\n', '\n\n\n\n ']);
|
|
47
|
+
assert.deepStrictEqual([...segment('a\n\n\n\n\n\n ')].map(t => t[0]), ['a\n', '\n\n\n\n\n ']);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it('codeblock', () => {
|
|
51
|
-
assert.deepStrictEqual([...segment('```')], ['```']);
|
|
52
|
-
assert.deepStrictEqual([...segment('```\n```')], ['```\n```']);
|
|
53
|
-
assert.deepStrictEqual([...segment('```\n\n```')], ['```\n\n```']);
|
|
54
|
-
assert.deepStrictEqual([...segment('```\n\n\n```')], ['```\n', '\n\n', '```']);
|
|
55
|
-
assert.deepStrictEqual([...segment('```\n\n0\n```')], ['```\n', '\n', '0\n```']);
|
|
56
|
-
assert.deepStrictEqual([...segment('```\n0\n\n```')], ['```\n0\n\n```']);
|
|
57
|
-
assert.deepStrictEqual([...segment('```\n````\n```')], ['```\n````\n```']);
|
|
58
|
-
assert.deepStrictEqual([...segment('````\n```\n````')], ['````\n```\n````']);
|
|
59
|
-
assert.deepStrictEqual([...segment('```\n\n```\n\n')], ['```\n\n```\n', '\n']);
|
|
51
|
+
assert.deepStrictEqual([...segment('```')].map(t => t[0]), ['```']);
|
|
52
|
+
assert.deepStrictEqual([...segment('```\n```')].map(t => t[0]), ['```\n```']);
|
|
53
|
+
assert.deepStrictEqual([...segment('```\n\n```')].map(t => t[0]), ['```\n\n```']);
|
|
54
|
+
assert.deepStrictEqual([...segment('```\n\n\n```')].map(t => t[0]), ['```\n', '\n\n', '```']);
|
|
55
|
+
assert.deepStrictEqual([...segment('```\n\n0\n```')].map(t => t[0]), ['```\n', '\n', '0\n```']);
|
|
56
|
+
assert.deepStrictEqual([...segment('```\n0\n\n```')].map(t => t[0]), ['```\n0\n\n```']);
|
|
57
|
+
assert.deepStrictEqual([...segment('```\n````\n```')].map(t => t[0]), ['```\n````\n```']);
|
|
58
|
+
assert.deepStrictEqual([...segment('````\n```\n````')].map(t => t[0]), ['````\n```\n````']);
|
|
59
|
+
assert.deepStrictEqual([...segment('```\n\n```\n\n')].map(t => t[0]), ['```\n\n```\n', '\n']);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
it('mathblock', () => {
|
|
63
|
-
assert.deepStrictEqual([...segment('$$')], ['$$']);
|
|
64
|
-
assert.deepStrictEqual([...segment('$$\n$$')], ['$$\n$$']);
|
|
65
|
-
assert.deepStrictEqual([...segment('$$\n\n$$')], ['$$\n\n$$']);
|
|
66
|
-
assert.deepStrictEqual([...segment('$$\n\n\n$$')], ['$$\n', '\n\n', '$$']);
|
|
67
|
-
assert.deepStrictEqual([...segment('$$\n\n0\n$$')], ['$$\n', '\n', '0\n$$']);
|
|
68
|
-
assert.deepStrictEqual([...segment('$$\n0\n\n$$')], ['$$\n0\n\n$$']);
|
|
69
|
-
assert.deepStrictEqual([...segment('$$\n\n$$\n\n')], ['$$\n\n$$\n', '\n']);
|
|
63
|
+
assert.deepStrictEqual([...segment('$$')].map(t => t[0]), ['$$']);
|
|
64
|
+
assert.deepStrictEqual([...segment('$$\n$$')].map(t => t[0]), ['$$\n$$']);
|
|
65
|
+
assert.deepStrictEqual([...segment('$$\n\n$$')].map(t => t[0]), ['$$\n\n$$']);
|
|
66
|
+
assert.deepStrictEqual([...segment('$$\n\n\n$$')].map(t => t[0]), ['$$\n', '\n\n', '$$']);
|
|
67
|
+
assert.deepStrictEqual([...segment('$$\n\n0\n$$')].map(t => t[0]), ['$$\n', '\n', '0\n$$']);
|
|
68
|
+
assert.deepStrictEqual([...segment('$$\n0\n\n$$')].map(t => t[0]), ['$$\n0\n\n$$']);
|
|
69
|
+
assert.deepStrictEqual([...segment('$$\n\n$$\n\n')].map(t => t[0]), ['$$\n\n$$\n', '\n']);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
it('extension', () => {
|
|
73
|
-
assert.deepStrictEqual([...segment('~~~')], ['~~~']);
|
|
74
|
-
assert.deepStrictEqual([...segment('~~~\n~~~')], ['~~~\n~~~']);
|
|
75
|
-
assert.deepStrictEqual([...segment('~~~\n\n~~~')], ['~~~\n\n~~~']);
|
|
76
|
-
assert.deepStrictEqual([...segment('~~~\n\n\n~~~')], ['~~~\n', '\n\n', '~~~']);
|
|
77
|
-
assert.deepStrictEqual([...segment('~~~\n\n0\n~~~')], ['~~~\n', '\n', '0\n~~~']);
|
|
78
|
-
assert.deepStrictEqual([...segment('~~~\n0\n\n~~~')], ['~~~\n0\n\n~~~']);
|
|
79
|
-
assert.deepStrictEqual([...segment('~~~\n~~~~\n~~~')], ['~~~\n~~~~\n~~~']);
|
|
80
|
-
assert.deepStrictEqual([...segment('~~~~\n~~~\n~~~~')], ['~~~~\n~~~\n~~~~']);
|
|
81
|
-
assert.deepStrictEqual([...segment('~~~\n\n~~~\n\n')], ['~~~\n\n~~~\n', '\n']);
|
|
82
|
-
assert.deepStrictEqual([...segment('~~~\n```\n~~~\n```\n~~~')], ['~~~\n```\n~~~\n```\n~~~']);
|
|
83
|
-
assert.deepStrictEqual([...segment('~~~\ninvalid\n~~~')], ['~~~\ninvalid\n~~~']);
|
|
84
|
-
assert.deepStrictEqual([...segment('~~~\ninvalid\n\ncaption\n~~~')], ['~~~\ninvalid\n\ncaption\n~~~']);
|
|
85
|
-
assert.deepStrictEqual([...segment('~~~figure [$-name]\n$$\n$$\n\n```\n~~~')], ['~~~figure [$-name]\n$$\n$$\n\n```\n~~~']);
|
|
73
|
+
assert.deepStrictEqual([...segment('~~~')].map(t => t[0]), ['~~~']);
|
|
74
|
+
assert.deepStrictEqual([...segment('~~~\n~~~')].map(t => t[0]), ['~~~\n~~~']);
|
|
75
|
+
assert.deepStrictEqual([...segment('~~~\n\n~~~')].map(t => t[0]), ['~~~\n\n~~~']);
|
|
76
|
+
assert.deepStrictEqual([...segment('~~~\n\n\n~~~')].map(t => t[0]), ['~~~\n', '\n\n', '~~~']);
|
|
77
|
+
assert.deepStrictEqual([...segment('~~~\n\n0\n~~~')].map(t => t[0]), ['~~~\n', '\n', '0\n~~~']);
|
|
78
|
+
assert.deepStrictEqual([...segment('~~~\n0\n\n~~~')].map(t => t[0]), ['~~~\n0\n\n~~~']);
|
|
79
|
+
assert.deepStrictEqual([...segment('~~~\n~~~~\n~~~')].map(t => t[0]), ['~~~\n~~~~\n~~~']);
|
|
80
|
+
assert.deepStrictEqual([...segment('~~~~\n~~~\n~~~~')].map(t => t[0]), ['~~~~\n~~~\n~~~~']);
|
|
81
|
+
assert.deepStrictEqual([...segment('~~~\n\n~~~\n\n')].map(t => t[0]), ['~~~\n\n~~~\n', '\n']);
|
|
82
|
+
assert.deepStrictEqual([...segment('~~~\n```\n~~~\n```\n~~~')].map(t => t[0]), ['~~~\n```\n~~~\n```\n~~~']);
|
|
83
|
+
assert.deepStrictEqual([...segment('~~~\ninvalid\n~~~')].map(t => t[0]), ['~~~\ninvalid\n~~~']);
|
|
84
|
+
assert.deepStrictEqual([...segment('~~~\ninvalid\n\ncaption\n~~~')].map(t => t[0]), ['~~~\ninvalid\n\ncaption\n~~~']);
|
|
85
|
+
assert.deepStrictEqual([...segment('~~~figure [$-name]\n$$\n$$\n\n```\n~~~')].map(t => t[0]), ['~~~figure [$-name]\n$$\n$$\n\n```\n~~~']);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
88
|
it('mixed', () => {
|
|
89
|
-
assert.deepStrictEqual([...segment('```\n\n\n')], ['```\n', '\n\n']);
|
|
90
|
-
assert.deepStrictEqual([...segment('~~~\n\n\n')], ['~~~\n', '\n\n']);
|
|
91
|
-
assert.deepStrictEqual([...segment('```\n~~~\n```')], ['```\n~~~\n```']);
|
|
92
|
-
assert.deepStrictEqual([...segment('~~~\n```\n~~~')], ['~~~\n```\n~~~']);
|
|
93
|
-
assert.deepStrictEqual([...segment('```\n```\n\n~~~\n~~~')], ['```\n```\n', '\n', '~~~\n~~~']);
|
|
94
|
-
assert.deepStrictEqual([...segment('~~~\n~~~\n\n```\n```')], ['~~~\n~~~\n', '\n', '```\n```']);
|
|
95
|
-
assert.deepStrictEqual([...segment(' ```\n\n```')], [' ```\n', '\n', '```']);
|
|
96
|
-
assert.deepStrictEqual([...segment(' ~~~\n\n~~~')], [' ~~~\n', '\n', '~~~']);
|
|
89
|
+
assert.deepStrictEqual([...segment('```\n\n\n')].map(t => t[0]), ['```\n', '\n\n']);
|
|
90
|
+
assert.deepStrictEqual([...segment('~~~\n\n\n')].map(t => t[0]), ['~~~\n', '\n\n']);
|
|
91
|
+
assert.deepStrictEqual([...segment('```\n~~~\n```')].map(t => t[0]), ['```\n~~~\n```']);
|
|
92
|
+
assert.deepStrictEqual([...segment('~~~\n```\n~~~')].map(t => t[0]), ['~~~\n```\n~~~']);
|
|
93
|
+
assert.deepStrictEqual([...segment('```\n```\n\n~~~\n~~~')].map(t => t[0]), ['```\n```\n', '\n', '~~~\n~~~']);
|
|
94
|
+
assert.deepStrictEqual([...segment('~~~\n~~~\n\n```\n```')].map(t => t[0]), ['~~~\n~~~\n', '\n', '```\n```']);
|
|
95
|
+
assert.deepStrictEqual([...segment(' ```\n\n```')].map(t => t[0]), [' ```\n', '\n', '```']);
|
|
96
|
+
assert.deepStrictEqual([...segment(' ~~~\n\n~~~')].map(t => t[0]), [' ~~~\n', '\n', '~~~']);
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
it('blockquote', () => {
|
|
100
|
-
assert.deepStrictEqual([...segment('> ```\n\n```')], ['> ```\n', '\n', '```']);
|
|
101
|
-
assert.deepStrictEqual([...segment('!> ```\n\n```')], ['!> ```\n', '\n', '```']);
|
|
100
|
+
assert.deepStrictEqual([...segment('> ```\n\n```')].map(t => t[0]), ['> ```\n', '\n', '```']);
|
|
101
|
+
assert.deepStrictEqual([...segment('!> ```\n\n```')].map(t => t[0]), ['!> ```\n', '\n', '```']);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
it('heading', () => {
|
|
105
|
-
assert.deepStrictEqual([...segment('# a\n\n# b\n')], ['# a\n', '\n', '# b\n']);
|
|
106
|
-
assert.deepStrictEqual([...segment('# a\n# b\n')], ['# a\n', '# b\n']);
|
|
107
|
-
assert.deepStrictEqual([...segment('# a\n# b')], ['# a\n', '# b']);
|
|
108
|
-
assert.deepStrictEqual([...segment('# a\n # b')], ['# a\n # b']);
|
|
105
|
+
assert.deepStrictEqual([...segment('# a\n\n# b\n')].map(t => t[0]), ['# a\n', '\n', '# b\n']);
|
|
106
|
+
assert.deepStrictEqual([...segment('# a\n# b\n')].map(t => t[0]), ['# a\n', '# b\n']);
|
|
107
|
+
assert.deepStrictEqual([...segment('# a\n# b')].map(t => t[0]), ['# a\n', '# b']);
|
|
108
|
+
assert.deepStrictEqual([...segment('# a\n # b')].map(t => t[0]), ['# a\n # b']);
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
});
|
package/src/parser/segment.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { MarkdownParser } from '../../markdown';
|
|
2
|
-
import { Context, Command } from './context';
|
|
2
|
+
import { Context, Segment, Command } from './context';
|
|
3
3
|
import { union, some } from '../combinator';
|
|
4
4
|
import { segment as heading } from './block/heading';
|
|
5
5
|
import { segment as codeblock } from './block/codeblock';
|
|
6
6
|
import { segment as mathblock } from './block/mathblock';
|
|
7
7
|
import { segment as extension } from './block/extension';
|
|
8
|
-
import { contentline,
|
|
8
|
+
import { contentline, emptysegment } from './source';
|
|
9
9
|
|
|
10
10
|
import SegmentParser = MarkdownParser.SegmentParser;
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@ export const MAX_SEGMENT_SIZE = 100_000; // 100,000 bytes (Max value size of FDB
|
|
|
13
13
|
export const MAX_INPUT_SIZE = MAX_SEGMENT_SIZE * 10;
|
|
14
14
|
|
|
15
15
|
const parser: SegmentParser = union([
|
|
16
|
-
some(
|
|
16
|
+
some(emptysegment, MAX_SEGMENT_SIZE + 1),
|
|
17
17
|
input => {
|
|
18
18
|
const { context: { source, position } } = input;
|
|
19
19
|
if (position === source.length) return;
|
|
@@ -26,21 +26,19 @@ const parser: SegmentParser = union([
|
|
|
26
26
|
break;
|
|
27
27
|
case '$':
|
|
28
28
|
if (source[position + 1] === '$') return mathblock(input);
|
|
29
|
-
|
|
29
|
+
return extension(input);
|
|
30
30
|
case '[':
|
|
31
31
|
if (source[position + 1] === '$') return extension(input);
|
|
32
32
|
break;
|
|
33
33
|
case '#':
|
|
34
34
|
return heading(input);
|
|
35
|
-
case '$':
|
|
36
|
-
return extension(input);
|
|
37
35
|
}
|
|
38
36
|
},
|
|
39
37
|
some(contentline, MAX_SEGMENT_SIZE + 1),
|
|
40
38
|
]) as any;
|
|
41
39
|
|
|
42
|
-
export function* segment(source: string): Generator<string, undefined, undefined> {
|
|
43
|
-
if (!validate(source, MAX_INPUT_SIZE)) return yield `${Command.Error}Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}
|
|
40
|
+
export function* segment(source: string): Generator<readonly [string, Segment], undefined, undefined> {
|
|
41
|
+
if (!validate(source, MAX_INPUT_SIZE)) return yield [`${Command.Error}Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`, Segment.unknown];
|
|
44
42
|
assert(source.length < Number.MAX_SAFE_INTEGER);
|
|
45
43
|
for (let position = 0; position < source.length;) {
|
|
46
44
|
const context = new Context({ source, position });
|
|
@@ -51,13 +49,13 @@ export function* segment(source: string): Generator<string, undefined, undefined
|
|
|
51
49
|
? result.foldl<string[]>((acc, { value }) => void acc.push(value) || acc, [])
|
|
52
50
|
: [source.slice(position, context.position)];
|
|
53
51
|
assert(segs.join('') === source.slice(position, context.position));
|
|
52
|
+
position = context.position;
|
|
54
53
|
for (let i = 0; i < segs.length; ++i) {
|
|
55
54
|
const seg = segs[i];
|
|
56
55
|
validate(seg, MAX_SEGMENT_SIZE)
|
|
57
|
-
? yield seg
|
|
58
|
-
: yield `${Command.Error}Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}
|
|
56
|
+
? yield [seg, context.segment]
|
|
57
|
+
: yield [`${Command.Error}Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`, Segment.unknown];
|
|
59
58
|
}
|
|
60
|
-
position = context.position;
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { AnyLineParser, EmptyLineParser, ContentLineParser } from '../source';
|
|
1
|
+
import { AnyLineParser, EmptyLineParser, EmptySegmentParser, ContentLineParser } from '../source';
|
|
2
|
+
import { Segment } from '../context';
|
|
2
3
|
import { List } from '../../combinator/data/parser';
|
|
3
4
|
|
|
4
5
|
export const anyline: AnyLineParser = input => {
|
|
@@ -14,14 +15,32 @@ export const emptyline: EmptyLineParser = input => {
|
|
|
14
15
|
const { context } = input;
|
|
15
16
|
const { source, position } = context;
|
|
16
17
|
if (position === source.length) return;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const i = eoel(source, position);
|
|
19
|
+
if (i === position) return;
|
|
20
|
+
context.position = i;
|
|
21
|
+
return new List();
|
|
22
|
+
};
|
|
23
|
+
export const emptysegment: EmptySegmentParser = input => {
|
|
24
|
+
const { context } = input;
|
|
25
|
+
const { source, position, segment } = context;
|
|
26
|
+
if (position === source.length) return;
|
|
27
|
+
if (segment & Segment.write) {
|
|
28
|
+
if (segment !== (Segment.empty | Segment.write)) return;
|
|
29
|
+
context.position = source.length;
|
|
30
|
+
return new List();
|
|
31
|
+
}
|
|
32
|
+
const i = eoel(source, position);
|
|
33
|
+
if (i === position) return;
|
|
22
34
|
context.position = i;
|
|
35
|
+
context.segment = Segment.empty;
|
|
23
36
|
return new List();
|
|
24
37
|
};
|
|
38
|
+
function eoel(source: string, position: number): number {
|
|
39
|
+
if (source[position] === '\n') return position + 1;
|
|
40
|
+
regEmptyline.lastIndex = position;
|
|
41
|
+
regEmptyline.test(source);
|
|
42
|
+
return regEmptyline.lastIndex || position;
|
|
43
|
+
}
|
|
25
44
|
|
|
26
45
|
const regContentline = /[^\S\n]*\S[^\n]*(?:$|\n)/y;
|
|
27
46
|
export const contentline: ContentLineParser = input => {
|