プログラミング言語の スコープ階層を反映した

プログラミング言語の
スコープ階層を反映した
構造化解析器
神谷 年洋† 石尾 隆* 井上 克郎*
†科学技術振興機構
さきがけ
*大阪大学 大学院情報科学研究科
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
1
背景:ソースコードを処理する
CASEツール
◆ ソースコードを処理する様々なCASEツール
■
■
コンパイラ、lint
リバースエンジニアリングツール
◆ 「ソースコードからドキュメントを作成する」
■
影響波及解析、スライス
◆ ソースコードを処理するための手法
■
2004/02/24
字句解析、プリプロセッシング、構文解析、意味解析
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
2
コンパイラ・
コンパイラの利用
ソースファイル
(文字の並び)
字句解析
ソースコード処理ツール
トークンの並び
構文解析
コンパイラ・コンパイラ
字句規則
(正規表現)
字句解析規則
(BNF)
AST等
…
意味解析
2004/02/24
出力
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
3
問題点
◆ コンパイラ・コンパイラを使ってもなお、
複雑なプログラミングを要求される
■
構文エラーからの回復処理の記述
◆構文解析器の内部処理に関する知識が必要
■
OOPLのスコープおよびアクセス制御
◆前方参照の解消
■
曖昧な構文の処理
◆(型)式
◆型<型,
2004/02/24
…>
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
4
スコープおよびアクセス制御
パッケージ
クラス
不可視
または
アクセス不可
メソッド
自動変数
他のメソッド
インスタンス変数
他のクラス
2004/02/24
公開されている場合
可視
かつ
アクセス可能
公開されている場合
公開されている場合
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
5
アプローチ
◆ 漸進的解析器
■
■
OOPLのスコープとアクセス制御の階層性=解
析器のクラス階層
ソースコードの外側から内側へと解析を進める
◆パッケージ
2004/02/24
→ クラス → メソッド
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
6
漸進的解析器の構成
•ソースコード中でクラスを定義して
•クラスのインターフェイス(公開メソッ
いる部分すべて見つけだす。 ドのシグネチャや公開インスタンス変
•parseInterface()を呼び出して、そ
数の型)を調べてPackageParser に返
れらをInterfaceParser に渡す。
す。
•PackageParser はその情報を
interfaces に追加していく。 実行順序
PackageParser
#interfaces
-code
ClassParser
+parse(partialSrc):code
#parserInterface(partialSrc)
:interface
#parseClass(partialSrc):code
InterfaceParser
#parseInterface
(partialSrc):interface
2004/02/24
#instanceVars
#methods
#parseClass(partialSrc):code
#parseMethod(partialSrc):code
MethodParser
#parameters
#parseMethod(partialSrc):code
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
7
漸進的解析器の利点
■
構文エラーの処理=構造化例外処理
◆パッケージ
■
← クラス ← メソッド
前方参照の問題が生じない/識別子が変数、メ
ソッド、型のいずれかであるかを利用した文法
を使うことができる
◆メソッドを解析する前に、インスタンス変数が判明
している
■
繰り返し開発・回帰テストへの対応
◆パッケージの解析器から順に開発していく
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
8
class C extends Object {
Object value;
C() {
super();
this.value = new Pair(new A(), new
B()).setfst(new B());
}
}
例題: Featherweight Java
◆ FWJ[7]の漸進的解析器を
開発する
■
■
■
階層的なスコープを持ち
強く型付けされた
非常に単純な構文規則
(Javaのサブセット)を持つ
class Pair extends Object {
Object fst;
Object snd;
Pair(Object fst, Object snd) {
super();
this.fst = fst;
this.snd = snd;
}
Pair setfst(Object newfst) {
return new Pair(newfst, this.snd);
}
}
class A extends Object {
A() { super(); }
}
[7] Igarashi, A., Pierce, B., and Wadler, P.:
Featherweight Java: A Minimal Core Calculus
for Java and GJ, ACM Trans. Programming
class B extends Object {
Languages and Systems, 23(3), pp. 396-450
B() { super(); }
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
(2001).
}
ルソフトウェア ワークショップ
9
構文規則
Input() : // 開始シンボル
( CL() )* <EOF>
CL() : // クラス定義
<CLASS> <ID> <EXTENDS> <ID>
<LB>
( LOOKAHEAD(2) <ID> <ID> <SEMICOLON> )*
K() ( M() )*
<RB>
字句規則
K() : // コンストラクタ
SKIP : {
<ID> <LP> ( ParamL() )? <RP>
" " | "\t" | "\n" | "\r" | "\f"
<LB>
}
<SUPER> <LP> ( ArgL() )? <RP>
TOKEN : {
<SEMICOLON>
< LP: "(" > | < RP: ")" >
( <THIS> <DOT> <ID> <EQUAL> e()
| < LB: "{" > | < RB: "}" >
<SEMICOLON> )*
| < COMMA: ","> | < SEMICOLON: ";" >
<RB>
| < DOT: "." > | < EQUAL: "=" >
ParamL() : // パラメータの並び
| < CLASS: "class" >
<ID> <ID> ( <COMMA> <ID> <ID> )*
| < EXTENDS: "extends" >
ArgL() : // 実引数の並び
| < NEW: "new" > | < RETURN: "return" >
e() ( LOOKAHEAD(2) <COMMA> e() )*
| < SUPER: "super" > | < THIS: "this" >
M() : // メソッド定義
| < ID: ( <LETTER> )+ >
<ID> <ID> <LP> ( ParamL() )? <RP>
| < #LETTER: ["\u0041"-"\u005a", "\u005f",
<LB> <RETURN> e() <SEMICOLON> <RB>
"\u0061"-"\u007a" ] >
e() : // 式
10
日本ソフトウェア科学会 第1回 ディペンダブ
} 2004/02/24
ルソフトウェア
ワークショップ
・・・
通常の解析器
Input() :
ClassParser
<LB>
( LOOKAHEAD(2) V() )*
K() ( M() )*
<RB>
V() :
<TYPEID> <ID> <SEMICOLON>
K() :
<TYPEID> <LP> ( ParamL() )? <RP>
BlockLike()
ParamL() :
<TYPEID> <ID>
PackageParser
( <COMMA> <TYPEID> <ID> )*
Input() :
M() :
( CL() ) * <EOF>
( <TYPEID> | <VOID> ) <ID>
CL() :
<LP> ( ParamL() )? <RP>
<CLASS> <ID> <EXTENDS> <ID>
BlockLike()
BlockLike()
BlockLike() :
BlockLike() :
<LB>
<LB>
( <ID> | <TYPEID> | <LP> | <RP>
( <ID> | <LP> | <RP> | <SUPER>
| <SUPER> | <THIS>
| <THIS> | <DOT> | <EQUAL>
| <DOT> | <EQUAL>
| <SEMICOLON> | <COMMA>
| <SEMICOLON> | <COMMA>
| <RETURN> | <NEW> | BlockLike()
)* 第1回 ディペンダブ
2004/02/24
11
日本ソフトウェア科学会
| <RETURN> | <NEW> | BlockLike() )*
ルソフトウェア ワークショップ
<RB>
<RB>
漸進的解析器
構文エラーを含む入力例
2004/02/24
1: class A extends Object {
2:
C() { super(); }
3: }
4: class Pair extends Object {
5:
Object fst;
6:
Object snd;
7:
Pair(Object fst, Object snd) {
8:
super();
9:
this.fst = fst;
10:
this.snd = snd;
11:
}
12:
Pair Pair(Object newfst) {
13:
return new Pair(newfst, this.snd);
14: 日本ソフトウェア科学会
}
第1回 ディペンダブ
ルソフトウェア ワークショップ
15: }
12
課題
◆ 技術的な問題
■
JavaCCは構文解析器をクラス階層内に作り込
むことを想定していない
◆ 本質的に曖昧な名前への対応
■
クラス、メソッド、フィールドが同じ名前の場
合
◆ BNFで与えられた構文全体から、
PackageParser, InterfaceParser等のための
部分規則を自動的に作り出す方法
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
13
まとめ
◆ 「漸進的解析器」を提案した
■
■
■
■
スコープやアクセス制御=クラス階層
構文エラーからの回復を構造化例外によって処
理する
識別子が型であるか無いかで異なった構文であ
ると見なす構文規則を利用できる
返し型開発、テスト駆動型開発とも親和性があ
る
◆ 例題を通じて、その開発の詳細を説明した
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
14
終わり
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
15
QA「関連技術」
[Q] 構文解析というのは古典的な領域ですの
で,提案されている着想がなぜいままでな
されていなかったかについて,若干のコメ
ントがあればと思います.
◆ よくわかりません。
◆ ただし、近年、従来の字句解析・意味解
析・意味解析という境界を崩そうとする試
みが徐々に登場しています。
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
16
QA 「曖昧な名前」
[Q] 型名と識別子の区別についてですが,例えば,以下のCプ
ログラムは正しいですが,どう思います?
struct sss { short mmm; };
typedef int mmm;
mmm foo(struct sss s){ return s.mmm; }
型名となってもフィールド名としても使えないといけないです.
◆
◆
このような、同じ名前の型とフィールドを区別するために
は、字句解析にヒューリスティックを導入するしかありま
せん。
ただし、これについてはプログラミング言語の変化を待っ
た方が良いとも思います。
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
17
QA「エラー回復処理」
[Q] クラス内部は後にして,クラス宣言を取り出す部分です
が,括弧の対応をとるというのは,そこを間違ったプログ
ラムに対して困りませんか?「<CLASS>」まで skip する
のと比較してどうでしょう?
◆
◆
外側の括弧の対応が崩れているソースコードでは、その場
でエラー回復しても大量のエラーメッセージが再生産され
るだけなので、そのような場合は考慮していません。
構造化例外処理の方が、プログラミング言語の機能を直接
使っているという点でシンプルな手法であると思います。
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
18
QA「クラス階層」
[Q] 後から足す parser はオーバライドで足すとのことですが,それだと
後から足す部分が複数あったときに,どのクラスを使ったらよいのか
困りませんか?
(中略)
おそらく関係のないClassParser と,MethodParasr をクラス拡張で関
係付けるのは間違っていて,何もしない MethodParasr の代わりにそ
れを拡張してある程度解析するようにしたStdMethodParser を使いた
いなどのときには
new StdClassParser (new MethodParser, new VariableParser)
ではなく
new StdClassParser (new StdMethodParser, new StdVariableParser)
のようにする,ということにしたほうがよいと思います.
◆
はい、そう思います。現実の利用に堪えるツールを開発することを想
定すれば、何かと制約の多い継承(導出)をがんばって使うよりは、
インターフェイスによる分離と委譲を使った方が良いでしょう。
2004/02/24
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
19
コンパイラ・コンパイラ
ベース言語
ANTLR
字句解析
構文解析
意味解析
LALR(1)
Java, C#,
C++
LL(k)
Boost Spirit
C++
LL(k)
JavaCC
Java
LALR(1)
Lex+Yacc
C
LALR(1)
SableCC
2004/02/24
Java
日本ソフトウェア科学会 第1回 ディペンダブ
ルソフトウェア ワークショップ
20