静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文...

24
静静静静静静静静静静静 静静静静静静静静静静静静静静静静静 静静静静静静 11M37011 静静 静静 静静静静静

Upload: kolton

Post on 22-Feb-2016

56 views

Category:

Documents


0 download

DESCRIPTION

静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法. 11M37011 市川 和央 千葉研究室. 言語内 DSL. 言語の中に作った小さな言語 目的に応じて最適な文法の言語を利用 データベースは SQL 風の文法、構文解析は BNF など より細粒度な例 : Map は連想配列、文字列のマッチは正規表現 実現には言語拡張が必要 もとの言語とは異なる字句規則 もとの言語では解析できない構文. 言語内 DSL の例. SQL + ユニットテスト + 正規表現 in Java ( 注 ) 普通の Java では書けない. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

静的型付け言語における汎用的なユーザ定義演算子を含む式の構文解析手法

11M37011

市川 和央千葉研究室

Page 2: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

言語内 DSL• 言語の中に作った小さな言語

• 目的に応じて最適な文法の言語を利用• データベースは SQL 風の文法、構文解析は BNF など• より細粒度な例 : Map は連想配列、文字列のマッチは正規表現

• 実現には言語拡張が必要• もとの言語とは異なる字句規則• もとの言語では解析できない構文

Page 3: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

言語内 DSL の例• SQL + ユニットテスト + 正規表現 in Java

• ( 注 ) 普通の Java では書けない@Testpublic void studentNumberTest() { ResultSet ids = select id from students; for (String id : ids.toList()) { assert id matches [0-9]{2}(B|M|D)[0-9]{5}; }}

SQL

正規表現ユニットテスト

Page 4: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

ユーザ定義演算子• 演算子として新しい構文を導入する構文拡張手法

• ユーザ定義二項演算子 : Scala など• mixfix operators: Agda など

• 静的型付け言語では composable• 似た構文を持つ演算子を一緒に使っても安全• 型によるオーバーロードを持つため

• 表現力が弱い• 特定の形式を持つ演算子しか導入できない• 字句レベルの拡張はできない

Page 5: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

mixfix operators• infix, prefix, postfix, outfix 演算子の総称

• infix : _ op1 _ op2 _ ... _ opn _• prefix : op1 _ op2 _ ... _ opn _• postfix: _ op1 _ op2 _ ... _ opn

• outfix : op1 _ op2 _ ... _ opn

_ はオペランドに対応 : hole と呼ぶ opi はオペレータに対応 : name part と呼ぶ

SQL のselect _ from _

Page 6: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

mixfix operators の限界• infix, prefix, postfix, outfix 以外は表現できない

• 連続するオペランドを含む構文は表現できない• 字句規則は変えられない

• ユーザ定義リテラルは作れない

assert _ _ は mixfix に含まれない

正規表現リテラルとして解析する必要 for (String id : ids.toList()) { assert id matches [0-9]{2}(B|M|D)[0-9]{5}; }

Page 7: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

提案 : protean operators• 演算子のパターンは hole と name part の並び

• assert _ _ のような演算子を含む• mixfix operators を包含

• 演算子ごとに異なる字句規則• 正規表現の演算子は正規表現のルールを持つ• ユーザ定義リテラルも表現可能

ナイーブな解析手法では時間がかかりすぎる

Page 8: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

提案 : 期待される型を利用した再帰下降構文解析• 字句・構文・型解析器が連携

• 期待される型の情報を構文解析器にフィードバック• 利用する演算子によって字句規則を変える

• 型のあわない演算子は解析に使わない• 期待される型の情報から演算子を特定• 不要な解析パスを枝刈り

• メモ化を利用してバックトラックのコストを最小化• 左再帰を許す packrat parsing を利用

Page 9: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

具体例assert id matches [0-9]{2}(B|M|D)[0-9]{5};

void 型を期待 => assert _ _ で解析assert id matches [0-9]{2}(B|M|D)[0-9]{5};

String 型を期待 => String 型の変数assert id matches [0-9]{2}(B|M|D)[0-9]{5};

Matcher 型を期待 => matches _ で解析assert id matches [0-9]{2}(B|M|D)[0-9]{5};

Regex 型を期待 => 正規表現の演算子で解析

Page 10: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

解析手順1. 型のわからない部分は通常のルールで解析

• 代入文の左辺など2. 型がわかる部分まで解析したら型解析3. 期待される型を特定4. 期待される型を返す演算子を使って構文解析

• name part は対応するトークンを読み込む字句解析• hole は対応する型を期待する型として再帰的に解析

Page 11: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

演算子の解析順序• 同じ型を返す演算子の間には順序関係が必要

• どちらの演算子が special case であるか• if _ then _ より if _ then _ else _ を優先• + と * の間の演算子優先順位とは異なる

• 構文解析器はこの順序に従って解析を試す• 成功するまでバックトラックを繰り返す• 成功した時点で終了

スライド中ではコードの上にある方を優先するものとする

Page 12: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

演算子優先順位・結合性• コンパイル時の型の書き換えにより実現

• 優先順位を含む型を作る• 結合性に従って型情報を書き換える_:Int + _:Int :: Int ( 優先順位 2, 左結合 )_:Int * _:Int :: Int ( 優先順位 1, 左結合 )

