1 / 27

ユーザ定義演算子による内部 DSL の構成法

ユーザ定義演算子による内部 DSL の構成法. 市川 和央 千葉 滋 東京工業大学大学院. Domain Specific Language (DSL). 用途に応じたミニ言語. SQL. select name from register where age < 30. Make. hello : hello.c cc –c hello.c. 内部 DSL. 一つの汎用的な言語の中で DSL を実現. SQL. ResultSet rs = select name from register where age < 30 ;. Make.

clea
Télécharger la présentation

ユーザ定義演算子による内部 DSL の構成法

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  16. Select文の作り方

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

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

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

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

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

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

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

  24. ResultSetrs = select name from register where age < 30; operators SQLOperators { ResultSet “select” col “from” table “where” cond (readasSQLColumn col, SQLTable table, SQLCondcond) : priority = 200 { ... } SQLCond col “<“ val (readasSQLColumn col, intval) : priority = 100 { ... } }

  25. MakeRule hello = “hello.c” cc –c “hello.c”; operators MakeOperators { MakeRulefile “\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 { ... } }

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

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

More Related