tag:crieit.net,2005:https://crieit.net/tags/Racc/feed
「Racc」の記事 - Crieit
Crieitでタグ「Racc」に投稿された最近の記事
2021-11-03T08:04:56+09:00
https://crieit.net/tags/Racc/feed
tag:crieit.net,2005:PublicArticle/17735
2021-11-03T08:04:56+09:00
2021-11-03T08:04:56+09:00
https://crieit.net/posts/simple-compiler-parser-ruby-racc
Raccでかんたんな自作言語のパーサを書いた
<p><a href="https://crieit.now.sh/upload_images/856c469c026e97152123f812f323d67e6181c1b9a3869.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/856c469c026e97152123f812f323d67e6181c1b9a3869.png?mw=700" alt="image" /></a></p>
<p><自作言語処理系の説明用テンプレ></p>
<p>自分がコンパイラ実装に入門するために作った素朴なトイ言語とその処理系です。簡単に概要を書くと下記のような感じ。</p>
<ul>
<li>小規模: コンパイラ部分は 1,000 行程度</li>
<li>pure Ruby / 標準ライブラリ以外への依存なし</li>
<li>独自VM向けにコンパイルする</li>
<li>ライフゲームのために必要な機能だけ
<ul>
<li>変数宣言、代入、反復、条件分岐、関数呼び出し</li>
<li>演算子: <code>+</code>, <code>*</code>, <code>==</code>, <code>!=</code> のみ(優先順位なし)</li>
<li>型なし(値は整数のみ)</li>
</ul></li>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/08/30/132314">Ruby 以外の言語への移植</a>(コンパイラ部分のみ)</li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/1e683276541cf1b87b76">セルフホスト版</a>(別リポジトリ)</li>
</ul>
<p>下記も参照してください。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/f9cb3fc4a496b354b729">RubyでオレオレVMとアセンブラとコード生成器を2週間で作ってライフゲームを動かした話</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/2b95378b43a22109513c">Rubyでかんたんな自作言語のコンパイラを作った</a></li>
</ul>
<p><説明用テンプレおわり></p>
<hr />
<p>もともとパーサ部分は手書きの再帰下降パーサでしたが、Racc 版を作ってみました。</p>
<p>Racc で四則演算のパーサを作る方法は分かったがもう少しプログラム言語らしきものを扱っているサンプルを見たいとか、パースしたそばから実行する方式(インタプリタ方式)ではなく構文木が欲しいんだけど、という感じの人には参考になるかもしれません(昔の自分に送ってあげたい)。</p>
<h1 id="できたもの"><a href="#%E3%81%A7%E3%81%8D%E3%81%9F%E3%82%82%E3%81%AE">できたもの</a></h1>
<p><code>vgparser_racc.y</code> を追加したブランチです。</p>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2/tree/alt_parser_racc">github.com/sonota88/vm2gol-v2/tree/alt_parser_racc</a></p>
<p>300行弱なので全部貼ってもいいのですが、雰囲気程度ということで途中を省略したものを貼ります。全体は GitHub の方で見てください。</p>
<pre><code class="ruby">class Parser
prechigh
left "+" "*"
preclow
rule
program:
top_stmts
{
top_stmts = val[0]
result = ["top_stmts", *top_stmts]
}
top_stmts:
top_stmt
{
top_stmt = val[0]
result = [top_stmt]
}
| top_stmts top_stmt
{
top_stmts, top_stmt = val
result = [*top_stmts, top_stmt]
}
top_stmt:
func_def
func_def:
"func" IDENT "(" args ")" "{" stmts "}"
{
_, fn_name, _, args, _, _, stmts, _, = val
result = ["func", fn_name, args, stmts]
}
args:
# nothing
{
result = []
}
| arg
{
arg = val[0]
result = [arg]
}
| args "," arg
{
args, _, arg = val
result = [*args, arg]
}
arg:
IDENT | INT
stmts:
# nothing
{
result = []
}
| stmt
{
stmt = val[0]
result = [stmt]
}
| stmts stmt
{
stmts, stmt = val
result = [*stmts, stmt]
}
stmt:
stmt_var
| stmt_set
| stmt_return
| stmt_call
| stmt_call_set
| stmt_while
| stmt_case
| stmt_vm_comment
| stmt_debug
stmt_var:
"var" IDENT ";"
{
_, ident, _ = val
result = ["var", ident]
}
| "var" IDENT "=" expr ";"
{
_, ident, _, expr = val
result = ["var", ident, expr]
}
# ... 途中省略 ...
---- header
require "json"
require_relative "common"
---- inner
def next_token
@tokens.shift
end
def to_token(line)
token = Token.from_line(line)
return nil if token.nil?
if token.kind == :int
Token.new(token.kind, token.value.to_i)
else
token
end
end
def read_tokens(src)
tokens = []
src.each_line do |line|
token = to_token(line)
next if token.nil?
tokens << token
end
tokens
end
def to_racc_token(token)
kind =
case token.kind
when :ident then :IDENT
when :int then :INT
when :str then :STR
else
token.value
end
[kind, token.value]
end
def parse(src)
tokens = read_tokens(src)
@tokens = tokens.map { |token| to_racc_token(token) }
@tokens << [false, false]
do_parse()
end
---- footer
if $0 == __FILE__
ast = Parser.new.parse(ARGF.read)
puts JSON.pretty_generate(ast)
end
</code></pre>
<h1 id="実行の例"><a href="#%E5%AE%9F%E8%A1%8C%E3%81%AE%E4%BE%8B">実行の例</a></h1>
<p>入力とするプログラムの例です。</p>
<pre><code class="javascript">// sample.vg.txt
func main() {
return 1 + (2 * 3);
}
</code></pre>
<p>次のように実行するとASTに変換できます。</p>
<pre><code class="sh">## vgparser_racc.rb を生成
$ bundle exec racc -t -o vgparser_racc.rb vgparser_racc.y
$ ruby vglexer.rb sample.vg.txt > tmp/tokens.txt
$ ruby vgparser_racc.rb tmp/tokens.txt
[
"top_stmts",
[
"func",
"main",
[
],
[
[
"return",
[
"+",
1,
[
"*",
2,
3
]
]
]
]
]
]
</code></pre>
<h1 id="スタックの動きを見てみる"><a href="#%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%81%AE%E5%8B%95%E3%81%8D%E3%82%92%E8%A6%8B%E3%81%A6%E3%81%BF%E3%82%8B">スタックの動きを見てみる</a></h1>
<p>せっかく Racc を使っているので、適当なサンプルコード(下記)をパースさせてスタックの動きを図にしてみました。</p>
<pre><code class="javascript">func add(a, b) {
var c = a + b;
return c;
}
func main() {
var x = -1;
var i = 0;
while (i != 10) {
case when (i == 1) {
call_set x = add(i, x);
} when (i == 2) {
set x = 1 + 2;
} when (1) {
set x = 1 + (2 * 3);
}
set i = i + 1;
}
}
</code></pre>
<p>図の描き方については <a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/6a96d2bcea9d134e38b7">Ruby/Racc: パース時のスタックの動きをFlameGraphっぽくビジュアライズする</a> を参照。</p>
<p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/66f17460-1b71-e4bc-f82a-7a48d3fa269c.png" alt="image.png" /></p>
<p>左端の、全体の 1/5 くらいの小さめの山が <code>add</code> 関数で、残りが <code>main</code> 関数ですね。</p>
<h1 id="メモ"><a href="#%E3%83%A1%E3%83%A2">メモ</a></h1>
<ul>
<li>レキサはすでにあるのでそれを流用した
<ul>
<li><code>to_racc_token</code> メソッドで Token オブジェクトを Racc が期待する形式に変換</li>
</ul></li>
<li>1時間くらいで書けた。これより大きめの SQL パーサを少し前にすでに書いていてある程度慣れていたのと、パーサのテストがそのまま使えたのが良かった。</li>
</ul>
<h1 id="関連"><a href="#%E9%96%A2%E9%80%A3">関連</a></h1>
<p>他に Racc 関連で書いたもの。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/6a96d2bcea9d134e38b7">Ruby/Racc: パース時のスタックの動きをFlameGraphっぽくビジュアライズする</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/f55c1654fe101fa0e557">Ruby/Racc: パースに失敗した位置(行、桁)を得る</a></li>
</ul>
<h1 id="この記事を読んだ人はこちらの記事も読んでいます(たぶん)"><a href="#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%92%E8%AA%AD%E3%82%93%E3%81%A0%E4%BA%BA%E3%81%AF%E3%81%93%E3%81%A1%E3%82%89%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%82%E8%AA%AD%E3%82%93%E3%81%A7%E3%81%84%E3%81%BE%E3%81%99%EF%BC%88%E3%81%9F%E3%81%B6%E3%82%93%EF%BC%89">この記事を読んだ人はこちらの記事も読んでいます(たぶん)</a></h1>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/edce05e86d3248f49401">Parsletでかんたんな自作言語のパーサを書いた</a></li>
</ul>
sonota486
tag:crieit.net,2005:PublicArticle/16575
2021-01-10T08:38:01+09:00
2021-01-10T09:43:15+09:00
https://crieit.net/posts/Ruby-Racc-FlameGraph-5ffa3e59ccb77
Ruby/Racc: パース時のスタックの動きをFlameGraphっぽくビジュアライズする
<p>(少し前に同じ記事を投稿していましたが、すぐに次の記事を投稿するとモバイル版では一覧で(折りたたまれて)見えなくなるようだったので一度引っ込めて投稿し直しました。まだ Crieit に慣れてないのでご容赦くださいませ……)</p>
<p><a href="https://crieit.now.sh/upload_images/0e1305922bed9360c37ac2ff5dc93c965ff91788b6896.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0e1305922bed9360c37ac2ff5dc93c965ff91788b6896.png?mw=700" alt="image" /></a></p>
<p><a href="https://crieit.now.sh/upload_images/8872ba6bd00c951d584f5e0daae90a325ff917c1c163e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8872ba6bd00c951d584f5e0daae90a325ff917c1c163e.png?mw=700" alt="image" /></a></p>
<p>(↓これは FlameGraph に描かせたもの)<br />
<a href="https://crieit.now.sh/upload_images/e6294cd60200a969e3a5fac9581380e25ff917fa962f1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e6294cd60200a969e3a5fac9581380e25ff917fa962f1.png?mw=700" alt="image" /></a></p>
<p>こういう図を描いてみました。</p>
<h1 id="手順"><a href="#%E6%89%8B%E9%A0%86">手順</a></h1>
<h2 id="準備"><a href="#%E6%BA%96%E5%82%99">準備</a></h2>
<p>説明用の簡単なサンプルです。<code>1 + 2</code> のような入力を受理するパーサ。</p>
<p>ログ出力のために <code>@yydebug</code>, <code>@racc_debug_out</code> を設定しておきます。</p>
<pre><code class="ruby"># sample_add.y
class Parser
rule
expr: INT "+" INT
{
puts "expression found"
result = val
}
end
---- inner
def initialize
# parser.rb を使ったときにデバッグ情報を出力する
@yydebug = true
# デバッグ情報の出力先をファイルに変更(デフォルトでは標準エラー出力) …… これは必須ではない
@racc_debug_out = File.open("debug.log", "wb")
end
def next_token
@tokens.shift
end
def parse(src)
@tokens = src.split(" ").map do |s|
case s
when /^\d+$/ then [:INT, s.to_i]
else [s, s]
end
end
@tokens << [false, false]
do_parse
end
---- footer
src = ARGV[0]
result = Parser.new().parse(src)
puts "result: " + result.inspect
</code></pre>
<p><code>-t (--debug)</code> オプションを付けて racc コマンドを実行。</p>
<pre><code class="bash"># パーサを生成
racc -t -o parser.rb sample_add.y
</code></pre>
<p>生成されたパーサを実行すると次のような内容が debug.log に出力されます。</p>
<pre><code class="bash">$ ruby parser.rb "1 + 2"
expression found
result: [1, "+", 2]
$ head -30 debug.log
read :INT(INT) 1
shift INT
[ (INT 1) ]
goto 2
[ 0 2 ]
read "+"("+") "+"
shift "+"
[ (INT 1) ("+" "+") ]
goto 4
[ 0 2 4 ]
read :INT(INT) 2
shift INT
[ (INT 1) ("+" "+") (INT 2) ]
goto 6
[ 0 2 4 6 ]
reduce INT "+" INT --> expr
[ (expr [1, "+", 2]) ]
goto 1
[ 0 1 ]
</code></pre>
<p>スタックの部分だけ欲しいので、 grep してみましょうか。</p>
<pre><code class="bash">$ grep '\[ (' debug.log
[ (INT 1) ]
[ (INT 1) ("+" "+") ]
[ (INT 1) ("+" "+") (INT 2) ]
[ (expr [1, "+", 2]) ]
[ (expr [1, "+", 2]) ($end false) ]
[ (expr [1, "+", 2]) ($end false) ($end false) ]
</code></pre>
<p>軽く眺める程度ならこれだけでいいかもしれませんね。で、もっと複雑になったときにきれいに表示して見たい……ということで次へ。</p>
<h2 id="パースしやすいログを出力する"><a href="#%E3%83%91%E3%83%BC%E3%82%B9%E3%81%97%E3%82%84%E3%81%99%E3%81%84%E3%83%AD%E3%82%B0%E3%82%92%E5%87%BA%E5%8A%9B%E3%81%99%E3%82%8B">パースしやすいログを出力する</a></h2>
<p>ログに出力されたスタックの情報を使いたいのですが、そのままではパースしづらそうです。まじめにやろうとすると、これ用のパーサが必要になってしまいます(上の例のような簡単なものであればそんなに難しくなさそうですが)。</p>
<p>そこで、横着して最初からパースしやすいフォーマットで出力することにしました。</p>
<p>スタックの情報をどこで出力しているか調べると、ここ(<code>Racc::Parser#racc_print_stacks</code>)です。<br />
<a target="_blank" rel="nofollow noopener" href="https://github.com/ruby/racc/blob/v1.5.2/lib/racc/parser.rb#L604-L611">https://github.com/ruby/racc/blob/v1.5.2/lib/racc/parser.rb#L604-L611</a><br />
嬉しいことに単独のメソッドになっています。</p>
<p>※ ちなみに呼び出し元を追っていくと分かりますが、スタックの実体は<br />
<code>Racc::Parser#racc_tstack</code><br />
<code>Racc::Parser#racc_vstack</code><br />
です(それぞれ記号と値のスタック)。</p>
<p>racc コマンドの出力は <code>Racc::Parser</code> を継承したクラスになるので、<code>parser.y</code> の inner セクションに <code>racc_print_stacks</code> メソッドを書いておくと動作をオーバーライドできます。</p>
<p>……というわけで、下記のようにしました。スタックの情報だけ JSON で別ファイルに出力します。</p>
<pre><code class="ruby">---- header
require "json"
---- inner
def initialize
# ...
@racc_stack_out = File.open("stack.log", "wb")
end
# Override Racc::Parser#racc_print_stacks
def racc_print_stacks(tstack, vstack)
super(tstack, vstack)
stack = tstack.zip(vstack).map { |t, v| [racc_token2str(t), v] }
@racc_stack_out.puts JSON.generate(stack)
end
</code></pre>
<pre><code class="bash">$ ruby parser.rb "1 + 2"
expression found
result: [1, "+", 2]
$ cat stack.log
[["INT",1]]
[["INT",1],["\"+\"","+"]]
[["INT",1],["\"+\"","+"],["INT",2]]
[["expr",[1,"+",2]]]
[["expr",[1,"+",2]],["$end",false]]
[["expr",[1,"+",2]],["$end",false],["$end",false]]
</code></pre>
<p>これでパースしやすくなりました 👌</p>
<h2 id="仕込み部分のまとめ"><a href="#%E4%BB%95%E8%BE%BC%E3%81%BF%E9%83%A8%E5%88%86%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81">仕込み部分のまとめ</a></h2>
<p>ここまでのポイントをまとめておきます。</p>
<ul>
<li><code>@yydebug = true</code></li>
<li><code>@racc_debug_out</code> を設定
<ul>
<li>これは必須ではないが、標準エラー出力に出てほしくなければファイルなど別の出力先を設定しておく</li>
</ul></li>
<li><code>Racc::Parser#racc_print_stacks</code> をオーバーライド</li>
<li>racc コマンドに <code>-t (--debug)</code> オプションを付けてパーサを生成</li>
</ul>
<h2 id="図に変換する"><a href="#%E5%9B%B3%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B">図に変換する</a></h2>
<p>ここまでできたら、あとは stack.log を図に変換するだけ。</p>
<pre><code class="bash">ruby stack_graph.rb stack.log > stack_graph.html
</code></pre>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/racc-stack-graph/blob/main/stack_graph.rb">stack_graph.rb</a> は 160行くらい(2021-01-04 時点)の簡単なスクリプトです。</p>
<p>出力された HTML をブラウザで開くとこういう図が表示されます。x軸が処理の経過の方向、y軸がスタックが伸びる方向です。</p>
<p><a href="https://crieit.now.sh/upload_images/0e1305922bed9360c37ac2ff5dc93c965ff918285b7f4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0e1305922bed9360c37ac2ff5dc93c965ff918285b7f4.png?mw=700" alt="image" /></a></p>
<p>※ この例では、 <code>INT</code> や <code>expr</code> など定型的なものはパレット指定、それ以外はランダムに色を決めています。</p>
<hr />
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/racc-stack-graph">https://github.com/sonota88/racc-stack-graph</a></p>
<p>このリポジトリを git clone して <code>./run.sh sample_add.y "1 + 2"</code> で試せます(パーサの生成 → パーサの実行 → 図の生成をまとめて実行)。</p>
<h1 id="例1: 左結合・右結合の違いを見てみる"><a href="#%E4%BE%8B1%3A+%E5%B7%A6%E7%B5%90%E5%90%88%E3%83%BB%E5%8F%B3%E7%B5%90%E5%90%88%E3%81%AE%E9%81%95%E3%81%84%E3%82%92%E8%A6%8B%E3%81%A6%E3%81%BF%E3%82%8B">例1: 左結合・右結合の違いを見てみる</a></h1>
<p>せっかくなので他の例も見てみましょう。</p>
<p>まずは左結合と右結合の違い。他の部分は同じなので <code>class Parser ... end</code> の部分だけ示します。</p>
<pre><code class="ruby"># sample_lr.y
class Parser
prechigh
left "+"
# right "+"
preclow
rule
program: expr
{
puts "program found"
result = val[0]
}
expr:
INT
| expr "+" expr { result = val }
end
</code></pre>
<pre><code class="bash">./run.sh sample_lr.y "1 + 2 + 3"
</code></pre>
<p><a href="https://crieit.now.sh/upload_images/9e4478b04a779832b64ec14dbca107fa5ff91857c4753.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9e4478b04a779832b64ec14dbca107fa5ff91857c4753.png?mw=700" alt="image" /></a></p>
<p><code>1 + 2</code> が来た時点ですぐ還元(reduce)され、次に <code>expr + 3</code> となったときにまた還元されています。</p>
<hr />
<p>右結合に変えてみるとこう。パースの結果が <code>[1, "+", [2, "+", 3]]</code> になります。</p>
<p><a href="https://crieit.now.sh/upload_images/709009c6f1e1ae5c0851581a79578a7d5ff91875cf671.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/709009c6f1e1ae5c0851581a79578a7d5ff91875cf671.png?mw=700" alt="image" /></a></p>
<p>最初の <code>1 + 2</code> までシフト(shift)した時点では <code>1 + 2</code> の還元は発生せず、さらにスタックが積み上がったところで <code>2 + 3</code> が還元され、その後で <code>1 + expr</code> が還元されています。</p>
<h1 id="例2: SQL"><a href="#%E4%BE%8B2%3A+SQL">例2: SQL</a></h1>
<p><a href="https://crieit.now.sh/upload_images/861923e32cdc780d5e38bd128b69907a5ff918970228e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/861923e32cdc780d5e38bd128b69907a5ff918970228e.png?mw=700" alt="image" /></a></p>
<p>もう少し大きめの実際的なものということで、別件で作っているSQLパーサの例。select句、from句、…… と順番に還元され、最後に全体が1個の select文に還元されている様子です。</p>
<p>次のような入力を与えました。動作確認用なので内容は適当。</p>
<pre><code class="sql">select
123, 'str', null
,t1.a
,max(b)
,(
case
when a = 1 then 2
else 3
end
) as foo
from
db1 . table1 as t1
left outer join db2 . table2 as t2
on (
t2 . a = t1 . a
and t2 . b = t1 . b
)
and t2.a = t1.a
where
a = 123
and b <> 456
and c in (1, 2)
group by a, b
order by a, b
limit 10
;
</code></pre>
<h1 id="FlameGraph に渡してみる"><a href="#FlameGraph+%E3%81%AB%E6%B8%A1%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">FlameGraph に渡してみる</a></h1>
<p>stack.log をちょっと加工すると <a target="_blank" rel="nofollow noopener" href="https://github.com/brendangregg/FlameGraph">FlameGraph</a> に渡せるのでは? と後から気づいてやってみました(この節は後から追記しました)。</p>
<pre><code class="ruby"># to_flamegraph.rb
require "json"
File.open(ARGV[0]).each_line do |line|
labels = JSON.parse(line).map { |t, _| t.to_s.gsub(";", "(semicolon)") }
puts labels.join(";") + " 1"
end
</code></pre>
<pre><code class="bash">ruby to_flamegraph.rb stack.log | path/to/flamegraph.pl > flamegraph.svg
</code></pre>
<p><a href="https://crieit.now.sh/upload_images/5cd6c4261e93c57cbbd204b6ef818ded5ff918c088109.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5cd6c4261e93c57cbbd204b6ef818ded5ff918c088109.png?mw=700" alt="image" /></a></p>
<p><a href="https://crieit.now.sh/upload_images/e6294cd60200a969e3a5fac9581380e25ff918cb98474.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e6294cd60200a969e3a5fac9581380e25ff918cb98474.png?mw=700" alt="image" /></a></p>
<p>できますね 😃</p>
<p>FlameGraph で生成した SVG だと、キーワードにマッチする部分のハイライトや一部分のズーム表示といったインタラクティブな機能が利用できて便利。</p>
<p>参考: <a target="_blank" rel="nofollow noopener" href="https://yuroyoro.hatenablog.com/entry/2017/11/09/124805">ディスク使用量をFlameGraphで可視化する - ( ꒪⌓꒪) ゆるよろ日記</a></p>
<h1 id="バージョン"><a href="#%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3">バージョン</a></h1>
<pre><code>racc 1.5.2
</code></pre>
<h1 id="関連"><a href="#%E9%96%A2%E9%80%A3">関連</a></h1>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/10/31/113917">Ruby/Racc: パースに失敗した位置(行、桁)を得る</a></li>
</ul>
<p>以下は Racc 関連ではありませんが、Ruby + パーサ関連で書いたものということで。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/05/04/155425">Rubyで素朴な自作言語のコンパイラを作った</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/07/05/111103">四則演算と剰余のみのexprコマンドをRubyで作ってみた</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/07/23/090115">正規表現エンジン(ロブ・パイクのバックトラック実装)をRubyで写経した</a></li>
</ul>
sonota486