racc 1.4.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitattributes +2 -0
- data/.gitignore +7 -0
- data/COPYING +515 -0
- data/ChangeLog +846 -0
- data/DEPENDS +4 -0
- data/README.en.rdoc +86 -0
- data/README.ja.rdoc +96 -0
- data/Rakefile +15 -0
- data/TODO +5 -0
- data/bin/racc +308 -0
- data/bin/racc2y +195 -0
- data/bin/y2racc +339 -0
- data/doc/en/NEWS.en.rdoc +282 -0
- data/doc/en/command.en.html +78 -0
- data/doc/en/debug.en.rdoc +20 -0
- data/doc/en/grammar.en.rdoc +230 -0
- data/doc/en/index.en.html +10 -0
- data/doc/en/parser.en.rdoc +74 -0
- data/doc/en/usage.en.html +92 -0
- data/doc/ja/NEWS.ja.rdoc +307 -0
- data/doc/ja/command.ja.html +94 -0
- data/doc/ja/debug.ja.rdoc +36 -0
- data/doc/ja/grammar.ja.rdoc +348 -0
- data/doc/ja/index.ja.html +10 -0
- data/doc/ja/parser.ja.rdoc +125 -0
- data/doc/ja/usage.ja.html +414 -0
- data/ext/racc/cparse/MANIFEST +4 -0
- data/ext/racc/cparse/cparse.c +824 -0
- data/ext/racc/cparse/depend +1 -0
- data/ext/racc/cparse/extconf.rb +7 -0
- data/fastcache/extconf.rb +2 -0
- data/fastcache/fastcache.c +185 -0
- data/lib/racc.rb +6 -0
- data/lib/racc/compat.rb +40 -0
- data/lib/racc/debugflags.rb +59 -0
- data/lib/racc/exception.rb +15 -0
- data/lib/racc/grammar.rb +1115 -0
- data/lib/racc/grammarfileparser.rb +559 -0
- data/lib/racc/info.rb +16 -0
- data/lib/racc/iset.rb +91 -0
- data/lib/racc/logfilegenerator.rb +214 -0
- data/lib/racc/parser.rb +439 -0
- data/lib/racc/parserfilegenerator.rb +511 -0
- data/lib/racc/pre-setup +13 -0
- data/lib/racc/sourcetext.rb +34 -0
- data/lib/racc/state.rb +971 -0
- data/lib/racc/statetransitiontable.rb +316 -0
- data/lib/racc/static.rb +5 -0
- data/misc/dist.sh +31 -0
- data/sample/array.y +67 -0
- data/sample/array2.y +59 -0
- data/sample/calc-ja.y +66 -0
- data/sample/calc.y +65 -0
- data/sample/conflict.y +15 -0
- data/sample/hash.y +60 -0
- data/sample/lalr.y +17 -0
- data/sample/lists.y +57 -0
- data/sample/syntax.y +46 -0
- data/sample/yyerr.y +46 -0
- data/setup.rb +1587 -0
- data/tasks/doc.rb +12 -0
- data/tasks/email.rb +55 -0
- data/tasks/file.rb +37 -0
- data/tasks/gem.rb +37 -0
- data/tasks/test.rb +16 -0
- data/test/assets/chk.y +126 -0
- data/test/assets/conf.y +16 -0
- data/test/assets/digraph.y +29 -0
- data/test/assets/echk.y +118 -0
- data/test/assets/err.y +60 -0
- data/test/assets/expect.y +7 -0
- data/test/assets/firstline.y +4 -0
- data/test/assets/ichk.y +102 -0
- data/test/assets/intp.y +546 -0
- data/test/assets/mailp.y +437 -0
- data/test/assets/newsyn.y +25 -0
- data/test/assets/noend.y +4 -0
- data/test/assets/nonass.y +41 -0
- data/test/assets/normal.y +27 -0
- data/test/assets/norule.y +4 -0
- data/test/assets/nullbug1.y +25 -0
- data/test/assets/nullbug2.y +15 -0
- data/test/assets/opt.y +123 -0
- data/test/assets/percent.y +35 -0
- data/test/assets/recv.y +97 -0
- data/test/assets/rrconf.y +14 -0
- data/test/assets/scan.y +72 -0
- data/test/assets/syntax.y +50 -0
- data/test/assets/unterm.y +5 -0
- data/test/assets/useless.y +12 -0
- data/test/assets/yyerr.y +46 -0
- data/test/bench.y +36 -0
- data/test/helper.rb +88 -0
- data/test/infini.y +8 -0
- data/test/scandata/brace +7 -0
- data/test/scandata/gvar +1 -0
- data/test/scandata/normal +4 -0
- data/test/scandata/percent +18 -0
- data/test/scandata/slash +10 -0
- data/test/src.intp +34 -0
- data/test/start.y +20 -0
- data/test/test_chk_y.rb +51 -0
- data/test/test_grammar_file_parser.rb +15 -0
- data/test/test_racc_command.rb +155 -0
- data/test/test_scan_y.rb +51 -0
- data/test/testscanner.rb +51 -0
- data/web/racc.en.rhtml +42 -0
- data/web/racc.ja.rhtml +51 -0
- metadata +166 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
<h1>Racc ユーザマニュアル</h1>
|
2
|
+
<p>バージョン 1.4 対応</p>
|
3
|
+
<ul>
|
4
|
+
<li><a href="usage.html">Racc の使い方</a>
|
5
|
+
<li><a href="command.html">racc コマンドリファレンス</a>
|
6
|
+
<li><a href="grammar.html">規則ファイル文法リファレンス</a>
|
7
|
+
<li><a href="parser.html">Parser クラスリファレンス</a>
|
8
|
+
<li><a href="debug.html">パーサのデバッグ</a>
|
9
|
+
<li><a href="NEWS.html">リリースノート</a>
|
10
|
+
</ul>
|
@@ -0,0 +1,125 @@
|
|
1
|
+
= class Racc::Parser
|
2
|
+
Racc の生成するパーサはすべて Racc::Parser クラスを継承します。
|
3
|
+
Racc::Parser クラスにはパース中に使用するメソッドがいくつかあり、
|
4
|
+
そのようなメソッドをオーバーロードすると、パーサを初期化したり
|
5
|
+
することができます。
|
6
|
+
|
7
|
+
== Super Class
|
8
|
+
|
9
|
+
Object
|
10
|
+
|
11
|
+
== Constants
|
12
|
+
|
13
|
+
プリフィクス "Racc_" がついた定数はパーサの予約定数です。
|
14
|
+
そのような定数は使わないでください。動作不可能になります。
|
15
|
+
== Instance Methods
|
16
|
+
ここに載っているもののほか、プリフィクス "racc_" および "_racc_" が
|
17
|
+
ついたメソッドはパーサの予約名です。そのようなメソッドは使わないで
|
18
|
+
ください。
|
19
|
+
|
20
|
+
: do_parse -> Object
|
21
|
+
パースを開始します。
|
22
|
+
また、トークンが必要になった時は #next_token を呼び出します。
|
23
|
+
|
24
|
+
--
|
25
|
+
# Example
|
26
|
+
---- inner
|
27
|
+
def parse
|
28
|
+
@q = [[1,1],
|
29
|
+
[2,2],
|
30
|
+
[3,3],
|
31
|
+
[false, '$']]
|
32
|
+
do_parse
|
33
|
+
end
|
34
|
+
|
35
|
+
def next_token
|
36
|
+
@q.shift
|
37
|
+
end
|
38
|
+
--
|
39
|
+
|
40
|
+
: next_token -> [Symbol, Object]
|
41
|
+
[abstract method]
|
42
|
+
|
43
|
+
パーサが次のトークンを読みこむ時に使います。
|
44
|
+
[記号, その値] の形式の配列を返してください。
|
45
|
+
記号はデフォルトでは
|
46
|
+
|
47
|
+
* 文法中、引用符でかこまれていないもの
|
48
|
+
→ その名前の文字列のシンボル (例えば :ATOM )
|
49
|
+
* 引用符でかこまれているもの<br>
|
50
|
+
→ その文字列そのまま (例えば '=' )
|
51
|
+
|
52
|
+
で表します。これを変更する方法については、
|
53
|
+
文法リファレンスを参照してください。
|
54
|
+
|
55
|
+
また、もう送るシンボルがなくなったときには
|
56
|
+
[false, なにか] または nil を返してください。
|
57
|
+
|
58
|
+
このメソッドは抽象メソッドなので、#do_parse を使う場合は
|
59
|
+
必ずパーサクラス中で再定義する必要があります。
|
60
|
+
定義しないままパースを始めると例外 NotImplementedError が
|
61
|
+
発生します。
|
62
|
+
|
63
|
+
: yyparse( receiver, method_id )
|
64
|
+
パースを開始します。このメソッドでは始めてトークンが
|
65
|
+
必要になった時点で receiver に対して method_id メソッドを
|
66
|
+
呼び出してトークンを得ます。
|
67
|
+
|
68
|
+
receiver の method_id メソッドはトークンを yield しなければ
|
69
|
+
なりません。形式は #next_token と同じで [記号, 値] です。
|
70
|
+
つまり、receiver の method_id メソッドの概形は以下のように
|
71
|
+
なるはずです。
|
72
|
+
--
|
73
|
+
def method_id
|
74
|
+
until end_of_file
|
75
|
+
:
|
76
|
+
yield 記号, 値
|
77
|
+
:
|
78
|
+
end
|
79
|
+
end
|
80
|
+
--
|
81
|
+
少し注意が必要なのは、method_id が呼び出されるのは始めて
|
82
|
+
トークンが必要になった時点であるということです。method_id
|
83
|
+
メソッドが呼び出されたときは既にパースが進行中なので、
|
84
|
+
アクション中で使う変数を method_id の冒頭で初期化すると
|
85
|
+
まず失敗します。
|
86
|
+
|
87
|
+
トークンの終端を示す [false, なにか] を渡したらそれ以上は
|
88
|
+
yield しないでください。その場合には例外が発生します。
|
89
|
+
|
90
|
+
最後に、method_id メソッドからは必ず yield してください。
|
91
|
+
しない場合は何が起きるかわかりません。
|
92
|
+
|
93
|
+
: on_error( error_token_id, error_value, value_stack )
|
94
|
+
パーサコアが文法エラーを検出すると呼び出します (yacc の yyerror)。
|
95
|
+
エラーメッセージを出すなり、例外を発生するなりしてください。
|
96
|
+
このメソッドから正常に戻った場合、パーサはエラー回復モード
|
97
|
+
に移行します。
|
98
|
+
|
99
|
+
error_token はパースエラーを起こした記号の内部表現 (整数) です。
|
100
|
+
#token_to_str で文法ファイル上の文字列表現に直せます。
|
101
|
+
|
102
|
+
error_value はその値です。
|
103
|
+
|
104
|
+
value_stack はエラーの時点での値スタックです。
|
105
|
+
value_stack を変更してはいけません。
|
106
|
+
|
107
|
+
on_error のデフォルトの実装は例外 ParseError を発生します。
|
108
|
+
|
109
|
+
: token_to_str( t ) -> String
|
110
|
+
Racc トークンの内部表現 (整数)
|
111
|
+
を文法ファイル上の記号表現の文字列に変換します。
|
112
|
+
|
113
|
+
t が整数でない場合は TypeError を発生します。
|
114
|
+
t が範囲外の整数だった場合は nil を返します。
|
115
|
+
|
116
|
+
: yyerror
|
117
|
+
エラー回復モードに入ります。このとき #on_error は呼ばれません。
|
118
|
+
アクション以外からは呼び出さないでください。
|
119
|
+
|
120
|
+
: yyerrok
|
121
|
+
エラー回復モードから復帰します。
|
122
|
+
アクション以外からは呼び出さないでください。
|
123
|
+
|
124
|
+
: yyaccept
|
125
|
+
すぐに値スタックの先頭の値を返して #do_parse、#yyparse を抜けます。
|
@@ -0,0 +1,414 @@
|
|
1
|
+
<h1>Racc の使い方</h1>
|
2
|
+
<p>
|
3
|
+
Racc は文法規則から Ruby で書かれたパーサを生成するパーサジェネレータです。
|
4
|
+
パーサ生成アルゴリズムには yacc などと同じ LALR(1) を使用しています。
|
5
|
+
</p>
|
6
|
+
<p>
|
7
|
+
yacc を知っている人は記述法の違いだけわかれば使えると思います。
|
8
|
+
yacc を知らない人は
|
9
|
+
拙著『Ruby を 256 倍使うための本 無道編』(青木峰郎著、ASCII)
|
10
|
+
などを一読していただくのがよいかと思います。
|
11
|
+
他の UNIX コマンドなどとは異なり、
|
12
|
+
いきなり使うだけで Racc を理解するのはかなり困難です。
|
13
|
+
</p>
|
14
|
+
|
15
|
+
<h2>Racc とはなにか</h2>
|
16
|
+
<p>
|
17
|
+
Racc は文法を処理するツールです。
|
18
|
+
文字列はただの文字の列で、コンピュータにとっては意味を持ちません。
|
19
|
+
しかし人間はその文字の列の中になにか意味を見出すことができます。
|
20
|
+
コンピュータにもそのようなことを、部分的にでも、させられたら便利でしょう。
|
21
|
+
Racc はその手伝いをしてくれます。完全な自動化ではありませんが、
|
22
|
+
人間が全部やるよりも遥かに簡単になります。
|
23
|
+
</p>
|
24
|
+
<p>
|
25
|
+
Racc が自動化してくれる部分とは、文字列の含む「構造」の処理です。
|
26
|
+
たとえば Ruby の if 文を考えてみると、次のように定式化できます。
|
27
|
+
</p>
|
28
|
+
<pre>
|
29
|
+
if 条件式 [then]
|
30
|
+
文
|
31
|
+
:
|
32
|
+
[elsif 条件式 [then]
|
33
|
+
文
|
34
|
+
:]
|
35
|
+
[else
|
36
|
+
文
|
37
|
+
:]
|
38
|
+
end
|
39
|
+
</pre>
|
40
|
+
<p>
|
41
|
+
if 文では if という単語が最初になくてはならず、
|
42
|
+
elsif 節は else 節より前になくてはいけません。
|
43
|
+
このような配置の関係 (構造) が、Racc が処理する対象です。
|
44
|
+
</p>
|
45
|
+
<p>
|
46
|
+
一方、Racc で処理できないのはどういうことでしょうか。それは、たとえば
|
47
|
+
if の条件式にあたる部分が「なんであるか」ということです。つまり、条件
|
48
|
+
式が if の条件だということです。これは、こっちで条件として扱うコードを
|
49
|
+
書いてやらないといけません。
|
50
|
+
</p>
|
51
|
+
<p>
|
52
|
+
と言っても、わかりにくいでしょう。こういう抽象的なものは実際にいじって
|
53
|
+
みるのが一番です。
|
54
|
+
</p>
|
55
|
+
|
56
|
+
<h2>実際の話</h2>
|
57
|
+
<p>
|
58
|
+
実際に Racc をどのように使うかという話をします。Racc には独自のソース
|
59
|
+
コードみたいなものがあって、この中に処理したい「構造」を記述しておきま
|
60
|
+
す。このソースファイルを「文法ファイル」と呼ぶことにしましょう。この文
|
61
|
+
法ファイルの名前が parse.y と仮定すると、コマンドラインから以下のよう
|
62
|
+
に打ちこめば、その構造を処理するためのクラスを含んだファイルが得られま
|
63
|
+
す。
|
64
|
+
</p>
|
65
|
+
<pre>
|
66
|
+
$ racc parse.y
|
67
|
+
</pre>
|
68
|
+
<p>
|
69
|
+
生成されるファイルはデフォルトでは "ファイル名.tab.rb" です。他の名前
|
70
|
+
にしたいなら、-o オプションで変更できます。
|
71
|
+
</p>
|
72
|
+
<pre>
|
73
|
+
$ racc parse.y -o myparser.rb
|
74
|
+
</pre>
|
75
|
+
<p>
|
76
|
+
このようにして作ったクラス、またはそのような処理を担当するパート、
|
77
|
+
のことはパーサ (parser) と呼ぶことになっています。解析するヤツ、
|
78
|
+
というくらいに適当にとらえてください。
|
79
|
+
</p>
|
80
|
+
|
81
|
+
<h2>文法ファイルを書く</h2>
|
82
|
+
<p>
|
83
|
+
Racc は文法ファイルから Ruby のクラスを生成するツールだと言いました。
|
84
|
+
そのクラスは全て Racc::Parser の下位クラスで、名前は文法ファイル中で
|
85
|
+
指定します。以下、ここに書くべきことが「なんなのか」を説明します。
|
86
|
+
ここでは内容に重点を置くので、文法ファイル自体の文法の詳細は
|
87
|
+
<a href="grammar.html">文法リファレンス</a>を見てください。
|
88
|
+
</p>
|
89
|
+
|
90
|
+
<h3>文法</h3>
|
91
|
+
<p>
|
92
|
+
まずは、全体の概形です。
|
93
|
+
</p>
|
94
|
+
<pre>
|
95
|
+
class MyParser
|
96
|
+
rule
|
97
|
+
|
98
|
+
if_stmt: IF expr then stmt_list elsif else END
|
99
|
+
|
100
|
+
then : THEN
|
101
|
+
|
|
102
|
+
|
103
|
+
elsif :
|
104
|
+
| ELSIF stmt_list
|
105
|
+
|
106
|
+
else :
|
107
|
+
| ELSE stmt_list
|
108
|
+
|
109
|
+
expr : NUMBER
|
110
|
+
| IDENT
|
111
|
+
| STRING
|
112
|
+
|
113
|
+
stmt_list : ふにゃふにゃ
|
114
|
+
|
115
|
+
end
|
116
|
+
</pre>
|
117
|
+
<p>
|
118
|
+
Ruby スクリプトのように class でパーサクラス名を指定し、rule ... end
|
119
|
+
の間にパーサに解析させたい文法を記述します。
|
120
|
+
</p>
|
121
|
+
<p>
|
122
|
+
文法は、記号の並びでもって表します。rule ... end の間にあるコロンとバー
|
123
|
+
以外のもの、if_stmt IF expr then などが全て「記号」です。そしてコロン
|
124
|
+
が日本語で言う「〜は××だ」の「は」みたいなもんで、その左の記号が右の
|
125
|
+
記号の列と同じものを指す、というふうに定義します。また、バーは「または」
|
126
|
+
を意味します。それと、単純にコロンの左の記号のことを左辺、右を右辺とも
|
127
|
+
言います。以下はこちらのほうを使って説明しましょう。
|
128
|
+
</p>
|
129
|
+
<p>
|
130
|
+
少し注意が必要な点を述べます。まず、then の、バーのあとの定義 (規則) を
|
131
|
+
見てください。ここには何も書いていないので、これはその通り「無」であっ
|
132
|
+
てもいい、ということを表しています。つまり、then は記号 THEN 一個か、
|
133
|
+
またはなにもなし(省略する)でよい、ということです。記号 then は実際の
|
134
|
+
Ruby のソースコードにある then とは切り離して考えましょう
|
135
|
+
(それは実は大文字の記号 THEN が表しています)。
|
136
|
+
</p>
|
137
|
+
<p>
|
138
|
+
さて、そろそろ「記号」というものがなんなのか書きましょう。
|
139
|
+
ただし順番に話をしないといけないので、まずは聞いていてください。
|
140
|
+
この文章の最初に、パーサとは文字の列から構造を見出す部分だと言いました。
|
141
|
+
しかし文字の列からいきなり構造を探すのは面倒なので、実際にはまず
|
142
|
+
文字の列を単語の列に分割します。その時点でスペースやコメントは捨てて
|
143
|
+
しまい、以降は純粋にプログラムの一部をなす部分だけを相手にします。
|
144
|
+
たとえば文字列の入力が次のようだったとすると、
|
145
|
+
</p>
|
146
|
+
<pre>
|
147
|
+
if flag then # item found.
|
148
|
+
puts 'ok'
|
149
|
+
end
|
150
|
+
</pre>
|
151
|
+
<p>
|
152
|
+
単語の列は次のようになります。
|
153
|
+
</p>
|
154
|
+
<pre>
|
155
|
+
if flag then puts 'ok' end
|
156
|
+
</pre>
|
157
|
+
<p>
|
158
|
+
ここで、工夫が必要です。どうやら flag はローカル変数名だと思われますが、
|
159
|
+
変数名というのは他にもいろいろあります。しかし名前が i だろうが a だろ
|
160
|
+
うが vvvvvvvvvvvv だろうが、「構造」は同じです。つまり同じ扱いをされる
|
161
|
+
べきです。変数 a を書ける場所なら b も書けなくてはいけません。だったら
|
162
|
+
一時的に同じ名前で読んでもいいじゃん。ということで、この単語の列を以下
|
163
|
+
のように読みかえましょう。
|
164
|
+
</p>
|
165
|
+
<pre>
|
166
|
+
IF IDENT THEN IDENT STRING END
|
167
|
+
</pre>
|
168
|
+
<p>
|
169
|
+
これが「記号」の列です。パーサではこの記号列のほうを扱い、構造を見付け
|
170
|
+
ていきます。
|
171
|
+
</p>
|
172
|
+
<p>
|
173
|
+
さらに記号について見ていきましょう。
|
174
|
+
記号は二種類に分けられます。「左辺にある記号」と「ない記号」です。
|
175
|
+
左辺にある記号は「非終端」記号と言います。ないほうは「終端」記号と
|
176
|
+
言います。最初の例では終端記号はすべて大文字、非終端記号は小文字で
|
177
|
+
書いてあるので、もう一度戻って例の文法を見てください。
|
178
|
+
</p>
|
179
|
+
<p>
|
180
|
+
なぜこの区分が重要かと言うと、入力の記号列はすべて終端記号だからです。
|
181
|
+
一方、非終端記号はパーサの中でだけ、終端記号の列から「作りだす」ことに
|
182
|
+
よって始めて存在します。例えば次の規則をもう一度見てください。
|
183
|
+
</p>
|
184
|
+
<pre>
|
185
|
+
expr : NUMBER
|
186
|
+
| IDENT
|
187
|
+
| STRING
|
188
|
+
</pre>
|
189
|
+
<p>
|
190
|
+
expr は NUMBER か IDENT か STRING だと言っています。逆に言うと、
|
191
|
+
IDENT は expr に「なることができます」。文法上 expr が存在できる
|
192
|
+
場所に IDENT が来ると、それは expr になります。例えば if の条件式の
|
193
|
+
部分は expr ですから、ここに IDENT があると expr になります。その
|
194
|
+
ように文法的に「大きい」記号を作っていって、最終的に一個になると、
|
195
|
+
その入力は文法を満たしていることになります。実際にさっきの入力で
|
196
|
+
試してみましょう。入力はこうでした。
|
197
|
+
</p>
|
198
|
+
<pre>
|
199
|
+
IF IDENT THEN IDENT STRING END
|
200
|
+
</pre>
|
201
|
+
<p>
|
202
|
+
まず、IDENT が expr になります。
|
203
|
+
</p>
|
204
|
+
<pre>
|
205
|
+
IF expr THEN IDENT STRING END
|
206
|
+
</pre>
|
207
|
+
<p>
|
208
|
+
次に THEN が then になります。
|
209
|
+
</p>
|
210
|
+
<pre>
|
211
|
+
IF expr then IDENT STRING END
|
212
|
+
</pre>
|
213
|
+
<p>
|
214
|
+
IDENT STRING がメソッドコールになります。この定義はさきほどの例には
|
215
|
+
ないですが、実は省略されているんだと考えてください。そしていろいろな
|
216
|
+
過程を経て、最終的には stmt_list (文のリスト)になります。
|
217
|
+
</p>
|
218
|
+
<pre>
|
219
|
+
IF expr then stmt_list END
|
220
|
+
</pre>
|
221
|
+
<p>
|
222
|
+
elsif と else は省略できる、つまり無から生成できます。
|
223
|
+
</p>
|
224
|
+
<pre>
|
225
|
+
IF expr then stmt_list elsif else END
|
226
|
+
</pre>
|
227
|
+
<p>
|
228
|
+
最後に if_stmt を作ります。
|
229
|
+
</p>
|
230
|
+
<pre>
|
231
|
+
if_stmt
|
232
|
+
</pre>
|
233
|
+
<p>
|
234
|
+
ということでひとつになりました。
|
235
|
+
つまりこの入力は文法的に正しいということがわかりました。
|
236
|
+
</p>
|
237
|
+
|
238
|
+
<h3>アクション</h3>
|
239
|
+
<p>
|
240
|
+
ここまでで入力の文法が正しいかどうかを確認する方法はわかりましたが、
|
241
|
+
これだけではなんにもなりません。最初に説明したように、ここまででは
|
242
|
+
構造が見えただけで、プログラムは「意味」を理解できません。そしてその
|
243
|
+
部分は Racc では自動処理できないので、人間が書く、とも言いました。
|
244
|
+
それを書くのが以下に説明する「アクション」という部分です。
|
245
|
+
</p>
|
246
|
+
<p>
|
247
|
+
前項で、記号の列がだんだんと大きな単位にまとめられていく過程を見ました。
|
248
|
+
そのまとめる時に、同時になにかをやらせることができます。それが
|
249
|
+
アクションです。アクションは、文法ファイルで以下のように書きます。
|
250
|
+
</p>
|
251
|
+
<pre>
|
252
|
+
class MyParser
|
253
|
+
rule
|
254
|
+
|
255
|
+
if_stmt: IF expr then stmt_list elsif else END
|
256
|
+
{ puts 'if_stmt found' }
|
257
|
+
|
258
|
+
then : THEN
|
259
|
+
{ puts 'then found' }
|
260
|
+
|
|
261
|
+
{ puts 'then is omitted' }
|
262
|
+
|
263
|
+
elsif :
|
264
|
+
{ puts 'elsif is omitted' }
|
265
|
+
| ELSIF stmt_list
|
266
|
+
{ puts 'elsif found' }
|
267
|
+
|
268
|
+
else :
|
269
|
+
{ puts 'else omitted' }
|
270
|
+
| ELSE stmt_list
|
271
|
+
{ puts 'else found' }
|
272
|
+
|
273
|
+
expr : NUMBER
|
274
|
+
{ puts 'expr found (NUMBER)' }
|
275
|
+
| IDENT
|
276
|
+
{ puts 'expr found (IDENT)' }
|
277
|
+
| STRING
|
278
|
+
{ puts 'expr found (STRING)' }
|
279
|
+
|
280
|
+
stmt_list : ふにゃふにゃ
|
281
|
+
|
282
|
+
end
|
283
|
+
</pre>
|
284
|
+
<p>
|
285
|
+
見てのとおり、規則のあとに { と } で囲んで書きます。
|
286
|
+
アクションにはだいたい好きなように Ruby スクリプトが書けます。
|
287
|
+
</p>
|
288
|
+
<p>
|
289
|
+
(この節、未完)
|
290
|
+
</p>
|
291
|
+
<hr>
|
292
|
+
|
293
|
+
<p>
|
294
|
+
yacc での <code>$$</code> は Racc ではローカル変数 <code>result</code>
|
295
|
+
で、<code>$1,$2...</code> は配列 <var>val</var>です。
|
296
|
+
<code>result</code> は <code>val[0]</code> ($1) の値に初期化され、
|
297
|
+
アクションを抜けたときの <code>result</code> の値が左辺値になります。
|
298
|
+
Racc ではアクション中の <code>return</code> はアクションから抜けるだけで、
|
299
|
+
パース自体は終わりません。アクション中からパースを終了するには、
|
300
|
+
メソッド <code>yyaccept</code> を使ってください。
|
301
|
+
</p>
|
302
|
+
<p>
|
303
|
+
演算子の優先順位、スタートルールなどの yacc の一般的な機能も用意されて
|
304
|
+
います。ただしこちらも少し文法が違います。
|
305
|
+
</p>
|
306
|
+
<p>
|
307
|
+
yacc では生成されたコードに直接転写されるコードがありました。
|
308
|
+
Racc でも同じように、ユーザ指定のコードが書けます。
|
309
|
+
Racc ではクラスを生成するので、クラス定義の前/中/後の三個所があります。
|
310
|
+
Racc ではそれを上から順番に header inner footer と呼んでいます。
|
311
|
+
</p>
|
312
|
+
|
313
|
+
<h3>ユーザが用意すべきコード</h3>
|
314
|
+
<p>
|
315
|
+
パースのエントリポイントとなるメソッドは二つあります。ひとつは
|
316
|
+
<code>do_parse</code>で、こちらはトークンを
|
317
|
+
<code>Parser#next_token</code> から得ます。もうひとつは
|
318
|
+
<code>yyparse</code> で、こちらはスキャナから <code>yield</code> され
|
319
|
+
ることによってトークンを得ます。ユーザ側ではこのどちらか(両方でもいい
|
320
|
+
けど)を起動する簡単なメソッドを inner に書いてください。これらメソッド
|
321
|
+
の引数など、詳しいことはリファレンスを見てください。
|
322
|
+
</p>
|
323
|
+
<ul>
|
324
|
+
<li><a href="parser.html#Racc%3a%3aParser-do_parse">do_parse</a>
|
325
|
+
<li><a href="parser.html#Racc%3a%3aParser-yyparse">yyparse</a>
|
326
|
+
</ul>
|
327
|
+
<p>
|
328
|
+
どちらのメソッドにも共通なのはトークンの形式です。必ずトークンシンボル
|
329
|
+
とその値の二要素を持つ配列を返すようにします。またスキャンが終了して、
|
330
|
+
もう送るものがない場合は <code>[false,<var>なにか</var>]</code> を返し
|
331
|
+
てください。これは一回返せば十分です (逆に、<code>yyparse</code> を使
|
332
|
+
う場合は二回以上 <code>yield</code> してはいけない)。
|
333
|
+
</p>
|
334
|
+
<p>
|
335
|
+
パーサは別に文字列処理にだけ使われるものではありませんが、実際問題とし
|
336
|
+
て、パーサを作る場面ではたいてい文字列のスキャナとセットで使うことが多
|
337
|
+
いでしょう。Ruby ならスキャナくらい楽勝で作れますが、高速なスキャナと
|
338
|
+
なると実は難しかったりします。そこで高速なスキャナを作成するためのライ
|
339
|
+
ブラリも作っています。詳しくは
|
340
|
+
<a href="#WritingScanner">「スキャナを作る」の項</a>を見てください。
|
341
|
+
</p>
|
342
|
+
<p>
|
343
|
+
Racc には error トークンを使ったエラー回復機能もあります。yacc の
|
344
|
+
<code>yyerror()</code> は Racc では
|
345
|
+
<a href="parser.html#Racc%3a%3aParser-on_error"><code>Racc::Parser#on_error</code></a>
|
346
|
+
で、エラーが起きたトークンとその値、値スタック、の三つの引数をとります。
|
347
|
+
<code>on_error</code> のデフォルトの実装は例外
|
348
|
+
<code>Racc::ParseError</code> を発生します。
|
349
|
+
</p>
|
350
|
+
<p>
|
351
|
+
ユーザがアクション中でパースエラーを発見した場合は、メソッド
|
352
|
+
<a href="parser.html#Racc%3a%3aParser-yyerror"><code>yyerror</code></a>
|
353
|
+
を呼べばパーサがエラー回復モードに入ります。
|
354
|
+
ただしこのとき <code>on_error</code>は呼ばれません。
|
355
|
+
</p>
|
356
|
+
|
357
|
+
<h3>パーサを生成する</h3>
|
358
|
+
<p>
|
359
|
+
これだけあればだいたい書けると思います。あとは、最初に示した方法で文法
|
360
|
+
ファイルを処理し、Ruby スクリプトを得ます。
|
361
|
+
</p>
|
362
|
+
<p>
|
363
|
+
うまくいけばいいのですが、大きいものだと最初からはうまくいかないでしょ
|
364
|
+
う。racc に -g オプションをつけてコンパイルし、@yydebug を true にする
|
365
|
+
とデバッグ用の出力が得られます。デバッグ出力はパーサの @racc_debug_out
|
366
|
+
に出力されます(デフォルトは stderr)。また、racc に -v オプションをつけ
|
367
|
+
ると、状態遷移表を読みやすい形で出力したファイル(*.output)が得られます。
|
368
|
+
どちらもデバッグの参考になるでしょう。
|
369
|
+
</p>
|
370
|
+
|
371
|
+
|
372
|
+
<h2>作ったパーサを配布する</h2>
|
373
|
+
<p>
|
374
|
+
Racc の生成したパーサは動作時にランタイムルーチンが必要です。
|
375
|
+
具体的には parser.rb と cparse.so です。
|
376
|
+
ただし cparse.so は単にパースを高速化するためのライブラリなので
|
377
|
+
必須ではありません。なくても動きます。
|
378
|
+
</p>
|
379
|
+
<p>
|
380
|
+
まず Ruby 1.8.0 以降にはこのランタイムが標準添付されているので、
|
381
|
+
Ruby 1.8 がある環境ならばランタイムについて考慮する必要はありません。
|
382
|
+
Racc 1.4.x のランタイムと Ruby 1.8 に添付されているランタイムは
|
383
|
+
完全互換です。
|
384
|
+
</p>
|
385
|
+
<p>
|
386
|
+
問題は Ruby 1.8 を仮定できない場合です。
|
387
|
+
Racc をユーザみんなにインストールしてもらうのも一つの手ですが、
|
388
|
+
これでは不親切です。そこでRacc では回避策を用意しました。
|
389
|
+
</p>
|
390
|
+
<p>
|
391
|
+
racc に -E オプションをつけてコンパイルすると、
|
392
|
+
パーサと racc/parser.rb を合体したファイルを出力できます。
|
393
|
+
これならばファイルは一つだけなので簡単に扱えます。
|
394
|
+
racc/parser.rb は擬似的に require したような扱いになるので、
|
395
|
+
この形式のパーサが複数あったとしてもクラスやメソッドが衝突することもありません。
|
396
|
+
ただし -E を使った場合は cparse.so が使えませんので、
|
397
|
+
必然的にパーサの速度は落ちます。
|
398
|
+
</p>
|
399
|
+
|
400
|
+
|
401
|
+
<h2><a name="WritingScanner">おまけ: スキャナを書く</a></h2>
|
402
|
+
<p>
|
403
|
+
パーサを使うときは、たいてい文字列をトークンに切りわけてくれるスキャナ
|
404
|
+
が必要になります。しかし実は Ruby は文字列の最初からトークンに切りわけ
|
405
|
+
ていくという作業があまり得意ではありません。
|
406
|
+
正確に言うと、簡単にできるのですが、それなりのオーバーヘッドがかかります。
|
407
|
+
</p>
|
408
|
+
<p>
|
409
|
+
そのオーバーヘッドを回避しつつ、
|
410
|
+
手軽にスキャナを作れるように strscan というパッケージを作りました。
|
411
|
+
Ruby 1.8 以降には標準添付されていますし、
|
412
|
+
<a href="http://i.loveruby.net/ja/">筆者のホームページ</a>には
|
413
|
+
単体パッケージがあります。
|
414
|
+
</p>
|