Presentation is loading. Please wait.

Presentation is loading. Please wait.

ユーザ定義演算子による 内部 DSL の構成法 市川 和央 千葉 滋 東京工業大学大学院 1. Domain Specific Language (DSL) 用途に応じたミニ言語 select name from register where age < 30 SQL hello : hello.c.

Similar presentations


Presentation on theme: "ユーザ定義演算子による 内部 DSL の構成法 市川 和央 千葉 滋 東京工業大学大学院 1. Domain Specific Language (DSL) 用途に応じたミニ言語 select name from register where age < 30 SQL hello : hello.c."— Presentation transcript:

1 ユーザ定義演算子による 内部 DSL の構成法 市川 和央 千葉 滋 東京工業大学大学院 1

2 Domain Specific Language (DSL) 用途に応じたミニ言語 select name from register where age < 30 SQL hello : hello.c cc –c hello.c hello : hello.c cc –c hello.c Make 2

3 内部 DSL 一つの汎用的な言語の中で DSL を実現 ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; SQL MakeRule hello = “hello.c” cc –c “hello.c”; MakeRule hello = “hello.c” cc –c “hello.c”; Make 3 タブ

4 HashMap modifiers = { “public” -> 1 “private” -> 2 “protected” -> 4... }; HashMap modifiers = { “public” -> 1 “private” -> 2 “protected” -> 4... }; if ( 0 <= a < 10 ) {... } if ( 0 <= a < 10 ) {... } 比較 Map 4

5 従来の手法 (ホスト言語に組み込む) ソースコード変換 マクロ 柔軟な記法を用意 シンタックスシュガーを利用 5

6 柔軟な記法を用意 ☓ DSL の構文がホスト言語に制限される ◯ 複数の DSL やホスト言語と同時に利用できる 6 val rs = sql select “name” from register where (“age” < 30) if (a < 30) {... } val rs = sql select “name” from register where (“age” < 30) if (a < 30) {... } val rs = sql.select(”name”).from(register).where(“age”.<(30)) if(a.<(30)) {... } val rs = sql.select(”name”).from(register).where(“age”.<(30)) if(a.<(30)) {... } 等価 例 : Scala 構文の制限 他の DSL やホスト言語を壊さな い

7 ソースコード変換 ◯ DSL の構文がホスト言語に殆ど制限されない ☓ 複数の DSL やホスト言語と同時に利用できない 7 ResultSet rs = select name from register where age < 30; if (a < 30) {... } ResultSet rs = select name from register where age < 30; if (a < 30) {... } ResultSet rs = sql_select(name, register, sql_lt(age, 30)); if (sql_lt(a, 30)) {... } ResultSet rs = sql_select(name, register, sql_lt(age, 30)); if (sql_lt(a, 30)) {... } 変換 自由な構文 もとの意味を破壊

8 提案: ユーザ定義演算子によ る内部 DSL の構成 8

9 ユーザ定義演算子の利用 DSL の構文を N 項演算子として表現 N 項演算子の形式は自由 ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; 三項演算子 select from where 二項演算子 < 9

10 期待される型により演算子を制限 返り値の型が期待される型である場合のみ有効 に オペランドも再帰的に制限される ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; 10

11 期待される型により演算子を制限 返り値の型が期待される型である場合のみ有効 に オペランドも再帰的に制限される ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; ResultSet 型が期待されるので、 select...from...where... が有効 に 11

12 期待される型により演算子を制限 返り値の型が期待される型である場合のみ有効 に オペランドも再帰的に制限される ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; ResultSet 型が期待されるので、 select...from...where... が有効 に 条件節が期待されるので、 専用の < 演算子が有効に 12

13 演算子によって文法を切り替える 利用される演算子に応じて解析ルールを変更 13 ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; select...from...where... を利用するので、 select, from, where はキーワードで、 name はカラム 名 条件節の < 演算子を利用するので、 < はキーワードで、 age はカラム 名

14 従来の手法の問題点を解決 ◯ DSL の構文がホスト言語に殆ど制限されない  演算子によって文法が切り替わる ◯ 複数の DSL を組み合わせて利用できる  期待される型により有効な演算子が制限される 14

15 制限 型推論との相性が最悪 クラス定義等の式より大きいものは表現できない 左辺値等の期待される型がない所では利用できな い 動的型付け言語では不可能 15

16 LasticJ Java のサブセットに前述のアイデアを導入 ジェネリクス・インターフェース等はなし コンパイラは Java により実装 名前は変わるかも 16

17 Select 文の作り方 17

18 三項演算子 select from where の定義 メソッド定義と似た形式 処理内容はホスト言語で記述 18 ResultSet “select” col “from” table “where” cond (readas SQLColumn col, SQLTable table, SQLCond cond) : priority = 200 { Connection conn =...; Statement stmt = conn.prepareStatement(...); return stmt.executeQuery(); } ResultSet “select” col “from” table “where” cond (readas SQLColumn col, SQLTable table, SQLCond cond) : priority = 200 { Connection conn =...; Statement stmt = conn.prepareStatement(...); return stmt.executeQuery(); } 返り値の型 処理内容 キーワー ド オペランドの 型 オペラン ド

19 演算子モジュール 同一の機能に関する演算子を集めたもの 演算子定義はここに記述 19 operators SQLOperators { ResultSet “select” col “from” table “where” cond (readas SQLColumn col, SQLTable table, SQLCond cond) : priority = 200 {... } SQLCond col “<“ val (readas SQLColumn col, int val) : priority = 100 {... }... } operators SQLOperators { ResultSet “select” col “from” table “where” cond (readas SQLColumn col, SQLTable table, SQLCond cond) : priority = 200 {... } SQLCond col “<“ val (readas SQLColumn col, int val) : priority = 100 {... }... } 三項演算子 select from where 二項演算子 <

