securemark 0.259.1 → 0.260.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 +13 -0
- package/design.md +16 -5
- package/dist/index.js +548 -226
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.test.ts +5 -5
- package/src/combinator/control/constraint/block.ts +2 -2
- package/src/combinator/control/constraint/contract.ts +4 -4
- package/src/combinator/control/constraint/line.test.ts +6 -6
- package/src/combinator/control/constraint/line.ts +5 -5
- package/src/combinator/control/manipulation/convert.ts +5 -5
- package/src/combinator/control/manipulation/fence.ts +1 -1
- package/src/combinator/control/manipulation/indent.test.ts +14 -14
- package/src/combinator/control/manipulation/indent.ts +5 -5
- package/src/combinator/control/manipulation/lazy.ts +2 -2
- package/src/combinator/control/manipulation/match.ts +2 -2
- package/src/combinator/control/manipulation/recovery.ts +6 -6
- package/src/combinator/control/manipulation/scope.ts +11 -10
- package/src/combinator/control/manipulation/surround.ts +9 -8
- package/src/combinator/control/manipulation/trim.test.ts +9 -9
- package/src/combinator/control/monad/bind.ts +2 -2
- package/src/combinator/data/parser/context/memo.ts +3 -4
- package/src/combinator/data/parser/context.test.ts +9 -9
- package/src/combinator/data/parser/context.ts +18 -17
- package/src/combinator/data/parser/inits.ts +2 -2
- package/src/combinator/data/parser/sequence.test.ts +10 -10
- package/src/combinator/data/parser/sequence.ts +2 -2
- package/src/combinator/data/parser/some.test.ts +13 -13
- package/src/combinator/data/parser/some.ts +2 -2
- package/src/combinator/data/parser/subsequence.test.ts +14 -14
- package/src/combinator/data/parser/union.test.ts +10 -10
- package/src/combinator/data/parser/union.ts +2 -2
- package/src/combinator/data/parser.ts +6 -1
- package/src/parser/api/bind.ts +1 -1
- package/src/parser/api/header.ts +1 -1
- package/src/parser/api/normalize.ts +1 -1
- package/src/parser/api/parse.test.ts +19 -19
- package/src/parser/api/parse.ts +1 -1
- package/src/parser/autolink.test.ts +7 -7
- package/src/parser/autolink.ts +2 -2
- package/src/parser/block/blockquote.test.ts +9 -9
- package/src/parser/block/blockquote.ts +1 -1
- package/src/parser/block/codeblock.test.ts +5 -5
- package/src/parser/block/codeblock.ts +1 -1
- package/src/parser/block/dlist.test.ts +2 -2
- package/src/parser/block/extension/aside.test.ts +1 -1
- package/src/parser/block/extension/example.test.ts +1 -1
- package/src/parser/block/extension/example.ts +1 -1
- package/src/parser/block/extension/fig.test.ts +1 -1
- package/src/parser/block/extension/figbase.test.ts +1 -1
- package/src/parser/block/extension/figure.test.ts +2 -2
- package/src/parser/block/extension/figure.ts +1 -1
- package/src/parser/block/extension/message.test.ts +1 -1
- package/src/parser/block/extension/message.ts +1 -1
- package/src/parser/block/extension/placeholder.test.ts +1 -1
- package/src/parser/block/extension/table.test.ts +1 -1
- package/src/parser/block/extension/table.ts +1 -1
- package/src/parser/block/extension.test.ts +1 -1
- package/src/parser/block/heading.test.ts +8 -8
- package/src/parser/block/heading.ts +1 -1
- package/src/parser/block/horizontalrule.test.ts +1 -1
- package/src/parser/block/ilist.test.ts +1 -1
- package/src/parser/block/mathblock.test.ts +1 -1
- package/src/parser/block/olist.test.ts +2 -2
- package/src/parser/block/olist.ts +5 -5
- package/src/parser/block/paragraph.test.ts +15 -15
- package/src/parser/block/reply/cite.test.ts +12 -12
- package/src/parser/block/reply/cite.ts +2 -2
- package/src/parser/block/reply/quote.test.ts +4 -4
- package/src/parser/block/reply/quote.ts +2 -2
- package/src/parser/block/reply.test.ts +9 -9
- package/src/parser/block/sidefence.test.ts +7 -7
- package/src/parser/block/table.test.ts +1 -1
- package/src/parser/block/table.ts +1 -1
- package/src/parser/block/ulist.test.ts +2 -2
- package/src/parser/block/ulist.ts +1 -1
- package/src/parser/block.ts +2 -2
- package/src/parser/context.ts +8 -7
- package/src/parser/header.test.ts +1 -1
- package/src/parser/header.ts +3 -3
- package/src/parser/inline/annotation.test.ts +4 -4
- package/src/parser/inline/autolink/account.test.ts +12 -12
- package/src/parser/inline/autolink/account.ts +4 -2
- package/src/parser/inline/autolink/anchor.test.ts +10 -10
- package/src/parser/inline/autolink/anchor.ts +14 -11
- package/src/parser/inline/autolink/channel.test.ts +4 -4
- package/src/parser/inline/autolink/email.test.ts +1 -1
- package/src/parser/inline/autolink/email.ts +1 -1
- package/src/parser/inline/autolink/hashnum.test.ts +1 -1
- package/src/parser/inline/autolink/hashnum.ts +4 -2
- package/src/parser/inline/autolink/hashtag.test.ts +21 -21
- package/src/parser/inline/autolink/hashtag.ts +4 -2
- package/src/parser/inline/autolink/url.test.ts +56 -56
- package/src/parser/inline/bracket.test.ts +1 -1
- package/src/parser/inline/code.test.ts +1 -1
- package/src/parser/inline/code.ts +2 -2
- package/src/parser/inline/comment.test.ts +1 -1
- package/src/parser/inline/deletion.test.ts +1 -1
- package/src/parser/inline/emphasis.test.ts +1 -1
- package/src/parser/inline/emstrong.ts +2 -2
- package/src/parser/inline/escape.ts +3 -3
- package/src/parser/inline/extension/index.test.ts +1 -1
- package/src/parser/inline/extension/indexer.test.ts +1 -1
- package/src/parser/inline/extension/label.test.ts +1 -1
- package/src/parser/inline/extension/placeholder.test.ts +1 -1
- package/src/parser/inline/html.test.ts +1 -3
- package/src/parser/inline/html.ts +1 -1
- package/src/parser/inline/htmlentity.test.ts +1 -1
- package/src/parser/inline/htmlentity.ts +1 -1
- package/src/parser/inline/insertion.test.ts +1 -1
- package/src/parser/inline/link.test.ts +111 -111
- package/src/parser/inline/link.ts +30 -28
- package/src/parser/inline/mark.test.ts +1 -1
- package/src/parser/inline/math.test.ts +1 -1
- package/src/parser/inline/math.ts +1 -1
- package/src/parser/inline/media.test.ts +2 -1
- package/src/parser/inline/media.ts +3 -3
- package/src/parser/inline/reference.test.ts +4 -4
- package/src/parser/inline/ruby.test.ts +1 -1
- package/src/parser/inline/ruby.ts +3 -3
- package/src/parser/inline/shortmedia.test.ts +1 -1
- package/src/parser/inline/strong.test.ts +1 -1
- package/src/parser/inline/template.test.ts +1 -1
- package/src/parser/inline.test.ts +52 -52
- package/src/parser/locale.test.ts +1 -1
- package/src/parser/segment.ts +1 -1
- package/src/parser/source/escapable.test.ts +1 -1
- package/src/parser/source/escapable.ts +1 -1
- package/src/parser/source/line.test.ts +1 -1
- package/src/parser/source/line.ts +2 -2
- package/src/parser/source/str.ts +4 -4
- package/src/parser/source/text.test.ts +1 -1
- package/src/parser/source/text.ts +3 -3
- package/src/parser/source/unescapable.test.ts +1 -1
- package/src/parser/source/unescapable.ts +1 -1
- package/src/parser/visibility.ts +13 -13
- package/src/renderer/render/media/pdf.ts +1 -0
- package/src/renderer/render/media/twitter.ts +7 -1
- package/src/util/info.ts +2 -4
- package/src/util/quote.ts +1 -1
package/package.json
CHANGED
|
@@ -4,14 +4,14 @@ import { inspect } from '../../../debug.test';
|
|
|
4
4
|
describe('Unit: combinator/block', () => {
|
|
5
5
|
describe('block', () => {
|
|
6
6
|
it('invalid', () => {
|
|
7
|
-
assert.throws(() => block(_ => [[], '\n'])(' \n'));
|
|
7
|
+
assert.throws(() => block(_ => [[], '\n'])({ source: ' \n', context: {} }));
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
it('valid', () => {
|
|
11
|
-
assert.deepStrictEqual(inspect(block(_ => [[], ''])('\n')), [[], '']);
|
|
12
|
-
assert.deepStrictEqual(inspect(block(_ => [[], ''])(' \n')), [[], '']);
|
|
13
|
-
assert.deepStrictEqual(inspect(block(_ => [[], ''])('\n\n')), [[], '']);
|
|
14
|
-
assert.deepStrictEqual(inspect(block(_ => [[], '\n'])('\n\n')), [[], '\n']);
|
|
11
|
+
assert.deepStrictEqual(inspect(block(_ => [[], ''])({ source: '\n', context: {} })), [[], '']);
|
|
12
|
+
assert.deepStrictEqual(inspect(block(_ => [[], ''])({ source: ' \n', context: {} })), [[], '']);
|
|
13
|
+
assert.deepStrictEqual(inspect(block(_ => [[], ''])({ source: '\n\n', context: {} })), [[], '']);
|
|
14
|
+
assert.deepStrictEqual(inspect(block(_ => [[], '\n'])({ source: '\n\n', context: {} })), [[], '\n']);
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
});
|
|
@@ -6,10 +6,10 @@ import { firstline, isEmpty } from './line';
|
|
|
6
6
|
export function block<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
|
|
7
7
|
export function block<T>(parser: Parser<T>, separation = true): Parser<T> {
|
|
8
8
|
assert(parser);
|
|
9
|
-
return (source, context
|
|
9
|
+
return ({ source, context }) => {
|
|
10
10
|
if (source === '') return;
|
|
11
11
|
context.memo ??= new Memo();
|
|
12
|
-
const result = parser(source, context);
|
|
12
|
+
const result = parser({ source, context });
|
|
13
13
|
if (!result) return;
|
|
14
14
|
const rest = exec(result);
|
|
15
15
|
if (separation && !isEmpty(firstline(rest))) return;
|
|
@@ -24,10 +24,10 @@ export function validate<T>(patterns: string | RegExp | (string | RegExp)[], has
|
|
|
24
24
|
? `|| source.slice(0, ${pattern.length}) === '${pattern}'`
|
|
25
25
|
: `|| /${pattern.source}/${pattern.flags}.test(source)`),
|
|
26
26
|
].join(''))();
|
|
27
|
-
return (source, context) => {
|
|
27
|
+
return ({ source, context }) => {
|
|
28
28
|
if (source === '') return;
|
|
29
29
|
if (!match(source)) return;
|
|
30
|
-
const result = parser!(source, context);
|
|
30
|
+
const result = parser!({ source, context });
|
|
31
31
|
assert(check(source, result));
|
|
32
32
|
if (!result) return;
|
|
33
33
|
assert(exec(result).length < source.length);
|
|
@@ -40,9 +40,9 @@ export function validate<T>(patterns: string | RegExp | (string | RegExp)[], has
|
|
|
40
40
|
export function verify<P extends Parser<unknown>>(parser: P, cond: (results: readonly Tree<P>[], rest: string, context: Context<P>) => boolean): P;
|
|
41
41
|
export function verify<T>(parser: Parser<T>, cond: (results: readonly T[], rest: string, context: Ctx) => boolean): Parser<T> {
|
|
42
42
|
assert(parser);
|
|
43
|
-
return (source, context) => {
|
|
43
|
+
return ({ source, context }) => {
|
|
44
44
|
if (source === '') return;
|
|
45
|
-
const result = parser(source, context);
|
|
45
|
+
const result = parser({ source, context });
|
|
46
46
|
assert(check(source, result));
|
|
47
47
|
if (!result) return;
|
|
48
48
|
if (!cond(eval(result), exec(result), context)) return;
|
|
@@ -4,15 +4,15 @@ import { inspect } from '../../../debug.test';
|
|
|
4
4
|
describe('Unit: combinator/line', () => {
|
|
5
5
|
describe('line', () => {
|
|
6
6
|
it('invalid', () => {
|
|
7
|
-
assert.deepStrictEqual(inspect(line(_ => [[], ''])('')), undefined);
|
|
7
|
+
assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: '', context: {} })), undefined);
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
it('valid', () => {
|
|
11
|
-
assert.deepStrictEqual(inspect(line(_ => [[], ''])(' ')), [[], '']);
|
|
12
|
-
assert.deepStrictEqual(inspect(line(_ => [[], ''])('\n')), [[], '']);
|
|
13
|
-
assert.deepStrictEqual(inspect(line(_ => [[], ''])('\n\n')), [[], '\n']);
|
|
14
|
-
assert.deepStrictEqual(inspect(line(_ => [[], ''])(' \n')), [[], '']);
|
|
15
|
-
assert.deepStrictEqual(inspect(line(_ => [[], '\n'])(' \n')), [[], '']);
|
|
11
|
+
assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: ' ', context: {} })), [[], '']);
|
|
12
|
+
assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: '\n', context: {} })), [[], '']);
|
|
13
|
+
assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: '\n\n', context: {} })), [[], '\n']);
|
|
14
|
+
assert.deepStrictEqual(inspect(line(_ => [[], ''])({ source: ' \n', context: {} })), [[], '']);
|
|
15
|
+
assert.deepStrictEqual(inspect(line(_ => [[], '\n'])({ source: ' \n', context: {} })), [[], '']);
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
});
|
|
@@ -5,15 +5,15 @@ import { Memo } from '../../data/parser/context/memo';
|
|
|
5
5
|
export function line<P extends Parser<unknown>>(parser: P): P;
|
|
6
6
|
export function line<T>(parser: Parser<T>): Parser<T> {
|
|
7
7
|
assert(parser);
|
|
8
|
-
return (source, context
|
|
8
|
+
return ({ source, context }) => {
|
|
9
9
|
if (source === '') return;
|
|
10
10
|
context.memo ??= new Memo();
|
|
11
11
|
const line = firstline(source);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const result = parser(line, context);
|
|
12
|
+
context.offset ??= 0;
|
|
13
|
+
context.offset += source.length - line.length;
|
|
14
|
+
const result = parser({ source: line, context });
|
|
15
15
|
assert(check(line, result));
|
|
16
|
-
|
|
16
|
+
context.offset -= source.length - line.length;
|
|
17
17
|
if (!result) return;
|
|
18
18
|
return isEmpty(exec(result))
|
|
19
19
|
? [eval(result), source.slice(line.length)]
|
|
@@ -3,15 +3,15 @@ import { Parser, check } from '../../data/parser';
|
|
|
3
3
|
export function convert<P extends Parser<unknown>>(conv: (source: string) => string, parser: P): P;
|
|
4
4
|
export function convert<T>(conv: (source: string) => string, parser: Parser<T>): Parser<T> {
|
|
5
5
|
assert(parser);
|
|
6
|
-
return (source, context
|
|
6
|
+
return ({ source, context }) => {
|
|
7
7
|
if (source === '') return;
|
|
8
8
|
const src = conv(source);
|
|
9
9
|
if (src === '') return [[], ''];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const result = parser(src, context);
|
|
10
|
+
context.offset ??= 0;
|
|
11
|
+
context.offset += source.length - src.length;
|
|
12
|
+
const result = parser({ source: src, context });
|
|
13
13
|
assert(check(src, result));
|
|
14
|
-
|
|
14
|
+
context.offset -= source.length - src.length;
|
|
15
15
|
return result;
|
|
16
16
|
};
|
|
17
17
|
}
|
|
@@ -3,7 +3,7 @@ import { firstline, isEmpty } from '../constraint/line';
|
|
|
3
3
|
import { unshift } from 'spica/array';
|
|
4
4
|
|
|
5
5
|
export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: RegExp, limit: number, separation = true): Parser<string, C, D> {
|
|
6
|
-
return source => {
|
|
6
|
+
return ({ source }) => {
|
|
7
7
|
if (source === '') return;
|
|
8
8
|
const matches = source.match(opener);
|
|
9
9
|
if (!matches) return;
|
|
@@ -4,20 +4,20 @@ import { inspect } from '../../../debug.test';
|
|
|
4
4
|
describe('Unit: combinator/indent', () => {
|
|
5
5
|
describe('indent', () => {
|
|
6
6
|
it('valid', () => {
|
|
7
|
-
const parser = indent((
|
|
8
|
-
assert.deepStrictEqual(inspect(parser('', {})), undefined);
|
|
9
|
-
assert.deepStrictEqual(inspect(parser(' ', {})), undefined);
|
|
10
|
-
assert.deepStrictEqual(inspect(parser(' ', {})), undefined);
|
|
11
|
-
assert.deepStrictEqual(inspect(parser('a ', {})), undefined);
|
|
12
|
-
assert.deepStrictEqual(inspect(parser(' a\n', {})), [['a'], '']);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser(' a ', {})), [['a '], '']);
|
|
14
|
-
assert.deepStrictEqual(inspect(parser(' a \n', {})), [['a '], '']);
|
|
15
|
-
assert.deepStrictEqual(inspect(parser(' a', {})), [['a'], '']);
|
|
16
|
-
assert.deepStrictEqual(inspect(parser(' a\n a', {})), [['a\na'], '']);
|
|
17
|
-
assert.deepStrictEqual(inspect(parser(' a\n a', {})), [['a\n a'], '']);
|
|
18
|
-
assert.deepStrictEqual(inspect(parser(' a\n a', {})), [['a'], ' a']);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser(' \ta', {})), [['\ta'], '']);
|
|
20
|
-
assert.deepStrictEqual(inspect(parser('\ta', {})), [['a'], '']);
|
|
7
|
+
const parser = indent(({ source }) => [[source], '']);
|
|
8
|
+
assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
|
|
9
|
+
assert.deepStrictEqual(inspect(parser({ source: ' ', context: {} })), undefined);
|
|
10
|
+
assert.deepStrictEqual(inspect(parser({ source: ' ', context: {} })), undefined);
|
|
11
|
+
assert.deepStrictEqual(inspect(parser({ source: 'a ', context: {} })), undefined);
|
|
12
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a\n', context: {} })), [['a'], '']);
|
|
13
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a ', context: {} })), [['a '], '']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a \n', context: {} })), [['a '], '']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a', context: {} })), [['a'], '']);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a\n a', context: {} })), [['a\na'], '']);
|
|
17
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a\n a', context: {} })), [['a\n a'], '']);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a\n a', context: {} })), [['a'], ' a']);
|
|
19
|
+
assert.deepStrictEqual(inspect(parser({ source: ' \ta', context: {} })), [['\ta'], '']);
|
|
20
|
+
assert.deepStrictEqual(inspect(parser({ source: '\ta', context: {} })), [['a'], '']);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
});
|
|
@@ -17,14 +17,14 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
|
|
|
17
17
|
opener,
|
|
18
18
|
memoize(
|
|
19
19
|
([indent]) =>
|
|
20
|
-
some(line(open(indent, source => [[source], '']))),
|
|
20
|
+
some(line(open(indent, ({ source }) => [[source], '']))),
|
|
21
21
|
([indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
|
|
22
22
|
(lines, rest, context) => {
|
|
23
23
|
assert(parser = parser as Parser<T>);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const result = parser(trimBlockEnd(lines.join('')), context);
|
|
27
|
-
|
|
24
|
+
context.offset ??= 0;
|
|
25
|
+
context.offset += rest.length;
|
|
26
|
+
const result = parser({ source: trimBlockEnd(lines.join('')), context });
|
|
27
|
+
context.offset -= rest.length;
|
|
28
28
|
return result && exec(result) === ''
|
|
29
29
|
? [eval(result), rest]
|
|
30
30
|
: undefined;
|
|
@@ -3,6 +3,6 @@ import { Parser } from '../../data/parser';
|
|
|
3
3
|
export function lazy<P extends Parser<unknown>>(builder: () => P): P;
|
|
4
4
|
export function lazy<T>(builder: () => Parser<T>): Parser<T> {
|
|
5
5
|
let parser: Parser<T>;
|
|
6
|
-
return
|
|
7
|
-
(parser ??= builder())(
|
|
6
|
+
return input =>
|
|
7
|
+
(parser ??= builder())(input);
|
|
8
8
|
}
|
|
@@ -4,12 +4,12 @@ import { Parser, exec, check } from '../../data/parser';
|
|
|
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
6
|
assert(!pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^'));
|
|
7
|
-
return (source, context) => {
|
|
7
|
+
return ({ source, context }) => {
|
|
8
8
|
if (source === '') return;
|
|
9
9
|
const param = source.match(pattern);
|
|
10
10
|
if (!param) return;
|
|
11
11
|
assert(source.startsWith(param[0]));
|
|
12
|
-
const result = f(param)(source, context);
|
|
12
|
+
const result = f(param)({ source, context });
|
|
13
13
|
assert(check(source, result, false));
|
|
14
14
|
if (!result) return;
|
|
15
15
|
return exec(result).length < source.length && exec(result).length <= source.length
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { Parser,
|
|
1
|
+
import { Parser, Input, Result, Tree, Context } from '../../data/parser';
|
|
2
2
|
|
|
3
|
-
export function recover<P extends Parser<unknown>>(parser: P, fallback: (
|
|
4
|
-
export function recover<T>(parser: Parser<T>, fallback: (
|
|
5
|
-
return
|
|
3
|
+
export function recover<P extends Parser<unknown>>(parser: P, fallback: (input: Input<Context<P>>, reason: unknown) => Result<Tree<P>>): P;
|
|
4
|
+
export function recover<T>(parser: Parser<T>, fallback: (input: Input, reason: unknown) => Result<T>): Parser<T> {
|
|
5
|
+
return input => {
|
|
6
6
|
try {
|
|
7
|
-
return parser(
|
|
7
|
+
return parser(input);
|
|
8
8
|
}
|
|
9
9
|
catch (reason) {
|
|
10
10
|
assert(reason instanceof Error && reason.name === 'AssertionError' && !+console.error(reason) && eval(`throw new Error("${reason.name}")`) || 1);
|
|
11
|
-
return fallback(
|
|
11
|
+
return fallback(input, reason);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
}
|
|
@@ -8,16 +8,16 @@ export function focus<T>(scope: string | RegExp, parser: Parser<T>): Parser<T> {
|
|
|
8
8
|
const match: (source: string) => string = typeof scope === 'string'
|
|
9
9
|
? source => source.slice(0, scope.length) === scope ? scope : ''
|
|
10
10
|
: source => source.match(scope)?.[0] ?? '';
|
|
11
|
-
return (source, context
|
|
11
|
+
return ({ source, context }) => {
|
|
12
12
|
if (source === '') return;
|
|
13
13
|
const src = match(source);
|
|
14
14
|
assert(source.startsWith(src));
|
|
15
15
|
if (src === '') return;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const result = parser(src, context);
|
|
16
|
+
context.offset ??= 0;
|
|
17
|
+
context.offset += source.length - src.length;
|
|
18
|
+
const result = parser({ source: src, context });
|
|
19
19
|
assert(check(src, result));
|
|
20
|
-
|
|
20
|
+
context.offset -= source.length - src.length;
|
|
21
21
|
if (!result) return;
|
|
22
22
|
assert(exec(result).length < src.length);
|
|
23
23
|
return exec(result).length < src.length
|
|
@@ -31,21 +31,22 @@ export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Contex
|
|
|
31
31
|
export function rewrite<T>(scope: Parser<unknown>, parser: Parser<T>): Parser<T> {
|
|
32
32
|
assert(scope);
|
|
33
33
|
assert(parser);
|
|
34
|
-
return (source, context
|
|
34
|
+
return ({ source, context }) => {
|
|
35
35
|
if (source === '') return;
|
|
36
36
|
const memo = context.memo;
|
|
37
37
|
context.memo = undefined;
|
|
38
|
-
const res1 = scope(source, context);
|
|
38
|
+
const res1 = scope({ source, context });
|
|
39
39
|
assert(check(source, res1));
|
|
40
40
|
context.memo = memo;
|
|
41
41
|
if (!res1 || exec(res1).length >= source.length) return;
|
|
42
42
|
const src = source.slice(0, source.length - exec(res1).length);
|
|
43
43
|
assert(src !== '');
|
|
44
44
|
assert(source.startsWith(src));
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
context.offset ??= 0;
|
|
46
|
+
context.offset += source.length - src.length;
|
|
47
|
+
const res2 = parser({ source: src, context });
|
|
47
48
|
assert(check(src, res2));
|
|
48
|
-
|
|
49
|
+
context.offset -= source.length - src.length;
|
|
49
50
|
if (!res2) return;
|
|
50
51
|
assert(exec(res2) === '');
|
|
51
52
|
return exec(res2).length < src.length
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
|
-
import { Parser, Result, Ctx, Tree, Context, SubParsers, SubTree, IntermediateParser, eval, exec, check } from '../../data/parser';
|
|
2
|
+
import { Parser, Input, Result, Ctx, Tree, Context, SubParsers, SubTree, IntermediateParser, eval, exec, check } from '../../data/parser';
|
|
3
3
|
import { fmap } from '../monad/fmap';
|
|
4
4
|
import { unshift, push } from 'spica/array';
|
|
5
5
|
|
|
@@ -38,19 +38,20 @@ export function surround<T>(
|
|
|
38
38
|
case 'object':
|
|
39
39
|
return surround(opener, parser, match(closer), optional, f, g);
|
|
40
40
|
}
|
|
41
|
-
return (
|
|
41
|
+
return ({ source, context }) => {
|
|
42
|
+
const lmr_ = source;
|
|
42
43
|
if (lmr_ === '') return;
|
|
43
|
-
const res1 = opener(lmr_, context);
|
|
44
|
+
const res1 = opener({ source: lmr_, context });
|
|
44
45
|
assert(check(lmr_, res1, false));
|
|
45
46
|
if (!res1) return;
|
|
46
47
|
const rl = eval(res1);
|
|
47
48
|
const mr_ = exec(res1);
|
|
48
|
-
const res2 = mr_ !== '' ? parser(mr_, context) : undefined;
|
|
49
|
+
const res2 = mr_ !== '' ? parser({ source: mr_, context }) : undefined;
|
|
49
50
|
assert(check(mr_, res2));
|
|
50
51
|
const rm = eval(res2);
|
|
51
52
|
const r_ = exec(res2, mr_);
|
|
52
53
|
if (!rm && !optional) return;
|
|
53
|
-
const res3 = closer(r_, context);
|
|
54
|
+
const res3 = closer({ source: r_, context });
|
|
54
55
|
assert(check(r_, res3, false));
|
|
55
56
|
const rr = eval(res3);
|
|
56
57
|
const rest = exec(res3, r_);
|
|
@@ -65,12 +66,12 @@ export function surround<T>(
|
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
function match(pattern: string | RegExp): (
|
|
69
|
+
function match(pattern: string | RegExp): (input: Input) => [never[], string] | undefined {
|
|
69
70
|
switch (typeof pattern) {
|
|
70
71
|
case 'string':
|
|
71
|
-
return source => source.slice(0, pattern.length) === pattern ? [[], source.slice(pattern.length)] : undefined;
|
|
72
|
+
return ({ source }) => source.slice(0, pattern.length) === pattern ? [[], source.slice(pattern.length)] : undefined;
|
|
72
73
|
case 'object':
|
|
73
|
-
return source => {
|
|
74
|
+
return ({ source }) => {
|
|
74
75
|
const m = source.match(pattern);
|
|
75
76
|
return m
|
|
76
77
|
? [[], source.slice(m[0].length)]
|
|
@@ -4,15 +4,15 @@ import { inspect } from '../../../debug.test';
|
|
|
4
4
|
describe('Unit: combinator/trim', () => {
|
|
5
5
|
describe('trim', () => {
|
|
6
6
|
it('', () => {
|
|
7
|
-
const parser = trim(
|
|
8
|
-
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
9
|
-
assert.deepStrictEqual(inspect(parser('a')), [['a'], '']);
|
|
10
|
-
assert.deepStrictEqual(inspect(parser('a\n')), [['a'], '']);
|
|
11
|
-
assert.deepStrictEqual(inspect(parser('a ')), [['a'], '']);
|
|
12
|
-
assert.deepStrictEqual(inspect(parser('a \n')), [['a'], '']);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser(' a')), [['a'], '']);
|
|
14
|
-
assert.deepStrictEqual(inspect(parser(' a ')), [['a'], '']);
|
|
15
|
-
assert.deepStrictEqual(inspect(parser(' a \n b \n')), [['a \n b'], '']);
|
|
7
|
+
const parser = trim(({ source }) => [[source], '']);
|
|
8
|
+
assert.deepStrictEqual(inspect(parser({ source: '', context: {} })), undefined);
|
|
9
|
+
assert.deepStrictEqual(inspect(parser({ source: 'a', context: {} })), [['a'], '']);
|
|
10
|
+
assert.deepStrictEqual(inspect(parser({ source: 'a\n', context: {} })), [['a'], '']);
|
|
11
|
+
assert.deepStrictEqual(inspect(parser({ source: 'a ', context: {} })), [['a'], '']);
|
|
12
|
+
assert.deepStrictEqual(inspect(parser({ source: 'a \n', context: {} })), [['a'], '']);
|
|
13
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a', context: {} })), [['a'], '']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a ', context: {} })), [['a'], '']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser({ source: ' a \n b \n', context: {} })), [['a \n b'], '']);
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
});
|
|
@@ -7,9 +7,9 @@ export function bind<T, P extends Parser<unknown>>(parser: Parser<T, Context<P>,
|
|
|
7
7
|
export function bind<U, P extends Parser<unknown>>(parser: P, f: (nodes: Tree<P>[], rest: string, context: Context<P>) => Result<U, Context<P>, SubParsers<P>>): Parser<U, Context<P>, SubParsers<P>>;
|
|
8
8
|
export function bind<T, U>(parser: Parser<T>, f: (nodes: T[], rest: string, context: Ctx) => Result<U>): Parser<U> {
|
|
9
9
|
assert(parser);
|
|
10
|
-
return (source, context) => {
|
|
10
|
+
return ({ source, context }) => {
|
|
11
11
|
if (source === '') return;
|
|
12
|
-
const res1 = parser(source, context);
|
|
12
|
+
const res1 = parser({ source, context });
|
|
13
13
|
assert(check(source, res1));
|
|
14
14
|
if (!res1) return;
|
|
15
15
|
const res2 = f(eval(res1), exec(res1), context);
|
|
@@ -3,14 +3,13 @@ export class Memo {
|
|
|
3
3
|
public get length(): number {
|
|
4
4
|
return this.memory.length;
|
|
5
5
|
}
|
|
6
|
-
public offset = 0;
|
|
7
6
|
public get(
|
|
8
7
|
position: number,
|
|
9
8
|
syntax: number,
|
|
10
9
|
state: number,
|
|
11
10
|
): readonly [any[], number] | readonly [] | undefined {
|
|
12
11
|
//console.log('get', position + this.offset, syntax, state, this.memory[position + this.offset - 1]?.[`${syntax}:${state}`]);;
|
|
13
|
-
const cache = this.memory[position
|
|
12
|
+
const cache = this.memory[position - 1]?.[`${syntax}:${state}`];
|
|
14
13
|
return cache?.length === 2
|
|
15
14
|
? [cache[0].slice(), cache[1]]
|
|
16
15
|
: cache;
|
|
@@ -22,7 +21,7 @@ export class Memo {
|
|
|
22
21
|
nodes: any[] | undefined,
|
|
23
22
|
offset: number,
|
|
24
23
|
): void {
|
|
25
|
-
const record = this.memory[position
|
|
24
|
+
const record = this.memory[position - 1] ??= {};
|
|
26
25
|
assert(!record[`${syntax}:${state}`]);
|
|
27
26
|
record[`${syntax}:${state}`] = nodes
|
|
28
27
|
? [nodes.slice(), offset]
|
|
@@ -31,7 +30,7 @@ export class Memo {
|
|
|
31
30
|
}
|
|
32
31
|
public clear(position: number): void {
|
|
33
32
|
const memory = this.memory;
|
|
34
|
-
for (let i = position
|
|
33
|
+
for (let i = position, len = memory.length; i < len; ++i) {
|
|
35
34
|
memory.pop();
|
|
36
35
|
}
|
|
37
36
|
//console.log('clear', position + this.offset + 1);
|
|
@@ -10,26 +10,26 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
10
10
|
|
|
11
11
|
describe('reset', () => {
|
|
12
12
|
const parser: Parser<number> = some(creation(
|
|
13
|
-
(
|
|
13
|
+
({ source, context }) => [[context.resources?.clock ?? NaN], source.slice(1)]));
|
|
14
14
|
|
|
15
15
|
it('root', () => {
|
|
16
16
|
const base: Context = { resources: { clock: 3, recursion: 1 } };
|
|
17
17
|
const ctx: Context = {};
|
|
18
|
-
assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
|
|
18
|
+
assert.deepStrictEqual(reset(base, parser)({ source: '123', context: ctx }), [[3, 2, 1], '']);
|
|
19
19
|
assert(base.resources?.clock === 3);
|
|
20
20
|
assert(ctx.resources?.clock === undefined);
|
|
21
|
-
assert.throws(() => reset(base, parser)('1234', ctx));
|
|
21
|
+
assert.throws(() => reset(base, parser)({ source: '1234', context: ctx }));
|
|
22
22
|
assert(ctx.resources?.clock === undefined);
|
|
23
|
-
assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
|
|
23
|
+
assert.deepStrictEqual(reset(base, parser)({ source: '123', context: ctx }), [[3, 2, 1], '']);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it('node', () => {
|
|
27
27
|
const base: Context = { resources: { clock: 3, recursion: 1 } };
|
|
28
28
|
const ctx: Context = { resources: { clock: 1, recursion: 1 } };
|
|
29
|
-
assert.deepStrictEqual(reset(base, parser)('1', ctx), [[1], '']);
|
|
29
|
+
assert.deepStrictEqual(reset(base, parser)({ source: '1', context: ctx }), [[1], '']);
|
|
30
30
|
assert(base.resources?.clock === 3);
|
|
31
31
|
assert(ctx.resources?.clock === 0);
|
|
32
|
-
assert.throws(() => reset(base, parser)('1', ctx));
|
|
32
|
+
assert.throws(() => reset(base, parser)({ source: '1', context: ctx }));
|
|
33
33
|
assert(ctx.resources?.clock === 0);
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -37,15 +37,15 @@ describe('Unit: combinator/data/parser/context', () => {
|
|
|
37
37
|
|
|
38
38
|
describe('context', () => {
|
|
39
39
|
const parser: Parser<boolean, Context> = some(creation(
|
|
40
|
-
(
|
|
40
|
+
({ source, context }) => [[context.status!], source.slice(1)]));
|
|
41
41
|
|
|
42
42
|
it('', () => {
|
|
43
43
|
const base: Context = { status: true };
|
|
44
44
|
const ctx: Context = { resources: { clock: 3, recursion: 1 } };
|
|
45
|
-
assert.deepStrictEqual(context(base, parser)('123', ctx), [[true, true, true], '']);
|
|
45
|
+
assert.deepStrictEqual(context(base, parser)({ source: '123', context: ctx }), [[true, true, true], '']);
|
|
46
46
|
assert(ctx.resources?.clock === 0);
|
|
47
47
|
assert(ctx.status === undefined);
|
|
48
|
-
assert.throws(() => reset(base, parser)('1', ctx));
|
|
48
|
+
assert.throws(() => reset(base, parser)({ source: '1', context: ctx }));
|
|
49
49
|
assert(ctx.resources?.clock === 0);
|
|
50
50
|
assert(ctx.status === undefined);
|
|
51
51
|
});
|
|
@@ -9,7 +9,7 @@ export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
|
9
9
|
assert(Object.freeze(base));
|
|
10
10
|
const changes = Object.entries(base);
|
|
11
11
|
const values = Array(changes.length);
|
|
12
|
-
return (source, context) =>
|
|
12
|
+
return ({ source, context }) =>
|
|
13
13
|
apply(parser, source, ObjectCreate(context), changes, values);
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
|
19
19
|
assert(Object.freeze(base));
|
|
20
20
|
const changes = Object.entries(base);
|
|
21
21
|
const values = Array(changes.length);
|
|
22
|
-
return (source, context) =>
|
|
22
|
+
return ({ source, context }) =>
|
|
23
23
|
apply(parser, source, context, changes, values);
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -41,7 +41,7 @@ function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: [str
|
|
|
41
41
|
context[prop] = change[1];
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
const result = parser(source, context);
|
|
44
|
+
const result = parser({ source, context });
|
|
45
45
|
if (context) for (let i = 0; i < changes.length; ++i) {
|
|
46
46
|
const change = changes[i];
|
|
47
47
|
const prop = change[0];
|
|
@@ -58,11 +58,12 @@ 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, state: number, parser: P): P;
|
|
60
60
|
export function syntax<T>(syntax: number, prec: number, cost: number, state: number, parser?: Parser<T>): Parser<T> {
|
|
61
|
-
return creation(cost, precedence(prec, (source, context) => {
|
|
61
|
+
return creation(cost, precedence(prec, ({ source, context }) => {
|
|
62
62
|
if (source === '') return;
|
|
63
63
|
const memo = context.memo ??= new Memo();
|
|
64
64
|
context.memorable ??= ~0;
|
|
65
|
-
|
|
65
|
+
context.offset ??= 0;
|
|
66
|
+
const position = source.length + context.offset!;
|
|
66
67
|
const st0 = context.state ?? 0;
|
|
67
68
|
const st1 = context.state = st0 | state;
|
|
68
69
|
const cache = syntax && memo.get(position, syntax, st1);
|
|
@@ -70,7 +71,7 @@ export function syntax<T>(syntax: number, prec: number, cost: number, state: num
|
|
|
70
71
|
? cache.length === 0
|
|
71
72
|
? undefined
|
|
72
73
|
: [cache[0], source.slice(cache[1])]
|
|
73
|
-
: parser!(source, context);
|
|
74
|
+
: parser!({ source, context });
|
|
74
75
|
if (syntax && st0 & context.memorable!) {
|
|
75
76
|
cache ?? memo.set(position, syntax, st1, eval(result), source.length - exec(result, '').length);
|
|
76
77
|
assert.deepStrictEqual(cache && cache, cache && memo.get(position, syntax, st1));
|
|
@@ -89,12 +90,12 @@ export function creation<P extends Parser<unknown>>(cost: number, parser: P): P;
|
|
|
89
90
|
export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown>): Parser<unknown> {
|
|
90
91
|
if (typeof cost === 'function') return creation(1, cost);
|
|
91
92
|
assert(cost > 0);
|
|
92
|
-
return (source, context) => {
|
|
93
|
-
const
|
|
93
|
+
return ({ source, context }) => {
|
|
94
|
+
const resources = context.resources ?? { clock: 1, recursion: 1 };
|
|
94
95
|
if (resources.clock <= 0) throw new Error('Too many creations');
|
|
95
96
|
if (resources.recursion <= 0) throw new Error('Too much recursion');
|
|
96
97
|
--resources.recursion;
|
|
97
|
-
const result = parser!(source, context);
|
|
98
|
+
const result = parser!({ source, context });
|
|
98
99
|
++resources.recursion;
|
|
99
100
|
if (result) {
|
|
100
101
|
resources.clock -= cost;
|
|
@@ -106,10 +107,10 @@ export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown
|
|
|
106
107
|
export function precedence<P extends Parser<unknown>>(precedence: number, parser: P): P;
|
|
107
108
|
export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T> {
|
|
108
109
|
assert(precedence > 0);
|
|
109
|
-
return (source, context) => {
|
|
110
|
+
return ({ source, context }) => {
|
|
110
111
|
const p = context.precedence;
|
|
111
112
|
context.precedence = precedence;
|
|
112
|
-
const result = parser(source, context);
|
|
113
|
+
const result = parser({ source, context });
|
|
113
114
|
context.precedence = p;
|
|
114
115
|
return result;
|
|
115
116
|
};
|
|
@@ -117,9 +118,9 @@ export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T>
|
|
|
117
118
|
|
|
118
119
|
export function guard<P extends Parser<unknown>>(f: (context: Context<P>) => boolean | number, parser: P): P;
|
|
119
120
|
export function guard<T>(f: (context: Ctx) => boolean | number, parser: Parser<T>): Parser<T> {
|
|
120
|
-
return (source, context) =>
|
|
121
|
+
return ({ source, context }) =>
|
|
121
122
|
f(context)
|
|
122
|
-
? parser(source, context)
|
|
123
|
+
? parser({ source, context })
|
|
123
124
|
: undefined;
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -131,12 +132,12 @@ export function constraint<T>(state: number, positive: boolean | Parser<T>, pars
|
|
|
131
132
|
positive = true;
|
|
132
133
|
}
|
|
133
134
|
assert(state);
|
|
134
|
-
return (source, context) => {
|
|
135
|
+
return ({ source, context }) => {
|
|
135
136
|
const s = positive
|
|
136
137
|
? state & context.state!
|
|
137
138
|
: state & ~context.state!;
|
|
138
139
|
return s === state
|
|
139
|
-
? parser!(source, context)
|
|
140
|
+
? parser!({ source, context })
|
|
140
141
|
: undefined;
|
|
141
142
|
};
|
|
142
143
|
}
|
|
@@ -149,12 +150,12 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
|
|
|
149
150
|
positive = true;
|
|
150
151
|
}
|
|
151
152
|
assert(state);
|
|
152
|
-
return (source, context) => {
|
|
153
|
+
return ({ source, context }) => {
|
|
153
154
|
const s = context.state ?? 0;
|
|
154
155
|
context.state = positive
|
|
155
156
|
? s | state
|
|
156
157
|
: s & ~state;
|
|
157
|
-
const result = parser!(source, context);
|
|
158
|
+
const result = parser!({ source, context });
|
|
158
159
|
context.state = s;
|
|
159
160
|
return result;
|
|
160
161
|
};
|
|
@@ -6,13 +6,13 @@ export function inits<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?
|
|
|
6
6
|
export function inits<T, D extends Parser<T>[]>(parsers: D, resume?: (nodes: T[], rest: string) => boolean): Parser<T, Ctx, D> {
|
|
7
7
|
assert(parsers.every(f => f));
|
|
8
8
|
if (parsers.length === 1) return parsers[0];
|
|
9
|
-
return (source, context) => {
|
|
9
|
+
return ({ source, context }) => {
|
|
10
10
|
let rest = source;
|
|
11
11
|
let nodes: T[] | undefined;
|
|
12
12
|
for (let i = 0, len = parsers.length; i < len; ++i) {
|
|
13
13
|
if (rest === '') break;
|
|
14
14
|
if (context.delimiters?.match(rest, context.precedence)) break;
|
|
15
|
-
const result = parsers[i](rest, context);
|
|
15
|
+
const result = parsers[i]({ source: rest, context });
|
|
16
16
|
assert(check(rest, result));
|
|
17
17
|
if (!result) break;
|
|
18
18
|
nodes = nodes
|