securemark 0.258.3 → 0.258.6
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 +12 -0
- package/dist/index.js +130 -78
- package/markdown.d.ts +11 -13
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +3 -1
- package/src/combinator/control/constraint/line.ts +6 -1
- package/src/combinator/control/manipulation/indent.ts +3 -0
- package/src/combinator/data/parser/context/delimiter.ts +6 -3
- package/src/combinator/data/parser/context/memo.ts +1 -1
- package/src/combinator/data/parser/context.test.ts +13 -13
- package/src/combinator/data/parser/context.ts +32 -23
- package/src/combinator/data/parser.ts +1 -1
- package/src/parser/autolink.test.ts +3 -2
- package/src/parser/autolink.ts +17 -1
- package/src/parser/block/extension/table.ts +1 -1
- package/src/parser/block/olist.ts +1 -1
- package/src/parser/block/paragraph.test.ts +3 -1
- package/src/parser/block/reply/quote.ts +2 -2
- package/src/parser/block/table.ts +1 -1
- package/src/parser/block/ulist.ts +3 -3
- package/src/parser/block.ts +1 -1
- package/src/parser/inline/annotation.ts +3 -3
- package/src/parser/inline/autolink/email.test.ts +3 -3
- package/src/parser/inline/autolink/url.test.ts +6 -6
- package/src/parser/inline/autolink.ts +4 -4
- package/src/parser/inline/extension/index.ts +2 -2
- package/src/parser/inline/extension/label.ts +2 -2
- package/src/parser/inline/html.test.ts +16 -16
- package/src/parser/inline/html.ts +9 -8
- package/src/parser/inline/link.ts +20 -7
- package/src/parser/inline/media.ts +2 -2
- package/src/parser/inline/reference.ts +3 -3
- package/src/parser/inline/shortmedia.ts +2 -2
- package/src/parser/inline/template.ts +1 -1
- package/src/parser/inline.test.ts +3 -3
- package/src/parser/source/text.test.ts +16 -1
- package/src/parser/source/text.ts +5 -4
- package/src/parser/visibility.ts +5 -5
|
@@ -21,7 +21,10 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
|
|
|
21
21
|
([indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
|
|
22
22
|
(lines, rest, context) => {
|
|
23
23
|
assert(parser = parser as Parser<T>);
|
|
24
|
+
const memo = context.memo;
|
|
25
|
+
memo && (memo.offset += rest.length);
|
|
24
26
|
const result = parser(trimBlockEnd(lines.join('')), context);
|
|
27
|
+
memo && (memo.offset -= rest.length);
|
|
25
28
|
return result && exec(result) === ''
|
|
26
29
|
? [eval(result), rest]
|
|
27
30
|
: undefined;
|
|
@@ -57,12 +57,15 @@ export class Delimiters {
|
|
|
57
57
|
public match(source: string, precedence = 1): boolean {
|
|
58
58
|
const { matchers } = this;
|
|
59
59
|
for (let i = 0; i < matchers.length; ++i) {
|
|
60
|
-
|
|
60
|
+
const matcher = matchers[i];
|
|
61
|
+
if (precedence >= matcher[2]) continue;
|
|
62
|
+
switch (matcher[3](source)) {
|
|
61
63
|
case true:
|
|
62
|
-
|
|
63
|
-
continue;
|
|
64
|
+
return true;
|
|
64
65
|
case false:
|
|
65
66
|
return false;
|
|
67
|
+
default:
|
|
68
|
+
continue;
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
return false;
|
|
@@ -10,27 +10,27 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
10
10
|
|
|
11
11
|
describe('reset', () => {
|
|
12
12
|
const parser: Parser<number> = some(creation(
|
|
13
|
-
(s, context) => [[context.resources?.
|
|
13
|
+
(s, context) => [[context.resources?.clock ?? NaN], s.slice(1)]));
|
|
14
14
|
|
|
15
15
|
it('root', () => {
|
|
16
|
-
const base: Context = { resources: {
|
|
16
|
+
const base: Context = { resources: { clock: 3, recursion: 1 } };
|
|
17
17
|
const ctx: Context = {};
|
|
18
18
|
assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
|
|
19
|
-
assert(base.resources?.
|
|
20
|
-
assert(ctx.resources?.
|
|
19
|
+
assert(base.resources?.clock === 3);
|
|
20
|
+
assert(ctx.resources?.clock === undefined);
|
|
21
21
|
assert.throws(() => reset(base, parser)('1234', ctx));
|
|
22
|
-
assert(ctx.resources?.
|
|
22
|
+
assert(ctx.resources?.clock === undefined);
|
|
23
23
|
assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it('node', () => {
|
|
27
|
-
const base: Context = { resources: {
|
|
28
|
-
const ctx: Context = { resources: {
|
|
27
|
+
const base: Context = { resources: { clock: 3, recursion: 1 } };
|
|
28
|
+
const ctx: Context = { resources: { clock: 1, recursion: 1 } };
|
|
29
29
|
assert.deepStrictEqual(reset(base, parser)('1', ctx), [[1], '']);
|
|
30
|
-
assert(base.resources?.
|
|
31
|
-
assert(ctx.resources?.
|
|
30
|
+
assert(base.resources?.clock === 3);
|
|
31
|
+
assert(ctx.resources?.clock === 0);
|
|
32
32
|
assert.throws(() => reset(base, parser)('1', ctx));
|
|
33
|
-
assert(ctx.resources?.
|
|
33
|
+
assert(ctx.resources?.clock === 0);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
});
|
|
@@ -41,12 +41,12 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
41
41
|
|
|
42
42
|
it('', () => {
|
|
43
43
|
const base: Context = { status: true };
|
|
44
|
-
const ctx: Context = { resources: {
|
|
44
|
+
const ctx: Context = { resources: { clock: 3, recursion: 1 } };
|
|
45
45
|
assert.deepStrictEqual(context(base, parser)('123', ctx), [[true, true, true], '']);
|
|
46
|
-
assert(ctx.resources?.
|
|
46
|
+
assert(ctx.resources?.clock === 0);
|
|
47
47
|
assert(ctx.status === undefined);
|
|
48
48
|
assert.throws(() => reset(base, parser)('1', ctx));
|
|
49
|
-
assert(ctx.resources?.
|
|
49
|
+
assert(ctx.resources?.clock === 0);
|
|
50
50
|
assert(ctx.status === undefined);
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -58,41 +58,31 @@ function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: [str
|
|
|
58
58
|
|
|
59
59
|
export function syntax<P extends Parser<unknown>>(syntax: number, precedence: number, cost: number, parser: P): P;
|
|
60
60
|
export function syntax<T>(syntax: number, precedence: number, cost: number, parser?: Parser<T>): Parser<T> {
|
|
61
|
-
return (source, context) => {
|
|
61
|
+
return creation(cost, (source, context) => {
|
|
62
62
|
if (source === '') return;
|
|
63
63
|
const memo = context.memo ??= new Memo();
|
|
64
64
|
context.memorable ??= ~0;
|
|
65
65
|
const p = context.precedence;
|
|
66
66
|
context.precedence = precedence;
|
|
67
|
-
const
|
|
68
|
-
if (resources.budget <= 0) throw new Error('Too many creations');
|
|
69
|
-
if (resources.recursion <= 0) throw new Error('Too much recursion');
|
|
70
|
-
--resources.recursion;
|
|
71
|
-
const pos = source.length;
|
|
67
|
+
const position = source.length;
|
|
72
68
|
const state = context.state ?? 0;
|
|
73
|
-
const cache = syntax && memo.get(
|
|
69
|
+
const cache = syntax && memo.get(position, syntax, state);
|
|
74
70
|
const result: Result<T> = cache
|
|
75
71
|
? cache.length === 0
|
|
76
72
|
? undefined
|
|
77
73
|
: [cache[0], source.slice(cache[1])]
|
|
78
74
|
: parser!(source, context);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
if (syntax && state & context.memorable!) {
|
|
76
|
+
cache ?? memo.set(position, syntax, state, eval(result), source.length - exec(result, '').length);
|
|
77
|
+
assert.deepStrictEqual(cache && cache, cache && memo.get(position, syntax, state));
|
|
82
78
|
}
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
assert.deepStrictEqual(cache && cache, cache && memo.get(pos, syntax, state));
|
|
87
|
-
}
|
|
88
|
-
else if (result && memo.length! >= pos) {
|
|
89
|
-
assert(!(state & context.memorable!));
|
|
90
|
-
memo.clear(pos);
|
|
91
|
-
}
|
|
79
|
+
if (result && !state && memo.length! >= position) {
|
|
80
|
+
assert(!(state & context.memorable!));
|
|
81
|
+
memo.clear(position);
|
|
92
82
|
}
|
|
93
83
|
context.precedence = p;
|
|
94
84
|
return result;
|
|
95
|
-
};
|
|
85
|
+
});
|
|
96
86
|
}
|
|
97
87
|
|
|
98
88
|
export function creation<P extends Parser<unknown>>(parser: P): P;
|
|
@@ -101,14 +91,14 @@ export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown
|
|
|
101
91
|
if (typeof cost === 'function') return creation(1, cost);
|
|
102
92
|
assert(cost >= 0);
|
|
103
93
|
return (source, context) => {
|
|
104
|
-
const { resources = {
|
|
105
|
-
if (resources.
|
|
94
|
+
const { resources = { clock: 1, recursion: 1 } } = context;
|
|
95
|
+
if (resources.clock <= 0) throw new Error('Too many creations');
|
|
106
96
|
if (resources.recursion <= 0) throw new Error('Too much recursion');
|
|
107
97
|
--resources.recursion;
|
|
108
98
|
const result = parser!(source, context);
|
|
109
99
|
++resources.recursion;
|
|
110
100
|
if (result) {
|
|
111
|
-
resources.
|
|
101
|
+
resources.clock -= cost;
|
|
112
102
|
}
|
|
113
103
|
return result;
|
|
114
104
|
};
|
|
@@ -133,6 +123,24 @@ export function guard<T>(f: (context: Ctx) => boolean | number, parser: Parser<T
|
|
|
133
123
|
: undefined;
|
|
134
124
|
}
|
|
135
125
|
|
|
126
|
+
export function constraint<P extends Parser<unknown>>(state: number, parser: P): P;
|
|
127
|
+
export function constraint<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
|
|
128
|
+
export function constraint<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
|
|
129
|
+
if (typeof positive === 'function') {
|
|
130
|
+
parser = positive;
|
|
131
|
+
positive = true;
|
|
132
|
+
}
|
|
133
|
+
assert(state);
|
|
134
|
+
return (source, context) => {
|
|
135
|
+
const s = positive
|
|
136
|
+
? state & context.state!
|
|
137
|
+
: state & ~context.state!;
|
|
138
|
+
return s === state
|
|
139
|
+
? parser!(source, context)
|
|
140
|
+
: undefined;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
136
144
|
export function state<P extends Parser<unknown>>(state: number, parser: P): P;
|
|
137
145
|
export function state<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
|
|
138
146
|
export function state<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
|
|
@@ -140,6 +148,7 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
|
|
|
140
148
|
parser = positive;
|
|
141
149
|
positive = true;
|
|
142
150
|
}
|
|
151
|
+
assert(state);
|
|
143
152
|
return (source, context) => {
|
|
144
153
|
const s = context.state ?? 0;
|
|
145
154
|
context.state = positive
|
|
@@ -13,13 +13,14 @@ describe('Unit: parser/autolink', () => {
|
|
|
13
13
|
assert.deepStrictEqual(inspect(parser('@a#b')), [['<a href="/@a?ch=b" class="channel">@a#b</a>'], '']);
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '<br>'], '']);
|
|
15
15
|
assert.deepStrictEqual(inspect(parser('a#b')), [['a#b'], '']);
|
|
16
|
-
assert.deepStrictEqual(inspect(parser('0a#b')), [['
|
|
16
|
+
assert.deepStrictEqual(inspect(parser('0a#b')), [['0a#b'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('あい#b')), [['あ', 'い#b'], '']);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0a
|
|
19
|
+
assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0aあ#b'], '']);
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('0aあい#b')), [['0a', 'あ', 'い#b'], '']);
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '\\', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
|
|
23
|
+
assert.deepStrictEqual(inspect(parser('0a>>b')), [['0a>>b'], '']);
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
});
|
package/src/parser/autolink.ts
CHANGED
|
@@ -5,7 +5,23 @@ import { linebreak, unescsource } from './source';
|
|
|
5
5
|
|
|
6
6
|
export import AutolinkParser = MarkdownParser.AutolinkParser;
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const delimiter = /[@#>0-9A-Za-z\n]|\S[#>]/;
|
|
9
|
+
|
|
10
|
+
export const autolink: AutolinkParser = (source, context) => {
|
|
11
|
+
if (source === '') return;
|
|
12
|
+
assert(source[0] !== '\x1B');
|
|
13
|
+
const i = source.search(delimiter);
|
|
14
|
+
switch (i) {
|
|
15
|
+
case -1:
|
|
16
|
+
return [[source], ''];
|
|
17
|
+
case 0:
|
|
18
|
+
return parser(source, context);
|
|
19
|
+
default:
|
|
20
|
+
return [[source.slice(0, i)], source.slice(i)];
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const parser: AutolinkParser = lazy(() => union([
|
|
9
25
|
autolink_,
|
|
10
26
|
linebreak,
|
|
11
27
|
unescsource
|
|
@@ -228,7 +228,7 @@ function format(rows: Tree<RowParser>[]): HTMLTableSectionElement[] {
|
|
|
228
228
|
: cells[j];
|
|
229
229
|
const isHeadCell = cell.tagName === 'TH';
|
|
230
230
|
heads |= BigInt(isHeadCell) << jn;
|
|
231
|
-
highlights |= BigInt(cell.
|
|
231
|
+
highlights |= BigInt(cell.className === 'highlight') << jn;
|
|
232
232
|
hasDataCell ||= !isHeadCell;
|
|
233
233
|
if (isHeadCell && !hasDataCell) {
|
|
234
234
|
lHeadCellIdx = jn;
|
|
@@ -115,7 +115,7 @@ function initial(type: string): RegExp {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
function format(el: HTMLOListElement, type: string, form: string): HTMLOListElement {
|
|
118
|
-
if (el.firstElementChild?.firstElementChild?.
|
|
118
|
+
if (el.firstElementChild?.firstElementChild?.className === 'checkbox') {
|
|
119
119
|
el.setAttribute('class', 'checklist');
|
|
120
120
|
}
|
|
121
121
|
define(el, {
|
|
@@ -45,13 +45,15 @@ describe('Unit: parser/block/paragraph', () => {
|
|
|
45
45
|
assert.deepStrictEqual(inspect(parser('>>11 a')), [['<p><a href="?at=11" class="anchor">>>11</a> a</p>'], '']);
|
|
46
46
|
assert.deepStrictEqual(inspect(parser('>>>11 a')), [['<p>><a href="?at=11" class="anchor">>>11</a> a</p>'], '']);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser('>> a\n>>1')), [['<p>>> a<br><a href="?at=1" class="anchor">>>1</a></p>'], '']);
|
|
48
|
-
assert.deepStrictEqual(inspect(parser('a>>1')), [['<p>a
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('a>>1')), [['<p>a>>1</p>'], '']);
|
|
49
|
+
assert.deepStrictEqual(inspect(parser('ab>>1')), [['<p>ab>>1</p>'], '']);
|
|
49
50
|
assert.deepStrictEqual(inspect(parser('a >>1')), [['<p>a <a href="?at=1" class="anchor">>>1</a></p>'], '']);
|
|
50
51
|
assert.deepStrictEqual(inspect(parser('a\n>>1')), [['<p>a<br><a href="?at=1" class="anchor">>>1</a></p>'], '']);
|
|
51
52
|
assert.deepStrictEqual(inspect(parser('a\n>>1\nb')), [['<p>a<br><a href="?at=1" class="anchor">>>1</a><br>b</p>'], '']);
|
|
52
53
|
assert.deepStrictEqual(inspect(parser('a\n>> b\nc')), [['<p>a<br>>> b<br>c</p>'], '']);
|
|
53
54
|
assert.deepStrictEqual(inspect(parser('\t>>1')), [['<p>\t<a href="?at=1" class="anchor">>>1</a></p>'], '']);
|
|
54
55
|
assert.deepStrictEqual(inspect(parser('\t>>>1')), [['<p>\t><a href="?at=1" class="anchor">>>1</a></p>'], '']);
|
|
56
|
+
assert.deepStrictEqual(inspect(parser('あ>>1')), [['<p>あ<a href="?at=1" class="anchor">>>1</a></p>'], '']);
|
|
55
57
|
});
|
|
56
58
|
|
|
57
59
|
it('comment', () => {
|
|
@@ -57,8 +57,8 @@ const qblock: ReplyParser.QuoteParser.BlockParser = (source, context) => {
|
|
|
57
57
|
++i;
|
|
58
58
|
continue;
|
|
59
59
|
}
|
|
60
|
-
if (child.
|
|
61
|
-
context.resources && (context.resources.
|
|
60
|
+
if (child.className === 'cite' || child.classList.contains('quote')) {
|
|
61
|
+
context.resources && (context.resources.clock -= child.childNodes.length);
|
|
62
62
|
nodes.splice(i, 1, ...child.childNodes as NodeListOf<HTMLElement>);
|
|
63
63
|
--i;
|
|
64
64
|
continue;
|
|
@@ -61,7 +61,7 @@ const data: CellParser.DataParser = creation(fmap(
|
|
|
61
61
|
ns => [html('td', trimNode(defrag(ns)))]));
|
|
62
62
|
|
|
63
63
|
function format(rows: HTMLTableRowElement[]): HTMLTableRowElement[] {
|
|
64
|
-
const aligns = rows[0].
|
|
64
|
+
const aligns = rows[0].className === 'invalid'
|
|
65
65
|
? []
|
|
66
66
|
: duffReduce(rows.shift()!.children, (acc, el) => push(acc, [el.textContent!]), [] as string[]);
|
|
67
67
|
for (let i = 0; i < rows.length; ++i) {
|
|
@@ -26,11 +26,11 @@ export const ulist_: UListParser = lazy(() => block(fmap(validate(
|
|
|
26
26
|
])))),
|
|
27
27
|
es => [format(html('ul', es))])));
|
|
28
28
|
|
|
29
|
-
export const checkbox = focus(
|
|
29
|
+
export const checkbox = creation(focus(
|
|
30
30
|
/^\[[xX ]\](?=$|\s)/,
|
|
31
31
|
source => [[
|
|
32
32
|
html('span', { class: 'checkbox' }, source[1].trimStart() ? '☑' : '☐'),
|
|
33
|
-
], '']);
|
|
33
|
+
], '']));
|
|
34
34
|
|
|
35
35
|
export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | string)[] {
|
|
36
36
|
return ns.length === 1
|
|
@@ -41,7 +41,7 @@ export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | stri
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function format(el: HTMLUListElement): HTMLUListElement {
|
|
44
|
-
if (el.firstElementChild?.firstElementChild?.
|
|
44
|
+
if (el.firstElementChild?.firstElementChild?.className === 'checkbox') {
|
|
45
45
|
el.setAttribute('class', 'checklist');
|
|
46
46
|
}
|
|
47
47
|
return el;
|
package/src/parser/block.ts
CHANGED
|
@@ -36,7 +36,7 @@ export import ReplyParser = BlockParser.ReplyParser;
|
|
|
36
36
|
export import ParagraphParser = BlockParser.ParagraphParser;
|
|
37
37
|
|
|
38
38
|
export const block: BlockParser = creation(error(
|
|
39
|
-
reset({ resources: {
|
|
39
|
+
reset({ resources: { clock: 50 * 1000, recursion: 20 } },
|
|
40
40
|
union([
|
|
41
41
|
emptyline,
|
|
42
42
|
horizontalrule,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { AnnotationParser } from '../inline';
|
|
3
|
-
import { union, some,
|
|
3
|
+
import { union, some, context, syntax, constraint, state, surround, lazy } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
5
|
import { optimize } from './link';
|
|
6
6
|
import { Syntax, State } from '../context';
|
|
@@ -9,7 +9,7 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
9
9
|
|
|
10
10
|
export const annotation: AnnotationParser = lazy(() => surround(
|
|
11
11
|
'((',
|
|
12
|
-
|
|
12
|
+
constraint(State.annotation, false,
|
|
13
13
|
syntax(Syntax.annotation, 6, 1,
|
|
14
14
|
state(State.annotation | State.media,
|
|
15
15
|
startLoose(
|
|
@@ -18,4 +18,4 @@ export const annotation: AnnotationParser = lazy(() => surround(
|
|
|
18
18
|
'))',
|
|
19
19
|
false,
|
|
20
20
|
([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNode(defrag(ns)))])], rest],
|
|
21
|
-
([, ns, rest], next) => next[0] === ')' ? undefined : optimize('((', ns, rest, next)));
|
|
21
|
+
([, ns, rest], next, context) => next[0] === ')' ? undefined : optimize('((', ns, rest, next, context)));
|
|
@@ -20,9 +20,9 @@ describe('Unit: parser/inline/autolink/email', () => {
|
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('a@b#1')), [['a@b#1'], '']);
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('a@@')), [['a@@'], '']);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('a@@b')), [['a@@b'], '']);
|
|
23
|
-
assert.deepStrictEqual(inspect(parser('a+@b')),
|
|
24
|
-
assert.deepStrictEqual(inspect(parser('a..b@c')),
|
|
25
|
-
assert.deepStrictEqual(inspect(parser('a++b@c')),
|
|
23
|
+
assert.deepStrictEqual(inspect(parser('a+@b')), [['a'], '+@b']);
|
|
24
|
+
assert.deepStrictEqual(inspect(parser('a..b@c')), [['a'], '..b@c']);
|
|
25
|
+
assert.deepStrictEqual(inspect(parser('a++b@c')), [['a'], '++b@c']);
|
|
26
26
|
assert.deepStrictEqual(inspect(parser(`a@${'b'.repeat(64)}`)), [[`a@${'b'.repeat(64)}`], '']);
|
|
27
27
|
assert.deepStrictEqual(inspect(parser(' a@b')), undefined);
|
|
28
28
|
});
|
|
@@ -8,12 +8,12 @@ describe('Unit: parser/inline/autolink/url', () => {
|
|
|
8
8
|
|
|
9
9
|
it('invalid', () => {
|
|
10
10
|
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
11
|
-
assert.deepStrictEqual(inspect(parser('http')),
|
|
12
|
-
assert.deepStrictEqual(inspect(parser('ttp')),
|
|
13
|
-
assert.deepStrictEqual(inspect(parser('http://')),
|
|
14
|
-
assert.deepStrictEqual(inspect(parser('http://[')),
|
|
15
|
-
assert.deepStrictEqual(inspect(parser('http://]')),
|
|
16
|
-
assert.deepStrictEqual(inspect(parser('Http://host')),
|
|
11
|
+
assert.deepStrictEqual(inspect(parser('http')), [['http'], '']);
|
|
12
|
+
assert.deepStrictEqual(inspect(parser('ttp')), [['ttp'], '']);
|
|
13
|
+
assert.deepStrictEqual(inspect(parser('http://')), [['http'], '://']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser('http://[')), [['http'], '://[']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('http://]')), [['http'], '://]']);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser('Http://host')), [['Http'], '://host']);
|
|
17
17
|
//assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0%1]')), [['<a class="invalid">http://[::ffff:0:0%1]</a>'], '']);
|
|
18
18
|
//assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0/96]')), [['<a class="invalid">http://[::ffff:0:0/96]</a>'], '']);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser(' http://a')), undefined);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutolinkParser } from '../inline';
|
|
2
|
-
import { union, some, syntax,
|
|
2
|
+
import { union, some, syntax, constraint, validate, fmap } from '../../combinator';
|
|
3
3
|
import { url } from './autolink/url';
|
|
4
4
|
import { email } from './autolink/email';
|
|
5
5
|
import { channel } from './autolink/channel';
|
|
@@ -12,14 +12,14 @@ import { Syntax, State } from '../context';
|
|
|
12
12
|
import { stringify } from '../util';
|
|
13
13
|
|
|
14
14
|
export const autolink: AutolinkParser = fmap(
|
|
15
|
-
validate(/^(?:[@#>0-9A-Za-z]|\S
|
|
16
|
-
|
|
15
|
+
validate(/^(?:[@#>0-9A-Za-z]|\S[#>])/,
|
|
16
|
+
constraint(State.autolink, false,
|
|
17
17
|
syntax(Syntax.autolink, 1, 1,
|
|
18
18
|
some(union([
|
|
19
19
|
url,
|
|
20
20
|
email,
|
|
21
21
|
// Escape unmatched email-like strings.
|
|
22
|
-
str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)
|
|
22
|
+
str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)*/),
|
|
23
23
|
channel,
|
|
24
24
|
account,
|
|
25
25
|
// Escape unmatched account-like strings.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { ExtensionParser } from '../../inline';
|
|
3
|
-
import { union, some, syntax, creation, precedence,
|
|
3
|
+
import { union, some, syntax, creation, precedence, constraint, state, validate, surround, open, lazy, fmap } from '../../../combinator';
|
|
4
4
|
import { inline } from '../../inline';
|
|
5
5
|
import { indexee, identity } from './indexee';
|
|
6
6
|
import { txt, str, stropt } from '../../source';
|
|
@@ -12,7 +12,7 @@ import IndexParser = ExtensionParser.IndexParser;
|
|
|
12
12
|
|
|
13
13
|
export const index: IndexParser = lazy(() => validate('[#', fmap(indexee(surround(
|
|
14
14
|
'[#',
|
|
15
|
-
|
|
15
|
+
constraint(State.index, false,
|
|
16
16
|
syntax(Syntax.index, 2, 1,
|
|
17
17
|
state(State.annotation | State.reference | State.index | State.label | State.link | State.media | State.autolink,
|
|
18
18
|
startTight(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Array } from 'spica/global';
|
|
2
2
|
import { ExtensionParser } from '../../inline';
|
|
3
|
-
import { union,
|
|
3
|
+
import { union, constraint, creation, validate, surround, clear, fmap } from '../../../combinator';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { State } from '../../context';
|
|
6
6
|
import { html } from 'typed-dom/dom';
|
|
@@ -13,7 +13,7 @@ export const segment: ExtensionParser.LabelParser.SegmentParser = clear(validate
|
|
|
13
13
|
])));
|
|
14
14
|
|
|
15
15
|
export const label: ExtensionParser.LabelParser = validate(['[$', '$'], creation(fmap(
|
|
16
|
-
|
|
16
|
+
constraint(State.label, false,
|
|
17
17
|
union([
|
|
18
18
|
surround('[', body, ']'),
|
|
19
19
|
body,
|
|
@@ -21,10 +21,10 @@ describe('Unit: parser/inline/html', () => {
|
|
|
21
21
|
it('invalid', () => {
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
23
23
|
assert.deepStrictEqual(inspect(parser('<0>')), undefined);
|
|
24
|
-
assert.deepStrictEqual(inspect(parser('<aT>')),
|
|
24
|
+
assert.deepStrictEqual(inspect(parser('<aT>')), [['<span class="invalid"><aT></span>'], '']);
|
|
25
25
|
assert.deepStrictEqual(inspect(parser('<a,b>')), undefined);
|
|
26
26
|
assert.deepStrictEqual(inspect(parser('<a, b>')), undefined);
|
|
27
|
-
assert.deepStrictEqual(inspect(parser('<T>')),
|
|
27
|
+
assert.deepStrictEqual(inspect(parser('<T>')), [['<span class="invalid"><T></span>'], '']);
|
|
28
28
|
assert.deepStrictEqual(inspect(parser('<bdi>')), [['<span class="invalid"><bdi></span>'], '']);
|
|
29
29
|
assert.deepStrictEqual(inspect(parser('<bdi>z')), [['<span class="invalid"><bdi>z</span>'], '']);
|
|
30
30
|
assert.deepStrictEqual(inspect(parser('<bdi></bdi>')), [['<span class="invalid"><bdi></bdi></span>'], '']);
|
|
@@ -39,8 +39,8 @@ describe('Unit: parser/inline/html', () => {
|
|
|
39
39
|
assert.deepStrictEqual(inspect(parser('<bdi>\\\na</bdi>')), [['<span class="invalid"><bdi><span class="linebreak"> </span>a</bdi></span>'], '']);
|
|
40
40
|
assert.deepStrictEqual(inspect(parser('<bdi>a')), [['<span class="invalid"><bdi>a</span>'], '']);
|
|
41
41
|
assert.deepStrictEqual(inspect(parser('<bdi>a</BDO>')), [['<span class="invalid"><bdi>a</BDO></span>'], '']);
|
|
42
|
-
assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')),
|
|
43
|
-
assert.deepStrictEqual(inspect(parser('<BDI>a</bdo>')),
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')), [['<span class="invalid"><BDI>a</BDI></span>'], '']);
|
|
43
|
+
assert.deepStrictEqual(inspect(parser('<BDI>a</bdo>')), [['<span class="invalid"><BDI>a</bdo></span>'], '']);
|
|
44
44
|
assert.deepStrictEqual(inspect(parser('</bdi>')), undefined);
|
|
45
45
|
assert.deepStrictEqual(inspect(parser('<bdi/>')), undefined);
|
|
46
46
|
assert.deepStrictEqual(inspect(parser('<b><b><b>a</b></b></b>')), [['<span class="invalid"><b><span class="invalid"><b><span class="invalid"><b>a</b></span></b></span></b></span>'], '']);
|
|
@@ -87,9 +87,9 @@ describe('Unit: parser/inline/html', () => {
|
|
|
87
87
|
assert.deepStrictEqual(inspect(parser('<a>')), [['<span class="invalid"><a></span>'], '']);
|
|
88
88
|
assert.deepStrictEqual(inspect(parser('<bdi><a>a</a></bdi>')), [['<bdi><span class="invalid"><a>a</a></span></bdi>'], '']);
|
|
89
89
|
assert.deepStrictEqual(inspect(parser('<bdi>a<a>b</a>c</bdi>')), [['<bdi>a<span class="invalid"><a>b</a></span>c</bdi>'], '']);
|
|
90
|
-
assert.deepStrictEqual(inspect(parser('<img>')), [['<img'], '
|
|
91
|
-
assert.deepStrictEqual(inspect(parser('<bdi><img></bdi>')), [['<bdi><img></bdi>'], '']);
|
|
92
|
-
assert.deepStrictEqual(inspect(parser('<img />')),
|
|
90
|
+
assert.deepStrictEqual(inspect(parser('<img>')), [['<span class="invalid"><img></span>'], '']);
|
|
91
|
+
assert.deepStrictEqual(inspect(parser('<bdi><img></bdi>')), [['<bdi><span class="invalid"><img></span></bdi>'], '']);
|
|
92
|
+
assert.deepStrictEqual(inspect(parser('<img />')), undefined);
|
|
93
93
|
assert.deepStrictEqual(inspect(parser('<bdi><img /></bdi>')), [['<bdi><img /></bdi>'], '']);
|
|
94
94
|
});
|
|
95
95
|
|
|
@@ -100,15 +100,15 @@ describe('Unit: parser/inline/html', () => {
|
|
|
100
100
|
assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>')), [['<bdi>a</bdi>'], '']);
|
|
101
101
|
assert.deepStrictEqual(inspect(parser('<bdi __proto__>a</bdi>')), undefined);
|
|
102
102
|
assert.deepStrictEqual(inspect(parser('<bdi constructor>a</bdi>')), [['<span class="invalid"><bdi constructor>a</bdi></span>'], '']);
|
|
103
|
-
assert.deepStrictEqual(inspect(parser('<bdi toString>a</bdi>')),
|
|
104
|
-
assert.deepStrictEqual(inspect(parser('<bdi X>a</bdi>')),
|
|
103
|
+
assert.deepStrictEqual(inspect(parser('<bdi toString>a</bdi>')), [['<span class="invalid"><bdi toString>a</bdi></span>'], '']);
|
|
104
|
+
assert.deepStrictEqual(inspect(parser('<bdi X>a</bdi>')), [['<span class="invalid"><bdi X>a</bdi></span>'], '']);
|
|
105
105
|
assert.deepStrictEqual(inspect(parser('<bdi x>a</bdi>')), [['<span class="invalid"><bdi x>a</bdi></span>'], '']);
|
|
106
106
|
assert.deepStrictEqual(inspect(parser('<bdo>a</bdo>')), [['<span class="invalid"><bdo>a</bdo></span>'], '']);
|
|
107
107
|
assert.deepStrictEqual(inspect(parser('<bdo >a</bdo>')), [['<span class="invalid"><bdo >a</bdo></span>'], '']);
|
|
108
108
|
assert.deepStrictEqual(inspect(parser('<bdo __proto__>a</bdo>')), undefined);
|
|
109
109
|
assert.deepStrictEqual(inspect(parser('<bdo constructor>a</bdo>')), [['<span class="invalid"><bdo constructor>a</bdo></span>'], '']);
|
|
110
|
-
assert.deepStrictEqual(inspect(parser('<bdo toString>a</bdo>')),
|
|
111
|
-
assert.deepStrictEqual(inspect(parser('<bdo X>a</bdo>')),
|
|
110
|
+
assert.deepStrictEqual(inspect(parser('<bdo toString>a</bdo>')), [['<span class="invalid"><bdo toString>a</bdo></span>'], '']);
|
|
111
|
+
assert.deepStrictEqual(inspect(parser('<bdo X>a</bdo>')), [['<span class="invalid"><bdo X>a</bdo></span>'], '']);
|
|
112
112
|
assert.deepStrictEqual(inspect(parser('<bdo x>a</bdo>')), [['<span class="invalid"><bdo x>a</bdo></span>'], '']);
|
|
113
113
|
assert.deepStrictEqual(inspect(parser('<bdo dir>a</bdo>')), [['<span class="invalid"><bdo dir>a</bdo></span>'], '']);
|
|
114
114
|
assert.deepStrictEqual(inspect(parser('<bdo dir=>a</bdo>')), undefined);
|
|
@@ -116,16 +116,16 @@ describe('Unit: parser/inline/html', () => {
|
|
|
116
116
|
assert.deepStrictEqual(inspect(parser('<bdo dir=">a</bdo>')), undefined);
|
|
117
117
|
assert.deepStrictEqual(inspect(parser('<bdo dir="">a</bdo>')), [['<span class="invalid"><bdo dir="">a</bdo></span>'], '']);
|
|
118
118
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" dir="rtl">a</bdo>')), [['<span class="invalid"><bdo dir="rtl" dir="rtl">a</bdo></span>'], '']);
|
|
119
|
-
assert.deepStrictEqual(inspect(parser('<bdo diR="rtl">a</bdo>')),
|
|
119
|
+
assert.deepStrictEqual(inspect(parser('<bdo diR="rtl">a</bdo>')), [['<span class="invalid"><bdo diR="rtl">a</bdo></span>'], '']);
|
|
120
120
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
|
|
121
121
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
|
|
122
122
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
|
|
123
123
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
|
|
124
124
|
assert.deepStrictEqual(inspect(parser('<wbr\n>')), undefined);
|
|
125
|
-
assert.deepStrictEqual(inspect(parser('<wbr >')), [['<wbr'], '
|
|
126
|
-
assert.deepStrictEqual(inspect(parser('<wbr constructor>')), [['<wbr'], '
|
|
127
|
-
assert.deepStrictEqual(inspect(parser('<wbr X>')), [['<wbr'], '
|
|
128
|
-
assert.deepStrictEqual(inspect(parser('<wbr x>')), [['<wbr'], '
|
|
125
|
+
assert.deepStrictEqual(inspect(parser('<wbr >')), [['<wbr>'], '']);
|
|
126
|
+
assert.deepStrictEqual(inspect(parser('<wbr constructor>')), [['<span class="invalid"><wbr constructor></span>'], '']);
|
|
127
|
+
assert.deepStrictEqual(inspect(parser('<wbr X>')), [['<span class="invalid"><wbr X></span>'], '']);
|
|
128
|
+
assert.deepStrictEqual(inspect(parser('<wbr x>')), [['<span class="invalid"><wbr x></span>'], '']);
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
});
|
|
@@ -19,14 +19,15 @@ const attrspecs = {
|
|
|
19
19
|
Object.setPrototypeOf(attrspecs, null);
|
|
20
20
|
Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
|
|
21
21
|
|
|
22
|
-
export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)
|
|
22
|
+
export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/i, syntax(Syntax.none, 5, 1, union([
|
|
23
23
|
focus(
|
|
24
|
-
|
|
24
|
+
/^<wbr[^\S\n]*>/i,
|
|
25
25
|
() => [[h('wbr')], '']),
|
|
26
|
-
|
|
26
|
+
surround(
|
|
27
27
|
// https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
28
|
-
/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)
|
|
29
|
-
|
|
28
|
+
str(/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/i), some(union([attribute])), str(/^[^\S\n]*>/), true,
|
|
29
|
+
([as, bs = [], cs], rest) =>
|
|
30
|
+
[[elem(as[0].slice(1), push(unshift(as, bs), cs), [], [])], rest]),
|
|
30
31
|
match(
|
|
31
32
|
new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`),
|
|
32
33
|
memoize(
|
|
@@ -44,7 +45,7 @@ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^
|
|
|
44
45
|
[[elem(tag, as, bs, [])], rest]),
|
|
45
46
|
([, tag]) => TAGS.indexOf(tag), [])),
|
|
46
47
|
match(
|
|
47
|
-
/^<([a-z]+)(?=[^\S\n]|>)
|
|
48
|
+
/^<([a-z]+)(?=[^\S\n]|>)/i,
|
|
48
49
|
memoize(
|
|
49
50
|
([, tag]) =>
|
|
50
51
|
surround<HTMLParser.TagParser, string>(surround(
|
|
@@ -62,8 +63,8 @@ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^
|
|
|
62
63
|
new Cache(10000))),
|
|
63
64
|
])))));
|
|
64
65
|
|
|
65
|
-
export const attribute: HTMLParser.
|
|
66
|
-
str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/),
|
|
66
|
+
export const attribute: HTMLParser.AttributeParser = union([
|
|
67
|
+
str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/i),
|
|
67
68
|
]);
|
|
68
69
|
|
|
69
70
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|