小さなインタプリタ

回 6-3 道 6 言語と処理系

インタプリタは、プログラムを読みながら、その場で 1 文ずつ実行していくプログラムです。先に全部を機械の命令に訳して実行ファイルを作るコンパイラと違い、書いてすぐ動かせる代わりに、誤りはその行を実行するまで見つかりません。Python も中ではまずバイトコードという命令の列に直し、それを仮想マシンが 1 つずつ実行しています。

木をたどって文を実行するインタプリタと、先に命令へ直すコンパイラの違いを、誤りが見つかる時と止まり方で切り分けられるようになります。

書いたプログラムを、誰が動かしているのか

インタプリタが動かしています。Python で print(1 + 2) と書いて実行すると 3 が出ますが、CPU (計算をする部品) が Python の字を直に読めるわけではありません。字を読み、意味を取り、その通りに計算するプログラムが間にいます。それがインタプリタです。

前の回 6-2 では、1 + 2 * 3 のような式を字句に切り、木に組み立て、木をたどって 7 を出しました。この回はその続きです。式だけでは「変数に入れる」「条件で分ける」「くり返す」ができないので、木に文を足します。

この小さな言語を「ちび電卓」と呼びます。set、print、if、while の 4 つの文を持たせ、木のまま実行するインタプリタを書いて、1 から 10 の合計 55 を出します。止まらない場面と誤りに気づくのが遅れる場面も動かし、最後にコンパイラの考え方と比べます。

インタプリタが台本を上から 1 行ずつ実行し、3 行目の無い変数に着いた所で止まる様子の図

インタプリタはどう動くのか

インタプリタは、プログラムを木に組み立て、その木を上からたどりながら、文を 1 つずつその場で実行します。変数の値は、名前から値を引ける表 (環境) に入れておきます。

インタプリタとは何か

インタプリタ (interpreter) は、英語で「通訳する人」という意味です。聞きながらその場で訳す通訳のように、プログラムを読みながらその場で実行します。中は、おおむね次の 3 段で動きます。

  1. 字句解析 (6-1 と 6-2)。字の並びを、数、名前、記号の字句に切る
  2. 構文解析 (6-2)。字句を、どれが先に計算されるかが分かる木に組み立てる
  3. 評価と実行 (この回)。木をたどって、計算したり、文を実行したりする

この回で書くのは 3 段目です。1 段目と 2 段目は 6-2 で書いたので、ここでは 6-2 の構文解析が作る木を、手で書きます。

式と文と環境

ちび電卓の木は、Python のタプル (値を並べた組) で書きます。式は値を返す物、文は何かをする物です。

種類木の形意味
数33 そのもの
変数"i"環境から i の値を引く
2 項の演算("+", "s", "i")左と右を計算して足す
入れる文("set", "i", 1)i に 1 を入れる
表示する文("print", "s")s の値を表示する
分ける文("if", 条件, [文…])条件が成り立てば中の文を実行する
くり返す文("while", 条件, [文…])条件が成り立つ間、中の文をくり返す

演算子は + - * / と、比べる < と == です。/ は整数の割り算で、Python の // と同じです。プログラムは文のリストで、[文…] の部分を「ブロック」(文のまとまり) と呼びます。

環境は、変数の名前から値を引く辞書です。("set", "i", 1) を実行すると環境は {"i": 1} になり、式の中の "i" は環境を引いて 1 になります。

コンパイラとの違い

いちばんの違いは、いつ訳すかです。コンパイラは、実行の前にプログラム全体を機械の命令に訳して、実行ファイルを作ります。インタプリタは、実行しながらその場で 1 文ずつ処理します。

比べる点インタプリタコンパイラ
訳す時実行しながら実行の前に全部
出来る物何も残さない実行ファイル
誤りが見つかる時その行を実行した時訳す時に見つかる物がある
書いて試すまですぐ訳す手間がかかる
実行の速さ遅くなりやすい速くなりやすい
代表の言語Python、Ruby、JavaScript、PHPC、C++、Go、Rust

