securemark 0.234.2 → 0.235.1
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/securemark.js +173 -158
- package/package-lock.json +40 -37
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +0 -2
- package/src/combinator/control/constraint/contract.ts +1 -1
- package/src/combinator/control/manipulation/context.test.ts +4 -4
- package/src/combinator/control/manipulation/context.ts +7 -0
- package/src/combinator/control/manipulation/match.ts +1 -1
- package/src/combinator/control/manipulation/resource.ts +6 -3
- package/src/combinator/control/manipulation/scope.ts +1 -1
- package/src/combinator/control/manipulation/surround.ts +3 -4
- package/src/combinator/data/parser/inits.ts +1 -1
- package/src/combinator/data/parser/sequence.ts +1 -1
- package/src/combinator/data/parser/some.ts +41 -21
- package/src/combinator/data/parser.ts +33 -7
- package/src/parser/api/bind.test.ts +3 -3
- package/src/parser/api/normalize.ts +7 -6
- package/src/parser/api/parse.test.ts +12 -5
- package/src/parser/block/blockquote.test.ts +1 -1
- package/src/parser/block/extension/aside.test.ts +1 -1
- package/src/parser/block/extension/example.test.ts +2 -2
- package/src/parser/block/extension/fig.test.ts +20 -20
- package/src/parser/block/extension/figure.test.ts +31 -28
- package/src/parser/block/extension/figure.ts +4 -2
- package/src/parser/block/extension/table.ts +6 -6
- package/src/parser/block/heading.test.ts +1 -1
- package/src/parser/block.ts +1 -2
- package/src/parser/inline/bracket.ts +3 -3
- package/src/parser/inline/emphasis.ts +7 -10
- package/src/parser/inline/emstrong.ts +7 -8
- package/src/parser/inline/extension/index.test.ts +19 -18
- package/src/parser/inline/extension/index.ts +3 -4
- package/src/parser/inline/extension/indexer.test.ts +1 -0
- package/src/parser/inline/extension/indexer.ts +1 -0
- package/src/parser/inline/html.ts +7 -11
- package/src/parser/inline/htmlentity.ts +8 -11
- package/src/parser/inline/link.ts +3 -4
- package/src/parser/inline/mark.ts +3 -6
- package/src/parser/inline/media.ts +8 -5
- package/src/parser/inline/ruby.ts +3 -4
- package/src/parser/inline/strong.test.ts +1 -1
- package/src/parser/inline/strong.ts +7 -10
- package/src/parser/inline.test.ts +0 -1
- package/src/parser/processor/figure.test.ts +28 -28
- package/src/parser/processor/figure.ts +1 -1
- package/src/parser/util.ts +14 -51
package/package-lock.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securemark",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.235.1",
|
|
4
4
|
"lockfileVersion": 1,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"dependencies": {
|
|
@@ -449,9 +449,9 @@
|
|
|
449
449
|
},
|
|
450
450
|
"dependencies": {
|
|
451
451
|
"lru-cache": {
|
|
452
|
-
"version": "7.7.
|
|
453
|
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.
|
|
454
|
-
"integrity": "sha512-
|
|
452
|
+
"version": "7.7.3",
|
|
453
|
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz",
|
|
454
|
+
"integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==",
|
|
455
455
|
"dev": true
|
|
456
456
|
},
|
|
457
457
|
"mkdirp": {
|
|
@@ -1760,10 +1760,13 @@
|
|
|
1760
1760
|
"dev": true
|
|
1761
1761
|
},
|
|
1762
1762
|
"builtins": {
|
|
1763
|
-
"version": "
|
|
1764
|
-
"resolved": "https://registry.npmjs.org/builtins/-/builtins-
|
|
1765
|
-
"integrity": "
|
|
1766
|
-
"dev": true
|
|
1763
|
+
"version": "5.0.0",
|
|
1764
|
+
"resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.0.tgz",
|
|
1765
|
+
"integrity": "sha512-aizhtbxgT1Udg0Fj6GssXshAVK+nxbtCV+1OtTrMNy67jffDFBY6CUBAkhO4owbleAx6fdbnWdpsmmcXydbzNw==",
|
|
1766
|
+
"dev": true,
|
|
1767
|
+
"requires": {
|
|
1768
|
+
"semver": "^7.0.0"
|
|
1769
|
+
}
|
|
1767
1770
|
},
|
|
1768
1771
|
"bytes": {
|
|
1769
1772
|
"version": "3.1.2",
|
|
@@ -1798,9 +1801,9 @@
|
|
|
1798
1801
|
},
|
|
1799
1802
|
"dependencies": {
|
|
1800
1803
|
"lru-cache": {
|
|
1801
|
-
"version": "7.7.
|
|
1802
|
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.
|
|
1803
|
-
"integrity": "sha512-
|
|
1804
|
+
"version": "7.7.3",
|
|
1805
|
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz",
|
|
1806
|
+
"integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==",
|
|
1804
1807
|
"dev": true
|
|
1805
1808
|
},
|
|
1806
1809
|
"mkdirp": {
|
|
@@ -1924,9 +1927,9 @@
|
|
|
1924
1927
|
"dev": true
|
|
1925
1928
|
},
|
|
1926
1929
|
"caniuse-lite": {
|
|
1927
|
-
"version": "1.0.
|
|
1928
|
-
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.
|
|
1929
|
-
"integrity": "sha512-
|
|
1930
|
+
"version": "1.0.30001322",
|
|
1931
|
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz",
|
|
1932
|
+
"integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==",
|
|
1930
1933
|
"dev": true
|
|
1931
1934
|
},
|
|
1932
1935
|
"chalk": {
|
|
@@ -3030,9 +3033,9 @@
|
|
|
3030
3033
|
"dev": true
|
|
3031
3034
|
},
|
|
3032
3035
|
"electron-to-chromium": {
|
|
3033
|
-
"version": "1.4.
|
|
3034
|
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.
|
|
3035
|
-
"integrity": "sha512-
|
|
3036
|
+
"version": "1.4.103",
|
|
3037
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz",
|
|
3038
|
+
"integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==",
|
|
3036
3039
|
"dev": true
|
|
3037
3040
|
},
|
|
3038
3041
|
"elliptic": {
|
|
@@ -7092,9 +7095,9 @@
|
|
|
7092
7095
|
}
|
|
7093
7096
|
},
|
|
7094
7097
|
"make-fetch-happen": {
|
|
7095
|
-
"version": "10.1.
|
|
7096
|
-
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.
|
|
7097
|
-
"integrity": "sha512-
|
|
7098
|
+
"version": "10.1.1",
|
|
7099
|
+
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.1.tgz",
|
|
7100
|
+
"integrity": "sha512-3/mCljDQNjmrP7kl0vhS5WVlV+TvSKoZaFhdiYV7MOijEnrhrjaVnqbp/EY/7S+fhUB2KpH7j8c1iRsIOs+kjw==",
|
|
7098
7101
|
"dev": true,
|
|
7099
7102
|
"requires": {
|
|
7100
7103
|
"agentkeepalive": "^4.2.1",
|
|
@@ -7116,9 +7119,9 @@
|
|
|
7116
7119
|
},
|
|
7117
7120
|
"dependencies": {
|
|
7118
7121
|
"lru-cache": {
|
|
7119
|
-
"version": "7.7.
|
|
7120
|
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.
|
|
7121
|
-
"integrity": "sha512-
|
|
7122
|
+
"version": "7.7.3",
|
|
7123
|
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz",
|
|
7124
|
+
"integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==",
|
|
7122
7125
|
"dev": true
|
|
7123
7126
|
}
|
|
7124
7127
|
}
|
|
@@ -8128,14 +8131,14 @@
|
|
|
8128
8131
|
"dev": true
|
|
8129
8132
|
},
|
|
8130
8133
|
"npm-package-arg": {
|
|
8131
|
-
"version": "9.0.
|
|
8132
|
-
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.0.
|
|
8133
|
-
"integrity": "sha512-
|
|
8134
|
+
"version": "9.0.2",
|
|
8135
|
+
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.0.2.tgz",
|
|
8136
|
+
"integrity": "sha512-v/miORuX8cndiOheW8p2moNuPJ7QhcFh9WGlTorruG8hXSA23vMTEp5hTCmDxic0nD8KHhj/NQgFuySD3GYY3g==",
|
|
8134
8137
|
"dev": true,
|
|
8135
8138
|
"requires": {
|
|
8136
8139
|
"hosted-git-info": "^5.0.0",
|
|
8137
8140
|
"semver": "^7.3.5",
|
|
8138
|
-
"validate-npm-package-name": "^
|
|
8141
|
+
"validate-npm-package-name": "^4.0.0"
|
|
8139
8142
|
},
|
|
8140
8143
|
"dependencies": {
|
|
8141
8144
|
"hosted-git-info": {
|
|
@@ -8148,9 +8151,9 @@
|
|
|
8148
8151
|
}
|
|
8149
8152
|
},
|
|
8150
8153
|
"lru-cache": {
|
|
8151
|
-
"version": "7.7.
|
|
8152
|
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.
|
|
8153
|
-
"integrity": "sha512-
|
|
8154
|
+
"version": "7.7.3",
|
|
8155
|
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz",
|
|
8156
|
+
"integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==",
|
|
8154
8157
|
"dev": true
|
|
8155
8158
|
}
|
|
8156
8159
|
}
|
|
@@ -9187,9 +9190,9 @@
|
|
|
9187
9190
|
}
|
|
9188
9191
|
},
|
|
9189
9192
|
"lru-cache": {
|
|
9190
|
-
"version": "7.7.
|
|
9191
|
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.
|
|
9192
|
-
"integrity": "sha512-
|
|
9193
|
+
"version": "7.7.3",
|
|
9194
|
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz",
|
|
9195
|
+
"integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==",
|
|
9193
9196
|
"dev": true
|
|
9194
9197
|
},
|
|
9195
9198
|
"normalize-package-data": {
|
|
@@ -11307,12 +11310,12 @@
|
|
|
11307
11310
|
}
|
|
11308
11311
|
},
|
|
11309
11312
|
"validate-npm-package-name": {
|
|
11310
|
-
"version": "
|
|
11311
|
-
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-
|
|
11312
|
-
"integrity": "
|
|
11313
|
+
"version": "4.0.0",
|
|
11314
|
+
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz",
|
|
11315
|
+
"integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==",
|
|
11313
11316
|
"dev": true,
|
|
11314
11317
|
"requires": {
|
|
11315
|
-
"builtins": "^
|
|
11318
|
+
"builtins": "^5.0.0"
|
|
11316
11319
|
}
|
|
11317
11320
|
},
|
|
11318
11321
|
"value-or-function": {
|
package/package.json
CHANGED
|
@@ -6,10 +6,8 @@ export function block<P extends Parser<unknown>>(parser: P, separation?: boolean
|
|
|
6
6
|
export function block<T>(parser: Parser<T>, separation = true): Parser<T> {
|
|
7
7
|
assert(parser);
|
|
8
8
|
return (source, context) => {
|
|
9
|
-
assert(!context?.delimiters);
|
|
10
9
|
if (source === '') return;
|
|
11
10
|
const result = parser(source, context);
|
|
12
|
-
assert(!context?.delimiters);
|
|
13
11
|
if (!result) return;
|
|
14
12
|
const rest = exec(result);
|
|
15
13
|
if (separation && !isEmpty(firstline(rest))) return;
|
|
@@ -15,7 +15,7 @@ export function validate<T>(patterns: string | RegExp | (string | RegExp)[], has
|
|
|
15
15
|
if (typeof end === 'function') return validate(patterns, has, '', end);
|
|
16
16
|
if (!isArray(patterns)) return validate([patterns], has, end!, parser!);
|
|
17
17
|
assert(patterns.length > 0);
|
|
18
|
-
assert(patterns.every(pattern => pattern instanceof RegExp ? !pattern.
|
|
18
|
+
assert(patterns.every(pattern => pattern instanceof RegExp ? !pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^') : true));
|
|
19
19
|
assert(parser);
|
|
20
20
|
const match: (source: string) => boolean = Function([
|
|
21
21
|
'"use strict";',
|
|
@@ -13,7 +13,7 @@ describe('Unit: combinator/context', () => {
|
|
|
13
13
|
(s, context) => [[context.resources?.budget ?? NaN], s.slice(1)]));
|
|
14
14
|
|
|
15
15
|
it('root', () => {
|
|
16
|
-
const base: Context = { resources: { budget: 3 } };
|
|
16
|
+
const base: Context = { resources: { budget: 3, recursion: 1 } };
|
|
17
17
|
const ctx: Context = {};
|
|
18
18
|
assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
|
|
19
19
|
assert(base.resources?.budget === 3);
|
|
@@ -24,8 +24,8 @@ describe('Unit: combinator/context', () => {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it('node', () => {
|
|
27
|
-
const base: Context = { resources: { budget: 3 } };
|
|
28
|
-
const ctx: Context = { resources: { budget: 1 } };
|
|
27
|
+
const base: Context = { resources: { budget: 3, recursion: 1 } };
|
|
28
|
+
const ctx: Context = { resources: { budget: 1, recursion: 1 } };
|
|
29
29
|
assert.deepStrictEqual(reset(base, parser)('1', ctx), [[1], '']);
|
|
30
30
|
assert(base.resources?.budget === 3);
|
|
31
31
|
assert(ctx.resources?.budget === 0);
|
|
@@ -41,7 +41,7 @@ describe('Unit: combinator/context', () => {
|
|
|
41
41
|
|
|
42
42
|
it('', () => {
|
|
43
43
|
const base: Context = { state: true };
|
|
44
|
-
const ctx: Context = { resources: { budget: 3 } };
|
|
44
|
+
const ctx: Context = { resources: { budget: 3, recursion: 1 } };
|
|
45
45
|
assert.deepStrictEqual(context(base, parser)('123', ctx), [[true, true, true], '']);
|
|
46
46
|
assert(ctx.resources?.budget === 0);
|
|
47
47
|
assert(ctx.state === undefined);
|
|
@@ -17,6 +17,7 @@ export function reset<P extends Parser<unknown>>(context: Context<P>, parser: P)
|
|
|
17
17
|
export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
18
18
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
19
19
|
assert(Object.freeze(base));
|
|
20
|
+
if (isEmpty(base)) return parser;
|
|
20
21
|
return (source, context) =>
|
|
21
22
|
parser(source, inherit(ObjectCreate(context), base));
|
|
22
23
|
}
|
|
@@ -25,6 +26,7 @@ export function context<P extends Parser<unknown>>(context: Context<P>, parser:
|
|
|
25
26
|
export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
26
27
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
27
28
|
assert(Object.freeze(base));
|
|
29
|
+
if (isEmpty(base)) return parser;
|
|
28
30
|
const override = memoize<Ctx, Ctx>(context => inherit(ObjectCreate(context), base), new WeakMap());
|
|
29
31
|
return (source, context) =>
|
|
30
32
|
parser(source, override(context));
|
|
@@ -56,3 +58,8 @@ const inherit = template((prop, target, source) => {
|
|
|
56
58
|
return target[prop] = source[prop];
|
|
57
59
|
}
|
|
58
60
|
});
|
|
61
|
+
|
|
62
|
+
function isEmpty(context: Ctx): boolean {
|
|
63
|
+
for (const _ in context) return false;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
@@ -3,7 +3,7 @@ import { Parser, exec, check } from '../../data/parser';
|
|
|
3
3
|
|
|
4
4
|
export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
|
|
5
5
|
export function match<T>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<T>): Parser<T> {
|
|
6
|
-
assert(!pattern.
|
|
6
|
+
assert(!pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^'));
|
|
7
7
|
return (source, context) => {
|
|
8
8
|
if (source === '') return;
|
|
9
9
|
const param = source.match(pattern);
|
|
@@ -6,10 +6,13 @@ export function creator(cost: number | Parser<unknown>, parser?: Parser<unknown>
|
|
|
6
6
|
if (typeof cost === 'function') return creator(1, cost);
|
|
7
7
|
assert(cost > 0);
|
|
8
8
|
return (source, context) => {
|
|
9
|
-
const { resources } = context;
|
|
10
|
-
if (resources
|
|
9
|
+
const { resources = { budget: 1, recursion: 1 } } = context;
|
|
10
|
+
if (resources.budget <= 0) throw new Error('Too many creations.');
|
|
11
|
+
if (resources.recursion <= 0) throw new Error('Too much recursion.');
|
|
12
|
+
--resources.recursion;
|
|
11
13
|
const result = parser!(source, context);
|
|
12
|
-
|
|
14
|
+
++resources.recursion;
|
|
15
|
+
if (result) {
|
|
13
16
|
resources.budget -= cost;
|
|
14
17
|
}
|
|
15
18
|
return result;
|
|
@@ -3,7 +3,7 @@ import { Parser, Context, eval, exec, check } from '../../data/parser';
|
|
|
3
3
|
|
|
4
4
|
export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P): P;
|
|
5
5
|
export function focus<T>(scope: string | RegExp, parser: Parser<T>): Parser<T> {
|
|
6
|
-
assert(scope instanceof RegExp ? !scope.
|
|
6
|
+
assert(scope instanceof RegExp ? !scope.flags.match(/[gmy]/) && scope.source.startsWith('^') : scope);
|
|
7
7
|
assert(parser);
|
|
8
8
|
const match: (source: string) => string = typeof scope === 'string'
|
|
9
9
|
? source => source.slice(0, scope.length) === scope ? scope : ''
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { Parser, Result, Ctx, Tree, Context, SubParsers, SubTree, IntermediateParser, eval, exec, check } from '../../data/parser';
|
|
3
3
|
import { fmap } from '../monad/fmap';
|
|
4
|
-
import { creator } from './resource';
|
|
5
4
|
import { unshift, push } from 'spica/array';
|
|
6
5
|
|
|
7
6
|
export function surround<P extends Parser<unknown>, S = string>(
|
|
@@ -69,14 +68,14 @@ export function surround<T>(
|
|
|
69
68
|
function match(pattern: string | RegExp): (source: string, context: Ctx) => [never[], string] | undefined {
|
|
70
69
|
switch (typeof pattern) {
|
|
71
70
|
case 'string':
|
|
72
|
-
return
|
|
71
|
+
return source => source.slice(0, pattern.length) === pattern ? [[], source.slice(pattern.length)] : undefined;
|
|
73
72
|
case 'object':
|
|
74
|
-
return
|
|
73
|
+
return source => {
|
|
75
74
|
const m = source.match(pattern);
|
|
76
75
|
return m
|
|
77
76
|
? [[], source.slice(m[0].length)]
|
|
78
77
|
: undefined;
|
|
79
|
-
}
|
|
78
|
+
};
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
|
|
@@ -14,7 +14,7 @@ export function inits<T, D extends Parser<T>[]>(parsers: D): Parser<T, Ctx, D> {
|
|
|
14
14
|
const result = parsers[i](rest, context);
|
|
15
15
|
assert(check(rest, result));
|
|
16
16
|
if (!result) break;
|
|
17
|
-
assert(!context?.delimiters?.
|
|
17
|
+
assert(!context?.delimiters?.match(rest));
|
|
18
18
|
nodes = nodes
|
|
19
19
|
? push(nodes, eval(result))
|
|
20
20
|
: eval(result);
|
|
@@ -14,7 +14,7 @@ export function sequence<T, D extends Parser<T>[]>(parsers: D): Parser<T, Ctx, D
|
|
|
14
14
|
const result = parsers[i](rest, context);
|
|
15
15
|
assert(check(rest, result));
|
|
16
16
|
if (!result) return;
|
|
17
|
-
assert(!context?.delimiters?.
|
|
17
|
+
assert(!context?.delimiters?.match(rest));
|
|
18
18
|
nodes = nodes
|
|
19
19
|
? push(nodes, eval(result))
|
|
20
20
|
: eval(result);
|
|
@@ -1,33 +1,56 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
|
-
import { Parser, eval, exec, check } from '../parser';
|
|
2
|
+
import { Parser, Delimiters, eval, exec, check } from '../parser';
|
|
3
|
+
import { memoize, reduce } from 'spica/memoize';
|
|
3
4
|
import { push } from 'spica/array';
|
|
4
5
|
|
|
6
|
+
const signature = (pattern: string | RegExp | undefined): string => {
|
|
7
|
+
switch (typeof pattern) {
|
|
8
|
+
case 'undefined':
|
|
9
|
+
return signature('');
|
|
10
|
+
case 'string':
|
|
11
|
+
return `s:${pattern}`;
|
|
12
|
+
case 'object':
|
|
13
|
+
return `r/${pattern.source}/${pattern.flags}`;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const matcher = memoize(
|
|
17
|
+
(pattern: string | RegExp | undefined): (source: string) => boolean => {
|
|
18
|
+
switch (typeof pattern) {
|
|
19
|
+
case 'undefined':
|
|
20
|
+
return () => false;
|
|
21
|
+
case 'string':
|
|
22
|
+
return source => source.slice(0, pattern.length) === pattern;
|
|
23
|
+
case 'object':
|
|
24
|
+
return reduce(source => pattern.test(source));
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
signature);
|
|
28
|
+
|
|
5
29
|
export function some<P extends Parser<unknown>>(parser: P, until?: string | RegExp | number, deep?: string | RegExp, limit?: number): P;
|
|
6
30
|
export function some<T>(parser: Parser<T>, until?: string | RegExp | number, deep?: string | RegExp, limit = -1): Parser<T> {
|
|
7
31
|
assert(parser);
|
|
8
|
-
assert(until instanceof RegExp ? !until.
|
|
32
|
+
assert(until instanceof RegExp ? !until.flags.match(/[gmy]/) && until.source.startsWith('^') : true);
|
|
33
|
+
assert(deep instanceof RegExp ? !deep.flags.match(/[gmy]/) && deep.source.startsWith('^') : true);
|
|
9
34
|
if (typeof until === 'number') return some(parser, undefined, deep, until);
|
|
10
|
-
const match
|
|
11
|
-
|
|
12
|
-
:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
: source => !!deep && deep.test(source);
|
|
16
|
-
let memory = '';
|
|
35
|
+
const match = matcher(until);
|
|
36
|
+
const delimiter = {
|
|
37
|
+
signature: signature(deep),
|
|
38
|
+
matcher: matcher(deep),
|
|
39
|
+
} as const;
|
|
17
40
|
return (source, context) => {
|
|
18
|
-
if (source ===
|
|
41
|
+
if (source === '') return;
|
|
19
42
|
let rest = source;
|
|
20
43
|
let nodes: T[] | undefined;
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
if (deep && context) {
|
|
45
|
+
// bracket > annotation > bracket > reference > bracket > link > media | bracket
|
|
46
|
+
// bracket > annotation > bracket > reference > bracket > index > bracket
|
|
47
|
+
context.delimiters ??= new Delimiters();
|
|
48
|
+
context.delimiters.push(delimiter);
|
|
26
49
|
}
|
|
27
50
|
while (true) {
|
|
28
51
|
if (rest === '') break;
|
|
29
52
|
if (match(rest)) break;
|
|
30
|
-
if (context
|
|
53
|
+
if (context.delimiters?.match(rest)) break;
|
|
31
54
|
const result = parser(rest, context);
|
|
32
55
|
assert.doesNotThrow(() => limit < 0 && check(rest, result));
|
|
33
56
|
if (!result) break;
|
|
@@ -37,12 +60,9 @@ export function some<T>(parser: Parser<T>, until?: string | RegExp | number, dee
|
|
|
37
60
|
rest = exec(result);
|
|
38
61
|
if (limit >= 0 && source.length - rest.length > limit) break;
|
|
39
62
|
}
|
|
40
|
-
if (
|
|
41
|
-
context.delimiters
|
|
42
|
-
? context.delimiters?.pop()
|
|
43
|
-
: context.delimiters = undefined;
|
|
63
|
+
if (deep && context.delimiters) {
|
|
64
|
+
context.delimiters.pop();
|
|
44
65
|
}
|
|
45
|
-
memory = limit < 0 && rest || memory;
|
|
46
66
|
assert(rest.length <= source.length);
|
|
47
67
|
return nodes && rest.length < source.length
|
|
48
68
|
? [nodes, rest]
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export interface Ctx {
|
|
2
|
-
readonly resources?: {
|
|
3
|
-
budget: number;
|
|
4
|
-
};
|
|
5
|
-
delimiters?: ((source: string) => boolean)[];
|
|
6
|
-
}
|
|
7
|
-
|
|
8
1
|
export type Parser<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
|
|
9
2
|
= (source: string, context: C) => Result<T, C, D>;
|
|
10
3
|
export type Result<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
|
|
11
4
|
= readonly [T[], string, C, D]
|
|
12
5
|
| readonly [T[], string]
|
|
13
6
|
| undefined;
|
|
7
|
+
export interface Ctx {
|
|
8
|
+
readonly resources?: {
|
|
9
|
+
budget: number;
|
|
10
|
+
recursion: number;
|
|
11
|
+
};
|
|
12
|
+
delimiters?: Delimiters;
|
|
13
|
+
}
|
|
14
14
|
export type Tree<P extends Parser<unknown>> = P extends Parser<infer T> ? T : never;
|
|
15
15
|
export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, any, infer D> ? D : never;
|
|
16
16
|
export type Context<P extends Parser<unknown>> = P extends Parser<unknown, infer C> ? C : never;
|
|
@@ -19,6 +19,32 @@ export type IntermediateParser<P extends Parser<unknown>> = Parser<SubTree<P>, C
|
|
|
19
19
|
type ExtractSubTree<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer T ? T extends Parser<infer U> ? U : never : never;
|
|
20
20
|
type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
|
|
21
21
|
|
|
22
|
+
export class Delimiters {
|
|
23
|
+
private readonly stack: string[] = [];
|
|
24
|
+
private readonly matchers: Record<string, (source: string) => boolean> = {};
|
|
25
|
+
public push(delimiter: { readonly signature: string; readonly matcher: (source: string) => boolean; }): void {
|
|
26
|
+
const { signature, matcher } = delimiter;
|
|
27
|
+
this.stack.push(signature);
|
|
28
|
+
this.matchers[signature] ??= matcher;
|
|
29
|
+
assert(this.matchers[signature] === matcher);
|
|
30
|
+
}
|
|
31
|
+
public pop(): void {
|
|
32
|
+
assert(this.stack.length > 0);
|
|
33
|
+
this.stack.pop();
|
|
34
|
+
}
|
|
35
|
+
public match(source: string): boolean {
|
|
36
|
+
const { stack, matchers } = this;
|
|
37
|
+
const log = {};
|
|
38
|
+
for (let i = 0; i < stack.length; ++i) {
|
|
39
|
+
const sig = stack[i];
|
|
40
|
+
if (sig in log) continue;
|
|
41
|
+
if (matchers[sig](source)) return true;
|
|
42
|
+
log[sig] = false;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
22
48
|
export { eval_ as eval };
|
|
23
49
|
function eval_<T>(result: NonNullable<Result<T>>, default_?: T[]): T[];
|
|
24
50
|
function eval_<T>(result: Result<T>, default_: T[]): T[];
|
|
@@ -34,7 +34,7 @@ describe('Unit: parser/api/bind', () => {
|
|
|
34
34
|
assert.deepStrictEqual(
|
|
35
35
|
inspect(iter),
|
|
36
36
|
[
|
|
37
|
-
'<h1 class="error"
|
|
37
|
+
'<h1 class="error">Error: Too large input over 1,000,000 bytes.</h1>',
|
|
38
38
|
`<pre class="error" translate="no">${'\n'.repeat(997)}...</pre>`,
|
|
39
39
|
]);
|
|
40
40
|
});
|
|
@@ -46,9 +46,9 @@ describe('Unit: parser/api/bind', () => {
|
|
|
46
46
|
assert.deepStrictEqual(
|
|
47
47
|
inspect(iter, 3),
|
|
48
48
|
[
|
|
49
|
-
'<h1 class="error"
|
|
49
|
+
'<h1 class="error">Error: Too large segment over 100,000 bytes.</h1>',
|
|
50
50
|
`<pre class="error" translate="no">${'\n'.repeat(997)}...</pre>`,
|
|
51
|
-
'<h1 class="error"
|
|
51
|
+
'<h1 class="error">Error: Too large segment over 100,000 bytes.</h1>',
|
|
52
52
|
]);
|
|
53
53
|
});
|
|
54
54
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eval } from '../../combinator/data/parser';
|
|
2
|
-
import {
|
|
3
|
-
import { stringify } from '../util';
|
|
2
|
+
import { unsafehtmlentity } from '../inline/htmlentity';
|
|
4
3
|
|
|
5
4
|
const UNICODE_REPLACEMENT_CHARACTER = '\uFFFD';
|
|
6
5
|
assert(UNICODE_REPLACEMENT_CHARACTER.trim());
|
|
@@ -25,9 +24,9 @@ function sanitize(source: string): string {
|
|
|
25
24
|
|
|
26
25
|
// https://dev.w3.org/html5/html-author/charref
|
|
27
26
|
// https://en.wikipedia.org/wiki/Whitespace_character
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
export const invisibleHTMLEntityNames = [
|
|
28
|
+
'Tab',
|
|
29
|
+
'NewLine',
|
|
31
30
|
'NonBreakingSpace',
|
|
32
31
|
'nbsp',
|
|
33
32
|
'shy',
|
|
@@ -59,8 +58,10 @@ const unreadableHTMLEntityNames = [
|
|
|
59
58
|
'InvisibleComma',
|
|
60
59
|
'ic',
|
|
61
60
|
] as const;
|
|
61
|
+
const unreadableHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames.slice(2);
|
|
62
62
|
const unreadableEscapableCharacters = unreadableHTMLEntityNames
|
|
63
|
-
.map(name =>
|
|
63
|
+
.map(name => eval(unsafehtmlentity(`&${name};`, {}))![0]);
|
|
64
|
+
assert(unreadableEscapableCharacters.length === unreadableHTMLEntityNames.length);
|
|
64
65
|
assert(unreadableEscapableCharacters.every(c => c.length === 1));
|
|
65
66
|
const unreadableEscapableCharacter = new RegExp(`[${
|
|
66
67
|
[...new Set<string>(unreadableEscapableCharacters)].join('')
|
|
@@ -118,7 +118,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
118
118
|
'<p><a href="https://source/hashtags/a" target="_blank" class="hashtag">#a</a></p>',
|
|
119
119
|
'<p><a href="https://domain/hashtags/a" target="_blank" class="hashtag">#domain/a</a></p>',
|
|
120
120
|
'<p><a class="index" href="#index:a">a</a></p>',
|
|
121
|
-
'<figure data-label="$-a" data-group="$" data-number="1" id="label:$-a"><div class="figcontent"><div class="math" translate="no">$$\n$$</div></div><span class="figindex">(1)</span
|
|
121
|
+
'<figure data-label="$-a" data-group="$" data-number="1" id="label:$-a"><div class="figcontent"><div class="math" translate="no">$$\n$$</div></div><figcaption><span class="figindex">(1)</span></figcaption></figure>',
|
|
122
122
|
'<p><a class="label" data-label="$-a" href="#label:$-a">(1)</a></p>',
|
|
123
123
|
'<p><sup class="annotation" id="annotation:ref:1" title="a"><span hidden="">a</span><a href="#annotation:def:1">*1</a></sup></p>',
|
|
124
124
|
'<p><a href="https://source/x/a" target="_blank">a</a></p>',
|
|
@@ -201,7 +201,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
201
201
|
assert.deepStrictEqual(
|
|
202
202
|
[...parse('$-a\n$$\n$$\n\n(($-a[[b]][[c*d*]]))', { footnotes }).children].map(el => el.outerHTML),
|
|
203
203
|
[
|
|
204
|
-
'<figure data-label="$-a" data-group="$" data-number="1" id="label:$-a"><div class="figcontent"><div class="math" translate="no">$$\n$$</div></div><span class="figindex">(1)</span
|
|
204
|
+
'<figure data-label="$-a" data-group="$" data-number="1" id="label:$-a"><div class="figcontent"><div class="math" translate="no">$$\n$$</div></div><figcaption><span class="figindex">(1)</span></figcaption></figure>',
|
|
205
205
|
'<p><sup class="annotation" id="annotation:ref:1" title="(1)[1][2]"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" id="reference:ref:1" title="b"><span hidden="">b</span><a href="#reference:def:1">[1]</a></sup><sup class="reference" id="reference:ref:2" title="cd"><span hidden="">c<em>d</em></span><a href="#reference:def:2">[2]</a></sup></span><a href="#annotation:def:1">*1</a></sup></p>',
|
|
206
206
|
]);
|
|
207
207
|
assert.deepStrictEqual(
|
|
@@ -218,10 +218,17 @@ describe('Unit: parser/api/parse', () => {
|
|
|
218
218
|
['<p>a<span class="linebreak"> </span>b</p>']);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
-
it('
|
|
221
|
+
it('recursion', () => {
|
|
222
222
|
assert.deepStrictEqual(
|
|
223
|
-
[...parse(
|
|
224
|
-
['
|
|
223
|
+
[...parse('('.repeat(199)).children].map(el => el.outerHTML),
|
|
224
|
+
[`<p>${'('.repeat(199)}</p>`]);
|
|
225
|
+
assert.deepStrictEqual(
|
|
226
|
+
[...parse('('.repeat(200) + '\n\na').children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
227
|
+
[
|
|
228
|
+
'<h1 id="error:rnd" class="error">Error: Too much recursion.</h1>',
|
|
229
|
+
`<pre class="error" translate="no">${'('.repeat(200)}\n</pre>`,
|
|
230
|
+
'<p>a</p>',
|
|
231
|
+
]);
|
|
225
232
|
});
|
|
226
233
|
|
|
227
234
|
});
|
|
@@ -95,7 +95,7 @@ describe('Unit: parser/block/blockquote', () => {
|
|
|
95
95
|
assert.deepStrictEqual(inspect(parser('!>\n> a')), [['<blockquote><section><p>a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
|
|
96
96
|
assert.deepStrictEqual(inspect(parser('!>> ## a\n> ## a')), [['<blockquote><blockquote><section><h2>a</h2><ol class="annotations"></ol><ol class="references"></ol></section></blockquote><section><h2>a</h2><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
|
|
97
97
|
assert.deepStrictEqual(inspect(parser('!>> ~ a\n> ~ a')), [['<blockquote><blockquote><section><dl><dt>a</dt><dd></dd></dl><ol class="annotations"></ol><ol class="references"></ol></section></blockquote><section><dl><dt>a</dt><dd></dd></dl><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
|
|
98
|
-
assert.deepStrictEqual(inspect(parser('!>> ~~~figure $fig-a\n>> > \n>>\n~~~\n> ~~~figure $fig-a\n> > \n>\n[#a]\n~~~')), [['<blockquote><blockquote><section><figure data-label="fig-a" data-group="fig" data-number="1"><div class="figcontent"><blockquote></blockquote></div><span class="figindex">Fig. 1. </span
|
|
98
|
+
assert.deepStrictEqual(inspect(parser('!>> ~~~figure $fig-a\n>> > \n>>\n~~~\n> ~~~figure $fig-a\n> > \n>\n[#a]\n~~~')), [['<blockquote><blockquote><section><figure data-label="fig-a" data-group="fig" data-number="1"><div class="figcontent"><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1. </span></figcaption></figure><ol class="annotations"></ol><ol class="references"></ol></section></blockquote><section><figure data-label="fig-a" data-group="fig" data-number="1"><div class="figcontent"><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1. </span><a class="index">a</a></figcaption></figure><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
|
|
99
99
|
assert.deepStrictEqual(inspect(parser('!>> ((a))\n> ((a))')), [['<blockquote><blockquote><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup></p><ol class="annotations"><li>a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></blockquote><section><p><sup class="annotation disabled" title="a"><span hidden="">a</span><a>*1</a></sup></p><ol class="annotations"><li>a<sup><a>^1</a></sup></li></ol><ol class="references"></ol></section></blockquote>'], '']);
|
|
100
100
|
});
|
|
101
101
|
|
|
@@ -16,7 +16,7 @@ describe('Unit: parser/block/extension/aside', () => {
|
|
|
16
16
|
it('valid', () => {
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('~~~aside\n# 0\n~~~')), [['<aside id="index:0" class="aside"><h1>0</h1><ol class="annotations"></ol><ol class="references"></ol></aside>'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('~~~aside\n## 0\n~~~')), [['<aside id="index:0" class="aside"><h2>0</h2><ol class="annotations"></ol><ol class="references"></ol></aside>'], '']);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser('~~~aside\n# 0\n\n$-0.0\n\n## 1\n\n$fig-a\n> \n~~~')), [['<aside id="index:0" class="aside"><h1>0</h1><figure data-label="$-0.0" data-group="$" hidden="" data-number="0.0"></figure><h2>1</h2><figure data-label="fig-a" data-group="fig" data-number="1.1"><div class="figcontent"><blockquote></blockquote></div><span class="figindex">Fig. 1.1. </span
|
|
19
|
+
assert.deepStrictEqual(inspect(parser('~~~aside\n# 0\n\n$-0.0\n\n## 1\n\n$fig-a\n> \n~~~')), [['<aside id="index:0" class="aside"><h1>0</h1><figure data-label="$-0.0" data-group="$" hidden="" data-number="0.0"></figure><h2>1</h2><figure data-label="fig-a" data-group="fig" data-number="1.1"><div class="figcontent"><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1.1. </span></figcaption></figure><ol class="annotations"></ol><ol class="references"></ol></aside>'], '']);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
});
|