securemark 0.289.2 → 0.289.4
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 +8 -0
- package/design.md +7 -6
- package/dist/index.js +141 -88
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +1 -1
- package/src/combinator/control/constraint/contract.ts +7 -7
- package/src/combinator/control/constraint/line.ts +1 -1
- package/src/combinator/control/manipulation/clear.ts +7 -0
- package/src/combinator/control/manipulation/convert.ts +1 -1
- package/src/combinator/control/manipulation/duplicate.ts +4 -4
- package/src/combinator/control/manipulation/fallback.ts +3 -3
- package/src/combinator/control/manipulation/indent.ts +3 -3
- package/src/combinator/control/manipulation/lazy.ts +2 -2
- package/src/combinator/control/manipulation/match.ts +1 -1
- package/src/combinator/control/manipulation/recovery.ts +3 -3
- package/src/combinator/control/manipulation/reverse.ts +1 -1
- package/src/combinator/control/manipulation/scope.ts +3 -3
- package/src/combinator/control/manipulation/surround.ts +59 -61
- package/src/combinator/control/manipulation/trim.ts +3 -3
- package/src/combinator/control/monad/bind.ts +6 -6
- package/src/combinator/control/monad/fmap.ts +6 -6
- package/src/combinator/data/parser/context/delimiter.ts +47 -26
- package/src/combinator/data/parser/context.ts +8 -8
- package/src/combinator/data/parser/inits.ts +4 -4
- package/src/combinator/data/parser/sequence.ts +4 -4
- package/src/combinator/data/parser/some.ts +3 -3
- package/src/combinator/data/parser/subsequence.ts +3 -3
- package/src/combinator/data/parser/tails.ts +3 -3
- package/src/combinator/data/parser/union.ts +3 -3
- package/src/combinator/data/parser.ts +14 -13
- package/src/combinator.ts +1 -0
- package/src/parser/api/parse.test.ts +2 -2
- package/src/parser/block/extension/figure.ts +1 -1
- package/src/parser/block/extension/table.ts +3 -3
- package/src/parser/block/olist.ts +4 -4
- package/src/parser/block/reply/cite.ts +3 -3
- package/src/parser/block/ulist.ts +1 -1
- package/src/parser/inline/autolink/hashnum.ts +5 -1
- package/src/parser/inline/code.ts +2 -1
- package/src/parser/inline/emstrong.ts +2 -1
- package/src/parser/inline/extension/indexer.ts +6 -0
- package/src/parser/inline/extension/label.ts +1 -1
- package/src/parser/inline/html.ts +2 -2
- package/src/parser/inline.test.ts +1 -0
- package/src/parser/inline.ts +19 -4
- package/src/parser/util.ts +10 -10
- package/src/parser/visibility.ts +11 -11
- package/src/util/quote.ts +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ObjectCreate, min } from 'spica/alias';
|
|
2
|
-
import { Parser, Result, Ctx,
|
|
2
|
+
import { Parser, Result, Ctx, Node, Context } from '../../data/parser';
|
|
3
3
|
import { clone } from 'spica/assign';
|
|
4
4
|
|
|
5
5
|
export function reset<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
|
|
6
|
-
export function reset<
|
|
6
|
+
export function reset<N>(base: Ctx, parser: Parser<N>): Parser<N> {
|
|
7
7
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
8
8
|
assert(Object.freeze(base));
|
|
9
9
|
const changes = Object.entries(base);
|
|
@@ -13,7 +13,7 @@ export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function context<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
|
|
16
|
-
export function context<
|
|
16
|
+
export function context<N>(base: Ctx, parser: Parser<N>): Parser<N> {
|
|
17
17
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
18
18
|
assert(Object.freeze(base));
|
|
19
19
|
const changes = Object.entries(base);
|
|
@@ -22,8 +22,8 @@ export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
|
22
22
|
apply(parser, source, context, changes, values);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<
|
|
26
|
-
function apply<
|
|
25
|
+
function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Node<P>>;
|
|
26
|
+
function apply<N>(parser: Parser<N>, source: string, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<N> {
|
|
27
27
|
if (reset) {
|
|
28
28
|
context.backtracks = {};
|
|
29
29
|
}
|
|
@@ -97,7 +97,7 @@ export function recursion(recursion: number, parser: Parser<unknown>): Parser<un
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
export function precedence<P extends Parser<unknown>>(precedence: number, parser: P): P;
|
|
100
|
-
export function precedence<
|
|
100
|
+
export function precedence<N>(precedence: number, parser: Parser<N>): Parser<N> {
|
|
101
101
|
assert(precedence >= 0);
|
|
102
102
|
return input => {
|
|
103
103
|
const { context } = input;
|
|
@@ -115,7 +115,7 @@ export function precedence<T>(precedence: number, parser: Parser<T>): Parser<T>
|
|
|
115
115
|
|
|
116
116
|
export function state<P extends Parser<unknown>>(state: number, parser: P): P;
|
|
117
117
|
export function state<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
|
|
118
|
-
export function state<
|
|
118
|
+
export function state<N>(state: number, positive: boolean | Parser<N>, parser?: Parser<N>): Parser<N> {
|
|
119
119
|
if (typeof positive === 'function') {
|
|
120
120
|
parser = positive;
|
|
121
121
|
positive = true;
|
|
@@ -136,7 +136,7 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
|
|
|
136
136
|
|
|
137
137
|
export function constraint<P extends Parser<unknown>>(state: number, parser: P): P;
|
|
138
138
|
export function constraint<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
|
|
139
|
-
export function constraint<
|
|
139
|
+
export function constraint<N>(state: number, positive: boolean | Parser<N>, parser?: Parser<N>): Parser<N> {
|
|
140
140
|
if (typeof positive === 'function') {
|
|
141
141
|
parser = positive;
|
|
142
142
|
positive = true;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Parser, Ctx,
|
|
1
|
+
import { Parser, Ctx, Node, Context, SubParsers, SubNode, eval, exec, check } from '../parser';
|
|
2
2
|
import { push } from 'spica/array';
|
|
3
3
|
|
|
4
|
-
export function inits<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes:
|
|
5
|
-
export function inits<
|
|
4
|
+
export function inits<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
5
|
+
export function inits<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
|
|
6
6
|
assert(parsers.every(f => f));
|
|
7
7
|
if (parsers.length === 1) return parsers[0];
|
|
8
8
|
return ({ source, context }) => {
|
|
9
9
|
let rest = source;
|
|
10
|
-
let nodes:
|
|
10
|
+
let nodes: N[] | undefined;
|
|
11
11
|
for (let len = parsers.length, i = 0; i < len; ++i) {
|
|
12
12
|
if (rest === '') break;
|
|
13
13
|
if (context.delimiters?.match(rest, context)) break;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Parser, Ctx,
|
|
1
|
+
import { Parser, Ctx, Node, Context, SubParsers, SubNode, eval, exec, check } from '../parser';
|
|
2
2
|
import { push } from 'spica/array';
|
|
3
3
|
|
|
4
|
-
export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes:
|
|
5
|
-
export function sequence<
|
|
4
|
+
export function sequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
5
|
+
export function sequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
|
|
6
6
|
assert(parsers.every(f => f));
|
|
7
7
|
if (parsers.length === 1) return parsers[0];
|
|
8
8
|
return ({ source, context }) => {
|
|
9
9
|
let rest = source;
|
|
10
|
-
let nodes:
|
|
10
|
+
let nodes: N[] | undefined;
|
|
11
11
|
for (let len = parsers.length, i = 0; i < len; ++i) {
|
|
12
12
|
if (rest === '') return;
|
|
13
13
|
if (context.delimiters?.match(rest, context)) return;
|
|
@@ -6,13 +6,13 @@ type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number,
|
|
|
6
6
|
|
|
7
7
|
export function some<P extends Parser<unknown>>(parser: P, limit?: number): P;
|
|
8
8
|
export function some<P extends Parser<unknown>>(parser: P, end?: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
|
|
9
|
-
export function some<
|
|
9
|
+
export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delimiters: readonly DelimiterOption[] = [], limit = -1): Parser<N> {
|
|
10
10
|
if (typeof end === 'number') return some(parser, undefined, delimiters, end);
|
|
11
11
|
assert(parser);
|
|
12
12
|
assert([end].concat(delimiters.map(o => o[0])).every(d => d instanceof RegExp ? !d.flags.match(/[gmy]/) && d.source.startsWith('^') : true));
|
|
13
13
|
const match = Delimiters.matcher(end);
|
|
14
14
|
const delims = delimiters.map(([delimiter, precedence, linebreakable = true]) => ({
|
|
15
|
-
signature: Delimiters.signature(delimiter),
|
|
15
|
+
signature: Delimiters.signature(delimiter, linebreakable),
|
|
16
16
|
matcher: Delimiters.matcher(delimiter),
|
|
17
17
|
precedence,
|
|
18
18
|
linebreakable,
|
|
@@ -21,7 +21,7 @@ export function some<T>(parser: Parser<T>, end?: string | RegExp | number, delim
|
|
|
21
21
|
if (source === '') return;
|
|
22
22
|
assert(context.backtracks ??= {});
|
|
23
23
|
let rest = source;
|
|
24
|
-
let nodes:
|
|
24
|
+
let nodes: N[] | undefined;
|
|
25
25
|
if (delims.length > 0) {
|
|
26
26
|
context.delimiters ??= new Delimiters();
|
|
27
27
|
context.delimiters.push(delims);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Parser, Ctx,
|
|
1
|
+
import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { inits } from './inits';
|
|
4
4
|
|
|
5
|
-
export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes:
|
|
6
|
-
export function subsequence<
|
|
5
|
+
export function subsequence<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
6
|
+
export function subsequence<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
|
|
7
7
|
assert(parsers.every(f => f));
|
|
8
8
|
return union(
|
|
9
9
|
parsers.map((_, i) =>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Parser, Ctx,
|
|
1
|
+
import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
import { union } from './union';
|
|
3
3
|
import { sequence } from './sequence';
|
|
4
4
|
|
|
5
|
-
export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes:
|
|
6
|
-
export function tails<
|
|
5
|
+
export function tails<P extends Parser<unknown>>(parsers: SubParsers<P>, resume?: (nodes: SubNode<P>[], rest: string) => boolean): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
6
|
+
export function tails<N, D extends Parser<N>[]>(parsers: D, resume?: (nodes: N[], rest: string) => boolean): Parser<N, Ctx, D> {
|
|
7
7
|
return union(parsers.map((_, i) => sequence(parsers.slice(i), resume)) as D);
|
|
8
8
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Parser, Ctx,
|
|
1
|
+
import { Parser, Ctx, Node, Context, SubParsers, SubNode } from '../parser';
|
|
2
2
|
|
|
3
|
-
export function union<P extends Parser<unknown>>(parsers: SubParsers<P>):
|
|
4
|
-
export function union<
|
|
3
|
+
export function union<P extends Parser<unknown>>(parsers: SubParsers<P>): SubNode<P> extends Node<P> ? P : Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
4
|
+
export function union<N, D extends Parser<N>[]>(parsers: D): Parser<N, Ctx, D> {
|
|
5
5
|
assert(parsers.every(f => f));
|
|
6
6
|
switch (parsers.length) {
|
|
7
7
|
case 0:
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Delimiters } from './parser/context/delimiter';
|
|
2
2
|
|
|
3
|
-
export type Parser<
|
|
4
|
-
= (input: Input<C>) => Result<
|
|
3
|
+
export type Parser<N, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
|
|
4
|
+
= (input: Input<C>) => Result<N, C, D>;
|
|
5
5
|
export interface Input<C extends Ctx = Ctx> {
|
|
6
6
|
readonly source: string;
|
|
7
7
|
readonly context: C;
|
|
8
8
|
}
|
|
9
|
-
export type Result<
|
|
10
|
-
= readonly [
|
|
11
|
-
| readonly [
|
|
9
|
+
export type Result<N, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
|
|
10
|
+
= readonly [N[], string, C, D]
|
|
11
|
+
| readonly [N[], string]
|
|
12
12
|
| undefined;
|
|
13
13
|
export interface Ctx {
|
|
14
14
|
readonly resources?: {
|
|
@@ -19,24 +19,25 @@ export interface Ctx {
|
|
|
19
19
|
precedence?: number;
|
|
20
20
|
delimiters?: Delimiters;
|
|
21
21
|
state?: number;
|
|
22
|
+
depth?: number;
|
|
22
23
|
backtracks?: Record<number, number>;
|
|
23
24
|
backtrack?: number;
|
|
24
25
|
linebreak?: number;
|
|
25
26
|
recent?: string[];
|
|
26
27
|
}
|
|
27
|
-
export type
|
|
28
|
+
export type Node<P extends Parser<unknown>> = P extends Parser<infer N> ? N : never;
|
|
28
29
|
export type SubParsers<P extends Parser<unknown>> = P extends Parser<unknown, Ctx, infer D> ? D : never;
|
|
29
30
|
export type Context<P extends Parser<unknown>> = P extends Parser<unknown, infer C> ? C : never;
|
|
30
|
-
export type
|
|
31
|
-
export type IntermediateParser<P extends Parser<unknown>> = Parser<
|
|
32
|
-
type
|
|
31
|
+
export type SubNode<P extends Parser<unknown>> = ExtractSubNode<SubParsers<P>>;
|
|
32
|
+
export type IntermediateParser<P extends Parser<unknown>> = Parser<SubNode<P>, Context<P>, SubParsers<P>>;
|
|
33
|
+
type ExtractSubNode<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer N ? N extends Parser<infer U> ? U : never : never;
|
|
33
34
|
type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
|
|
34
35
|
|
|
35
36
|
export { eval_ as eval };
|
|
36
|
-
function eval_<
|
|
37
|
-
function eval_<
|
|
38
|
-
function eval_<
|
|
39
|
-
function eval_<
|
|
37
|
+
function eval_<N>(result: NonNullable<Result<N>>, default_?: N[]): N[];
|
|
38
|
+
function eval_<N>(result: Result<N>, default_: N[]): N[];
|
|
39
|
+
function eval_<N>(result: Result<N>, default_?: undefined): N[] | undefined;
|
|
40
|
+
function eval_<N>(result: Result<N>, default_?: N[]): N[] | undefined {
|
|
40
41
|
return result
|
|
41
42
|
? result[0]
|
|
42
43
|
: default_;
|
package/src/combinator.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from './combinator/control/constraint/contract';
|
|
|
11
11
|
export * from './combinator/control/manipulation/fence';
|
|
12
12
|
export * from './combinator/control/manipulation/indent';
|
|
13
13
|
export * from './combinator/control/manipulation/scope';
|
|
14
|
+
export * from './combinator/control/manipulation/clear';
|
|
14
15
|
export * from './combinator/control/manipulation/surround';
|
|
15
16
|
export * from './combinator/control/manipulation/match';
|
|
16
17
|
export * from './combinator/control/manipulation/convert';
|
|
@@ -350,7 +350,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
350
350
|
|
|
351
351
|
it('backtrack', function () {
|
|
352
352
|
this.timeout(5000);
|
|
353
|
-
const str = `${'.'.repeat(
|
|
353
|
+
const str = `${'.'.repeat(5 + 0)}{{(([[[http://${'['.repeat(13)}${'.'.repeat(8323)}`;
|
|
354
354
|
assert.deepStrictEqual(
|
|
355
355
|
[...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
356
356
|
[`<p>${str}</p>`]);
|
|
@@ -358,7 +358,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
358
358
|
|
|
359
359
|
it('backtrack error', function () {
|
|
360
360
|
this.timeout(5000);
|
|
361
|
-
const str = `${'.'.repeat(
|
|
361
|
+
const str = `${'.'.repeat(5 + 1)}{{(([[[http://${'['.repeat(13)}${'.'.repeat(8323)}`;
|
|
362
362
|
assert.deepStrictEqual(
|
|
363
363
|
[...parse(str).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
364
364
|
[
|
|
@@ -43,7 +43,7 @@ export const segment: FigureParser.SegmentParser = block(match(
|
|
|
43
43
|
]),
|
|
44
44
|
]),
|
|
45
45
|
closer),
|
|
46
|
-
([, fence]) => fence.length, {})
|
|
46
|
+
([, fence]) => fence.length, {})));
|
|
47
47
|
|
|
48
48
|
export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
49
49
|
convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { max, min, isArray } from 'spica/alias';
|
|
2
2
|
import { ExtensionParser } from '../../block';
|
|
3
|
-
import {
|
|
4
|
-
import { union, subsequence, inits, some, block, line, validate, fence, rewrite, surround, open,
|
|
3
|
+
import { Node, eval } from '../../../combinator/data/parser';
|
|
4
|
+
import { union, subsequence, inits, some, block, line, validate, fence, rewrite, clear, surround, open, convert, dup, lazy, fmap } from '../../../combinator';
|
|
5
5
|
import { inline, medialink, media, shortmedia } from '../../inline';
|
|
6
6
|
import { str, anyline, emptyline, contentline } from '../../source';
|
|
7
7
|
import { lineable, invalid } from '../../util';
|
|
@@ -147,7 +147,7 @@ function attributes(source: string): Record<string, string | undefined> {
|
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
function format(rows:
|
|
150
|
+
function format(rows: Node<RowParser>[]): HTMLTableSectionElement[] {
|
|
151
151
|
const thead = html('thead');
|
|
152
152
|
const tbody = html('tbody');
|
|
153
153
|
const tfoot = html('tfoot');
|
|
@@ -24,10 +24,10 @@ export const olist: OListParser = lazy(() => block(validate(
|
|
|
24
24
|
export const olist_: OListParser = lazy(() => block(union([
|
|
25
25
|
match(
|
|
26
26
|
openers['.'],
|
|
27
|
-
memoize(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), [])
|
|
27
|
+
memoize(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), [])),
|
|
28
28
|
match(
|
|
29
29
|
openers['('],
|
|
30
|
-
memoize(ms => list(type(ms[1]), '('), ms => idx(ms[1]), [])
|
|
30
|
+
memoize(ms => list(type(ms[1]), '('), ms => idx(ms[1]), [])),
|
|
31
31
|
])));
|
|
32
32
|
|
|
33
33
|
const list = (type: string, form: string): OListParser.ListParser => fmap(
|
|
@@ -47,10 +47,10 @@ const list = (type: string, form: string): OListParser.ListParser => fmap(
|
|
|
47
47
|
const heads = {
|
|
48
48
|
'.': focus(
|
|
49
49
|
openers['.'],
|
|
50
|
-
({ source }) => [[source.trimEnd().split('.', 1)[0] + '.'], '']
|
|
50
|
+
({ source }) => [[source.trimEnd().split('.', 1)[0] + '.'], '']),
|
|
51
51
|
'(': focus(
|
|
52
52
|
openers['('],
|
|
53
|
-
({ source }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], '']
|
|
53
|
+
({ source }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], '']),
|
|
54
54
|
} as const;
|
|
55
55
|
|
|
56
56
|
function idx(value: string): number {
|
|
@@ -15,9 +15,9 @@ export const cite: ReplyParser.CiteParser = line(fmap(validate(
|
|
|
15
15
|
anchor,
|
|
16
16
|
// Subject page representation.
|
|
17
17
|
// リンクの実装は後で検討
|
|
18
|
-
focus(/^>>#\S*(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor' }, source)], '']
|
|
19
|
-
focus(/^>>https?:\/\/\S+(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)], '']
|
|
20
|
-
focus(/^>>.+(?=\s*$)/, ({ source }) => [[source], '']
|
|
18
|
+
focus(/^>>#\S*(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor' }, source)], '']),
|
|
19
|
+
focus(/^>>https?:\/\/\S+(?=\s*$)/, ({ source }) => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)], '']),
|
|
20
|
+
focus(/^>>.+(?=\s*$)/, ({ source }) => [[source], '']),
|
|
21
21
|
]),
|
|
22
22
|
)),
|
|
23
23
|
([quotes, node]: [string, HTMLElement | string]) => [
|
|
@@ -33,7 +33,7 @@ export const checkbox = focus(
|
|
|
33
33
|
/^\[[xX ]\](?=$|\s)/,
|
|
34
34
|
({ source }) => [[
|
|
35
35
|
html('span', { class: 'checkbox' }, source[1].trimStart() ? '☑' : '☐'),
|
|
36
|
-
], '']
|
|
36
|
+
], '']);
|
|
37
37
|
|
|
38
38
|
export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | string)[] {
|
|
39
39
|
return ns.length === 1
|
|
@@ -7,7 +7,11 @@ import { str } from '../../source';
|
|
|
7
7
|
import { define } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
9
|
export const hashnum: AutolinkParser.HashnumParser = lazy(() => rewrite(
|
|
10
|
-
open(
|
|
10
|
+
open(
|
|
11
|
+
'#',
|
|
12
|
+
str(new RegExp([
|
|
13
|
+
/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji)/u.source,
|
|
14
|
+
].join('').replace(/emoji/, emoji), 'u'))),
|
|
11
15
|
union([
|
|
12
16
|
constraint(State.autolink, false, state(State.autolink, fmap(convert(
|
|
13
17
|
source => `[${source}]{ ${source.slice(1)} }`,
|
|
@@ -5,7 +5,8 @@ import { html } from 'typed-dom/dom';
|
|
|
5
5
|
export const code: CodeParser = match(
|
|
6
6
|
/^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/,
|
|
7
7
|
([whole, , body]) => ({ source }) =>
|
|
8
|
-
[[html('code', { 'data-src': whole }, format(body))], source.slice(whole.length)]
|
|
8
|
+
[[html('code', { 'data-src': whole }, format(body))], source.slice(whole.length)],
|
|
9
|
+
true);
|
|
9
10
|
|
|
10
11
|
function format(text: string): string {
|
|
11
12
|
assert(text.length > 0);
|
|
@@ -28,7 +28,8 @@ const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
|
|
|
28
28
|
])),
|
|
29
29
|
])));
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず
|
|
32
|
+
// 可能な限り早く閉じるよう解析しなければならない。
|
|
32
33
|
// このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
|
|
33
34
|
export const emstrong: EmStrongParser = lazy(() => validate('***',
|
|
34
35
|
precedence(0, repeat('***', surround(
|
|
@@ -3,6 +3,12 @@ import { union, focus, surround } from '../../../combinator';
|
|
|
3
3
|
import { signature } from './index';
|
|
4
4
|
import { html } from 'typed-dom/dom';
|
|
5
5
|
|
|
6
|
+
// インデクスの重複解消は不要な重複を削除するのが最もよい。
|
|
7
|
+
// 複合生成インデクスは参照と同期させることが困難であり
|
|
8
|
+
// 複合生成インデクスを手動で同期させるより最初から重複のない
|
|
9
|
+
// テキストまたはインデクスを付けて同期が必要な機会を減らすのが
|
|
10
|
+
// 継続的編集において最も簡便となる。
|
|
11
|
+
|
|
6
12
|
export const indexer: ExtensionParser.IndexerParser = surround(
|
|
7
13
|
/^\s+\[(?=\|\S)/,
|
|
8
14
|
union([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../inline';
|
|
2
2
|
import { State, Backtrack } from '../../context';
|
|
3
|
-
import { union, constraint,
|
|
3
|
+
import { union, constraint, clear, surround, fmap } from '../../../combinator';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
@@ -53,8 +53,8 @@ export const html: HTMLParser = lazy(() => validate(/^<[a-z]+(?=[^\S\n]|>)/i,
|
|
|
53
53
|
/^<([a-z]+)(?=[^\S\n]|>)/i,
|
|
54
54
|
memoize(
|
|
55
55
|
([, tag]) =>
|
|
56
|
-
surround<HTMLParser.TagParser, string>(
|
|
57
|
-
str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
56
|
+
surround<HTMLParser.TagParser, string>(
|
|
57
|
+
surround(str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
58
58
|
precedence(3, recursion(Recursion.inline,
|
|
59
59
|
subsequence([
|
|
60
60
|
focus(/^[^\S\n]*\n/, some(inline)),
|
|
@@ -70,6 +70,7 @@ describe('Unit: parser/inline', () => {
|
|
|
70
70
|
assert.deepStrictEqual(inspect(parser('***a*b*c*')), [['**', '<em>a</em>', 'b', '<em>c</em>'], '']);
|
|
71
71
|
assert.deepStrictEqual(inspect(parser('***a*b*c**')), [['**', '<em>a</em>', 'b', '<em>c</em>', '*'], '']);
|
|
72
72
|
assert.deepStrictEqual(inspect(parser('***a*b*c***')), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
|
|
73
|
+
assert.deepStrictEqual(inspect(parser('***a**b**c***')), [['<em><strong>a</strong>b<strong>c</strong></em>'], '']);
|
|
73
74
|
assert.deepStrictEqual(inspect(parser('*(*a*)*')), [['<em><span class="paren">(<em>a</em>)</span></em>'], '']);
|
|
74
75
|
assert.deepStrictEqual(inspect(parser('**(**a**)**')), [['<strong><span class="paren">(<strong>a</strong>)</span></strong>'], '']);
|
|
75
76
|
assert.deepStrictEqual(inspect(parser('*[*]')), [['*', '[', '*', ']'], '']);
|
package/src/parser/inline.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MarkdownParser } from '../../markdown';
|
|
2
|
-
import { union, lazy } from '../combinator';
|
|
2
|
+
import { union, verify, lazy } from '../combinator';
|
|
3
3
|
import { annotation } from './inline/annotation';
|
|
4
4
|
import { reference } from './inline/reference';
|
|
5
5
|
import { template } from './inline/template';
|
|
@@ -47,10 +47,12 @@ export import ShortMediaParser = InlineParser.ShortMediaParser;
|
|
|
47
47
|
export import BracketParser = InlineParser.BracketParser;
|
|
48
48
|
export import AutolinkParser = InlineParser.AutolinkParser;
|
|
49
49
|
|
|
50
|
-
export const inline: InlineParser = lazy(() => union([
|
|
50
|
+
export const inline: InlineParser = lazy(() => verify(union([
|
|
51
51
|
input => {
|
|
52
|
-
const { source } = input;
|
|
52
|
+
const { source, context } = input;
|
|
53
53
|
if (source === '') return;
|
|
54
|
+
context.depth ??= 0;
|
|
55
|
+
++context.depth;
|
|
54
56
|
switch (source.slice(0, 2)) {
|
|
55
57
|
case '((':
|
|
56
58
|
return annotation(input);
|
|
@@ -104,7 +106,20 @@ export const inline: InlineParser = lazy(() => union([
|
|
|
104
106
|
bracket,
|
|
105
107
|
autolink,
|
|
106
108
|
text
|
|
107
|
-
]))
|
|
109
|
+
]), (_, rest, context) => {
|
|
110
|
+
--context.depth!;
|
|
111
|
+
assert([rest]);
|
|
112
|
+
// ヒープを効率的に削除可能な場合は削除する。
|
|
113
|
+
// ヒープサイズは括弧類など特定の構文が完成しなかった場合にしか増加しないため
|
|
114
|
+
// ブロックごとに平均数ノード以下となることから削除せずとも平均的にはあまり影響はない。
|
|
115
|
+
//if (context.depth === 0) {
|
|
116
|
+
// const { backtracks } = context;
|
|
117
|
+
// while (backtracks.peek()?.key! > rest.length) {
|
|
118
|
+
// backtracks.extract();
|
|
119
|
+
// }
|
|
120
|
+
//}
|
|
121
|
+
return true;
|
|
122
|
+
})) as any;
|
|
108
123
|
|
|
109
124
|
export { indexee } from './inline/extension/indexee';
|
|
110
125
|
export { indexer } from './inline/extension/indexer';
|
package/src/parser/util.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { min } from 'spica/alias';
|
|
2
2
|
import { Command } from './context';
|
|
3
|
-
import { Parser, Result, Ctx,
|
|
3
|
+
import { Parser, Result, Ctx, Node, Context, eval, exec } from '../combinator/data/parser';
|
|
4
4
|
import { convert } from '../combinator';
|
|
5
5
|
import { define } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
7
|
export function lineable<P extends Parser<HTMLElement | string>>(parser: P, fillTrailingLinebreak?: boolean): P;
|
|
8
|
-
export function lineable<
|
|
8
|
+
export function lineable<N extends HTMLElement | string>(parser: Parser<N>, fillTrailingLinebreak = false): Parser<N> {
|
|
9
9
|
return convert(
|
|
10
10
|
source => `\r${source}${fillTrailingLinebreak && source.at(-1) !== '\n' ? '\n' : ''}`,
|
|
11
11
|
parser,
|
|
12
12
|
!fillTrailingLinebreak);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function repeat<P extends Parser<HTMLElement | string>>(symbol: string, parser: P, cons: (nodes:
|
|
16
|
-
export function repeat<
|
|
15
|
+
export function repeat<P extends Parser<HTMLElement | string>>(symbol: string, parser: P, cons: (nodes: Node<P>[], context: Context<P>) => Node<P>[], termination?: (acc: Node<P>[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | Node<P>>): P;
|
|
16
|
+
export function repeat<N extends HTMLElement | string>(symbol: string, parser: Parser<N>, cons: (nodes: N[], context: Ctx) => N[], termination: (acc: N[][], rest: string, prefix: number, postfix: number, state: boolean) => Result<string | N> = (acc, rest, prefix, postfix) => {
|
|
17
17
|
const nodes = [];
|
|
18
18
|
if (prefix > 0) {
|
|
19
19
|
nodes.push(symbol[0].repeat(prefix));
|
|
@@ -26,11 +26,11 @@ export function repeat<T extends HTMLElement | string>(symbol: string, parser: P
|
|
|
26
26
|
rest = rest.slice(postfix);
|
|
27
27
|
}
|
|
28
28
|
return [nodes, rest];
|
|
29
|
-
}): Parser<string |
|
|
29
|
+
}): Parser<string | N> {
|
|
30
30
|
return input => {
|
|
31
31
|
const { source, context } = input;
|
|
32
32
|
assert(source.startsWith(symbol));
|
|
33
|
-
let acc:
|
|
33
|
+
let acc: N[][] = [];
|
|
34
34
|
let i = symbol.length;
|
|
35
35
|
while (source[i] === source[0]) ++i;
|
|
36
36
|
let rest = source.slice(i);
|
|
@@ -87,12 +87,12 @@ export function invalid(
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
export function markInvalid<
|
|
91
|
-
el:
|
|
90
|
+
export function markInvalid<N extends HTMLElement>(
|
|
91
|
+
el: N,
|
|
92
92
|
syntax: string,
|
|
93
93
|
type: string,
|
|
94
94
|
message: string,
|
|
95
|
-
):
|
|
95
|
+
): N {
|
|
96
96
|
assert(!message.endsWith('.'));
|
|
97
97
|
return define(el, {
|
|
98
98
|
class: void el.classList.add('invalid'),
|
|
@@ -102,7 +102,7 @@ export function markInvalid<T extends Element>(
|
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
export function unmarkInvalid<
|
|
105
|
+
export function unmarkInvalid<N extends HTMLElement>(el: N): N {
|
|
106
106
|
return define(el, {
|
|
107
107
|
class: void el.classList.remove('invalid'),
|
|
108
108
|
'data-invalid-syntax': null,
|
package/src/parser/visibility.ts
CHANGED
|
@@ -18,7 +18,7 @@ export namespace blank {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function visualize<P extends Parser<HTMLElement | string>>(parser: P): P;
|
|
21
|
-
export function visualize<
|
|
21
|
+
export function visualize<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
|
|
22
22
|
return union([
|
|
23
23
|
convert(
|
|
24
24
|
source => source.replace(blank.line, line => line.replace(/[\\&<]/g, `${Command.Escape}$&`)),
|
|
@@ -60,7 +60,7 @@ export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegEx
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
//export function looseStart<P extends Parser<HTMLElement | string>>(parser: P, except?: string): P;
|
|
63
|
-
//export function looseStart<
|
|
63
|
+
//export function looseStart<N extends HTMLElement | string>(parser: Parser<N>, except?: string): Parser<N> {
|
|
64
64
|
// return input =>
|
|
65
65
|
// isLooseStart(input, except)
|
|
66
66
|
// ? parser(input)
|
|
@@ -71,7 +71,7 @@ export function blankWith(starts: '' | '\n', delimiter?: string | RegExp): RegEx
|
|
|
71
71
|
//}, ({ source }, except = '') => `${source}${Command.Separator}${except}`);
|
|
72
72
|
|
|
73
73
|
export function tightStart<P extends Parser<unknown>>(parser: P, except?: string): P;
|
|
74
|
-
export function tightStart<
|
|
74
|
+
export function tightStart<N>(parser: Parser<N>, except?: string): Parser<N> {
|
|
75
75
|
return input =>
|
|
76
76
|
isTightStart(input, except)
|
|
77
77
|
? parser(input)
|
|
@@ -155,31 +155,31 @@ function isVisible(node: HTMLElement | string, strpos?: number): boolean {
|
|
|
155
155
|
|
|
156
156
|
// デフラグ前の非効率な後方トリムを避けるため必要のない限りtrimBlankStart+trimNodeEndで処理する。
|
|
157
157
|
export function trimBlank<P extends Parser<HTMLElement | string>>(parser: P): P;
|
|
158
|
-
export function trimBlank<
|
|
158
|
+
export function trimBlank<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
|
|
159
159
|
return trimBlankStart(trimBlankEnd(parser));
|
|
160
160
|
}
|
|
161
161
|
export function trimBlankStart<P extends Parser<unknown>>(parser: P): P;
|
|
162
|
-
export function trimBlankStart<
|
|
162
|
+
export function trimBlankStart<N>(parser: Parser<N>): Parser<N> {
|
|
163
163
|
return convert(
|
|
164
164
|
source => source.replace(blank.start, ''),
|
|
165
165
|
parser,
|
|
166
166
|
true);
|
|
167
167
|
}
|
|
168
168
|
export function trimBlankEnd<P extends Parser<HTMLElement | string>>(parser: P): P;
|
|
169
|
-
export function trimBlankEnd<
|
|
169
|
+
export function trimBlankEnd<N extends HTMLElement | string>(parser: Parser<N>): Parser<N> {
|
|
170
170
|
return fmap(
|
|
171
171
|
parser,
|
|
172
172
|
trimBlankNodeEnd);
|
|
173
173
|
}
|
|
174
|
-
//export function trimBlankNode<
|
|
174
|
+
//export function trimBlankNode<N extends HTMLElement | string>(nodes: N[]): N[] {
|
|
175
175
|
// return trimBlankNodeStart(trimBlankNodeEnd(nodes));
|
|
176
176
|
//}
|
|
177
|
-
//function trimBlankNodeStart<
|
|
177
|
+
//function trimBlankNodeStart<N extends HTMLElement | string>(nodes: N[]): N[] {
|
|
178
178
|
// for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
|
|
179
179
|
// if (typeof node === 'string') {
|
|
180
180
|
// const pos = node.trimStart().length;
|
|
181
181
|
// if (pos > 0) {
|
|
182
|
-
// nodes[0] = node.slice(-pos) as
|
|
182
|
+
// nodes[0] = node.slice(-pos) as N;
|
|
183
183
|
// break;
|
|
184
184
|
// }
|
|
185
185
|
// }
|
|
@@ -190,7 +190,7 @@ export function trimBlankEnd<T extends HTMLElement | string>(parser: Parser<T>):
|
|
|
190
190
|
// }
|
|
191
191
|
// return nodes;
|
|
192
192
|
//}
|
|
193
|
-
export function trimBlankNodeEnd<
|
|
193
|
+
export function trimBlankNodeEnd<N extends HTMLElement | string>(nodes: N[]): N[] {
|
|
194
194
|
const skip = nodes.length > 0 &&
|
|
195
195
|
typeof nodes.at(-1) === 'object' &&
|
|
196
196
|
nodes.at(-1)!['className'] === 'indexer'
|
|
@@ -200,7 +200,7 @@ export function trimBlankNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[
|
|
|
200
200
|
if (typeof node === 'string') {
|
|
201
201
|
const len = node.trimEnd().length;
|
|
202
202
|
if (len > 0) {
|
|
203
|
-
nodes[nodes.length - 1] = node.slice(0, len) as
|
|
203
|
+
nodes[nodes.length - 1] = node.slice(0, len) as N;
|
|
204
204
|
break;
|
|
205
205
|
}
|
|
206
206
|
}
|
package/src/util/quote.ts
CHANGED
|
@@ -82,7 +82,7 @@ function fit(range: Range): void {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
function trim<
|
|
85
|
+
function trim<N extends Node>(node: N): N {
|
|
86
86
|
for (let child: ChildNode & Node | null; child = node.firstChild;) {
|
|
87
87
|
if (child.textContent) break;
|
|
88
88
|
child.remove();
|