下の図は、変数の名前を 1 か所書き間違えた 4 行を、両方の方式に流した物です。コンパイラ方式は実行の前に全部を調べて 1 行も動かずに止まり、インタプリタ方式は間違えた行に着くまで動いてから止まります。実行の前にどこまで調べるかは言語によって違います。

先に全部訳すか、その場で訳すか

インタプリタの長所と短所

長所は、書いてすぐ動かせることです。訳す手間が無いので、1 行書いて試し、直してまた試す、をすばやく回せます。対話の形で 1 行ずつ打ち込んで動かすのにも向きます。

短所は 2 つです。1 つは遅くなりやすいことで、この回のインタプリタは while を 1 周するたびに同じ木をたどり直し、節の種類を見分け直します。もう 1 つは、誤りがその行を実行するまで見つからないことです。めったに通らない枝の誤りは、本番でその枝を通った時に初めて分かります。

Python はインタプリタなのか

Python はインタプリタ型の言語ですが、ソースを字のまま 1 行ずつ読んでいるわけではありません。Python の公式文書の用語集は、Python のソースはバイトコード (仮想マシン用の命令の列) に直され、それを仮想マシン (ソフトで作られた計算機) が実行すると書いています。直したバイトコードは .pyc という名前のファイルに残され、同じファイルを 2 回目に実行するときは直す手間を省けるとも書いています。

同じ用語集は、Python をインタプリタの言語としたうえで、バイトコードへ直すコンパイラがあるので境目はあいまいになり得ると書いています。実行ファイルを作らずにソースをそのまま実行できるので、インタプリタ型と呼ばれます。

s = s + i の 1 行がどんな命令になるかは、標準ライブラリの dis で見られます。次は手元の Python 3.14 の出力から、命令の名前と相手だけを抜き出した物です。

手元の Python 3.14 の dis から抜粋

LOAD_NAME   s
LOAD_NAME   i
BINARY_OP   +
STORE_NAME  s

s を積み、i を積み、足して、s に入れる 4 つの命令です。用語集によるとバイトコードは版の間で同じとは限らないので、版が違えば名前も変わり得ます。

動かして確かめる

自分の言語で 1 から 10 の合計を出す

1 本目は、ちび電卓のインタプリタです。ev が式を計算し、ex が文を 1 つ実行し、run が文のリストを順に回します。プログラムは、i を 1 から 10 まで回して s に足し、s を表示し、s が 55 なら i も表示します。6-2 の構文解析が作る木を、ここでは手で書きます。

動かす ちび電卓のインタプリタ

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

55
11
{'i': 11, 's': 55}

1 から 10 の合計 55 が出ました。while は i が 11 になった所で i < 11 が成り立たなくなって抜けるので、if の中で表示した i は 11 です。最後の行は、実行し終えた後の環境です。

ev は自分を呼んで木の下へ降ります。while の中身もただの文のリストなので、run をもう一度呼べば実行できます。インタプリタの芯は、木の形ごとに「何をするか」を書いた、この数行です。

同じ木を、スタックの機械の命令に直す

2 本目は、同じ木を先に命令の列に直し、その列を小さな機械で実行します。これがコンパイラの考え方です。機械は、値を積んで上から取り出すスタック (皿の山) を 1 本だけ持ちます。

push と load は数や変数の値を積み、store と print は取り出して入れたり表示したりし、op は 2 つ取り出して計算します。jz は取り出した値が成り立たなければ指定の番地へ跳び、jump は必ず跳びます。

動かす 木を命令に直して実行する

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

0 push 1
1 store i
2 push 0
3 store s
4 load i
5 push 11
6 op <
7 jz 17
8 load s
9 load i
10 op +
11 store s
12 load i
13 push 1
14 op +
15 store i
16 jump 4
17 load s
18 print
命令 19
55
実行 140 回

木は 19 個の命令の平らな列になりました。while は 4 番から 7 番で条件を調べ、成り立たなければ 17 番へ跳び、成り立てば 8 番から 15 番を実行して 16 番で 4 番へ戻ります。8 番から 11 番は、上の dis で見た Python の命令とほぼ同じ形です。

