日本ソフトウェア科学会第 38 回大会 (2021 年度) 講演論文集

レベル付き持続型例外処理機構の設計
八杉 昌宏

江本 健斗

平石 拓

安全な計算状態操作機構は，C 言語などの実行スタック中に眠る呼び出し元の変数の値への合法的アクセスを提供するもので，高信頼・高性能な高水準言語のための，ごみ集め，一級継続，動的負荷分散といった，計算状態の動的再構成を必要とする高水準サービスの効率的実装を可能とする．入れ子関数の形態をとる場合，クロージャを生成し，それへのポインタを用いて間接的に入れ子関数を呼び出すことで，クロージャ生成時の環境に含まれる変数へのアクセスが可能となる．一方，新しい形態として設計中の持続型例外処理機構は，非局所脱出することなく例外ハンドラを呼び出せるとしたもので，ポインタではなく動的スコープに基づく．また，一級継続生成中のごみ集めといった複数レベルでの計算状態操作機構の利用も想定する．本発表では，その性質の理解や検証を可能とするため，レベル付
き持続型例外処理機構を備えたコア言語の仕様を整理する．

る場合，クロージャを生成し，それへのポインタを用

1 はじめにいて間接的に入れ子関数を呼び出すことで，クロー逐次命令型言語（手続き型言語）に，計算状態操

ジャ生成時の環境に含まれる変数へのアクセスが可能

作機構 [8] [4] [3] [9] [5] を備えさせる研究を行っている．
計算状態操作機構とは，存命の変数の値へ（手順を踏むことで）いつでも合法的にアクセスするための機
構で，（デバッガなどではなく）計算主体が自分自身で利用する．例えば，C 言語などの実行スタック中に眠る呼び出し元の変数の値への合法的アクセスを提供する．これより，高信頼・高性能な高水準言語のための，ごみ集め，一級継続，動的負荷分散といった，計算状態の動的再構成を必要とする高水準サービスの効率的実装を可能とする．

となる．例えば，
Pair copy_tree ( void (* scan0 )() , Pair x ) {if ( IS_NIL ( x )) return x ;Pair a = NIL , d = NIL ;//GC に備え，クロージャ scan1 を生成
void scan1 () {scan0 (); //scan0 で呼び出し元のルートをスキャン
x = move ( x ); a = move ( a ); d = move ( d );}Pair a = copy_tree ( scan1 , CAR ( x )); //scan1 を渡す
Pair d = copy_tree ( scan1 , CDR ( x ));return CONS ( scan0 , a , d );}のように，より古くに生成され仮引数（scan0）に

これまで計算状態操作機構は入れ子関数の形態で渡されたクロージャを，呼び出し先でより最近生成

提供してきた [8] [4] [3] [9] [5]．入れ子関数の形態をとされたクロージャ（scan1）が連鎖的に呼び出すこと∗ Designing Mechanisms for Leveled Restartable Ex-

ception Handling.
This is an unrefereed paper. Copyrights belong to
the Author(s).
Masahiro Yasugi, 九州工業大学, Kyushu Institute ofTechnology.
Kento Emoto, 九 州 工 業 大 学, Kyushu Institute ofTechnology.
Tasuku Hiraishi, 京 都 橘 大 学, Kyoto Tachibana
University.

で，実行スタック中に眠る任意段数前の（この場合，
再帰）呼び出し元の変数（x，a，d）へアクセスできる．この例では，コピー型ごみ集めにおけるルートとして，実行スタック全体の存命の変数へ全面的にアクセスできる．
一方，新しい形態として設計中の持続型例外処理機
構は，非局所脱出することなく例外ハンドラを呼び出せるとしたもので，ポインタではなく動的スコープに基づくため，より安全となる．上の入れ子関数の例で持続型例外処理機構を追加したコア言語を整理する．

は仮引数（scan0）として受け取ったクロージャへの

ここでのコア言語（core language）とは低レベルでポインタを，クロージャを所有する関数（copy_tree）

実装用あるいは中間言語としてできるだけ最小の言の終了に伴いクロージャや変数（x，a，d）の寿命が語である．つまり，後の拡張の元とすることができる

尽きた後に利用しようとしてしまう可能性がある．ポ言語である．また，内部と外部を分離したときに，内部ができるだけ安全に保たれているもの †1 とする．

インタは「いつでも」だけでなく，「いつまでも」（不正）利用できてしまう．延命により安全性を高めるに

コア言語としては SC 言語処理系 [10] の利用を想定

は自動的に「ごみ集めされる」ヒープに対象を確保まして，S 式ベースの C 言語のサブセットとする．SC

たは移動することが考えられるが，計算状態操作機構

言語処理系は Common Lisp をベースとした処理系

としては，高水準言語のための「ごみ集め」の効率的であり，SC 言語（S 式ベースの構文を持つ C 言語の実装は，基盤となる低水準言語として提供したいこ総称）間の翻訳を，指定された変形規則セットによるとの 1 つであるため，提供を受ける側になってしまっS 式の変形により処理できる．

ては目的に逆行する．
入れ子関数の形態のままで，一定の規律に従うこと

本研究の貢献は以下の 2 点である．

• 逐次命令型言語であるコア SC 言語の仕様

で存命でない対象へのアクセスが発生しないことを• コア SC 言語に持続型例外処理機構を追加した保証する型システムも考えられるが，本研究では本質

コア SC-RE 言語の仕様，ならびに静的リンクを的に安全となる言語仕様に基づくアプローチをとる．

明示化したコア SC-REs 言語の仕様

動的スコープにより例外ハンドラが呼び出せるのは，

本稿の構成は以下の通りである．2 章では既存手法

例外ハンドラが存命の間に限られ，例外ハンドラによを大局的観点から俯瞰する．3 章では計算状態操作機

る変数アクセスも変数が存命の間に限られる．実行ス

構のこれまでと現在，設計中の持続型例外処理機構に

タック全体の変数にアクセスするための連鎖は例外ハついて述べる．4 章では，S 式ベースの構文を持つ逐

ンドラから例外ハンドラを呼び出すことで可能とな

次命令型言語であるコア SC 言語の仕様を示す．5 章

る．これは非局所脱出の場合の例外の再 throw に類

では，持続型例外処理機構を備えたコア SC-RE 言語似している．

（静的リンク非明示で元の文脈においてハンドラ起動）

一方，入れ子関数の形態では，同じクロージャをならびにコア SC-REs 言語（静的リンクを明示化し（一級継続生成とごみ集めのルートスキャンのように）て隣接する呼び出しは隣接する配置にしたもの）の設

別の用途に用いられたが，持続型例外処理機構ではハ計（構文，意味論など）を示す．また，意味論に基づ

ンドラの同じ実体が多重実行されることはない．よっく展望（今後の課題）を述べる．6 章でまとめと今後て，一級継続生成中のごみ集めといった複数レベルでの課題について述べる．

の計算状態操作機構の利用も想定する．例えば，一級継続生成のためのハンドラの実行中に，（そのハンド2 既存手法ラ（だけ）が利用する局所変数を対象とした）ルート

2. 1 宣言型言語

スキャンのためのハンドラの別レベル（より基礎的なプログラミング言語は，宣言型言語と命令型言語にレベル）での実行が必要といった場合である．これは大別される．宣言型言語はどれも高水準言語であり，レベル付けされた（階層化された）限定継続に類似し論理型言語と関数型言語に大別される．副作用のないている．

純粋関数型言語では参照透過性などの利点があるが，本発表では，その性質の理解や検証を可能とする

実用的な評価戦略として non-strict な遅延評価を用ため，レベル付き持続型例外処理機構を備えたコア
言語の仕様を整理する．まず，伝統的な逐次命令型言語（手続き型言語）のコア言語の仕様を整理し，次に†1 いわゆるマルチコア・プロセッサのコアとは異なるが，内部にレジスタを持つ CPU コアが，外部のメモリなどを扱うという点では共通点もある．

いるなど，高水準言語の実装は複雑となる．

スタック」が伸び縮みする．
実行スタックを利用する際，実際には一部のデータ

2. 2 低水準逐次命令型言語と実行スタック

を callee save レジスタに置くこと（レジスタ割り当

通常，「コンピュータ」は，機械語プログラムの逐て）で効率化がはかれる．
（ただし却って無駄になる

次実行が可能な機械として順序回路をベースとしてこともあり，callee save レジスタの lazy save，eager

作られている．機械語プログラムは低水準（低レベル

restore が望ましいが，普及した手法とはいいがたい．
）／低級）の逐次命令型言語とみなすことができるが，

SPARC のような命令セットアーキテクチャではレジ

低水準ではあるもの，ある程度うまく抽象化された

スタ・ウインドウも利用できる．また，実行スタッ（多くの部分では）機械独立な命令型言語が一般にはクへのアクセスは参照局所性が高く，CPU が用いる用いられている．機械独立な逐次命令型言語は，コンキャッシュとも相性が良い．

パイラやインタプリンタといった言語処理系により，call と return に基づく場合，return 前に呼び出し

様々な命令セットアーキテクチャの「コンピュータ」

元に眠る変数の値へアクセスすることは正式にはでで実行可能となる．機械語の分岐命令の代わりに機きない．特に，レジスタ割り当てを考慮すると，実行械独立な逐次命令型言語では if 文や while 文が用いスタックを単なるメモリとしてメモリアドレスやポイられる他，多くの場合，手続き呼び出しが利用可能でンタを用いて正しくアクセスすることは困難で危険ある．ここで，命令型言語であっても「関数」が手続といえる．一般には，ソースレベルデバッガは，このきの意味で用いられることも多い．関数や手続きは困難に頑張って対処するが，デバッグオプションを有

