securemark 0.258.3 → 0.258.6

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +130 -78
  3. package/markdown.d.ts +11 -13
  4. package/package.json +1 -1
  5. package/src/combinator/control/constraint/block.ts +3 -1
  6. package/src/combinator/control/constraint/line.ts +6 -1
  7. package/src/combinator/control/manipulation/indent.ts +3 -0
  8. package/src/combinator/data/parser/context/delimiter.ts +6 -3
  9. package/src/combinator/data/parser/context/memo.ts +1 -1
  10. package/src/combinator/data/parser/context.test.ts +13 -13
  11. package/src/combinator/data/parser/context.ts +32 -23
  12. package/src/combinator/data/parser.ts +1 -1
  13. package/src/parser/autolink.test.ts +3 -2
  14. package/src/parser/autolink.ts +17 -1
  15. package/src/parser/block/extension/table.ts +1 -1
  16. package/src/parser/block/olist.ts +1 -1
  17. package/src/parser/block/paragraph.test.ts +3 -1
  18. package/src/parser/block/reply/quote.ts +2 -2
  19. package/src/parser/block/table.ts +1 -1
  20. package/src/parser/block/ulist.ts +3 -3
  21. package/src/parser/block.ts +1 -1
  22. package/src/parser/inline/annotation.ts +3 -3
  23. package/src/parser/inline/autolink/email.test.ts +3 -3
  24. package/src/parser/inline/autolink/url.test.ts +6 -6
  25. package/src/parser/inline/autolink.ts +4 -4
  26. package/src/parser/inline/extension/index.ts +2 -2
  27. package/src/parser/inline/extension/label.ts +2 -2
  28. package/src/parser/inline/html.test.ts +16 -16
  29. package/src/parser/inline/html.ts +9 -8
  30. package/src/parser/inline/link.ts +20 -7
  31. package/src/parser/inline/media.ts +2 -2
  32. package/src/parser/inline/reference.ts +3 -3
  33. package/src/parser/inline/shortmedia.ts +2 -2
  34. package/src/parser/inline/template.ts +1 -1
  35. package/src/parser/inline.test.ts +3 -3
  36. package/src/parser/source/text.test.ts +16 -1
  37. package/src/parser/source/text.ts +5 -4
  38. package/src/parser/visibility.ts +5 -5
