Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JJUG CCC 2017 Fall オレオレJVM言語を作ってみる

1,514 views

Published on

JJUG CCC 2017 Fall オレオレJVM言語を作ってみる(四則演算するだけだけど)

#ccc_c5
このセッションでは、四則演算するだけの簡単なJVM言語の作り方を紹介します。 言語はCode -(Lexer)-> Tokens -(Parser)-> AST -(Compiler)-> JVM bytecodeと変換して、JVMで実行します。今回Lexer/Parserの部分にANTLRを、それ以降の部分にTruffleを使います。とくにANTLRやTruffleについての事前知識は必要ありません。BNFを定義してANTLRを使ってASTを構築し、それをTruffleのASTに変換して渡すという実装部分について、各ライブラリの概要やAPIを紹介しながら説明します。すべてを丁寧に説明する時間はありませんので、このセッションで出たキーワードを各自あとで調べて学びを深めていただくことになるでしょう。この点をご了承いただいて、ぜひお越しください! なお、このセッションはDevoxxUS 2017でのOleg Šelajev-sanのセッション"How to Create a New JVM Language"にインスパイアされたセッションです (https://speakerdeck.com/shelajev/how-to-create-a-new-jvm-language-devoxx-us-17)。

対象者:ANTLR/Truffleに興味がある人、オレオレJVM言語を作ってみたい人

Published in: Technology
  • Be the first to comment

JJUG CCC 2017 Fall オレオレJVM言語を作ってみる

  1. 1. オレオレJVM言語を 作ってみる (四則演算するだけだけど) 関西Javaエンジニアの会 / ポノス株式会社 阪田 浩一 @jyukutyo #ccc_c5
  2. 2. 会長だけどじゅくちょー 阪田 浩一 @jyukutyo 通称: じゅくちょー 関ジャバ会長 JVMが大好き ポノス株式会社(スマホゲーム会社)
  3. 3. CCCと僕 • JJUG CCC 5回目となりました –2017 Fall –2017 Spring –2016 Fall –2016 Spring –2015 Spring
  4. 4. ハッシュタグ #ccc_c5 ガンガンツイートを お願いします!!
  5. 5. 今日のゴール JVMプログラミング言語 の作り方を持って帰る
  6. 6. 実装対象として 四則演算
  7. 7. 言語のデモ
  8. 8. 今日のソースコード • https://github.com/jyukutyo/JVM-Math-Language
  9. 9. 構造の説明
  10. 10. サンプル (1 + 2) * 3 ( 1 + 2 ) * 3 レキサー パーサー
  11. 11. サンプル (1 + 2) * 3 ( 1 + 2 ) * 3 テキスト トークン ツリー
  12. 12. サンプル (1 + 2) * 3 ( 1 + 2 ) * 3 言語認識器
  13. 13. サンプル (1 + 2) * 3 ( 1 + 2 ) * 3 ツリーを インタプリタに渡し、 定義した 処理を実行させる
  14. 14. モジュール 入力 出力 レキサー (字句解析) テキスト (コード) トークン パーサー (構文解析) トークン AST(抽象構文木) AST インタプリタ AST 実行結果 構成
  15. 15. モジュール ライブラリ レキサー ANTLR 4.7 パーサー ANTLR 4.7 ASTインタプリタ Truffle 0.29 ライブラリの役割
  16. 16. ANTLR
  17. 17. ANTLR
  18. 18. ANTLR • パーサージェネレータ – http://www.antlr.org/
  19. 19. ツリーを構築するために “文法”を定義する
  20. 20. ANTLRがしてくれること “文法”から レキサー/パーサーの コードを生成してくれる
  21. 21. “文法”を定義する 方法は?
  22. 22. BNF (EBNF) (Extended) Backus–Naur Form
  23. 23. たとえば ScalaのBNFは Scalaの仕様にある
  24. 24. The lexical syntax of Scala is given by the following grammar in EBNF form ... Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr | Expr1 Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] else Expr] | ‘while’ ‘(’ Expr ‘)’ {nl} Expr | ‘try’ ‘{’ Block ‘}’ [‘catch’ ‘{’ CaseClauses ‘}’] [‘finally’ Expr] | ‘do’ Expr [semi] ‘while’ ‘(’ Expr ’)’ | ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr | ‘throw’ Expr | ‘return’ [Expr] | [SimpleExpr ‘.’] id ‘=’ Expr | SimpleExpr1 ArgumentExprs ‘=’ Expr | PostfixExpr | PostfixExpr Ascription | PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’ … The Scala Language Specification Version 2.9 http://www.scala-lang.org/docu/files/ScalaReference.pdf
  25. 25. 今回のBNF Math.g4 表示します
  26. 26. ANTLRは .g4ファイルから レキサー/パーサーを 生成する
  27. 27. $ antlr4 Math.g4 (このコマンドは ANTLRのJARにある Toolクラスの実行)
  28. 28. 定義した文法を テストできる
  29. 29. $ antlr4 Math.g4 $ javac -cp antlr-4.7- complete.jar Math*.java $ grun Math prog –gui (1 + 2) * 3 ^D
  30. 30. ツリーとノードクラス ProgContext ExprContext ExprContext ExprContext NumberExprContextParensExprContext NumberExprContext NumberExprContext ExprContext ExprContextInfixExprContext InfixExprContext ExprContext
  31. 31. ただ、ANTLRの ツリーのままでは 次のTruffleに 渡せない
  32. 32. ツリーの 移し替えをする
  33. 33. ANTLRは tree walkingメカニズム を提供している
  34. 34. tree walk enterExpr() ProgContext ExprContext ExprContext ExprContext NumberExprContextParensExprContext NumberExprContext NumberExprContext ExprContext ExprContextInfixExprContext InfixExprContext ExprContext exitExpr()
  35. 35. リスナーを実装し、 ParseTreeWalkerに 渡すだけ
  36. 36. ParseTreeWalker enterProg(ProgContext) exitProg(ProgContext) enterInfixExpr(InfixExprContext) exitInfixExpr(InfixExprContext) enterParensExpr(ParensExprContext) exitParensExpr(ParensExprContext) enterNumberExpr(NumberExprContext) exitNumberExpr(NumberExprContext) Walker リスナーのメソッド
  37. 37. リスナーのベースクラス もコマンドで 自動生成されている
  38. 38. MathBaseListener 表示します
  39. 39. 今回は各ルールから exitするタイミングで 各ノードを移す (詳細は後述)
  40. 40. 小まとめ • BNFを定義し、ANTLRにパーサーを 生成させた • ANTLRのツリーをTruffleでのツリー に移し替えるため、リスナーを実装 する
  41. 41. よりANTLRを学ぶには
  42. 42. モジュール ライブラリ レキサー ANTLR 4.7 パーサー ANTLR 4.7 ASTインタプリタ Truffle 0.29 ライブラリの役割
  43. 43. Truffle
  44. 44. Truffle
  45. 45. Truffle • トラフル(トリュフ) • 言語実装用フレームワーク – ASTインタプリタ構築の基盤を提供 • Graalプロジェクトの一部 – Graal自体は新しいJITコンパイラ – Graal/Truffleを含むGraalVMは 多言語実行環境となるJVM(polyglot) – Oracle Labs主導 – https://github.com/graalvm/graal/tree/master/truffle
  46. 46. Truffleでの多言語環境 HotSpot VM JVMCI Graal JVM lang Truffle LLVMJS R Ruby C C++ Fortran インタープリタ
  47. 47. Truffleでの言語実装 • TruffleRuby (Ruby) – https://github.com/graalvm/truffleruby • FastR (GNU R) – https://github.com/graalvm/fastr • Graal.js (JavaScript) – ECMAScript 262 Version 6/Node.js • SimpleLanguage (オレオレ言語) – https://github.com/graalvm/simplelanguage – 学習用
  48. 48. オレオレJVM言語の実装 HotSpot VM JVMCI Graal JVM lang Truffle オレオレJVM言語 インタープリタ
  49. 49. (余談)Eclipse OMR https://www.slideshare.net/MarkStoodley/javaone-2017-mark-stoodley-open-sourcing-ibm-j9-jvm
  50. 50. (余談)Graal & Truffle • Graalは言語実装そのものは知らない – Truffleを間に挟んでいる(JVM言語以外) • JITコンパイルでは(結果として) 複数言語にまたがったコンパイルが できる
  51. 51. call call call many times
  52. 52. プロファイリングし ホットな部分を JITコンパイル
  53. 53. call call
  54. 54. もちろん 必要に応じて Deoptimization する
  55. 55. Truffleを使った言語 Graalは 必須ではなく 通常のHotSpotでも 動作はする(している)
  56. 56. Truffleでの言語実装 1. Truffleでのノードクラスを実装 2. ANTLRのノードからこのノードに 移し替えるANTLRのリスナを実装 3. Truffleでの言語実装に必須の クラスを実装 4. (入出力などの実装)
  57. 57. Truffleでの言語実装 1. Truffleでのノードクラスを実装 2. 3. 4.
  58. 58. ノードクラス • Truffle DSL APIを使う –提供されるアノテーションを 実装コードに付与する –Truffleのアノテーションプロセッサが コードを生成する –アノテーションの種類、意味はJavadoc を読むしかない • ドキュメントは整備されていない
  59. 59. 今回のノード • 数値ノード(子ノードなし) – Long – BigDecimal(これだけでもよかったがあえて2種類) • 演算子ノード(子ノード:2つ) – Add(+) – Subtract(-) – Multiply(*) – Divide(/) • 括弧ノード 今回は”-1”といった 書き方はサポートしない
  60. 60. 演算子ノードのクラス図
  61. 61. ノードのクラス図
  62. 62. ノードのクラス図 かっこに対応する ノード
  63. 63. 実際のコードを 表示します Binary/Add/BigDecimal /JVMMathLang
  64. 64. 子ノードを持つノードの 処理は、 自動生成される
  65. 65. ノードのクラス図
  66. 66. 自動生成コードを 表示します
  67. 67. Truffleでの言語実装 1. 2. ANTLRのノードからこのノードに 移し替えるANTLRのリスナを実装 3. 4.
  68. 68. ANTLR Truffle NumberExpr LongNode(整数) BigDecimalNode(それ以外) InfixExpr AddNodeGen(+) SubNodeGen(-) MulNodeGen(*) DivNodeGen(/) ノードクラスの対応づけ
  69. 69. ParseTreeWalker enterProg(ProgContext) exitProg(ProgContext) enterInfixExpr(InfixExprContext) exitInfixExpr(InfixExprContext) enterParensExpr(ParensExprContext) exitParensExpr(ParensExprContext) enterNumberExpr(NumberExprContext) exitNumberExpr(NumberExprContext) Walker リスナーのメソッド
  70. 70. MathParseTreeListener 表示します
  71. 71. Truffleでの言語実装 1. 2. 3. Truffleでの言語実装に必須の クラスを実装 4.
  72. 72. com.oracle.truffle.api. TruffleLanguage を継承して実装する
  73. 73. CallTarget parse(ParsingRequest r) 実装は1メソッドのみ
  74. 74. CallTarget parse(ParsingRequest r) Truffleのエンジンが 呼び出すメソッド
  75. 75. CallTarget parse(ParsingRequest r) コードをパースし、 AST表現を返す メソッド
  76. 76. クラス 説明 ParsingRequest getSource().getInputStream()で 実際のコードを取得する CallTarget Truffle.getRuntime() .createCallTarget(RootNode)で 生成できる CallTarget parse(ParsingRequest r)
  77. 77. CallTarget parse(ParsingRequest r) 1. ParsingRequest#getSource() .getInputStream()でInputStreamを取得 2. IuputStreamをANTLRのレキサー/パーサーに 渡す 3. ANTLRのツリーをリスナで Truffleのノードツリーに変換する 4. ルートノードを Truffle.getRuntime() .createCallTarget(RootNode)に渡す
  78. 78. JvmMathLang 表示します
  79. 79. Truffleでの言語実装 1. 2. 3. 4. (入出力などの実装)
  80. 80. 最後 テキストを Truffleのエンジンに どのように渡し、 結果をどのように 受け取るか?
  81. 81. PolyglotEngine#eval(Source) すると PolyglotEngine.Value で結果が返ってくる あとはValueからget()する
  82. 82. JvmMathLangMain 表示します
  83. 83. よりTruffleを学ぶには • コードを読むしかありません
  84. 84. そもそも阪田はどうやったのか? https://www.youtube.com/watch?v=8Lt8au76emA
  85. 85. このセッションのコード は公開されていないので、 何度も見て、 見えないコードは 調べて書いて試した
  86. 86. Devoxxセッションの コードも • SimpleLanguage (オレオレ言語) – https://github.com/graalvm/simplelanguage – 学習用 ベースだった
  87. 87. まとめ • ANTLR+TruffleであなたもJVM言語が 作れる! • https://github.com/jyukutyo/JV M-Math-Language
  88. 88. ご清聴 ありがとうございました

×