メインコンテンツまでスキップ

Appendix A. Implementation Hints (実装のヒント)

展開に関する規範的なセクションでは、記述の明確性のために、各演算子に個別の展開プロセスを記述しています。実際の実装では、式は各演算子ごとにプロセスにわずかな変更のみがある共通のアルゴリズムを使用して左から右に処理されることが期待されます。この非規範的な付録では、そのようなアルゴリズムの1つを説明します。

空の結果文字列とその非エラー状態を初期化します。

テンプレートをスキャンし、"{"で示される式、"{"以外の非リテラル文字の存在で示されるエラー、またはテンプレートの終わりまで、リテラルを結果文字列にコピーします(セクション3.1のように)。終わったら、結果文字列とその現在のエラーまたは非エラー状態を返します。

  • 式が見つかった場合、テンプレートを次の"}"までスキャンし、中括弧の間の文字を抽出します。
  • テンプレートが"}"の前に終了した場合、"{"と抽出された文字を結果文字列に追加し、式が不正であることを示すエラー状態で返します。

演算子を探すために、抽出された式の最初の文字を調べます。

  • 式が終了した場合(つまり、"")、未知または未実装の演算子が見つかった場合、または文字がvarcharセット(セクション2.3)にない場合、"、抽出された式、および"を結果文字列に追加し、結果がエラー状態にあることを記憶し、テンプレートの残りをスキャンに戻ります。
  • 既知で実装された演算子が見つかった場合、演算子を格納し、varspecリストを開始するために次の文字にスキップします。
  • それ以外の場合、演算子をNUL(単純な文字列展開)として格納します。

次の値テーブルを使用して、式タイプ演算子ごとの処理動作を決定します。"first"エントリは、式の変数のいずれかが定義されている場合に最初に結果に追加する文字列です。"sep"エントリは、2番目(またはそれ以降)の定義された変数展開の前に結果に追加するセパレータです。"named"エントリは、explode修飾子が指定されていない場合に展開に変数またはキー名を含めるかどうかのブール値です。"ifemp"エントリは、対応する値が空の場合に名前に追加する文字列です。"allow"エントリは、値展開内でエンコードされずに許可される文字を示します:(U) は、unreservedセットにない任意の文字がエンコードされることを意味します;(U+R) は、(unreserved / reserved / pct-encoding) の和集合にない任意の文字がエンコードされることを意味します;両方のケースで、各許可されない文字は、最初にUTF-8でのオクテットシーケンスとしてエンコードされ、次に各オクテットがパーセントエンコードされたトリプレットとしてエンコードされます。

┌──────────────────────────────────────────────────────────────┐
│ NUL + . / ; ? & #│
├──────────────────────────────────────────────────────────────┤
│ first │ "" "" "." "/" ";" "?" "&" "#"│
│ sep │ "," "," "." "/" ";" "&" "&" "," │
│ named │ false false false false true true true false│
│ ifemp │ "" "" "" "" "" "=" "=" "" │
│ allow │ U U+R U U U U U U+R │
└──────────────────────────────────────────────────────────────┘

上記のテーブルを念頭に置いて、次のように変数リストを処理します:

各varspecについて、varnameセットにない文字が見つかるか式の終わりに達するまで変数リストをスキャンして、式から変数名とオプションの修飾子を抽出します。

  • 式の終わりでvarnameが空の場合、テンプレートの残りのスキャンに戻ります。
  • 式の終わりでなく、見つかった最後の文字が修飾子(""または":")を示している場合、その修飾子を記憶します。explode("")の場合、次の文字をスキャンします。prefix(":")の場合、10進整数として表されるmax-lengthの次の1〜4文字をスキャンし続け、それでも式の終わりでない場合は次の文字をスキャンします。
  • 式の終わりでなく、見つかった最後の文字がカンマ(",")でない場合、"、格納された演算子(ある場合)、スキャンされたvarnameと修飾子、残りの式、および"を結果文字列に追加し、結果がエラー状態にあることを記憶し、テンプレートの残りのスキャンに戻ります。

スキャンされた変数名の値を検索し、次に

  • varnameが不明であるか、未定義の値を持つ変数に対応している場合(セクション2.3)、次のvarspecにスキップします。
  • これがこの式の最初の定義された変数である場合、この式タイプのfirst文字列を結果文字列に追加し、それが完了したことを記憶します。それ以外の場合、sep文字列を結果文字列に追加します。
  • この変数の値が文字列の場合、
    • namedがtrueの場合、リテラルと同じエンコードプロセスを使用してvarnameを結果文字列に追加し、
      • 値が空の場合、ifemp文字列を結果文字列に追加し、次のvarspecにスキップします;
      • それ以外の場合、"="を結果文字列に追加します。
    • prefix修飾子が存在し、プレフィックス長がUnicode文字数での値文字列長より小さい場合、allowセットにない文字をパーセントエンコードした後、値文字列の先頭からその数の文字を結果文字列に追加します。単一のUnicodeコードポイントを表すマルチオクテットまたはパーセントエンコードされたトリプレット文字を分割しないように注意してください;
    • それ以外の場合、allowセットにない文字をパーセントエンコードした後、値を結果文字列に追加します。
  • それ以外で、explode修飾子が指定されていない場合、
    • namedがtrueの場合、リテラルと同じエンコードプロセスを使用してvarnameを結果文字列に追加し、
      • 値が空の場合、ifemp文字列を結果文字列に追加し、次のvarspecにスキップします;
      • それ以外の場合、"="を結果文字列に追加します;そして
    • この変数の値がリストの場合、allowセットにない文字をパーセントエンコードした後、各定義されたリストメンバーを結果文字列に追加し、各定義されたリストメンバーの間にカンマ(",")を結果に追加します;
    • この変数の値が連想配列またはその他の形式のペア(name, value)構造の場合、allowセットにない文字をパーセントエンコードした後、定義された値を持つ各ペアを"name,value"として結果文字列に追加し、各定義されたペアの間にカンマ(",")を結果に追加します。
  • それ以外で、explode修飾子が指定されている場合、
    • namedがtrueの場合、定義された値を持つ各定義されたリストメンバーまたは配列(name, value)ペアに対して:
      • これが最初の定義されたメンバー/値でない場合、sep文字列を結果文字列に追加します;
      • これがリストの場合、リテラルと同じエンコードプロセスを使用してvarnameを結果文字列に追加します;
      • これがペアの場合、リテラルと同じエンコードプロセスを使用してnameを結果文字列に追加します;
      • メンバー/値が空の場合、ifemp文字列を結果文字列に追加します;それ以外の場合、allowセットにないメンバー/値文字をパーセントエンコードした後、"="とメンバー/値を結果文字列に追加します。
    • それ以外で、namedがfalseの場合、
      • これがリストの場合、allowセットにない文字をパーセントエンコードした後、各定義されたリストメンバーを結果文字列に追加し、各定義されたリストメンバーの間にsep文字列を結果に追加します。
      • これが(name, value)ペアの配列の場合、allowセットにない文字をパーセントエンコードした後、定義された値を持つ各ペアを"name=value"として結果文字列に追加し、各定義されたペアの間にsep文字列を結果に追加します。

この式の変数リストが使い果たされたら、テンプレートの残りのスキャンに戻ります。