updated for v0.9.1
これは暫定仕様です。未来には確実に変更されるので気をつけてください。
0.9 になってまた大幅に文法がかわりました。 最も変更が大きいのはアクションです。ダサダサのピリオド方式をやめて { と } で囲むように しました。詳しくは「文法記述」を参照してください。
トップレベルは、規則部とユーザーコード部に分けられます。ユーザーコード部は クラス定義の後に来なければいけません。
規則部は以下のような形をしています。
class クラス名 文法記述 [演算子順位] [スタート規則] [トークンシンボル値定義] end"クラス名"はここで定義するパーサクラスの名前です。これはそのままRubyのクラス名に なるので、大文字ではじまっている必要があります。
racc で生成するパーサが理解できる文法を記述します。 書式は次のようなものです。
rule トークン: トークンの並び アクション ; トークン: トークンの並び アクション | トークンの並び アクション | トークンの並び アクション ; (必要なだけ同じようにつづける) end
書式は yacc とほぼ同じです。ただし、セミコロンは必須です。アクションがあっても 省略してはいけません。
アクションはイテレータのように、{ } で囲みます。ただしまだ対応が不十分なので、
中では % 文字列やヒアドキュメントは使えません。コメントは # タイプのみ。
正規表現は // タイプのみです。
本当は'}'がはいってなければどれも大丈夫ですが…やらないほうが無難です。
また、アクションは省略してもかまいません。その場合は空文字列が使われます。
アクションからの返り値が左辺の値になります。通常どおりreturnしても 大丈夫ですし、resultに返したい値を代入しておけば、自動的にそれが返り値と なります(その場合はreturnしてはいけません。ただしreturn(result)ならOKです)。
以下に文法記述の全体の例をしめします。
rule goal: definition ruls source { result = val } ; # セミコロンを忘れずに definition: /* 空配列 */ { result = [] } | definition startdesig { result[0] = val[1] } # '|'で継続できる | definition precrule # これは上の行の続きです。 { result[1] = val[1] } ; (略) end # endで規則部終了
アクション内では、いくつか特別な意味をもった変数が使えます。そのような変数には、 以下のものがあります(将来この名前は変えられるようになる予定です)。かっこの中は、 yacc での表記です。
また、規則中では特別なトークンシンボル「$end」がつかえます。
このシンボルが最後にある規則では、その規則を還元したあとでちょうど入力が終了すれば
パース終了になります。Rubyレベルでは、「false」が相当します。
あるトークン上でシフト・還元衝突がおこったとき、そのトークンに演算子優先順位が設定して あると、衝突を解消できる場合があります。そのようなものとして特に有名なのは数式の 演算子と (だから演算子優先順位っていうのか (^^;; ) if...else構文です。
優先順位で解決できる文法は、うまく文法をくみかえてやれば、必ず優先順位なしでも同じ 効果を得られます。しかしたいていの場合、優先順位を設定するほうが、文法が簡単になります。
シフト・還元衝突がおこったとき、Raccはまずその規則に順位が設定されているか調べます。 規則の順位は、その規則で一番うしろにある終端トークンの優先順位です。たとえば、
target: TERM_A non_terma TERM_B non_termc ;のような規則の順位はTERM_Bの優先順位になります。もしTERM_Bに優先順位が設定されて いなかったら、優先順位で衝突を解決することはできないと判断し、「衝突がある」と 報告します。
演算子優先順位は、つぎのように書きます。
prechighに近いほうが、順位の「高い」トークンです。上下をまるごとさかさまにして、
preclow...prechighの順番に書くこともできます。
prechigh nonassoc '++' left '*' '/' left '+' '-' right '=' preclow
ここで、rightという予約語は、そのトークンが「右結合」であるということを表しています。 右結合とは、「もし同じ順位のトークンが並んでいたら、右の演算子を先に使う」ということ です。たとえば、上のように順位が設定されているとすると、x = y = z は (x = (y = z)) と 書いたように扱われるということです。同様に、leftは「左結合」です。1 + 2 + 3 は、 ((1 + 2) + 3)です。
nonassocは、「非結合」を表します。x++ ++ のような表現はありえないということを示し、 実際にこのようなトークンの並びがきたときには、パースエラーをだすようになります。
また、通常は還元する規則の、最後のトークンが順位を決めますが、ある規則に限って 順位をあげたいときがあります。yaccで言えば%precです。たとえば、符号反転のマイナスは 引き算のマイナスより順位を高くしないといけません。
prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow : : exp: exp '*' exp | exp '-' exp | '-' exp = UMINUS # 順位を上げる
このように記述すると、'-' exp の規則の順位がUMINUSの順位になります。こうすることで、 符号反転の'-'は'*'よりも順位が高くなるので、正常に処理されます。
パーサをつくるためには、どの規則が「最初の」規則か、ということをRaccにおしえて やらなければいけません。それを明示的に書くのがスタート規則です。スタート規則は 次のように書きます。
start real_target
このように書くと、real_targetの規則をスタート規則として使います。省略すると、 一番上にある規則がスタート規則になります。普通は、最初の規則を一番上にかくほうが 書きやすく、わかりやすくなりますから、start はあまりつかう必要はないでしょう。
トークンシンボルを表す値は、デフォルトでは
となっていますが、たとえば他の形式のスキャナがすでに存在する場合などは、 これにあわせなければならず、このままでは不便です。このような場合には、 token節を加えることで、トークンシンボルを表す値を変えることができます。 以下がその例です。
token PLUS 'PlusClass' # --> PlusClass MIN 'MinusClass' # --> MinusClass end
デフォルトでは、トークンシンボルPLUSに対してはトークンシンボル値は:PLUSですが、 上のような記述がある場合は、PlusClassになります。変換後の値はほぼ任意のものを 使えますが、false と nil だけは例外です。
変換後の値として文字列を使うときは、次のように引用符を重ねる必要があることに 注意してください。
token PLUS '"plus"' # --> "plus" end
ちなみに、引用符には「'」だけでなく「"」も使えます。 ただし、どちらを使っても生成された Ruby のコード上では「"」になります。
また、バックスラッシュによるクオートは有効ですが、バックスラッシュは消えずにそのまま 残りますので注意してください。これは「仕様」です。バグではありません。
PLUS '"plus\n"' # --> "plus\n" MIN "\"minus#{val}\"" # --> \"minus#{val}\"
ユーザーコード部には、パーサクラスの内部または外部で使用するRubyのコードを書きます。 ここに書いたコードは、Raccオブジェクトのcodeプロパティに「名前=>ソースコード」の ような連想配列(Hash)として格納されます。それをどう使うかは、ドライバによります。
たとえば、添付してあるracc.rbでは、driver prepare inner の 3つを特別な名前として使い、それぞれを生成するファイルの特定の場所に転写しています。
ユーザーコード部の書式は以下の通りです。
---- ユーザーコードの識別子 rubyの文 rubyの文 rubyの文 ---- 次のユーザーコードの識別子 rubyの文 :
行の先頭から4つ以上連続した「-」があるとユーザーコードとみなされます。 識別子は一つの単語で、そのあとには「=」以外なにを書いてもかまいません。
また、次のような文で外部ファイルをユーザーコードとしてインクルードすることもできます。
---- 識別子 = ファイル名 ファイル名 ファイル名 .....
このように書くと、すべてのファイルの内容をその順番につなげたものが そのユーザーコードになります。次はその例です。
---- driver = init.rb err.rb run.rb print "this line is added, too\n"
このように書くと'driver'コードは init.rb と run.rb をつなげたものになります。
----のある行の下に書いたもの(printのある行など)もつづけて加えられます。
さきほど ---- のある行の識別子のあとはなにを書いてもいいと言いましたが、この場合は
なにも書いてはいけません。コメントなどもだめです。正確にファイル名だけをならべて
書いてください。引用符なども使えません。
この制限はもちろん「対応するのがめんどくさかったから」ですが ^^;;;