XML は SGML のサブセットであることを旨として設計されていますが、一部旧来の SGML と互換性のない部分があります。そこで ISO は、XML を SGML の完全なサブセットとするために、XML の仕様を変更するのではなく SGML の仕様の方を改正してしまうという逆転の発想を用いました。この新しい SGML 仕様として策定されたのが SGML TC2 Annex K (Web SGML 適応) です。
XML が SGML でどのように定義されているかを理解するためには、旧来の SGML の知識だけではなく、この Web SGML についての知識が必要になります。以下では、旧来の SGML ついての簡単な知識があることを前提に、Web SGML と妥当な XML の SGML 宣言の定義について解説します。
なお、旧来の SGML の文法についてはマニアックな文法論議などが詳しいと思いますので、適宜そちらを参照して下さい。
SGML Annex K 準拠の文書であることを示すためには、SGML 宣言の最小リテラルを "ISO 8879:1986 (WWW)"
と記述します。WWW
は文字通り Web SGML であることを示すものです。なお、Annex K は Annex J Extended Naming Rules (拡張命名規則、最小リテラル "ISO 8879:1986 (ENR)"
)を包含するものなので、拡張命名規則の構文を使用できます。
Web SGML では、十六進数文字参照という新しい参照の定義と、NET (簡略終了タグ) の定義変更に伴い、いくつかの新しい区切り子が導入されています。
十六進数文字参照は HTML 4.01 でも使用可能ですが(*1)、Web SGML で導入された新しい文字参照です(旧来の UA が十六進数文字参照に対応していないのはこのためです)。
この文字参照の導入により、文字参照の構文は次のように変更されました。
文字参照 = 名前指定文字参照 | 数値指定文字参照 | 十六進数文字参照
十六進数文字参照 = hcro, 十六進数+, 参照終了
十六進数 = [0-9a-fA-F]
hcro (十六進数文字参照開き) は新しく導入された区切り子であり、デフォルトでは定義されません。このため、十六進数文字参照を使用するには、SGML 宣言の DELIM 項目で HCRO を明示的に定義する必要があります。XML や HTML 4.01 では HCRO "&#x"
となっており、十六進数文字参照は &#x
で開始することになります。
なお、XML は NAMECASE GENERAL NO
ですから、区切り子の大文字小文字についても区別があり、HCRO を &#X
として記述することはできません。一方、HTML 4.01 は NAMECASE GENERAL YES
であるため、区切り子の大文字小文字についても区別がなく、十六進数文字参照を 文
と記述することもできます。ちなみに十六進数そのものは 十六進数 = [0-9a-fA-F]
と定義されているので、XML の場合にも 書
書
どちらで記述しても構いません。
hcro の認知様相は、他の文字参照同様 CON LIT となっています。また、hcro は制約 HEX (直後に十六進数が続くこと) を持ちます。
なお、HTML 4.0 は従来の SGML ("ISO 8879:1986"
) で定義されているにも関わらず、十六進数文字参照の使用が認められていました(これは当時 Web SGML が策定中であったためです)。このため、実は HTML 4.0 の仕様には SGML に適合していない部分があったのです。この不整合は、HTML 4.01 において、定義言語を Web SGML ("ISO 8879:1986 (WWW)"
) とすることで修正されています。
旧来の SGML では、NET 可能開始タグは次のように定義されていました。
NET 可能開始タグ = stago, 共通識別子指定, 属性指定並び, s*, net
Web SGML では、この構文が次のように変更されています。
NET 可能開始タグ = stago, 共通識別子指定, 属性指定並び, s*, nestc
nestc (NET 可能開始タグ閉じ) は、文字通り NET 可能開始タグのタグ閉じとなる新しい区切り子です。デフォルトでは net と同じ文字列が割り当てられることになっているため、特に区切り子の指定をしない限り、NET を用いた要素の構文は旧来のものと変わりません。
なお、nestc の認知様相は TAG であり、併せて net の認知様相は CON に変更されています (nestc は制約なし、net の制約は旧来通り ELEM)。それぞれの機能に併せ、旧来の net から認知様相と制約が分割されたと見なしてよいでしょう。
XML での定義はそれぞれ NESTC "/" NET ">"
となっているので、例えば <br></br>
を <br/>
と記述することができます。ただし、NET の記述規則は後述する SGML 宣言の SHORTTAG STARTTAG NETENABL 項目で細かく定義されており、XML では <p>…段落…</p>
という記述を <p/…段落…>
と最小化することはできません。
旧来の SGML と XML との大きな相違の一つとして、XML では強制空要素の終了タグの省略が禁じられているということが挙げられます。
旧来の SGML では、強制空要素の終了タグは他の要素の終了タグとは異なる制約を持ち、いかなる場合も記述してはならない(常に省略しなければならない)ことになっていました(*2)。これに対し、Web SGML では SGML 宣言に EMPTYNRM (NRM
は normal の略?) という新しい項目が導入され、強制空要素の終了タグを通常の要素の終了タグと同様に扱えるよう設定することができるようになりました。
EMPTYNRM の宣言値は YES
または NO
となります。ここで EMPTYNRM YES
が宣言された場合、強制空要素の終了タグに関する特殊な制約は全てなくなり、扱いは通常の要素の終了タグと同様になります(なお、強制空要素の開始タグと終了タグの間には、空白文字を含め一切の文字が許されません)。XMLでは EMPTYNRM YES
かつ OMITTAG NO
が宣言されているため、強制空要素の終了タグも必ず記述しなければならないことになるわけです。
一方、EMPTYNRM NO
が宣言された場合には、空要素の終了タグは旧来の SGML と同じ制約を受けることになります (EMPTYNRM が省略された場合にも、暗黙に EMPTYNRM NO
が宣言されたものと見なされます)。
なお、構文上 EMPTYNRM 項目は後述する IMPLYDEF 項目と組で記述することになっているため、一方を記述する場合には必ず他方も記述しなければなりません。また、Web SGML の仕様書には、備考として省略可能な項目/パラメタも全て指定することが望ましい
という記述があります。
旧来の規則では、例えば OMITTAG NO
の場合や、要素型宣言が <!ELEMENT foo - - EMPTY >
などと記述された場合にも、強制空要素の終了タグは省略することになっていました。
Another HTML-lint : Notice によると、IE 3.0 のβ版 DTD には <!ELEMENT NOEMBED - - EMPTY>
という記述があったそうですが、この場合にも noembed の終了タグは記述できません。終了タグを省略すれば妥当な記述と見なされますが、タグ省略最小化の指定につられて終了タグを記述してしまうと、逆にエラーとなってしまいます。
実は EMPTYNRM NO
の場合、強制空要素の要素型宣言でのタグ省略指定は全く意味を持ちません。強制空要素が一般に <!ELEMENT foo - O EMPTY >
として宣言されるのは、単に備忘のためにそう指定するのが望ましい
とされていることによるものです。
SGML には、タグを短縮するためのいくつかの機構が用意されています。旧来、SGML 宣言の SHORTTAG (短縮タグ機構) 項目の宣言値は YES
または NO
の二者択一だったため、これらの短縮機構は「全て利用できる」か「全て利用できない」の両極端から選択しなければなりませんでした。
これに対し Web SGML では、これらの短縮機構のそれぞれについて「利用する」「利用しない」を設定できるようになっています(旧来通りに一括して YES/NO で設定することもできます)。具体的には SHORTTAG 項目に、STARTTAG オプション、ENDTAG オプション、ATTRIB オプションの三つのオプションを設定できるようになりました。XML の SGML 宣言では、SHORTTAG 項目は次のように宣言されています。
SHORTTAG
STARTTAG
EMPTY NO
UNCLOSED NO
NETENABL IMMEDNET
ENDTAG
EMPTY NO
UNCLOSED NO
ATTRIB
DEFAULT YES
OMITNAME NO
VALUE NO
STARTTAG オプションは、文字通り開始タグの短縮について定めるものです。EMPTY 項目と UNCLOSED 項目は、それぞれ空の開始タグと閉じない開始タグの使用の可否を YES/NO で設定します。XML ではどちらも NO
です。
NETENABL 項目は、NET の使用の可否について NO/ALL/IMMEDNET で設定します。NETENABL NO
と NETENABL ALL
は、それぞれ旧来の SHORTTAG NO
と SHORTTAG YES
の場合の NET の扱いに相当します。
NETENABL IMMEDNET
は、「内容を持たない要素に対し」「NET 可能開始タグ及び NET を同時に記述」することのみを可能にします。要素の内容が空でない場合や、NET を記述しない場合には NET 可能開始タグを記述することはできません。XML では NETENABL IMMEDNET
が宣言されているため、前述の通り <p/…段落…>
という短縮はできないことになるわけです。
なお、NET 可能開始タグを記述せずに NET を記述することができないのは、旧来の SGML と同様です。また EMPTYNRM YES
が宣言されていない場合、強制空要素には終了タグを記述できませんから、当然これに NET を記述することもできません。
ちなみに、内容を持たない要素
というのは、強制空要素だけでなく、たまたま内容を持たない要素 ― 例えば表の中の空のセル<td></td>
など ― を含みます。従って、例えば XHTML 1.1 の場合には、次のような記述が行えることになるわけです。
<p>一行目<br/>
二行目</p>
<table>
<tr><th/><th>列見出し1</th><th>列見出し2</th></tr>
<tr><th>行見出し1</th><td>項目1-1</td><td>項目1-2</td></tr>
<tr><th>行見出し2</th><td>項目2-1</td><td>項目2-2</td></tr>
</table>
ここで <br/>
及び <th/>
の完全形式は、それぞれ <br></br>
<th></th>
となります。
ENDTAG オプションは、STARTTAG オプション同様、終了タグの短縮について定めるものです。EMPTY 項目は空の終了タグの使用の可否について、UNCLOSED 項目は閉じない終了タグの使用の可否について、それぞれ YES/NO で設定します。XML ではどちらも NO
です。
STARTTAG オプションの記述と併せて、XML では空タグ及び閉じないタグは一切使用できないことになります。
ATTRIB オプションは属性の短縮と省略について定めます。
DEFAULT 項目は、省略時値を利用した属性の省略の可否を YES/NO で設定します。XML は DEFAULT YES
であるため、必須属性 (#REQUIRED
) 以外の属性は、省略されても省略時値を与えられたものとしてパーザに補われます。旧来の SGML では、SHORTTAG YES
/ SHORTTAG NO
のどちらが宣言されていても省略時値を利用した属性の省略を行うことができましたが、これを禁止するよう設定することもできるようになったわけです。なお、DEFAULT NO
であっても属性リスト宣言に (#REQUIRED 以外の) 省略時値を設定することもできますが、省略された属性は補われないことになります。
OMITNAME 項目は、一意な名前からなる値を与えられた属性の、属性名及び vi(値標識) =
の省略の可否について、YES/NO で設定します。なお、OMITNAME YES
で属性名と vi を省略する場合には、属性値指定を属性値リテラルとしてでなく属性値として記述する必要があります。XML は OMITNAME NO
ですから、属性名の省略はできません。
VALUE 項目は、属性値が名前からなる場合に、属性値指定を属性値リテラルとしてでなく属性値として記述することの可否について、YES/NO で定めます。XML は VALUE NO
ですから、属性値指定を属性値として記述することはできず、常に属性値リテラルとして記述する必要があります。
旧来の SGML での属性リスト宣言は、次のように定義されていました。
属性リスト宣言 = mdo, "ATTLIST", ps+, (結合要素型 | 結合記法名),
ps+, 属性定義リスト, ps*, mdc
属性定義リスト = 属性定義, (ps+, 属性定義)*
Web SGML では、これが次のように変更されています(属性リスト宣言の構文変更については、直接 XML に関係がないので割愛します)。
属性定義リスト = (属性定義, (ps+, 属性定義)*)?
つまり、旧来の属性リスト宣言では最低一つ以上の属性定義がなされる必要があったのですが、これが空でもよいことになりました。XML もこの構文を採用しており、次のような宣言は妥当と見なされます。
<!ATTLIST foo >
なお、このような記述が認められるようになった理由は、恐らく <!ENTITY % foo.attrib "" >
の時にも <!ATTLIST foo %foo.attrib; >
という宣言が妥当となるようにするためであると思います。実際、XHTML のモジュールを利用する場合など、この記述を行えないと不便であることは多々あります。
旧来の SGML では、foo 要素型の属性リストが空であるかも知れない場合には、例えば属性リスト宣言全体を条件付き区間で囲み、状態キーワードとなるパラメタ実体を新たに定義する必要があったのですが、そのような面倒をしなくとも済むようになったわけです。
旧来の SGML には次のような規則がありました。
内容の中に現れる RS は、マークと解釈できない場合、無視する。
内容の中で、すべての参照の置換及びマークの認知の後に残る RE は、次の(1)〜(3) のとおりにそれがマークのためだけに存在するのでなければ、データとして扱う。
- (1) 要素の最初の RE は、その直前に RS、データ又は真の部分要素がなければ、無視する。
- (2) 要素の最後の RE は、その直後にデータ又は真の部分要素がなければ、無視する。
- (3) RS 又は RE の直後に位置しない RE は、その間にデータ又は真の部分要素がなければ、無視する。
このため、例えば HTML 4.01 では、次のような記述は等価に扱われます。
<pre>…内容…</pre>
<pre>
…内容…
</pre>
Web SGML では、SGML 宣言に OTHER KEEPRSRE という項目が設けられ、オプションでこの規則を適用せず、要素内容中の全ての空白文字を保持することができるようになりました。
RS 及び RE を保持する場合は KEEPRSRE YES
、保持しない場合は KEEPRSRE NO
を宣言します。XML は KEEPRSRE YES
ですので、2.10 White Space Handling にある通り、パージングの段階では全ての空白文字が保持されることになります。
Web SGML では、SGML 宣言の容量集合・量集合それぞれの項目において、新たに NONE
という宣言値を取ることができるようになりました。CAPACITY NONE
、SYNTAX QUANTITY NONE
は、どの容量も指定されていないことを示します。
XML ではどちらも NONE
が宣言されているため、容量に関する構文的な制約は存在しません。