Your SlideShare is downloading. ×
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Boost.Spirit.QiとLLVM APIで遊ぼう
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Boost.Spirit.QiとLLVM APIで遊ぼう

311

Published on

@歌舞伎座.tech#8(2015/05/17)

@歌舞伎座.tech#8(2015/05/17)

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
311
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Boost.Spirit.QiとLLVM APIで 遊ぼう NV(@nvsofts)
  • 2. 自己紹介 •  HN:NV •  Twitter:@nvsoftsでやっています(*´ω`*) •  Gentoo大大学院修士2年 •  (株)ウサギィでC++とかDとかPythonとかを使ってバイト しています •  昔にプログラミング言語KQなんてものを作りました
  • 3. 本日のテーマについて •  この2つを使ってプログラミング言語の処理系を作るとき の話 •  Boost.Spirit.Qi •  LLVM API (こう書くと多方面からフルボッコにされそうですが…)
  • 4. こんな感じに
  • 5. 作成した処理系のソース •  2つのライブラリを使って作成した処理系は、GitHubに公 開しています(少し前に作ったものです) https://github.com/nvsofts/GikoLLVM/ •  コードをちらっと見ながらだと分かりやすいかも…? •  個人差があります
  • 6. Boost.Spirit.Qiとはなんぞや •  オープンソースライブラリであるBoost C++ Librariesに 含まれている構文解析のためのライブラリ •  Qiは「キー」って読むらしい(from:BoostConの動画) •  演算子オーバーロードを巧みに使い、EBNFっぽいコード を書くだけで構文解析器ができあがる •  構文規則に対するアクションは、Boost.Phoenixという無 名関数記述ライブラリが使うことで楽に書ける
  • 7. Boost.Sprit.Qiの使い方 •  基本的に文法を定義して、以下の関数を使って構文解析 を行うことで使う •  boost::spirit::qi::phrase_parse! •  空白など、解析時に飛ばしたいもの(Skipper)があるときに使う •  boost::spirit::qi::parse! •  上のものとは違い、飛ばしたいものがないときに使う •  ここでは、空白を飛ばしたいのでphrase_parseを使う
  • 8. Boost.Sprit.Qiの使い方 •  phrase_parseのコード例 using namespace boost::spirit;! ! std::string input;! std::vector<int> result;! auto it = input.begin();! ! bool success = qi::phrase_parse(it, input.end(), int_ % ',', ascii::space, result);! •  解析が成功していればsuccessがtrueかつ、it == input.end()になっている
  • 9. ルール •  文法は1つ以上のルールで構成され、 boost::spirit::qi::ruleのインスタンス •  セマンティックアクションと呼ばれる、そのルールが使わ れたときに呼ばれる関数を設定できる •  当然、渡すのは関数オブジェクトでもOK •  しかし、C++11のラムダ式や関数オブジェクトでやるには少 し面倒な要素が含まれているので、Boost.Phoenixを使った 無名関数を使うのがおすすめ
  • 10. 標準で用意されている文字種 名前 概要 型 char_ 任意の1文字 char alpha アルファベット1文字 char digit 数字1文字 char alnum アルファベットまたは数字1文字 char print 印刷可能な文字1文字 char int_ 符号付き整数 int uint_ 符号無し整数 unsigned int float_ 単精度浮動小数点数 float double_ 倍精度浮動小数点数 double bool_ 真偽値 bool
  • 11. ルール同士の結合 •  ルール同士の結合には、>>演算子を使う •  注意するべきことは、>>演算子が入るところにはSkipper のような無視するべき文字列が入ることがあること •  これは、識別子のような途中にSkipperが入るとまずい場合 に遭遇したときに対応しないといけない •  途中でSkipperが入って欲しくない場合は、lexeme[〜]で 囲えばOK
  • 12. ルールの繰り返しと選択 •  標準の文字種はほとんど1文字のみ読むもので、複数文 字読みたい場合は繰り返しルールを適用する必要がある •  0回以上の繰り返しにはルールの前に*演算子を付ける •  1回以上の繰り返しにはルールの前に+演算子を付ける •  また、複数のルールのうちどれかを選択するようなルー ルを記述する場合がある •  ルールの選択には|(パイプ記号)演算子を使う
  • 13. Boost.Phoenixの概要 •  プレースホルダーと組み合わせると、演算子オーバー ロードを駆使して目的の関数オブジェクトを作ってくれる •  プレースホルダーとして以下のものなどがある •  _1, _2, …, _N! •  N番目の引数、Boost.Spirit.Qiではほとんど_1のみ使用 •  _val! •  ルールや文法における、最終的に返す値 •  _pass! •  falseを代入すると、強制的に構文解析を失敗させる
  • 14. Boost.Phoenixの概要 •  基本的に、プレースホルダーを使ってやりたいことを書け ばOK •  複数の処理を行いたい場合は、カンマで区切る •  しかし、一部の処理は記法が違うので注意 •  例:new演算子 •  _new<T>(引数)! •  例:コンテナへのpush_back! •  push_back(対象, 値)!
  • 15. Boost.Phoenixの概要 •  構造体やクラスへのアクセス •  phoenix::at_c<N>を使う •  例:返す構造体の0番目の要素に1番目の引数を代入する phoenix::at_c<0>(_val) = _1! ! •  独自の構造体やクラスを使う場合は、 BOOST_FUSION_ADAPT_STRUCTマクロでの登録が必要!
  • 16. 複雑な文法を定義する •  前の例では簡単な文法だったので引数に入れた •  しかし、複雑なルールを含んだ文法になる場合は、別途 構造体等を定義してそこに入れる方が良い場合もある •  文法はboost::spirit::qi::grammarを継承して 作る(テンプレート引数は最高で4つ) •  Iterator:使用するイテレータの型 •  A1:最終的に返す型、()を付ける •  A2:飛ばすべきものを示した文法(Skipper)の型 •  A3:文法内のローカル変数の型、通常は使わない
  • 17. 複雑な文法を定義する •  カンマ区切りの整数を解析してstd::vector<int>として返すような文法の例 template <typename Iter, typename Skip>! struct grammar : qi::grammar<Iter, std::vector<int>(), Skip>! {! template <typename T>! using rule = qi::rule<Iterator, T, Skipper>;! ! rule<std::vector<int>()> int_rule;! ! grammar() : grammar::base_type(int_rule)! {! int_rule = int_[push_back(_val, _1)] >> ',' >> *(int_[push_back(_val, _1)]); ! }! };
  • 18. Boost.Spirit.Qiで抽象構文解析木(AST)を作る •  ルールを書き、それに対するセマンティックアクション内で ルールに対応するASTのインスタンスを生成して返す •  _newが必要なのはこのため •  例 template <typename T> using rule = qi::rule<Iterator, T, Skipper>; rule<AssignAST *()> assign;! assign = id[_val = new_<AssignAST>(_1)] >> '=' >> expr[phoenix::at_c<1>(*_val) = _1];! (ここで、idは識別子、exprは式を表すルールとする)!
  • 19. ASTはできたが… •  このASTをネイティブコードへ変換しないといけない •  ネイティブコードへの変換は気が遠くなるような作業 •  LLVM APIなら、ネイティブコードへの変換を比較的容易 に行える
  • 20. LLVMとはなんぞや •  任意のプログラミング言語に対応可能なオープンソース のコンパイラ基盤 •  Clangなどで使われている •  仮想機械をターゲットとしたIRと呼ばれる中間コードを扱 う •  IRはネイティブコードへ変換することができる •  つまり、IRへの変換プログラムだけ書けばどんなプログラミ ング言語にも対応できる •  C++で記述されており、APIも豊富
  • 21. LLVM IRを出力する •  LLVM IRはC++においてはクラスのインスタンスで表現さ れるが、インスタンスを作る方法も物によってまちまち •  そこで、LLVM APIにあるIRBuilderを使うことで、簡単 にLLVM IRのC++による表現を作ることができる •  IRBuilderに含まれる関数の一部 •  CreateAdd(左辺値, 右辺値, 命令に付けるラベル) •  加算命令を生成する •  CreateCall(関数, 引数の配列) •  関数の呼び出し命令を生成する
  • 22. IRBuilderの実際の使用例 •  2項演算子の処理部分 •  ASTの注目している葉に含まれている演算子の情報から 対応する命令を生成する関数を呼んでいる std::string &op = bin_expr->getOp(); if (op == "+") { return this->builder->CreateAdd(v_lhs, v_rhs, "add"); }else if (op == "-") { return this->builder->CreateSub(v_lhs, v_rhs, "sub"); }else if (op == "*") { return this->builder->CreateMul(v_lhs, v_rhs, "mul"); }else if (op == "/") { return this->builder->CreateSDiv(v_lhs, v_rhs, "div"); }else if (op == "%") { return this->builder->CreateSRem(v_lhs, v_rhs, "rem"); }
  • 23. LLVM IRのファイルへの出力 •  LLVM IRのC++による表現ができあがったら、ファイルへ 出力する •  これにはraw_fd_ostreamとWriteBitcodeToFile 関数を使う using namespace llvm; Module *module; std::error_code error; raw_fd_ostream raw_stream("out.bc", error, sys::fs::OpenFlags::F_RW); WriteBitcodeToFile(module, raw_stream); raw_stream.close();
  • 24. ネイティブコードへの変換 •  ネイティブコードへの変換は、llvm-asコマンドを使う •  $ llvm-as <*.bcへのパス>! •  これだけで、ネイティブなアセンブリ言語のコードを得られる •  Clangが入っているのなら、clangコマンドに直接*.bcを投 げ込んでも大丈夫 •  ネイティブコードに変換したら、あとは用意した標準ライブ ラリとリンクして実行可能なファイルを得るだけ
  • 25. 全体の流れ 1.  ソースコードを読み込む 2.  Boost.Spirit.Qiによってソースコードの構文解析を行 い、ASTを作成する 3.  作成されたASTをもとに、LLVM APIのIRBuilderで LLVM IRを作成する 4.  作成されたLLVM IRをファイルへ出力する 5.  ファイルへ出力されたLLVM IRをネイティブコードへと 変換し、実行可能な形式にする
  • 26. 参考になりそうなページや本 •  Let’s BoostのBoost.Spiritの項目 •  http://www.kmonos.net/alang/boost/classes/spirit.html •  きつねさんでもわかるLLVM •  少し現在のLLVMでは動かないコードがありますが、基本は おさえています •  ライブラリのドキュメントやDoxygen •  http://www.boost.org/libs/spirit/ •  http://llvm.org/docs/ •  http://llvm.org/doxygen/
  • 27. まとめ •  Boost.Spirit.QiとLLVM APIを組み合わせることで、プロ グラミング言語の処理系を生産性を確保して作成できる •  これなら「ネタ言語って言ってもどうせBrainf*ck系で しょ?」って言われても切り返せるネタ言語が作れる •  LLVM IRの形にしてしまえば、LLVMに付属している最適 化機構が使えるので本質のみに集中できる •  この組み合わせで実用的なネタ言語(?)を作ろう!

×