仮引数（あるいはパラメータ）を用いて定義され，呼効にしてデバッグ対象をコンパイルする必要がある．

び出し（call）時には実引数が通常，値として呼び出し元から呼び出し先に渡される．また，呼び出し先で

2. 3 制御構造

求めた結果が呼び出し元に返（return）される．手続

遠隔手続き呼び出し（remote procedure call）は，

き（procedure）や関数（function）は，コンパイラ

呼び出し元と呼び出し先が離れていても手続き呼びを用いた場合は機械語命令列に翻訳され，その呼び出しを可能としたものである．また，オブジェクト指出し（call）とリターン（return）のためには，命令向言語でのメッセージパッシングに基づくメソッド呼セットアーキテクチャに適した呼び出し慣例が用いら

び出しでは，呼び出し側からは起動されるメソッドをれている．

直接は定めず，メッセージのみ定める．呼び出し先で呼び出し（call）とリターン（return）は，データ構造のスタックにおける push と pop のようにペア

は，メッセージに適したメソッドが選択され，起動される．

になっていて，呼び出された手続き A からさらに手

低水準逐次命令型言語で入れ子関数（入れ子手続

続き B を呼び出すと，A からのリターンは B からのき）や λ 式が提供される場合，前章で述べたように，

リターンの後になるなど，call と return には自然な低水準であるか安全であるかのどちらかを犠牲にす

順序がつく．呼び出しが完了すれば求められた結果とる必要がある．入れ子関数定義からそれが置かれた

しての状態更新を除き基本的には，状態が元に戻され環境を伴うクロージャを生成するには，安全のため，るため，呼び出し元で想定すべき状態変化は限定的

ごみ集めされるヒープに場所を確保したくなる．こで想定しやすくなる．
「実行スタック」をうまく利用の場合，環境（変数から location への写像），スト

して call と return できる．call 時には実行スタック

ア（location から値への写像）といったすっきりしたを介して引数を渡したり，戻り番地（リターンアドレ

理論的道具立てで意味論を定めたりもできる．一方，ス）を渡したり，実行スタック上に局所変数を確保し（機械語に近いものの，適度に機種独立な）低水準言

でき，return 時には実行スタックの形状は復元され語としての役割を果たし続けるにはこのアプローチ

る．呼び出しが入れ子になる場合もその分だけ「実行をとることはできない．

最初から存在する大域環境で定義される大域関数

2. 4 並行性／並列実行主体

のみでも，再帰的計算や分割統治，バックトラック探並行計算は，計算したいことを十分な並行性を持つ索といった有意義な計算は実現できる．バックトラッ

形で表現し，進められる計算は進めるといったものとク探索では命令型言語であることを活かして，単一のいえる．関数型言語で non-strict で eager な評価戦略

作業空間で副作用による状態変化をバックトラック時を用いると，計算したいことはデータフロー計算のに元に戻す形で限られた作業空間を逐次的に利用し形に表現される．高水準言語として十分な並行性をた効率化も可能である．

表現した上で，限られたあるいは多すぎる並列実行

末尾呼び出しなどで適切な末尾再帰（proper tail

主体（CPU 等などのワーカ）との対応関係はどうす

recursion）が求められる高水準言語を実装するには，べきか，各実行主体（ワーカ）はどのような低水準命

低水準命令型言語で凝った工夫が必要となる，つま

令型言語プログラムの逐次実行を行えばよいかといっり，高水準言語の呼び出しを低水準命令型言語での呼た点が課題となる．

び出しとなるようなコンパイル方式や，インタプリタ

プロセス計算では，プロセスが互いに協調して計実装を開発するのは難しい．実行スタックには典型的算を進める．協調にはチャネルやリンクを介してのつには，引数，戻り番地が，この順で push されているながりが利用され，新しいチャネルやリンクが追加さため，末尾呼び出し先の関数の引数が元の関数より増れることもある．アクターや並行オブジェクト計算でえた場合は戻り番地の位置を変えないで末尾呼び出は，アクターや並行オブジェクトを宛先として非同し先へ「ジャンプ」するのは面倒となる．

期メッセージ送信に基づく．これらも実行主体（ワー

一級継続，限定継続，コルーチンなどは，高水準言カ）との対応関係をどうすべきか，どのような低水準

語なら提供できる機能と考えられる．その意味論は継命令型言語プログラムの逐次実行を行えばよいかと

続，環境，ストアなどを用いてすっきりと表現できる

いった点が課題となる．

ものの，そのためにはごみ集めが必須になる．また，共有メモリ環境を前提としたマルチスレッド計算でCPS 変換によっても実装できるが，変換の結果，末はデータレースフリーが課題となる．計算したいこと

尾呼び出しばかりとなり，properly tail recursive なが分割統治と fork/join（spawn/sync）で表現できる

高水準言語処理系を要することとなる．

ならワーカ間でのワークスティール実行に適している

例外処理や catch ＆ throw は C++言語にも採用といえる．

されているように，低水準逐次命令型言語でも利用

ワーカを主体に考えた場合，逐次実行する低水準できる．例外の throw に対して，これを catch する

命令型言語プログラムの実行効率の観点からいえば，

例外ハンドラは動的スコープで決まるといえ，非局

できるたけ自分だけで逐次実行できることが望まし所脱出に伴い実行スタックを縮める形で実行スタック

いといえる．そのためには，ワークスティールのためとの相性もよい．また，例外を再 throw することで，のタスク要求がない限り自分で計算，あるいは当初は実行スタック全体にわたり例外ハンドラを動かすことすべて自分で逐次実行する計画で始め，他のワーカかもできる．ただし，Common Lisp のコンディション

ら結果が得られた部分の計算は省略するような並列システムを除くと，catch 後は例外ハンドラが設定さ実行方式が効率良いといえる．

れたところまでの限定継続が非局所脱出により捨てられ，その部分の再実行／再開が不可能となる．

3 計算状態操作機構

比較的低レベルの視点から例外処理が必要な状況

計算状態操作機構は，高性能・高信頼な高水準言

は，割り込みやシグナルハンドリングでも生じるが．

語において，実行スタック全体の存命の変数に関す

catch ＆ throw の catch 先に対して，割り込みハンる全面的操作が必要な高水準サービスの実装に適しドラやシグナルハンドラから例外を throw するのはている．例えば，ごみ集めや一級継続や proper tail

危険と思われる．

recursion の実装 [7] [5]，ワークスティールにおける

タスクの遅延生成の実装 [2]，計算省略のための遅延持たない，シンプルとするため値は整数値のみとし，された結果出力の実装 [6] などである．

ポインタはないとする．

入れ子関数（から生成されるクロージャ）の形態

また，仕様においてα同値などは用いない．インラの場合，GCC が提供する入れ子関数 [1] も利用でき

イン展開の形では呼び出しを実行しないため，変数名

るが，これを改良するものとして，これまで，GCCのつけかえはない．また，意味論においてフレッシュ

改造による L-closure [8] [4]，GCC 改造による clo-

な location は用いない．

sure [8] [4] [9]，標準 C への変換によるスタック再構

入出力を備える．入出力は，外部からの要求や通知築に基づく L-closure [3]，標準 C への変換によるフのポーリング，データの送受信，（コア言語側でごみ集

レーム毎復元に基づく L-closure [9]，標準 C への変めをする）ヒープにもなり得る（そのアドレスとデー換による closure [5] などの実装を行ってきた．

タはコア言語では整数値となる）外部メモリの利用，

本研究では，計算状態操作機構の新しい形態としてといった使い道を想定するが，これら入出力先の仕様

持続型例外処理機構を提案する．入れ子関数の形態

はコア言語の仕様に含まれない．これら外部に起因すの場合は，存命でない対象にアクセスしないようにる非決定性を除き，コア言語のプログラムは決定的で注意する必要があるが，持続型例外処理機構の場合，逐次的に動作する．

呼び出せるのは利用可能なハンドラだけでそのアクセス対象も存命のものとできる．
持続型例外処理機構を利用した継続キャプチャ中に意味論は small step とし，構文は，実行時のプログラムを表現可能とする．適切な型システムを利用することで型安全性が得られるようにする．

持続型例外処理機構を利用した GC 開始，持続型例
外処理機構を利用したワークスティール処理中に持続4. 1 Syntax

型例外処理機構を利用した GC 開始のように，複数

コア SC 言語の構文を以下に示す．

のレベルで持続型例外処理機構を利用できるようにする．このような場合のプログラムの意味を明確にするため，本研究では言語仕様を整理することとした．
これにより，計算状態操作機構の安全性，有効性を高めるほか，その性質の検証に取り組めるようにする．
次章で，ベースとなるコア言語として S 式ベースの C 言語のサブセット「コア SC 言語」について述
べたのち，持続型例外処理機構を追加したコア言語「コア SC-RE 言語」を整理する．
「コア SC-RE 言語」
としては静的リンク非明示で元の文脈においてハンドラ起動としたものと，「コア SC-REs 言語」（静的リ
ンクを明示化して隣接する呼び出しは隣接する配置

