securemark 0.293.2 → 0.293.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +7 -10
- package/dist/index.js +323 -132
- package/package.json +1 -1
- package/src/combinator/control/manipulation/indent.test.ts +6 -1
- package/src/combinator/control/manipulation/indent.ts +1 -1
- package/src/combinator/control/manipulation/surround.ts +2 -1
- package/src/combinator/data/parser/context/delimiter.ts +7 -12
- package/src/combinator/data/parser/some.ts +4 -8
- package/src/parser/block/olist.test.ts +8 -6
- package/src/parser/block/olist.ts +2 -2
- package/src/parser/block.ts +38 -36
- package/src/parser/inline/annotation.test.ts +1 -1
- package/src/parser/inline/bracket.test.ts +4 -2
- package/src/parser/inline/bracket.ts +114 -88
- package/src/parser/inline/html.ts +24 -13
- package/src/parser/inline/link.ts +1 -1
- package/src/parser/inline/math.ts +2 -2
- package/src/parser/inline/media.ts +3 -2
- package/src/parser/inline/reference.test.ts +1 -1
- package/src/parser/inline/remark.ts +2 -2
- package/src/parser/inline/template.ts +1 -1
- package/src/parser/inline.test.ts +16 -16
- package/src/parser/inline.ts +46 -47
- package/src/parser/segment.ts +12 -12
- package/src/parser/source/escapable.ts +5 -3
- package/src/parser/source/text.ts +161 -14
- package/src/parser/source/unescapable.ts +5 -3
package/package.json
CHANGED
|
@@ -12,12 +12,17 @@ describe('Unit: combinator/indent', () => {
|
|
|
12
12
|
assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
|
|
13
13
|
assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
|
|
14
14
|
assert.deepStrictEqual(inspect(parser(input('a ', ctx)), ctx), undefined);
|
|
15
|
-
assert.deepStrictEqual(inspect(parser(input(' a
|
|
15
|
+
assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser(input(' a ', ctx)), ctx), [['a '], '']);
|
|
17
|
+
assert.deepStrictEqual(inspect(parser(input(' a\n', ctx)), ctx), [['a'], '']);
|
|
17
18
|
assert.deepStrictEqual(inspect(parser(input(' a \n', ctx)), ctx), [['a '], '']);
|
|
18
19
|
assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
|
|
20
|
+
assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
|
|
21
|
+
assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
|
|
22
|
+
assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [[' a'], '']);
|
|
19
23
|
assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\na'], '']);
|
|
20
24
|
assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\n a'], '']);
|
|
25
|
+
assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\n a'], '']);
|
|
21
26
|
assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a'], ' a']);
|
|
22
27
|
assert.deepStrictEqual(inspect(parser(input(' \ta', ctx)), ctx), [['\ta'], '']);
|
|
23
28
|
assert.deepStrictEqual(inspect(parser(input('\ta', ctx)), ctx), [['a'], '']);
|
|
@@ -13,7 +13,7 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
|
|
|
13
13
|
if (typeof opener === 'function') {
|
|
14
14
|
separation = parser as boolean;
|
|
15
15
|
parser = opener;
|
|
16
|
-
opener = /
|
|
16
|
+
opener = / {1,4}|\t{1,2}/y;
|
|
17
17
|
}
|
|
18
18
|
assert(!opener.flags.match(/[gm]/) && opener.sticky && !opener.source.startsWith('^'));
|
|
19
19
|
assert(parser);
|
|
@@ -157,7 +157,8 @@ export function setBacktrack(
|
|
|
157
157
|
position: number,
|
|
158
158
|
length: number = 1,
|
|
159
159
|
): void {
|
|
160
|
-
const { source } = context;
|
|
160
|
+
const { source, state = 0 } = context;
|
|
161
|
+
if (state === 0) return;
|
|
161
162
|
if (position === source.length) return;
|
|
162
163
|
if (length === 0) return;
|
|
163
164
|
for (const backtrack of backtracks) {
|
|
@@ -7,26 +7,24 @@ interface Delimiter {
|
|
|
7
7
|
readonly signature: number | string;
|
|
8
8
|
readonly matcher: (input: Input) => boolean | undefined;
|
|
9
9
|
readonly precedence: number;
|
|
10
|
-
readonly linebreakable: boolean;
|
|
11
10
|
state: boolean;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
export class Delimiters {
|
|
15
14
|
// 手間を惜しまなければ規定のパターンはすべて配列のインデクスに変換可能。
|
|
16
|
-
public static signature(pattern: string | RegExp | undefined
|
|
15
|
+
public static signature(pattern: string | RegExp | undefined): number | string {
|
|
17
16
|
switch (typeof pattern) {
|
|
18
17
|
case 'undefined':
|
|
19
|
-
return
|
|
18
|
+
return 1 << 7;
|
|
20
19
|
case 'string':
|
|
21
20
|
assert(pattern !== '\x00');
|
|
22
21
|
if (pattern.length === 1) {
|
|
23
22
|
const code = pattern.charCodeAt(0);
|
|
24
|
-
|
|
25
|
-
if ((code & 1 << 7) === 0) return code | +linebreakable << 7;
|
|
23
|
+
return code;
|
|
26
24
|
}
|
|
27
|
-
return `s:${pattern}
|
|
25
|
+
return `s:${pattern}`;
|
|
28
26
|
case 'object':
|
|
29
|
-
return `r/${pattern.source}
|
|
27
|
+
return `r/${pattern.source}`;
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
30
|
public static matcher(pattern: string | RegExp | undefined): (input: Input<Ctx>) => true | undefined {
|
|
@@ -61,14 +59,13 @@ export class Delimiters {
|
|
|
61
59
|
readonly signature: number | string;
|
|
62
60
|
readonly matcher: (input: Input) => boolean | undefined;
|
|
63
61
|
readonly precedence: number;
|
|
64
|
-
readonly linebreakable: boolean;
|
|
65
62
|
}[]
|
|
66
63
|
): void {
|
|
67
64
|
const { delimiters, stack } = this;
|
|
68
65
|
// シグネチャ数以下
|
|
69
66
|
assert(delimiters.length < 100);
|
|
70
67
|
for (let i = 0; i < delims.length; ++i) {
|
|
71
|
-
const { signature, matcher, precedence
|
|
68
|
+
const { signature, matcher, precedence } = delims[i];
|
|
72
69
|
const memory = this.registry(signature);
|
|
73
70
|
const index = memory[0]?.index ?? delimiters.length;
|
|
74
71
|
assert(memory.length === 0 || precedence === delimiters[index].precedence);
|
|
@@ -79,7 +76,6 @@ export class Delimiters {
|
|
|
79
76
|
signature,
|
|
80
77
|
matcher,
|
|
81
78
|
precedence,
|
|
82
|
-
linebreakable,
|
|
83
79
|
state: true,
|
|
84
80
|
};
|
|
85
81
|
delimiters[index] = delimiter;
|
|
@@ -134,14 +130,13 @@ export class Delimiters {
|
|
|
134
130
|
}
|
|
135
131
|
}
|
|
136
132
|
public match(input: Input): boolean {
|
|
137
|
-
const { precedence = 0
|
|
133
|
+
const { precedence = 0 } = input.context;
|
|
138
134
|
const { delimiters } = this;
|
|
139
135
|
for (let i = delimiters.length; i--;) {
|
|
140
136
|
const delimiter = delimiters[i];
|
|
141
137
|
if (delimiter.precedence <= precedence || !delimiter.state) continue;
|
|
142
138
|
switch (delimiter.matcher(input)) {
|
|
143
139
|
case true:
|
|
144
|
-
if (!delimiter.linebreakable && linebreak > 0) return false;
|
|
145
140
|
return true;
|
|
146
141
|
case false:
|
|
147
142
|
return false;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Parser, eval } from '../parser';
|
|
2
2
|
import { Delimiters } from './context/delimiter';
|
|
3
|
-
import { unshift, push } from 'spica/array';
|
|
4
3
|
|
|
5
|
-
type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number
|
|
4
|
+
type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
|
|
6
5
|
|
|
7
6
|
export function some<P extends Parser<unknown>>(parser: P, limit?: number): P;
|
|
8
7
|
export function some<P extends Parser<unknown>>(parser: P, end?: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
|
|
@@ -10,11 +9,10 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
10
9
|
if (typeof end === 'number') return some(parser, undefined, delimiters, end);
|
|
11
10
|
assert(parser);
|
|
12
11
|
const match = Delimiters.matcher(end);
|
|
13
|
-
const delims = delimiters.map(([delimiter, precedence
|
|
14
|
-
signature: Delimiters.signature(delimiter
|
|
12
|
+
const delims = delimiters.map(([delimiter, precedence]) => ({
|
|
13
|
+
signature: Delimiters.signature(delimiter),
|
|
15
14
|
matcher: Delimiters.matcher(delimiter),
|
|
16
15
|
precedence,
|
|
17
|
-
linebreakable,
|
|
18
16
|
}));
|
|
19
17
|
return input => {
|
|
20
18
|
const { context } = input;
|
|
@@ -32,9 +30,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
|
|
|
32
30
|
const result = parser(input);
|
|
33
31
|
if (result === undefined) break;
|
|
34
32
|
nodes = nodes
|
|
35
|
-
? nodes.
|
|
36
|
-
? unshift(nodes, eval(result))
|
|
37
|
-
: push(nodes, eval(result))
|
|
33
|
+
? (nodes.push(...eval(result)), nodes)
|
|
38
34
|
: eval(result);
|
|
39
35
|
if (limit >= 0 && context.position - position > limit) break;
|
|
40
36
|
}
|
|
@@ -32,6 +32,8 @@ describe('Unit: parser/block/olist', () => {
|
|
|
32
32
|
assert.deepStrictEqual(inspect(parser('(1)'), ctx), undefined);
|
|
33
33
|
assert.deepStrictEqual(inspect(parser('(1)\n'), ctx), undefined);
|
|
34
34
|
assert.deepStrictEqual(inspect(parser('(1)\n(1) a'), ctx), undefined);
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('I. '), ctx), undefined);
|
|
36
|
+
assert.deepStrictEqual(inspect(parser('A. '), ctx), undefined);
|
|
35
37
|
assert.deepStrictEqual(inspect(parser('(I) '), ctx), undefined);
|
|
36
38
|
assert.deepStrictEqual(inspect(parser('(A) '), ctx), undefined);
|
|
37
39
|
assert.deepStrictEqual(inspect(parser(' 1. '), ctx), undefined);
|
|
@@ -117,12 +119,12 @@ describe('Unit: parser/block/olist', () => {
|
|
|
117
119
|
});
|
|
118
120
|
|
|
119
121
|
it('type', () => {
|
|
120
|
-
assert.deepStrictEqual(inspect(parser('i. '), ctx), [['<ol type="i" data-type="lower-roman"><li></li></ol>'], '']);
|
|
121
|
-
assert.deepStrictEqual(inspect(parser('a. '), ctx), [['<ol type="a" data-type="lower-alpha"><li></li></ol>'], '']);
|
|
122
|
-
assert.deepStrictEqual(inspect(parser('I. '), ctx), [['<ol type="I" data-type="upper-roman"><li></li></ol>'], '']);
|
|
123
|
-
assert.deepStrictEqual(inspect(parser('A. '), ctx), [['<ol type="A" data-type="upper-alpha"><li></li></ol>'], '']);
|
|
124
|
-
assert.deepStrictEqual(inspect(parser('a. \
|
|
125
|
-
assert.deepStrictEqual(inspect(parser('i. 1'), ctx), [['<ol type="i" data-type="lower-roman"><li id="index::1">1</li></ol>'], '']);
|
|
122
|
+
assert.deepStrictEqual(inspect(parser('1. \n i. '), ctx), [['<ol><li><br><ol type="i" data-type="lower-roman"><li></li></ol></li></ol>'], '']);
|
|
123
|
+
assert.deepStrictEqual(inspect(parser('1. \n a. '), ctx), [['<ol><li><br><ol type="a" data-type="lower-alpha"><li></li></ol></li></ol>'], '']);
|
|
124
|
+
assert.deepStrictEqual(inspect(parser('1. \n I. '), ctx), [['<ol><li><br><ol type="I" data-type="upper-roman"><li></li></ol></li></ol>'], '']);
|
|
125
|
+
assert.deepStrictEqual(inspect(parser('1. \n A. '), ctx), [['<ol><li><br><ol type="A" data-type="upper-alpha"><li></li></ol></li></ol>'], '']);
|
|
126
|
+
assert.deepStrictEqual(inspect(parser('1. \n a. \n 1.\n c'), ctx), [['<ol><li><br><ol type="a" data-type="lower-alpha"><li></li><li data-marker="1."></li><li data-marker="c."></li></ol></li></ol>'], '']);
|
|
127
|
+
assert.deepStrictEqual(inspect(parser('1. \n i. 1'), ctx), [['<ol><li><br><ol type="i" data-type="lower-roman"><li id="index::1">1</li></ol></li></ol>'], '']);
|
|
126
128
|
});
|
|
127
129
|
|
|
128
130
|
it('checkbox', () => {
|
|
@@ -16,8 +16,8 @@ const openers = {
|
|
|
16
16
|
|
|
17
17
|
export const olist: OListParser = lazy(() => block(validate(
|
|
18
18
|
new RegExp([
|
|
19
|
-
/([0-9]
|
|
20
|
-
/\(([0-9]
|
|
19
|
+
/(?:[0-9]+)(?:-[0-9]+)*\. /y.source,
|
|
20
|
+
/\((?:[0-9]+)\)(?:-[0-9]+)* /y.source,
|
|
21
21
|
].join('|'), 'y'),
|
|
22
22
|
olist_)));
|
|
23
23
|
|
package/src/parser/block.ts
CHANGED
|
@@ -58,51 +58,53 @@ export const block: BlockParser = reset(
|
|
|
58
58
|
input => {
|
|
59
59
|
const { context: { source, position } } = input;
|
|
60
60
|
if (position === source.length) return;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
case '
|
|
67
|
-
return codeblock(input);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
const fst = source[position];
|
|
62
|
+
switch (fst) {
|
|
63
|
+
case '=':
|
|
64
|
+
if (source.startsWith('===', position)) return pagebreak(input);
|
|
65
|
+
break;
|
|
66
|
+
case '`':
|
|
67
|
+
if (source.startsWith('```', position)) return codeblock(input);
|
|
68
|
+
break;
|
|
69
|
+
case '~':
|
|
70
|
+
if (source.startsWith('~~~', position)) return extension(input);
|
|
71
|
+
if (source[position + 1] === ' ') return dlist(input);
|
|
72
|
+
break;
|
|
73
|
+
case '-':
|
|
74
|
+
if (source[position + 1] === ' ') return ulist(input) || ilist(input);
|
|
75
|
+
break;
|
|
76
|
+
case '+':
|
|
77
|
+
case '*':
|
|
78
|
+
if (source[position + 1] === ' ') return ilist(input);
|
|
79
|
+
break;
|
|
80
|
+
case '[':
|
|
81
|
+
switch (source[position + 1]) {
|
|
82
|
+
case '$':
|
|
83
|
+
return extension(input);
|
|
84
|
+
case '!':
|
|
85
|
+
return mediablock(input);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
case '!':
|
|
89
|
+
if (source[position + 1] === '>') return blockquote(input);
|
|
75
90
|
return mediablock(input);
|
|
76
|
-
case '
|
|
91
|
+
case '>':
|
|
92
|
+
if (source[position + 1] === '>') return blockquote(input) || reply(input);
|
|
77
93
|
return blockquote(input);
|
|
78
|
-
case '>>':
|
|
79
|
-
return blockquote(input)
|
|
80
|
-
|| reply(input);
|
|
81
|
-
case '- ':
|
|
82
|
-
return ulist(input)
|
|
83
|
-
|| ilist(input);
|
|
84
|
-
case '+ ':
|
|
85
|
-
case '* ':
|
|
86
|
-
return ilist(input);
|
|
87
|
-
case '~ ':
|
|
88
|
-
return dlist(input);
|
|
89
|
-
}
|
|
90
|
-
switch (source[position]) {
|
|
91
94
|
case '#':
|
|
92
95
|
return heading(input);
|
|
93
|
-
case '|':
|
|
94
|
-
return table(input)
|
|
95
|
-
|| sidefence(input);
|
|
96
96
|
case '$':
|
|
97
|
+
if (source[position + 1] === '$') return mathblock(input);
|
|
97
98
|
return extension(input);
|
|
98
|
-
case '
|
|
99
|
-
return
|
|
100
|
-
case '
|
|
101
|
-
return
|
|
99
|
+
case '|':
|
|
100
|
+
return table(input) || sidefence(input);
|
|
101
|
+
case '(':
|
|
102
|
+
return olist(input);
|
|
103
|
+
default:
|
|
104
|
+
if ('0' <= fst && fst <= '9') return olist(input);
|
|
102
105
|
}
|
|
103
106
|
},
|
|
104
107
|
emptyline,
|
|
105
|
-
olist,
|
|
106
108
|
paragraph
|
|
107
109
|
]) as any));
|
|
108
110
|
|
|
@@ -17,7 +17,7 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('(()))'), ctx), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('(("))'), ctx), undefined);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('(([))'), ctx), undefined);
|
|
20
|
-
assert.deepStrictEqual(inspect(parser('((
|
|
20
|
+
assert.deepStrictEqual(inspect(parser('(([%))'), ctx), undefined);
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('(( ))'), ctx), undefined);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('(( (a'), ctx), undefined);
|
|
23
23
|
assert.deepStrictEqual(inspect(parser('((\n))'), ctx), undefined);
|
|
@@ -41,6 +41,7 @@ describe('Unit: parser/inline/bracket', () => {
|
|
|
41
41
|
assert.deepStrictEqual(inspect(parser('(ABBR, ABBR)'), ctx), [['(', 'ABBR, ABBR', ')'], '']);
|
|
42
42
|
assert.deepStrictEqual(inspect(parser('(\\a)'), ctx), [['<span class="paren">(a)</span>'], '']);
|
|
43
43
|
assert.deepStrictEqual(inspect(parser('(==)'), ctx), [['<span class="paren">(==)</span>'], '']);
|
|
44
|
+
assert.deepStrictEqual(inspect(parser('("(\n))"(")'), ctx), [['<span class="paren">("(<br>)</span>'], ')"(")']);
|
|
44
45
|
assert.deepStrictEqual(inspect(parser('($)$'), ctx), [['(', '<span class="math" translate="no" data-src="$)$">$)$</span>'], '']);
|
|
45
46
|
assert.deepStrictEqual(inspect(parser(')'), ctx), undefined);
|
|
46
47
|
assert.deepStrictEqual(inspect(parser('(1,2)'), ctx), [['(', '1,2', ')'], '']);
|
|
@@ -82,8 +83,9 @@ describe('Unit: parser/inline/bracket', () => {
|
|
|
82
83
|
assert.deepStrictEqual(inspect(parser('"(")"'), ctx), [['"', '(', '"'], ')"']);
|
|
83
84
|
assert.deepStrictEqual(inspect(parser('"(("'), ctx), [['"', '(', '(', '"'], '']);
|
|
84
85
|
assert.deepStrictEqual(inspect(parser('"(\\")"'), ctx), [['"', '<span class="paren">(")</span>', '"'], '']);
|
|
85
|
-
assert.deepStrictEqual(inspect(parser('"\n"'), ctx), [['"', '
|
|
86
|
-
assert.deepStrictEqual(inspect(parser('"\n"
|
|
86
|
+
assert.deepStrictEqual(inspect(parser('"(\n)"(")'), ctx), [['"', '('], '\n)"(")']);
|
|
87
|
+
assert.deepStrictEqual(inspect(parser('"\n"'), ctx), [['"'], '\n"']);
|
|
88
|
+
assert.deepStrictEqual(inspect(parser('"\n"(")'), ctx), [['"'], '\n"(")']);
|
|
87
89
|
});
|
|
88
90
|
|
|
89
91
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BracketParser } from '../inline';
|
|
2
2
|
import { State, Recursion, Backtrack } from '../context';
|
|
3
|
-
import { union, some, recursion, precedence,
|
|
3
|
+
import { union, some, recursion, precedence, surround, isBacktrack, setBacktrack, lazy } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
5
|
import { textlink } from './link';
|
|
6
6
|
import { str } from '../source';
|
|
@@ -11,93 +11,119 @@ const indexA = /^[0-9A-Za-z]+(?:(?:[.-]|, )[0-9A-Za-z]+)*$/;
|
|
|
11
11
|
const indexF = new RegExp(indexA.source.replace(', ', '[,、]')
|
|
12
12
|
.replace(/[09AZaz.]|\-(?!\w)/g, c => String.fromCodePoint(c.codePointAt(0)! + 0xFEE0)));
|
|
13
13
|
|
|
14
|
-
export const bracket: BracketParser = lazy(() =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
str
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
14
|
+
export const bracket: BracketParser = lazy(() => union([
|
|
15
|
+
input => {
|
|
16
|
+
const { context: { source, position } } = input;
|
|
17
|
+
switch (source[position]) {
|
|
18
|
+
case '(':
|
|
19
|
+
return p1(input);
|
|
20
|
+
case '(':
|
|
21
|
+
return p2(input);
|
|
22
|
+
case '[':
|
|
23
|
+
return s1(input);
|
|
24
|
+
case '[':
|
|
25
|
+
return s2(input);
|
|
26
|
+
case '{':
|
|
27
|
+
return c1(input);
|
|
28
|
+
case '{':
|
|
29
|
+
return c2(input);
|
|
30
|
+
case '"':
|
|
31
|
+
return d1(input);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
])) as any;
|
|
35
|
+
|
|
36
|
+
const p1 = lazy(() => surround(
|
|
37
|
+
str('('),
|
|
38
|
+
precedence(1, recursion(Recursion.bracket, some(inline, ')', [[')', 1]]))),
|
|
39
|
+
str(')'),
|
|
40
|
+
true,
|
|
41
|
+
([as, bs = [], cs], { source, position, range = 0 }) => {
|
|
42
|
+
const str = source.slice(position - range + 1, position - 1);
|
|
43
|
+
return indexA.test(str)
|
|
44
|
+
? [[as[0], str, cs[0]]]
|
|
45
|
+
: [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))]];
|
|
46
|
+
},
|
|
47
|
+
([as, bs = []]) => [unshift(as, bs)],
|
|
48
|
+
[2 | Backtrack.bracket]));
|
|
49
|
+
|
|
50
|
+
const p2 = lazy(() => surround(
|
|
51
|
+
str('('),
|
|
52
|
+
precedence(1, recursion(Recursion.bracket, some(inline, ')', [[')', 1]]))),
|
|
53
|
+
str(')'),
|
|
54
|
+
true,
|
|
55
|
+
([as, bs = [], cs], { source, position, range = 0 }) => {
|
|
56
|
+
const str = source.slice(position - range + 1, position - 1);
|
|
57
|
+
return indexF.test(str)
|
|
58
|
+
? [[as[0], str, cs[0]]]
|
|
59
|
+
: [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))]];
|
|
60
|
+
},
|
|
61
|
+
([as, bs = []]) => [unshift(as, bs)],
|
|
62
|
+
[2 | Backtrack.bracket]));
|
|
63
|
+
|
|
64
|
+
const s1 = lazy(() => surround(
|
|
65
|
+
str('['),
|
|
66
|
+
precedence(1, recursion(Recursion.bracket, some(inline, ']', [[']', 1]]))),
|
|
67
|
+
str(']'),
|
|
68
|
+
true,
|
|
69
|
+
([as, bs = [], cs], context) => {
|
|
70
|
+
if (context.state! & State.link) {
|
|
71
|
+
const { source, position, range = 0 } = context;
|
|
72
|
+
const head = position - range;
|
|
73
|
+
if (context.linebreak !== 0 || source[position] !== '{') {
|
|
74
|
+
setBacktrack(context, [2 | Backtrack.link], head);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
context.state! ^= State.link;
|
|
78
|
+
const result = !isBacktrack(context, [1 | Backtrack.link])
|
|
79
|
+
? textlink({ context })
|
|
80
|
+
: undefined;
|
|
81
|
+
context.position = position;
|
|
82
|
+
if (!result) {
|
|
50
83
|
setBacktrack(context, [2 | Backtrack.link], head);
|
|
51
84
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const result = !isBacktrack(context, [1 | Backtrack.link])
|
|
55
|
-
? textlink({ context })
|
|
56
|
-
: undefined;
|
|
57
|
-
context.position = position;
|
|
58
|
-
if (!result) {
|
|
59
|
-
setBacktrack(context, [2 | Backtrack.link], head);
|
|
60
|
-
}
|
|
61
|
-
context.state! ^= State.link;
|
|
62
|
-
context.range = range;
|
|
63
|
-
}
|
|
85
|
+
context.state! ^= State.link;
|
|
86
|
+
context.range = range;
|
|
64
87
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
88
|
+
}
|
|
89
|
+
return [push(unshift(as, bs), cs)];
|
|
90
|
+
},
|
|
91
|
+
([as, bs = []]) => [unshift(as, bs)],
|
|
92
|
+
[2 | Backtrack.bracket]));
|
|
93
|
+
|
|
94
|
+
const s2 = lazy(() => surround(
|
|
95
|
+
str('['),
|
|
96
|
+
precedence(1, recursion(Recursion.bracket, some(inline, ']', [[']', 1]]))),
|
|
97
|
+
str(']'),
|
|
98
|
+
true,
|
|
99
|
+
undefined,
|
|
100
|
+
([as, bs = []]) => [unshift(as, bs)],
|
|
101
|
+
[2 | Backtrack.bracket]));
|
|
102
|
+
|
|
103
|
+
const c1 = lazy(() => surround(
|
|
104
|
+
str('{'),
|
|
105
|
+
precedence(1, recursion(Recursion.bracket, some(inline, '}', [['}', 1]]))),
|
|
106
|
+
str('}'),
|
|
107
|
+
true,
|
|
108
|
+
undefined,
|
|
109
|
+
([as, bs = []]) => [unshift(as, bs)],
|
|
110
|
+
[2 | Backtrack.bracket]));
|
|
111
|
+
|
|
112
|
+
const c2 = lazy(() => surround(
|
|
113
|
+
str('{'),
|
|
114
|
+
precedence(1, recursion(Recursion.bracket, some(inline, '}', [['}', 1]]))),
|
|
115
|
+
str('}'),
|
|
116
|
+
true,
|
|
117
|
+
undefined,
|
|
118
|
+
([as, bs = []]) => [unshift(as, bs)],
|
|
119
|
+
[2 | Backtrack.bracket]));
|
|
120
|
+
|
|
121
|
+
const d1 = lazy(() => surround(
|
|
122
|
+
str('"'),
|
|
123
|
+
// 改行の優先度を構文ごとに変える場合シグネチャの優先度対応が必要
|
|
124
|
+
precedence(2, recursion(Recursion.bracket, some(inline, /["\n]/y, [['"', 2], ['\n', 3]]))),
|
|
125
|
+
str('"'),
|
|
126
|
+
true,
|
|
127
|
+
undefined,
|
|
128
|
+
([as, bs = []]) => [unshift(as, bs)],
|
|
129
|
+
[2 | Backtrack.bracket]));
|
|
@@ -7,7 +7,7 @@ import { str } from '../source';
|
|
|
7
7
|
import { isLooseNodeStart, blankWith } from '../visibility';
|
|
8
8
|
import { invalid } from '../util';
|
|
9
9
|
import { memoize } from 'spica/memoize';
|
|
10
|
-
import { unshift, push
|
|
10
|
+
import { unshift, push } from 'spica/array';
|
|
11
11
|
import { html as h, defrag } from 'typed-dom/dom';
|
|
12
12
|
|
|
13
13
|
const tags: readonly string[] = ['wbr', 'bdo', 'bdi'];
|
|
@@ -42,10 +42,14 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
|
|
|
42
42
|
true,
|
|
43
43
|
([as, bs = [], cs]) => [push(unshift(as, bs), cs)],
|
|
44
44
|
([as, bs = []]) => [unshift(as, bs)]),
|
|
45
|
-
|
|
45
|
+
// 不可視のHTML構造が可視構造を変化させるべきでない。
|
|
46
|
+
// 可視のHTMLは優先度変更を検討する。
|
|
47
|
+
// このため<>は将来的に共通構造を変化させる可能性があり
|
|
48
|
+
// 共通構造を変更させない非構造文字列としては依然としてエスケープを要する。
|
|
49
|
+
precedence(0, recursion(Recursion.inline,
|
|
46
50
|
some(union([
|
|
47
|
-
some(inline, blankWith('\n', `</${tag}>`)
|
|
48
|
-
open('\n', some(inline, `</${tag}
|
|
51
|
+
some(inline, blankWith('\n', `</${tag}>`)),
|
|
52
|
+
open('\n', some(inline, `</${tag}>`), true),
|
|
49
53
|
])))),
|
|
50
54
|
str(`</${tag}>`),
|
|
51
55
|
true,
|
|
@@ -81,7 +85,7 @@ function elem(tag: string, content: boolean, as: string[], bs: (HTMLElement | st
|
|
|
81
85
|
if (bs.length === 0) return ielem('content', `Missing the content`, context);
|
|
82
86
|
if (!isLooseNodeStart(bs)) return ielem('content', `Missing the visible content in the same line`, context);
|
|
83
87
|
}
|
|
84
|
-
const attrs = attributes('html', attrspecs[tag], as.slice(1, as.at(-1) === '>' ? -1 : as.length));
|
|
88
|
+
const [attrs] = attributes('html', attrspecs[tag], as.slice(1, as.at(-1) === '>' ? -1 : as.length));
|
|
85
89
|
if (/(?<!\S)invalid(?!\S)/.test(attrs['class'] ?? '')) return ielem('attribute', 'Invalid HTML attribute', context)
|
|
86
90
|
if (as.at(-1) !== '>') return ielem('tag', `Missing the closing symbol ">"`, context);
|
|
87
91
|
return h(tag as 'span', attrs, defrag(bs));
|
|
@@ -101,11 +105,12 @@ const requiredAttributes = memoize(
|
|
|
101
105
|
export function attributes(
|
|
102
106
|
syntax: string,
|
|
103
107
|
spec: Readonly<Record<string, readonly (string | undefined)[] | undefined>> | undefined,
|
|
104
|
-
params: string[],
|
|
105
|
-
): Record<string, string | undefined
|
|
108
|
+
params: readonly string[],
|
|
109
|
+
): [Record<string, string | undefined>, string[]] {
|
|
106
110
|
assert(spec instanceof Object === false);
|
|
107
111
|
assert(!spec?.['__proto__']);
|
|
108
112
|
assert(!spec?.toString);
|
|
113
|
+
const remains = [];
|
|
109
114
|
let invalidation = false;
|
|
110
115
|
const attrs: Record<string, string | undefined> = {};
|
|
111
116
|
for (let i = 0; i < params.length; ++i) {
|
|
@@ -116,19 +121,25 @@ export function attributes(
|
|
|
116
121
|
? param.slice(name.length + 2, -1).replace(/\\(.?)/g, '$1')
|
|
117
122
|
: undefined;
|
|
118
123
|
invalidation ||= name === '' || !spec || name in attrs;
|
|
119
|
-
if (name === ''
|
|
120
|
-
spec
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
if (name === '')continue;
|
|
125
|
+
if (spec && name in spec && !spec[name]) {
|
|
126
|
+
remains.push(params[i]);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (spec?.[name]?.includes(value) || spec?.[name]?.length === 0 && value !== undefined) {
|
|
130
|
+
attrs[name] = value ?? ''
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
invalidation ||= !!spec;
|
|
134
|
+
}
|
|
123
135
|
assert(!(name in {} && attrs.hasOwnProperty(name)));
|
|
124
|
-
splice(params, i--, 1);
|
|
125
136
|
}
|
|
126
137
|
invalidation ||= !!spec && !requiredAttributes(spec).every(name => name in attrs);
|
|
127
138
|
if (invalidation) {
|
|
128
139
|
attrs['class'] = 'invalid';
|
|
129
140
|
Object.assign(attrs, invalid(syntax, 'argument', 'Invalid argument'));
|
|
130
141
|
}
|
|
131
|
-
return attrs;
|
|
142
|
+
return [attrs, remains];
|
|
132
143
|
}
|
|
133
144
|
|
|
134
145
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
|
@@ -135,7 +135,7 @@ function parse(
|
|
|
135
135
|
context.host?.origin || location.origin);
|
|
136
136
|
return el.classList.contains('invalid')
|
|
137
137
|
? el
|
|
138
|
-
: define(el, attributes('link', optspec, params));
|
|
138
|
+
: define(el, attributes('link', optspec, params)[0]);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
function elem(
|