第2回 XML勧告読解に必須のEBNF

XML 1.0は、1998年にW3Cから勧告として公開された。当然中身は英語で、しかもEBNFと呼ばれる式によって重要な部分が記述してある。この連載では、XML 1.0を深く理解するための、そのXML 1.0勧告を誰でも分かるように、やさしく読み解きながら解説していくことを目指している。(編集局)

川俣 晶
株式会社ピーデー
2002/9/27



仕様書の中で別の言語を定義
今回の主な内容
仕様書の中で別の言語を定義
なぜEBNFが必要なのか?
文字コードによる文字の表記
範囲指定による文字の表記
列挙による文字の表記
範囲の除外と、列挙された文字の除外
リテラル文字列
オプション選択
順序
選択
除外

1回以上の繰り返し

0回以上の繰り返し

整形式制約と妥当性制約
 

 前回「第1回 XMLの仕様書によると、XMLの目標とは……」で予告したとおり、XML勧告の最初から読んでいくのではなく、いきなり第6章「6 Notation(記法)」の部分から始めることにしよう。その理由は、XML勧告を記述するために使用されているEBNFと呼ばれる言語の定義がここに記述されているためだ。先頭から順番に読んでいくと、第2章に入った瞬間に何が何やら良く分からない記述に出くわしてしまう。

 では、EBNFとはいったい何だろうか? これはExtended Backus-Naur Formの略で、拡張されたBNF(Backus-Naur Form)ということである。BNFとは、Backusさんと、Naurさんによって生み出された記法である。それをさらに拡張したものがEBNFというわけである。

 しかしEBNFといっても必ずしも1つの固まった仕様があるわけではないようで、XML勧告では“simple Extended Backus-Naur Form (EBNF) notation”というように、シンプルであると断って、仕様書内に定義を載せている。EBNFの歴史的な経緯については、檜山正幸さんの記事「「XML深層探求 第4回 BNF記法入門(1)XML関連仕様を読むために」で触れられているので、興味のある人は、それを参照していただきたい。しかし、XML勧告を読むために、EBNFの経緯を知ることは必須ではない。

XML 1.0第2版のWebページのセクション6、EBNFの解説

 EBNFの役割は、構文の定義を記述することである。例えば、性別の情報を記述したいとしよう。「男」または「女」のみが正しい表記であるとして、それ以外の文字が記述された場合はすべて間違いとしたいとしよう。これをEBNFで表記すると、以下のようになる。

  '男'|'女'

 |(縦線)記号は前後のどちらか1つだけであることを示すEBNFの表記である。しかし、この程度のことなら日本語で書くことも容易である。

  「男」または「女」の1文字を記述する

 少し長くなったが、日本語で書けば誰でも意味が分かるようになる。一見、この方がよいのではないかと思えてくるが、条件が複雑になってくると話は変わる。例えば、年齢を表記するために、以下のような定義を書いてみたとしよう。

  0から9までの文字の任意個数の並び

 この定義では、0や9という文字が半角文字だけなのか全角文字も含むのかあいまいである。また、世界には数字を表記する文字がたくさんあり、日本の漢数字もその1つである。そういう世界の数字を表す文字も含むのではないかと考える人が出てこないとも限らない。また、任意個数の並びというのも、0個を許すのか、許さないのかで人によって解釈に差が出てくる可能性があるし、前後に空白文字を入れてよいのか、いけないのか、という余計なことを考える人も出てくるかもしれない。

 これらに配慮して書き直すと、こんなふうになるだろうか。

  半角数字の0(Unicode値でU+0030)から9(Unicode値でU+0039)までの文字の1個以上の任意個数の並びで、前後にそれ以外の文字を含まない

 かなり文章が長くなり、しかも、本質とは関係ない注意書きが多く、どこが重要なポイントだか分かりにくい。しかし、EBNFならこれを短く簡潔に書ける。実際に書いてみると、たったこれだけである。

  [0-9]+

 もちろん、こうした定義が1個か2個で済むなら、日本語の方がよいという意見もあるだろう。だが、XML勧告には、EBNFで記述されたルールが89も存在している。何十回も込み入った長い文章を読まされる苦労を考えれば、新しい表記を学ぶ手間を費やしても、簡潔な表記を読む方が、トータルとしては楽ではないだろうか?

 もう1つ、EBNFで記述すると、あいまいさが入り込む余地が少なく、プログラム言語で実現しやすいという特徴もある。筆者は以前勉強のためにXMLパーサを記述してみたことがあるが、XML仕様書の全体像がさっぱり分からなくても、EBNFの構文をそのままプログラム上に移し替えるだけで、構文解析部がかなりのレベルまで組み上がってしまった。EBNFで記述されたルールを忠実にプログラムに移し替えれば、プログラム間の互換性は高いものになるだろう。この長所は、XML勧告を読むだけの人には関係がないかもしれないが、XMLの互換性を高めることに貢献しているといえるだろう。

