For Development HEAD DRAFTSearch (procedure/syntax/module):

12.59 sxml.ssax - 関数的なXMLパーザ

Module: sxml.ssax

sxml.*モジュールは、XML構造のS式表現に基づく Oleg KiselyovのSXMLフレームワーク(http://okmij.org/ftp/Scheme/xml.html)の適合です。

SSAXは、SXMLフレームワークのパーザ部分です。以下は、 SSAXのウェブページからの引用です。

SSAXは関数的なXMLパージングフレームワークで、DOM/SXMLパーザ、SAXパーザ、 字句解析・構文解析手続きのサポートライブラリから構成されます。 パッケージ内の手続きは、XML文書の様々な部分をトークナイズ、あるいは パーズするために独立して使うことができます。 このフレームワークは、XML名前空間、文字、内部および外部解析済み実体、 属性値の正規化、処理命令とCDATAセクションをサポートしています。 パッケージは、ある程度の妥当性検査を行うSXMLパーザ: SAXパーザの インスタンスであるDOMモードのパーザ(SSAXと呼ばれます)を含んでいます。

現在のバージョンは、SXMLツールセットの最新の’公式な’リリース(4.9)よりも 新しい、SSAXのCVSバージョンをベースにしており、パッケージSXML-gauche-0.9は、 SXML-4.9をベースにしています。 SXMLのリリース4.9以降では、重要な変更があります。 現在のAPIでは、大文字の接頭辞SSAX:の代わりに小文字のssax:を 使います。Gaucheはデフォルトで文字の大小を区別するために、この違いは 問題となります。 後方互換性のためにエイリアスされた名前が定義されていますが、 大文字の接頭辞付きの名前の使用は推奨されません。

マニュアルのこのパートの内容はSSAXのソースコードから抽出されたもので、 単にそのコメントをTexinfoのフォーマットに変換しただけです。 オリジナルのテキストは、Oleg Kiselyovによるものです。 変換により生じた誤字・誤植やフォーマットエラーの責任は、 Shiro Kawaiにあります。

このマニュアルのエントリは、低レベルの構造から高レベルのユーティリティへと “ボトムアップ”の方法で並べられています。 もし、あなたが単にXMLドキュメントをパーズしたりSXMLを得たいだけならば、 SSAXの高レベルのパーザ - XMLからSXMLへssax:xml->sxmlを チェックして下さい。


12.59.1 SSAXデータタイプ

TAG-KIND

シンボル’START、’END、’PI、’DECL、’COMMENT、 ’CDSECTは、マークアップトークンを識別するものです。

UNRES-NAME

XML文書で、マークアップトークン: 開始タグ、PIターゲット、属性名に 与えられる名前(XML勧告ではGIと呼ばれます)です。 GINCNameである場合、UNRES-NAMEはこのNCNameが Schemeのシンボルに変換されたものになります。 GIQNameならば、UNRES-NAMEは、シンボルのペア、 (PREFIX . LOCALPART)となります。

RES-NAME

展開された名前、つまりUNRES-NAMEの解決されたバージョンです。 名前空間URIが空でない場合の要素や属性名では、RES-NAMEはシンボルのペア、 (URI-SYMB . LOCALPART)です。そうでない場合は、1つのシンボルです。

ELEM-CONTENT-MODEL

以下のシンボルのうちの1つです。

ANY何でもよく、ENDタグがあるもの。
EMPTY-TAG内容がなく、ENDタグのないもの。
EMPTY内容がなく、次のトークンがENDタグであるもの。
PCDATA文字データのみで、子要素がないもの。
MIXED
ELEM-CONTENT
URI-SYMB

名前空間を表すシンボル、あるいはURIを表すためにユーザが選んだ他のシンボルです。 前者の場合、URI-SYMBは不正なURI文字が%でクォートされた 文字列をシンボルに変換したものです。

NAMESPACES

効力を持つ名前空間を表すリストです。リストの要素は、以下のフォームのうちの1つです。

(prefix uri-symb . uri-symb)

あるいは、

(prefix user-prefix . uri-symb)

user-prefixは、そのURIを表現するためにユーザにより選ばれたシンボル。

(#f user-prefix . uri-symb)

ユーザが選んだプリフィックスとuri-symbolの指定。

(*DEFAULT* user-prefix . uri-symb)

デフォルト名前空間の宣言。

(*DEFAULT* #f . #f)

デフォルト名前空間を宣言しない。この記法は、それ以前の宣言を上書き することを表す。

NAMESPACESのリストは、同じPREFIXについていくつかの要素を含むかも しれません。リストの先頭に近いものが効力を持ちます。

ATTLIST

ペア(NAME . VALUE)の順序付きのコレクションで、NAMERES-NAMEUNRES-NAMEです。このコレクションはADTです。

STR-HANDLER

3引数の手続き (string1 string2 seed)で、 新しいseedを返します。 この手続きは、文字データstring2が後に続く、文字データstring1を 扱うものです。string2は、“\n”や“”のような短い文字列です。

ENTITIES

ペア (named-entity-name . named-entity-body)の連想リストで、 named-entity-nameはその実体が宣言されたシンボル、 named-entity-bodyは文字列か、(外部実体の場合は) (そこから実体が読み込める)入力ポートを返す手続きです。 named-entity-bodyはまた、#fかも知れません。 これは、named-entity-nameがその時点で展開されていることを 示します。 このnamed-entity-nameへの参照は、WFC非再帰違反としてエラーに なります。

XML-TOKEN

kindtokenという2つのスロットを持つレコードです。 このレコードは、XML勧告によれば、「開始タグ、終了タグ、空要素タグ、 実体参照、文字参照、コメント、CDATAセクションの区切り、 文書型宣言、処理命令の形を取る」マークアップを表します。

kind

TAG-KIND

head

UNRES-NAME。’COMMENTと’CDSECTというkindのXMLトークンでは、 そのheadは#fになります。

例を示します。

<P>  => kind='START, head='P
</P> => kind='END, head='P
<BR/> => kind='EMPTY-EL, head='BR
<!DOCTYPE OMF ...> => kind='DECL, head='DOCTYPE
<?xml version="1.0"?> => kind='PI, head='xml
&my-ent; => kind = 'ENTITY-REF, head='my-ent

文字参照は、対応する文字へと透過的に解決されるので、XMLトークンとしては 表現されません。

XML-DECL

elemsentitiesnotationsという3つのスロットを持つレコードです。

このレコードは、XML文書のデータタイプを表現します。それは、 宣言された要素とその属性のリスト、宣言された記法、 解析済み一般実体の置換文字列やロードされる手続きのリストなどです。 通常、xml-declレコードは、それを作るには他にたくさんの方法 (例えばファイルからロードするなど)があるにも関わらず、DTDかXML Schemaから 作られます。

elems: decl-elemか#fの(連想)リスト。後者は、パーザに、 要素と属性の妥当性検査を行わないように指示します。

decl-elem: 1つの要素の宣言: (elem-name elem-content decl-attrs); elem-nameはその要素のUNRES-NAMEelem-contentELEM-CONTENT-MODELdecl-attrsATTLISTか、(attr-name . value)の 連想リスト。 この要素は、要素のパージングを扱うユーザ手続きを宣言できます。 (例えば、カスタムな妥当性検査を行ったり、タグに出会うたびに IDのハッシュを構築するなど。)

decl-attr: ATTLISTの要素で、1つの属性 (attr-name content-type use-type default-value) の宣言: attr-nameはその宣言された属性のUNRES-NAMEcontent-typeはシンボルCDATANMTOKENNMTOKENS、 あるいは列挙されたタイプの文字列のリスト。 use-typeはシンボルREQUIREDIMPLIEDFIXED。 default-valueは、デフォルト値としての文字列か、与えられなければ#f

Function: make-empty-attlist
Function: attlist-add attlist name-value
Function: attlist-null?
Function: attlist-remove-top attlist
Function: attlist->alist attlist
Function: attlist-fold

{sxml.ssax} 名前-値の属性リストを扱うユーティリティ手続きです。

Function: make-xml-token kind head
Function: xml-token? token

{sxml.ssax} XML-TOKENレコードのコンストラクタと述語です。

Macro: xml-token-kind token
Macro: xml-token-head token

{sxml.ssax} XML-TOKENレコードのアクセッサマクロです。


12.59.2 SSAXの低レベルパージングコード

これらは、プリミティブな字句解析ユニット(名前、空白、タグ)や、 より一般的な断片を扱います。 これらのパーザのほとんどは、適切なコンテキストで呼ばれなければなりません。 例えば、ssax:complete-start-tagは、開始タグが検知されそのGIが 読み込まれたときにのみ呼ばれなければなりません。

Function: ssax:skip-S port

{sxml.ssax} 次のように定義されるS(空白)をスキップします。

 [3] S ::= (#x20 | #x9 | #xD | #xA)

この手続きは、portのスキャン中に遭遇した最初の空白ではない文字を 返します。この文字は、入力ストリームに残されます。

Function: ssax:ncname-starting-char? a-char

{sxml.ssax} NCNameがa-charで始まるかどうかを検査します。

Function: ssax:read-NCName port

{sxml.ssax} portで現在の位置から始まるNCNameを読み込み、それをシンボルとして 返します。

Function: ssax:read-QName port

{sxml.ssax} (名前空間)完全修飾名、QNameportの現在の位置から読み込みます。

REC-xml-namesは、

 [6] QName ::= (Prefix ':')? LocalPart
 [7] Prefix ::= NCName
 [8] LocalPart ::= NCName

戻り値は、UNRES-NAMEです。

Variable: ssax:Prefix-XML

{sxml.ssax} 定義済みのXML名前空間の接頭辞、つまり、’xmlです。

Function: ssax:read-markup-token port

{sxml.ssax} この手続きは、マークアップトークンのパージングを開始します。 ストリームの現在の位置は、#\<でなければなりません。 この手続きは、見ているマークアップトークンがどの種類のものか見当を つけるに十分な程度、入力ストリームをスキャンします。 この手続きは、そのトークンを表現するxml-token構造を返します。 通常、その時点のマークアップの読み込みは完了していないことに注意して下さい。 特に、開始タグトークンの属性はスキャンされていません。

特定の値が返されたときの戻り値とportでの位置を詳細に説明します。

PI-token

PIターゲットのみが読み込まれました。 処理命令の読み込みを完了してそれを無視するためには、ssax:skip-piを呼びます。 (PIの内容が、属性-値のペアの場合は、)ssax:read-attributesも 便利です。

END-token

終了タグが完全に読み込まれました。 現在の位置は、終了の#\>文字の直後です。

COMMENT

コメントが完全に読み込まれスキップされました。 現在の位置は、コメントが終了する“-->”の直後です。

CDSECT

現在の位置は、"<!CDATA["の直後です。 残りを読むためには、ssax:read-cdata-bodyを使います。

DECL

この宣言マークアップを識別するキーワード(“<!”に続くもの)を 読み込んだところです。現在の位置は、(通常は空白文字である) そのキーワードの直後です。

START-token

この開始タグのキーワード(GI)を読み込んだところです。 属性はまだスキャンされていません。 また、このタグが空の要素を持つかどうかも分かりません。 このトークンのパージングを終了するためには、 ssax:complete-start-tagを使います。

Function: ssax:skip-pi port

{sxml.ssax} 現在の位置は、PIの内側です。 PIの残りをスキップします。

Function: ssax:read-pi-body-as-string port

{sxml.ssax} 現在の位置は、PITargetを読み込んだ直後です。 PIのボディを読み込んで、それを文字列として返します。 ポートでは、PIを終了する’?>’の直後の文字を指します。

 [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
Function: ssax:skip-internal-dtd port

{sxml.ssax} ポートでの現在の位置は、内部DTDサブセットの内側です (例えば、内部DTDサブセットの始まりである#\[ を読み込んだところ)。 このDTDを終了する、組み合わせとなる“]>”までをスキップします。

Function: ssax:read-cdata-body port str-handler seed

{sxml.ssax} この手続きは、CDATAセクションを開始する文字列、"<![CDATA["を 読み込んだ後に呼ばれなければなりません。 現在の位置は、CDATAのボディの最初の位置です。 この手続きは、CDATAのボディのデータを読み込み、それらを STR-HANDLER(文字データのコンシューマ)へ渡します。

str-handlerは、string1 string2 seedを取る手続き STR-HANDLERです。 STR-HANDLERの最初の引数string1は、改行を含みません。 2番目の引数string2は、改行を含むことがよくあります。 STR-HANDLERの最初の呼び出しでは、seedはssax:read-cdata-bodyの 第3引数として渡されるものです。 この最初の呼び出しの結果は、文字データのコンシューマの引数seedとして渡され、 以降同じように続きます。 STR-HANDLERの最後の呼び出しの結果は、ssax:read-cdata-body から返されるものです。 基本的な’fold’イテレータに似ています。

CDATAセクションでは、以下の3つだけの例外を除いて、全ての文字は その表面上の値を持ちます。

  • CRLFCRLFは行区切りとして扱われ、STR-HANDLERへは 1つの#\newlineとして渡されます。
  • 組み合わせとなる“]]>”は、CDATAセクションの終わりであると されます。
  • &gt;は、#\>文字の埋め込みとして扱われます。 &lt;&amp;は特別なものとして認識されない(よって展開されない)ことに 注意が必要です!
Function: ssax:read-char-ref port

{sxml.ssax}

 [66]  CharRef ::=  '&#' [0-9]+ ';'
                  | '&#x' [0-9a-fA-F]+ ';'

この手続きは、文字参照を表す“&#”を読み込んだ後に呼ばれなければ なりません。 この手続きは、この参照を読み込んで対応する文字を返します。 portでの現在の位置は、文字参照の終わりとなる“;”の後と なります。 WFC: XML-Spec.html#wf-Legalcharも参照のこと。

XML勧告のセクション“4.1 文字と実体参照”によると、

“[定義: 文字参照は、ISO/IEC 10646文字集合にある特定の文字を参照する。 例えば、利用できる入力デバイスからは直接アクセスできないものなど。]”

したがって、入力ストリームの現在の文字エンコーディングに関係なく、 文字コードを文字に変換するために関数ucscode->charを使います。

Function: ssax:handle-parsed-entity port name entities content-handler str-handler seed

{sxml.ssax} 解析済み実体参照を展開し処理します。

  • port - ポート
  • name - 展開する解析済み実体の名前。シンボル。
  • entities - ENTITIESを参照。
  • content-handler - port entities seedを取る手続きで、 seedを返す。
  • str-handler - STR-HANDLER。対象となる実体が宣言済み実体となった 場合に呼ばれる。

戻り値は、content-handlerstr-handlerから返された値です。

こちらも参照のこと。

  WFC: XML-Spec.html#wf-entdeclared
  WFC: XML-Spec.html#norecursion
Function: ssax:read-attributes port entities

{sxml.ssax} この手続きは、Attribute*を読み込みパーズします。

 [41] Attribute ::= Name Eq AttValue
 [10] AttValue ::=  '"' ([^<&"] | Reference)* '"'
                 | "'" ([^<&'] | Reference)* "'"
 [25] Eq ::= S? '=' S?

この手続きは、Name(UNRES-NAME)とValue(文字列)のペアである ATTLISTを返します。 portでの現在の文字は、NCNameの開始文字ではなく、空白ではない文字です。

’AttValue’を読み込むときには、以下のルールに留意して下さい。 “属性の値がアプリケーションに渡されるか妥当性が検査される前に、 XMLプロセッサはそれを以下のように正規化しなければならない:

  • 文字参照は、属性値に参照された文字を追加することで処理される。
  • 実体参照は、その実体のテキストの置換を再帰的に行うことにより 処理される。[ENTITIES参照] [名前付きのエンティティ、amp lt gt quot aposは定義済みと想定される]
  • 空白文字(#x20#xD#xA#x9)は、 外部解析済み実体か内部解析済み実体のリテラルの実体の値の一部である “#xD#xA”のシーケンスにただ1つの#x20が追加されることを 除いて、正規化された値に#x20を追加することで処理される。
  • 他の文字は、正規化された値をそれらに追加することにより処理される”

こちらも参照のこと。

 WFC: XML-Spec.html#CleanAttrVals
 WFC: XML-Spec.html#uniqattspec
Function: ssax:resolve-name port unres-name namespaces apply-default-ns?

{sxml.ssax} 与えられた適切なnamespacesの宣言を用いて、unres-nameres-nameに変換する。 最後の引数apply-default-ns?は、デフォルト名前空間の適用を行うか どうかを決めます(例えば、属性名には適用しないなど)。

REC-xml-names/#nsc-NSDeclaredによれば、接頭辞“xml”は 名前空間名“http://www.w3.org/XML/1998/namespace”に定義済みで束縛されていると されます。

この手続きは、名前空間の制約をテストします: http://www.w3.org/TR/REC-xml-names/#nsc-NSDeclared

Function: ssax:uri-string->symbol uri-str

{sxml.ssax} uri-strを適切なシンボルに変換します。

Function: ssax:complete-start-tag tag port elems entities namespaces

{sxml.ssax} この手続きは、開始タグのマークアップのパージングを完了するためのものです。 この手続きは、開始タグトークンが読み込まれた後に呼ばれなければなりません。 tagUNRES-NAMEです。 elemsxml-decl::elemsのインスタンスで、 手続きに、要素とそれらの属性の妥当性検査を行わないように 指示するために、#fを指定することができます。

この手続きはいくつかの値を返します。

elem-gi

RES-NAME

attributes

要素の属性。(res-name . string)というペアのATTLIST。 このリストは、xmlns属性を含みません

namespaces

パージング中の開始タグに含まれる名前空間(再)宣言により修正された後の 名前空間の入力リスト。

終了時のportでの現在の位置は、開始タグのマークアップを終了する #\>の後になります。

こちらも参照のこと。

 VC: XML-Spec.html#enum
 VC: XML-Spec.html#RequiredAttr
 VC: XML-Spec.html#FixedAttr
 VC: XML-Spec.html#ValueType
 WFC: XML-Spec.html#uniqattspec (after namespaces prefixes are resolved)
 VC: XML-Spec.html#elementvalid
 WFC: REC-xml-names/#dt-NSName

XML勧告では明示されていませんが、xmlnsxmlns:属性は、 (そのデフォルト値を指定するために宣言されることが出来ますが) 宣言される必要がないことに注意して下さい。

Function: ssax:read-external-id port

{sxml.ssax} この手続きは、ExternalIDをパーズします。

 [75] ExternalID ::= 'SYSTEM' S SystemLiteral
                 | 'PUBLIC' S PubidLiteral S SystemLiteral
 [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
 [12] PubidLiteral ::=  '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
 [13] PubidChar ::=  #x20 | #xD | #xA | [a-zA-Z0-9]
                | [-'()+,./:=?;!*#@$_%]

この手続きは、ExternalIDが期待されるところで呼ばれます。 つまり、現在の文字は、それぞれSYSTEMPUBLICトークンを開始する #\S#\Pでなければなりません。 この手続きは、SystemLiteralを文字列として返します。 PubidLiteralは、存在したとしても無視されます。


12.59.3 SSAXの高レベルのパーザとスキャナ

これらは、全体的な実体(ドキュメント)あるいはその高レベルな断片 (プロローグ、ルート要素など)をパーズします。

Function: ssax:scan-Misc port

{sxml.ssax} そのコンテキストでのMiscをスキャンします。

[1]  document ::=  prolog element Misc*
[22] prolog ::= XMLDecl? Misc* (doctypedec l Misc*)?
[27] Misc ::= Comment | PI |  S

以下の関数は、プロローグかエピローグのコンテキストで呼ばれます。 これらのコンテキストでは、空白文字は完全に無視されます。 ssax:scan-Miscからの戻り値は、PIトークンかDECLトークン、 STARTトークン、EOFのいずれかです。 コメントは無視され報告されません。

Function: ssax:read-char-data port expect-eof? str-handler seed

{sxml.ssax} この手続きは、XML文書かXML要素の文字内容を読むためのものです。

 [43] content ::=
        (element | CharData | Reference | CDSect | PI
         | Comment)*

具体的には、この手続きはCharDataを読み込み、CDSectと 文字実体を展開し、コメントをスキップします。 この手続きは、名前付き参照、EOF、PIあるいは開始/終了タグの開始地点で 停止します。

port

読み込むポート。

expect-eof?

EOFがノーマルかどうか、つまり、文字データがEOFで終わるかどうかを 表す真偽値。解析済み実体を処理している間はEOFはノーマル。

str-handler

STR-HANDLER

seed

STR-HANDLERの最初の呼び出し時に渡される引数。

この手続きは2つの結果、seedtokenを返します。

seedstr-handlerの最後の呼び出しの結果、あるいは str-handlerが一度も呼ばれなかった場合はオリジナルのseedです。

tokenはEOFオブジェクト(これはexpect-eof?#tの場合のみ)か、

  • STARTタグかENDタグを表すxml-token。 開始トークンの場合は、呼び出し側が読み込みを完了する必要がある。
  • PIの開始を表すxml-token。 このPIの残りを読み込むかスキップするかはアプリケーションに 任される。
  • 名前付き実体参照を表すxml-token。

CDATAセクションと文字参照はインラインで展開され返されません。 コメントは無視されます。

XML勧告が要求するように、文字データ中の全ての空白文字は保存されなければなりません。 しかし、CR文字(#xD)は、LF文字(#A)の前に現れるか #xA文字で置き換えられた場合は、無視されなければなりません。 XML勧告のセクション2.10と2.11を参照して下さい。 また、正規のXML勧告も参照して下さい。

Function: ssax:assert-token token kind gi error-cont

{sxml.ssax} tokenが、予想されたkindのもので、予想されたgiを 持つことを確認します。gi引数は、実際には2つのシンボル、 名前空間URIかその接頭辞と、そのローカル名のペアでしょう。 アサーションが失敗したら、error-contに3つの引数、token kind gi を渡されて評価されます。 error-contの結果が返されます。


12.59.4 SSAXの高レベルのパーザ - XMLからSXMLへ

これらのパーザは、SSAXパーザをインスタンス化するための構文的フォームのセットです。 ユーザは、完全な妥当性検査、妥当性検査なし、特定の妥当性検査を行うために このパーザをインスタンス化できます。 ユーザは、どのPIについて通知されたいかを指定します。 ユーザは、解析済み文字と要素のデータで何をしたいかを知らせます。 後者のハンドラは、パージングがSAXやDOMモデルに従うかを決定します。

Macro: ssax:make-pi-parser my-pi-handlers

{sxml.ssax} 1つの処理命令(PI)をパーズして処理するパーザを作ります。

my-pi-handlers: (PI-TAG . PI-HANDLER)のペアの連想リスト。 PI-TAGNCNameのシンボル、PIターゲット。 PI-HANDLERport pi-tag seedを引数とする手続きで、 portではPIターゲットの後の最初のシンボルを指しています。 ハンドラは、PIを終了する組み合わせとなる’?>’を含む、 PIの残りを読み込みます。ハンドラは新しいseedを返します。 PI-TAGの1つは、シンボル*DEFAULT*でしょう。 これに対応するハンドラは、他のハンドラが扱わないPIを処理します。 *DEFAULT* PI-TAGが指定されていない場合は、 ssax:make-pi-parserは、PIのボディをスキップするパーザを 作ります。

ssax:make-pi-parserが返すのは、port pi-tag seedを 取る手続きで、ユーザ指定のハンドラに従い現在のPIをパーズします。

Macro: ssax:make-elem-parser my-new-level-seed my-finish-element my-char-data-handler my-pi-handlers

{sxml.ssax} その文字内容や子要素をも含む1つの要素をパーズし処理するパーザを作ります。 このパーザは通常、ドキュメントのルート要素の適用されます。

my-new-level-seed

elem-gi attributes namespaces expected-content seed
を引数に取る手続きで、elem-giは処理されようとしている要素のRES-NAMEです。 この手続きは、要素の内容を処理するハンドラに渡されるseedを生成します。

my-finish-element

elem-gi attributes namespaces parent-seed seed
を引数に取る手続きです。この手続きは、elem-giのパージングが完了した時に 呼ばれます。 seedは、最後に呼ばれたパーザからの(あるいは、 要素が空要素であった場合は、my-new-level-seedからの)結果です。 parent-seedは、my-new-level-seedへ渡されたのと同じseedです。 この手続きは、パーザの結果となるseedを生成するためのものです。

my-char-data-handler

STR-HANDLER

my-pi-handlers

ssax:make-pi-handlerを参照して下さい。

生成されたパーザは: start-tag-head port elems entities namespaces preserve-ws? seed
を引数に取る手続きです。 この手続きは、開始タグのトークンが読み込まれた後に呼ばれなければなりません。 start-tag-headは要素の開始タグのUNRES-NAMEです。 elemsxml-decl::elemsのインスタンスです。 ssax:complete-start-tag::preserve-ws?も参照して下さい。

こちらも参照のこと。

 VC: XML-Spec.html#elementvalid
 WFC: XML-Spec.html#GIMatch
Macro: ssax:make-parser user-handler-tag user-handler-proc …

{sxml.ssax} XMLパージングフレームワークのインスタンスである、XMLパーザを作ります。 これは、提供されるユーザハンドラによって、SAX、DOM、あるいは特化された パーザになります。

user-handler-tagはシンボルで、タグに続く手続き的な式を識別します。 以下にタグと対応する手続きのシグネチャを示します。 全てのタグが指定される必要はありません。 いくつかが省略されると、合理的なデフォルトのものが適用されます。

tag: DOCTYPE

ハンドラ手続きの引数: port docname systemid internal-subset? seed

internal-subset?#tなら、ポートでの現在の位置は内部DTDサブセットの 開始となる#\[を読んだ直後です。 手続きから戻る前に、このサブセットの残りの読み込みを完了しなければなりません (あるいは、それを読むことに興味がなければ、skip-internal-subsetを呼ばなければなりません)。 終了時のポートでの位置は、DOCTYPE宣言全体のあとの最初のシンボルでなければなりません。

ハンドラ手続きは4つの値:
elems entities namespaces seed
を生成しなければなりません。 elemsについては、xml-decl::elemsを参照して下さい。 妥当性検査をオフにするためには、#fになるでしょう。 namespacesは、通常、選択されたURI-SYMBに対してUSER-PREFIXを含む でしょう。 デフォルトのハンドラ手続きは、内部サブセットがあってもそれをスキップし、 (values #f '() '() seed)を返します。

tag: UNDECL-ROOT

ハンドラ手続きの引数: elem-gi seed
elem-giはルート要素のUNRES-NAMEです。 この手続きは、パージング中のXML文書がDOCTYPE宣言を含まない時に 呼ばれます。 ハンドラ手続きは、上ではDOCTYPEハンドラですが、4つの値:
elems entities namespaces seed
を生成しなければなりません。 デフォルトのハンドラ手続きは、(values #f '() '() seed)を返します。

tag: DECL-ROOT

ハンドラ手続きの引数: elem-gi seed
elem-giは、ルート要素のUNRES-NAMEです。 この手続きは、パージング中のXML文書がDOCTYPE宣言を含む場合に呼ばれます。 このハンドラ手続きは、新しいseedを生成しなければなりません (そして、ハンドラが望めば、ルート要素の名前がDOCTYPEにマッチするかを 検証します)。 デフォルトのハンドラ手続きは、それ自身を返す手続きです。

tag: NEW-LEVEL-SEED

ハンドラ手続きの引数: ssax:make-elem-parsermy-new-level-seedを参照して下さい。

tag: FINISH-ELEMENT

ハンドラ手続きの引数: ssax:make-elem-parsermy-finish-elementを参照して下さい。

tag: CHAR-DATA-HANDLER

ハンドラ手続きの引数: ssax:make-elem-parsermy-char-data-handlerを参照して下さい。

tag: PI

ハンドラ手続きの引数: ssax:make-pi-parserを参照して下さい。
デフォルトの値は、'()です。

生成されるパーザは、
PORT SEEDを取る手続き、
です。

この手続きは、ドキュメントのプロローグをパーズして、 その残りを処理するために(ssax:make-elem-parserで作られた)パーザへ 引き継いで終了します。

 [1]  document ::=  prolog element Misc*
 [22] prolog ::= XMLDecl? Misc* (doctypedec | Misc*)?
 [27] Misc ::= Comment | PI |  S

 [28] doctypedecl ::=  '<!DOCTYPE' S Name (S ExternalID)? S?
                        ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
 [29] markupdecl ::= elementdecl | AttlistDecl
                      | EntityDecl
                      | NotationDecl | PI
                      | Comment

いくつかの便利なユーティリティ手続きがあります。

Function: ssax:reverse-collect-str fragments

{sxml.ssax} fragments(そのいくつかはテキスト文字列)のリストを渡すと、 そのリストを逆順にして隣り合ったテキスト文字列を連結します。

Function: ssax:reverse-collect-str-drop-ws fragments

{sxml.ssax} fragments(そのいくつかはテキスト文字列)のリストを渡すと、 そのリストを逆順にして隣り合ったテキスト文字列を連結します。 “重要でない”空白文字、つまり、最初や最後、要素の間にある空白文字を 削除します。文字データに含まれる空白文字には影響を与えません。 この手続きは、パーズされたSXMLにある“重要でない”空白文字を “知的に”削除するために使います。空白文字に関して、厳密に XML勧告に準拠したい場合は、代わりに手続き ssax:reverse-collect-strを使って下さい。

Function: ssax:xml->sxml port namespace-prefix-assig

{sxml.ssax} これは、上のSSAXパーザのインスタンスで、portから読み込まれる XMLドキュメントのSXML表現を返します。 namespace-prefix-assigは、(USER-PREFIX . URI-STRING) のリストで、特定のURI-STRINGで識別されるある名前空間を USER-PREFIXに割り当てます。これは空リストでも構いません。 この手続きは、SXMLツリーを返します。 ポートでの位置は、ルート要素の後の最初の文字を指します。

簡単な例を示します:

(call-with-input-string
  "<book>
     <title>Land of Lisp</title>
     <author>Conrad Barski</author>
      <publisher>No Starch Press</publisher>
   </book>
   <book>
     <title>Programming Gauche</title>
     <author>Kahua Project</author>
     <author>Shiro Kawai</author>
     <publisher>O'Reilly Japan</publisher>
   </book>"
  (^p (ssax:xml->sxml p '())))
⇒
  (*TOP*
    (book (title "Land of Lisp")
          (author "Conrad Barski")
          (publisher "No Starch Press"))
    (book (title "Programming Gauche")
          (author "Kahua Project")
          (author "Shiro Kawai")
          (publisher "O'Reilly Japan")))

ドキュメントは複数のトップレベルノードを持っている場合があるので、 ドキュメント全体は擬似ノード*TOP*の下にまとめられます。 *TOP*ノードはまたメタ情報を保持するのにも使われます。 次の例ではXML宣言が*PI* (processing infomation) ノードとして パーズされています。

(call-with-input-string
  "<?xml version=\"1.0\" encoding=\"utf-8\"?>
   <book>
     <title>Programming Gauche</title>
     <author>Kahua Project</author>
     <author>Shiro Kawai</author>
     <publisher>O'Reilly Japan</publisher>
   </book>"
  (^p (ssax:xml->sxml p '())))
⇒
  (*TOP*
    (*PI* xml "version=\"1.0\" encoding=\"utf-8\"")
    (book (title "Programming Gauche") (author "Kahua Project")
          (author "Shiro Kawai") (publisher "O'Reilly Japan")))

名前空間も認識されます。デフォルトでは名前空間のエイリアスは全て 完全なURIへと展開されます:

(call-with-input-string
  "<b:book xmlns:b=\"https://example.com/book/\">
     <b:title>Programming Gauche</b:title>
     <b:author>Kahua Project</b:author>
     <b:author>Shiro Kawai</b:author>
     <b:publisher>O'Reilly Japan</b:publisher>
   </b:book>"
  (^p (ssax:xml->sxml p '())))
⇒
  (*TOP*
   (https://example.com/book/:book
    (https://example.com/book/:title "Programming Gauche")
    (https://example.com/book/:author "Kahua Project")
    (https://example.com/book/:author "Shiro Kawai")
    (https://example.com/book/:publisher "O'Reilly Japan")))

(名前空間エイリアスは入れ子になったスコープを持つため、 ただエイリアスをそのまま保持するだけでは情報が失われるためです。)

ただ、完全なURIに展開された名前空間プレフィクスは扱い辛いので、 独自の名前空間エイリアスをnamespace-prefix-assig引数で指定できます。

(call-with-input-string
  "<b:book xmlns:b=\"https://example.com/book/\">
     <b:title>Programming Gauche</b:title>
     <b:author>Kahua Project</b:author>
     <b:author>Shiro Kawai</b:author>
     <b:publisher>O'Reilly Japan</b:publisher>
   </b:book>"
  (^p (ssax:xml->sxml p '((Book . "https://example.com/book/")))))
⇒
  (*TOP*
   (@ (*NAMESPACES* (Book "https://example.com/book/")))
   (Book:book (Book:title "Programming Gauche")
              (Book:author "Kahua Project")
              (Book:author "Shiro Kawai")
              (Book:publisher "O'Reilly Japan")))


For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT