securemark 0.294.1 → 0.294.3

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.294.3
4
+
5
+ - Refactoring.
6
+
7
+ ## 0.294.2
8
+
9
+ - Refactoring.
10
+
3
11
  ## 0.294.1
4
12
 
5
13
  - Refactoring.
package/design.md CHANGED
@@ -272,37 +272,39 @@ HTMLEntity構文はエンティティ追加時の互換性確保のため不正
272
272
 
273
273
  ## 備考
274
274
 
275
- ### 実行速度
276
-
277
- ブラウザの以下の欠陥および低速性により速度低下が生じており今後のブラウザの改善が求められる。
278
-
279
- - IntersectionObserverが描画完了前に交差を計算するためすべて同時発火する欠陥
280
- - CSSカウンター(オーダードリスト)
281
-
282
275
  ### AST(CST)
283
276
 
284
277
  Securemarkはブロック単位の差分更新によりリアルタイムレンダリングを高速化しているがCSTを生成する場合行の追加削除により以降すべてのノードの位置情報を更新する必要が生じ許容範囲を超える遅延が生じる可能性がある。このためこの最適化を行う必要があるが行位置の更新のみであればこれのみを行う処理経路の追加は容易であろう。Markdownでも行数は数万行以下と想定できることから行数を全ノードに直接記述せず行ノードの行数を参照する実装に変更すれば更新対象を数万以下の行ノードだけに局限できる。これらは数百行以下に規律されるソースコードにおいては生じない問題であり数十万文字を許容しなければならないマークアップ言語に新出の問題であるため新規にCSTの設計および運用を再考する必要がある。
285
278
 
286
279
  またBlockquote構文を内包テキストまでシンタックスハイライトする場合、構文(>)と内包テキストが垂直に分割されるため構文と1対1で対応する単純なノードではCSTを表現できず構文の断片を表現するノードが必要となり、さらにこれをエディタ上でシンタックスハイライトするためのHTMLに変換する際に各行においてこの断片のCSTを断片のHTMLに正しく変換および表示しなければならずかなり煩雑な作業を要する。
287
280
 
288
- CodeMirrorが素では速いがVimModeでは数万文字程度でも耐え難く非常に遅いので高速な専用エディタの開発が必要。当面は数万文字以上のテキストではTextareaに切り替えることとする。
289
-
290
281
  ### バックトラック
291
282
 
292
- 同符号異文脈異構造構文のバックトラックは避けられない。
283
+ バックトラック後にしか到達不能な構文はバックトラックなしに定義不可能。従って他の構文をバックトラック後にしか到達不能にする構文をバックトラックを発生させず追加することも不可能。共通構造を利用してバックトラックを回避する場合も事前に共通構造解析が必要なため複数回解析が必要であり計算量的におよび本質的にバックトラックしているのと変わりない。
293
284
 
294
- - `{ {{A` -> `{{A` -> `{A` -> `A`
295
285
  - `[[A` -> `[A` -> `A`
286
+ - `{{A` -> `{A` -> `A`
296
287
 
297
- 本質的に括弧として解釈できる符号は予言的構文と見なしてバックトラックを避けられるがそうでない符号は避けられない。
288
+ 本質的に括弧として解釈できる記号は開放可能構文と見なして予言的構文として定義することでバックトラックを回避できるがそうでない記号は回避できない。
298
289
 
299
290
  - `"A` -> `(A`
