securemark 0.258.9 → 0.259.2
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/design.md +6 -5
- package/dist/index.js +535 -278
- package/markdown.d.ts +4 -3
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.test.ts +5 -5
- package/src/combinator/control/constraint/block.ts +3 -2
- package/src/combinator/control/constraint/contract.ts +7 -5
- package/src/combinator/control/constraint/line.test.ts +6 -6
- package/src/combinator/control/constraint/line.ts +6 -5
- package/src/combinator/control/manipulation/convert.ts +6 -5
- package/src/combinator/control/manipulation/fence.ts +2 -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 +3 -2
- package/src/combinator/control/manipulation/recovery.ts +6 -6
- package/src/combinator/control/manipulation/scope.ts +13 -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 +3 -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 +38 -34
- package/src/combinator/data/parser/inits.ts +3 -2
- package/src/combinator/data/parser/sequence.test.ts +10 -10
- package/src/combinator/data/parser/sequence.ts +3 -2
- package/src/combinator/data/parser/some.test.ts +13 -13
- package/src/combinator/data/parser/some.ts +3 -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 +3 -3
- package/src/parser/api/header.ts +1 -1
- package/src/parser/api/normalize.ts +1 -1
- package/src/parser/api/parse.ts +3 -3
- package/src/parser/autolink.test.ts +1 -1
- package/src/parser/autolink.ts +2 -2
- package/src/parser/block/blockquote.test.ts +1 -1
- package/src/parser/block/blockquote.ts +1 -1
- package/src/parser/block/codeblock.test.ts +1 -1
- package/src/parser/block/codeblock.ts +1 -1
- package/src/parser/block/dlist.test.ts +1 -1
- 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 +1 -1
- 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 +3 -3
- 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 +1 -1
- package/src/parser/block/olist.ts +5 -5
- package/src/parser/block/paragraph.test.ts +1 -1
- package/src/parser/block/reply/cite.test.ts +4 -2
- package/src/parser/block/reply/cite.ts +2 -1
- package/src/parser/block/reply/quote.test.ts +1 -1
- package/src/parser/block/reply/quote.ts +2 -2
- package/src/parser/block/reply.test.ts +1 -1
- package/src/parser/block/sidefence.test.ts +1 -1
- package/src/parser/block/table.test.ts +1 -1
- package/src/parser/block/table.ts +1 -1
- package/src/parser/block/ulist.test.ts +1 -1
- package/src/parser/block/ulist.ts +1 -1
- package/src/parser/block.ts +2 -2
- package/src/parser/context.ts +15 -6
- package/src/parser/header.test.ts +1 -1
- package/src/parser/header.ts +3 -3
- package/src/parser/inline/annotation.test.ts +1 -1
- package/src/parser/inline/annotation.ts +3 -4
- package/src/parser/inline/autolink/account.test.ts +1 -1
- package/src/parser/inline/autolink/anchor.test.ts +1 -1
- package/src/parser/inline/autolink/channel.test.ts +1 -1
- 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/hashtag.test.ts +1 -1
- package/src/parser/inline/autolink/url.test.ts +1 -1
- package/src/parser/inline/autolink.ts +1 -1
- package/src/parser/inline/bracket.test.ts +1 -1
- package/src/parser/inline/bracket.ts +8 -8
- 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/comment.ts +2 -2
- package/src/parser/inline/deletion.test.ts +1 -1
- package/src/parser/inline/deletion.ts +2 -2
- package/src/parser/inline/emphasis.test.ts +1 -1
- package/src/parser/inline/emphasis.ts +2 -2
- package/src/parser/inline/emstrong.ts +4 -4
- package/src/parser/inline/escape.ts +3 -3
- package/src/parser/inline/extension/index.test.ts +1 -1
- package/src/parser/inline/extension/index.ts +3 -4
- 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/extension/placeholder.ts +2 -2
- package/src/parser/inline/html.test.ts +1 -1
- package/src/parser/inline/html.ts +2 -2
- 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/insertion.ts +2 -2
- package/src/parser/inline/link.test.ts +1 -1
- package/src/parser/inline/link.ts +14 -22
- package/src/parser/inline/mark.test.ts +1 -1
- package/src/parser/inline/mark.ts +2 -2
- package/src/parser/inline/math.test.ts +1 -1
- package/src/parser/inline/math.ts +1 -1
- package/src/parser/inline/media.test.ts +1 -1
- package/src/parser/inline/media.ts +3 -3
- package/src/parser/inline/reference.test.ts +1 -1
- package/src/parser/inline/reference.ts +3 -4
- package/src/parser/inline/ruby.test.ts +1 -1
- package/src/parser/inline/ruby.ts +5 -5
- package/src/parser/inline/shortmedia.test.ts +1 -1
- package/src/parser/inline/strong.test.ts +1 -1
- package/src/parser/inline/strong.ts +2 -2
- package/src/parser/inline/template.test.ts +1 -1
- package/src/parser/inline/template.ts +2 -2
- package/src/parser/inline.test.ts +1 -1
- 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 +4 -4
- 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/util/quote.ts +1 -1
package/markdown.d.ts
CHANGED
|
@@ -577,6 +577,7 @@ export namespace MarkdownParser {
|
|
|
577
577
|
InlineParser.AutolinkParser.AnchorParser,
|
|
578
578
|
Parser<HTMLAnchorElement, Context, []>,
|
|
579
579
|
Parser<HTMLAnchorElement, Context, []>,
|
|
580
|
+
Parser<HTMLAnchorElement, Context, []>,
|
|
580
581
|
]>,
|
|
581
582
|
]> {
|
|
582
583
|
}
|
|
@@ -842,7 +843,7 @@ export namespace MarkdownParser {
|
|
|
842
843
|
// { uri }
|
|
843
844
|
// [abc]{uri nofollow}
|
|
844
845
|
Inline<'link'>,
|
|
845
|
-
Parser<
|
|
846
|
+
Parser<HTMLAnchorElement, Context, [
|
|
846
847
|
LinkParser.MediaLinkParser,
|
|
847
848
|
LinkParser.TextLinkParser,
|
|
848
849
|
]> {
|
|
@@ -850,7 +851,7 @@ export namespace MarkdownParser {
|
|
|
850
851
|
export namespace LinkParser {
|
|
851
852
|
export interface TextLinkParser extends
|
|
852
853
|
Inline<'link/textlink'>,
|
|
853
|
-
Parser<
|
|
854
|
+
Parser<HTMLAnchorElement, Context, [
|
|
854
855
|
Parser<(HTMLElement | string)[], Context, [
|
|
855
856
|
InlineParser,
|
|
856
857
|
]>,
|
|
@@ -859,7 +860,7 @@ export namespace MarkdownParser {
|
|
|
859
860
|
}
|
|
860
861
|
export interface MediaLinkParser extends
|
|
861
862
|
Inline<'link/medialink'>,
|
|
862
|
-
Parser<
|
|
863
|
+
Parser<HTMLAnchorElement, Context, [
|
|
863
864
|
Parser<HTMLElement[], Context, [
|
|
864
865
|
MediaParser,
|
|
865
866
|
ShortmediaParser,
|
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,11 @@ 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
|
|
9
|
+
return input => {
|
|
10
|
+
const { source, context } = input;
|
|
10
11
|
if (source === '') return;
|
|
11
12
|
context.memo ??= new Memo();
|
|
12
|
-
const result = parser(
|
|
13
|
+
const result = parser(input);
|
|
13
14
|
if (!result) return;
|
|
14
15
|
const rest = exec(result);
|
|
15
16
|
if (separation && !isEmpty(firstline(rest))) return;
|
|
@@ -24,10 +24,11 @@ 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
|
|
27
|
+
return input => {
|
|
28
|
+
const { source } = input;
|
|
28
29
|
if (source === '') return;
|
|
29
30
|
if (!match(source)) return;
|
|
30
|
-
const result = parser!(
|
|
31
|
+
const result = parser!(input);
|
|
31
32
|
assert(check(source, result));
|
|
32
33
|
if (!result) return;
|
|
33
34
|
assert(exec(result).length < source.length);
|
|
@@ -40,12 +41,13 @@ export function validate<T>(patterns: string | RegExp | (string | RegExp)[], has
|
|
|
40
41
|
export function verify<P extends Parser<unknown>>(parser: P, cond: (results: readonly Tree<P>[], rest: string, context: Context<P>) => boolean): P;
|
|
41
42
|
export function verify<T>(parser: Parser<T>, cond: (results: readonly T[], rest: string, context: Ctx) => boolean): Parser<T> {
|
|
42
43
|
assert(parser);
|
|
43
|
-
return
|
|
44
|
+
return input => {
|
|
45
|
+
const { source } = input;
|
|
44
46
|
if (source === '') return;
|
|
45
|
-
const result = parser(
|
|
47
|
+
const result = parser(input);
|
|
46
48
|
assert(check(source, result));
|
|
47
49
|
if (!result) return;
|
|
48
|
-
if (!cond(eval(result), exec(result), context)) return;
|
|
50
|
+
if (!cond(eval(result), exec(result), input.context)) return;
|
|
49
51
|
assert(exec(result).length < source.length);
|
|
50
52
|
return exec(result).length < source.length
|
|
51
53
|
? result
|
|
@@ -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,16 @@ 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
|
|
8
|
+
return input => {
|
|
9
|
+
const { source, context } = input;
|
|
9
10
|
if (source === '') return;
|
|
10
11
|
context.memo ??= new Memo();
|
|
11
12
|
const line = firstline(source);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const result = parser(line, context);
|
|
13
|
+
context.offset ??= 0;
|
|
14
|
+
context.offset += source.length - line.length;
|
|
15
|
+
const result = parser({ source: line, context });
|
|
15
16
|
assert(check(line, result));
|
|
16
|
-
|
|
17
|
+
context.offset -= source.length - line.length;
|
|
17
18
|
if (!result) return;
|
|
18
19
|
return isEmpty(exec(result))
|
|
19
20
|
? [eval(result), source.slice(line.length)]
|
|
@@ -3,15 +3,16 @@ 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
|
|
6
|
+
return input => {
|
|
7
|
+
const { source, context } = input;
|
|
7
8
|
if (source === '') return;
|
|
8
9
|
const src = conv(source);
|
|
9
10
|
if (src === '') return [[], ''];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const result = parser(src, context);
|
|
11
|
+
context.offset ??= 0;
|
|
12
|
+
context.offset += source.length - src.length;
|
|
13
|
+
const result = parser({ source: src, context });
|
|
13
14
|
assert(check(src, result));
|
|
14
|
-
|
|
15
|
+
context.offset -= source.length - src.length;
|
|
15
16
|
return result;
|
|
16
17
|
};
|
|
17
18
|
}
|
|
@@ -3,7 +3,8 @@ 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
|
|
6
|
+
return input => {
|
|
7
|
+
const { source } = input;
|
|
7
8
|
if (source === '') return;
|
|
8
9
|
const matches = source.match(opener);
|
|
9
10
|
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,13 @@ 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
|
|
7
|
+
return input => {
|
|
8
|
+
const { source } = input;
|
|
8
9
|
if (source === '') return;
|
|
9
10
|
const param = source.match(pattern);
|
|
10
11
|
if (!param) return;
|
|
11
12
|
assert(source.startsWith(param[0]));
|
|
12
|
-
const result = f(param)(
|
|
13
|
+
const result = f(param)(input);
|
|
13
14
|
assert(check(source, result, false));
|
|
14
15
|
if (!result) return;
|
|
15
16
|
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,17 @@ 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
|
|
11
|
+
return input => {
|
|
12
|
+
const { source, context } = input;
|
|
12
13
|
if (source === '') return;
|
|
13
14
|
const src = match(source);
|
|
14
15
|
assert(source.startsWith(src));
|
|
15
16
|
if (src === '') return;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const result = parser(src, context);
|
|
17
|
+
context.offset ??= 0;
|
|
18
|
+
context.offset += source.length - src.length;
|
|
19
|
+
const result = parser({ source: src, context });
|
|
19
20
|
assert(check(src, result));
|
|
20
|
-
|
|
21
|
+
context.offset -= source.length - src.length;
|
|
21
22
|
if (!result) return;
|
|
22
23
|
assert(exec(result).length < src.length);
|
|
23
24
|
return exec(result).length < src.length
|
|
@@ -31,21 +32,23 @@ export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Contex
|
|
|
31
32
|
export function rewrite<T>(scope: Parser<unknown>, parser: Parser<T>): Parser<T> {
|
|
32
33
|
assert(scope);
|
|
33
34
|
assert(parser);
|
|
34
|
-
return
|
|
35
|
+
return input => {
|
|
36
|
+
const { source, context } = input;
|
|
35
37
|
if (source === '') return;
|
|
36
38
|
const memo = context.memo;
|
|
37
39
|
context.memo = undefined;
|
|
38
|
-
const res1 = scope(
|
|
40
|
+
const res1 = scope(input);
|
|
39
41
|
assert(check(source, res1));
|
|
40
42
|
context.memo = memo;
|
|
41
43
|
if (!res1 || exec(res1).length >= source.length) return;
|
|
42
44
|
const src = source.slice(0, source.length - exec(res1).length);
|
|
43
45
|
assert(src !== '');
|
|
44
46
|
assert(source.startsWith(src));
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
context.offset ??= 0;
|
|
48
|
+
context.offset += source.length - src.length;
|
|
49
|
+
const res2 = parser({ source: src, context });
|
|
47
50
|
assert(check(src, res2));
|
|
48
|
-
|
|
51
|
+
context.offset -= source.length - src.length;
|
|
49
52
|
if (!res2) return;
|
|
50
53
|
assert(exec(res2) === '');
|
|
51
54
|
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 input => {
|
|
42
|
+
const { source: lmr_, context } = input;
|
|
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,10 @@ 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
|
|
10
|
+
return input => {
|
|
11
|
+
const { source, context } = input;
|
|
11
12
|
if (source === '') return;
|
|
12
|
-
const res1 = parser(
|
|
13
|
+
const res1 = parser(input);
|
|
13
14
|
assert(check(source, res1));
|
|
14
15
|
if (!res1) return;
|
|
15
16
|
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
|
});
|