jjug ccc 2017 fall オレオレjvm言語を作ってみる

Post on 21-Jan-2018

652 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

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

• パーサージェネレータ– http://www.antlr.org/

ツリーを構築するために

“文法”を定義する

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

ご清聴

ありがとうございました

top related