機械は 140 回の命令を実行して 55 を出しました。訳すのは最初の 1 回だけで、実行の間は「次の命令を取り、種類で分ける」をくり返すだけです。その代わり、実行の前に直す手間が増えます。

皿の山のように、最後に積んだ物から先に取り出すスタックの図

インタプリタはどこで崩れるのか

インタプリタは書かれた通りに実行するので、終わらないプログラムは終わりません。誤りはその行を実行するまで見つからず、通らない枝の誤りはずっと見つかりません。

終わらない while を、手順の上限で止める

i を増やし忘れると、i < 11 はいつまでも成り立ち、while は終わりません。終わるかどうかを実行の前にいつでも見分ける方法は無いことが知られています (停止問題)。そこで文を 1 つ実行するたびに 1 数え、上限を超えたら止めます。

次のコードは、上限 1000 のインタプリタに、i を増やし忘れた while と、値を入れていない変数を読むプログラムを流します。while の条件を計算した回数は数えません。

動かす 上限と、置いていない変数

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

止めた RuntimeError set
手順 1001
{'i': 1, 's': 997}
---
0
止めた NameError u
手順 4
{'s': 0}

1 つ目は、上限 1000 を超えた 1001 手目の set で止めました。最初の 2 つの set と while で 3 手、残りの 997 手は中の set で、s は 1 を 997 回足した 997、i は 1 のままです。上限が無ければ止まりません。

まだ置いていない変数は、実行するまで分からない

2 つ目は、0 を表示したあとの 4 手目、print u に着いた所で止まりました。u に値を入れた文がどこにも無いのに、1 行目から 3 行目までは何事もなく動いています。

もっと危ないのは t です。t もどこにも置いていませんが、s < 0 が成り立たないので if の中は実行されず、誤りは出ませんでした。このプログラムを何度動かしても、s が負になる日まで t の誤りは見つかりません。

Python も同じです。次のコードは、Python の組み込みの compile でソースを先にバイトコードに直してから、exec で実行します。

動かす Python も先に直してから動かす

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

code
('print', 'y', 'z')
1
NameError
name 'z' is not defined

compile は誤りを出さずに code (バイトコードの入れ物) を作り、使う名前の一覧に y も z も入れました。名前に値があるかは調べていません。実行すると 1 を表示したあと z の行で止まり、通らない if の中の y は誤りになりませんでした。

先に直しても、何を調べるかを決めなければ誤りは早く見つかりません。

インタプリタの言語を、仕事でどう確かめるのか

誤りが実行の時まで見つからないので、仕事では「全部の行を一度は実行したか」と「実行の前に何を調べるか」を確かめます。

  1. 試験で if の両方の枝を通し、どの行を通ったかを測る道具 (網羅率の測り) で、通っていない行を探す
  2. 構文の誤りは、python -m py_compile ファイル名 でバイトコードに直すと実行せずに見つかる。名前に値があるかは調べない
  3. 型の注釈を書き、型検査の道具で実行の前に調べる
  4. 外から来たプログラムや式を自分のインタプリタで動かすなら、手順の数や時間に上限を置く

次の 6-4 では、同じ木に型を付け、実行の前に木を全部調べて、こうした誤りの一部を止めます。

関わる実例

手順の上限の値付けを直した Ethereum の分岐 (2016年10月18日)

Ethereum (イーサリアム) は、だれかが送ったプログラムを多くの計算機が仮想マシンで同じように実行する仕組みです。ethereum.org の公式の解説は、うっかりや悪意の無限ループを避けるため、どの取引 (送られた処理) にも計算の手順の上限を決めさせ、その単位を gas (ガス) と呼ぶと書いています。この回で文ごとに 1 数えたのと同じ考えを、命令ごとの値段で行います。

ethereum.org の歴史の頁によると、2016年9月から10月にかけてネットワークへの DoS 攻撃 (処理を詰まらせる攻撃) があり、2016年10月18日の Tangerine Whistle という分岐 (ブロック番号 2,463,000) が最初の対応でした。中身は、値段の安すぎた命令の値段を上げることです。