num ::= 0 | 1 | 2 | . . .
dn ::= num
v (値) ::= nume (式) ::= v | id | (+ e e) | (> e e)vdef ::= (def id int v)
s (文) ::= (= id e)| (= id (in e)) | (out e e)
| (if e s s) | (while e s)
| (begin vdef ∗ s∗ )| (= id (call id e∗ ))| (= id (calling dn))
| (return e)
gdef ::= vdef
| (def (id id ∗ ) (fn int int∗ ) s)
exec ::= (exec dn id vdef ∗ s)prog ::= (prog gdef ∗exec ∗ (exec 0 main vdef ∗ s))にしたもの）の設計（構文，意味論など）を示す．

dn は動的リンク番号で戻り番地に相当し，(begin)

SC-RE 言語の翻訳/変換に基づく実装としては，標が何もしない文（典型的には skip 等）に相当する．

準 C への変換，入れ子関数を利用するコードへ翻訳，

gdef ∗ で定義される大域変数や大域関数の id は dis-

例外処理を利用したコードへの変換を予定している．

tinct とする．exec は実行中の関数を表し，初期プロ
グラムは main を除き，これを持たないとする．

4 コア SC 言語
コア SC 言語の基本設計としては，スタックベースの逐次命令型言語であって，ごみ集めされるヒープは

init-prog ::= (prog gdef ∗(exec 0 main vdef ∗ s))

4. 2 Semantics
コア SC 言語の意味論を small step semantics で示す．

4. 2. 1 Contexts and instructions
e を ec[einsn] と分解するための文脈とインストラ
クションを示す．
ec ::= [ ] | (+ ec e) | (+ v ec)| (> ec e) | (> v ec)
einsn ::= id | (+ v v) | (> v v)

s を ecs[einsn] と分解するための文脈を示す．
ecs ::= (= id ec)| (= id (in ec))
| (out ec e)| (out v ec)| (if ec s s)
| (= id (call id v ∗ ec e∗ ))| (return ec)

s を sc[sinsn] と分解するための文脈とインストラク(def (id 1 id ∗ ) (fn int int∗ ) s) ∈ gdef ∗vdef ∗2 = (def id ∗ int∗ v ∗ )dn 2 = dn 1 + 1((prog gdef ∗scexec[(= id 2 (call id 1 v ∗ ))]calling-exec ∗ ), dn 1 ) →((prog gdef ∗(exec dn 2 id 1 vdef ∗2 s)
scexec[(= id 2 (calling dn 2 ))]calling-exec ∗ ), dn 2 )dn 2 = dn 1 + 1((prog gdef ∗(exec dn 2 id 1 vdef ∗2 (return v 3 ))scexec[(= id 2 (calling dn 2 ))]calling-exec ∗ ), dn 2 ) →((prog gdef ∗scexec[(= id 2 v 3 )]calling-exec ∗ ), dn 1 )(call)(return)

ションを示す．
sc ::= [ ]| (begin vdef ∗ sc s∗ )skip-or-return ::= (begin) | (return e)sinsn ::= (= id e)| (= id (in e))| (out e e)
| (if e s s)
| (while e s)
| (begin vdef + )| (begin vdef ∗skip-or-return s∗ )| (= id (call id e∗ ))prog を scprog[sinsn] と分解するための文脈を示す．
calling-s ::= sc[(= id (calling dn))]calling-exec ::= (exec dn id vdef ∗ calling-s)
scexec ::= (exec dn id vdef ∗ sc)
scprog ::= (prog gdef ∗ scexec calling-exec ∗ )call された関数の本体は，呼び出し元にインライン展開されるのではなく，大域環境中で実行される．このため呼び出し元の変数名をつけかえたりする必要はない．これら call と return 以外では動的リンクの変更はなく，評価関係 prog 1 → prog 2 に基づく．
prog 1 → prog 2(prog 1 , dn) → (prog 2 , dn)(prog)

4. 2. 3 Expressions
prog 1 = scprog[ecs[einsn]] の と き の 評 価 関 係
prog 1 → prog 2 を与える．プログラム全体として対象となる式が一意に定まる（つまり，逐次実行である）．id については，きれいな環境やストアではなく，

4. 2. 2 Conﬁgurations

実行中のプログラムが文脈として持つ変数の値を用

動的リンク番号 dn までを用いている prog に関するいる．これは文脈から lookup する．コア SC 言語の評価関係 (prog 1 , dn 1 ) → (prog 2 , dn 2 ) を与える．

lookup は後で示す．id 以外の式に関しては，標準的な操作的意味論といえる．

beginEnd と beginReturn に つ い て は 変 数 を 削
lookup(id, scprog) = v
scprog[ecs[id]] → scprog[ecs[v]](refValDef)

v3 = v1 + v2
scprog[ecs[(+ v 1 v 2 )]] → scprog[ecs[v 3 ]]除している．このような削除を行っても安全に計算が進められる必要がある．
scprog[(begin vdef + )] → scprog[(begin)] (beginEnd)(plus)
scprog[(begin vdef ∗ (begin) s∗ )](beginNext)→ scprog[(begin vdef ∗ s∗ )]v1 > v2(gt1)

scprog[ecs[(> v 1 v 2 )]] → scprog[ecs[1]]v 1 ̸> v 2(gt0)

scprog[ecs[(> v 1 v 2 )]] → scprog[ecs[0]]scprog[(begin vdef ∗ (return v) s∗ )](beginReturn)→ scprog[(return v)]

4. 2. 5 lookup
lookup(id , scprog) = v は 変 数 ア ク セ ス の 文 脈

4. 2. 4 Statements

scprog において最も近い vdef から外側の大域変数

prog 1 = scprog[sinsn] のときの評価関係 prog 1 →に向かい id が一致する vdef を見つけ（てその値 v をprog 2 を与える．ただし，call については 4. 2. 2 節得）る形で定義できる．

で (prog 1 , dn) → (prog 2 , dn) としてすでに与えた．

(def id int v) ∈ gdef ∗id への値のアサインメントについては，すっきりと

lookup(id, (prog gdef ∗(exec dn 1 id 1 [ ]) calling-exec ∗ )) = v(refGdef)

した環境やストアではなく，実行中のプログラムの文
脈自体を update で更新する．
update(id, v, scprog 1 ) = scprog 2
scprog 1 [(= id v)] → scprog 2 [(begin)](updateValDef)

コア SC 言語の update は後で示す．以下は入出力命令である．
v 1 is read from port v
scprog[(= id (in v))] → scprog[(= id v 1 )]v 1 is written to port v
scprog[(out v v 1 )] → scprog[(begin)](in)v ̸= 0
scprog[(if v s1 s2 )] → scprog[s1 ]id 1 ̸= id 3
lookup(id 3 , (prog gdef ∗(exec dn 2 id 2 vdef ∗ [ ])calling-exec ∗ )) = v 3
lookup(id 3 , (prog gdef ∗(exec dn 2 id 2 vdef ∗(def id 1 int v 1 ) [ ])calling-exec ∗ )) = v 3(refParamFind)(out)

以下は if 文，while 文に関する標準的な遷移である．
scprog[(if 0 s1 s2 )] → scprog[s2 ]lookup(id 1 , (prog gdef ∗(exec dn 2 id 2 vdef ∗(refParam)(def id 1 int v 1 ) [ ])∗calling-exec )) = v 1

lookup(id, scprog[(begin vdef ∗ (def id int v)[ ] s∗ )]) = v(refLdef)(if0)

id 1 ̸= id 2
lookup(id 2 , scprog[(begin vdef ∗ [ ] s∗ )]) = v 2(if1)

lookup(id 2 , scprog[(begin vdef ∗ (def id 1 int v 1 )[ ] s∗ )]) = v 2(refLdefFind)

scprog[(while e s)]→ scprog[(if e (begin s (while e s)) (while)(begin))]以下は実行を完了して次に進むためであるが，lookup(id, scprog) = v
lookup(id, scprog[(begin [ ] s∗ )]) = v(refFindBegin)4. 2. 6 update
update(id , v, scprog 1 ) = scprog 2 も変数アクセスの文脈 scprog 1 において最も近い vdef から外側の大域変数に向かい id が一致する vdef を見つけ（てそのupdate(id, v, scprog 1 ) = scprog 2
update(id, v, scprog 1 [(begin [ ] s∗ )])= scprog 2 [(begin [ ] s∗ )](updateFindBegin)

値を v に更新した scprog 2 を得）る形で定義できる．

4. 3 Typing
update(id 1 , v 1 ,(prog gdef ∗1 (def id 1 int v 2 ) gdef ∗(exec dn 2 id 2 [ ]) calling-exec ∗ ))= (prog gdef ∗1 (def id 1 int v 1 ) gdef ∗(exec dn 2 id 2 [ ]) calling-exec ∗ )(updateGdef)

コア SC 言語の仕様で stuck する可能性があるのは，• 整数演算の対象となる値が式から求められない．
特に削除された局所変数を含め，存在しない変数の値を得ようとするアクセス．

