Racc文法リファレンス


updated for v0.10.1

これは暫定仕様です。未来には確実に変更されるので気をつけてください。 バージョン 0.10 では class に対応する end がなくなり、class...rule...end の形になりました。

全体の構造

0.9 になってまた大幅に文法がかわりました。 最も変更が大きいのはアクションです。ダサダサのピリオド方式をやめて { と } で囲むように しました。詳しくは「文法記述」を参照してください。

トップレベルは、規則部とユーザーコード部に分けられます。ユーザーコード部は クラス定義の後に来なければいけません。

コメント

文法ファイルには、一部例外を除いて、ほとんどどこにでもコメントを 書くことができます。コメントは、Rubyの #.....(行末) スタイルと、 Cの /*......*/ スタイルを使うことができます。

規則部

規則部は以下のような形をしています。


    class クラス名
      [演算子順位]
      [スタート規則]
      [トークンシンボル値定義]
    rule
      文法記述
    end

"クラス名"はここで定義するパーサクラスの名前です。 これはそのままRubyのクラス名になります。

文法の記述

racc で生成するパーサが理解できる文法を記述します。 文法は、予約語 rule と end の間に、以下のような書式で書きます。


      トークン: トークンの並び アクション ;

      トークン: トークンの並び アクション
              | トークンの並び アクション
              | トークンの並び アクション
              ;
                  (必要なだけ同じようにつづける)

書式は yacc とほぼ同じです。ただし、セミコロンは必須です。アクションがあっても 省略してはいけません。

アクションは { } で囲みます。ただしまだ対応が不十分なので、 中では % 文字列やヒアドキュメントは使えません。コメントは # タイプのみ。 正規表現は // タイプのみです (本当は'}'がはいってなければどれも大丈夫ですが、やらないほうが無難です)。

また、アクションは省略してもかまわず、その場合は空文字列が使われます。

右辺の返り値($$)は、アクションから出たときのローカル変数resultの値、 もしくは明示的にreturnで返した値になります。
以下に文法記述の全体の例をしめします。

rule
  goal: definition ruls source { result = val } ;

  definition: /* none */   { result = [] }
    | definition startdesig  { result[0] = val[1] }
    | definition
           precrule   # これは上の行の続きです。
      {
        result[1] = val[1]
      }
    ;
(略)
end  # endで規則部終了

アクション内では、いくつか特別な意味をもった変数が使えます。そのような変数には、 以下のものがあります(将来この名前は変えられるようになる予定です)。かっこの中は、 yacc での表記です。

result ($$)
左辺の値。val[0]がデフォルトの値として使われます。
val ($1,$2,$3…)
右辺の値の配列。Rubyの配列なので当然インデックスはゼロから始まります。 この配列は好きなように使えます。
_values (...,$-2,$-1,$0)
値スタック。サイズを変更してはいけません。

演算子優先順位

あるトークン上でシフト・還元衝突がおこったとき、そのトークンに演算子優先順位が設定して あると、衝突を解消できる場合があります。そのようなものとして特に有名なのは数式の 演算子と (だから演算子優先順位っていうのか (^^;; ) if...else構文です。

優先順位で解決できる文法は、うまく文法をくみかえてやれば、必ず優先順位なしでも同じ 効果を得られます。しかしたいていの場合、優先順位を設定するほうが、文法が簡単になります。

シフト・還元衝突がおこったとき、Raccはまずその規則に順位が設定されているか調べます。 規則の順位は、その規則で一番うしろにある終端トークンの優先順位です。たとえば、

      target: TERM_A non_terma TERM_B non_termc ;
のような規則の順位はTERM_Bの優先順位になります。もしTERM_Bに優先順位が設定されて いなかったら、優先順位で衝突を解決することはできないと判断し、「衝突がある」と 報告します。

演算子優先順位は、つぎのように書きます。
prechighに近いほうが、順位の「高い」トークンです。上下をまるごとさかさまにして、 preclow...prechighの順番に書くこともできます。


    prechigh
      nonassoc PLUSPLUS
      left     MULTI DEVIDE
      left     PLUS MINUS
      right    '='
    preclow

left right nonassoc はそれぞれ「右結合」「左結合」「結合しない」をあらわします。

また、通常は還元する規則の、最後のトークンが順位を決めますが、ある規則に限って 順位をあげたいときがあります。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コマンドでは、prepare inner driver の 3つをコードの名前として使い、それぞれを生成するファイルの特定の場所に転写しています。

ユーザーコード部の書式は以下の通りです。


---- ユーザーコードの識別子
  rubyの文
  rubyの文
  rubyの文

---- 次のユーザーコードの識別子
  rubyの文
     :

行の先頭から4つ以上連続した「-」があるとユーザーコードとみなされます。 識別子は一つの単語で、そのあとには「=」以外なにを書いてもかまいません。

また、次のような文で外部ファイルをユーザーコードとしてインクルードすることもできます。


---- 識別子 = ファイル名 ファイル名 ファイル名 .....

このように書くと、すべてのファイルの内容をその順番につなげたものが そのユーザーコードになります。次はその例です。

---- driver = init.rb err.rb run.rb

print "this line is added, too\n"

ここでは init.rb と run.rb を driver コードとして指定しています。 ----のある行の下に書いたもの(printのある行など)もつづけて加えられます。 さきほど ---- のある行の識別子のあとはなにを書いてもいいと言いましたが、 ファイルを記述する場合に限ってはなにも書くことができません。 コメントなどもだめです。正確にファイル名だけをならべて書いてください。 この制限はもちろん「対応するのがめんどくさかったから」です ^^;;;
Copyright(c) 1998-1999 Minero Aoki.