@@ -21,7 +21,10 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
21
21
  ([indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
22
22
  (lines, rest, context) => {
23
23
  assert(parser = parser as Parser<T>);
24
+ const memo = context.memo;
25
+ memo && (memo.offset += rest.length);
24
26
  const result = parser(trimBlockEnd(lines.join('')), context);
27
+ memo && (memo.offset -= rest.length);
25
28
  return result && exec(result) === ''
26
29
  ? [eval(result), rest]
27
30
  : undefined;
@@ -57,12 +57,15 @@ export class Delimiters {
57
57
  public match(source: string, precedence = 1): boolean {
58
58
  const { matchers } = this;
59
59
  for (let i = 0; i < matchers.length; ++i) {
60
- switch (matchers[i][3](source)) {
60
+ const matcher = matchers[i];
61
+ if (precedence >= matcher[2]) continue;
62
+ switch (matcher[3](source)) {
61
63
  case true:
62
- if (precedence < matchers[i][2]) return true;
63
- continue;
64
+ return true;
64
65
  case false:
65
66
  return false;
67
+ default:
68
+ continue;
66
69
  }
67
70
  }
68
71
  return false;
@@ -34,6 +34,6 @@ export class Memo {
34
34
  for (let i = position + this.offset, len = memory.length; i < len; ++i) {
35
35
  memory.pop();
36
36
  }
37
- //console.log('clear', position);
37
+ //console.log('clear', position + 1);
38
38
  }
39
39
  }
@@ -10,27 +10,27 @@ describe('Unit: combinator/data/parser/context', () => {
10
10
 
11
11
  describe('reset', () => {
12
12
  const parser: Parser<number> = some(creation(
13
- (s, context) => [[context.resources?.budget ?? NaN], s.slice(1)]));
13
+ (s, context) => [[context.resources?.clock ?? NaN], s.slice(1)]));
14
14
 
15
15
  it('root', () => {
16
- const base: Context = { resources: { budget: 3, recursion: 1 } };
16
+ const base: Context = { resources: { clock: 3, recursion: 1 } };
17
17
  const ctx: Context = {};
18
18
  assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
19
- assert(base.resources?.budget === 3);
20
- assert(ctx.resources?.budget === undefined);
19
+ assert(base.resources?.clock === 3);
20
+ assert(ctx.resources?.clock === undefined);
21
21
  assert.throws(() => reset(base, parser)('1234', ctx));
22
- assert(ctx.resources?.budget === undefined);
22
+ assert(ctx.resources?.clock === undefined);
23
23
  assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
24
24
  });
25
25
 
26
26
  it('node', () => {
27
- const base: Context = { resources: { budget: 3, recursion: 1 } };
28
- const ctx: Context = { resources: { budget: 1, recursion: 1 } };
27
+ const base: Context = { resources: { clock: 3, recursion: 1 } };
28
+ const ctx: Context = { resources: { clock: 1, recursion: 1 } };
29
29
  assert.deepStrictEqual(reset(base, parser)('1', ctx), [[1], '']);
30
- assert(base.resources?.budget === 3);
31
- assert(ctx.resources?.budget === 0);
30
+ assert(base.resources?.clock === 3);
31
+ assert(ctx.resources?.clock === 0);
32
32
  assert.throws(() => reset(base, parser)('1', ctx));
33
- assert(ctx.resources?.budget === 0);
33
+ assert(ctx.resources?.clock === 0);
34
34
  });
35
35
 
36
36
  });
@@ -41,12 +41,12 @@ describe('Unit: combinator/data/parser/context', () => {
41
41
 
42
42
  it('', () => {
43
43
  const base: Context = { status: true };
44
- const ctx: Context = { resources: { budget: 3, recursion: 1 } };
44
+ const ctx: Context = { resources: { clock: 3, recursion: 1 } };
45
45
  assert.deepStrictEqual(context(base, parser)('123', ctx), [[true, true, true], '']);
46
- assert(ctx.resources?.budget === 0);
46
+ assert(ctx.resources?.clock === 0);
47
47
  assert(ctx.status === undefined);
48
48
  assert.throws(() => reset(base, parser)('1', ctx));
49
- assert(ctx.resources?.budget === 0);
49
+ assert(ctx.resources?.clock === 0);
50
50
  assert(ctx.status === undefined);
51
51
  });
52
52
 
@@ -58,41 +58,31 @@ 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, parser: P): P;
60
60
  export function syntax<T>(syntax: number, precedence: number, cost: number, parser?: Parser<T>): Parser<T> {
61
- return (source, context) => {
61
+ return creation(cost, (source, context) => {
62
62
  if (source === '') return;
63
63
  const memo = context.memo ??= new Memo();
64
64
  context.memorable ??= ~0;
65
65
  const p = context.precedence;
66
66
  context.precedence = precedence;
67
- const { resources = { budget: 1, recursion: 1 } } = context;
68
- if (resources.budget <= 0) throw new Error('Too many creations');
69
- if (resources.recursion <= 0) throw new Error('Too much recursion');
70
- --resources.recursion;
71
- const pos = source.length;
67
+ const position = source.length;
72
68
  const state = context.state ?? 0;
73
- const cache = syntax && memo.get(pos, syntax, state);
69
+ const cache = syntax && memo.get(position, syntax, state);
74
70
  const result: Result<T> = cache
75
71
  ? cache.length === 0
76
72
  ? undefined
77
73
  : [cache[0], source.slice(cache[1])]
78
74
  : parser!(source, context);
79
- ++resources.recursion;
80
- if (result && !cache) {
81
- resources.budget -= cost;
75
+ if (syntax && state & context.memorable!) {
76
+ cache ?? memo.set(position, syntax, state, eval(result), source.length - exec(result, '').length);
77
+ assert.deepStrictEqual(cache && cache, cache && memo.get(position, syntax, state));
82
78
  }
83
- if (syntax) {
84
- if (state & context.memorable!) {
85
- cache ?? memo.set(pos, syntax, state, eval(result), source.length - exec(result, '').length);
86
- assert.deepStrictEqual(cache && cache, cache && memo.get(pos, syntax, state));
87
- }
88
- else if (result && memo.length! >= pos) {
89
- assert(!(state & context.memorable!));
90
- memo.clear(pos);
91
- }
79
+ if (result && !state && memo.length! >= position) {
80
+ assert(!(state & context.memorable!));
81
+ memo.clear(position);
92
82
  }
93
83
  context.precedence = p;
94
84
  return result;
95
- };
85
+ });
96
86
  }
97
87
 
98
88
  export function creation<P extends Parser<unknown>>(parser: P): P;
@@ -101,14 +91,14 @@ export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown
101
91
  if (typeof cost === 'function') return creation(1, cost);
102
92
  assert(cost >= 0);
103
93
  return (source, context) => {
104
- const { resources = { budget: 1, recursion: 1 } } = context;
105
- if (resources.budget <= 0) throw new Error('Too many creations');
94
+ const { resources = { clock: 1, recursion: 1 } } = context;
95
+ if (resources.clock <= 0) throw new Error('Too many creations');
106
96
  if (resources.recursion <= 0) throw new Error('Too much recursion');
107
97
  --resources.recursion;
108
98
  const result = parser!(source, context);
109
99
  ++resources.recursion;
110
100
  if (result) {
111
- resources.budget -= cost;
101
+ resources.clock -= cost;
112
102
  }
113
103
  return result;
114
104
  };
@@ -133,6 +123,24 @@ export function guard<T>(f: (context: Ctx) => boolean | number, parser: Parser<T
133
123
  : undefined;
134
124
  }
135
125
 
126
+ export function constraint<P extends Parser<unknown>>(state: number, parser: P): P;
127
+ export function constraint<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
128
+ export function constraint<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
129
+ if (typeof positive === 'function') {
130
+ parser = positive;
131
+ positive = true;
132
+ }
133
+ assert(state);
134
+ return (source, context) => {
135
+ const s = positive
136
+ ? state & context.state!
137
+ : state & ~context.state!;
138
+ return s === state
139
+ ? parser!(source, context)
140
+ : undefined;
141
+ };
142
+ }
143
+
136
144
  export function state<P extends Parser<unknown>>(state: number, parser: P): P;
137
145
  export function state<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
138
146
  export function state<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
@@ -140,6 +148,7 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
140
148
  parser = positive;
141
149
  positive = true;
142
150
  }
151
+ assert(state);
143
152
  return (source, context) => {
144
153
  const s = context.state ?? 0;
145
154
  context.state = positive
@@ -9,7 +9,7 @@ export type Result<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
9
9
  | undefined;
10
10
  export interface Ctx {
11
11
  readonly resources?: {
12
- budget: number;
12
+ clock: number;
13
13
  recursion: number;
14
14
  };
15
15
  precedence?: number;
@@ -13,13 +13,14 @@ describe('Unit: parser/autolink', () => {
13
13
  assert.deepStrictEqual(inspect(parser('@a#b')), [['<a href="/@a?ch=b" class="channel">@a#b</a>'], '']);
14
14
  assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '<br>'], '']);
15
15
  assert.deepStrictEqual(inspect(parser('a#b')), [['a#b'], '']);
16
- assert.deepStrictEqual(inspect(parser('0a#b')), [['0', 'a#b'], '']);
16
+ assert.deepStrictEqual(inspect(parser('0a#b')), [['0a#b'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('あい#b')), [['あ', 'い#b'], '']);
19
- assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0a', 'あ#b'], '']);
19
+ assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0aあ#b'], '']);
20
20
  assert.deepStrictEqual(inspect(parser('0aあい#b')), [['0a', 'あ', 'い#b'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '\\', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
23
+ assert.deepStrictEqual(inspect(parser('0a>>b')), [['0a>>b'], '']);
23
24
  });
24
25
 
25
26
  });
@@ -5,7 +5,23 @@ import { linebreak, unescsource } from './source';
5
5
 
6
6
  export import AutolinkParser = MarkdownParser.AutolinkParser;
7
7
 
8
- export const autolink: AutolinkParser = lazy(() => union([
8
+ const delimiter = /[@#>0-9A-Za-z\n]|\S[#>]/;
9
+
10
+ export const autolink: AutolinkParser = (source, context) => {
11
+ if (source === '') return;
12
+ assert(source[0] !== '\x1B');
13
+ const i = source.search(delimiter);
14
+ switch (i) {
15
+ case -1:
16
+ return [[source], ''];
17
+ case 0:
18
+ return parser(source, context);
19
+ default:
20
+ return [[source.slice(0, i)], source.slice(i)];
21
+ }
22
+ };
23
+
24
+ const parser: AutolinkParser = lazy(() => union([
9
25
  autolink_,
10
26
  linebreak,
11
27
  unescsource
@@ -228,7 +228,7 @@ function format(rows: Tree<RowParser>[]): HTMLTableSectionElement[] {
228
228
  : cells[j];
229
229
  const isHeadCell = cell.tagName === 'TH';
230
230
  heads |= BigInt(isHeadCell) << jn;
231
- highlights |= BigInt(cell.classList.contains('highlight')) << jn;
231
+ highlights |= BigInt(cell.className === 'highlight') << jn;
232
232
  hasDataCell ||= !isHeadCell;
233
233
  if (isHeadCell && !hasDataCell) {
234
234
  lHeadCellIdx = jn;
@@ -115,7 +115,7 @@ function initial(type: string): RegExp {
115
115
  }
116
116
 
117
117
  function format(el: HTMLOListElement, type: string, form: string): HTMLOListElement {
118
- if (el.firstElementChild?.firstElementChild?.classList.contains('checkbox')) {
118
+ if (el.firstElementChild?.firstElementChild?.className === 'checkbox') {
119
119
  el.setAttribute('class', 'checklist');
120
120
  }
121
121
  define(el, {
@@ -45,13 +45,15 @@ describe('Unit: parser/block/paragraph', () => {
45
45
  assert.deepStrictEqual(inspect(parser('>>11 a')), [['<p><a href="?at=11" class="anchor">&gt;&gt;11</a> a</p>'], '']);
46
46
  assert.deepStrictEqual(inspect(parser('>>>11 a')), [['<p>&gt;<a href="?at=11" class="anchor">&gt;&gt;11</a> a</p>'], '']);
47
47
  assert.deepStrictEqual(inspect(parser('>> a\n>>1')), [['<p>&gt;&gt; a<br><a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
48
- assert.deepStrictEqual(inspect(parser('a>>1')), [['<p>a<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
48
+ assert.deepStrictEqual(inspect(parser('a>>1')), [['<p>a&gt;&gt;1</p>'], '']);
49
+ assert.deepStrictEqual(inspect(parser('ab>>1')), [['<p>ab&gt;&gt;1</p>'], '']);
49
50
  assert.deepStrictEqual(inspect(parser('a >>1')), [['<p>a <a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
50
51
  assert.deepStrictEqual(inspect(parser('a\n>>1')), [['<p>a<br><a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
51
52
  assert.deepStrictEqual(inspect(parser('a\n>>1\nb')), [['<p>a<br><a href="?at=1" class="anchor">&gt;&gt;1</a><br>b</p>'], '']);
52
53
  assert.deepStrictEqual(inspect(parser('a\n>> b\nc')), [['<p>a<br>&gt;&gt; b<br>c</p>'], '']);
53
54
  assert.deepStrictEqual(inspect(parser('\t>>1')), [['<p>\t<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
54
55
  assert.deepStrictEqual(inspect(parser('\t>>>1')), [['<p>\t&gt;<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
56
+ assert.deepStrictEqual(inspect(parser('あ>>1')), [['<p>あ<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
55
57
  });
56
58
 
57
59
  it('comment', () => {
@@ -57,8 +57,8 @@ const qblock: ReplyParser.QuoteParser.BlockParser = (source, context) => {
57
57
  ++i;
58
58
  continue;
59
59
  }
60
- if (child.classList.contains('cite') || child.classList.contains('quote')) {
61
- context.resources && (context.resources.budget -= child.childNodes.length);
60
+ if (child.className === 'cite' || child.classList.contains('quote')) {
61
+ context.resources && (context.resources.clock -= child.childNodes.length);
62
62
  nodes.splice(i, 1, ...child.childNodes as NodeListOf<HTMLElement>);
63
63
  --i;
64
64
  continue;
@@ -61,7 +61,7 @@ const data: CellParser.DataParser = creation(fmap(
61
61
  ns => [html('td', trimNode(defrag(ns)))]));
62
62
 
63
63
  function format(rows: HTMLTableRowElement[]): HTMLTableRowElement[] {
64
- const aligns = rows[0].classList.contains('invalid')
64
+ const aligns = rows[0].className === 'invalid'
65
65
  ? []
66
66
  : duffReduce(rows.shift()!.children, (acc, el) => push(acc, [el.textContent!]), [] as string[]);
67
67
  for (let i = 0; i < rows.length; ++i) {
@@ -26,11 +26,11 @@ export const ulist_: UListParser = lazy(() => block(fmap(validate(
26
26
  ])))),
27
27
  es => [format(html('ul', es))])));
28
28
 
29
- export const checkbox = focus(
29
+ export const checkbox = creation(focus(
30
30
  /^\[[xX ]\](?=$|\s)/,
31
31
  source => [[
32
32
  html('span', { class: 'checkbox' }, source[1].trimStart() ? '☑' : '☐'),
33
- ], '']);
33
+ ], '']));
34
34
 
35
35
  export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | string)[] {
36
36
  return ns.length === 1
@@ -41,7 +41,7 @@ export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | stri
41
41
  }
42
42
 
43
43
  function format(el: HTMLUListElement): HTMLUListElement {
44
- if (el.firstElementChild?.firstElementChild?.classList.contains('checkbox')) {
44
+ if (el.firstElementChild?.firstElementChild?.className === 'checkbox') {
45
45
  el.setAttribute('class', 'checklist');
46
46
  }
47
47
  return el;
@@ -36,7 +36,7 @@ export import ReplyParser = BlockParser.ReplyParser;
36
36
  export import ParagraphParser = BlockParser.ParagraphParser;
37
37
 
38
38
  export const block: BlockParser = creation(error(
39
- reset({ resources: { budget: 50 * 1000, recursion: 20 } },
39
+ reset({ resources: { clock: 50 * 1000, recursion: 20 } },
40
40
  union([
41
41
  emptyline,
42
42
  horizontalrule,
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { AnnotationParser } from '../inline';
3
- import { union, some, guard, context, syntax, state, surround, lazy } from '../../combinator';
3
+ import { union, some, context, syntax, constraint, state, surround, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { optimize } from './link';
6
6
  import { Syntax, State } from '../context';
@@ -9,7 +9,7 @@ import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const annotation: AnnotationParser = lazy(() => surround(
11
11
  '((',
12
- guard(context => ~context.state! & State.annotation,
12
+ constraint(State.annotation, false,
13
13
  syntax(Syntax.annotation, 6, 1,
14
14
  state(State.annotation | State.media,
15
15
  startLoose(
@@ -18,4 +18,4 @@ export const annotation: AnnotationParser = lazy(() => surround(
18
18
  '))',
19
19
  false,
20
20
  ([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNode(defrag(ns)))])], rest],
21
- ([, ns, rest], next) => next[0] === ')' ? undefined : optimize('((', ns, rest, next)));
21
+ ([, ns, rest], next, context) => next[0] === ')' ? undefined : optimize('((', ns, rest, next, context)));
@@ -20,9 +20,9 @@ describe('Unit: parser/inline/autolink/email', () => {
20
20
  assert.deepStrictEqual(inspect(parser('a@b#1')), [['a@b#1'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('a@@')), [['a@@'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('a@@b')), [['a@@b'], '']);
23
- assert.deepStrictEqual(inspect(parser('a+@b')), undefined);
24
- assert.deepStrictEqual(inspect(parser('a..b@c')), undefined);
25
- assert.deepStrictEqual(inspect(parser('a++b@c')), undefined);
23
+ assert.deepStrictEqual(inspect(parser('a+@b')), [['a'], '+@b']);
24
+ assert.deepStrictEqual(inspect(parser('a..b@c')), [['a'], '..b@c']);
25
+ assert.deepStrictEqual(inspect(parser('a++b@c')), [['a'], '++b@c']);
26
26
  assert.deepStrictEqual(inspect(parser(`a@${'b'.repeat(64)}`)), [[`a@${'b'.repeat(64)}`], '']);
27
27
  assert.deepStrictEqual(inspect(parser(' a@b')), undefined);
28
28
  });
@@ -8,12 +8,12 @@ describe('Unit: parser/inline/autolink/url', () => {
8
8
 
9
9
  it('invalid', () => {
10
10
  assert.deepStrictEqual(inspect(parser('')), undefined);
11
- assert.deepStrictEqual(inspect(parser('http')), undefined);
12
- assert.deepStrictEqual(inspect(parser('ttp')), undefined);
13
- assert.deepStrictEqual(inspect(parser('http://')), undefined);
14
- assert.deepStrictEqual(inspect(parser('http://[')), undefined);
15
- assert.deepStrictEqual(inspect(parser('http://]')), undefined);
16
- assert.deepStrictEqual(inspect(parser('Http://host')), undefined);
11
+ assert.deepStrictEqual(inspect(parser('http')), [['http'], '']);
12
+ assert.deepStrictEqual(inspect(parser('ttp')), [['ttp'], '']);
13
+ assert.deepStrictEqual(inspect(parser('http://')), [['http'], '://']);
14
+ assert.deepStrictEqual(inspect(parser('http://[')), [['http'], '://[']);
15
+ assert.deepStrictEqual(inspect(parser('http://]')), [['http'], '://]']);
16
+ assert.deepStrictEqual(inspect(parser('Http://host')), [['Http'], '://host']);
17
17
  //assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0%1]')), [['<a class="invalid">http://[::ffff:0:0%1]</a>'], '']);
18
18
  //assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0/96]')), [['<a class="invalid">http://[::ffff:0:0/96]</a>'], '']);
19
19
  assert.deepStrictEqual(inspect(parser(' http://a')), undefined);
@@ -1,5 +1,5 @@
1
1
  import { AutolinkParser } from '../inline';
2
- import { union, some, syntax, guard, validate, fmap } from '../../combinator';
2
+ import { union, some, syntax, constraint, validate, fmap } from '../../combinator';
3
3
  import { url } from './autolink/url';
4
4
  import { email } from './autolink/email';
5
5
  import { channel } from './autolink/channel';
@@ -12,14 +12,14 @@ import { Syntax, State } from '../context';
12
12
  import { stringify } from '../util';
13
13
 
14
14
  export const autolink: AutolinkParser = fmap(
15
- validate(/^(?:[@#>0-9A-Za-z]|\S#)/,
16
- guard(context => ~context.state! & State.autolink,
15
+ validate(/^(?:[@#>0-9A-Za-z]|\S[#>])/,
16
+ constraint(State.autolink, false,
17
17
  syntax(Syntax.autolink, 1, 1,
18
18
  some(union([
19
19
  url,
20
20
  email,
21
21
  // Escape unmatched email-like strings.
22
- str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)+/),
22
+ str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)*/),
23
23
  channel,
24
24
  account,
25
25
  // Escape unmatched account-like strings.
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ExtensionParser } from '../../inline';
3
- import { union, some, syntax, creation, precedence, guard, state, validate, surround, open, lazy, fmap } from '../../../combinator';
3
+ import { union, some, syntax, creation, precedence, constraint, state, validate, surround, open, lazy, fmap } from '../../../combinator';
4
4
  import { inline } from '../../inline';
5
5
  import { indexee, identity } from './indexee';
6
6
  import { txt, str, stropt } from '../../source';
@@ -12,7 +12,7 @@ import IndexParser = ExtensionParser.IndexParser;
12
12
 
13
13
  export const index: IndexParser = lazy(() => validate('[#', fmap(indexee(surround(
14
14
  '[#',
15
- guard(context => ~context.state! & State.index,
15
+ constraint(State.index, false,
16
16
  syntax(Syntax.index, 2, 1,
17
17
  state(State.annotation | State.reference | State.index | State.label | State.link | State.media | State.autolink,
18
18
  startTight(
@@ -1,6 +1,6 @@
1
1
  import { Array } from 'spica/global';
2
2
  import { ExtensionParser } from '../../inline';
3
- import { union, guard, creation, validate, surround, clear, fmap } from '../../../combinator';
3
+ import { union, constraint, creation, validate, surround, clear, fmap } from '../../../combinator';
4
4
  import { str } from '../../source';
5
5
  import { State } from '../../context';
6
6
  import { html } from 'typed-dom/dom';
@@ -13,7 +13,7 @@ export const segment: ExtensionParser.LabelParser.SegmentParser = clear(validate
13
13
  ])));
14
14
 
15
15
  export const label: ExtensionParser.LabelParser = validate(['[$', '$'], creation(fmap(
16
- guard(context => ~context.state! & State.label,
16
+ constraint(State.label, false,
17
17
  union([
18
18
  surround('[', body, ']'),
19
19
  body,
@@ -21,10 +21,10 @@ describe('Unit: parser/inline/html', () => {
21
21
  it('invalid', () => {
22
22
  assert.deepStrictEqual(inspect(parser('')), undefined);
23
23
  assert.deepStrictEqual(inspect(parser('<0>')), undefined);
24
- assert.deepStrictEqual(inspect(parser('<aT>')), undefined);
24
+ assert.deepStrictEqual(inspect(parser('<aT>')), [['<span class="invalid">&lt;aT&gt;</span>'], '']);
25
25
  assert.deepStrictEqual(inspect(parser('<a,b>')), undefined);
26
26
  assert.deepStrictEqual(inspect(parser('<a, b>')), undefined);
27
- assert.deepStrictEqual(inspect(parser('<T>')), undefined);
27
+ assert.deepStrictEqual(inspect(parser('<T>')), [['<span class="invalid">&lt;T&gt;</span>'], '']);
28
28
  assert.deepStrictEqual(inspect(parser('<bdi>')), [['<span class="invalid">&lt;bdi&gt;</span>'], '']);
29
29
  assert.deepStrictEqual(inspect(parser('<bdi>z')), [['<span class="invalid">&lt;bdi&gt;z</span>'], '']);
30
30
  assert.deepStrictEqual(inspect(parser('<bdi></bdi>')), [['<span class="invalid">&lt;bdi&gt;&lt;/bdi&gt;</span>'], '']);
@@ -39,8 +39,8 @@ describe('Unit: parser/inline/html', () => {
39
39
  assert.deepStrictEqual(inspect(parser('<bdi>\\\na</bdi>')), [['<span class="invalid">&lt;bdi&gt;<span class="linebreak"> </span>a&lt;/bdi&gt;</span>'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('<bdi>a')), [['<span class="invalid">&lt;bdi&gt;a</span>'], '']);
41
41
  assert.deepStrictEqual(inspect(parser('<bdi>a</BDO>')), [['<span class="invalid">&lt;bdi&gt;a&lt;/BDO&gt;</span>'], '']);
42
- assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')), undefined);
43
- assert.deepStrictEqual(inspect(parser('<BDI>a</bdo>')), undefined);
42
+ assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')), [['<span class="invalid">&lt;BDI&gt;a&lt;/BDI&gt;</span>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('<BDI>a</bdo>')), [['<span class="invalid">&lt;BDI&gt;a&lt;/bdo&gt;</span>'], '']);
44
44
  assert.deepStrictEqual(inspect(parser('</bdi>')), undefined);
45
45
  assert.deepStrictEqual(inspect(parser('<bdi/>')), undefined);
46
46
  assert.deepStrictEqual(inspect(parser('<b><b><b>a</b></b></b>')), [['<span class="invalid">&lt;b&gt;<span class="invalid">&lt;b&gt;<span class="invalid">&lt;b&gt;a&lt;/b&gt;</span>&lt;/b&gt;</span>&lt;/b&gt;</span>'], '']);
@@ -87,9 +87,9 @@ describe('Unit: parser/inline/html', () => {
87
87
  assert.deepStrictEqual(inspect(parser('<a>')), [['<span class="invalid">&lt;a&gt;</span>'], '']);
88
88
  assert.deepStrictEqual(inspect(parser('<bdi><a>a</a></bdi>')), [['<bdi><span class="invalid">&lt;a&gt;a&lt;/a&gt;</span></bdi>'], '']);
89
89
  assert.deepStrictEqual(inspect(parser('<bdi>a<a>b</a>c</bdi>')), [['<bdi>a<span class="invalid">&lt;a&gt;b&lt;/a&gt;</span>c</bdi>'], '']);
90
- assert.deepStrictEqual(inspect(parser('<img>')), [['<img'], '>']);
91
- assert.deepStrictEqual(inspect(parser('<bdi><img></bdi>')), [['<bdi>&lt;img&gt;</bdi>'], '']);
92
- assert.deepStrictEqual(inspect(parser('<img />')), [['<img'], ' />']);
90
+ assert.deepStrictEqual(inspect(parser('<img>')), [['<span class="invalid">&lt;img&gt;</span>'], '']);
91
+ assert.deepStrictEqual(inspect(parser('<bdi><img></bdi>')), [['<bdi><span class="invalid">&lt;img&gt;</span></bdi>'], '']);
92
+ assert.deepStrictEqual(inspect(parser('<img />')), undefined);
93
93
  assert.deepStrictEqual(inspect(parser('<bdi><img /></bdi>')), [['<bdi>&lt;img /&gt;</bdi>'], '']);
94
94
  });
95
95
 
@@ -100,15 +100,15 @@ describe('Unit: parser/inline/html', () => {
100
100
  assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>')), [['<bdi>a</bdi>'], '']);
101
101
  assert.deepStrictEqual(inspect(parser('<bdi __proto__>a</bdi>')), undefined);
102
102
  assert.deepStrictEqual(inspect(parser('<bdi constructor>a</bdi>')), [['<span class="invalid">&lt;bdi constructor&gt;a&lt;/bdi&gt;</span>'], '']);
103
- assert.deepStrictEqual(inspect(parser('<bdi toString>a</bdi>')), undefined);
104
- assert.deepStrictEqual(inspect(parser('<bdi X>a</bdi>')), undefined);
103
+ assert.deepStrictEqual(inspect(parser('<bdi toString>a</bdi>')), [['<span class="invalid">&lt;bdi toString&gt;a&lt;/bdi&gt;</span>'], '']);
104
+ assert.deepStrictEqual(inspect(parser('<bdi X>a</bdi>')), [['<span class="invalid">&lt;bdi X&gt;a&lt;/bdi&gt;</span>'], '']);
105
105
  assert.deepStrictEqual(inspect(parser('<bdi x>a</bdi>')), [['<span class="invalid">&lt;bdi x&gt;a&lt;/bdi&gt;</span>'], '']);
106
106
  assert.deepStrictEqual(inspect(parser('<bdo>a</bdo>')), [['<span class="invalid">&lt;bdo&gt;a&lt;/bdo&gt;</span>'], '']);
107
107
  assert.deepStrictEqual(inspect(parser('<bdo >a</bdo>')), [['<span class="invalid">&lt;bdo &gt;a&lt;/bdo&gt;</span>'], '']);
108
108
  assert.deepStrictEqual(inspect(parser('<bdo __proto__>a</bdo>')), undefined);
109
109
  assert.deepStrictEqual(inspect(parser('<bdo constructor>a</bdo>')), [['<span class="invalid">&lt;bdo constructor&gt;a&lt;/bdo&gt;</span>'], '']);
110
- assert.deepStrictEqual(inspect(parser('<bdo toString>a</bdo>')), undefined);
111
- assert.deepStrictEqual(inspect(parser('<bdo X>a</bdo>')), undefined);
110
+ assert.deepStrictEqual(inspect(parser('<bdo toString>a</bdo>')), [['<span class="invalid">&lt;bdo toString&gt;a&lt;/bdo&gt;</span>'], '']);
111
+ assert.deepStrictEqual(inspect(parser('<bdo X>a</bdo>')), [['<span class="invalid">&lt;bdo X&gt;a&lt;/bdo&gt;</span>'], '']);
112
112
  assert.deepStrictEqual(inspect(parser('<bdo x>a</bdo>')), [['<span class="invalid">&lt;bdo x&gt;a&lt;/bdo&gt;</span>'], '']);
113
113
  assert.deepStrictEqual(inspect(parser('<bdo dir>a</bdo>')), [['<span class="invalid">&lt;bdo dir&gt;a&lt;/bdo&gt;</span>'], '']);
114
114
  assert.deepStrictEqual(inspect(parser('<bdo dir=>a</bdo>')), undefined);
@@ -116,16 +116,16 @@ describe('Unit: parser/inline/html', () => {
116
116
  assert.deepStrictEqual(inspect(parser('<bdo dir=">a</bdo>')), undefined);
117
117
  assert.deepStrictEqual(inspect(parser('<bdo dir="">a</bdo>')), [['<span class="invalid">&lt;bdo dir=""&gt;a&lt;/bdo&gt;</span>'], '']);
118
118
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" dir="rtl">a</bdo>')), [['<span class="invalid">&lt;bdo dir="rtl" dir="rtl"&gt;a&lt;/bdo&gt;</span>'], '']);
119
- assert.deepStrictEqual(inspect(parser('<bdo diR="rtl">a</bdo>')), undefined);
119
+ assert.deepStrictEqual(inspect(parser('<bdo diR="rtl">a</bdo>')), [['<span class="invalid">&lt;bdo diR="rtl"&gt;a&lt;/bdo&gt;</span>'], '']);
120
120
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
121
121
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
122
122
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
123
123
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
124
124
  assert.deepStrictEqual(inspect(parser('<wbr\n>')), undefined);
125
- assert.deepStrictEqual(inspect(parser('<wbr >')), [['<wbr'], ' >']);
126
- assert.deepStrictEqual(inspect(parser('<wbr constructor>')), [['<wbr'], ' constructor>']);
127
- assert.deepStrictEqual(inspect(parser('<wbr X>')), [['<wbr'], ' X>']);
128
- assert.deepStrictEqual(inspect(parser('<wbr x>')), [['<wbr'], ' x>']);
125
+ assert.deepStrictEqual(inspect(parser('<wbr >')), [['<wbr>'], '']);
126
+ assert.deepStrictEqual(inspect(parser('<wbr constructor>')), [['<span class="invalid">&lt;wbr constructor&gt;</span>'], '']);
127
+ assert.deepStrictEqual(inspect(parser('<wbr X>')), [['<span class="invalid">&lt;wbr X&gt;</span>'], '']);
128
+ assert.deepStrictEqual(inspect(parser('<wbr x>')), [['<span class="invalid">&lt;wbr x&gt;</span>'], '']);
129
129
  });
130
130
 
131
131
  });
@@ -19,14 +19,15 @@ const attrspecs = {
19
19
  Object.setPrototypeOf(attrspecs, null);
20
20
  Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
21
21
 
22
- export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/, syntax(Syntax.none, 5, 1, union([
22
+ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/i, syntax(Syntax.none, 5, 1, union([
23
23
  focus(
24
- '<wbr>',
24
+ /^<wbr[^\S\n]*>/i,
25
25
  () => [[h('wbr')], '']),
26
- focus(
26
+ surround(
27
27
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
28
- /^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/,
29
- source => [[source], '']),
28
+ str(/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/i), some(union([attribute])), str(/^[^\S\n]*>/), true,
29
+ ([as, bs = [], cs], rest) =>
30
+ [[elem(as[0].slice(1), push(unshift(as, bs), cs), [], [])], rest]),
30
31
  match(
31
32
  new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`),
32
33
  memoize(
@@ -44,7 +45,7 @@ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^
44
45
  [[elem(tag, as, bs, [])], rest]),
45
46
  ([, tag]) => TAGS.indexOf(tag), [])),
46
47
  match(
47
- /^<([a-z]+)(?=[^\S\n]|>)/,
48
+ /^<([a-z]+)(?=[^\S\n]|>)/i,
48
49
  memoize(
49
50
  ([, tag]) =>
50
51
  surround<HTMLParser.TagParser, string>(surround(
@@ -62,8 +63,8 @@ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^
62
63
  new Cache(10000))),
63
64
  ])))));
64
65
 
65
- export const attribute: HTMLParser.TagParser.AttributeParser = union([
66
- str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/),
66
+ export const attribute: HTMLParser.AttributeParser = union([
67
+ str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/i),
67
68
  ]);
68
69
 
69
70
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Element