• 整数演算の対象となる値が整数でない．
update(id 1 , v 1 ,(prog gdef ∗(exec dn 2 id 2 vdef ∗ (def id 1 int v 2 ) [ ])calling-exec ∗ ))= (prog gdef ∗(exec dn 2 id 2 vdef ∗ (def id 1 int v 1 ) [ ])calling-exec ∗ )(updateParam)
id 1 ̸= id 3
update(id 3 , v 3 ,(prog gdef ∗1(exec dn 2 id 2 vdef ∗1 [ ])calling-exec ∗ ))= (prog gdef ∗2(exec dn 2 id 2 vdef ∗2 [ ])calling-exec ∗ )update(id 3 , v 3 ,(prog gdef ∗1(exec dn 2 id 2 vdef ∗1 (def id 1 int v 1 ) [ ])calling-exec ∗ ))= (prog gdef ∗2(exec dn 2 id 2 vdef ∗2 (def id 1 int v 1 ) [ ])calling-exec ∗ )(updateParamFind)• 文の実行に必要な値が式から求められない．特に削除された局所変数を含め，存在しない変数の値を得ようとするアクセス．

• 削除された局所変数を含め，アサインメントの対象となる変数が存在しない．

• 整数値を必要とする文の実行で値が整数でない．
• 存在しない関数の call
• 呼び出しの引数の個数が異なる• main 以外の関数で，実行すべき文が return 前に無くなる．

• return 先が無い．
• 呼び出し中の文の実行が return 後に再開されない• 入出力待ち．入力された値が整数でない．
があるが，これらは入出力を除き適切な型システムにより防止できる．特に gdef の関数定義の文が calling
文を含んでいる場合は型検査をパスさせない．入出力が常に正常に可能とするなら，「型検査をパスしたプ

update(id 1 , v 2 ,scprog[(begin vdef ∗ (def id 1 int v 1 ) [ ] s∗ )])= scprog[(begin vdef ∗ (def id 1 int v 2 ) [ ] s∗ )](updateLdef)
id 1 ̸= id 2
update(id 2 , v 2 , scprog 1 [(begin vdef ∗1 [ ] s∗ )])= scprog 2 [(begin vdef ∗2 [ ] s∗ )]update(id 2 , v 2 ,scprog 1 [(begin vdef ∗1 (def id 1 int v 1 )[ ] s∗ )])= scprog 2 [(begin vdef ∗2 (def id 1 int v 1 )[ ] s∗ )](updateLdefFind)

ログラムの実行が停止するなら，それは main で実行する文が無くなる形の正常終了」となるような型シス
テムとできる．

5 コア SC-RE 言語
コア SC 言語の計算状態を do-handle 構文を用いて操作できるようにしたコア SC-RE 言語を示す．静的リンク非明示で元の文脈においてハンドラ起動とするコア SC-RE 言語については有効となる S 式や遷移規則に「#-s」を付与して示す．一方，コア SC-REs
言語（静的リンクを明示化して隣接する呼び出しは隣接する配置にしたもの）については有効となる S 式や

hcalls は gdef の大域関数でも指定する．これは Java

遷移規則に「#+s」を付与して示す．コア SC-RE 言

言語でメソッドが潜在的に throw し得る例外のリス語とコア SC-REs 言語は本来等価であるが，SC-REs

トをメソッド定義時に throws で指定させるのと同様

言語には意味論において静的リンクを明示化するたである．

めの識別子を構文においても求めることとする．

do-handle 構文は実際のハンドラ定義 hdef を持
つ．1 つの hdecl に対応して複数の hdef が存在してよ5. 1 Syntax

く，do-handle 文を複数回実行するときはそのたび

コア SC-RE 言語／コア SC-REs 言語の構文を以下に hdef のインスタンスが存在し，gdef 内の hdef の再に示す．

帰呼び出しなどでは同時に複数のインスタンスも存

