¬<><∪∪では、字句解析(テキストをトークンの列に変換する処理)と、構文解析(トークンの列を構文木に変換する処理)の両方を1つのファイルに記述します。¬<><∪∪は、このファイルから、1つのJavaソースファイルを出力します。
ファイルの先頭に、出力されるJavaのクラスの名前を記述します。
$parser packageName.Parser; : :
$token INTEGER = ('0'..'9')+ ;
$token に続けてトークンを定義します。この例では、'0'~'9'が1文字以上つながったものは、INTEGERという種類のトークンに変換されます。
$token IDENTIFIER = IDENTIFIER_START IDENTIFIER_PART* ; $subtoken IDENTIFIER_START = 'a'..'z' | 'A'..'Z' ; $subtoken IDENTIFIER_PART = '0'..'9' | IDENTIFIER_START ;
$subtoken は、等号の右辺の式に名前を付けて、$token や $subtoken の中で参照できるようにします。この例では、アルファベット1文字で始まり、アルファベットか数字が0文字以上続いたものが、IDENTIFIERという種類のトークンに変換されます。
再帰的には使用できません。
$white $token WHITE_SPACES = WHITE_SPACE+ ; $subtoken WHITE_SPACE = ' ' | '\t' | '\f' | LINE_TERMINATOR ; $subtoken LINE_TERMINATOR = "\n" | "\r" | "\r\n" ;
$white は、空白やコメントなどの、無視されるトークンを定義します。"\r\n" は '\r' '\n' と同じ意味です。"\n" は '\n' と同じ意味です。
$subtoken ANY_CHARACTER = '\u0000'..'\uFFFF' ; $subtoken COMMENT_LONGEST_MATCH = "/*" ANY_CHARACTER* "*/" ; $token COMMENT = COMMENT_LONGEST_MATCH - ( COMMENT_LONGEST_MATCH ANY_CHARACTER+ ) ;
トークンへの変換は常に最長一致で行われます。CやJavaのコメントのように最短一致でマッチさせたい場合、上のように記述します。ここで - は、左の正規表現にマッチして、右の正規表現にマッチしない文字列を表します。これによって、COMMENT は途中に "*/" を含むようなものにはマッチしなくなります。
Substitution { identifier:IDENTIFIER "=" number:Number ";" }
Number { integer:INTEGER ";" }
BNFの「Substitution ::= IDENTIFIER "=" Number ";"」「Number ::= INTEGER」と同様にプロダクションを定義します。また、同時に次のようなインターフェイスを出力し、構文木のノードをこのインターフェイスのインスタンスで表します。: は、右側の記号を左側の識別子でラベル付けします。identifier() は IDENTIFIER に対応するトークンを返し、number() は Number を返します。
interface Substitution { Token identifier(); Number number(); }
interface Number { Token integer(); }
$parsable A { ... }
$parsableと指定することで、A が表す構文を解析するメソッド(parseA)が生成されます。
A { integers:INTEGER+ }
B { integers:INTEGER ( "," integers:INTEGER )* }
interface A { List integers(); }
interface B { List integers(); }
正規表現を利用できます(ただし、トークンの定義とは異なり、-は使用できません)。ラベル付けされる記号が複数になりうる場合、戻り値はリストになります。integers() は INTEGER に対応するトークンのリストを返します。
A { ... }
B { ... }
C -> A & B { ... }
出力されるクラスに継承関係を設定できます。
interface A { ... }
interface B { ... }
interface C extends A, B { ... }
Foo { bar }
bar = A B label:C ;
トークンの定義における$subtokenと同様に、右辺の式に名前を付けて、参照できるようにします。上の例は、次と同じです。
Foo { A B label:C }
Foo { label2:bar }
bar = A B label:C ;
エイリアスに含まれるすべての記号をラベル付けします。
Foo { label2:A label2:B label2:label:C }
C は、labelとlabel2の両方でラベル付けされます。
ただし、$label が使用されている場合、$label がラベル付けしている記号だけをラベル付けします。
Foo { label2:bar }
bar = A $label:B label:C ;
次と同じです。
Foo { A label2:B label:C }
エイリアスは再帰的に使用できます。これを利用すると、算術式を次のように記述できます。
Expression { }
expression = Addition
| term
;
Addition -> Expression
{ operand1:expression "+" operand2:term }
term = Multiplication
| factor
;
Multiplication -> Expression
{ operand1:term "*" operand2:factor }
factor = parenthesizedExpression
| Number
;
Number -> Expression { value:INTEGER }
parenthesizedExpression
= "(" $label:expression ")"
;
エイリアスを展開すると、Multiplicationは次のようになります。
Multiplication -> Expression
{ operand1:Multiplication "*" operand2:Number
| operand1:Multiplication "*" "(" operand2:Addition ")"
| operand1:Multiplication "*" "(" operand2:Multiplication ")"
| operand1:Multiplication "*" "(" operand2:Number ")"
| operand1:Multiplication "*" "(" "(" operand2:Addition ")" ")"
| operand1:Multiplication "*" "(" "(" operand2:Multiplication ")" ")"
| operand1:Multiplication "*" "(" "(" operand2:Number ")" ")"
| operand1:Multiplication "*" "(" "(" "(" operand2:Addition ")" ")" ")"
| :
| :
| operand1:Number "*" operand2:Number
| operand1:Number "*" "(" operand2:Addition ")"
| operand1:Number "*" "(" operand2:Multiplication ")"
| operand1:Number "*" "(" operand2:Number ")"
| operand1:Number "*" "(" "(" operand2:Addition ")" ")"
| operand1:Number "*" "(" "(" operand2:Multiplication ")" ")"
| operand1:Number "*" "(" "(" operand2:Number ")" ")"
| operand1:Number "*" "(" "(" "(" operand2:Addition ")" ")" ")"
| :
| :
| "(" operand1:Addition ")" "*" operand2:Number
| "(" operand1:Addition ")" "*" "(" operand2:Addition ")"
| "(" operand1:Addition ")" "*" "(" operand2:Multiplication ")"
| "(" operand1:Addition ")" "*" "(" operand2:Number ")"
| "(" operand1:Addition ")" "*" "(" "(" operand2:Addition ")" ")"
| "(" operand1:Addition ")" "*" "(" "(" operand2:Multiplication ")" ")"
| "(" operand1:Addition ")" "*" "(" "(" operand2:Number ")" ")"
| "(" operand1:Addition ")" "*" "(" "(" "(" operand2:Addition ")" ")" ")"
| :
| :
| "(" operand1:Multiplication ")" "*" operand2:Number
| "(" operand1:Multiplication ")" "*" "(" operand2:Addition ")"
| "(" operand1:Multiplication ")" "*" "(" operand2:Multiplication ")"
| "(" operand1:Multiplication ")" "*" "(" operand2:Number ")"
| "(" operand1:Multiplication ")" "*" "(" "(" operand2:Addition ")" ")"
| "(" operand1:Multiplication ")" "*" "(" "(" operand2:Multiplication ")" ")"
| "(" operand1:Multiplication ")" "*" "(" "(" operand2:Number ")" ")"
| "(" operand1:Multiplication ")" "*" "(" "(" "(" operand2:Addition ")" ")" ")"
| :
| :
| "(" operand1:Number ")" "*" operand2:Number
| "(" operand1:Number ")" "*" "(" operand2:Addition ")"
| "(" operand1:Number ")" "*" "(" operand2:Multiplication ")"
| "(" operand1:Number ")" "*" "(" operand2:Number ")"
| "(" operand1:Number ")" "*" "(" "(" operand2:Addition ")" ")"
| "(" operand1:Number ")" "*" "(" "(" operand2:Multiplication ")" ")"
| "(" operand1:Number ")" "*" "(" "(" operand2:Number ")" ")"
| "(" operand1:Number ")" "*" "(" "(" "(" operand2:Addition ")" ")" ")"
| :
| :
| "(" "(" operand1:Addition ")" ")" "*" operand2:Number
| "(" "(" operand1:Addition ")" ")" "*" "(" operand2:Addition ")"
| "(" "(" operand1:Addition ")" ")" "*" "(" operand2:Multiplication ")"
| "(" "(" operand1:Addition ")" ")" "*" "(" operand2:Number ")"
| "(" "(" operand1:Addition ")" ")" "*" "(" "(" operand2:Addition ")" ")"
| "(" "(" operand1:Addition ")" ")" "*" "(" "(" operand2:Multiplication ")" ")"
| "(" "(" operand1:Addition ")" ")" "*" "(" "(" operand2:Number ")" ")"
| "(" "(" operand1:Addition ")" ")" "*" "(" "(" "(" operand2:Addition ")" ")" ")"
| :
| :
:
:
}
これによって、1*(2+3) は次の構文木で表されます。

¬<><∪∪は、いったんエイリアスもプロダクションとみなして解析木を作成した後、エイリアスに対応するノードを削除することによって構文木を得ます。¬<><∪∪は、1つの入力から複数の解析木を構築可能な文法を扱うことはできません。例えば次の文法を扱うことはできません。
Foo { bar | "A" }
bar = "A" ;
しかし、次の文法であれば扱えます。
Baz { "A" | "A" }
これは、次と同じです。
Baz { "A" }