securemark 0.259.1 → 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 +4 -0
- package/design.md +2 -1
- package/dist/index.js +427 -154
- 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 +23 -18
- 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 +1 -1
- package/src/parser/api/header.ts +1 -1
- package/src/parser/api/normalize.ts +1 -1
- package/src/parser/api/parse.ts +1 -1
- 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 +1 -1
- package/src/parser/block/reply/cite.ts +2 -2
- 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/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/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/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 -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 +1 -1
- package/src/parser/inline/link.ts +1 -1
- 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 +1 -1
- package/src/parser/inline/media.ts +2 -2
- package/src/parser/inline/reference.test.ts +1 -1
- 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 +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 +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/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,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
|
});
|
|
@@ -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,13 @@ 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,
|
|
61
|
+
return creation(cost, precedence(prec, input => {
|
|
62
|
+
const { source, context } = input;
|
|
62
63
|
if (source === '') return;
|
|
63
64
|
const memo = context.memo ??= new Memo();
|
|
64
65
|
context.memorable ??= ~0;
|
|
65
|
-
|
|
66
|
+
context.offset ??= 0;
|
|
67
|
+
const position = source.length + context.offset!;
|
|
66
68
|
const st0 = context.state ?? 0;
|
|
67
69
|
const st1 = context.state = st0 | state;
|
|
68
70
|
const cache = syntax && memo.get(position, syntax, st1);
|
|
@@ -70,7 +72,7 @@ export function syntax<T>(syntax: number, prec: number, cost: number, state: num
|
|
|
70
72
|
? cache.length === 0
|
|
71
73
|
? undefined
|
|
72
74
|
: [cache[0], source.slice(cache[1])]
|
|
73
|
-
: parser!(
|
|
75
|
+
: parser!(input);
|
|
74
76
|
if (syntax && st0 & context.memorable!) {
|
|
75
77
|
cache ?? memo.set(position, syntax, st1, eval(result), source.length - exec(result, '').length);
|
|
76
78
|
assert.deepStrictEqual(cache && cache, cache && memo.get(position, syntax, st1));
|
|
@@ -89,12 +91,12 @@ export function creation<P extends Parser<unknown>>(cost: number, parser: P): P;
|
|
|
89
91
|
export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown>): Parser<unknown> {
|
|
90
92
|
if (typeof cost === 'function') return creation(1, cost);
|
|
91
93
|
assert(cost > 0);
|
|
92
|
-
return
|
|
93
|
-
const
|
|
94
|
+
return input => {
|
|
95
|
+
const resources = input.context.resources ?? { clock: 1, recursion: 1 };
|
|
94
96
|
if (resources.clock <= 0) throw new Error('Too many creations');
|
|
95
97
|
if (resources.recursion <= 0) throw new Error('Too much recursion');
|
|
96
98
|
--resources.recursion;
|
|
97
|
-
const result = parser!(
|
|
99
|
+
const result = parser!(input);
|
|
98
100
|
++resources.recursion;
|
|
99
101
|
if (result) {
|
|
100
102
|
resources.clock -= cost;
|
|
@@ -106,10 +108,11 @@ export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown
|
|
|
106
108
|
export function precedence<P extends Parser<unknown>>(precedence: number, parser: P): P;
|
|
107
109
|
export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T> {
|
|
108
110
|
assert(precedence > 0);
|
|
109
|
-
return
|
|
111
|
+
return input => {
|
|
112
|
+
const { context } = input;
|
|
110
113
|
const p = context.precedence;
|
|
111
114
|
context.precedence = precedence;
|
|
112
|
-
const result = parser(
|
|
115
|
+
const result = parser(input);
|
|
113
116
|
context.precedence = p;
|
|
114
117
|
return result;
|
|
115
118
|
};
|
|
@@ -117,9 +120,9 @@ export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T>
|
|
|
117
120
|
|
|
118
121
|
export function guard<P extends Parser<unknown>>(f: (context: Context<P>) => boolean | number, parser: P): P;
|
|
119
122
|
export function guard<T>(f: (context: Ctx) => boolean | number, parser: Parser<T>): Parser<T> {
|
|
120
|
-
return
|
|
121
|
-
f(context)
|
|
122
|
-
? parser(
|
|
123
|
+
return input =>
|
|
124
|
+
f(input.context)
|
|
125
|
+
? parser(input)
|
|
123
126
|
: undefined;
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -131,12 +134,13 @@ export function constraint<T>(state: number, positive: boolean | Parser<T>, pars
|
|
|
131
134
|
positive = true;
|
|
132
135
|
}
|
|
133
136
|
assert(state);
|
|
134
|
-
return
|
|
137
|
+
return input => {
|
|
138
|
+
const { context } = input;
|
|
135
139
|
const s = positive
|
|
136
140
|
? state & context.state!
|
|
137
141
|
: state & ~context.state!;
|
|
138
142
|
return s === state
|
|
139
|
-
? parser!(
|
|
143
|
+
? parser!(input)
|
|
140
144
|
: undefined;
|
|
141
145
|
};
|
|
142
146
|
}
|
|
@@ -149,12 +153,13 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
|
|
|
149
153
|
positive = true;
|
|
150
154
|
}
|
|
151
155
|
assert(state);
|
|
152
|
-
return
|
|
156
|
+
return input => {
|
|
157
|
+
const { context } = input;
|
|
153
158
|
const s = context.state ?? 0;
|
|
154
159
|
context.state = positive
|
|
155
160
|
? s | state
|
|
156
161
|
: s & ~state;
|
|
157
|
-
const result = parser!(
|
|
162
|
+
const result = parser!(input);
|
|
158
163
|
context.state = s;
|
|
159
164
|
return result;
|
|
160
165
|
};
|
|
@@ -6,13 +6,14 @@ 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
|
|
9
|
+
return input => {
|
|
10
|
+
const { source, context } = input;
|
|
10
11
|
let rest = source;
|
|
11
12
|
let nodes: T[] | undefined;
|
|
12
13
|
for (let i = 0, len = parsers.length; i < len; ++i) {
|
|
13
14
|
if (rest === '') break;
|
|
14
15
|
if (context.delimiters?.match(rest, context.precedence)) break;
|
|
15
|
-
const result = parsers[i](rest, context);
|
|
16
|
+
const result = parsers[i]({ source: rest, context });
|
|
16
17
|
assert(check(rest, result));
|
|
17
18
|
if (!result) break;
|
|
18
19
|
nodes = nodes
|