num ::= 0 | 1 | 2 | ...
dn ::= num#+ssid ::= idv ::= nume ::= v | id | (+ e e) | (> e e)vdef ::= (def id int v)
hcalls ::= (hcalls id + ) | ε
hexec ::= (hexec dn id vdef ∗ s)#+scexec ::= (seeing (sid dn) hexec)
hdef ::= (def (id id ∗ ) s)s ::= (= id e)| (= id (in e)) | (out e e)
| (if e s s) | (while e s)
| (begin vdef ∗ s∗ )| (do-handle #-shexec ∗ #+ssid hdef ∗ s)
| (= id (call id e∗ ))| (= id (calling dn))
| (return e)| (= id (hcall id e∗ ))| (= id (hcalling dn))
| (hreturn e)
hdecl ::= (decl id (handler int int∗ ) hcalls v)
gdef ::= vdef
| (def (id id ∗ ) (fn int int∗ ) hcalls s)
exec ::= (exec dn id vdef ∗ s)
| #+scexec
prog ::= (prog hdecl ∗ gdef ∗exec ∗ (exec 0 main vdef ∗ s))

在する．

hcall によりハンドラを呼び出し，hreturn により呼び出し元に復帰する．動的スコープに基づき，
特定の hdef のインスタンスが呼び出されたときには

do-handle インスタンスの実行文脈において呼び出される．SC-RE 言語では hdef に対応する hexec が（最大 1 回）この場で起動する形となり，SC-REs 言語
では同様の hexec をこの場（sid で識別）への静的リ
ンク (sid dn) 付きの cexec として起動する形となる．

SC-REs 言語の sid については実際にはプログラムとしては書かず，実行時に exec 内で一意になるように自動生成してもよいし，コンパイラなどが各 exec 内（gdef の関数定義内）で一意になるように自動生成してもよい．
初期プログラムはハンドラ宣言などの違いを除き，
コア SC 言語と同様である．
init-prog ::= (prog hdecl ∗ gdef ∗(exec 0 main vdef ∗ s))

5. 2 Semantics
コア SC-RE 言語，コア SC-REs 言語の意味論を

hdecl はハンドラ宣言であり，ハンドラ id （レベル），引数の個数，hcalls とデフォルト値を宣言する．

small step semantics で示す．
5. 2. 1 Contexts and instructions

この宣言はプログラム全体における大域的宣言であe を ec[einsn] と分解するための文脈とインストラ

る．hcalls には，そのハンドラの実行中に新たに呼びクションは 4. 2. 1 節と同じである．s を ecs[einsn] と出せる別（レベル）のハンドラ id のリストを指定す

分解するための文脈はハンドラ呼び出しの分 4. 2. 1 節

る．宣言済のハンドラ id のみ許すことでレベル間のより拡大している．

関係は半順序関係となる．hdecl で指定したデフォル
ト値は実行スタック全体について連鎖的にハンドラを呼び出した場合などで，main に達してそれ以上呼び出せるハンドラが存在しないときに返す値とする．

(prog hdecl ∗ gdef ∗ exec ∗ ) と main を含めた exec ∗のうち，任意の 1 つに（高々1 つに）sinsn のための

ecs ::= (= id ec)| (= id (in ec))
| (out ec e)| (out v ec)| (if ec s s)
| (= id (call id v ∗ ec e∗ ))| (return ec)| (= id (hcall id v ∗ ec e∗ ))| (hreturn ec)[ ] を持てる．scprog は hcall 時に対象となる hdef を持
つ do-handle 文の文脈としても用いる．これにより手順を踏んでハンドラを連鎖的に hcall することで実行スタックに存在する任意の変数へアクセスできる．

s を sc[sinsn] と分解するための文脈とインストラ
クションも拡大している．SC-RE 言語の場合，sc はdo-handle の持つ hexec ∗ の任意の 1 つに（高々1 つに）sinsn のための [ ] を持てる．このとき，hexec ∗ のcalling-exec ::= (exec dn id vdef ∗ calling-s)| #+s(seeing (sid dn) calling-hexec)
scexec ::= (exec dn id vdef ∗ sc)| #+s(seeing (sid dn) schexec)
scprog ::= (prog hdecl ∗ gdef ∗calling-exec ∗scexeccalling-exec ∗ )残りは呼び出し中の calling-hexec で，do-handle の本体も呼び出し中の calling-s である．do-handle の∗本体が sinsn のための [ ] を持つなら，hexec はすべ

5. 2. 2 Conﬁgurations
評価関係 (prog 1 , dn 1 ) → (prog 2 , dn 2 ) を与える．

て呼び出し中の calling-hexec である．
vdef ∗2 = (def id ∗ int∗ v ∗ )dn 2 = dn 1 + 1(def (id 1 id ∗ ) (fn int int∗ ) hcalls s) ∈ gdef ∗calling-s ::= sc[(= id (calling dn))]| sc[(= id (hcalling dn))]calling-hexec ::= (hexec dn id vdef ∗ calling-s)
schexec ::= (hexec dn id vdef ∗ sc)
sc ::= [ ]| (begin vdef ∗ sc s∗ )| (do-handle #-scalling-hexec ∗#+ssid hdef ∗ sc)| #-s(do-handle
calling-hexec ∗ schexec
calling-hexec ∗hdef ∗ calling-s)
skip-or-return ::= (begin) | (return e)| (hreturn e)sinsn ::= (= id e)| (= id (in e))| (out e e)
| (if e s s)
| (while e s)
| (begin vdef + )| (begin vdef ∗skip-or-return s∗ )| (do-handle #+ssid hdef ∗skip-or-return)| #-s(do-handle(hexec dn id vdef ∗(hreturn e))
calling-hexec ∗hdef ∗ calling-s)| (= id (call id e∗ ))| (= id (hcall id e∗ ))応する例外ハンドラ定義を見つける．これは動的ス

prog を scprog[sinsn] と 分 解 す る た め の 文

コープに基づいており，main に達しても例外ハンド

脈 も 4. 2. 1 節 よ り 拡 大 し て い る ．scprog は

ラ定義が見つからないときは hcall の結果としてデ((prog hdecl ∗ gdef ∗#-scalling-exec ∗1
scexec[(= id 2 (call id 1 v ∗ ))]calling-exec ∗ ), dn 1 ) →((prog hdecl ∗ gdef ∗(exec dn 2 id 1 vdef ∗2 s)#-scalling-exec ∗1
scexec[(= id 2 (calling dn 2 ))]calling-exec ∗ ), dn 2 )(call)
dn 2 = dn 1 + 1((prog hdecl ∗ gdef ∗(exec dn 2 id 1 vdef ∗ (return v 3 ))#-scalling-exec ∗1
scexec[(= id 2 (calling dn 2 ))]calling-exec ∗2 ), dn 2 ) →((prog hdecl ∗ gdef ∗#-scalling-exec ∗1
scexec[(= id 2 v 3 )]calling-exec ∗2 ), dn 1 )(return)

ここで，SC-RE 言語／ SC-REs 言語では，実行中のハンドラが大域関数を call することがある．
例外ハンドラの「hcall」では，hlookup により，対フォルト値を返す．
dn 2 = dn 1 + 1
hlookup(id, scprog, (= id 1 (hcalling dn 2 )))= ((prog hdecl ∗ gdef ∗ calling-exec ∗(exec 0 main vdef ∗ [ ])),
calling-s 1 )(decl id (handler int int∗ ) hcalls v) ∈ hdecl ∗(scprog[(= id 1 (hcall id v ∗ ))], dn 1 )→ (scprog[(= id 1 v)], dn 1 )(hcallFail)

また，hlookup で例外ハンドラ定義 (def (id id ∗ ) s)が見つかるとき，これを持つ do-handle において（SC-RE 言語），あるいは，これを持つ do-handle
への静的リンク下において（SC-REs 言語; このとき

do-handle を持つのが大域関数のことと例外ハンド
ラのことがある），ハンドラを hexec として起動する．

dn 2 = dn 1 + 1
hlookup(id, scprog, (= id 1 (hcalling dn 2 )))= ((prog hdecl ∗ gdef ∗calling-exec ∗1
scexec[(do-handle calling-hexec ∗ hdef ∗ [ ])]calling-exec ∗ ), calling-s 1 )(decl id (handler int int∗ ) hcalls v) ∈ hdecl ∗(def (id id ∗ ) s) ∈ hdef ∗vdef ∗ = (def id ∗ int∗ v ∗ )(scprog[(= id 1 (hcall id v ∗ ))], dn 1 )→ ((prog hdecl ∗ gdef ∗calling-exec ∗1
scexec[(do-handle(hexec dn 2 id vdef ∗ s)calling-hexec ∗hdef ∗ calling-s 1 )]calling-exec ∗ ), dn 2 )(#-s:hcall)hlookup(id , scprog 1 , calling-s 1 ) = (scprog 2 , calling-s 2 )は，文脈 scprog 1 で calling-s 1 がハンドラ id を hcall
するとき，実行中のプログラム全体 scprog 1 [calling-s 1 ]を全体としては等しい scprog 2 [calling-s 2 ] に分け直し，scprog 2 が対応する hdef を持つ do-handle を直接含む文脈で，calling-s 2 が捨てずに残す文となるようにしたものである．ただし，calling-s 2 以外にscprog 2 にも捨てないでいる文脈（動的リンクをたどる前の文脈）が残っている．また，対応する hdef がmain までさかのぼっても見つからなければ，その文脈を scprog 2 として返す．さらに同一レベルの hcallの場合は，ハンドラの呼び出し元へと動的リンクをた
どるのではなく，例外ハンドラを持つ（静的リンク先の）do-handle として hcall することで連鎖できるようにする．これは catch ＆ throw にて catch 先の例
外ハンドラで再 throw したとき，同じ例外ハンドラに再度 catch されないのと同様である．hlookup の具体的な定義は後で述べる．

dn 2 = dn 1 + 1
hlookup(id, scprog, (= id 1 (hcalling dn 2 )))= ((prog hdecl ∗ gdef ∗calling-exec ∗1(exec dn 3 id 3 vdef ∗sc[(do-handle sid hdef ∗ [ ])])
calling-exec ∗ ), calling-s 1 )(decl id (handler int int∗ ) hcalls v) ∈ hdecl ∗(def (id id ∗ ) s) ∈ hdef ∗vdef ∗ = (def id ∗ int∗ v ∗ )(scprog[(= id 1 (hcall id v ∗ ))], dn 1 )→ ((prog hdecl ∗ gdef ∗(seeing (sid dn 3 ) (hexec dn 2 id vdef ∗ s))calling-exec ∗1(exec dn 3 id 3 vdef ∗sc[(do-handle sid hdef ∗ [ ])])
calling-exec ∗ ), dn 2 )(#+s:hcallExec)これら call と return と hcall と hreturn 以外では
動的リンクの変更はなく，評価関係 prog 1 → prog 2
dn 2 = dn 1 + 1
hlookup(id, scprog, (= id 1 (hcalling dn 2 )))= ((prog hdecl ∗ gdef ∗calling-exec ∗1(seeing (sid 1 dn 4 )(hexec dn 3 id 3 vdef ∗sc[(do-handle sid hdef ∗ [ ])])
calling-exec ∗ ), calling-s 1 )(decl id (handler int int∗ ) hcalls v) ∈ hdecl ∗(def (id id ∗ ) s) ∈ hdef ∗vdef ∗ = (def id ∗ int∗ v ∗ )(scprog[(= id 1 (hcall id v ∗ ))], dn 1 )→ ((prog hdecl ∗ gdef ∗(seeing (sid dn 3 ) (hexec dn 2 id vdef ∗ s))calling-exec ∗1(seeing (sid 1 dn 4 )(hexec dn 3 id 3 vdef ∗sc[(do-handle sid hdef ∗ [ ])])
calling-exec ∗ ), dn 2 )(#+s:hcallHexec)

以下は SC-RE 言語，SC-REs 言語それぞれでの

hreturn による遷移である．SC-RE 言語の場合，制御を戻す動的リンク先は実行中プログラム内の様々なところに存在し得る．SC-REs 言語の場合は，prog のexec ∗ の先頭の exec から隣接する exec へ制御を渡し，
exec ∗ は 1 つ縮める（先頭を除く）形となる．
scprog 1 [(do-handle calling-hexec ∗3 hdef ∗ calling-s)]= scprog 2 [(= id 2 (hcalling dn 2 ))]dn 2 = dn 1 + 1(scprog 1 [(do-handle(hexec dn 2 id 1 vdef ∗ (hreturn v 3 ))calling-hexec ∗3 hdef ∗ calling-s)], dn 2 )→ (scprog 2 [(= id 2 v 3 )], dn 1 )(#-s:hreturn)dn 2 = dn 1 + 1((prog hdecl ∗ gdef ∗(seeing (sid dn 3 )(hexec dn 2 id 1 vdef ∗ (hreturn v 3 )))scexec[(= id 2 (hcalling dn 2 ))]calling-exec ∗2 ), dn 2 ) →((prog hdecl ∗ gdef ∗scexec[(= id 2 v 3 )]calling-exec ∗2 ), dn 1 )(#+s:hreturn)に基づく．
prog 1 → prog 2(prog 1 , dn) → (prog 2 , dn)(prog)

5. 2. 3 Expressions
prog 1 = scprog[ecs[einsn]] の と き の 評 価 関 係
prog 1 → prog 2 は，4. 2. 3 節と同じとなる．
5. 2. 4 Statements
prog 1 = scprog[sinsn] のときの評価関係 prog 1 →prog 2 には，4. 2. 4 節のものに以下を加える．
scprog[(begin vdef ∗ (hreturn v) s∗ )]→ scprog[(hreturn v)](beginHreturn)
scprog[(do-handle #+ssid hdef ∗ (begin))]→ scprog[(begin)](doHandleBegin)scprog[(do-handle #+ssid hdef ∗ (return v))]
→ scprog[(return v)](doHandleReturn)
scprog[(do-handle #+ssid hdef ∗ (hreturn v))]
→ scprog[(hreturn v)](doHandleHreturn)

5. 2. 5 hlookup
hlookup(id , scprog 1 , calling-s 1 ) = (scprog 2 , calling-s 2 )は，文脈 scprog 1 で calling-s 1 がハンドラ id を hcall
するとき，実行中のプログラム全体 scprog 1 [calling-s 1 ]を全体としては等しい scprog 2 [calling-s 2 ] に分け直し，scprog 2 が対応する hdef を持つ do-handle を直接含む文脈で，calling-s 2 が捨てずに残す文となるよ
うにしたものである．

do-handle が対応する hdef を持たない場合は，その外側の探索結果を用いる．

∀(def (id 1 id ∗ ) s) ∈ hdef ∗ .id 1 ̸= id
hlookup(id, scprog,(do-handle #-scalling-hexec ∗ #+ssid
hdef ∗ calling-s))= (scprog 1 , calling-s 1 )hlookup(id,scprog[(do-handle #-scalling-hexec ∗ #+ssid
hdef ∗ [ ])],
calling-s)= (scprog 1 , calling-s 1 )(hlookupDoHandle)

以下のように対応する hdef を持つ do-handle を直接含む文脈 scprog が見つかるときはこれを返す．また calling-s が捨てずに残す文となるようする．ただし，calling-s 以外に scprog にも捨てないでいる文脈（動的リンクをたどる前の文脈）が残っている．
(def (id id ∗ ) s) ∈ hdef ∗hlookup(id,scprog[(do-handle #-scalling-hexec ∗#+ssid hdef ∗ [ ])],
calling-s)= (scprog[(do-handle #-scalling-hexec ∗#+ssid hdef ∗ [ ])],
calling-s)(hlookupHdef)

以下のように main の時点の文脈までさかのぼっても対応する hdef が見つからなければ，その文脈を返す．
hlookup(id,(prog hdecl ∗ gdef ∗ calling-exec ∗(exec 0 main vdef ∗ [ ])),
calling-s)= ((prog hdecl ∗ gdef ∗ calling-exec ∗(exec 0 main vdef ∗ [ ])),
calling-s)(hlookupFail)(prog hdecl ∗1(decl id 1 (handler int int∗1 )hcalls 1 v 1 )hdecl ∗2(decl id 2 (handler int int∗2 )(hcalls id ∗2 id 1 id ∗3 ) v 2 )hdecl ∗3 gdef ∗ calling-exec ∗ )= scprog 1 [(do-handle
calling-hexec ∗1(hexec dn 1 id 2 vdef ∗ calling-s 1 )calling-hexec ∗2
hdef ∗ calling-s)]= scprog 2 [(= id 3 (hcalling dn 1 ))]hlookup(id 1 , scprog 2 , (= id 3 (hcalling dn 1 )))= (scprog 3 , calling-s 3 )hlookup(id 1 ,scprog 1 [(do-handle
calling-hexec ∗1(hexec dn 1 id 2 vdef ∗ [ ])calling-hexec ∗2
hdef ∗ calling-s)], calling-s 1 )= (scprog 3 , calling-s 3 )(#-s:hlookupHexec)(prog hdecl ∗1(decl id 1 (handler int int∗1 )hcalls 1 v 1 )hdecl ∗2(decl id 2 (handler int int∗2 )(hcalls id ∗2 id 1 id ∗3 ) v 2 )hdecl ∗3 gdef ∗ calling-exec ∗ )= (prog hdecl ∗ gdef ∗calling-exec ∗1(seeing (sid dn 2 )(hexec dn 1 id 2 vdef ∗ calling-s 1 ))calling-exec ∗2 )= scprog 1 [(= id 3 (hcalling dn 1 ))]hlookup(id 1 , scprog 1 , (= id 3 (hcalling dn 1 )))= (scprog 2 , calling-s 2 )hlookup(id 1 , (prog hdecl ∗ gdef ∗calling-exec ∗1(seeing (sid dn 2 )(hexec dn 1 id 2 vdef ∗ [ ]))calling-exec ∗2 ), calling-s 1 )= (scprog 2 , calling-s 2 )(#+s:hlookupHexec)

同一レベルの hcall の場合はハンドラの呼び出し元

id 2 の例外ハンドラの処理内部に別レベルの例外

へと動的リンクをたどるのではなく，例外ハンドラを

id 1 に対応するハンドラが見つからなければ，id 2 の持つ（静的リンク先の）do-handle として hcall する

呼び出し元への動的リンク dn 1 をたどる．

ことで連鎖できるようにする．

また，以下のように，大域関数の呼び出し元やhlookup(id 1 , scprog,(do-handle
calling-hexec ∗1(hexec dn 1 id 1 vdef ∗ calling-s 1 )calling-hexec ∗2
hdef ∗ calling-s)) = (scprog 2 , calling-s 2 )hlookup(id 1 ,scprog[(do-handle
calling-hexec ∗1(hexec dn 1 id 1 vdef ∗ [ ])calling-hexec ∗2
hdef ∗ calling-s)], calling-s 1 )= (scprog 2 , calling-s 2 )(#-s:hlookupDoHandleHexec)
hlookup(id 1 ,(prog hdecl ∗ gdef ∗(seeing (sid dn)(hexec dn 1 id 1 calling-s 1 ))calling-exec ∗1(exec dn id 2 vdef ∗ sc[ ])calling-exec ∗2 ),(do-handle sid hdef ∗ calling-s))= (scprog 2 , calling-s 2 )hlookup(id 1 ,(prog hdecl ∗ gdef ∗(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(exec dn id 2 vdef ∗sc[(do-handle sid hdef ∗ calling-s)])calling-exec ∗2 ), calling-s 1 )= (scprog 2 , calling-s 2 )(#+s:hlookupDoHandleHexecExec)

begin の外側へと探索を続ける．
(def (id 2 id ∗ ) (fn int int∗ )(hcalls id ∗1 ) s) ∈ gdef ∗id 1 ∈ id ∗1(prog hdecl ∗ gdef ∗calling-exec ∗1(exec dn 1 id 2 vdef ∗ calling-s 1 )calling-exec ∗2 )= scprog 1 [(= id 3 (calling dn 1 ))]hlookup(id 1 , scprog 1 , (= id 3 (calling dn 1 )))= (scprog 2 , calling-s 2 )hlookup(id 1 , (prog hdecl ∗ gdef ∗calling-exec ∗1(exec dn 1 id 2 vdef ∗ [ ])calling-exec ∗2 ), calling-s 1 )= (scprog 2 , calling-s 2 )(hlookupExec)hlookup(id 1 , scprog, (begin vdef ∗ calling-s 1 s∗ ))= (scprog 2 , calling-s 2 )hlookup(id 1 , scprog[(begin vdef ∗ [ ] s∗ )],calling-s 1 ) = (scprog 2 , calling-s 2 )(hlookupBegin)

5. 2. 6 lookup
lookup(id , scprog) = v は 4. 2. 5 節同様，変数アクセスの文脈 scprog において最も近い vdef から外側の大域変数に向かい id が一致する vdef を見つけ（てそhlookup(id 1 ,(prog hdecl ∗ gdef ∗(seeing (sid dn) (hexec dn 1 id 1 calling-s 1 ))calling-exec ∗1(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗ sc[ ]))calling-exec ∗2 ),(do-handle sid hdef ∗ calling-s))= (scprog 2 , calling-s 2 )hlookup(id 1 ,(prog hdecl ∗ gdef ∗(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗sc[(do-handle sid hdef ∗ calling-s)]))calling-exec ∗2 ), calling-s 1 )= (scprog 2 , calling-s 2 )(#+s:hlookupDoHandleHexecHexec)の値 v を得）る形で定義できるが，起動されたハンド
ラの「外側」はその定義を持つ do-handle の文脈となる．これは SC-RE 言語においては構文的な外側であり，SC-REs 言語においては静的リンク先である．
(def id 1 int v 1 ) ∈ gdef ∗lookup(id 1 , (prog hdecl ∗ gdef ∗#-scalling-exec ∗1(exec dn 2 id 2 [ ]) calling-exec ∗ ))= v1
(refGdef)lookup(id 1 , (prog hdecl ∗ gdef ∗#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗ (def id 1 int v 1 ) [ ])calling-exec ∗ )) = v 1(refParam)id 1 ̸= id 3
lookup(id 3 , (prog hdecl ∗ gdef ∗#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗ [ ])calling-exec ∗ )) = v 3
lookup(id 3 , (prog hdecl ∗ gdef ∗#-scalling-exec ∗1(exec dn 2 id 2
vdef ∗ (def id 1 int v 1 ) [ ])calling-exec ∗ )) = v 3(refParamFind)
lookup(id 1 , scprog[(do-handle
calling-hexec ∗1(hexec dn 2 id 2
vdef ∗ (def id 1 int v 1 ) [ ])calling-hexec ∗hdef ∗ calling-s)]) = v 1(#-s:refHparam)
lookup(id 1 ,(prog hdecl ∗ gdef ∗(seeing (sid dn)(hexec dn 2 id 2 vdef ∗ (def id 1 int v 1 ) [ ]))calling-exec ∗ )) = v 1(#+s:refHparam)
id 1 ̸= id 3
lookup(id 3 , scprog[(do-handle
calling-hexec ∗1(hexec dn 2 id 2 vdef ∗ [ ])calling-hexec ∗hdef ∗ calling-s)]) = v 3
lookup(id 3 , scprog[(do-handle
calling-hexec ∗1(hexec dn 2 id 2
vdef ∗ (def id 1 int v 1 ) [ ])calling-hexec ∗hdef ∗ calling-s)]) = v 3(#-s:refHparamFind)
id 1 ̸= id 3
lookup(id 3 ,(prog hdecl ∗ gdef ∗(seeing (sid dn)(hexec dn 2 id 2 vdef ∗ [ ]))calling-exec ∗ )) = v 3
lookup(id 3 ,(prog hdecl ∗ gdef ∗(seeing (sid dn)(hexec dn 2 id 2 vdef ∗ (def id 1 int v 1 ) [ ]))calling-exec ∗ )) = v 3(#+s:refHparamFind)

id 1 ̸= id 2
lookup(id 2 , scprog[(begin vdef ∗ [ ] s∗ )]) = v 2
lookup(id 2 , scprog[(begin vdef ∗ (def id 1 int v 1 )[ ] s∗ )]) = v 2(refLdefFind)
lookup(id, scprog) = v
lookup(id, scprog[(begin [ ] s∗ )]) = v(refFindBegin)lookup(id, scprog) = v
lookup(id,scprog[(do-handle #-scalling-hexec ∗#+ssid hdef ∗ [ ])]) = v(refFindDoHandle)
lookup(id, scprog) = v
lookup(id, scprog[(do-handle
calling-hexec ∗1(hexec dn 1 id 1 [ ])calling-hexec ∗hdef ∗ calling-s)]) = v(#-s:refFindHexec)
lookup(id, (prog hdecl ∗ gdef ∗(exec dn id 2 vdef ∗ sc)
calling-exec ∗2 )) = v
lookup(id, (prog hdecl ∗ gdef ∗(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(exec dn id 2 vdef ∗sc[(do-handle sid hdef ∗ calling-s)])calling-exec ∗2 )) = v(#+s:refFindHexecExec)lookup(id, (prog hdecl ∗ gdef ∗(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗ sc))calling-exec ∗2 )) = v
lookup(id, (prog hdecl ∗ gdef ∗(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗sc[(do-handle sid hdef ∗ calling-s)]))calling-exec ∗2 )) = v(#+s:refFindHexecHexec)

5. 2. 7 update
update(id , v, scprog 1 ) = scprog 2 も 4. 2. 6 節同様，
変数アクセスの文脈 scprog 1 において最も近い vdef
から外側の大域変数に向かい id が一致する vdef を見

lookup(id 1 , scprog[(begin vdef ∗ (def id 1 int v 1 )[ ] s∗ )]) = v 1(refLdef)

つけ（てその値を v に更新した scprog 2 を得）る形で定義できるが，起動されたハンドラの「外側」はその

定義を持つ do-handle の文脈となる．これは SC-RE
言語においては構文的な外側であり，SC-REs 言語においては静的リンク先である．
update(id 1 , v 1 ,(prog hdecl ∗ gdef ∗1 (def id 1 int v 2 ) gdef ∗#-scalling-exec ∗1(exec dn 2 id 2 [ ]) calling-exec ∗ ))= (prog hdecl ∗ gdef ∗1 (def id 1 int v 1 ) gdef ∗#-scalling-exec ∗1(exec dn 2 id 2 [ ]) calling-exec ∗ )(updateGdef)
update(id 1 , v 1 ,(prog hdecl ∗ gdef ∗#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗ (def id 1 int v 2 ) [ ])calling-exec ∗ ))= (prog hdecl ∗ gdef ∗#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗ (def id 1 int v 1 ) [ ])calling-exec ∗ )(updateParam)
id 1 ̸= id 3
update(id 3 , v 3 ,(prog hdecl ∗ gdef ∗1#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗1 [ ])calling-exec ∗ ))= (prog hdecl ∗ gdef ∗2#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗2 [ ])calling-exec ∗ )update(id 3 , v 3 ,(prog hdecl ∗ gdef ∗1#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗1 (def id 1 int v 1 ) [ ])calling-exec ∗ ))= (prog hdecl ∗ gdef ∗2#-scalling-exec ∗1(exec dn 2 id 2 vdef ∗2 (def id 1 int v 1 ) [ ])calling-exec ∗ )(updateParamFind)

update(id 1 , v 3 ,scprog[(do-handle
calling-hexec ∗1(hexec dn 2 id 2
vdef ∗ (def id 1 int v 1 ) [ ])calling-hexec ∗hdef ∗ calling-s)])= scprog[(do-handle
calling-hexec ∗1(hexec dn 2 id 2
vdef ∗ (def id 1 int v 3 ) [ ])calling-hexec ∗hdef ∗ calling-s)](#-s:updateHparam)
update(id 1 , v 3 ,(prog hdecl ∗ gdef ∗(seeing (sid dn)(hexec dn 2 id 2 vdef ∗ (def id 1 int v 1 ) [ ]))calling-exec ∗ ))= (prog hdecl ∗ gdef ∗(seeing (sid dn)(hexec dn 2 id 2 vdef ∗ (def id 1 int v 3 ) [ ]))calling-exec ∗ )(#+s:updateHparam)
id 1 ̸= id 3
update(id 3 , v 3 ,scprog 1 [(do-handle
calling-hexec ∗1(hexec dn 2 id 2 vdef ∗1 [ ])calling-hexec ∗hdef ∗ calling-s)])= scprog 2 [(do-handle
calling-hexec ∗1(hexec dn 2 id 2 vdef ∗2 [ ])calling-hexec ∗hdef ∗ calling-s)]
update(id 3 , v 3 ,scprog 1 [(do-handle
calling-hexec ∗1(hexec dn 2 id 2
vdef ∗1 (def id 1 int v 1 ) [ ])calling-hexec ∗hdef ∗ calling-s)])= scprog 2 [(do-handle
calling-hexec ∗1(hexec dn 2 id 2
vdef ∗2 (def id 1 int v 1 ) [ ])calling-hexec ∗hdef ∗ calling-s)](#-s:updateHparamFind)id 1 ̸= id 3
update(id 3 , v 3 ,(prog hdecl ∗ gdef ∗1(seeing (sid dn) (hexec dn 2 id 2 vdef ∗1 [ ]))calling-exec ∗1 ))= (prog hdecl ∗ gdef ∗2(seeing (sid dn) (hexec dn 2 id 2 vdef ∗2 [ ]))calling-exec ∗2 )update(id 3 , v 3 ,(prog hdecl ∗ gdef ∗1(seeing (sid dn)(hexec dn 2 id 2 vdef ∗1 (def id 1 int v 1 ) [ ]))calling-exec ∗1 ))= (prog hdecl ∗ gdef ∗2(seeing (sid dn)(hexec dn 2 id 2 vdef ∗2 (def id 1 int v 1 ) [ ]))calling-exec ∗2 )(#+s:updateHparamFind)
update(id, v 2 ,scprog[(begin vdef ∗ (def id int v 1 ) [ ] s∗ )])= scprog[(begin vdef ∗ (def id int v 2 ) [ ] s∗ )](updateLdef)
id 1 ̸= id 2
update(id 2 , v 2 , scprog 1 [(begin vdef ∗1 [ ] s∗ )])= scprog 2 [(begin vdef ∗2 [ ] s∗ )]update(id 2 , v 2 ,scprog 1 [(begin vdef ∗1 (def id 1 int v 1 )[ ] s∗ )])= scprog 2 [(begin vdef ∗2 (def id 1 int v 1 )[ ] s∗ )](updateLdefFind)
update(id, v, scprog 1 ) = scprog 2
update(id, v, scprog 1 [(begin [ ] s∗ )])= scprog 2 [(begin [ ] s∗ )](updateFindBegin)
update(id, v, scprog 1 ) = scprog 2
update(id, v,
scprog 1 [(do-handle #-scalling-hexec ∗#+ssid hdef ∗ [ ])])
= scprog 2 [(do-handle #-scalling-hexec ∗#+ssid hdef ∗ [ ])](updateFindDoHandle)

update(id, v, scprog 1 ) = scprog 2
update(id, v, scprog 1 [(do-handle
calling-hexec ∗1(hexec dn 1 id 1 [ ])calling-hexec ∗hdef ∗ calling-s)])= scprog 2 [(do-handle
calling-hexec ∗1(hexec dn 1 id 1 [ ])calling-hexec ∗hdef ∗ calling-s)](#-s:updateFindHexec)
update(id, v,(prog hdecl ∗ gdef ∗1(exec dn id 2 vdef 1∗ sc 1 )calling-exec ∗2 ))= (prog hdecl ∗ gdef ∗2(exec dn id 2 vdef ∗2 sc 2 )calling-exec ∗3 )update(id, v,(prog hdecl ∗ gdef ∗1(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(exec dn id 2 vdef ∗1
sc 1 [(do-handle sid hdef ∗ calling-s)])calling-exec ∗2 ))= (prog hdecl ∗ gdef ∗2(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(exec dn id 2 vdef ∗2
sc 2 [(do-handle sid hdef ∗ calling-s)])calling-exec ∗3 )(#+s:updateFindHexecExec)update(id, v,(prog hdecl ∗ gdef ∗1(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗1 sc 1 ))calling-exec ∗2 ))= (prog hdecl ∗ gdef ∗2(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗2 sc 2 ))calling-exec ∗3 )update(id, v,(prog hdecl ∗ gdef ∗1(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗1
sc 1 [(do-handle sid hdef ∗ calling-s)]))calling-exec ∗2 ))= (prog hdecl ∗ gdef ∗2(seeing (sid dn) (hexec dn 1 id 1 [ ]))calling-exec ∗1(seeing (sid 2 dn 2 )(hexec dn id 2 vdef ∗2
sc 2 [(do-handle sid hdef ∗ calling-s)]))calling-exec ∗3 )(#+s:updateFindHexecHexec)

6 おわりに
本発表では，S 式ベースの構文を持つ逐次命令型言語であるコア SC 言語の仕様を示すとともに，持続型
例外処理機構を備えたコア SC-RE 言語（静的リンク
非明示で元の文脈においてハンドラ起動）ならびにコ
ア SC-REs 言語（静的リンクを明示化して隣接する呼び出しは隣接する配置にしたもの）の設計（構文，意味論など）を示した．また，意味論に基づく展望64.
[ 3 ] Hiraishi, T., Yasugi, M., and Yuasa, T.: A
Transformation-Based Implementation of Lightweight
Nested Functions, IPSJ Digital Courier, Vol. 2(2006),
pp. 262–279. (IPSJ Transactions on Programming,Vol. 47, No. SIG 6 (PRO 29), pp. 50–67, 2006).
[ 4 ] Yasugi, M., Hiraishi, T., and Yuasa, T.:
Lightweight Lexical Closures for Legitimate Execution Stack Access, Proceedings of the 15th International Conference on Compiler Construction(CC2006), Lecture Notes in Computer Science,
No. 3923, Springer-Verlag, March 2006, pp. 170–
184.
[ 5 ] Yasugi, M., Ikeuchi, R., Hiraishi, T., andKomiya, T.: Evaluating Portable Mechanisms for
Legitimate Execution Stack Access with a Scheme
Interpreter in an Extended SC Language, Journalof Information Processing, Vol. 27(2019), pp. 177–
189.
[ 6 ] Yasugi, M., Muraoka, D., Hiraishi, T., Umatani,S., and Emoto, K.: HOPE: A Parallel Execution
Model Based on Hierarchical Omission, Proceedingsof the 48th International Conference on Parallel
Processing (ICPP 2019), August 2019, pp. 77:1–
77:11.
[ 7 ] 八杉昌宏, 小島啓史, 小宮常康, 平石拓, 馬谷誠二, 湯淺太一: L-Closure を用いた真に末尾再帰的な Scheme
インタプリタ, 情報処理学会論文誌 プログラミング,
Vol. 3, No. 5(2010), pp. 1–17.
[ 8 ] 八杉昌宏, 平石拓, 篠原丈成, 湯淺太一: L-Closure：
高性能・高信頼プログラミング言語の実装向け言語
機構, 情報処理学会論文誌：プログラミング, Vol. 49,
No. SIG 1 (PRO 35)(2008), pp. 63–83.
[ 9 ] 田附正充, 八杉昌宏, 平石拓, 馬谷誠二: L-Closureの呼び出しコストの削減, 情報処理学会論文誌 プログ
ラミング, Vol. 6, No. 2(2013), pp. 13–32.
[10] 平石拓, 八杉昌宏, 湯淺太一: SC 言語処理系における変形規則の再利用機構, コンピュータソフトウェア,
Vol. 28, No. 1(2011), pp. 258–271.

A 付録: プログラム例（今後の課題）を述べた．
謝辞 本研究の一部は JSPS 科研費 JP19H04087，

JP21K19774，ならびに JP17K00099 の助成を受けたものである．

参 考 文 献[ 1 ] Breuel, T. M.: Lexical Closure for C++, In Proceedings of the USENIX C++ Conference, 1988,
pp. 293–304.
[ 2 ] Hiraishi, T., Yasugi, M., Umatani, S., andYuasa, T.: Backtracking-based Load Balancing,
Proceedings of the 14th ACM SIGPLAN Symposium on Principles and Practice of Parallel Programming (PPoPP 2009), February 2009, pp. 55–

A. 1 SC 言語
A. 1. 1 初期プログラム例( prog( def r int 0)( def ( fib n ) ( fn int int )( if ( > 3 n ) ( return 1)( begin ( def r0 int 0) ( def r1 int 0)(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))( exec 0 main (= r ( call fib 3))))

A. 1. 2 実行中プログラム例( prog( def r int 0)( def ( fib n ) ( fn int int )( if ( > 3 n ) ( return 1)( begin ( def r0 int 0) ( def r1 int 0)(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))( exec 2 fib ( def n int 1)( if ( > 3 1) ( return 1)( begin ( def r0 int 0) ( def r1 int 0)(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))( exec 1 fib ( def n int 3)( begin ( def r0 int 1) ( def r1 int 0)(= r1 ( calling 2))( return (+ r0 r1 ))))
( exec 0 main (= r ( calling 1))))A. 1. 3 終了時プログラム例( prog( def r int 2)( def ( fib n ) ( fn int int )( if ( > 3 n ) ( return 1)( begin ( def r0 int 0) ( def r1 int 0)(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))( exec 0 main ( begin )))A. 2 SC-RE 言語
A. 2. 1 初期プログラム例( prog( decl bt ( handler int ) 0)
( decl capt ( handler int ) ( hcalls bt ) 0)
( def _ int 0) ( def r int 0)( def ( fib n ) ( fn int int ) ( hcalls bt capt )( if ( > 3 n ) ( begin (= _ ( hcall capt )) ( return 1))( begin ( def r0 int 0) ( def r1 int 0)( do - handle( def ( bt )( begin ( def z100 int n )(= _ ( hcall bt ))( hreturn z100 )))( def ( capt )( do - handle( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcall capt ))( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1))))( begin(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))))( exec 0 main
( do - handle( def ( bt ) ( hreturn -2))
( def ( capt ) ( begin (= _ ( hcall bt )) ( hreturn -1)))(= r ( call fib 3)))))

A. 2. 2 実行中プログラム例( prog( decl bt ( handler int ) 0)
( decl capt ( handler int ) ( hcalls bt ) 0)
( def _ int 0) ( def r int 0)( def ( fib n ) ( fn int int ) ( hcalls bt capt )( if ( > 3 n ) ( begin (= _ ( hcall capt )) ( return 1))( begin ( def r0 int 0) ( def r1 int 0)( do - handle( def ( bt )( begin ( def z100 int n )(= _ ( hcall bt ))( hreturn z100 )))( def ( capt )( do - handle( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcall capt ))( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1))))( begin(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))))( exec 2 fib ( def n int 1)( begin (= _ ( hcalling 3)) ( return 1))) ;; ( capt )( exec 1 fib ( def n int 3)( begin ( def r0 int 1) ( def r1 int 0)( do - handle( hexec 6 bt
( begin ( def z100 int 3)(= _ ( hcalling 7)) ; ( bt )( hreturn z100 )))( hexec 3 capt
( do - handle( hexec 5 bt
( begin ( def z500 int 3)(= _ ( hcalling 6)) ; ( bt )( hreturn z500 )))( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcalling 4)) ;; ( capt )( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1))))( def ( bt )( begin ( def z100 int n )(= _ ( hcall bt ))( hreturn z100 )))( def ( capt )( do - handle( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcall capt ))( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1))))( begin(= r1 ( calling 2)) ;; ( fib 1)( return (+ r0 r1 ))))))( exec 0 main
( do - handle( hexec 7 bt ( hreturn -2))( hexec 4 capt
( begin (= _ ( hcalling 5)) ( hreturn -1))) ;; ( bt )( def ( bt ) ( hreturn -2))
( def ( capt ) ( begin (= _ ( hcall bt )) ( hreturn -1)))(= r ( calling 1))))) ;; ( fib 3)

A. 3 SC-REs 言語
A. 3. 1 初期プログラム例( prog( decl bt ( handler int ) 0)
( decl capt ( handler int ) ( hcalls bt ) 0)
( def _ int 0) ( def r int 0)( def ( fib n ) ( fn int int ) ( hcalls bt capt )( if ( > 3 n ) ( begin (= _ ( hcall capt )) ( return 1))( begin ( def r0 int 0) ( def r1 int 0)( do - handle f( def ( bt )( begin ( def z100 int n )(= _ ( hcall bt ))( hreturn z100 )))( def ( capt )( do - handle c( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcall capt ))( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1))))( begin(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))))( exec 0 main
( do - handle m( def ( bt ) ( hreturn -2))
( def ( capt ) ( begin (= _ ( hcall bt )) ( hreturn -1)))(= r ( call fib 3)))))

A. 3. 2 実行中プログラム例( prog( decl bt ( handler int ) 0)
( decl capt ( handler int ) ( hcalls bt ) 0)
( def _ int 0) ( def r int 0)( def ( fib n ) ( fn int int ) ( hcalls bt capt )( if ( > 3 n ) ( begin (= _ ( hcall capt )) ( return 1))( begin ( def r0 int 0) ( def r1 int 0)( do - handle f( def ( bt )( begin ( def z100 int n )(= _ ( hcall bt ))( hreturn z100 )))( def ( capt )( do - handle c( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcall capt ))( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1))))( begin(= r0 ( call fib (+ n -1)))(= r1 ( call fib (+ n -2)))( return (+ r0 r1 )))))))( seeing ( m 0)
( hexec 7 bt ( hreturn -2)))

( seeing ( f 1)
( hexec 6 bt
( begin ( def z100 int 3)(= _ ( hcalling 7)) ; ( bt )( hreturn z100 ))))( seeing ( c 3)
( hexec 5 bt
( begin ( def z500 int 3)(= _ ( hcalling 6)) ; ( bt )( hreturn z500 ))))( seeing ( m 0)
( hexec 4 capt
( begin (= _ ( hcalling 5)) ( hreturn -1)))) ;; ( bt )( seeing ( f 1)
( hexec 3 capt
( do - handle c( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcalling 4)) ;; ( capt )( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1)))))( exec 2 fib ( def n int 1)( begin (= _ ( hcalling 3)) ( return 1))) ;; ( capt )( exec 1 fib ( def n int 3)( begin ( def r0 int 1) ( def r1 int 0)( do - handle f( def ( bt )( begin ( def z100 int n )(= _ ( hcall bt ))( hreturn z100 )))( def ( capt )( do - handle c( def ( bt )( begin ( def z500 int n )(= _ ( hcall bt ))( hreturn z500 )))( begin(= _ ( hcall capt ))( out 10 n ) ( out 10 r0 ) ( out 10 r1 )( hreturn 1))))( begin(= r1 ( calling 2)) ;; ( fib 1)( return (+ r0 r1 ))))))( exec 0 main
( do - handle m( def ( bt ) ( hreturn -2))
( def ( capt ) ( begin (= _ ( hcall bt )) ( hreturn -1)))(= r ( calling 1))))) ;; ( fib 3)