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.
Files changed (48) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/design.md +7 -6
  3. package/dist/index.js +141 -88
  4. package/package.json +1 -1
  5. package/src/combinator/control/constraint/block.ts +1 -1
  6. package/src/combinator/control/constraint/contract.ts +7 -7
  7. package/src/combinator/control/constraint/line.ts +1 -1
  8. package/src/combinator/control/manipulation/clear.ts +7 -0
  9. package/src/combinator/control/manipulation/convert.ts +1 -1
  10. package/src/combinator/control/manipulation/duplicate.ts +4 -4
  11. package/src/combinator/control/manipulation/fallback.ts +3 -3
  12. package/src/combinator/control/manipulation/indent.ts +3 -3
  13. package/src/combinator/control/manipulation/lazy.ts +2 -2
  14. package/src/combinator/control/manipulation/match.ts +1 -1
  15. package/src/combinator/control/manipulation/recovery.ts +3 -3
  16. package/src/combinator/control/manipulation/reverse.ts +1 -1
  17. package/src/combinator/control/manipulation/scope.ts +3 -3
  18. package/src/combinator/control/manipulation/surround.ts +59 -61
  19. package/src/combinator/control/manipulation/trim.ts +3 -3
  20. package/src/combinator/control/monad/bind.ts +6 -6
  21. package/src/combinator/control/monad/fmap.ts +6 -6
  22. package/src/combinator/data/parser/context/delimiter.ts +47 -26
  23. package/src/combinator/data/parser/context.ts +8 -8
  24. package/src/combinator/data/parser/inits.ts +4 -4
  25. package/src/combinator/data/parser/sequence.ts +4 -4
  26. package/src/combinator/data/parser/some.ts +3 -3
  27. package/src/combinator/data/parser/subsequence.ts +3 -3
  28. package/src/combinator/data/parser/tails.ts +3 -3
  29. package/src/combinator/data/parser/union.ts +3 -3
  30. package/src/combinator/data/parser.ts +14 -13
  31. package/src/combinator.ts +1 -0
  32. package/src/parser/api/parse.test.ts +2 -2
  33. package/src/parser/block/extension/figure.ts +1 -1
  34. package/src/parser/block/extension/table.ts +3 -3
  35. package/src/parser/block/olist.ts +4 -4
  36. package/src/parser/block/reply/cite.ts +3 -3
  37. package/src/parser/block/ulist.ts +1 -1
  38. package/src/parser/inline/autolink/hashnum.ts +5 -1
  39. package/src/parser/inline/code.ts +2 -1
  40. package/src/parser/inline/emstrong.ts +2 -1
  41. package/src/parser/inline/extension/indexer.ts +6 -0
  42. package/src/parser/inline/extension/label.ts +1 -1
  43. package/src/parser/inline/html.ts +2 -2
  44. package/src/parser/inline.test.ts +1 -0
  45. package/src/parser/inline.ts +19 -4
  46. package/src/parser/util.ts +10 -10
  47. package/src/parser/visibility.ts +11 -11
  48. package/src/util/quote.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.289.4
4
+
5
+ - Refactoring.
6
+
7
+ ## 0.289.3
8
+
9
+ - Refactoring.
10
+
3
11
  ## 0.289.2
4
12
 
5
13
  - Refactoring.
package/design.md CHANGED
@@ -289,9 +289,9 @@ CodeMirrorが素では速いがVimModeでは数万文字程度でも耐え難く
289
289
 
290
290
  ### バックトラック
291
291
 
