• 検索結果がありません。

プログラミング言語処理系論 (4) Design and Implementation of Programming Language Processors

N/A
N/A
Protected

Academic year: 2021

シェア "プログラミング言語処理系論 (4) Design and Implementation of Programming Language Processors"

Copied!
55
0
0

読み込み中.... (全文を見る)

全文

(1)

プログラミング言語処理系論

(5)

Design and Implementation of

Programming Language Processors

佐藤周行

(情報基盤センター/電気系専攻融合情報学 コース)

(2)

今日やること

 Abstract Syntax Tree構築後 – 意味解析

◼ Symbolの解析

◼ Perlの吐き出すコードの観察

 環境と変数のバインディング

◼ フレームの構築

 Source Tree Traversalによる実行

(eval)

(3)

文法要素以降の作業

High Level Concept

Syntax Design Semantics Design Parsing

Semantic Analysis(Type/Class Check, Name Resolution) Code Generation Execution Optimization Optimization Interpreter Compiler VM

(4)

Parse → ASTの構築。その後

 シンボルテーブルの管理 ◼ 大域変数の管理 ◼ 局所変数の管理 ◼ 動的な言語では、実行時にも使用 ◼ 静的にすべての名前解決をする処理系ではリンク時まで  コードセグメントの管理 ◼ 関数コードの保存  データセグメントの管理 ◼ 変数にデータをバインドする ◼ では、一般的には、何を用意するとプログラムの実行に十 分なのか? ◼ 「実行環境」(次回以降のテーマ)

(5)

とにもかくにも

 Parserが実際に何を出力するか、ASTを観 察してみる ADD 0 3 MUL 1 2 LIT 5 LIT 4 LIT 3 --3 + 4 * 5 式の作る木構造をそのまま表現

(6)

 制御構造も木構造で表現 while (i > 0) { r := r * i; i := i – 1; }  VMを設計する場合は、ここか らさらにコンパイルする (Compiler)  このまま実行できるか? (Interpreter) (WHILE, 2, 11) (COMP, 6, 10) (MOV, i, 9) (SUB, 7, 8) (LIT, 1) (VAR, i) (MOV, r, 5) (MUL, 3, 4) (VAR, i) (VAR, r) (GT, 0, 1) (LIT, 0) (VAR, i)

(7)

Symbol table

 「名前」を格納する領域 は?  名前空間  スコープ ◼ 局所変数、大域変数  何を格納する場所を用意 するのが良いのか? (ヒープの設計)  まずは変数管理のために オブジェクトのテーブルを 作る

typedef struct _vardat { int kind; int name; void *val; int param; int paramlen; int res_t; } t_vardat; t_vardat vars[];

(8)

CPythonでは…

 symtable.c でやっていること ◼ ASTをトラバースして、 ◼ 変数の出現の観察 ◼ どの変数と変数の参照が同一のものかの同定 ◼ コード生成のときに本質的な情報として利用  Pythonの不思議な変数利用規約 ◼ 未定義の変数への参照は、大域変数 ◼ 一旦代入されると局所変数へ  実行例を観察する

(9)

symtable.cを見る

 ASTをトラバースして解析 ◼ 後述するが、この準同型ライクな解析はAbstract Interpretaionの基本  変数の解析 ◼ 「環境」ごとにDictionaryを準備 ◼ その中で、大域変数、定義されていない大域変数 、局所変数を同定

(10)

中心となる関数

#define SET_SCOPE(DICT, NAME, I) { ¥ PyObject *o = PyLong_FromLong(I); ¥ if (!o) ¥ return 0; ¥ if (PyDict_SetItem((DICT), (NAME), o) < 0) { ¥ Py_DECREF(o); ¥ return 0; ¥ } ¥ Py_DECREF(o); ¥ }

(11)

 analyze_name() ◼ 与えられたスコープを設定した上で解析  analyze_block() ◼ Scopeが変更され得る  Scopesを…  localsを… ◼ Scopeの調整をしてanalyze_nameの呼び出し

(12)

 symbol_update()

 ここまでがベースとなる処理

 次からのsymtable_visit_*()

(13)

関数の導入

 関数の導入 ◼ コードセグメント(関数本体) ◼ フレーム  フレーム=関数呼び出しを実行するための環境  大域変数、局所変数  スコープの管理  引数の渡し方(binding/association) ◼ コンパイル言語なら、パラメタと実引数の対応をきちんとと ることが前提

(14)

オブジェクトの導入

 Classの導入 ◼ Class 階層の導入  Extends keyword ◼ Access の許認可の問題  Private/Protected/Public  Type/Class Check  Semantic Analysis の最大の問題の一つ ◼ Static/Dynamic ◼ Subclass ◼ Polymorphic Types

