jjug ccc 2017 fall オレオレjvm言語を作ってみる
TRANSCRIPT
オレオレJVM言語を作ってみる
(四則演算するだけだけど)
関西Javaエンジニアの会 /
ポノス株式会社
阪田浩一 @jyukutyo
#ccc_c5
会長だけどじゅくちょー
阪田浩一 @jyukutyo
通称: じゅくちょー
関ジャバ会長
JVMが大好き
ポノス株式会社(スマホゲーム会社)
CCCと僕
• JJUG CCC 5回目となりました
–2017 Fall
–2017 Spring
–2016 Fall
–2016 Spring
–2015 Spring
ハッシュタグ
#ccc_c5ガンガンツイートを
お願いします!!
今日のゴール
JVMプログラミング言語
の作り方を持って帰る
実装対象として
四則演算
言語のデモ
今日のソースコード
• https://github.com/jyukutyo/JVM-Math-Language
構造の説明
サンプル
(1 + 2) * 3
( 1 + 2 ) * 3
レキサー
パーサー
サンプル
(1 + 2) * 3
( 1 + 2 ) * 3
テキスト
トークン
ツリー
サンプル
(1 + 2) * 3
( 1 + 2 ) * 3
言語認識器
サンプル
(1 + 2) * 3
( 1 + 2 ) * 3
ツリーをインタプリタに渡し、
定義した処理を実行させる
モジュール 入力 出力
レキサー(字句解析)
テキスト(コード)
トークン
パーサー(構文解析)
トークン AST(抽象構文木)
ASTインタプリタ
AST 実行結果
構成
モジュール ライブラリ
レキサー ANTLR 4.7
パーサー ANTLR 4.7
ASTインタプリタ Truffle 0.29
ライブラリの役割
ANTLR
ANTLR
ツリーを構築するために
“文法”を定義する
ANTLRがしてくれること
“文法”から
レキサー/パーサーの
コードを生成してくれる
“文法”を定義する
方法は?
BNF (EBNF)
(Extended)
Backus–Naur Form
たとえば
ScalaのBNFは
Scalaの仕様にある
The lexical syntax of Scala is given by the following grammar in EBNF form...Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr| Expr1Expr1 ::= ‘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.9http://www.scala-lang.org/docu/files/ScalaReference.pdf
今回のBNF
Math.g4
表示します
ANTLRは
.g4ファイルから
レキサー/パーサーを
生成する
$ antlr4 Math.g4
(このコマンドはANTLRのJARにあるToolクラスの実行)
定義した文法を
テストできる
$ antlr4 Math.g4
$ javac -cp antlr-4.7-
complete.jar Math*.java
$ grun Math prog –gui
(1 + 2) * 3
^D
ツリーとノードクラス
ProgContext
ExprContext
ExprContext ExprContext
NumberExprContextParensExprContext
NumberExprContext NumberExprContext
ExprContext ExprContextInfixExprContext
InfixExprContext
ExprContext
ただ、ANTLRの
ツリーのままでは
次のTruffleに
渡せない
ツリーの
移し替えをする
ANTLRは
tree walkingメカニズム
を提供している
tree walk
enterExpr()ProgContext
ExprContext
ExprContext ExprContext
NumberExprContextParensExprContext
NumberExprContext NumberExprContext
ExprContext ExprContextInfixExprContext
InfixExprContext
ExprContext
exitExpr()
リスナーを実装し、
ParseTreeWalkerに
渡すだけ
ParseTreeWalker
enterProg(ProgContext)
exitProg(ProgContext)
enterInfixExpr(InfixExprContext)
exitInfixExpr(InfixExprContext)
enterParensExpr(ParensExprContext)
exitParensExpr(ParensExprContext)
enterNumberExpr(NumberExprContext)exitNumberExpr(NumberExprContext)
Walker
リスナーのメソッド
リスナーのベースクラスもコマンドで
自動生成されている
MathBaseListener
表示します
今回は各ルールから
exitするタイミングで
各ノードを移す
(詳細は後述)
小まとめ
• BNFを定義し、ANTLRにパーサーを生成させた
• ANTLRのツリーをTruffleでのツリーに移し替えるため、リスナーを実装する
よりANTLRを学ぶには
モジュール ライブラリ
レキサー ANTLR 4.7
パーサー ANTLR 4.7
ASTインタプリタ Truffle 0.29
ライブラリの役割
Truffle
Truffle
Truffle
• トラフル(トリュフ)
• 言語実装用フレームワーク– ASTインタプリタ構築の基盤を提供
• Graalプロジェクトの一部– Graal自体は新しいJITコンパイラ
– Graal/Truffleを含むGraalVMは多言語実行環境となるJVM(polyglot)
– Oracle Labs主導– https://github.com/graalvm/graal/tree/master/truffle
Truffleでの多言語環境
HotSpot VM
JVMCI
Graal
JVM lang Truffle
LLVMJS R Ruby
C C++ Fortran
インタープリタ
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
– 学習用
オレオレJVM言語の実装
HotSpot VM
JVMCI
Graal
JVM lang Truffle
オレオレJVM言語
インタープリタ
(余談)Eclipse OMR
https://www.slideshare.net/MarkStoodley/javaone-2017-mark-stoodley-open-sourcing-ibm-j9-jvm
(余談)Graal & Truffle
• Graalは言語実装そのものは知らない– Truffleを間に挟んでいる(JVM言語以外)
• JITコンパイルでは(結果として)複数言語にまたがったコンパイルができる
call call
call many times
プロファイリングし
ホットな部分を
JITコンパイル
call call
もちろん
必要に応じて
Deoptimization
する
Truffleを使った言語
Graalは
必須ではなく
通常のHotSpotでも
動作はする(している)
Truffleでの言語実装
1. Truffleでのノードクラスを実装
2. ANTLRのノードからこのノードに移し替えるANTLRのリスナを実装
3. Truffleでの言語実装に必須のクラスを実装
4. (入出力などの実装)
Truffleでの言語実装
1. Truffleでのノードクラスを実装
2.
3.
4.
ノードクラス
• Truffle DSL APIを使う
–提供されるアノテーションを実装コードに付与する
–Truffleのアノテーションプロセッサがコードを生成する
–アノテーションの種類、意味はJavadocを読むしかない• ドキュメントは整備されていない
今回のノード
• 数値ノード(子ノードなし)– Long– BigDecimal(これだけでもよかったがあえて2種類)
• 演算子ノード(子ノード:2つ)– Add(+)– Subtract(-)– Multiply(*)– Divide(/)
• 括弧ノード
今回は”-1”といった書き方はサポートしない
演算子ノードのクラス図
ノードのクラス図
ノードのクラス図
かっこに対応するノード
実際のコードを
表示します
Binary/Add/BigDecimal/JVMMathLang
子ノードを持つノードの
処理は、
自動生成される
ノードのクラス図
自動生成コードを
表示します
Truffleでの言語実装
1.
2. ANTLRのノードからこのノードに移し替えるANTLRのリスナを実装
3.
4.
ANTLR Truffle
NumberExprLongNode(整数)
BigDecimalNode(それ以外)
InfixExpr
AddNodeGen(+)SubNodeGen(-)MulNodeGen(*)DivNodeGen(/)
ノードクラスの対応づけ
ParseTreeWalker
enterProg(ProgContext)
exitProg(ProgContext)
enterInfixExpr(InfixExprContext)
exitInfixExpr(InfixExprContext)
enterParensExpr(ParensExprContext)
exitParensExpr(ParensExprContext)
enterNumberExpr(NumberExprContext)exitNumberExpr(NumberExprContext)
Walker
リスナーのメソッド
MathParseTreeListener
表示します
Truffleでの言語実装
1.
2.
3. Truffleでの言語実装に必須のクラスを実装
4.
com.oracle.truffle.api.
TruffleLanguage
を継承して実装する
CallTarget parse(ParsingRequest r)
実装は1メソッドのみ
CallTarget parse(ParsingRequest r)
Truffleのエンジンが
呼び出すメソッド
CallTarget parse(ParsingRequest r)
コードをパースし、
AST表現を返す
メソッド
クラス 説明
ParsingRequestgetSource().getInputStream()で実際のコードを取得する
CallTargetTruffle.getRuntime().createCallTarget(RootNode)で生成できる
CallTarget parse(ParsingRequest r)
CallTarget parse(ParsingRequest r)
1. ParsingRequest#getSource() .getInputStream()でInputStreamを取得
2. IuputStreamをANTLRのレキサー/パーサーに渡す
3. ANTLRのツリーをリスナでTruffleのノードツリーに変換する
4. ルートノードを
Truffle.getRuntime() .createCallTarget(RootNode)に渡す
JvmMathLang
表示します
Truffleでの言語実装
1.
2.
3.
4. (入出力などの実装)
最後
テキストを
Truffleのエンジンに
どのように渡し、
結果をどのように
受け取るか?
PolyglotEngine#eval(Source)
すると
PolyglotEngine.Value
で結果が返ってくる
あとはValueからget()する
JvmMathLangMain
表示します
よりTruffleを学ぶには
• コードを読むしかありません
そもそも阪田はどうやったのか?
https://www.youtube.com/watch?v=8Lt8au76emA
このセッションのコード
は公開されていないので、
何度も見て、
見えないコードは
調べて書いて試した
Devoxxセッションのコードも
• SimpleLanguage (オレオレ言語)– https://github.com/graalvm/simplelanguage
– 学習用
ベースだった
まとめ
• ANTLR+TruffleであなたもJVM言語が作れる!
• https://github.com/jyukutyo/JVM-Math-Language
ご清聴
ありがとうございました