なぜEBNFが必要なのか?

 それでは、XML勧告の「6 Notation(記法)」を読んでいこう。最初の部分はこうだ。

The formal grammar of XML is given in this specification using a simple Extended Backus-Naur Form (EBNF) notation. Each rule in the grammar defines one symbol, in the form

symbol ::= expression

 ここでは、XMLのフォーマルな文法は、シンプルなEBNFによって与えられていることが説明されている。1つのシンボルを定義する文法のルールは、ここに示されているようなsymbol ::= expressionという形式となる。symbolは定義するシンボルの名前である。expressionはシンボルの内容となる式である。もちろん、symbolやexpressionは、実際には必要とされる文字列に置き換えられる。以下のような定義があったとする。

  Nmtoken ::= (NameChar)+

 このとき、シンボルはNmtokenで、式は(NameChar)+となる。式の詳細はこの後で述べる。ここでNmtokenという名前のシンボルを定義しているが、これはEBNFレベルのシンボルであって、XML文書内でNmtokenという名前の文字列が何か特別な意味を持つわけではない。DTDでNMTOKENというキーワードを記述する場合があり、これと似ているが、まったく無関係だ。つまり、Nmtokenは、EBNFを読む人にのみ意味のある名前で、EBNFを知らずにXML文書を作成する人には何の意味もない名前である。このシンボル名は、ほかのEBNF定義の式から参照される。例えば以下のような式が、このシンボルを式の中で利用している。

  Nmtokens ::= Nmtoken (S Nmtoken)*

 勧告の続きを読もう。

Symbols are written with an initial capital letter if they are the start symbol of a regular language, otherwise with an initial lower case letter. Literal strings are quoted.

 まず、シンボルの最初の1文字が大文字か小文字かが問題であるとしている。正規言語(regular language)の開始ならシンボルの最初の1文字は大文字、そうでないなら小文字であるとしている。正規言語とは、コンピュータサイエンスで使われる言葉で、言語のカテゴリの1つである。具体的な正規言語の定義は、我流でコンピュータを勉強した筆者には荷が重いが、ここでは自分自身への参照を持つ定義が小文字で始まり、そうでないものは大文字で始まると思えばよいかもしれない。式が参照しているシンボルの定義に自分自身への参照が含まれていれば、やはり小文字から始まる(もし、もっと適切な説明があれば、ご教示いただきたい)。

 とはいえ、XML勧告のEBNFを単に読むだけなら正規言語が何かを知らなくても読むことができる。正規言語という難しそうな言葉が出てきたらからと逃げる必要はない。

 最後に、リテラル文字列はクオートされると書かれている。例えば以下のような定義があったとする。

  EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )

 ここで、文字列encodingは、シングルクオーテーションでくくられているので、リテラル文字列と見なされる。つまり、XML文書の中に、encodingという文字列が出現する。しかし、クオートされていないEncNameは、リテラル文字列ではなく、シンボルの名前である。つまり、XML文書中にEncNameという文字列が出現するわけではない。そのほか、'"'(シングルクオーテーション、ダブルクオーテーション、シングルクオーテーション)は、シングルクオーテーションでくくられているので"(ダブルクオーテーション)記号がXML文書中に出現することを示し、"'"(ダブルクオーテーション、シングルクオーテーション、ダブルクオーテーション)はダブルクオーテーションでくくられているので、'(シングルクオーテーション)記号がXML文書中に出現することを示している。同時に、クオートされていないS、Eqはシンボル名である。

 勧告の続きを読もう。