(15)

ASTはそのまま実行できるか?

 Perl5を見てみましょう。

◼ Parse Tree → ASTをほぼそのまま保存 ◼ Tree Traversalでコードを実行  Interpreter方式で古典的な方式の一つ  今まで説明に使ってきた(電卓+)は、Parse Treeをそのまま実行コードにしていた  では、関数呼び出しは?スコープの管理は? ◼ 実行時のサポートが必要

(16)

実は、

Perlにおいて

 実はPerl5において ◼ 変数はグローバル ◼ myを使ってローカルな変数を定義できる ◼ Perl5の前近代的な部分 ◼ Objectが導入できなかった ◼ 名前空間を制御するmoduleでclassとobjectが 管理できると考えたのが… ◼ Rakuはどうなるのでしょうか?

(17)

Perl5で関数を実現?

 この方針にしたがって関数コールを実現して みる ◼ フレームを作る ◼ (スタック)フレームとは:関数呼び出しごとに作ら れるローカルな情報を格納する場所 ◼ これは、ASTを観察するとある程度見えてくる  次の次のスライド以降  関数の中でのスコープの扱いは?

(18)

もっとおそろしい言語があってな

 Fortranのごく初期においては ◼ 関数呼び出しにおいて、関数呼び出しごとの実行環境 (フレーム)は関数ごとに固定 ◼ グローバルな変数は存在せず、EQUIVALENCE文で 関数コールごとに対応を指定  (課題*:考古学) Fortranの関数コールにおける フレームの作り方について調査せよ。Fortranは 、「再帰」を理解できないプログラマを大量に養成 したといわれる(半分デマ)が、実際Fortranでは 特に指定しない限り再帰が書けない。その理由を フレームの作り方と関連付けて述べよ

(19)

Perlの実際

 Perl –MO=Concise,関数名,-src ファイル 名 ◼ B::Conciseモジュールを使ってみる  perl –MO=Concise,factorial,-src fact.pl

(20)

fact.pl

sub factorial { local($i) = @_; $r = 1; while ($i>0) { $r = $r * $i; $i = $i-1; } return $r; } $i = 7; Print factorial($i);

(21)

$ perl -MO=Concise,factorial,-src fact.pl main::factorial:

(22)

PerlのAbstract Syntax Tree

 Perlは、コードセグメントはほぼAST  実行のための最小限のヒープ、スタックが用 意されている ◼ Pushmarkなど、関連するコードが挿入されてい る  B::Conciseで内容を見ることができる

(23)

 似たことは、Javaのdisassemble

◼ javap -c

 Pythonのdisassemble

◼ import dis; dis.dis()でも

 両者とも、仮想マシン上の命令列を出力しま す

 この違い(Source Tree Traversal vs. Compile)が次のセクションの大きなテーマ

(24)

 Perl5の実行について (課題4) PerlのB::Conciseモジュールを利 用して、以下についてレポートせよ (1) 適当なプログラムに対してのソースとパー ス木の対応(-src)の観察 (2) 実行に際して必要となるデータ構造(ス タック、フレーム、ヒープ) ◼ (2)について無理する必要はありません

(25)

その他大切なこと:パス

 Def. Pass ◼ ソースプログラム(および中間生成物)を処理する 一単位。  Def. One-Pass ◼ パス一つ(パースを終了させるまで)でコード生成 まで済ませる  Def. Multi-Pass ◼ 複数のパスから構成されるもの。一般的にはパー スをした後で、解析(最適化等)をするパスを追加 する

(26)

複数パスの表現

 ソースファイル中での表現

main(int ac, char **av) { if (ac >= 2){ if (!strcmp(*++av, "-d")) debug = 1; } code = yyparse(); if (!code) exit(1); nextPass(Entry); } Node *Entry; Top: … {Entry = …}; /* 最初か最後に、後々の 処理のエントリーポイン トを設定しておく */

(27)

パスの考え方

 近年の考え方では、パースまでと、パース以 後の最適化をわけています。  パースまででできることは高が知れていると認 識が一般的になりました。 ◼ 最適化ルーチン  インタラクティブな言語では、パースまでの処 理と、それ以後の処理(最適化等)のバランス が問題になっています。

(28)

文法要素以降の作業

High Level Concept

Syntax Design Semantics Design Parsing

Semantic Analysis(Type/Class Check, Name Resolution) Code Generation Execution Optimization Optimization Interpreter Compiler VM

(29)

Semantic Analysis

 symtable.cの中を見てきた  typecheck, classcheck

(30)

プログラムの実行

 Source Tree Traversalによる実行

◼ Perl5はこの方式を取っています。

