# RelaxNGCC マニュアル

$Id: manual.htm,v 1.5 2002/06/26 13:08:48 kkawa Exp $
目次

- マークアップ

1. `c:alias`
2. `c:java`
3. `c:java-body`
4. `c:java-import`
5. `c:class`
6. `c:package`
7. `c:access`
8. `c:runtime-type`
9. `c:return-type/c:return-value`
10. `c:params/c:with-params`

- 生成したコードの使い方
- 制約事項
- 未サポート機能

## 1 マークアップ

RelaxNGCC独自のマークアップはすべて
`http://www.xml.gr.jp/xmlns/relaxngcc`
のnamespace-URIを持ちますが、このマニュアルではプレフィックス "`c`" を使用しています。マークアップは次に説明するように３種のエレメントと５種のアトリビュートから構成されます。

### 1.1 `c:alias`アトリビュート

ユーザ定義のコードからアクセスするために、データに名前をつけます。この変数名で、生成されたクラス内にフィールドが追加されます。このアトリビュートをつけることのできるRELAX NGのエレメントは、`data`, `text`, `ref`, `value`, `list`の５つです。

    <data type="nonNegativeInteger" c:alias="count"/>

`ref`/`parentRef`パターンに`c:alias`をつけた場合、それらが参照する先のスコープで戻り値として指定された値が得られます（一般には、これはスコープに対応するRelaxNGCCが生成したクラスそのものです）。

`list`パターンに`c:alias`をつけたときにはリスト全体が１個の文字列として取り出されます。

### 1.2 `c:java`エレメント

ユーザ定義のJavaコードを記述します。コードから`c:alias`アトリビュートで定義した名前を使うことができます。記述したコードは、入力XMLの該当個所を読んだところで実行されます。

    <element name="name">
      <text c:alias="name"/>
      <c:java>System.out.println(name);</c:java>
    </element>

生成されたコードは最終的にはSAX2ベースの処理になるので、外に投げることのできる例外はSAXExceptionとその派生クラスに限られます。

`java`エレメントは`choice, interleave`以外の子パターンをとる全てのパターンの中に書けます（`start, define, group, optional, zeroOrMore, oneOrMore, mixed, list`パターンの中）。

なおエレメント名に`java`を使っている理由は、将来他のプログラム言語（例えばC#）にも対応するかもしれないからです。

### 1.3 `java-body`エレメント

生成されるクラスについて、補助的に使用するメソッドやデータメンバを記述します。ここで定義したものはjavaエレメント内のコードからアクセスできます。

    <define name="x">
      <c:java-body>
      private void echo(String msg) {
          System.out.println(msg);
      }
      </c:java-body>
      ...
    </define>

たとえばこのようにすると、この`define`ブロックの`java`エレメント内から`echo`メソッドが使えるようになります。

`java-body`エレメントは、次の場所に書くことができます。

a. `grammar`エレメントの直下
  RelaxNGCCが生成するすべてのクラスのコードに、java-body内のコードが挿入されます。
b. `start`, `define`エレメントの直下
  その`start/define`エレメントに対応して生成されるクラスのコードにのみ挿入されます。

### 1.4 `java-import`エレメント

`java-body`と違い、ソースコード中のクラス定義本体の外に記述するコードを記述します。ふつうJavaのimport宣言を記述します。これを置くことのできる位置と効果はjava-bodyと同じです。

    <c:java-import>
        import java.util.Set;
        import java.util.Iterator;
    </c:java-import>

### 1.5 `c:class`アトリビュート

`start`エレメント、`define`エレメントに記述して、それに対応するクラスの名前を指定します。Javaのクラス名として正しい文字列を指定する必要があります。省略時には、RelaxNGCCが適当な名前をつけてソースコードを生成します。

    <start c:class="Root">
    ...
    </start>

### 1.6 `c:package`アトリビュート

生成するソースコードがどのパッケージに属すかを指定します。ルートエレメントに記入します。この宣言は生成されるすべてのクラスに対して有効です。

    <grammar ... c:package="com.example.project1">
    ...

### 1.7 `c:access`アトリビュート

生成するソースコードのアクセス修飾子("public final"など)を指定します。このアトリビュートは`define`エレメント、`start`エレメント、`class`アトリビュートを書いたエレメントに書くことができます。

    <start c:class="sample1" c:access="public final">
    ...
    </start>

### 1.8 `c:runtime-type`アトリビュート

標準の`NGCCRuntime`クラスの代わりに、この属性で指定されたユーザー定義のクラスを使うようにコンパイラに指示します。

この属性は、RELAX NG文法ファイルのルート要素でのみ指定可能です。

    <?xml version="1.0"?>
    <grammar c:runtime-type="org.acme.foo.MyNGCCRuntime" ...>
      ....
    </grammar>