292
- SecuremarkのAnnotation構文に典型的であるように文脈を変更する構文の中にその文脈に依存し変更される他の構文が存在する場合文脈の相違から解析結果を再利用不能(`αAβ | αA'B`)なバックトラックが生じる。またこの結果再帰的バックトラックが生じる可能性があり再帰的バックトラックは一般的にメモ化により解決されるがCommonMarkは実行性能追及のためメモ化を廃止しているためメモ化により性能を低下させてまで文脈依存構文の問題を解決するつもりはないと思われる(すなわちCommonMarkは機械を至上とし人間に制約を課す低水準の言語であり人間の需要を至上とするSecuremarkとは対極に位置する)。従って現在の再帰的バックトラックなしで解析可能な構文と最小計算量に制約されるCommonMarkにはこれ以上再帰的バックトラックが生じる可能性を増加させて文脈依存構文を追加できないという拡張性の欠陥が存在する。CommonMarkの仕様策定者が構文の拡張に(名称を維持するか否かにかかわらず)不自然なまでに消極的または進展がないのは正当な理由や怠慢からでなく文脈依存構文を追加するにつれて構文解析戦略の失敗が明白になっていくためおよび現在の高い実行性能を低下させたくないためであり陳腐な自尊心を守るためにこのような拡張性の欠陥を秘匿しCommonMarkとその仕様策定者である自分の評価が下がらないよう画策しているからである。でなければ何年も隠さず速やかにこの拡張性の欠陥を公表して助力を求めていなければならず不都合な事実を隠し全Markdown利用者および開発者を不必要に足止めした罪は重い。CommonMarkは`~~a~~`のような文脈自由構文は容易に追加できるがこうしたマージンを失えばもはや後はない。
293
- CommonMarkは小さく単純であるがゆえに正しくいられる象牙の塔であり仕様策定者はこの正しさを失わず正しいままでいたいがために象牙の塔に引きこもり小さな表面的完全性に固執し続けているに過ぎない。しかしCommonMarkは実際にはまったく完全ではなく本来文脈依存構文である構文を文脈自由構文として解析しているため破綻している部分があり実際のところCommonMarkは最初から現在までずっと壊れている。またCommonMarkはバックトラックなく最小計算量で解析するために文脈自由言語として設計されているが実際には文脈依存言語であるMarkdownから文脈依存構文を文脈自由構文に変換して除去することに失敗しているためCommonMarkは最初の数年間は再帰的バックトラックに気づかず最悪計算量が指数関数計算量になっており修正後は最悪計算量が当初の想定の2nから32nへと劇的に悪化している。CommonMarkが最初の数年間指数関数計算量であった事実はCommonMarkが初歩的な再帰的バックトラックの原理すら理解していない素人により設計された素人仕事である事実を証明している。一貫して素人により設計開発仕様策定されているCommonMarkは未だにバックトラックを忌避し2nの最小計算量に固執しているがそんなものはとっくの昔に破綻してるのを未練がましく執着しているだけである。最悪計算量が32nにまで悪化するのであれば計算量が少ないよう適切に設計された文脈依存言語と大差なく最初から文脈依存言語として適切に設計するほうが自然で破綻がなく拡張性を確保できていた。文脈依存構文を強引に文脈自由構文として解析して最悪計算量が当初の想定の2nから32nに劇的に悪化し結局文脈依存言語の妥当な最悪計算量の水準に落ちていることから文脈自由言語として設計されたCommonMarkの破綻と失敗は明らかでありCommonMarkは文脈自由構文に固執せず最初から多少の文脈依存構文を許容するよう設計しなければならなかった。実際には文脈依存言語であるにもかかわらず文脈自由言語としてしか構文解析できなければ構文解析が破綻し構文が増えるほど破綻が拡大することは自明でありすでに破綻済みで失敗済みのCommonMarkに未来などない。文脈依存言語であるMarkdownに対して文脈自由構文解析器として作られたCommonMarkは最初から技術選択を間違え失敗しており最初から破綻していた。CommonMarkが文脈依存言語を文脈自由言語として最小計算量で解析するために使用した手法は邪道の小手先の技術に過ぎずCommonMarkは邪道を選んだ挙句失敗に終わったのである。文脈依存言語を文脈依存言語のまま解析する正道を選んだSecuremarkが正着し文脈自由言語に歪める邪道を選んだCommonMarkが失着に終わったのは当然の帰結であり最初の言語選択の時点で決まっていたことである。文脈依存言語であるMarkdownを文脈自由言語として解析しようとして行き詰ったCommonMarkとその閉塞に技術的合理性はなくCommonMarkは最初からの失敗していた過去の遺物であり廃棄すべき負債である。CommonMarkに動きがないのはすでに破綻しており死んでいることに気付かれないように死んでいるからに過ぎない。このようにCommonMarkは完全に破綻し失敗に終わっているためCommonMarkの拡張や発展を期待しても無駄であり既存の文脈依存構文による破綻がなく新たに文脈依存構文を追加可能な拡張性の高いMarkdown仕様は新しく作り直さなければ作れない。しかしCommonMarkの仕様策定者は独自の新しい仕様においてもMarkdownをバックトラックを排除した文脈自由言語として設計しているため救いようがない。しかもその構文と仕様は機械可読性を至上としているため非常に醜く人間が書くことも読むことも困難で実用性の欠如したものである。
294
- Securemarkはスーパークラス構文が解析に失敗した入力をサブクラス構文で解析しないことにより再帰的バックトラックを回避する(ここで解析中の構文自身はスーパークラスとサブクラスの両方に含まれるものとする)。スーパークラス構文A(`αAβ`)の解析が失敗すればサブクラス構文B(`α'A'β'`)の解析も失敗することは自明であり解析を試みるまでもなく省略できる。これは構文の文法が生成する言語空間がスーパーセットとサブセットの関係にあるならスーパーセットの言語空間の外にある文字列はサブセットの言語空間の内に入る余地がないことからも自明である(この解析法は事前処理によっても可能だが文脈内外のオートリンクURLの括弧解析などを高速に行うことは困難であるためMarkdownをこの事前処理により高速化することは難しい)。メモ化は解析結果を再利用することで結果的に副次的効果としてバックトラックを回避しているのでありメモ化はバックトラックを回避するだけなら過剰機能であり不要である(メモ化はバックトラックがなければ使用されないためバックトラックの少ないほとんどの入力に対してはほとんど使用されず無駄であり空間計算量を常に不必要に数倍以上に増加させてまで行う利益は少ないことから構文解析において必須ではない。バックトラック回避のためにメモ化するとバックトラックなしで解析可能な場合も常に不必要に空間計算量が増加することがメモ化の最大の欠点である(この問題は解析失敗時のみメモ化すれば解消可能のはずだが基本的にはこうなる)。特に文脈自由構文解析器におけるメモ化の使用は完全に無駄でありバグである。バックトラックが他の方法で解決されるならば最終的に文脈ごと破棄され使用されないメモ化も無駄であり複数の文脈で解析結果が同一である文脈独立性のある構文ならメモ化した解析結果を異なる文脈で再利用でき有用だがそのような構文は基本的に少数であるため効果が限定的であり最悪計算量は改善されない)。この独自の解析法により、CommonMarkならば最悪時間計算量384nを要する拡張Markdown言語をSecuremarkはメモ化なしで12nの最悪時間計算量で解析しさらに一定時間内で解析不能な入力の影響を解析時間と解析範囲の制限により局限している。またSecuremarkはメモ化を行っていないため実装依存の非効率性を除けば空間計算量も小さい。時間計算量と空間計算量を合わせてO(n, n)と表記すると文脈依存言語の通常の最悪計算量はO(n^2, n)、メモ化により効率化できた場合もO(nm, nm)(S(m)>=m byte)(解析結果の構文木等を記録するため空間使用量S(m)>=m byte)に過ぎないがSecuremarkのマーキング法はO(nm, nm)(S(m)=m bit)(解析の失敗フラグしか記録しないためS(m)=m bit。また包含文字列を含め全体でn byteの構文1つに対してメモ化は少なくともn byteを消費するがマーキング法のメモリ消費量は構文全体のサイズにかかわらず1bit固定である。よって100KBの構文1つに対してメモ化は100KB以上消費するがこの場合もマーキング法は1bitしか消費しない。なお成功フラグによる解析は解析済みかの情報が追加で必要になり処理が複雑化かつほとんどの成功した解析に対してメモリ消費と追加処理が発生し解析効率が全体として悪化するが失敗フラグは少数の失敗した解析でしか解析効率が悪化しないため失敗フラグを記録するほうが全体として解析効率が高く優れている)と極めて効率的であり最も優れている。以上のようにSecuremarkの構文解析アルゴリズムの優位性は理論と実践いずれにおいても革新的かつ圧倒的である。現在のSecuremarkは開発効率と安全性優先の実装により実行性能が大きく低下しているが最悪計算量で低速に動作させる入力に対してもこの実装をサーバーで使用し多数のユーザーのリクエストに応じるには低速で脆弱性となる可能性があるがクライアントで個別のユーザーの操作に応じるには十分高速であるためクライアントで解析する限り解析の効率または速度が実用上問題となることはなく仕様が固まり実行効率優先の高速な実装に移れば速度面の懸念もないだろう。またSecuremarkの再帰数制限はパーサーコンビネーターの使用による実装依存の制限であるため再帰が生じないよう書き換えれば再帰数制限もない。SecuremarkをCommonMarkのような再帰数制限のない実装に変換することは設計上何の支障もないがCommonMarkをSecuremarkのような正常な文脈依存言語解析器に変更することは解析規則の破壊的変更なしに不可能である。具体的には二重リンク`[[]()]()`を解析するときCommonMarkはバックトラックと計算量を最小化すべく文脈自由構文解析器として設計されているためリンク構文内をリンク構文が定義されていない異なる文脈として解析せず外側のリンク構文の解析を破棄して内側のみリンク構文として解析するがSecuremarkは文脈依存構文解析器とし設計されているためリンク構文内にリンク構文が定義されておらず外側のみリンクとして解析する(ここでCommonMarkはリンク構文`[]()`の文脈自由化に角括弧`[]`に対しては成功したが丸括弧`()`に対しては失敗したことで最悪計算量が指数関数計算量ないし32nに悪化した)。多くのプログラミング言語を見ても明らかなように文脈依存言語は構文内で使用可能な構文を定義しその他の構文は構文内で使用できず例外処理するのが通常でありCommonMarkのように本来使用不能な構文を外側の構文を無効化して使用可能に変える異常な言語はほとんどの人間はCommonMark以外に見たことがないだろう。ほぼすべての人間において他のすべての言語が同じ一貫した規則を持ち同じ規則で統一的に使用できるのに対してCommonMarkだけが他と異なる異常な挙動をして認知的負荷をかけるのである。破壊的変更を避けるため旧構文で使用した苦肉の策を不必要に新構文でも使用して一貫させれば文脈依存言語なのに文脈自由言語の苦肉の策で解析される非常に不自然で理論的に設計ミスが明白で実用的にも認知的負荷の高い言語となり旧構文だけ従来通り文脈自由構文として解析し新構文を文脈依存構文として解析すればキメラ的な非常に不自然かつ歪で一貫性のない解析規則によりCommonMarkという一つの言語の中だけでもユーザーを混乱させるものとなる。そして構文エラーであることが明らかな二重リンクを意図的に入力することはほぼないためCommonMarkの異常な挙動はこれまであまり人目に付かなかったがMarkdownに文脈依存構文を追加して明らかでない構文エラーが頻発すると他の言語と逆に外側の構文を無効化していくCommonMarkの異常な挙動を頻繁に目撃し認知的負荷をかけられることになる。このようにCommonMarkは内部設計だけ文脈依存構文解析器に変更しても理論的齟齬が解析結果と使用感に明白に表れるためCommonMarkが失敗した言語である事実は到底隠し切れるものではない。Markdownはもはや負債以外の何物でもないCommonMarkの異常な解析規則を捨てて素直な文脈依存構文言語として新しい仕様を作り直すのが賢明である。
292
+ SecuremarkのAnnotation構文に典型的であるように文脈を変更する構文の中にその文脈に依存し変更される他の構文が存在する場合文脈の相違から解析結果を再利用不能(`αAβ | αA'B`)なバックトラックが生じる。またこの結果再帰的バックトラックが生じる可能性があり再帰的バックトラックは一般的にメモ化により解決されるがCommonMarkは最小計算量と実行性能を追及するためメモ化を廃止していることからメモ化により性能を低下させてまで文脈依存構文の問題を解決するつもりはないと思われる(すなわちCommonMarkは機械を至上とし人間に制約を課す低水準の言語であり人間の需要を至上とするSecuremarkとは対極に位置する)。従って現在の再帰的バックトラックなしで解析可能な構文と最小計算量に制約されるCommonMarkにはこれ以上再帰的バックトラックが生じる可能性を増加させて文脈依存構文を追加できないという拡張性の欠陥が存在する(CommonMarkは`~~a~~`のような文脈自由構文は容易に追加できるがこうしたマージンを失えばもはや後はなく文脈依存構文を追加できないという事実に直面する)。CommonMarkの仕様策定者が構文の拡張に(名称を維持するか否かにかかわらず)不自然なまでに消極的または進展がないのは正当な理由や怠慢からでなく文脈依存構文を追加するにつれて構文解析戦略の失敗が明白になっていくためおよび最小計算量を放棄して現在の高い実行性能を低下させたくないためであり陳腐な自尊心を守るためにこのような拡張性の欠陥を秘匿しCommonMarkとその仕様策定者である自分の評価が下がらないよう画策しているからである。でなければこの拡張性の欠陥を何年も隠さず速やかに公表して助力を求めていなければならず不都合な事実を隠し陳腐な自尊心を守るために全Markdown利用者および開発者を不必要に足止めした罪は重い。
293
+ CommonMarkは小さく単純であるがゆえに正しくいられる象牙の塔であり仕様策定者はこの正しさを失わず正しいままでいたいがために象牙の塔に引きこもり小さな表面的完全性に固執し続けているに過ぎない。しかしCommonMarkは実際にはまったく完全ではなく本来文脈依存言語であるMarkdownを文脈自由言語として解析しているため破綻している部分があり実際のところCommonMarkは最初から最後までずっと壊れている。CommonMarkはバックトラックなく最小計算量で解析するために文脈自由言語として設計されているが実際には文脈依存言語であるMarkdownから文脈依存構文を文脈自由構文に変換して除去することに失敗しているためCommonMarkは最初の数年間は再帰的バックトラックに気づかず最悪計算量が指数関数計算量になっており修正後は最悪計算量が当初の想定の2nから32nへと劇的に悪化している(より正確にはCode構文とHTML構文によりCommonMarkもSecuremarkも+2nされる)。CommonMarkが最初の数年間他人に指摘され修正されるまで指数関数計算量であった事実(https://github.com/commonmark/cmark/commit/45f4fc9b917c11221aa03e70a41e3046335a235d)はCommonMarkが初歩的な再帰的バックトラックの原理すら理解していないド素人により設計された素人仕事である事実を証明しておりたかが強調構文の解析のためにメモ化を行い(https://github.com/commonmark/commonmark.js/commit/6d7d6cf150dedb53b7f0972b79313df3364ebbed https://github.com/commonmark/commonmark.js/blob/ac8529c9f55da7fdc1186e3f34313cf411de6e71/js/stmd.js )他人にメモ化を使わない正しい実装に修正された事実(https://github.com/commonmark/commonmark.js/commit/8837f199608ac2e321f75653736747b1e692072f)もまたCommonMarkの仕様策定者がその任に堪える能力のないド素人である事実を証明している。スタックの代わりにメモ化するド素人の能力を誰が擁護できるのか?ド素人が作った結果初歩的な再帰的バックトラックすら他人に指摘されるまで気づかず最悪計算量が32nにまで著しく悪化した設計が正しいと言えるのか?不可能である。一貫してド素人により設計開発仕様策定されているCommonMarkは未だにバックトラックを忌避し2nの最小計算量に固執しているがそんなものはとっくの昔に破綻してるのを未練がましく執着しているだけである。最悪計算量が32nにまで悪化するのであれば計算量が少ないよう適切に設計された文脈依存言語と大差なく最初から文脈依存言語として適切に設計するほうが自然で破綻がなく拡張性を確保できていた。。さらにSecuremarkは再帰的バックトラックを対策しているため文脈依存構文数が増えても最悪計算量は1+mで定数的にしか増加しない(新しい構文の新しい文脈も内部の括弧類のような基本構造は共通であるため一度解析すればあとは既存の解析情報を利用してバックトラックなしで1回で解析できる)がCommonMarkは再帰的バックトラックを対策していないため文脈依存構文数が増えると最悪計算量が2^mで指数関数的に致命的に激増する。文脈依存構文を強引に文脈自由構文として解析したために最悪計算量が当初の想定の2nから32nに劇的に悪化し結局文脈依存言語の妥当な最悪計算量の水準に落ちていることおよび文脈依存構文を追加すると最悪計算量が指数関数的に悪化することから文脈自由言語として設計されたCommonMarkの破綻と失敗は明らかでありCommonMarkは文脈自由構文に固執せず最初から多少の文脈依存構文を許容するよう設計しなければならなかった。実際には文脈依存言語であるにもかかわらず文脈自由言語としてしか構文解析できなければ構文解析が破綻し構文が増えるほど破綻が拡大することは自明でありすでに破綻済みで失敗済みのCommonMarkに未来などない。文脈依存言語であるMarkdownに対して文脈自由構文解析器として作られたCommonMarkは最初から技術選択を間違え失敗しており最初から破綻していた。CommonMarkが文脈依存言語を文脈自由言語として最小計算量で解析するために使用した手法は邪道の小手先の技術に過ぎずCommonMarkは邪道を選んだ挙句失敗に終わったのである。文脈依存言語を文脈依存言語のまま解析する正道を選んだSecuremarkが正着し文脈自由言語に歪める邪道を選んだCommonMarkが失着に終わったのは当然の帰結であり最初の言語種別選択の時点で決まっていたことである。文脈依存言語であるMarkdownを文脈自由言語として解析しようとして行き詰ったCommonMarkとその閉塞に技術的合理性はなくCommonMarkは最初からの失敗していた過去の遺物であり廃棄すべき負債である。CommonMarkに動きがないのはすでに破綻しており死んでいることに気付かれないように死んでいるからに過ぎない。このようにCommonMarkは完全に破綻し失敗に終わっているためCommonMarkの拡張や発展を期待しても無駄であり既存の文脈依存構文による破綻がなく新たに文脈依存構文を追加可能な拡張性の高いMarkdown仕様は新しく作り直さなければ作れない。しかしCommonMarkの仕様策定者は独自の新しい仕様においてもMarkdownをバックトラックを排除した文脈自由言語として設計しているため救いようがない。しかもその構文と仕様は機械可読性を至上としているため非常に醜く人間が書くことも読むことも困難で実用性の欠如したものである。
294
+ Securemarkはスーパークラス構文が解析に失敗した入力をサブクラス構文で解析しないことにより再帰的バックトラックを回避する(解析中の構文自身はスーパークラスとサブクラスの両方に含まれるものとする)。スーパークラス構文A(`αAβ`)の解析が失敗すればサブクラス構文B(`α'A'β'`)の解析も失敗することは自明であり試みるまでもなく解析を省略できる。これは二つの構文の文法が生成する各言語空間がスーパーセットとサブセットの関係にあるならスーパーセットの言語空間の外にある文字列はサブセットの言語空間の内に入る余地がないことからも自明である(この解析法は事前処理によっても可能だが文脈内外のオートリンクURLの括弧解析などを高速に行うことは困難であるためMarkdownをこの事前処理により高速化することは難しい)。メモ化は解析結果を再利用することで結果的に副次的効果としてバックトラックを回避しているのでありメモ化はバックトラックを回避するだけなら過剰機能であり不要である(メモ化はバックトラックがなければ使用されないためバックトラックの少ないほとんどの入力に対してはほとんど使用されず無駄であり空間計算量を常に不必要に数倍以上に増加させてまで行う利益は少ないことから構文解析において必須でも標準でもない。バックトラック回避のためにメモ化するとバックトラックなしで解析可能な場合も常に不必要に空間計算量が増加することがメモ化の最大の欠点である(この問題は解析失敗時のみメモ化すれば解消可能のはずだが基本的にはこうなる)。特に文脈自由構文解析器におけるメモ化の使用は完全に無駄でありバグである。バックトラックが発生しないか他の方法で解決されるならば最終的に使用されないメモ化は無駄であり複数の文脈で解析結果が同一である文脈独立性のある構文ならメモ化した解析結果を異なる文脈で再利用でき有用だがそのような構文は基本的に少数であるため効果が限定的であり最悪計算量は改善されない)。この独自の解析法により、CommonMarkならば最悪計算量384nを要する拡張Markdown言語をSecuremarkはメモ化なしで12nの最悪時間計算量で解析している。すなわち直接比較してもCommonMarkの最悪計算量32nに対してSecuremarkは12nでありSecuremarkはCommonMarkより最悪計算量が小さい。またSecuremarkはメモ化を行っていないため実装依存の非効率性を除けば空間計算量も小さい。時間計算量と空間計算量を合わせてO(n, n)と表記すると文脈依存言語の通常の最悪計算量はO(n^2, n)、メモ化により効率化できた場合もO(nm, nm)(S(m)>=m byte)(解析結果の構文木等を記録するため空間使用量S(m)>=m byte)に過ぎないがSecuremarkのマーキング法はO(nm, nm)(S(m)=m bit)(解析の失敗フラグしか記録しないためS(m)=m bit。また包含文字列を含め全体でn byteの構文1つに対してメモ化は少なくともn byteを消費するがマーキング法のメモリ消費量は構文全体のサイズにかかわらず1bit固定である。よって100KBの構文1つに対してメモ化は100KB以上消費するがこの場合もマーキング法は1bitしか消費しない。なお成功フラグによる解析は解析済みかの情報が追加で必要になり処理が複雑化かつほとんどの成功した解析に対してメモリ消費と追加処理が発生し解析効率が全体として悪化するが失敗フラグは少数の失敗した解析でしか解析効率が悪化しないため失敗フラグを記録するほうが全体として解析効率が高く優れている)と極めて効率的であり最も優れている。以上のようにSecuremarkの構文解析アルゴリズムの優位性は理論と実践いずれにおいても革新的かつ圧倒的である。現在のSecuremarkは開発効率と安全性優先の実装により実行性能が大きく低下しているが一定時間内で解析不能な入力の影響を解析時間と解析範囲の制限により局限しているため、最悪計算量で低速に動作させる入力に対してはこの実装をサーバーで使用し多数のユーザーのリクエストに応じるには低速で脆弱性となる可能性があるがクライアントで個別のユーザーの操作に応じるには十分高速であるためクライアントで解析する限り解析の効率または速度が実用上問題となることはなく仕様が固まり実行効率優先の高速な実装に移れば速度面の懸念もないだろう。またSecuremarkの再帰数制限はパーサーコンビネーターの使用による実装依存の制限であるため再帰が生じないよう書き換えれば再帰数制限もない。SecuremarkをCommonMarkのような再帰数制限のない実装に変換することは設計上何の支障もないがCommonMarkをSecuremarkのような正常な文脈依存言語解析器に変更することは解析規則の破壊的変更なしに不可能である。具体的には二重リンク`[[]()]()`を解析するときCommonMarkはバックトラックと計算量を最小化すべく文脈自由構文解析器として設計されているためリンク構文内をリンク構文が定義されていない異なる文脈として解析せず外側のリンク構文の解析を破棄して内側のみリンク構文として解析するがSecuremarkは文脈依存構文解析器とし設計されているためリンク構文内にリンク構文が定義されておらず外側のみリンクとして解析する(ここでCommonMarkはリンク構文`[]()`のバックトラック除去ひいては文脈自由化に角括弧`[]`に対しては成功したが丸括弧`()`に対しては失敗したことで最悪計算量が指数関数計算量ないし32nに悪化した。リンク構文を本来通り文脈依存構文として解析すればリンク構文の最悪計算量が2nとなり角括弧部分に限っては1nから2nに悪化するが丸括弧部分は32nから2nに著しく改善する。ここがCommonMarkの根本的な欠陥と失敗が最も明瞭に表出している部分である)。多くのプログラミング言語を見ても明らかなように文脈依存言語は構文内で使用可能な構文を定義しその他の構文は構文内で使用できず例外処理するのが通常でありCommonMarkのように本来使用不能な構文を外側の構文を無効化して使用可能に変える異常な言語はほとんどの人間はCommonMark以外に見たことがないだろう。ほぼすべての人間において他のすべての言語が同じ一貫した規則を持ち同じ規則で統一的に使用できるのに対してCommonMarkだけが他と異なる異常な挙動をして認知的負荷をかけるのである。破壊的変更を避けるため旧構文で使用した苦肉の策を不必要に新構文でも使用して一貫させれば文脈依存言語なのに文脈自由言語の苦肉の策で解析される非常に不自然で理論的に設計ミスが明白で実用的にも認知的負荷の高い言語となり旧構文だけ従来通り文脈自由構文として解析し新構文を文脈依存構文として解析すればキメラ的な非常に不自然かつ歪で一貫性のない解析規則によりCommonMarkという一つの言語の中だけでもユーザーを混乱させるものとなる。そして構文エラーであることが明らかな二重リンクを意図的に入力することはほぼないためCommonMarkの異常な挙動はこれまであまり人目に付かなかったがMarkdownに文脈依存構文を追加して明らかでない構文エラーが頻発すると他の言語と逆に外側の構文を無効化していくCommonMarkの異常な挙動を頻繁に目撃し認知的負荷をかけられることになる。このようにCommonMarkは内部設計だけ文脈依存構文解析器に変更しても理論的齟齬が解析結果と使用感に明白に表れるためCommonMarkが失敗した言語である事実は到底隠し切れるものではない。Markdownはもはや負債以外の何物でもないCommonMarkの異常な解析規則を捨てて素直な文脈依存構文言語として新しい仕様を作り直すのが賢明である。
295
295
 
296
296
  ### 最適化
297
297
 
@@ -321,9 +321,10 @@ Markdownに本当に必要な仕様はSecuremarkのクラス化制約のよう
321
321
  - 画像のalt(`![*a*]()`, `![![a]()`)をMarkdownとして解釈
322
322
  - 再帰的バックトラック回避の必要上修正不可能
323
323
  - リンクのURLを効率的に解析不可能
324
- - altはまだしもURLは文脈依存構文としての解析を避けられないため再帰もまた避けられず公式デモページのCommonMarkで`[](`を1万文字程度繰り返しただけで解析時間が1秒を超える
324
+ - altはまだしもURLは文脈依存構文としての解析を避けられないため再帰もまた避けられず公式デモページのCommonMarkで`[](`を1万回程度繰り返しただけで解析時間が1秒を超える
325
325
  - この欠陥は入れ子数を制限することで回避可能だがこれはCommonmarkは文脈依存構文全般に入れ子数制限を要することを意味する
326
326
  - CommonMarkは最初のバージョンから数年後にこの欠陥を入れ子数制限により修正しこれにより最悪計算量が当初の想定の2nから32nへと32倍に劇的に悪化したことから文脈自由言語および最小計算量としての設計と開発が破綻し失敗に終わったことが明らかである
327
+ - GFMも`[0]([1]()(...[33]()...))`で32回の再帰的バックトラックを行い最悪計算量が32nであることが確認できる
327
328
  - これほど計算量が悪ければ入れ子数制限付き文脈依存言語と大差ない計算量であり素直に文脈依存言語として作り直したほうが遥かに拡張性と発展性が高く優れている
328
329
  - 計算資源は使うためにあるにもかかわらず言語と一致しない不適切な解析方法を使用してまでこの程度の計算資源を惜しんで人間に不便と不自由を強いて生産性を下げるのは本末転倒である
329
330
  - 計算機は人間の生産性に奉仕しなければならない
@@ -334,14 +335,14 @@ Markdownに本当に必要な仕様はSecuremarkのクラス化制約のよう
334
335
 
335
336
  ### CommonMarkの解析規則の問題点
336
337
 
337
- 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず早く閉じるよう解析しなければならない。このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。従って記号内側の非空白要件以外のflanking要件は不要である。
338
+ 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず可能な限り早く閉じるよう解析しなければならない。このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。従って記号内側の非空白要件以外のflanking要件は不要である。
338
339
 
339
340
  ```
340
341
  flanking*!?*
341
342
  -> flanking*!?*
342
343
  ```
343
344
 
344
- `**`が`*`に分解されるか一貫性がなく直感的使用が困難。
345
+ `**`が`*`に分解されるか一貫性がなく直感的使用が困難。強調を多重化する不適切で不確実な使用方法より開始記号の信頼性を損なわないことのほうが重要であるため分解しないべきである。
345
346
 
346
347
  ```
347
348
  **a*b*
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.289.2 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.289.4 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
2
2
  (function webpackUniversalModuleDefinition(root, factory) {
3
3
  if(typeof exports === 'object' && typeof module === 'object')
4
4
  module.exports = factory(require("Prism"), require("DOMPurify"));
@@ -2680,6 +2680,7 @@ __exportStar(__webpack_require__(4271), exports);
2680
2680
  __exportStar(__webpack_require__(7190), exports);
2681
2681
  __exportStar(__webpack_require__(549), exports);
2682
2682
  __exportStar(__webpack_require__(7723), exports);
2683
+ __exportStar(__webpack_require__(8606), exports);
2683
2684
  __exportStar(__webpack_require__(5781), exports);
2684
2685
  __exportStar(__webpack_require__(1638), exports);
2685
2686
  __exportStar(__webpack_require__(6572), exports);
@@ -2821,6 +2822,24 @@ exports.isBlank = isBlank;
2821
2822
 
2822
2823
  /***/ },
2823
2824
 
2825
+ /***/ 8606
2826
+ (__unused_webpack_module, exports, __webpack_require__) {
2827
+
2828
+ "use strict";
2829
+
2830
+
2831
+ Object.defineProperty(exports, "__esModule", ({
2832
+ value: true
2833
+ }));
2834
+ exports.clear = void 0;
2835
+ const fmap_1 = __webpack_require__(2705);
2836
+ function clear(parser) {
2837
+ return (0, fmap_1.fmap)(parser, () => []);
2838
+ }
2839
+ exports.clear = clear;
2840
+
2841
+ /***/ },
2842
+
2824
2843
  /***/ 6572
2825
2844
  (__unused_webpack_module, exports, __webpack_require__) {
2826
2845
 
@@ -2972,7 +2991,7 @@ function indent(opener, parser, separation = false) {
2972
2991
  if (typeof opener === 'function') return indent(/^([ \t])\1*/, opener, parser);
2973
2992
  return (0, bind_1.bind)((0, block_1.block)((0, match_1.match)(opener, (0, memoize_1.memoize)(([indent]) => (0, some_1.some)((0, line_1.line)((0, surround_1.open)(indent, ({
2974
2993
  source
2975
- }) => [[source], '']))), ([indent]) => indent.length * 2 + +(indent[0] === ' '), {}), false), separation), (lines, rest, context) => {
2994
+ }) => [[source], '']))), ([indent]) => indent.length * 2 + +(indent[0] === ' '), {})), separation), (lines, rest, context) => {
2976
2995
  // 影響する使用はないはず
2977
2996
  //const { backtracks } = context;
2978
2997
  //context.backtracks = {};
@@ -3022,7 +3041,7 @@ Object.defineProperty(exports, "__esModule", ({
3022
3041
  exports.match = void 0;
3023
3042
  const parser_1 = __webpack_require__(605);
3024
3043
  const combinator_1 = __webpack_require__(3484);
3025
- function match(pattern, f, cost = true) {
3044
+ function match(pattern, f, cost = false) {
3026
3045
  return input => {
3027
3046
  const {
3028
3047
  source,
@@ -3156,9 +3175,8 @@ exports.rewrite = rewrite;
3156
3175
  Object.defineProperty(exports, "__esModule", ({
3157
3176
  value: true
3158
3177
  }));
3159
- exports.clear = exports.close = exports.open = exports.surround = void 0;
3178
+ exports.close = exports.open = exports.surround = void 0;
3160
3179
  const parser_1 = __webpack_require__(605);
3161
- const fmap_1 = __webpack_require__(2705);
3162
3180
  const array_1 = __webpack_require__(6876);
3163
3181
  function surround(opener, parser, closer, optional = false, f, g, backtracks = [], backtrackstate = 0) {
3164
3182
  switch (typeof opener) {
@@ -3176,19 +3194,19 @@ function surround(opener, parser, closer, optional = false, f, g, backtracks = [
3176
3194
  source,
3177
3195
  context
3178
3196
  }) => {
3179
- const lmr_ = source;
3180
- if (lmr_ === '') return;
3197
+ const sme_ = source;
3198
+ if (sme_ === '') return;
3181
3199
  const {
3182
3200
  linebreak
3183
3201
  } = context;
3184
3202
  context.linebreak = undefined;
3185
- const res1 = opener({
3186
- source: lmr_,
3203
+ const resultS = opener({
3204
+ source: sme_,
3187
3205
  context
3188
3206
  });
3189
- if (res1 === undefined) return void revert(context, linebreak);
3190
- const rl = (0, parser_1.eval)(res1);
3191
- const mr_ = (0, parser_1.exec)(res1);
3207
+ if (resultS === undefined) return void revert(context, linebreak);
3208
+ const nodesS = (0, parser_1.eval)(resultS);
3209
+ const me_ = (0, parser_1.exec)(resultS);
3192
3210
  for (const backtrack of backtracks) {
3193
3211
  if (backtrack & 1) {
3194
3212
  const {
@@ -3196,9 +3214,9 @@ function surround(opener, parser, closer, optional = false, f, g, backtracks = [
3196
3214
  backtrack: state = 0,
3197
3215
  offset = 0
3198
3216
  } = context;
3199
- for (let i = 0; i < source.length - mr_.length; ++i) {
3217
+ for (let i = 0; i < source.length - me_.length; ++i) {
3200
3218
  if (source[i] !== source[0]) break;
3201
- const pos = source.length + offset - i - 1;
3219
+ const pos = source.length - i + offset - 1;
3202
3220
  if (!(pos in backtracks)) continue;
3203
3221
  const shift = backtrack >>> statesize & state >>> statesize ? state & (1 << statesize) - 1 : 0;
3204
3222
  if (backtracks[pos] & 1 << size(backtrack >>> statesize) + shift) return void revert(context, linebreak);
@@ -3209,34 +3227,35 @@ function surround(opener, parser, closer, optional = false, f, g, backtracks = [
3209
3227
  backtrack = 0
3210
3228
  } = context;
3211
3229
  context.backtrack = backtrack | backtrackstate;
3212
- const res2 = mr_ !== '' ? parser({
3213
- source: mr_,
3230
+ const resultM = me_ !== '' ? parser({
3231
+ source: me_,
3214
3232
  context
3215
3233
  }) : undefined;
3216
3234
  context.backtrack = backtrack;
3217
- const rm = (0, parser_1.eval)(res2);
3218
- const r_ = (0, parser_1.exec)(res2, mr_);
3219
- if (!rm && !optional) return void revert(context, linebreak);
3220
- const res3 = closer({
3221
- source: r_,
3235
+ const nodesM = (0, parser_1.eval)(resultM);
3236
+ const e_ = (0, parser_1.exec)(resultM, me_);
3237
+ if (!nodesM && !optional) return void revert(context, linebreak);
3238
+ const resultE = closer({
3239
+ source: e_,
3222
3240
  context
3223
3241
  });
3224
- const rr = (0, parser_1.eval)(res3);
3225
- const rest = (0, parser_1.exec)(res3, r_);
3226
- if (rest.length === lmr_.length) return void revert(context, linebreak);
3242
+ const nodesE = (0, parser_1.eval)(resultE);
3243
+ const rest = (0, parser_1.exec)(resultE, e_);
3244
+ if (rest.length === sme_.length) return void revert(context, linebreak);
3227
3245
  for (const backtrack of backtracks) {
3228
- if (backtrack & 2 && rr === undefined) {
3246
+ if (backtrack & 2 && nodesE === undefined) {
3229
3247
  const {
3230
3248
  backtracks = {},
3231
3249
  backtrack: state = 0,
3232
3250
  offset = 0
3233
3251
  } = context;
3252
+ const pos = source.length + offset - 1;
3234
3253
  const shift = backtrack >>> statesize & state >>> statesize ? state & (1 << statesize) - 1 : 0;
3235
- backtracks[source.length + offset - 1] |= 1 << size(backtrack >>> statesize) + shift;
3254
+ backtracks[pos] |= 1 << size(backtrack >>> statesize) + shift;
3236
3255
  }
3237
3256
  }
3238
- context.recent = [lmr_.slice(0, lmr_.length - mr_.length), mr_.slice(0, mr_.length - r_.length), r_.slice(0, r_.length - rest.length)];
3239
- const result = rr ? f ? f([rl, rm, rr], rest, context) : [(0, array_1.push)((0, array_1.unshift)(rl, rm ?? []), rr), rest] : g ? g([rl, rm, mr_], rest, context) : undefined;
3257
+ context.recent = [sme_.slice(0, sme_.length - me_.length), me_.slice(0, me_.length - e_.length), e_.slice(0, e_.length - rest.length)];
3258
+ const result = nodesE ? f ? f([nodesS, nodesM, nodesE], rest, context) : [(0, array_1.push)((0, array_1.unshift)(nodesS, nodesM ?? []), nodesE), rest] : g ? g([nodesS, nodesM, me_], rest, context) : undefined;
3240
3259
  if (result) {
3241
3260
  context.linebreak ??= linebreak;
3242
3261
  } else {
@@ -3246,6 +3265,14 @@ function surround(opener, parser, closer, optional = false, f, g, backtracks = [
3246
3265
  };
3247
3266
  }
3248
3267
  exports.surround = surround;
3268
+ function open(opener, parser, optional = false) {
3269
+ return surround(opener, parser, '', optional);
3270
+ }
3271
+ exports.open = open;
3272
+ function close(parser, closer, optional = false) {
3273
+ return surround('', parser, closer, optional);
3274
+ }
3275
+ exports.close = close;
3249
3276
  function match(pattern) {
3250
3277
  switch (typeof pattern) {
3251
3278
  case 'string':
@@ -3261,30 +3288,19 @@ function match(pattern) {
3261
3288
  };
3262
3289
  }
3263
3290
  }
3291
+ function revert(context, linebreak) {
3292
+ context.linebreak = linebreak;
3293
+ }
3264
3294
  function size(bits) {
3265
3295
  if (bits === 0) return 0;
3266
3296
  let p = 0;
3267
3297
  for (let s = 32 / 2; s > 0; s >>>= 1) {
3268
- if (bits >>> p + s === 0) continue;
3269
- p += s;
3298
+ const q = p + s;
3299
+ if (bits >>> q === 0) continue;
3300
+ p = q;
3270
3301
  }
3271
3302
  return p + 1;
3272
3303
  }
3273
- function revert(context, linebreak) {
3274
- context.linebreak = linebreak;
3275
- }
3276
- function open(opener, parser, optional = false) {
3277
- return surround(opener, parser, '', optional);
3278
- }
3279
- exports.open = open;
3280
- function close(parser, closer, optional = false) {
3281
- return surround('', parser, closer, optional);
3282
- }
3283
- exports.close = close;
3284
- function clear(parser) {
3285
- return (0, fmap_1.fmap)(parser, () => []);
3286
- }
3287
- exports.clear = clear;
3288
3304
 
3289
3305
  /***/ },
3290
3306
 
@@ -3551,37 +3567,62 @@ exports.constraint = constraint;
3551
3567
  /***/ },
3552
3568
 
3553
3569
  /***/ 5691
3554
- (__unused_webpack_module, exports, __webpack_require__) {
3570
+ (__unused_webpack_module, exports) {
3555
3571
 
3556
3572
  "use strict";
3557
3573
 
3558
3574
 
3559
- var _a;
3560
3575
  Object.defineProperty(exports, "__esModule", ({
3561
3576
  value: true
3562
3577
  }));
3563
3578
  exports.Delimiters = void 0;
3564
- const memoize_1 = __webpack_require__(6925);
3565
3579
  class Delimiters {
3566
3580
  constructor() {
3567
- this.registry = (0, memoize_1.memoize)(() => []);
3581
+ this.heap = {};
3582
+ this.map = new Map();
3568
3583
  this.delimiters = [];
3569
3584
  this.stack = [];
3570
3585
  this.states = [];
3571
3586
  }
3572
- static signature(pattern) {
3587
+ // 手間を惜しまなければ規定のパターンはすべて配列のインデクスに変換可能。
3588
+ static signature(pattern, linebreakable) {
3589
+ switch (typeof pattern) {
3590
+ case 'undefined':
3591
+ return +linebreakable;
3592
+ case 'string':
3593
+ if (pattern.length === 1) {
3594
+ const code = pattern.charCodeAt(0);
3595
+ // 使用中のパターンの8ビット目が空いてるのでひとまずこうしとく
3596
+ if ((code & 1 << 7) === 0) return code | +linebreakable << 7;
3597
+ }
3598
+ return `s:${pattern}:${+linebreakable}`;
3599
+ case 'object':
3600
+ return `r/${pattern.source}/${+linebreakable}`;
3601
+ }
3602
+ }
3603
+ static matcher(pattern) {
3573
3604
  switch (typeof pattern) {
3574
3605
  case 'undefined':
3575
- return `undefined`;
3606
+ return () => undefined;
3576
3607
  case 'string':
3577
- return `s:${pattern}`;
3608
+ return source => source.slice(0, pattern.length) === pattern || undefined;
3578
3609
  case 'object':
3579
- return `r/${pattern.source}/${pattern.flags}`;
3610
+ return source => pattern.test(source) || undefined;
3611
+ }
3612
+ }
3613
+ registry(signature) {
3614
+ if (typeof signature === 'number') {
3615
+ return this.heap[signature] ??= [];
3616
+ } else {
3617
+ const ds = this.map.get(signature);
3618
+ if (ds) return ds;
3619
+ const blank = [];
3620
+ this.map.set(signature, blank);
3621
+ return blank;
3580
3622
  }
3581
3623
  }
3582
3624
  push(delims) {
3583
3625
  const {
3584
- registry,
3585
3626
  delimiters,
3586
3627
  stack
3587
3628
  } = this;
@@ -3594,15 +3635,16 @@ class Delimiters {
3594
3635
  precedence,
3595
3636
  linebreakable
3596
3637
  } = delims[i];
3597
- const memory = registry(signature);
3638
+ const memory = this.registry(signature);
3598
3639
  const index = memory[0]?.index ?? delimiters.length;
3599
- if (memory.length === 0 || precedence > delimiters[index].precedence) {
3640
+ if (memory.length === 0) {
3600
3641
  const delimiter = {
3642
+ memory,
3601
3643
  index,
3602
3644
  signature,
3603
3645
  matcher,
3604
3646
  precedence,
3605
- linebreakable: linebreakable,
3647
+ linebreakable,
3606
3648
  state: true
3607
3649
  };
3608
3650
  delimiters[index] = delimiter;
@@ -3616,14 +3658,15 @@ class Delimiters {
3616
3658
  }
3617
3659
  pop(count) {
3618
3660
  const {
3619
- registry,
3620
3661
  delimiters,
3621
3662
  stack
3622
3663
  } = this;
3623
3664
  for (let i = 0; i < count; ++i) {
3624
3665
  const index = stack.pop();
3625
3666
  if (index === -1) continue;
3626
- const memory = registry(delimiters[index].signature);
3667
+ const {
3668
+ memory
3669
+ } = delimiters[index];
3627
3670
  if (memory.length === 1) {
3628
3671
  memory.pop();
3629
3672
  delimiters.pop();
@@ -3679,17 +3722,6 @@ class Delimiters {
3679
3722
  }
3680
3723
  }
3681
3724
  exports.Delimiters = Delimiters;
3682
- _a = Delimiters;
3683
- Delimiters.matcher = (0, memoize_1.memoize)(pattern => {
3684
- switch (typeof pattern) {
3685
- case 'undefined':
3686
- return () => undefined;
3687
- case 'string':
3688
- return source => source.slice(0, pattern.length) === pattern || undefined;
3689
- case 'object':
3690
- return source => pattern.test(source) || undefined;
3691
- }
3692
- }, _a.signature);
3693
3725
 
3694
3726
  /***/ },
3695
3727
 
@@ -3788,7 +3820,7 @@ function some(parser, end, delimiters = [], limit = -1) {
3788
3820
  if (typeof end === 'number') return some(parser, undefined, delimiters, end);
3789
3821
  const match = delimiter_1.Delimiters.matcher(end);
3790
3822
  const delims = delimiters.map(([delimiter, precedence, linebreakable = true]) => ({
3791
- signature: delimiter_1.Delimiters.signature(delimiter),
3823
+ signature: delimiter_1.Delimiters.signature(delimiter, linebreakable),
3792
3824
  matcher: delimiter_1.Delimiters.matcher(delimiter),
3793
3825
  precedence,
3794
3826
  linebreakable
@@ -4802,7 +4834,7 @@ const memoize_1 = __webpack_require__(6925);
4802
4834
  const dom_1 = __webpack_require__(394);
4803
4835
  exports.segment = (0, combinator_1.block)((0, combinator_1.match)(/^(~{3,})(?:figure[^\S\n])?(?=\[?\$)/, (0, memoize_1.memoize)(([, fence], closer = new RegExp(String.raw`^${fence}[^\S\n]*(?:$|\n)`)) => (0, combinator_1.close)((0, combinator_1.sequence)([source_1.contentline, (0, combinator_1.inits)([
4804
4836
  // All parsers which can include closing terms.
4805
- (0, combinator_1.union)([codeblock_1.segment_, mathblock_1.segment_, table_2.segment_, blockquote_1.segment, placeholder_1.segment_, (0, combinator_1.some)(source_1.contentline, closer)]), source_1.emptyline, (0, combinator_1.union)([source_1.emptyline, (0, combinator_1.some)(source_1.contentline, closer)])])]), closer), ([, fence]) => fence.length, {}), false));
4837
+ (0, combinator_1.union)([codeblock_1.segment_, mathblock_1.segment_, table_2.segment_, blockquote_1.segment, placeholder_1.segment_, (0, combinator_1.some)(source_1.contentline, closer)]), source_1.emptyline, (0, combinator_1.union)([source_1.emptyline, (0, combinator_1.some)(source_1.contentline, closer)])])]), closer), ([, fence]) => fence.length, {})));
4806
4838
  exports.figure = (0, combinator_1.block)((0, combinator_1.fallback)((0, combinator_1.rewrite)(exports.segment, (0, combinator_1.fmap)((0, combinator_1.convert)(source => source.slice(source.match(/^~+(?:\w+\s+)?/)[0].length, source.trimEnd().lastIndexOf('\n')), (0, combinator_1.sequence)([(0, combinator_1.line)((0, combinator_1.sequence)([label_1.label, (0, source_1.str)(/^(?=\s).*\n/)])), (0, combinator_1.inits)([(0, combinator_1.block)((0, combinator_1.union)([ulist_1.ulist, olist_1.olist, table_1.table, codeblock_1.codeblock, mathblock_1.mathblock, example_1.example, table_2.table, blockquote_1.blockquote, placeholder_1.placeholder, (0, combinator_1.line)(inline_1.media), (0, combinator_1.line)(inline_1.shortmedia)])), source_1.emptyline, (0, combinator_1.block)((0, visibility_1.visualize)((0, visibility_1.trimBlank)((0, combinator_1.some)(inline_1.inline))))])]), false), ([label, param, content, ...caption]) => [(0, dom_1.html)('figure', attributes(label.getAttribute('data-label'), param, content, caption), [(0, dom_1.html)('figcaption', [(0, dom_1.html)('span', {
4807
4839
  class: 'figindex'
4808
4840
  }), (0, dom_1.html)('span', {
@@ -5349,7 +5381,7 @@ const openers = {
5349
5381
  '(': /^\(([0-9]*|[a-z]*)(?![^)\n])\)?(?:-(?!-)[0-9]*)*(?:$|\s)/
5350
5382
  };
5351
5383
  exports.olist = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.validate)(new RegExp([/^([0-9]+|[a-z]+|[A-Z]+)(?:-[0-9]+)*\.(?=[^\S\n]|\n[^\S\n]*\S)/.source, /^\(([0-9]+|[a-z]+)\)(?:-[0-9]+)*(?=[^\S\n]|\n[^\S\n]*\S)/.source].join('|')), exports.olist_)));
5352
- exports.olist_ = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.union)([(0, combinator_1.match)(openers['.'], (0, memoize_1.memoize)(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), []), false), (0, combinator_1.match)(openers['('], (0, memoize_1.memoize)(ms => list(type(ms[1]), '('), ms => idx(ms[1]), []), false)])));
5384
+ exports.olist_ = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.union)([(0, combinator_1.match)(openers['.'], (0, memoize_1.memoize)(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), [])), (0, combinator_1.match)(openers['('], (0, memoize_1.memoize)(ms => list(type(ms[1]), '('), ms => idx(ms[1]), []))])));
5353
5385
  const list = (type, form) => (0, combinator_1.fmap)((0, combinator_1.some)((0, combinator_1.recursion)(3 /* Recursion.listitem */, (0, combinator_1.union)([(0, inline_1.indexee)((0, combinator_1.fmap)((0, combinator_1.fallback)((0, combinator_1.inits)([(0, combinator_1.line)((0, combinator_1.open)(heads[form], (0, combinator_1.subsequence)([ulist_1.checkbox, (0, combinator_1.trim)((0, visibility_1.visualize)((0, util_1.lineable)((0, visibility_1.trimBlank)((0, combinator_1.some)((0, combinator_1.union)([inline_1.indexer, inline_1.inline]))))))]), true)), (0, combinator_1.indent)((0, combinator_1.union)([ulist_1.ulist_, exports.olist_, ilist_1.ilist_]))]), ilist_1.ilistitem), ns => [(0, dom_1.html)('li', {
5354
5386
  'data-index': (0, inline_1.dataindex)(ns),
5355
5387
  'data-marker': ns.shift() || undefined
@@ -5357,10 +5389,10 @@ const list = (type, form) => (0, combinator_1.fmap)((0, combinator_1.some)((0, c
5357
5389
  const heads = {
5358
5390
  '.': (0, combinator_1.focus)(openers['.'], ({
5359
5391
  source
5360
- }) => [[source.trimEnd().split('.', 1)[0] + '.'], ''], false),
5392
+ }) => [[source.trimEnd().split('.', 1)[0] + '.'], '']),
5361
5393
  '(': (0, combinator_1.focus)(openers['('], ({
5362
5394
  source
5363
- }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], ''], false)
5395
+ }) => [[source.trimEnd().replace(/^\($/, '(1)').replace(/^\((\w+)$/, '($1)')], ''])
5364
5396
  };
5365
5397
  function idx(value) {
5366
5398
  switch (value) {
@@ -5533,15 +5565,15 @@ exports.cite = (0, combinator_1.line)((0, combinator_1.fmap)((0, combinator_1.va
5533
5565
  source
5534
5566
  }) => [[(0, dom_1.html)('a', {
5535
5567
  class: 'anchor'
5536
- }, source)], ''], false), (0, combinator_1.focus)(/^>>https?:\/\/\S+(?=\s*$)/, ({
5568
+ }, source)], '']), (0, combinator_1.focus)(/^>>https?:\/\/\S+(?=\s*$)/, ({
5537
5569
  source
5538
5570
  }) => [[(0, dom_1.html)('a', {
5539
5571
  class: 'anchor',
5540
5572
  href: source.slice(2).trimEnd(),
5541
5573
  target: '_blank'
5542
- }, source)], ''], false), (0, combinator_1.focus)(/^>>.+(?=\s*$)/, ({
5574
+ }, source)], '']), (0, combinator_1.focus)(/^>>.+(?=\s*$)/, ({
5543
5575
  source
5544
- }) => [[source], ''], false)]))), ([quotes, node]) => [(0, dom_1.html)('span', typeof node === 'object' ? {
5576
+ }) => [[source], ''])]))), ([quotes, node]) => [(0, dom_1.html)('span', typeof node === 'object' ? {
5545
5577
  class: 'cite'
5546
5578
  } : {
5547
5579
  class: 'cite invalid',
@@ -5676,7 +5708,7 @@ exports.checkbox = (0, combinator_1.focus)(/^\[[xX ]\](?=$|\s)/, ({
5676
5708
  source
5677
5709
  }) => [[(0, dom_1.html)('span', {
5678
5710
  class: 'checkbox'
5679
- }, source[1].trimStart() ? '☑' : '☐')], ''], false);
5711
+ }, source[1].trimStart() ? '☑' : '☐')], '']);
5680
5712
  function fillFirstLine(ns) {
5681
5713
  return ns.length === 1 && typeof ns[0] === 'object' && ['UL', 'OL'].includes(ns[0].tagName) ? (0, array_1.unshift)([(0, dom_1.html)('br')], ns) : ns;
5682
5714
  }
@@ -5788,11 +5820,14 @@ const htmlentity_1 = __webpack_require__(470);
5788
5820
  const bracket_1 = __webpack_require__(4526);
5789
5821
  const autolink_1 = __webpack_require__(8072);
5790
5822
  const source_1 = __webpack_require__(8745);
5791
- exports.inline = (0, combinator_1.lazy)(() => (0, combinator_1.union)([input => {
5823
+ exports.inline = (0, combinator_1.lazy)(() => (0, combinator_1.verify)((0, combinator_1.union)([input => {
5792
5824
  const {
5793
- source
5825
+ source,
5826
+ context
5794
5827
  } = input;
5795
5828
  if (source === '') return;
5829
+ context.depth ??= 0;
5830
+ ++context.depth;
5796
5831
  switch (source.slice(0, 2)) {
5797
5832
  case '((':
5798
5833
  return (0, annotation_1.annotation)(input);
@@ -5835,7 +5870,19 @@ exports.inline = (0, combinator_1.lazy)(() => (0, combinator_1.union)([input =>
5835
5870
  case '&':
5836
5871
  return (0, htmlentity_1.htmlentity)(input);
5837
5872
  }
5838
- }, bracket_1.bracket, autolink_1.autolink, source_1.text]));
5873
+ }, bracket_1.bracket, autolink_1.autolink, source_1.text]), (_, rest, context) => {
5874
+ --context.depth;
5875
+ // ヒープを効率的に削除可能な場合は削除する。
5876
+ // ヒープサイズは括弧類など特定の構文が完成しなかった場合にしか増加しないため
5877
+ // ブロックごとに平均数ノード以下となることから削除せずとも平均的にはあまり影響はない。
5878
+ //if (context.depth === 0) {
5879
+ // const { backtracks } = context;
5880
+ // while (backtracks.peek()?.key! > rest.length) {
5881
+ // backtracks.extract();
5882
+ // }
5883
+ //}
5884
+ return true;
5885
+ }));
5839
5886
  var indexee_1 = __webpack_require__(7610);
5840
5887
  Object.defineProperty(exports, "indexee", ({
5841
5888
  enumerable: true,
@@ -6054,7 +6101,7 @@ const link_1 = __webpack_require__(3628);
6054
6101
  const hashtag_1 = __webpack_require__(5764);
6055
6102
  const source_1 = __webpack_require__(8745);
6056
6103
  const dom_1 = __webpack_require__(394);
6057
- exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji)/u.source.replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, false, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, link_1.unsafelink, false), ([el]) => [(0, dom_1.define)(el, {
6104
+ exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp([/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji)/u.source].join('').replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, false, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, link_1.unsafelink, false), ([el]) => [(0, dom_1.define)(el, {
6058
6105
  class: 'hashnum',
6059
6106
  href: null
6060
6107
  })]))), ({
@@ -6161,7 +6208,7 @@ exports.code = (0, combinator_1.match)(/^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/, ([who
6161
6208
  source
6162
6209
  }) => [[(0, dom_1.html)('code', {
6163
6210
  'data-src': whole
6164
- }, format(body))], source.slice(whole.length)]);
6211
+ }, format(body))], source.slice(whole.length)], true);
6165
6212
  function format(text) {
6166
6213
  return `${text[0]}${text.at(-1)}` === ' ' && text.trimStart() ? text.slice(1, -1) : text;
6167
6214
  }
@@ -6231,7 +6278,8 @@ const dom_1 = __webpack_require__(394);
6231
6278
  const array_1 = __webpack_require__(6876);
6232
6279
  const substrong = (0, combinator_1.lazy)(() => (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('**')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), (0, combinator_1.union)([exports.emstrong, strong_1.strong]))])));
6233
6280
  const subemphasis = (0, combinator_1.lazy)(() => (0, combinator_1.some)((0, combinator_1.union)([strong_1.strong, (0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('*')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), (0, combinator_1.union)([exports.emstrong, strong_1.strong, emphasis_1.emphasis]))])));
6234
- // 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず早く閉じるよう解析しなければならない。
6281
+ // 開閉が明示的でない構文は開閉の不明確な記号による再帰的適用を行わず
6282
+ // 可能な限り早く閉じるよう解析しなければならない。
6235
6283
  // このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
6236
6284
  exports.emstrong = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('***', (0, combinator_1.precedence)(0, (0, util_1.repeat)('***', (0, combinator_1.surround)('', (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, visibility_1.tightStart)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('*')), (0, combinator_1.open)((0, combinator_1.some)(inline_1.inline, '*'), inline_1.inline)])))), (0, source_1.str)(/^\*{1,3}/), false, ([, bs, cs], rest, context) => {
6237
6285
  switch (cs[0]) {
@@ -6504,6 +6552,11 @@ exports.indexer = void 0;
6504
6552
  const combinator_1 = __webpack_require__(3484);
6505
6553
  const index_1 = __webpack_require__(4688);
6506
6554
  const dom_1 = __webpack_require__(394);
6555
+ // インデクスの重複解消は不要な重複を削除するのが最もよい。
6556
+ // 複合生成インデクスは参照と同期させることが困難であり
6557
+ // 複合生成インデクスを手動で同期させるより最初から重複のない
6558
+ // テキストまたはインデクスを付けて同期が必要な機会を減らすのが
6559
+ // 継続的編集において最も簡便となる。
6507
6560
  exports.indexer = (0, combinator_1.surround)(/^\s+\[(?=\|\S)/, (0, combinator_1.union)([index_1.signature, (0, combinator_1.focus)(/^\|(?=\])/, () => [[(0, dom_1.html)('span', {
6508
6561
  class: 'indexer',
6509
6562
  'data-index': ''
@@ -8134,15 +8187,15 @@ function trimBlankEnd(parser) {
8134
8187
  return (0, combinator_1.fmap)(parser, trimBlankNodeEnd);
8135
8188
  }
8136
8189
  exports.trimBlankEnd = trimBlankEnd;
8137
- //export function trimBlankNode<T extends HTMLElement | string>(nodes: T[]): T[] {
8190
+ //export function trimBlankNode<N extends HTMLElement | string>(nodes: N[]): N[] {
8138
8191
  // return trimBlankNodeStart(trimBlankNodeEnd(nodes));
8139
8192
  //}
8140
- //function trimBlankNodeStart<T extends HTMLElement | string>(nodes: T[]): T[] {
8193
+ //function trimBlankNodeStart<N extends HTMLElement | string>(nodes: N[]): N[] {
8141
8194
  // for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
8142
8195
  // if (typeof node === 'string') {
8143
8196
  // const pos = node.trimStart().length;
8144
8197
  // if (pos > 0) {
8145
- // nodes[0] = node.slice(-pos) as T;
8198
+ // nodes[0] = node.slice(-pos) as N;
8146
8199
  // break;
8147
8200
  // }
8148
8201
  // }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.289.2",
3
+ "version": "0.289.4",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -2,7 +2,7 @@ import { Parser, exec } from '../../data/parser';
2
2
  import { firstline, isBlank } from './line';
3
3
 
4
4
  export function block<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
5
- export function block<T>(parser: Parser<T>, separation = true): Parser<T> {
5
+ export function block<N>(parser: Parser<N>, separation = true): Parser<N> {
6
6
  assert(parser);
7
7
  return input => {
8
8
  const { source } = input;