Within the expression on the right-hand side of a rule, the following expressions are used to match strings of one or more characters:

 このルールの右側は、1つまたは1つ以上の文字の並びを表現するために使用されるということが説明されている。EBNFとは、要するに文字の並び方のルールを表記する手段であって、正しい文字の並びとそうではない文字の並びを区別するルールを表現するために使われる。

 例えばXML宣言は<?xml version="1.0">と記述すれば正しいが、<?xyz version="1.0">は間違いである。その区別の根拠を与えるためにEBNFの記述が使われる。しかし、EBNFは意味を表現する役割を持っていない。あくまで、文字の並び順の正しさしか表現せず、それによって表現された意味の正しさについては、何も関知しない。

 この文字列の並び順を具体的に表記するための表現方法がこれ以後に記述されている。

文字コードによる文字の表記

 ここからは、具体的なEBNFの読み方についての説明が始まる。XML勧告の続きを読んでいこう。

#xN
where N is a hexadecimal integer, the expression matches the character in ISO/IEC 10646 whose canonical (UCS-4) code value, when interpreted as an unsigned binary number, has the value indicated. The number of leading zeros in the #xN form is insignificant; the number of leading zeros in the corresponding code value is governed by the character encoding in use and is not significant for XML

 まず出てきたのが、文字コードを使用した文字の表現である。すでに見たように、文字はクオートすれば表現できる。シンボルAが常にaという文字列を表現したければ、A ::= 'a'と書けばよい。

 しかし、この方法は、2つのケースでうまくいかない。1つは、空白文字や制御文字を表現する場合である。例えば改行文字を表現するために、改行文字を入れても、仕様書を読む人は、そこに改行文字があると表現したいのか、ただ単にページの右端で折り返しているのか区別がつかない危険がある。それに、改行文字といっても、細かく見れば、複数の表現がされている。Windowsパソコンでは復帰と改行の2つのコードで表現するが、Macintoshでは復帰のみ、LinuxのようなUNIX系OSでは改行のみの1つのコードで表現している。ただ改行ということを示すだけでは互換性のあるプログラムがにならない可能性がある。空白文字も、行の最後にきた場合など、そこに空白文字があるのかないのか、区別しにくい場合がある。

 もう1つのケースは、英語以外の世界の言語の文字を表現する場合である。XML勧告では、漢字を要素名に使えるように定義しているが、漢字を示すために漢字を書いては、日本語フォントを使っていない利用者には読めないことになる。

 そこでEBNFでは、英語以外の文字は、コードの番号で表記するようになっている。XML勧告の「B Character Classes」の部分を読めば、世界の言語の文字に関する定義が、コード表記でズラリと並んでいることが分かるだろう。

 実際の表記は、#記号に続き、半角小文字のアルファベットxを記述し、その後で16進表記の文字コードを記述する。文字コードは、ISO/IEC 10646のコードを記述する。例えば、漢数字の「一」は、ISO/IEC 10646ではU+4E00なので、以下のように表記することになる。

  #x4E00

 先頭に付くゼロの数は意味を持たないとしているが、実際にXML勧告のBNFを見ると、先頭にゼロは付けていない形式で表記していることが分かる。例えば4けたのU+4E00は4けたで表記しているが、1けたのU+000Dは1けたで表記している。以下のような感じである。

  #x4E00