◼ 直感的でわかりやすい

 簡単なものについてeval関数を書くことは straightforwardにできる。

◼ e.g. [ WHILE e st] = while [e] [st] ◼ e.g. [x := e] = [x] := [e]

◼ [f(e1,e2, …, en)] = [f]([e1],[e2],…[en])

(31)

Source Tree Traversal

 ASTをそのまま実行  evalの構造を見てみる  構造を保ったまま変換することを一般的に homomorphism (準同型)と言う ◼ 式の評価については、準同型

◼ assignment, function call等、少数のものにつ いては実行側で少し準備が必要

(32)

Source tree traversalに向けてさまざまな準備  コードの配置 ◼ 関数定義の実体  データオブジェクトの配置 ◼ データセグメントを定義する  変数のハンドリング

◼ symbol table + (code/data segment)

 関数コールの設計

(33)

alternative

 コンパイラシステムの構築

◼ 仮想マシンの設計 (ISA決めないといけない)

 stack machine vs. register machine

◼ 仮想マシン上のコンパイラ設計、コード生成

◼ 仮想マシン上での実行

 変数の評価

◼ By name (スクリプト言語では特に)

(34)

Type Check and Class Check

 Typeとは何か?

 基本型(primitive types)と派生型(derived type)(導来型)

◼ int, float, double, bool as primitive types ◼ As derived types,  Function type  Pointer type  Array type  Structure type  Union type

(35)

Type導入の目的

 Type導入の目的は2つ ◼ プログラムが正しく動作するための情報  e.g. ポインタどうしの足し算をしてはいけない ◼ プログラムが効率的に動作するための情報  プログラム内で+が出てきたときに、int対象の+か double対象の+かがわかると、効率的なコードが生 成できる  特に関数呼び出しの時の静的型チェックが歴 史的に重要視されてきた

(36)

Type Check

 変数、関数は型とともに宣言する

 型に関するルールを推論規則の形で書く e.g. a: point(int) → *a: int

f: (int, double)(int), i:int, d: double → f(i, d) : int

 プログラム中、推論規則を適用して、データ( 式)の型を決めていく

(37)

図式として

 下の図式はCommuteするか?

Program

Type

Value

Compute (dynamic) Typing (static)

(38)

すすんだ話題

 式が、複数の型を持つと考えられることがある e.g. (lambda (x) x)  Polymorphic Typesにより、複数の型を持 つことを表現することができる  現在、実用になっている(C++, Java, 多くの 関数型言語)polymorphic type system

◼ ad hoc polymorphism

(39)

C++では

 Signature ◼ Classの持つメンバーの集合  Pseudo Signature ◼ Signatureが特定のメンバーを持つかどうか ◼ これは1990年代の泥臭い成果があって…  SML# ◼ C++の人は多分これに気づいていない

(40)

抽象実行

(Abstract Interpretation)

 Type checkのためのコードは、evalの簡略版として 書くことができる ◼ 最初に変数宣言を強制するところでは、あらかじめ与えら れた型との整合性をチェック ◼ そうでなければ、一番「一般的な型」を想定してtype check 開始  CSでは、プログラム実行(動的)の前にプログラムの 性質を解析することが重要なテーマの一つになって いた ◼ Static Analysis

 Type Checkは、Static Analysisの重要な分野の 一つ(だった)

(41)

例えば

 eval([PLUS, e1, e2]) = [e1]+[e2]

に並行させて

 tychk([PLUS, e1, e2]) =

if (tychk(e1) == INT && tychk(e2)== INT)

INT;

(42)

CPython

 型チェックは動的に行われる

◼ Modules/mathmodules.c

◼ string → double, long → double, …

 クラスのチェック

◼ このチェックも基本的に動的

(43)

Cpythonのmathmodule.c

 ASSIGN_DOUBLE()を見てみる ◼ PyFloat_CheckExact()  これが動的型チェック  PyFloat_As ◼ PyFloat_AS_DOUBLE() (Include/floatobject.h) ◼ PyFloat_AsDouble() (Objects/floatobject.c) ◼ その他 Py*_From*()

(44)

Polymorphicな場合

 一番一般的な型(クラス)を最初に割り当て

 情報が増えるごとに具体化

 Milnerのアルゴリズム for predicative p-types Robin Milner. A theory of type

polymorphism in programming. Journal of Computer and System Sciences,

17(3):348–375, 1978.

(世間の評判:本人、何やっていたか、当時絶対わか

っていなかった→この論文は読んでも無駄です)

 後々、unificationを援用したすっきりしたアルゴリズ ムにまとめられた

(45)

