HABA 記述方法

独自拡張BNF解析(House Arranged BNF Analyzer、略して HABA)は文脈自由文法を扱います。文法は一般に終端記号、非終端記号、生成規則、開始記号の組で表されます。

生成規則

1つの生成規則は

規則名 ::= 規則の定義 ;

という形式で記述します。規則名は非終端記号 1 個です。規則の定義は終端記号と非終端記号の 0 個以上の組み合わせで構成されます。

生成規則が複数ある場合、その最初の規則の規則名を開始記号とします。例えば

Plus ::= Num '+' Num ;
Num ::= "[0-9]+" ;

という規則の並びがあった場合、開始記号は Plus になります。開始記号を間違うと意図した文法にならないので注意してください。

規則の定義に記述する記号は 0 個でもよいため、明示的にイプシロン遷移を指定することもできます。

Epsilon ::= ;

生成規則はフリーフォーマットです。ホワイトスペース(半角の空白、タブ、改行)は無視されます。

Plus ::= Num '+' Num ;

Plus::=Num'+'Num;

と書くことも

Plus
::= Num
    '+' Num
;

と書くことも可能で、いずれも同じ生成規則になります。

終端記号

2種類の書き方があります。

種類書き方
固定値単引用符で囲む'+'
正規表現二重引用符で囲む"[0-9]+"

正規表現には JavaScript の正規表現が全て使用できます。特殊記号をそのままの文字として使用したいときは、その直前に \ を置いてエスケープします。\ そのものを使用したいときもその前に \ を置いて \\ とします。ただし二重引用符の表記は \" ではなく、二重引用符を2つ重ねる "" となります。

固定値の中に単引用符を記述するときは単引用符を重ねて ' ' とします。固定値にはそれ以外に特殊な書き方はありません。固定値の中に二重引用符を書くときは " 1つだけでOKです。

非終端記号

非終端記号はアルファベットまたはアンダーバーで始まり、アルファベット、アンダーバー、数字が続く文字列です。アルファベットの大文字小文字は区別されます。

このページでは非終端記号は先頭を大文字に2文字目以降を小文字にしていますが、必ずしもそうする必要はありません。Name、name、NAME など、どれも有効な非終端記号です。

連結

2つ以上の記号が順番に登場する場合、それらの記号を続けて記述します。

Concatenation ::= Term1 Term2 ;

例えば「数字」「プラス記号」「数字」が順に登場する文字列を表す生成規則は

Plus ::= Num '+' Num ;

となります。非終端記号が続くときと、同じ種類の終端記号が続くときは、その間に1つ以上のホワイトスペースを置く必要があります。

選択

2つ以上の記号のいずれかが登場する場合、それらの記号を縦棒 | で繋いで記述します。

Alternatives ::= Term1 | Term2 ;

例えば四則演算子のいずれかを表す生成規則は

Operator ::= '+' | '-' | '*' | '/' ;

となります。

選択は次のように、規則名が同じ複数の生成規則に書き換えることができます。

Alternatives ::= Term1 ;
Alternatives ::= Term2 ;

なお、「書き換えることができる」とは受理する言語が同じという意味であって、構文解析の結果が全く同じというわけではありません。

量指定子

記号が登場してもしなくてもよい場合、あるいは任意回数の登場を許す場合に次のような量指定子を使用します。

記号意味
?0 回または 1 回
*0 回以上の繰り返し
+1 回以上の繰り返し

例えば正の数の先頭にプラス記号を付けても付けなくてもよいことを表す生成規則は

Positive ::= '+'? Absolute ;

で、1 個以上の単語からなる文を表す生成規則は

Sentence ::= Word+ ;

となります。

量指定子はそれぞれ次のように書き換えることができます。

// Expr ::= Term? ;
Expr ::= Term ;
Expr ::= ;

// Expr ::= Term* ;
Expr ::= Expr Term ;
Expr ::= ;

// Expr ::= Term+ ;
Expr ::= Expr Term ;
Expr ::= Term ;

グループ化

規則の定義における解釈の優先順位を変更する場合、記号列を丸括弧 ( ) で囲みます。グループ化をしない場合の優先順位は次の通りです。

順位要素
1量指定子Term*
2連結Term1 Term2
3選択Term1 | Term2

例えば

Expr ::= A | B C ;

という規則が「A または BC」を表すのに対して

Expr ::= (A | B) C ;

であれば「A または B」に C が続くもの、つまり「AC または BC」を表します。また、

Expr ::= D E+ ;

という規則が「DE、DEE、DEEE、…」を表すのに対して

Expr ::= (D E)+ ;

であれば「DE、DEDE、DEDEDE、…」を表します。

丸括弧を使用せず、その部分を別の生成規則として分離することもできます。

// Expr ::= (A | B) C ;
Expr ::= Term C ;
Term ::= A | B ;

// Expr ::= (D E)+ ;
Expr ::= Term+ ;
Term ::= D E ;

コメント、ダミー記号

生成規則の途中にコメントを記述することもできます。

// 1行コメント
/* 複数行に
渡る
コメント */

// から行末までが行コメント、/* から */ までがブロックコメントです。行コメントは1行に1つしか指定できず、複数行に渡ることもできません。一方でブロックコメントは複数行に渡って記述することもできますし、行の途中にいくつも記述することができます(ただしコメントの入れ子はできません)。

生成規則が定義されているものの、その規則名が開始記号でもなく他の規則の定義にも現れない場合、その規則は構文解析において無視されます。ただし字句解析は行なわれるので、結果的にその規則に現れる終端記号が無視されることになります。例えば

Space ::= "\s+" ;

という生成規則がある一方でこの Space という規則名がどこにも現れない場合、1文字以上のホワイトスペースはダミー記号として無視されます。つまり、この規則を追加することで文法をフリーフォーマットにすることができます。同様の考え方でコメントも定義することができます。

HABA による HABA の仕様

参考までに、HABA 文法の仕様を HABA 自身の記述方法で記しておきます。

Gram ::= Rule+ ;
Rule ::= Name '::=' Expr? ';' ;
Name ::= "[a-zA-Z_][a-zA-Z_0-9]*" ;
Expr ::= List ('|' List)* ;
List ::= Term+ ;
Term ::= Fact Rept? ;
Fact ::= Fixd | Flex | Name | Quot ;
Fixd ::= "'(''|[^'])+'" ;
Flex ::= """(""""|[^""])+""" ;
Quot ::= '(' Expr ')' ;
Rept ::= '?' | '*' | '+' ;
Spac ::= "\s+" ;
Line ::= "//[^\n]*(\n|$)" ;
Bloc ::= "/\*((?!\*/)[\s\S])*\*/" ;

Spac はホワイトスペース、Line は行コメント、Bloc はブロックコメントで、いずれも構文解析では無視されます。Bloc の定義にある [\s\S] は、改行を含む任意の1文字を表しています。