#xD

 話は変わるが、ここではUCS-4という言葉も見える。UCS-4とは、32bit表記の文字コード体系であり、われわれが普段使うUTF-16の上位に位置するものである。しかし、XMLでは使用できる文字の範囲をUTF-16の範囲内でしか規定しておらず、ここでUCS-4という名前が出たからといって、UTF-16で表記できないたくさんの種類の文字がXML文書に書けるわけではない。

 余談だが、W3CのXML勧告を日本語に訳してJISとして制定した「日本工業規格 JIS X 4159:2002 拡張可能なマーク付け言語 (XML)」では、文字コードの部分はISO/IEC 10646ではなく、JIS X 0221-1と記述されている。これは、JIS規格は、JIS規格に存在するものはそれを参照するという原則による。
 JIS X 0221-1はISO/IEC 10646-1の一致規格としてJIS化されたもので、同じと見なすことができる(はずである)。しかし、JIS X 0221-1はISO/IEC 10646-1と同じなのであって、ISO/IEC 10646と同じというわけではない。
 ISO/IEC 10646は複数のパートから構成される規格ということで、パート番号が付加されている。「-1」の部分はパート番号である。つまり、英語版では複数のパートからなるISO/IEC 10646の全体を参照しているのに対して、JIS版はパート1しか参照していないように見える。だが、実は深く読み込むと、そうではないことが分かる。XML勧告の参考文献でISO/IEC 10646として参照されているものは、ISO (International Organization for Standardization). ISO/IEC 10646-1993 (E). Information technology -- Universal Multiple-Octet Coded Character Set (UCS) -- Part 1: Architecture and Basic Multilingual Plane. [Geneva]: International Organization for Standardization, 1993 (plus amendments AM 1 through AM 7). となっている。つまり、パート1なのである。というわけで、どちらも最終的に同じものを指し示していると見ることができるのである。

範囲指定による文字の表記

