インタプリタの作成

2010年2月21日

ファイル

04-interpreter.tar.gz

内容

C で Python インタプリタを作りましょう。ただし、Py_Main() を使わずに、です。PyRun_SimpleString() は直接には使いません。その理由は、これに if 文を渡してみればわかります。

$ ./simple.exe
>>> a = 1
>>> if a == 1:
  File "<string>", line 1
    if a == 1:
             ^
SyntaxError: unexpected EOF while parsing

ここでは code モジュールの InteractiveConsole を使います。これは Python インタプリタの機能を提供します。

>>> import code
>>> i = code.InteractiveConsole()
>>> i.push("a = 1")
False
>>> i.push("a")
1
False
>>> i.push("a + 2")
3
False

push() を使うと Python コードを実行できます。PyRun_SimpleString() と違い、if 文でも大丈夫です。また、interact() を実行すると、まんま Python ぽいものになります。

>>> i.interact()
Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

ここでは push() を使ってインタプリタを実装しましょう。

さて、プログラムを見ていきましょう。

	printf("Python %s on %s\n", Py_GetVersion(), Py_GetPlatform());
	printf("Type \"help\", \"copyright\", \"credits\" or \"license\" "
	  "for more information.\n");

それっぽく見えるように、Python と同じメッセージを表示しています。

	Py_Initialize();

	PyRun_SimpleString("import code");
	PyRun_SimpleString("__interpreter = code.InteractiveConsole(locals())");

初期化後、PyRun_SimpleString() を使って code モジュールをインポートし、InteractiveConsole を取得しています。

	mainModule = PyImport_AddModule("__main__");
	globalDict = PyModule_GetDict(mainModule);
	interpreter = PyDict_GetItemString(globalDict, "__interpreter");
	if(interpreter) Py_INCREF(interpreter);

C から操作できるように、InteractiveConsole を取得します。Py_INCREF() というのは、ガベージコレクション用にオブジェクトの参照カウンタを増やす関数です。

	multiline = 0;

	printf(">>> ");

	while(fgets(buffer, sizeof(buffer), stdin)){
		result = PyObject_CallMethod(interpreter, "push", "s", buffer);

		if(result){
			if(PyArg_Parse(result, "i", &status)){
				multiline = status > 0;
			}
			Py_DECREF(result);
		}

		if(multiline){
			printf("... ");
		}else{
			printf(">>> ");
		}
	}

	Py_Finalize();

標準入力から文字列を 1 行取得し、PyObject_CallMethod() を使って InteractiveConsole の push() を呼び出しています。PyArg_Parse() で PyObject_CallMethod() の返り値から整数を取得します。これが 0 なら 1 行のコマンドで、さもなければ if 文のような複数行からなるコードになります。その値にしたがって表示するプロンプトを切り替えています。

実行してみましょう。

$ make
$ ./interpreter.exe
Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 1
>>> if a == 1:
...     print a
1

if 文にもちゃんと対応します。まんま Python です。ちなみに、exit() と quit() は効きません。

C で Python を使う 目次へ