300
291
  - `` `A `` -> `(A`
301
- - `$100` <> `(100`
292
+ - `$100` >< `(100`
293
+
294
+ CommonMarkは初手設計ミスったせいで構文一つで最悪計算量32nを叩き出すクソ言語になり拡張性がなく終わってる。
295
+
296
+ SecuremarkのAnnotation構文に典型的であるように文脈を変更する構文の中にその文脈に依存し変更される他の構文が存在する場合文脈の相違から解析結果を再利用不能(`αAβ | αA'B`)なバックトラックが生じる。またこの結果再帰的バックトラックが生じる可能性があり再帰的バックトラックは一般的にメモ化により解決されるがCommonMarkは最小計算量と実行性能を追及するためメモ化を廃止していることからメモ化により性能を低下させてまで文脈依存構文の問題を解決するつもりはないと思われる(すなわちCommonMarkは機械を至上とし人間に制約を課す低水準の言語であり人間の需要を至上とするSecuremarkとは対極に位置する)。従って再帰的バックトラックなしで解析可能な構文と最小計算量に制約される現在のCommonMarkは基本的に再帰的バックトラックが生じるものである文脈依存構文を追加できないという拡張性の欠陥が存在する(CommonMarkは`~~a~~`のような文脈自由構文は容易に追加できるが文脈依存構文はLink構文で発生している再帰的バックトラックまたはその修正のために激増した計算量を組み合わせ爆発により指数関数的に致命的に悪化させる可能性が高いため基本的に追加できない)。CommonMarkの仕様策定者が構文の拡張に(名称を維持するか否かにかかわらず)不自然なまでに消極的または進展がないのは正当な理由や単なる怠慢からでなく構文追加可否を議論すれば文脈依存構文を基本的に追加できずCommonMarkの構文解析戦略の失敗が明らかになることが避けられないためおよび最小計算量を放棄して現在の高い実行性能を低下させたくないためであり陳腐な自尊心を守るためにこのような拡張性の欠陥を秘匿しCommonMarkとその仕様策定者である自分の評価が下がらないよう画策しているからである。でなければこの拡張性の欠陥を何年も隠さず速やかに公表して助力を求めていなければならず不都合な事実を隠し陳腐な自尊心を守るために全Markdown利用者および開発者を不必要に足止めした罪は重い。
297
+ CommonMarkは小さく単純であるうちだけ破綻せずに正しくいられる象牙の塔であり仕様策定者はこの正しさを失わず正しいままでいたいがために象牙の塔に引きこもり小さな表面的正しさに固執し続けているに過ぎない。しかしCommonMarkは実際にはすでに致命的に破綻しており本来文脈依存言語であるMarkdownを文脈自由言語として解析しようとして失敗したことで指数関数計算量になっており実際のところCommonMarkは最初から最後までずっと壊れている。CommonMarkは可能な限り文脈自由言語として設計することでバックトラックなく最小計算量で解析しようとしているが実際には文脈依存言語であるMarkdownから文脈依存構文を文脈自由構文に変換して再帰的バックトラックを除去することに失敗しているためCommonMarkは最初の数年間は再帰的バックトラックに気づかず最悪計算量が指数関数計算量になっており修正後も依然として最悪計算量が当初の想定の1nから32nへと劇的に悪化している(より正確にはCommonMarkは少なくともCode構文とHTML構文により+2nされるが説明の簡略化のため省略しこれら以外のInline構文のみを比較する)。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は仕様策定が停滞し未だに1nの最小計算量に固執しているがそんなものはCommonMarkの破綻と失敗が露呈するを恐れて隠蔽しまたとっくの昔に破綻してい最小計算量のための設計に未練がましく執着しているだけである。32nという最悪計算量は適切に設計し計算量が抑制された文脈依存言語の最悪計算量より悪く最初から文脈依存言語として適切に設計するほうが自然で破綻がなく拡張性を確保できていた。たった一つの構文だけで最悪計算量が32nも増加するクソ言語に拡張性がないことは明らかである。さらにSecuremarkは再帰的バックトラックを予め対策して設計されているため文脈依存構文数が増えても最悪計算量はたかだか構文数に比例して線形にしか増加しないのに対してCommonMarkは再帰的バックトラックに根本的対策を取れないため文脈依存構文数が増えると最悪計算量が2^mで指数関数的に致命的に爆発する(より正確には通常の文脈依存構文を基準にすれば32\*2^m、リンク構文を基準にすれば32^m)。または計算量を組み合わせ爆発させないために文脈依存構文の入れ子使用を制限する、存在自体が破綻と失敗の宣言に等しい制限が必要になる(リンク構文とイメージ構文を入れ子にできているのはイメージ構文を入れる角括弧部分が文脈自由部分だからであり文脈依存部分である丸括弧部分は入れ子にできていない)。文脈依存構文を強引に文脈自由構文として解析しようとして失敗したために最悪計算量が当初の想定の1nから32nに劇的に悪化し結局文脈依存言語の妥当な最悪計算量より悪い水準に落ちていることおよび文脈依存構文を追加すると最悪計算量が指数関数的に悪化することから文脈自由化に失敗したCommonMarkの破綻と失敗は明らかでありCommonMarkは文脈自由構文に固執せず最初から多少の文脈依存構文を許容するよう設計しなければならなかった。実際には文脈依存言語であるにもかかわらず文脈自由言語の範囲でしか構文解析できなければ構文解析が破綻し構文が増えるほど破綻が拡大することは自明でありすでに破綻済みで失敗済みのCommonMarkに未来などない。文脈依存言語であるMarkdownに対して文脈自由構文解析器として作られたCommonMarkは最初から開発戦略と技術選択を間違え失敗しており最初から破綻していた。CommonMarkが文脈依存言語を文脈自由言語として最小計算量で解析するために使用した手法は邪道の小手先の技術に過ぎずCommonMarkは邪道を選んだ挙句失敗に終わったのである。文脈依存言語を文脈依存言語のまま解析する正道を選んだSecuremarkが正着し文脈依存言語を文脈自由言語に歪める邪道を選んだCommonMarkが失着に終わったのは当然の帰結であり最初の解析戦略時選択の時点で決まっていたことである。文脈依存言語であるMarkdownを文脈自由言語として解析しようとした結果行き詰ったCommonMarkとその閉塞に技術的合理性はなくCommonMarkは最初からの失敗していた過去の遺物であり廃棄すべき負債である。CommonMarkに動きがないのはすでに破綻しており死んでいることに気付かれないように死んでいるからに過ぎない。このようにCommonMarkは完全に破綻し失敗に終わっているためCommonMarkの拡張や発展を期待しても無駄であり既存の文脈依存構文の最悪計算量を健全化し新たに文脈依存構文を追加可能な拡張性の高いMarkdown仕様は新しく作り直さなければ作れない。しかしCommonMarkの仕様策定者は独自の新しい仕様においても依然としてMarkdownを文脈自由化して文脈自由言語として設計しているため文脈依存構文を追加すると指数関数計算量になり救いようがない。しかもその構文と仕様は機械可読性を至上としているため非常に醜く人間が書くことも読むことも困難で人間に対する実用性が欠如している。
298
+ Securemarkはスーパークラス構文が解析に失敗した入力をサブクラス構文で解析しないことにより再帰的バックトラックを回避する(解析中の構文自身はスーパークラスとサブクラスの両方に含まれるものとする)。スーパークラス構文A(`αAβ`)の解析が失敗すればサブクラス構文B(`α'Aβ'`)の解析も失敗することは自明であり試みるまでもなく解析を省略できる。これは二つの構文の文法が生成する各言語空間がスーパーセットとサブセットの関係にあるならスーパーセットの言語空間の外にある文字列はサブセットの言語空間の内に入る余地がないことからも自明である(この解析法は事前解析によっても可能だが文脈内外のオートリンクURLの括弧解析などを高速に行うこともバックトラックなしで解析することも困難であり本解析に近い解析コストを要するためMarkdownをこの事前解析により高速化することは難しい)。メモ化は解析結果を再利用することで結果的に副次的効果としてバックトラックを回避しているものでありメモ化はバックトラックを回避するだけなら過剰であり不要である(メモ化はバックトラックがなければ使用されないためバックトラックの少ないほとんどの入力に対してはほとんど使用されず無駄であり空間計算量を不必要に常時数倍以上増加させることから構文解析において必須にも標準にもできない。バックトラック回避のためにメモ化するとバックトラックなしで解析可能な場合も常に不必要に空間計算量が増加し平均計算量が有意に悪化することがメモ化の最大の欠点である(この問題は解析失敗時のみメモ化すれば解消可能のはずだが基本的にはこうなる)。特に文脈自由構文解析器におけるメモ化の使用は完全に無駄でありバグである。バックトラックが発生しないか他の方法で解決されるならば使用されず破棄されるメモ化は無駄である。複数の文脈で解析結果が同一である文脈独立性のある構文ならメモ化した解析結果を異なる文脈で再利用でき有用だがそのような構文は基本的に少数であるため効果が限定的であり最悪計算量は改善されない)。この解析法により、CommonMarkならば最悪計算量32n\*2^5=1024nを下らない拡張Markdown言語をSecuremarkはメモ化なしで5nの最悪時間計算量で解析している。すなわち言語差を考慮せず直接比較してさえ単純なCommonMarkの最悪計算量32nに対して複雑なSecuremarkは5nに過ぎずSecuremarkはCommonMarkより最悪計算量が非常に小さい。またSecuremarkはメモ化を行っていないため空間計算量も小さい。時間計算量と空間計算量を合わせてO(n, n)と表記すると文脈依存言語の通常の最悪計算量はO(n^2, n)、メモ化により効率化できた場合もO(nm, nm)(S(m)>=m byte)(解析結果の位置や構文木等を記録するため空間使用量S(m)>はm byte以上となり数値一つやポインタ一つだけでも8byteを消費する)に過ぎないが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は文脈依存構文解析器とし設計されているためリンク構文内にリンク構文が定義されておらず外側のみリンクとして解析し他のMarkdown構文解析器もこのように解析しなければならない(ここでCommonMarkはリンク構文`[]()`のバックトラック除去ひいては文脈自由化に角括弧`[]`に対しては成功したが丸括弧`()`に対しては失敗したことで最悪計算量が指数関数計算量ないし32nに悪化した。リンク構文を本来通り文脈依存構文として解析すればリンク構文の最悪計算量が2nとなり角括弧部分に限っては1nから2nに悪化するが丸括弧部分は32nから2nに著しく改善する。ここがCommonMarkの根本的な欠陥と失敗が最も明瞭に表出している部分である)。この問題はイメージ構文においてさらに顕著でありリンク構文と同じ問題が正当な表現`![![]()]()`で発生しさらにHTMLのaltはプレーンテキストとして表示されるためMarkdownのaltもプレーンテキストとしてそのまま表示されなければならないにもかかわらず文脈を一致させ再帰的バックトラックを防ぐためにMarkdown文字列として解析しaltに`*a*`と書かれたものを`a`に変換して表示する。無論新しい文脈依存構文を追加する場合も同じ制約が永遠についてまわり構文内文字列をMarkdownとして解析する文脈依存構文においてこの制約を破ると最悪計算量が2^m、より正確には32\*2^mないし32^mで指数関数的に増加する。すなわちCommonMarkは文脈依存構文を追加すると最悪計算量が32\*2^mないし32^mで指数関数的に悪化するという拡張性の致命的欠陥が存在する。こんな最悪計算量が32^mで組み合わせ爆発する欠陥言語を拡張できるわけがないことはもはや明白である。また大多数のプログラミング言語を見ても明らかなように文脈依存言語は構文内で使用可能な構文を定義しその他の構文は使用できず例外処理するのが通常でありCommonMarkのように本来使用不能な構文を外側の構文を無効化して使用可能に変える異常な言語はほとんどの人間はCommonMark以外に見たことがないだろう。ほぼすべての人間において他のすべての言語が同じ一貫した規則を持ち同じ規則で統一的に使用できるのに対してCommonMarkだけが他と異なる異常な挙動をして使用者に認知的負荷をかけるのである。破壊的変更を避けるため旧構文だけ従来通り文脈自由構文として解析し新構文を文脈依存構文として解析すればキメラ的な非常に不自然かつ歪で一貫性のない解析規則によりCommonMarkという一つの言語の中だけでもユーザーを混乱させるものとなり旧構文で使用した苦肉の策を不必要に新構文でも使用して一貫させれば文脈依存言語なのに文脈自由言語の苦肉の策の解析規則で解析されるこれもまたキメラ的な非常に不自然かつ理論的に設計ミスが明白で実用的にも認知的負荷の高い言語となる。正しく設計された言語と解析器にはありえないこのような解析規則の根本的不整合はその不整合の存在そのものが言語と解析器が誤った設計による失敗作であることを証明しており不整合が存在する限りその不整合により自らが失敗作であることを宣言し続けることになる。CommonMarkとはそういう失敗した言語と解析器なのである。このようにCommonMarkは新構文だけまたは解析器だけ文脈依存に変更しても理論的齟齬が解析結果と使用感の不自然さと違和感に明白に表れるためCommonMarkが失敗した言語である事実は到底隠し切れるものではない。Markdownはもはや負債以外の何物でもないCommonMarkの異常な解析規則を捨てて素直な文脈依存構文言語として新しい仕様を作り直すのが賢明である。
299
+ 文脈依存言語をバックトラックなしで解析可能な方法は2つある。一つは解釈を予言的に決定する方法、もう一つは予備的または中間的その他事前の解析結果に対する解釈を事後に決定する方法である。前者は構文を峻別するための制約が強く後者は共通構造の適切な設計が必要でありいずれもバックトラックを防ぐために未終端の構文も受理する必要がある。しかし予言的解析は類似の異文脈構文へのバックトラックが不可能になるため共通構造についての事前解析結果のような追加情報を利用できるのでなければ類似の異文脈構文の定義が不可能になる。また共通構造をバックトラックなしで解析するにはそのための言語設計が必要であることに変わりなく任意の文字列を使用可能である必要があるURL、コード、数式などに含まれる任意の文字列が他の構文と文脈で解釈される際に共通構造を破壊するためこれらを絶対的に分離する基本構造を共通構造に定義しなければ構造的共通化は不可能であり分離構造は終端不要な開放可能構文としての予言的構文でなければバックトラックが不可避となる。共通構造の事前解析は一見すると最悪計算量を2nに削減できるように見えるが実際には共通構造をバックトラックなしで解析することが元の言語より容易にはなっても依然として難しくバックトラックなしで解析できたとしてもすべての解析ひいては平均計算量が+1nされるため平均的にはむしろ好ましくない方法であり仮に最悪計算量32nを事前解析により2nに抑えられたとしても平均計算量が1nならば平均計算量が2nに倍加し実行性能が半減および実行コストが倍加するため明らかに事前解析を行うべきでない。メモ化が空間計算量を代償に悪化させる最悪時間計算量線形化手法であるのに対して事前解析は平均時間計算量を代償に悪化させる最悪時間計算量線形化手法でありマーキング法は何も代償に悪化させない最悪時間計算量線形化手法である。なおマーキング法を使用するSecuremarkは本来の構文解析と共通構造解析を統合し同時解析することで計算量の増加なく共通構造解析を行い共通構造を利用して最悪時間計算量を指数関数計算量から5nへ削減している。
300
+
301
+ ### 実行性能
302
+
303
+ 構文数と使用記号数が大きく異なるため単純な比較はできないがSecuremarkはパーサーコンビネータの多用によるオーバーヘッドが大きく、パーサーを展開しオーバーヘッドを削減すればCommonMarkとの性能差は数倍以下に収まると考えられる。CommonMarkの問題の解決を試みたdjotとの性能差はすでに2倍程度であり本質的な問題は存在しない。
304
+
305
+ そのほかブラウザの以下の欠陥および低速性により速度低下が生じており今後のブラウザの改善が求められる。
302
306
 
303
- SecuremarkのAnnotation構文に典型的であるように文脈を変更する構文の中にその文脈に依存し変更される他の構文が存在する場合文脈の相違から解析結果を再利用不能(`αAβ | αA'B`)なバックトラックが生じる。またこの結果再帰的バックトラックが生じる可能性があり再帰的バックトラックは一般的にメモ化により解決されるがCommonMarkは最小計算量と実行性能を追及するためメモ化を廃止していることからメモ化により性能を低下させてまで文脈依存構文の問題を解決するつもりはないと思われる(すなわちCommonMarkは機械を至上とし人間に制約を課す低水準の言語であり人間の需要を至上とするSecuremarkとは対極に位置する)。従って現在の再帰的バックトラックなしで解析可能な構文と最小計算量に制約されるCommonMarkにはこれ以上再帰的バックトラックが生じる可能性を増加させて文脈依存構文を追加できないという拡張性の欠陥が存在する(CommonMarkは`~~a~~`のような文脈自由構文は容易に追加できるがこうしたマージンを失えばもはや後はなく文脈依存構文を追加できないという事実に直面する)。CommonMarkの仕様策定者が構文の拡張に(名称を維持するか否かにかかわらず)不自然なまでに消極的または進展がないのは正当な理由や怠慢からでなく文脈依存構文を追加するにつれて構文解析戦略の失敗が明白になっていくためおよび最小計算量を放棄して現在の高い実行性能を低下させたくないためであり陳腐な自尊心を守るためにこのような拡張性の欠陥を秘匿しCommonMarkとその仕様策定者である自分の評価が下がらないよう画策しているからである。でなければこの拡張性の欠陥を何年も隠さず速やかに公表して助力を求めていなければならず不都合な事実を隠し陳腐な自尊心を守るために全Markdown利用者および開発者を不必要に足止めした罪は重い。
304
- CommonMarkは小さく単純であるがゆえに正しくいられる象牙の塔であり仕様策定者はこの正しさを失わず正しいままでいたいがために象牙の塔に引きこもり小さな表面的完全性に固執し続けているに過ぎない。しかしCommonMarkは実際にはまったく完全ではなく本来文脈依存言語であるMarkdownを文脈自由言語として解析しているため破綻している部分があり実際のところCommonMarkは最初から最後までずっと壊れている。CommonMarkはバックトラックなく最小計算量で解析するために文脈自由言語として設計されているが実際には文脈依存言語であるMarkdownから文脈依存構文を文脈自由構文に変換して除去することに失敗しているためCommonMarkは最初の数年間は再帰的バックトラックに気づかず最悪計算量が指数関数計算量になっており修正後は最悪計算量が当初の想定の2nから32nへと劇的に悪化している(より正確にはCommonMarkはCode構文とHTML構文により+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で指数関数的に致命的に激増する(より正確には通常の文脈依存構文を基準にすれば32\*2^m、リンク構文を基準にすれば32^m)。または計算量が組み合わせ爆発しないよう文脈依存構文の入れ子を制限する、存在自体が欠陥と失敗の宣言に等しい制限が必要になる(リンク構文とイメージ構文を入れ子にできるのはaltを不適切に外部と同じ文脈のMarkdownとして解析することで文脈自由化しているからであり本来文脈依存構文は異なる文脈を持つためこのようなことはできずイメージ構文もこの本来できない文脈自由化を行ったためにCommomMarkではaltがプレーンテキストでなくMarkdownとして不適切に解析されている)。文脈依存構文を強引に文脈自由構文として解析したために最悪計算量が当初の想定の2nから32nに劇的に悪化し結局文脈依存言語の妥当な最悪計算量の水準に落ちていることおよび文脈依存構文を追加すると最悪計算量が指数関数的に悪化することから文脈自由言語として設計されたCommonMarkの破綻と失敗は明らかでありCommonMarkは文脈自由構文に固執せず最初から多少の文脈依存構文を許容するよう設計しなければならなかった。実際には文脈依存言語であるにもかかわらず文脈自由言語としてしか構文解析できなければ構文解析が破綻し構文が増えるほど破綻が拡大することは自明でありすでに破綻済みで失敗済みのCommonMarkに未来などない。文脈依存言語であるMarkdownに対して文脈自由構文解析器として作られたCommonMarkは最初から技術選択を間違え失敗しており最初から破綻していた。CommonMarkが文脈依存言語を文脈自由言語として最小計算量で解析するために使用した手法は邪道の小手先の技術に過ぎずCommonMarkは邪道を選んだ挙句失敗に終わったのである。文脈依存言語を文脈依存言語のまま解析する正道を選んだSecuremarkが正着し文脈自由言語に歪める邪道を選んだCommonMarkが失着に終わったのは当然の帰結であり最初の言語種別選択の時点で決まっていたことである。文脈依存言語であるMarkdownを文脈自由言語として解析しようとして行き詰ったCommonMarkとその閉塞に技術的合理性はなくCommonMarkは最初からの失敗していた過去の遺物であり廃棄すべき負債である。CommonMarkに動きがないのはすでに破綻しており死んでいることに気付かれないように死んでいるからに過ぎない。このようにCommonMarkは完全に破綻し失敗に終わっているためCommonMarkの拡張や発展を期待しても無駄であり既存の文脈依存構文による破綻がなく新たに文脈依存構文を追加可能な拡張性の高いMarkdown仕様は新しく作り直さなければ作れない。しかしCommonMarkの仕様策定者は独自の新しい仕様においてもMarkdownをバックトラックを排除した文脈自由言語として設計しているため救いようがない。しかもその構文と仕様は機械可読性を至上としているため非常に醜く人間が書くことも読むことも困難で実用性の欠如したものである。
305
- Securemarkはスーパークラス構文が解析に失敗した入力をサブクラス構文で解析しないことにより再帰的バックトラックを回避する(解析中の構文自身はスーパークラスとサブクラスの両方に含まれるものとする)。スーパークラス構文A(`αAβ`)の解析が失敗すればサブクラス構文B(`α'A'β'`)の解析も失敗することは自明であり試みるまでもなく解析を省略できる。これは二つの構文の文法が生成する各言語空間がスーパーセットとサブセットの関係にあるならスーパーセットの言語空間の外にある文字列はサブセットの言語空間の内に入る余地がないことからも自明である(この解析法は事前処理によっても可能だが文脈内外のオートリンクURLの括弧解析などを高速に行うことは困難であるためMarkdownをこの事前処理により高速化することは難しい)。メモ化は解析結果を再利用することで結果的に副次的効果としてバックトラックを回避しているのでありメモ化はバックトラックを回避するだけなら過剰機能であり不要である(メモ化はバックトラックがなければ使用されないためバックトラックの少ないほとんどの入力に対してはほとんど使用されず無駄であり空間計算量を常に不必要に数倍以上に増加させてまで行う利益は少ないことから構文解析において必須でも標準でもない。バックトラック回避のためにメモ化するとバックトラックなしで解析可能な場合も常に不必要に空間計算量が増加することがメモ化の最大の欠点である(この問題は解析失敗時のみメモ化すれば解消可能のはずだが基本的にはこうなる)。特に文脈自由構文解析器におけるメモ化の使用は完全に無駄でありバグである。バックトラックが発生しないか他の方法で解決されるならば最終的に使用されないメモ化は無駄であり複数の文脈で解析結果が同一である文脈独立性のある構文ならメモ化した解析結果を異なる文脈で再利用でき有用だがそのような構文は基本的に少数であるため効果が限定的であり最悪計算量は改善されない)。この独自の解析法により、CommonMarkならば最悪計算量32n\*2^2+4n=132nを下らない拡張Markdown言語をSecuremarkはメモ化なしで5nの最悪時間計算量で解析している。すなわち直接比較してもCommonMarkの最悪計算量32nに対してSecuremarkは5nであり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の根本的な欠陥と失敗が最も明瞭に表出している部分である)。この問題はイメージ構文においてさらに顕著でありリンク構文と同じ問題が正当な表現`![![]()]()`で発生しさらにHTMLのaltはプレーンテキストとして表示されるためMarkdownのaltもプレーンテキストとしてそのまま表示されなければならないにもかかわらず文脈を一致させ再帰的バックトラックを防ぐためにMarkdown文字列として解析されaltに`*a*`と書かれたものを`a`に変換して表示する。無論新しい文脈依存構文を追加する場合も同じ制約が永遠についてまわり構文内文字列をMarkdownとして解析する文脈依存構文においてこの制約を破ると最悪計算量が2^m、より正確には32\*2^mないし32^mで指数関数的に増加する。すなわちCommonMarkは文脈依存構文を追加すると最悪計算量が32\*2^mないし32^mで指数関数的に悪化するという拡張性の致命的欠陥が存在する。こんな最悪計算量が32^mで組み合わせ爆発する欠陥言語を拡張できるわけがないことはもはや明白である。また多くのプログラミング言語を見ても明らかなように文脈依存言語は構文内で使用可能な構文を定義しその他の構文は構文内で使用できず例外処理するのが通常でありCommonMarkのように本来使用不能な構文を外側の構文を無効化して使用可能に変える異常な言語はほとんどの人間はCommonMark以外に見たことがないだろう。ほぼすべての人間において他のすべての言語が同じ一貫した規則を持ち同じ規則で統一的に使用できるのに対してCommonMarkだけが他と異なる異常な挙動をして認知的負荷をかけるのである。破壊的変更を避けるため旧構文だけ従来通り文脈自由構文として解析し新構文を文脈依存構文として解析すればキメラ的な非常に不自然かつ歪で一貫性のない解析規則によりCommonMarkという一つの言語の中だけでもユーザーを混乱させるものとなり旧構文で使用した苦肉の策を不必要に新構文でも使用して一貫させれば文脈依存言語なのに文脈自由言語の苦肉の策で解析されるこれもまたキメラ的な非常に不自然で理論的に設計ミスが明白で実用的にも認知的負荷の高い言語となる。そして構文エラーであることが明らかな二重リンクを意図的に入力することはほぼないためCommonMarkの異常な挙動はこれまであまり人目に付かなかったがMarkdownに文脈依存構文を追加して明らかでない構文エラーが頻発すると他の言語と逆に外側の構文を無効化していくCommonMarkの異常な挙動を頻繁に目撃し認知的負荷をかけられることになる。このようにCommonMarkは内部設計だけ文脈依存構文解析器に変更しても理論的齟齬が解析結果と使用感に明白に表れるためCommonMarkが失敗した言語である事実は到底隠し切れるものではない。Markdownはもはや負債以外の何物でもないCommonMarkの異常な解析規則を捨てて素直な文脈依存構文言語として新しい仕様を作り直すのが賢明である。
307
+ - IntersectionObserverが描画完了前に交差を計算するためすべて同時発火する欠陥
306
308
 
307
309
  ### 最適化
308
310
 
@@ -328,16 +330,14 @@ Markdownに本当に必要な仕様はSecuremarkのクラス化制約のよう
328
330
  - 文脈依存構文を追加不可能(要解析戦略変更)
329
331
  - 構文解析の破綻(文脈依存言語の文脈自由言語としての解析を仕様化したことによる破綻)
330
332
  - 二重リンク(`[<a@b>]()`)の生成
331
- -二重リンク自体は回避可能で杜撰なだけだが文脈依存構文として解析できないことが根本原因
332
- - 画像のalt(`![*a*]()`, `![![a]()`)をMarkdownとして解釈
333
- - 再帰的バックトラック回避の必要上修正不可能
333
+ - 画像のalt(`![*a*]()`, `![![a]()]()`)をMarkdownとして解釈
334
334
  - リンクのURLを効率的に解析不可能
335
335
  - altはまだしもURLは文脈依存構文としての解析を避けられないため再帰もまた避けられず公式デモページのCommonMarkで`[](`を1万回程度繰り返しただけで解析時間が1秒を超える
336
336
  - この欠陥は入れ子数を制限することで回避可能だがこれはCommonmarkは文脈依存構文全般に入れ子数制限を要することを意味する
337
- - CommonMarkは最初のバージョンから数年後にこの欠陥を入れ子数制限により修正しこれにより最悪計算量が当初の想定の2nから32nへと32倍に劇的に悪化したことから文脈自由言語および最小計算量としての設計と開発が破綻し失敗に終わったことが明らかである
337
+ - CommonMarkは最初のバージョンから数年後にこの欠陥を入れ子数制限により修正しこれにより最悪計算量が当初の想定の1nから32nへと32倍に劇的に悪化したことから文脈自由言語および最小計算量としての設計と開発が破綻し失敗に終わったことが明らかである
338
338
  - GFMも`[0]([1]()(...[33]()...))`で32回の再帰的バックトラックを行い最悪計算量が32nであることが確認できる
339
- - これほど計算量が悪ければ入れ子数制限付き文脈依存言語と大差ない計算量であり素直に文脈依存言語として作り直したほうが遥かに拡張性と発展性が高く優れている
340
- - 計算資源は使うためにあるにもかかわらず言語と一致しない不適切な解析方法を使用してまでこの程度の計算資源を惜しんで人間に不便と不自由を強いて生産性を下げるのは本末転倒である
339
+ - これほど計算量が悪ければ適切に設計された文脈依存言語より悪い計算量であり素直に文脈依存言語として作り直したほうが遥かに拡張性と発展性が高く優れている
340
+ - 計算資源は使うためにあるにもかかわらず言語と一致しない不適切な解析方法を使用してまでこの程度のわずかな計算資源を惜しんで人間に不便と不自由を強いて生産性を下げるのは本末転倒である
341
341
  - 計算機は人間の生産性に奉仕しなければならない
342
342
  - タブまたは4スペース以上で字下げ不可能(要構文削除)
343
343
  - 情報の再利用困難性
@@ -373,7 +373,7 @@ flanking*!?*
373
373
  ### djotの解析規則の問題点
374
374
 
375
375
  次の公式声明の通り解析規則が構文ごとに定義されず先に閉じられた構文を優先する異常な解析規則になっている。
376
- すべてをスタックベースで解析するために異常な解析規則に歪んでいる。
376
+ 構文解析をスタックベースで行うために異常な解析規則に歪んでいる。
377
377
 
378
378
  ```
379
379
  Most inline syntax is defined by opening and closing delimiters that surround inline content, defining it as emphasized, or as link text, for example. The basic principle governing “precedence” for inline containers is that the first opener that gets closed takes precedence. Containers can’t overlap, so once an opener gets closed, any potential openers between the opener and the closer get marked as regular text and can no longer open inline syntax. For example, in
@@ -389,32 +389,20 @@ the regular emphasis opened by the first _ gets closed by the second _, at which
389
389
  it goes just the opposite way.
390
390
  ```
391
391
 
392
- 結果として文脈の違いを考慮できなくなっている。
392
+ 構文すなわち文脈を自然に分離して入れ子にできないということはCommonMarkと同じく拡張困難な設計の言語ということである。
393
+
394
+ このため文脈を区別しない挙動によるバグが発生する。
393
395
 
394
396
  ```
395
397
  _[a](file_name)
396
398
  -> <em>[a](file</em>name)
397
- ```
398
-
399
- これにより常識的な期待に容易に反して頻繁にリンクが破壊される。
400
- 自然に構文すなわち文脈を分離して入れ子にできないということはCommonMarkと同じく拡張困難な設計ということである。
401
-
402
- 二重リンクも除去できていない。
403
399
 
404
- ```
405
- [[a](b)](c)
406
- -> <a href="c"><a href="b">a</a>
407
- ```
408
-
409
- 変なバグり方をする。
410
-
411
- ```
412
400
  []([]())
413
401
  _[a](file_name)
414
402
  -> <a href="[]())_[a](file_name"></a>
415
403
  ```
416
404
 
417
- すなわち一般的な通常の文法規則に従っておらずCommonMarkと同じく特定の実装技術を使用するために設計が根本的に歪んでいる。バックトラックと計算量を最小化するという戦術的開発目標と技術的手段のために他のすべてを犠牲にしており総じて手段に目的を従わせ歪ませる開発者の人間性と能力の低さが見て取れる。
405
+ すなわち一般的な通常の文法規則に従っておらずCommonMarkと同じく特定のバックトラック回避技術を使用するために設計と文法規則が根本的に歪んでいる。しかも設計と文法を歪めてまでバックトラック排除に固執していながらHTMLや正規表現ではバックトラックが多発しているため設計を歪めてまでバックトラック排除に固執する意味がなくMarkdownの主要構文に限ってバックトラックを完全排除する無意味な自己満足のために異常な解析規則を強いる非常に不自然で歪んだ言語になっている。CommonMarkとdjotはいずれもバックトラックと計算量を中途半端に最小化するという自己満足と技術的手段のために他のすべてを犠牲にしており総じて手段に目的を従わせ歪ませる開発者の人間性と能力の低さが見て取れる。
418
406
 
419
407
  ### トランスクルージョン
420
408
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.294.1 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.294.3 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"));
@@ -2869,7 +2869,7 @@ Object.defineProperty(exports, "__esModule", ({
2869
2869
  exports.lazy = void 0;
2870
2870
  function lazy(builder) {
2871
2871
  let parser;
2872
- return input => parser !== undefined ? parser(input) : (parser = builder())(input);
2872
+ return input => (parser ??= builder())(input);
2873
2873
  }
2874
2874
  exports.lazy = lazy;
2875
2875
 
@@ -3235,6 +3235,7 @@ class List {
3235
3235
  constructor(nodes) {
3236
3236
  this.length = 0;
3237
3237
  this.head = undefined;
3238
+ this.last = undefined;
3238
3239
  if (nodes === undefined) return;
3239
3240
  for (let i = 0; i < nodes.length; ++i) {
3240
3241
  this.push(nodes[i]);
@@ -3243,40 +3244,43 @@ class List {
3243
3244
  get tail() {
3244
3245
  return this.head?.next;
3245
3246
  }
3246
- get last() {
3247
- return this.head?.prev;
3248
- }
3249
3247
  insert(node, before) {
3248
+ if (before === undefined) return this.push(node);
3249
+ if (before === this.head) return this.unshift(node);
3250
3250
  if (++this.length === 1) {
3251
- return this.head = node.next = node.prev = node;
3251
+ return this.head = this.last = node;
3252
3252
  }
3253
- const next = node.next = before ?? this.head;
3253
+ const next = node.next = before;
3254
3254
  const prev = node.prev = next.prev;
3255
3255
  return next.prev = prev.next = node;
3256
3256
  }
3257
3257
  delete(node) {
3258
3258
  if (--this.length === 0) {
3259
- this.head = undefined;
3259
+ this.head = this.last = undefined;
3260
3260
  } else {
3261
3261
  const {
3262
3262
  next,
3263
3263
  prev
3264
3264
  } = node;
3265
- if (node === this.head) {
3266
- this.head = next;
3267
- }
3268
- // Error if not used.
3269
- prev.next = next;
3270
- next.prev = prev;
3265
+ prev === undefined ? this.head = next : prev.next = next;
3266
+ next === undefined ? this.last = prev : next.prev = prev;
3271
3267
  }
3272
3268
  node.next = node.prev = undefined;
3273
3269
  return node;
3274
3270
  }
3275
3271
  unshift(node) {
3276
- return this.head = this.insert(node, this.head);
3272
+ if (++this.length === 1) {
3273
+ return this.head = this.last = node;
3274
+ }
3275
+ node.next = this.head;
3276
+ return this.head = this.head.prev = node;
3277
3277
  }
3278
3278
  push(node) {
3279
- return this.insert(node, this.head);
3279
+ if (++this.length === 1) {
3280
+ return this.head = this.last = node;
3281
+ }
3282
+ node.prev = this.last;
3283
+ return this.last = this.last.next = node;
3280
3284
  }
3281
3285
  shift() {
3282
3286
  if (this.length === 0) return;
@@ -3284,21 +3288,22 @@ class List {
3284
3288
  }
3285
3289
  pop() {
3286
3290
  if (this.length === 0) return;
3287
- return this.delete(this.head.prev);
3291
+ return this.delete(this.last);
3288
3292
  }
3289
3293
  import(list, before) {
3290
3294
  if (list.length === 0) return this;
3291
3295
  if (this.length === 0) {
3292
3296
  this.head = list.head;
3293
- this.length += list.length;
3297
+ this.last = list.last;
3298
+ this.length = list.length;
3294
3299
  list.clear();
3295
3300
  return this;
3296
3301
  }
3297
3302
  const head = list.head;
3298
3303
  const last = list.last;
3299
- const next = last.next = before ?? this.head;
3300
- const prev = head.prev = next.prev;
3301
- next.prev = last;
3304
+ const next = last.next = before;
3305
+ const prev = head.prev = before?.prev ?? this.last;
3306
+ next === undefined ? this.last = last : next.prev = last;
3302
3307
  prev.next = head;
3303
3308
  this.length += list.length;
3304
3309
  list.clear();
@@ -3306,14 +3311,13 @@ class List {
3306
3311
  }
3307
3312
  clear() {
3308
3313
  this.length = 0;
3309
- this.head = undefined;
3314
+ this.head = this.last = undefined;
3310
3315
  }
3311
3316
  *[Symbol.iterator]() {
3312
3317
  for (let node = this.head; node && this.head;) {
3313
3318
  const next = node.next;
3314
3319
  yield node;
3315
3320
  node = next;
3316
- if (node === this.head) break;
3317
3321
  }
3318
3322
  }
3319
3323
  flatMap(f) {
@@ -3322,7 +3326,6 @@ class List {
3322
3326
  const next = node.next;
3323
3327
  acc.import(f(node));
3324
3328
  node = next;
3325
- if (node === this.head) break;
3326
3329
  }
3327
3330
  return acc;
3328
3331
  }
@@ -3331,15 +3334,13 @@ class List {
3331
3334
  const next = node.next;
3332
3335
  acc = f(acc, node);
3333
3336
  node = next;
3334
- if (node === this.head) break;
3335
3337
  }
3336
3338
  return acc;
3337
3339
  }
3338
3340
  foldr(f, acc) {
3339
- for (let node = this.head?.prev; node && this.head;) {
3341
+ for (let node = this.last; node && this.head;) {
3340
3342
  const prev = node.prev;
3341
3343
  acc = f(node, acc);
3342
- if (node === this.head) break;
3343
3344
  node = prev;
3344
3345
  }
3345
3346
  return acc;
@@ -3349,7 +3350,6 @@ class List {
3349
3350
  const next = node.next;
3350
3351
  if (f(node)) return node;
3351
3352
  node = next;
3352
- if (node === this.head) break;
3353
3353
  }
3354
3354
  }
3355
3355
  }
@@ -3375,7 +3375,7 @@ exports.List = List;
3375
3375
  Object.defineProperty(exports, "__esModule", ({
3376
3376
  value: true
3377
3377
  }));
3378
- exports.failsafe = exports.clean = exports.subinput = exports.input = exports.Data = exports.List = void 0;
3378
+ exports.failsafe = exports.subinput = exports.input = exports.Data = exports.List = void 0;
3379
3379
  const data_1 = __webpack_require__(3602);
3380
3380
  Object.defineProperty(exports, "List", ({
3381
3381
  enumerable: true,
@@ -3414,19 +3414,6 @@ function subinput(source, context) {
3414
3414
  };
3415
3415
  }
3416
3416
  exports.subinput = subinput;
3417
- function clean(context) {
3418
- const {
3419
- source,
3420
- position
3421
- } = context;
3422
- for (const p of Object.keys(context)) {
3423
- context[p] = undefined;
3424
- }
3425
- context.source = source;
3426
- context.position = position;
3427
- return context;
3428
- }
3429
- exports.clean = clean;
3430
3417
  function failsafe(parser) {
3431
3418
  return input => {
3432
3419
  const position = input.context.position;
@@ -4102,7 +4089,7 @@ const note_1 = __webpack_require__(165);
4102
4089
  const url_1 = __webpack_require__(1904);
4103
4090
  const array_1 = __webpack_require__(6876);
4104
4091
  function bind(target, settings) {
4105
- let context = {
4092
+ const context = {
4106
4093
  ...settings,
4107
4094
  host: settings.host ?? new url_1.ReadonlyURL(location.pathname, location.origin)
4108
4095
  };
@@ -4429,13 +4416,13 @@ const figure_1 = __webpack_require__(1657);
4429
4416
  const note_1 = __webpack_require__(165);
4430
4417
  const url_1 = __webpack_require__(1904);
4431
4418
  const dom_1 = __webpack_require__(394);
4432
- function parse(source, opts = {}, context) {
4419
+ function parse(source, options = {}, context) {
4433
4420
  const url = (0, header_2.headers)(source).find(field => field.toLowerCase().startsWith('url:'))?.slice(4).trim() ?? '';
4434
4421
  source = !context ? (0, normalize_1.normalize)(source) : source;
4435
4422
  context = {
4436
- host: opts.host ?? context?.host ?? new url_1.ReadonlyURL(location.pathname, location.origin),
4423
+ host: options.host ?? context?.host ?? new url_1.ReadonlyURL(location.pathname, location.origin),
4437
4424
  url: url ? new url_1.ReadonlyURL(url) : context?.url,
4438
- id: opts.id ?? context?.id,
4425
+ id: options.id ?? context?.id,
4439
4426
  caches: context?.caches,
4440
4427
  resources: context?.resources
4441
4428
  };
@@ -4450,9 +4437,9 @@ function parse(source, opts = {}, context) {
4450
4437
  value
4451
4438
  }) => void acc.push(value) || acc, []) ?? []));
4452
4439
  }
4453
- if (opts.test) return node;
4454
- for (const _ of (0, figure_1.figure)(node, opts.notes, context));
4455
- for (const _ of (0, note_1.note)(node, opts.notes, context));
4440
+ if (options.test) return node;
4441
+ for (const _ of (0, figure_1.figure)(node, options.notes, context));
4442
+ for (const _ of (0, note_1.note)(node, options.notes, context));
4456
4443
  return node;
4457
4444
  }
4458
4445
  exports.parse = parse;
@@ -4514,7 +4501,7 @@ exports.block = (0, combinator_1.reset)({
4514
4501
  recursions: [10 || 0 /* Recursion.block */, 20 || 0 /* Recursion.blockquote */, 40 || 0 /* Recursion.listitem */, 20 || 0 /* Recursion.inline */, 20 || 0 /* Recursion.bracket */, 20 || 0 /* Recursion.terminal */]
4515
4502
  },
4516
4503
  backtracks: {}
4517
- }, error((0, combinator_1.union)([input => {
4504
+ }, error((0, combinator_1.union)([source_1.emptyline, input => {
4518
4505
  const {
4519
4506
  context: {
4520
4507
  source,
@@ -4570,7 +4557,7 @@ exports.block = (0, combinator_1.reset)({
4570
4557
  default:
4571
4558
  if ('0' <= fst && fst <= '9') return (0, olist_1.olist)(input);
4572
4559
  }
4573
- }, source_1.emptyline, paragraph_1.paragraph])));
4560
+ }, paragraph_1.paragraph])));
4574
4561
  function error(parser) {
4575
4562
  const reg = new RegExp(String.raw`^${"\u0007" /* Command.Error */}.*\n`);
4576
4563
  return (0, combinator_1.recover)((0, combinator_1.fallback)((0, combinator_1.open)("\u0007" /* Command.Error */, ({
@@ -6393,7 +6380,7 @@ const parser_1 = __webpack_require__(605);
6393
6380
  const combinator_1 = __webpack_require__(3484);
6394
6381
  const link_1 = __webpack_require__(3628);
6395
6382
  const source_1 = __webpack_require__(8745);
6396
- exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9a-z][.+-]?)https?:\/\/(?=[\x21-\x7E])/y, (0, combinator_1.precedence)(1, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.verify)(bracket, ns => ns.length > 0), (0, combinator_1.some)(source_1.unescsource, /([-+*=~^_/])\1|[,.;:!?]{2}|[-+*=~^_,.;:!?]?(?=[\\"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y)]), undefined, [[/[^\x21-\x7E]|\$/y, 9]])), false, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.convert)(url => `{ ${url} }`, link_1.unsafelink, false))), ({
6383
+ exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9A-Za-z][.+-]?)https?:\/\/(?=[\x21-\x7E])/y, (0, combinator_1.precedence)(0, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.unescsource, /(?<![-+*=~^_,.;:!?]|\/{3})(?:[-+*=~^_,.;:!?]|\/{3,}(?!\/))*(?=[\\$"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y), (0, combinator_1.precedence)(1, (0, combinator_1.verify)(bracket, ns => ns.length > 0))]), undefined, [[/[^\x21-\x7E]|\$/y, 9]])), false, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.convert)(url => `{ ${url} }`, link_1.unsafelink, false))), ({
6397
6384
  context: {
6398
6385
  source
6399
6386
  }
@@ -8200,7 +8187,6 @@ Object.defineProperty(exports, "__esModule", ({
8200
8187
  value: true
8201
8188
  }));
8202
8189
  exports.validate = exports.segment = exports.MAX_INPUT_SIZE = exports.MAX_SEGMENT_SIZE = void 0;
8203
- const parser_1 = __webpack_require__(605);
8204
8190
  const combinator_1 = __webpack_require__(3484);
8205
8191
  const heading_1 = __webpack_require__(2778);
8206
8192
  const codeblock_1 = __webpack_require__(9194);
@@ -8238,18 +8224,14 @@ const parser = (0, combinator_1.union)([input => {
8238
8224
  }, (0, combinator_1.some)(source_1.contentline, exports.MAX_SEGMENT_SIZE + 1), (0, combinator_1.some)(source_1.emptyline, exports.MAX_SEGMENT_SIZE + 1)]);
8239
8225
  function* segment(source) {
8240
8226
  if (!validate(source, exports.MAX_INPUT_SIZE)) return yield `${"\u0007" /* Command.Error */}Too large input over ${exports.MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`;
8241
- const context = {
8242
- source,
8243
- position: 0
8244
- };
8245
- const input = {
8246
- context
8247
- };
8248
- for (; context.position < source.length;) {
8249
- const {
8227
+ for (let position = 0; position < source.length;) {
8228
+ const context = {
8229
+ source,
8250
8230
  position
8251
- } = context;
8252
- const result = parser(input);
8231
+ };
8232
+ const result = parser({
8233
+ context
8234
+ });
8253
8235
  const segs = result.length > 0 ? result.foldl((acc, {
8254
8236
  value
8255
8237
  }) => void acc.push(value) || acc, []) : [source.slice(position, context.position)];
@@ -8257,7 +8239,7 @@ function* segment(source) {
8257
8239
  const seg = segs[i];
8258
8240
  validate(seg, exports.MAX_SEGMENT_SIZE) ? yield seg : yield `${"\u0007" /* Command.Error */}Too large segment over ${exports.MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`;
8259
8241
  }
8260
- (0, parser_1.clean)(context);
8242
+ position = context.position;
8261
8243
  }
8262
8244
  }
8263
8245
  exports.segment = segment;
@@ -8854,7 +8836,8 @@ const alias_1 = __webpack_require__(5413);
8854
8836
  const parser_1 = __webpack_require__(605);
8855
8837
  const dom_1 = __webpack_require__(394);
8856
8838
  function* unwrap(nodes) {
8857
- for (const node of nodes ?? []) {
8839
+ if (nodes === undefined) return;
8840
+ for (const node of nodes) {
8858
8841
  yield node.value;
8859
8842
  }
8860
8843
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.294.1",
3
+ "version": "0.294.3",
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",
@@ -1,5 +1,5 @@
1
- import { Parser, List, CtxOptions } from '../../data/parser';
1
+ import { Parser, List, Ctx } from '../../data/parser';
2
2
 
3
- export function clear<D extends Parser<unknown, C>[], C extends CtxOptions>(parser: Parser<unknown, C, D>): Parser<never, C, D> {
3
+ export function clear<D extends Parser<unknown, C>[], C extends Ctx>(parser: Parser<unknown, C, D>): Parser<never, C, D> {
4
4
  return input => parser(input) && new List();
5
5
  }
@@ -4,7 +4,5 @@ export function lazy<P extends Parser<unknown>>(builder: () => P): P;
4
4
  export function lazy<N>(builder: () => Parser<N>): Parser<N> {
5
5
  let parser: Parser<N>;
6
6
  return input =>
7
- parser !== undefined
8
- ? parser(input)
9
- : (parser = builder())(input);
7
+ (parser ??= builder())(input);
10
8
  }