20 演算子の利用 using 節により利用する演算子モジュールを指定 20 using lasticj.test.sql.SQLOperators; class Test { public void run() { SQLTable register =...; ResultSet rs = select name from register where age < 30;... } using lasticj.test.sql.SQLOperators; class Test { public void run() { SQLTable register =...; ResultSet rs = select name from register where age < 30;... } SQLOperators を利用 SQLOperators の演算子を利用して解釈

21 ResultSet “select” col “from”... (readas SQLColumn col,...)... ResultSet “select” col “from”... (String col,...)... readas 修飾子 オペランドの位置の単語をリテラルとして読む 21 ResultSet rs = select “name” from register where “age” < 30; ResultSet rs = select “name” from register where “age” < 30; ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; SQLColumn 型の リテラルとして解 釈 このようなダブル クォートは書きたくな い

22 実装について 22

23 コンパイラ全体 Java により実装 ( 約 8000 行 ) 宣言部分と本体部分を分けて解析 コード生成には javassist を利用 23

24 本体部分の構文解析法 Packrat parsing のアルゴリズムを利用 字句解析器・構文解析器・型チェッカーが一体 化 式の解析は次のようにして行う 1. 期待される型を返す演算子を優先度順に試す 2. 最初に成功したものを結果として返す 3. 全て失敗した場合、通常の Java のルールで解析する 24

25 関連研究 SugarJ [ S. Erdweg ら ’11 ] ユーザがシンタックスシュガーをライブラリとして定 義 文法が衝突すると一方が破壊される Template Haskell [ T. Sheard ら ‘02 ] 型安全なコンパイル時メタプログラミング ユーザが直接構文木を操作しなければならない Scala メソッド呼び出しを単項・二項演算のように記述でき る 実現可能な内部 DSL の文法に制約がある 25

26 まとめ ユーザ定義演算子による内部 DSL の構成法を提 案 このアイデアを導入した言語 LasticJ を作成 26 今後の課題 記述力の強化 様々な内部 DSL を実際に構築する

27 27 operators SQLOperators { ResultSet “select” col “from” table “where” cond (readas SQLColumn col, SQLTable table, SQLCond cond) : priority = 200 {... } SQLCond col “<“ val (readas SQLColumn col, int val) : priority = 100 {... } } operators SQLOperators { ResultSet “select” col “from” table “where” cond (readas SQLColumn col, SQLTable table, SQLCond cond) : priority = 200 {... } SQLCond col “<“ val (readas SQLColumn col, int val) : priority = 100 {... } } ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30;

28 28 MakeRule hello = “hello.c” cc –c “hello.c”; MakeRule hello = “hello.c” cc –c “hello.c”; operators MakeOperators { MakeRule file “\t“ script (String file, Script script) : priority = 100 {... } Script command option file (readas Command command, Option option, String file) : priority = 100 {... } Option “-” op (readas String op) : priority = 100 {... } } operators MakeOperators { MakeRule file “\t“ script (String file, Script script) : priority = 100 {... } Script command option file (readas Command command, Option option, String file) : priority = 100 {... } Option “-” op (readas String op) : priority = 100 {... } }

29 29 operators BoolOperators { boolean a “<=“ b “<“ c (int a, int b, int c) : priority = 1000 {... } } operators BoolOperators { boolean a “<=“ b “<“ c (int a, int b, int c) : priority = 1000 {... } } if ( 0 <= a < 10 ) {... } if ( 0 <= a < 10 ) {... }

30 30 注) 現在の実装ではジェネリクス及び 0 回以上の繰り返しを示す... には 対応していないため、実際には余分なクラスと演算子をいくつか作る 必要がある。 HashMap modifiers = { “public” -> 1 “private” -> 2 “protected” -> 4... }; HashMap modifiers = { “public” -> 1 “private” -> 2 “protected” -> 4... }; operators MapOperators { Map “{“ entries “}” (MapEntry... entries) : priority = 100 {... } MapEntry key “->” val (K key, V val) : priority = 100 {... } } operators MapOperators { Map “{“ entries “}” (MapEntry... entries) : priority = 100 {... } MapEntry key “->” val (K key, V val) : priority = 100 {... } }

31 演算子優先順位 演算子ごとに優先度を設定 優先度が同じ場合は using 節の順序により決定 31 大 後 小 前 優先度 using 順序 AST

32 さらなる利点 IDE との相性が非常に良い ホスト言語を単純化できる 32

33 readas 修飾子 N 項演算子のオペランドに対して指定 単語を与えられた型のリテラルとして読む事を 示す 一旦文字列として読み、コンストラクタで変換 33 ResultSet “select” col “from” table “where” cond (readas SQLColumn col, SQLTable table, SQLCond cond) : priority = 200 {... } ResultSet rs = select name from register where age < 30; ResultSet rs = select name from register where age < 30; readas SQLColumn SQLColumn 型 リテラルとして解 釈


Download ppt "ユーザ定義演算子による 内部 DSL の構成法 市川 和央 千葉 滋 東京工業大学大学院 1. Domain Specific Language (DSL) 用途に応じたミニ言語 select name from register where age < 30 SQL hello : hello.c."

Similar presentations


Ads by Google