[a-zA-Z], [#xN-#xN]
matches any Char with a value in the range(s) indicated (inclusive).

 これは、ある範囲の文字のどれでもが有効であることを示す表記である。文字といっても、シンボルCharに該当するものだけが有効である。シンボルCharについては、後で説明を行う。XML文書で使用できるあらゆる文字、と理解しておけばよいだろう。

 a、z、A、Zというのは、任意の文字を示している。つまり、[a-zA-Z]と書いてあるからといって、アルファベットの小文字のaからzまたは大文字のAからZしか指定できないわけではない。#xNの方は上で出てきた文字コードの表記と同じ意味を持つ。

 例えば、半角数字の0から9は以下のように表記する。下の例は文字コードによる表記だ。

  [0-9]
[#x30-#x39]

 しかし、漢数字の一から九までという意図で、以下のように書くことはできない。半角数字は0から9までが文字コード順に並んでいるが、漢数字は数値と文字コード順が対応していないためである。

  [一-九]

  範囲は複数並べることができる。例えばA、B、C、a、b、cの6種類の文字のどれも有効とするには、以下のように記述する。

  [A-Ca-c]

 この表記はあくまで1文字に対応するものである。[0-9]は、0でも1でも2でも有効だが、123に対しては有効ではない。

列挙による文字の表記

[abc], [#xN#xN#xN]
matches any Char with a value among the characters enumerated. Enumerations and ranges can be mixed in one set of brackets

 これは、列挙された文字のどれでもが有効という意味を表現する。例えばA、B、C、a、b、cの6種類の文字のどれも有効とするには、以下のように記述する。

  [ABCabc]

 文字はシンボルCharに該当するものが記述できる。この表記でも、文字コードによる記述は有効である。この表記もあくまで1文字に対応するものである。

範囲の除外と、列挙された文字の除外

 勧告の次の解説は、範囲の除外を示す。

[^a-z], [^#xN-#xN]
matches any Char with a value outside the range indicated.

 これは、[a-zA-Z], [#xN-#xN] に似ているが、否定の効果を持つ。先頭の^(ハット)記号が否定の意味を持つ。以下のように記述すると、半角数字の0から9までを除いた文字(シンボルCharに該当する文字)という意味になる。

編集注:いわゆるハット記号(^)は、JISでは「アクサンシルコンフレックス」と呼ばれているが、あまり浸透している名称ではないため、ここではハットと呼ぶことにした。

  [^0-9]

 続いて勧告では、列挙された文字の除外について説明している。

[^abc], [^#xN#xN#xN]
matches any Char with a value not among the characters given. Enumerations and ranges of forbidden values can be mixed in one set of brackets.

 [abc]はa、b、cのどれかの文字を意味するが、以下はa、b、c以外のいずれかの文字(シンボルCharに該当する文字)という意味を表現している。

  [^abc]

 どちらの表記でも、文字コードによる記述は有効である。また、あくまで1文字に対応するものである。

リテラル文字列

 続いての説明は、"(ダブルクオーテーション)によるリテラル文字列だ。

"string"

matches a literal string matching that given inside the double quotes.

 これは、文字列がそのまま出現することを表現している。例えば、以下のように記述されていれば、XML文書中にそのままencodingという文字列が出現しなければならないことを意味している。

  "encoding"

 ダブルクオーテーションは区切り文字で、文字列の一部ではない。

 続いて、'(シングルクオーテーション)によるリテラル文字列についても説明している。

'string'

matches a literal string matching that given inside the single quotes.

These symbols may be combined to match more complex patterns as follows, where A and B represent simple expressions:

 同じように、シングルクオーテーションを使用した場合も有効であることを表現している。ダブルクオーテーションとシングルクオーテーションの両方があるのは、その方が便利だからである。つまり、ダブルクオーテーションを含む文字列は、シングルクオーテーションでくくって表現すれば分かりやすく、逆も真というわけである。

 さて、この章の最後には、ここまで記述してきた表記方法について、さらに複雑な表現をするための説明が付け加えられている。主なところをかいつまんで解説しよう。以下の説明に出てくるAとBは式であるとする。

オプション選択

A?
matches A or nothing; optional A

 これはある式がそこに出現してもよく、出現しなくてもよいことを示している。例えば、以下のような式があったとする。

  '山田' '太郎'?

 この式は以下の名前を許す。

  山田
山田太郎

 ?の手前の値は、出現しても、しなくてもよい。

順序

A B
matches A followed by B. This operator has higher precedence than alternation; thus A B | C D is identical to (A B) | (C D).

 これは、書かれた記号に相当する文字列が連続して出現しなければならないことを示す。例えば以下のような式があったとする。

 

'山' '田' '太' '郎'

 この場合、山田太郎の4文字がそのとおりの順番で出現しなければならないことを示している。途中に関係ない文字が入ってもいけないし、1文字でも欠けてはいけない。また順番が入れ替わって、山太田郎になってもいけない。

 この表記は、|(パイプ)記号よりも高い優先順位を与えられているので、A B | C Dと(A B) | (C D)は等しいと見なされる。

選択

A | B
matches A or B but not b

 すでに何度も出ているので、説明不要かもしれない。AまたはBのいずれか一方であるときに有効であることを示す。以下のような式があったとする。

  '山田' | '斉藤' | '鈴木' | '高橋'

 この式は、'山田'でも、'斉藤'でも、'鈴木'でも、'高橋'でも、どれでも有効である。しかし、'川俣'はダメである。また、'山田斉藤'のように有効な値が2回出てきてもダメである。

除外

A - B
matches any string that matches A but does not match B.

 これは、ある式に当てはまる文字列の中で、別の式で示される文字列だけは除外することを表現する。例えば、以下のような式があったとする。

  ([A-Z] [A-Z]) - 'PC'

 この式の前半[A-Z] [A-Z]は、アルファベットのAからZまでのいずれかの文字を2文字並べることを意味する。これは'AA'でも'ZZ'でも'ST'でも'EX'でも有効である。しかし式の後半で、- 'PC'を指定していることから、この式はアルファベット大文字2文字ならどれでも有効になるが、'PC'だけは除外される。

1回以上の繰り返し

A+

matches one or more occurrences of A.Concatenation has higher precedence than alternation; thus A+ | B+ is identical to (A+) | (B+).

 これは1回以上の繰り返しを示す。これまで、1文字にしか対応しないという表記をいくつも説明したが、これを使えば1文字以上に対応させることができる。例えば、[A-Z]はアルファベット大文字1文字に対応するが、これに+記号を付けることができる。

  [A-Z]+

 これは、以下のどの文字列も許す表記である。

  A
ALPHA
BETA

 しかし、アルファベットの大文字以外の文字が途中に入っていたり、空文字列であった場合はこの表記とはマッチしない。

 +(プラス)記号は、|(縦棒)記号よりも高い優先順位を与えられているので、A+ | B+と(A+) | (B+)は等しいと見なされる。

0回以上の繰り返し

A*
matches zero or more occurrences of A. Concatenation has higher precedence than alternation; thus A* | B* is identical to (A*) | (B*).
Other notations used in the productions are:

 これは、1回以上の繰り返しとほぼ同じである。相違点は、2つだけである。1つは、表記に使用する記号が+記号ではなく*記号であること。もう1つは、0回の繰り返しを許すということである。0回繰り返すということは何もないということで、空文字列を意味する。

整形式制約と妥当性制約

 ここまでは、構文を定義する表記についての説明だったが、「6 Notation (記法)」の最後は、それとはちょっと意味の違うものについて解説している。詳しくはカコミの中で解説したので、興味のある方はじっくり読んでいただきたい。

整形式制約

[ wfc: ... ]
well-formedness constraint; this identifies by name a constraint on well-formed documents associated with a production.

 これは、整形式のときに発生する制約事項を記述するための構文である。JIS版では、wfc:の代わりに、「整形式制約:」という表記になっている。

 ここでwfc:の後に記述されるのは、制約事項に付けられた名前である。その名前に対応する説明は仕様書の別の個所にあって、リンクが張られている。この制約事項は、EBNFでは表現できない特別な条件について記述するために使用される。EBNFは本当に基本的な構文についてのみ表記することしかできず、万能ではないということである。整形式制約については、また後で、詳しく取り上げる。

 例えば、XML勧告には以下のような形で整形式制約が記述されている。

  [WFC: External Subset]

 これは、[と]の間の文字のいずれか、という意味ではなく、External Subsetという名前の整形式制約と読む。また、定義ではwfc:と小文字だが、本文ではWFC:と大文字になっている。ここでは大文字小文字の相違は重視されていないようである。

妥当性制約

[ vc: ... ]
validity constraint; this identifies by name a constraint on valid documents associated with a production.

 これは、妥当なXML文書のときに発生する制約事項を記述するための構文である。JIS版では、vc:の代わりに、「妥当性制約:」という表記になっている。例えば、XML勧告には以下のような形で妥当性制約が記述されている。

  [VC: Root Element Type]

 整形式制約と同様、[と]の間の文字のいずれか、という意味ではなく、Root Element Typeという名前の妥当性制約と読む。vc:とVC:の大文字小文字の相違も、整形式制約と同様である。

 以上で、XML勧告の「6 Notation(記法)」をざっと見てきたことになる。EBNFについての細かい説明は本文の説明中でも随時加えるとして、次はXML勧告の第1章「1 Introduction(一般)」に戻って、いよいよXMLの仕様そのものについての記述を読んでいくことにしよう。

連載 やさしく読む「XML 1.0勧告」


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間