### 1.9 `c:return-type/c:return-value`アトリビュート

これらの属性を指定することで、生成されたハンドラクラスからの戻り値を指定できるようになります。これらの属性は、どちらも<define>か<start>パターンに対して指定可能です。

`c:return-value`属性には、戻り値として評価される式を書きます。子ハンドラが処理を終了して親ハンドラに戻る際に、この式が評価されて結果が親ハンドラに返ります。親ハンドラ側では、`c:alias`属性を使って戻り値にアクセスすることができます。`c:return-type`属性は、この戻り値の型を指定します。ディフォルトでは、`c:return-value`は"this"になっているので、ハンドラ自身が返されます。

次の例では、`makeResult`関数が呼び出されて、その値が戻り値となります。

    <define name="foo" c:return-type="String" c:return-value="makeResult()">
      <c:java-body>
        private String makeResult() {
          ....
        }
      </c:java-body>
      ...
    </define>

    ...

        <ref name="foo" c:alias="someStringVariable">

### 1.10 `c:params/c:with-params`アトリビュート

これらの属性は対で利用され、親ハンドラから子ハンドラへパラメータを渡すのに使われます。

`c:params`属性は、`<define>`か`<start>`パターンに対して指定でき、パラメータを宣言します。一方、`c:with-params`属性は、パラメータつきで宣言された`<define>`を参照する`<ref>`パターンに対して指定します。

`c:params`属性の値は、カンマ区切りの型-変数名ペアで、ちょうど関数を宣言する時のパラメータリストと同じ書式です。`c:with-params`は、同じくカンマ区切りの式の並びで、やはりちょうど関数を呼び出す時と同じ書式です。

あるブロックを`c:params`付きで宣言すると、そのブロックを参照する全ての`<ref/>`パターンに`c:with-params`が必要になるので注意してください。

渡されたパラメータは、同名で生成されたフィールド変数にコピーされるので、`<c:java-body>`や`<c:java>`の中からアクセスできます。

    <define name="foo" c:params="String a,boolean b,Object c">
      ...
      <c:java>
        System.out.println(a);
      </c:java>
    </define>

    ...

       <ref name="foo" c:with-params='"xyz",true,null' />

    ...

       <ref name="foo" c:with-params='"test",false,System.out' />

## 2 生成したコードの使い方

### (このセクションはJAXPの知識を仮定しています。)

RelaxNGCCが生成したコードをコンパイルしたり実行するには、JAXP対応のXMLパーサが必要です

生成したコードは、SAX2イベントを生成するあらゆるコンポーネントと組み合わせて利用可能です。SAX2イベントから生成されたコードを駆動する方法については、もとの文法の`start`エレメントに対応したクラスに付加される`main()`関数を参照してください。

### コンストラクタのカスタマイズ

RelaxNGCCはコンストラクタを自分で生成するため、そのままではコンストラクタを書き換えることはできません。オブジェクトの生成時の処理を追加するには、イニシャライザを利用してください。

    <define name="foo">
      <c:java-body>
        {// add your code here
            System.out.println("initializer");
        }
      </c:java-body>

## 3 制約事項

RelaxNGCCでは、どんな文法も読み込めるわけではなく、先読み１個で分岐を判断できる文法だけを受け付けます。

     <choice>
       <group>
         <element name="a"><text/></element>
         <c:java>System.out.println("a0");</c:java>
         <element name="x"><text/></element>
       </group>
       <group>
         <element name="a"><text/></element>
         <c:java>System.out.println("a1");</c:java>
         <element name="y"><text/></element>
       </group>
     </choice>

このようなとき、最初に`a`エレメントをみただけではどちらのchoiceが正しいのかは判定できません。先読みをすれば`x`または`y`がきて判定可能になりますが、RelaxNGCCはこのような判定はできません。次のように書き換える必要があります。

    <group>
     <element name="a"><text/></element>
     <choice>
       <group>
         <element name="x"><text/></element>
         <c:java>System.out.println("a0");</c:java>
       </group>
       <group>
         <element name="y"><text/></element>
         <c:java>System.out.println("a1");</c:java>
       </group>
     </choice>
    </group>

文法がこの制約を満たさないとき、RelaxNGCCは警告メッセージを発します。特に文法があいまいな場合にはどう変形してもこの制約を逃れることができなくなるので注意が必要です。

情報科学の言葉で言えば、`define`や`start`ブロックを、SAXイベントを終端記号とした文脈自由文法として解釈したとき、それがLL(1)であるときに限りRelaxNGCCは取り扱えます。このあたりの議論はコンパイラの教科書に詳しく載っています。

## 4 未サポート機能

RELAX NGの仕様のうち、次の機能はまだサポートされていません。将来のバージョンで徐々に実装するつもりです。

- `externalRef` エレメント
- name-classを使ったアトリビュート

RelaxNGCC Home