_:Int2 + _:Int1 :: Int2_:Int1 * _:Int0 :: Int1_:Int2 :: Int_:Int1 :: Int2_:Int0 :: Int1

引数の型

返り値の型

Page 13: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

サブタイプ関係• 演算子の追加により実現

• サブタイプをスーパータイプに関連付け• 演算子優先順位を考慮

Int <: Num_:Int + _:Int :: Int ( 優先順位 2, 左結合 )_:Int * _:Int :: Int ( 優先順位 1, 左結合 )

_:Int2 + _:Int1 :: Int2_:Int1 * _:Int0 :: Int1_:Int2 :: Int_:Int1 :: Int2_:Int0 :: Int1

_:Int2 :: Num2_:Int1 :: Num1_:Int0 :: Num0_:Num2 :: Num_:Num1 :: Num2_:Num0 :: Num1

Page 14: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

実装 : ProteaJ• Java 1.4 に protean operators を導入した言語

• コンパイラを Java で実装• http://www.csg.ci.i.u-tokyo.ac.jp/~ichikawa/ProteaJ.tar.gz

• 演算子モジュールと呼ばれるモジュール機構を持つ• using 節によりインポートして利用できる• 各モジュールが言語内 DSL を表現

Page 15: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

議論 :protean operators の解析の難しさ• 複数の DSL の利用は文法規則の曖昧性を発生させる

• 曖昧性は型チェックで解決する必要• 文法の非決定性も多数発生

• 字句規則の追加は特に曖昧性を発生させやすい• 例 : e には 変数、自然対数の底、正規表現リテラル等の解釈がある• 字句規則は衝突が起こりやすい

Page 16: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

ナイーブな解析手法• 字句・構文解析でありうる構文木を全て列挙• それぞれの構文木に対して型チェック

Page 17: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

ナイーブな手法の計算量• 構文木の列挙にかかるコストは O(|G|*n3)

• |G| は文法サイズ、 n は入力長• 字句・構文規則の追加は |G| を大きくする• n = 文字数なので、 n3 は非常に大きくなる

• 型チェックすべき構文木の数は爆発しやすい• 多くの部分木に影響する曖昧性があると構文木の数が増えやすい• 字句規則は多くの部分木に影響しやすい

DSL の composability が低い

Page 18: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

本手法の計算量• 本手法はほとんどの場合に O(n) で解析可能• 解析コストは通常 |G| に依らない

• 期待される型で演算子を限定するため• 同じ型を返す演算子の個数に比例するが、通常これは十分小さい

• 左再帰を許す packrat parsing は大抵の場合に O(n)• 現実的な文法では O(n) となることが知られている

Page 19: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

関連研究 : 構文マクロ• ポピュラーな言語拡張手法の1つ

• Lisp, Template Haskell, Nemerle, Boo, ScalaMacros, ...

• コンパイル時に構文木を書き換える• もとの言語と異なるセマンティクスを与えることができる• 構文規則は変えられない

• composable でない• 同じ構文木に対して複数のマクロを定義すると正しく動作しない

Page 20: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

関連研究 : リードマクロ• 強力な言語拡張手法の1つ

• Common Lisp, Template Haskell, Converge, ...

• 特定の構文内では字句・構文解析器を切り替える• 字句・構文規則は自由• 特殊な構文を必要とする

• composable でない• リードマクロ中で別のリードマクロを使えない可能性

Page 21: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

関連研究 : ユーザ定義の構文• Isabelle, OBJ3

• _ _ のような演算子を定義可能なプログラミング言語• 字句規則は変えることができない• ナイーブな解析手法であるため composability が低い

• Nemerle• ユーザ定義の構文を定義可能なプログラミング言語• 構文の最初の1単語はユニークな識別子でなければならない

Page 22: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

関連研究 : 構文解析手法• Metaborg [Bravenboer ら OOPSLA '04]

• 構文規則だけでなく字句規則の拡張も許す• ナイーブな解析手法であるため composability が低い

• Type-oriented island parser [Silkensen ら '12]• 部分木に対して型チェックしつつボトムアップ解析• 構文解析コストが |G| に比例しない• 字句規則は変えることができない

Page 23: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

まとめ• 期待される型を利用した再帰下降構文解析を提案

• 型情報を利用して解析パスを枝刈り• ユーザ定義の protean operators を解析可能• ほとんどの場合に O(n) で解析できる

• protean operators• hole と name part の並びで表現される演算子• 演算子ごとに異なる字句規則を持つ

Page 24: 静的型付け言語における 汎用的なユーザ定義演算子を 含む 式 の 構文 解析手法

発表など

• ジェネリクスや期待される型の推論• 代入文を _:TypeName[T] _:Id = _:T のような演算子で表現したい

• ローカル変数宣言などのメタレベルの表現• 演算子にメタレベルの挙動を持たせたい

今後の課題

• PPL2011 ポスター発表• ECOOP 2011 ポスター発表• PPL2012 ポスター発表

• 日本ソフトウェア科学会 第 28回大会 口頭発表「ユーザ定義演算子による内部 DSL の構成法」