その分岐に入った EIP-150 (2016年9月24日の作成) は、理由の欄に、最近の DoS 攻撃で、状態 (口座やデータ) を読む命令がほかの命令に比べて安すぎると分かったと書いています。値段は、たとえば SLOAD を 50 から 200 へ、BALANCE を 20 から 400 へ、EXTCODESIZE を 20 から 700 へ、CALL を 40 から 700 へ上げました。

崩れた前提は「上限で数える 1 手の値段が、実際の重さに見合っている」です。1 手が軽すぎる命令があると、上限の中に重い仕事を大量に詰め込めます。どの文も同じ 1 手と数えるこの回のインタプリタも、同じ弱点を持っています。

出典は Gas and fees (ethereum.org)、The history of Ethereum (ethereum.org)、EIP-150 (Ethereum Improvement Proposals) です。

よくある問い

インタプリタはコンパイラより必ず遅いのですか

遅くなりやすいですが、必ずではありません。木をたどり直す手間があるぶん、同じ仕事なら実行ファイルに訳した物より遅くなりがちです。ただ、重い計算をコンパイル済みの部品に任せたり、JIT のように実行中に訳したりする仕組みもあるので、言語の名前だけでは速さは決まりません。

Python のインタプリタはどこにあるのですか

ふだん打つ python3 という命令が、Python のインタプリタそのものです。ファイル名を付けずに起動すると、1 行ごとに実行して結果を返す対話の形になります。この頁のコードは、ブラウザの中の Python (Pyodide) が実行しています。

JIT コンパイルとは何ですか

JIT (ジャスト・イン・タイム) コンパイルは、実行しながら、何度も通る所だけをその場で機械の命令に訳す方法です。はじめはインタプリタのように動かし、くり返し通る所を訳して次から使います。すぐ動かせることと速さの両方を狙うやり方です。

インタプリタの言語は、配るのが簡単なのですか

ソースのまま配れば、相手の計算機にインタプリタがあれば動きます。コンパイラの言語は、Windows、Mac、Linux など動かす先ごとに実行ファイルを作るのがふつうです。その代わり相手にインタプリタをそろえてもらう手間があり、どちらが楽かは相手しだいです。

確認クイズ

答えだと思う選択肢を押すと、その場で正誤と理由が開きます。4 問すべてに正しく答えると、この回は修了で、石が緑に変わります (この端末のブラウザに残ります)。間違えた問いは、あとから解き直せます。

1 問目

インタプリタとコンパイラのいちばんの違いはどれですか。

2 問目

このコードを動かすと何が出ますか。

小さなスタックの機械

code = [
    ("push", 2),
    ("push", 3),
    ("op", "*"),
    ("push", 4),
    ("op", "+"),
]
st = []
for op, a in code:
    if op == "push":
        st.append(a)
    elif a == "*":
        y = st.pop()
        st.append(st.pop() * y)
    else:
        y = st.pop()
        st.append(st.pop() + y)
print(st)

3 問目

この回のインタプリタで、if の中にだけある「まだ値を入れていない変数 t」が誤りにならなかったのはなぜですか。

4 問目

Python の公式の用語集によると、Python のソースはどう実行されますか。

説明課題

インタプリタとコンパイラの違いを、「木」「環境」「命令の列」「誤りが見つかる時」という言葉を使って、3〜4 文で説明してください。書いたら、下の模範解答を開いて比べてください。

模範解答

自分の説明を書いてから開いて、比べてください。

この回の先の一歩

設定ファイルの式、テンプレート、表計算の数式など、仕事で出会う小さな言語の多くは木をたどって動いていて、誤りがいつ見つかり、どこで止まるかを知っていると、エラーの出方から原因を読み解けます。

一次資料

どれも 2026年10月27日に開いて、題と中身を確かめました。

次へ

道 6 言語と処理系の回

  1. 6-1正規表現とオートマトン後戻りが増える式頁で読む
  2. 6-2字句解析と構文解析電卓の式を木にする頁で読む
  3. 6-3小さなインタプリタ変数と if を持つ言語この回
  4. 6-4型と型検査型が間違いを先に止める頁で読む

道 6 の頁へ戻るほかの道を見る