スクリプト言語では

 柔軟な表現を求める ◼ 1+2.0 くらいは書けるようにしてよ ◼ “1.0” + 3.0 = 4.0 くらい書けるようにしてよ  動的にしか決まらない型は、動的にしかチェッ クできない  PythonのNumberなどは計算時の型計算を 許す

(46)

Objectとクラス

 クラスはオブジェクト指向での型に相当する

 Structure type + access qualifier  サブクラスを許すものが多い ◼ Extends ◼ サブクラスは、型の階層に自明でない構造を持ち込ん だ重要な概念  抽象クラスを許すものがある ◼ abstract ◼ implements ◼ methodの実体を後から定義する

(47)

Access Qualifier

 Private  Protected  Public  なんだかんだ言って、オブジェクトを導入しよう とすると、GCその他、実行環境のサポートが 大変なんだ… ◼ 「この言語にオブジェクトを導入しました」というブ ログの記事を簡単に信じてはいけません

(48)

環境と変数のバインディング

定義として  環境=変数と値の結合の世界  変数が大域、局所とスコープが切られている 場合変数の参照が何を指しているかを正しく 表現することが必要

(49)

def fac(n) { my(r); r = 1; while (n>0) { r=r*n; n=n-1; } r = 10; n = 3; fac(5); r; n; X X

(50)

関数の出現

 Homomorphicな考えをサポートするため ◼ この他には変数(シンボル)管理  関数の出現につれて考えなければならない問 題 ◼ コードセグメントの管理 ◼ フレームの管理  ローカル変数、グローバル変数  スコープの管理  引数の渡し方

(51)

変数のバインディング

 関数コールについて考えるべきこと (a) 関数には引数をどう渡すのか? (b) 引数が渡されると、変数名とデータの対応 がどのように変更されるか。また、関数からリ ターンするとき、どのような処理が必要か?

(52)

フレーム

 関数実行の時に必要な局所的な情報を格納して おく場所を一般にフレーム (frame) といいます  フレームを一つ作ると、環境が変化します  関数呼び出しが終了したら、環境を復帰させなけ ればなりません  Frameに含まれる情報には以下があります ◼ Return address ◼ 引数のバインディング情報(特に実引数と仮引数の対 応関係) ◼ 関数内のローカル変数のための領域

(53)

 局所的な環境は、任意の場所に現れるという のが現代的なプログラミング言語の常識  考え方はフレームと同じ ◼ 局所的な環境を定義するときに、変数と値の結合 の仕方を改めて定義する ◼ 局所的な環境が「終了」したら、変数と値の結合を 元にもどす

 source tree traversalでのやり方をまず見

(54)

 Perl5ではどうなっているか?

◼ entersub, leavesubでサポート

◼ @_で引数を渡す

(55)

バインディングの余談:

Call by **

 Call by Value ◼ 値を実引数として渡す (C) ◼ ほとんどの現代的言語で実装される ◼ JavaのreferenceやCのポインタをわたすのも実はCall by Value  Call by Reference ◼ 変数のアドレスを引数として渡す(Fortran, C++の一部機能)  Call by Name ◼ 変数が含まれる式を、呼び出しごとに評価する(現在ではごく少数)  Call by Keyword ◼ パラメタに「名前」が付いている (Fortran)  実は、変数の評価についても同じことが言える ◼ 特にスクリプト言語では、Referenceが実行時にしか決まらないことがある(by Nameの必要性) ◼ 局所変数等、Referenceが静的にわかる場合はby Referenceを採用すべきで ある

参照

関連したドキュメント

「父なき世界」あるいは「父なき社会」という概念を最初に提唱したのはウィーン出身 の精神分析学者ポール・フェダーン( Paul Federn,

2021] .さらに対応するプログラミング言語も作

Intervals graphs (denoted by INT ) are intersection graphs of intervals on a line, circular-arc graphs (CA ) are intersection graphs of intervals (arcs) on a circle, circle graphs (CI

What relates to Offline Turing Machines in the same way that functional programming languages relate to Turing Machines?.. Int Construction.. Understand the transition from

[3] Chari, Vyjayanthi, On the fermionic formula and the Kirillov-Reshetikhin conjecture, Int. and Yamada, Y., Remarks on fermionic formula, Contemp. and Tsuboi, Z., Paths, crystals

Tsouli, Infinitely many solutions for nonlocal elliptic p-Kirchhoff type equation under Neumann boundary condition, Int. Journal

しかし,物質報酬群と言語報酬群に分けてみると,言語報酬群については,言語報酬を与

Guasti, Maria Teresa, and Luigi Rizzi (1996) &#34;Null aux and the acquisition of residual V2,&#34; In Proceedings of the 20th annual Boston University Conference on Language