コンパイラ作成(44) コンパイラオプション
optparse
rubyのgetoptみたいなのを調べたらoptparseってのがそれみたいなんで使ってみるよ。optparseライブラリを使う(in Ruby) | 雑記帳を参考にさせてもらったよ。
今回合わせてrspcheck.oみたいなのを指定できるようにする。それとprint.oを自動的にリンクする機能は削除する。もう必要ないからね。以前作ったテストプログラムをコンパイルするときはprint.oをコマンドライン引数で指定しないとダメ。
メイン部
それじゃコーディングいくよ。
$ver = "0.04r" require 'optparse'
これをソースの頭の方に追加。
$opt_c = false $opt_d = false $opt_l = false $opt_O = 3 $opt_v = false $opt_cc = "clang-5.0" OptionParser.new do |opt| opt.on('-c', 'Compile only') {|v| $opt_c = v} opt.on('-d', 'Debug mode') {|v| $opt_d = v} opt.on('-l', 'Lexer module test') {|v| $opt_l = v} opt.on('-O val', 'Optimize level') {|v| $opt_O = v.to_i} opt.on('-v', 'Version') {|v| $opt_v = v} opt.on('--cc val','Assemble & Link') {|v| $opt_cc = v} begin opt.parse!(ARGV) rescue puts "Invalid option. \nsee #{opt}" exit end end # versionの表示 if $opt_v then puts "myc version #{$ver}" exit 0 end # 字句解析単体テスト if $opt_l then fname = ARGV[0] lex = Lexer.new(fname) lex.moduletest lex.close exit 0 end f = [] execfname = nil ARGV.each do |fname| if fname.match(/\.myc$/) then asmfname = fname.sub(/\.myc$/,'.s') # アセンブリコードのファイル名 # puts "compile #{fname}" if !execfname then execfname = fname.sub(/\.myc$/,'') end parser = Parser.new(fname) parser.program f << asmfname else # puts fname f << fname end end if !$opt_c then system("#{$opt_cc} #{f.join(' ')} -o #{execfname}") end exit 0
optparseはらくちんだね。使用方法も分かり易いし。自動的に-hに対応してusageも表示してくれるしね。今回、調子に乗ってオプションをいくつか増やしたよ。
不要コード
print.o関連の修正で不要になったコードをコメントアウトした。
# コンストラクタ def initialize(fname) @fname = fname # ソースファイルのファイル名 @asmfname = fname.sub(/\.myc$/,'.s') # アセンブリコードのファイル名 =begin @exefname = fname.sub(/\.myc$/,'') # 実行ファイル名 =end @lex = Lexer.new(@fname) # 字句解析 @funcname = nil # 現在処理している関数名 @labelcnt = nil # 自動生成するラベルの個数(関数単位) @literalcnt = 0 # 文字列リテラルの数 @literaltable = [] # 文字列リテラルのリスト =begin @needprint = false # print.oのリンクが必要か =end @functions = Hash.new # 関数 @lvars = nil # ローカル変数 @lvarsize = nil # スタックに確保する領域のサイズ end =begin # アセンブル、リンクコマンド def asmcmd() if @needprint then "clang-5.0 -s "+@asmfname+" print.o -o "+@exefname else "clang-5.0 -s "+@asmfname+" -o "+@exefname end end =end
elsif kind == TK::RESERVE && str == "print" then # 組み込み関数printの処理 =begin @needprint = true =end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken kind, str = expr kind, str codegen " mov edi, eax" codegen " call print" if kind != TK::SYMBOL || str != ")" then perror end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ";" then perror "expected ';' after builtin function" end
コメントアウトじゃなくて削除しちゃっても良かったかな。
動作テスト
いってみるよ。
~/myc$ myc -v myc version 0.04r ~/myc$ myc -h Usage: myc [options] -c Compile only -d Debug mode -l Lexer module test -O val Optimize level -v Version --cc val Assemble & Link ~/myc$
versionの表示とか単なる恰好付け。
~/myc$ myc m23.myc rspcheck.o ~/myc$ ./m23 rsp = 00000000fe515510 rsp = 00000000fe515500 ~/myc$
この前やりたくてできなかったことができたよ。コマンドラインで複数のファイルを指定されたときは.mycならコンパイル、それ以外はそのままclangに渡されるようにしたよ。
~/myc$ myc c.myc print.o ~/myc$ ./c 42 ~/myc$
print.oの場合の例。
~/myc$ myc c.myc print.o --cc 'gcc -no-pie' ~/myc$ ./c 42 ~/myc$
ccを使うとアセンブル、リンクに使うコマンドを指定できる。上の例ではgccを指定。-no-pieは付けないとgccに怒られるんで付けてるよ。assembly - Assembling with GCC causes weird relocation error with regards to .data - Stack Overflow
Optimize levelとか勢い余って入れちゃったけど、当然付けても何も起こらないよ。このオプションがちゃんと役立つ日は果たして来るのだろうか。
今回のはmycの機能追加ってよりは、Rubyのコマンドラインオプションの解析の仕方を理解するってのがメインだね。ここまでmycを作ってきて大分Rubyにも慣れてきたよ。でも、Rubyの深いところまではそんなに立ち入ってないかもなあ。ちなみにmycの開発目的はRubyに慣れる、暇つぶしの二つだよ。さて、次回は何やろうかな。