# Rpython
RPythonは、Python言語の制限付きサブセットです。PyPyツールチェーンで動的言語インタプリタを実装するために使用されます。
<!--この制限により、RPythonのプログラムの型推論(ひいては他の言語への翻訳)が可能になります。-->
## 1. HELLO WORD
これは、RPythonで書かれた普通のHello Worldプログラムのソースコードです。
```python=
def entry_point(argv): #entry_pointは関数の名前です
print "Hello, World!"
return 0
# ターゲット関数は、RPythonプログラムのメイン関数です。
# コマンドライン引数を入力として受け取り、エントリポイント関数を返します。
def target(*args):
return entry_point
```
コンパイラによって "hello-c "という名前の実行形式が生成される。実行ファイルを起動すると、"Hello, World!" が出ます。
```python=
vietanh@KU-info2019-031:~/Rpython$ ./hello-c
Hello, World!
```
----
RPythonの構文はPythonの制限付きバージョンなので、どんなPythonインタプリタでも以前のようにhello worldのサンプルを実行することができます。
```python=
def entry_point(argv):
print "Hello, World!"
return 0
def target(*args):
return entry_point
# Pythonでスクリプトを解釈するためのエントリーポイントです。
if __name__ == "__main__":
import sys #sysはPythonのインタプリタや実行環境に関する情報を扱うためのライブラリです。
entry_point(sys.argv) #sys.argvはコマンドライン引数を扱います。コマンドライン引数とは名前の通り、コマンドラインで指定した引数のことです#
#main() は関数を呼び出しています。
if __name__ == '__main__': # __name__ は Python で使用できる隠し変数の 1 つで、モジュール名を表す文字列が入っています.
#構文は自分がインポートされてないときだけ main() を呼び出すという意味になります。 __name__ の値を見ることで 1 つのファイルで実行スクリプトとモジュールの両方を実装することができるようになります。
```
-----
## 2.組み込み型
### 2.1 真偽判定テスト
Python と同じです。
以下の値はfalseとする:
- None : 変数に None というキーワードを代入すると、その変数はどの型にも属さない変数になります。
- False
- 任意の数値型のゼロ、例えば、0, 0L, 0.0。
- 空のシーケンス、例えば、''、()、[]。 空のタプルを作りたいときは () を使います
- 空のマッピング、例えば{}。 要素が空の辞書を作る場合は x = {} とします。
それ以外の値はすべて真(True)とみなされます.
ブール値の結果を持つ演算は、常に 偽(false) の場合は 0 または False を、真(true) の場合は 1 または True を返します。
<!-- 偽: 真: -->
----
### 2.2 ブール演算
and、or、not 使えます。
### 2.3 比較
<, <=, >, >=, ==, !=, is, is not :使えます。
isとis notはオブジェクトの同一性を比較する操作である。
### 2.4 数値型 (Numeric Types)
#### 2.4.1 組み込みの数値型(Built-in numeric types)
数値の種類には、整数(int)、長整数(long)、浮動小数点数(float フロート)の3種類があります。
なお、RPythonではint型とlong型は同じ型である。
- 数値に対しては四則演算ができます:
-- 加算: x+y
-- 減算:x-y
-- 乗算: x*y
-- 除算: x/y 11/4=2.75
-- 商: x//y 11//4=2
-- 剰余: x%y 11%4=3
:::info
long(), complex(), conjugate(), x ** y, divmode(x, y), pow(x, y), float(nan) などは Python で使用できる演算や組み込み関数ですが、RPython では現在サポートされていません。しかし、RPythonのmathライブラリはいくつかの数学関数を提供しています。
:::
#### 2.4.2 ビット演算
RPythonの整数型に対するビット演算は、Pythonと同じです。
<!--
x = 2 , y = 4
x | y = 6 (or) 論理和
x ^ y = 6 (XOR) 排他的論理和:^演算:
x & y = 0 (AND) 論理積
x << n = 32 (Bitwise left shift) ビットシフト: <<演算子、
x >> n = 1 (Bitwise right shift) ビットシフト: >>演算子
~x = -3 (Bitwise NOT) ビット反転
-->
2進数で表した整数型intの値の各ビットに対して、それぞれ論理積、論理和、排他的論理和、ビット反転、左ビットシフト、右ビットシフトを行う。
```python=
def numeric_bitwise():
x = 2
y = 4
n = 1
print "x = ", x, ", y = ", y
print "x | y =", x | y #論理和
print "x ^ y =", x ^ y #(XOR)排他的論理和:^演算
print "x & y =", x & y #(AND) 論理積
print "x << n =", x << n # (Bitwise left shift) ビットシフト: <<演算子、
print "x >> n =", x >> n #(Bitwise right shift) ビットシフト: >>演算子
print "~x =", ~x # ビット反転
def entry_point(argv):
numeric_bitwise()
return 0
def target(*args): #*args:複数の引数をタプルとして受け取る
return entry_point
# vietanh@KU-info2019-031:~/Rpython$ ./test-c
# x = 2 , y = 4
# x | y = 6
# x ^ y = 6
# x & y = 0
# x << n = 4
# x >> n = 1
# ~x = -3
```
<!--
*argsのように*をつけた引数を定義すると、任意の数の引数を指定できる
```python=
def my_sum(*args):
return sum(args)
print(my_sum(1, 2, 3, 4))
# 10
```
-->
- x&y: 論理積は、2つの数値の同じ位置にあるビットを互いに比較します。掛け算します。*
例えば、 2&4=???
まず、2つの数値10と12を2進数として表します:
2=0010
4=0100
| 2 | 4 | 2&4 |
| -- | ---| ------|
| 0 | 0 | 0 |
| 0 | 1| 0
| 1| 0 | 0
| 0 | 0 |0
その結果、2進数の0000が得られます。==>10進数では0という
- x|y 論理和は2つの数値の同じ位置にあるビットを比較し、足し算します。計算結果の1以上はすべて1として扱います(2→1)。
| 2 | 4 | 2 / 4 |
| -- | ---| ------|
| o |0 | 0 |
| 0 | 1| 1 |
| 1| 0 | 1 |
| 0 | 0 |0 |
その結果、2進数の0110が得られる。10進数の6である。
- ~x: (NOT)ビット反転は 各ビットの逆の値を返します.ビットの符号も変更する。
<!--RPythonでは上の桁が無限に0で埋められていると見なして、反転も上の桁が無限に1で埋められているという想定でマイナスを返します:-->
==ヒント: ~x→ -(x+1) という式==
例えば:
2=0100 (=0100 正の数なので、0前に付けます。)
~2=1011(1011になる。 1は負の整数、011は10進数で3という数.
==> 10進数では-3という
- x ^ y:排他的論理和 (XOR): 2つの数値の同じ位置にあるビットを互いに比較します。足し算して、計算結果の1以上はすべて0として扱います(1+1=2=0b10 → 0)
| 2 | 4 | 2^4 |
| -- | ---| ------|
| 0 |0 | 0 |
| 0 | 1 | 1 |
| 1| 0 | 1 |
| 0 | 0 |0 |
==> 10進数では 6という
- x << n: Bitwise left shift : 指定した桁だけ左にずらして、空いたビットには0が入ります。

2<<1:
>0010 =2
0100 =4
- x >> n: Bitwise right shift 指定した桁だけ右にずらして、最下位より先に押し出されたビットは消えます。0は正の数を示します, 数字の1は負の数字を表します

2 >> 1
>0011 = 2
001 = 1
#### 2.4.3 Additional methods(他のメソッド)
Pythonでは,整数型と浮動小数点型に対していくつかのメソッドが追加されています.しかし、これらのメソッドはすべてRPythonではサポートされていません。
例えば:float.is_integer();int.bit_length();long.bit_length();・・・サポートされてない。
#### 2.4.4 Math functions(数学関数)
RpythonではPythonと同じ数学関数を使用できます.
数学関数には、fabs, log, log10, log1p, copysign, atan2, frexp, modf, ldexp, pow, fmod, hypot, floor, sqrt, sin, cos, acos, asin, atan, ceil, cosh, exp, fabs, sinh, tan, tanh, acosh, asinh, atanh, expm1, が含まれています。
```python=
import math
def numeric_math():
print math.pi
print math.log(10)
print math.sqrt(2) #へいほうこん
def entry_point(argv):
numeric_math()
return 0
def target(*args):
return entry_point
# vietanh@KU-info2019-031:~/Rpython$ ./test-c
# 3.14159265359
# 2.302585
# 1.414214
```
### 2.5 Iterator Types(イテレータ型)
RPython のイテレータ型は Python と似ていて、コンテナ上のイテレータに役立ちます。イテレータオブジェクトは __ iter__() と next() メソッドをサポートする必要があります。
:::info
RPythonでは、__iter__()メソッドでリストのイテレータオブジェクトを取得することはできません。
my_list = [4, 7, 0, 3]
my_iter = my_list.__iter__() #rython で使えない。
:::
<!-- イテレータオブジェクト(list、tuple、set など)は内部にイテレータを持ち、for 文で要素を反復的に処理する際に自動的に使われる。要は、コンテナに入っている要素一つ一つアクセスして返している。 -->

- イテレータとは、list, tuple, set などの集合を表現するオブジェクトを iter 関数 を使って コピー したようなものです。
- イテレータからは next 関数 を使って、 1つずつ要素を 取り出す ことができます。
```python=
#PYTHON
my_list = [4, 7, 0, 3]
# iter()関数を使用してイテレーターを作成します
my_iter = iter(my_list)
#my_iter = my_list.__iter__() pythonで使える。
#next()関数
#prints 4
print(next(my_iter))
#prints 7
print(next(my_iter))
#prints 0
print(my_iter.__next__())
#prints 3
print(my_iter.__next__())
```
<!--while True:
try:
i = next(my_iter)
except StopIteration:
break
print(i)
-->
<!-- Class とは:
Pythonは、オブジェクト指向プログラミング言語の1つです。オブジェクト指向とは、値やそれを扱うためのソースコードをまとめて「オブジェクト(物)」として扱う考え方です。
オブジェクトを表現するために重要な機能が、「クラス」です。クラスは、オブジェクトを生成するときのひな型のようなものです。
-->
<!--
オブジェクト指向におけるクラスとは、オブジェクトを生成するうえで使われる型のようなものです
-->
```python=
#RPYTHON
#オブジェクト指向におけるクラスとは、オブジェクトを生成するうえで使われる型のようなものです
#selfは、インスタンス自身を示すものです。
class Counter():
c = 100
def __iter__(self): #selfは「オブジェクト(インスタンス)自身」を意味するものです。
return self
def next(self):
t = self.c
self.c += 1
return t
def iterator():
l = [1, 2, 3, 4, 5]
it = iter(l) # return an iterator object on the list l
print(it.next()) # リストの最初の要素をプリントする 1
d = Counter() #dにはクラスCounterのプロパティもあります
it = d.__iter__() #class Counter() の中に ”def __iter__(self)” 関数: 呼び出し ==> itはイテレータになります
print(it.next()) #イテレータからはnext関数を使って、 要素を 取り出すこと =100;self.c=101 になります。
print(next(it)) #"def next(self)" 関数を使って 101を返します。
def entry_point(argv):
iterator()
return 0
def target(*args): return entry_point
```
def iterator_unsupported():
l = [1, 2, 3, 4, 5]
# __iter__() method for list is not supported in RPython
it = l.__iter__()
print(it.next())
<!--
イテレータを理解して、自分で定義できるようになると、次のようなことができるようになります:
- for 文の in で使えるようになったり
- 集合を引数に取る関数で使えるようになったりします
-->
コンテナオブジェクトの __ iter__() メソッドがジェネレータとして実装されている場合、 __ iter__() と next() メソッドを持つイテレータオブジェクトが自動的に返されます。
<!--
return文でそのまま値を返す関数を作ったとします。一度に大きなリストが返ってくるような関数だと、たくさんのメモリを一度に消費してしまうことになります。
そのようなときは、yieldを使う事でその莫大な量の戻り値を小分けにして返すことが出来ます。
-->
- ジェネレータは、イテレータの一種になります。要素を取り出すごとに処理を実行して、要素を生成することが出来ます。
- 繰り返し処理はyield(イールド)を使うとメモリの消費が少ない
- ジェネレータのnext関数を使う
```python=
class Box():
content = []
def add(self, cat): #空のlist に要素を追加します
self.content.append(cat)
def __iter__(self):
for i in self.content: #リスト内の要素を繰り返します
yield i #要素が返す
def iterator_generator():
b = Box()
b.add("Tiger")
b.add("Kitty")
it = b.__iter__() #itはイテレータになります
print it.next()
for i in b:
print i
def entry_point(argv):
iterator_generator()
return 0
def target(*args):
return entry_point
```
### 2.6 Sequence Types (シーケンス型)
文字列、Unicode文字列、リスト、タプル、バイト配列、バッファ、xrangeオブジェクトの7種類の配列があります。
```python=
def sequence():
s = "this is a string" #文字列
u = u"this is a unicode string" #Unicode文字列
l = ["this", "is", "a", "list", "of", "strings"] #リスト
t = ("first", "second") #タプル
ba = bytearray(b"\x44\x45\x41\x44\x42\x45\x45\x46") #バイト配列
buf = buffer(s, 10, 6) #バッファ
r = xrange(0, 5, 2) #xrangeオブジェクト
print s; print u; print l; print t; print ba; print buf
# this is a string
# this is a unicode string
# ['this', 'is', 'a', 'list', 'of', 'strings']
# ('first', 'second')
# DEADBEEF
# string
for i in r: print i #0,2,4
# x in s: True if an item of s is equal to x, else False
if 't' in s:
print "t is in string", s #t is in string this is a string
# x not in s: False if an item of s is equal to x, else True
if 'b' not in u:
print "b is not in unicode string", u #b is not in unicode string this is a unicode string
# s + t: sとtの連結
print l + [":)"] #['this', 'is', 'a', 'list', 'of', 'strings', ':)']
# s * n, n * s: にn回足すのと同じ
print t * 2 #('first', 'second', 'first', 'second')
# s[i]: s の第 1 項、原点 0
print "3rd item of l is:", l[2] #3rd item of l is: a
# s[i:j]: からsの切れ端は、iからjまで
print "slice of s:", s[2:-1] #slice of s: is is a strin
# len(s): length of s
print "length of ba is", len(ba) #length of ba is 8
# s.index(x): sにおけるxの最初の出現位置のインデックス
print l.index("of") #4
# s.count(x): s におけるxの総発生数
print s.count("is") #2
def entry_point(argv):
sequence()
return 0
def target(*args): return entry_point
```
#### 2.6.1 STRING
RPythonでは、いくつかの文字列メソッドのみがサポートされています。
以下の例は、RPythonでサポートされているメソッドとその使用方法を示しています:
```python=
# -*- coding: utf-8 -*-
def strings():
s = "this is a string"
print s.count("this", 0, len(s)) #1
if s.endswith("ing"): print "s ends with string" #文字列の終わり: s ends with string
if s.find("is"): print "found is in s" #s文字列に"is"がある場合...
s2 = "thisisastring2"
if s2.isalnum(): print "s2 is alnum" #isalnum()メソッド は、英文字と数字以外が含まれていれば「Flase」を返すことができます。
s3 = "thisisastringthree"
if s3.isalpha(): print "s3 is alpha" #isalpha() メソッドは有効な文字列の場合、True を返します。数字や特殊文字などの他の文字が見つかった場合は、False を返します.
s4 = "12345"
if s4.isdigit(): print "s4 is digit" #isdigit() メソッドは文字列中のすべての文字が数字で使われる文字列の場合にTRUEを返します
l = ["this", "is", "a", "string"]
print " ".join(l) #リストの要素(str)を連結・結合する際はjoinを使います。 ==> this is a string
print "THI IS A STRING".lower() #小文字 : thi is a string
print ' spacious '.lstrip() #引数として渡された文字をStringの左から削除します : spacious
print s.rfind("is") #rfind()は、文字列strが見つかった最後のインデックスを返します。見つからなかった場合は-1を返します : 5
print s.rsplit(" ") #rsplitは右側から文字列を指定回数分割する : ['this', 'is', 'a', 'string']
print s.split(" ") #文字列を分割する ['this', 'is', 'a', 'string']
s_lines = "This is a string.\nAnd another string."
print s_lines.splitlines()
if s.startswith("this"): print "s starts with this"
print ' spacious '.strip()
print s.upper()
print "%s, %d, %x, %o, %f" % ("string", 1, 16, 9, 3.14)
def entry_point(argv):
strings()
return 0
def target(*args): return entry_point
```
----
## 3.組込み関数(built-in Functions)
Python インタープリタには多くの組み込み関数がありますが、RPython でサポートされているのはそのうちのわずかなものです。
:::info
RPythonでは組み込みの関数は非常に限られています。例えば、sum()はRPythonではサポートされていません。さらに、min()のようなサポートされている関数は、Pythonでは同じ機能を提供しません。RPythonのmin()関数は2つの値しか比較できませんが、Pythonではリスト中の最小値を見つけるのに使うことができます。
:::
```python=
def builtin_functions():
print bool(1) # return a boolean value =True
print int(1.0) # return an integer value =1
print float(1) # return a float value =1.0
print chr(65) # return a string of one character whose ASCII code is the input = A
print str(53) # return a string containing a nicely printable representation of an object
print unichr(97) # return the Unicode string of one character whose Unicode code is the integer input
print bytearray('abc') # return a new array of bytes
print list('abc') # return a list whose items are the same and in the same order as iterable’s items
for i in range(3): # create a list containing arithmetic progressions
print i
for i in xrange(3):
print i
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
for i in enumerate(seasons): # return an enumerate object
print i
print min(1, 2) # return the smallest of two arguments
print max(1, 2) # return the largest of two arguments
for i in reversed([1, 2, 3]): # return a reverse iterator
print i
def entry_point(argv):
builtin_functions()
return 0
def target(*args): return entry_point
```
```python=
vietanh@KU-info2019-031:~/Rpython$ ./built-in-c
True
1
1.0
A
53
a
abc
abc
0
1
2
0
1
2
(0, 'Spring')
(1, 'Summer')
(2, 'Fall')
(3, 'Winter')
1
2
3
2
1
```
## 4。 Class
RPythonのクラスはPythonと非常によく似ています。クラスを継承し、メソッドをオーバーライドすることができます。
:::info
RPythonにはいくつかの制約があります: 単一継承しかサポートされない。 単一継承サポートしない。多重継承サポートしません。
:::
クラスとは新しい型を定義するための仕組みです。クラスを使うことで複数の変数と関数を集約した変数を作ることができるようになります。
<!--
__ init__() はいわゆるコンストラクタです。コンストラクタには 2 つの役割があります:
- インスタンス化をするときに最初に呼び出される
- クラスのメンバ変数を定義し、それを初期化する
self というのはクラスインスタンスを表す変数です。
- インスタンス変数として参照する
- クラス変数として参照する
- クラス継承の際も使える
-->
```python=
class Animal(object): #親クラス (メソッドがある)
def __init__(self, name): #クラスのメンバ変数を定義#
self.name = name
def greet(self): #メソッドとして greet() という関数をそれぞれのクラスで定義しておきます。
self.say("I'm " + self.name + ".")
def say(self, msg): pass
class Cat(Animal): #子クラス (親クラスのメソッドを実行可能)
def say(self, msg):
print "Meow, " + msg
class Dog(Animal):
def say(self, msg):
print "Wuff, " + msg
def classes():
c = Cat("Kitty") #子クラスをインスタンス化
c.greet() # Meow, I'm Kitty.
d = Dog("Buddy")
d.greet() # Wuff, I'm Buddy.
def entry_point(argv):
classes()
return 0
def target(*args): return entry_point
```
```python=
vietanh@KU-info2019-031:~/Rpython$ ./class-c
Meow, I'm Kitty.
Wuff, I'm Buddy.
```
<!--
多重継承継承の例:Rpython サポートしない
```python=
class Calculation1:
def Summation(self, a, b):
return a + b;
class Calculation2:
def Multiplication(self, a, b):
return a * b;
class Derived(Calculation1, Calculation2):
def Divide(self, a, b):
return a / b;
d = Derived()
print(d.Summation(10, 20))
print(d.Multiplication(10, 20))
print(d.Divide(10, 20))
C:\Users\r201902979ij\Downloads\4年生\test>python class.py
30
200
0.5
```
-->
Pythonでは複数のクラスを継承することができ、これを多重継承と呼びます。多重継承をする場合は、親クラスをカンマ区切りで指定します。
:::info
多重継承は mixin のみで対応。
クラス本体で rpython.rlib.objectmodel.import_from_mixin(M) を使って、クラス(M)の内容を丸ごとコピーします。
:::
```python=
from rpython.rlib.objectmodel import import_from_mixin
class ActivityMixin(object):
def eat(self, food):
print "Eating " + food
class Animal(object):
import_from_mixin(ActivityMixin) # import ActivityMixin (ActivityMixinのメソッドを作ることができます)
def __init__(self, name):
self.name = name
def greet(self):
self.say("I'm " + self.name + ".")
def say(self, msg): pass
class Cat(Animal): #子クラス
def say(self, msg):
print "Meow, " + msg
def classes():
c = Cat("Kitty")
c.greet() # Meow, I'm Kitty.
c.eat("cat food") #Eating cat food
def entry_point(argv):
classes()
return 0
def target(*args): return entry_point
```
---
## 5. Flow Control (フローコントロール)
### 5.1 Variables
変数は、各制御フローポイントで最大1種類の値を含む必要があります。
```python=
def compiler_error(arg): #argument:関数に渡される引数
v = 0 # v is an integer
if arg:
v = 1 # vに整数1を代入する
else:
v = "" # 文字列 "" をvに代入する
return v
def entry_point(argv): compiler_error(argv[1]); return 0
def target(*args): return entry_point
```
compiler_error関数の制御フローグラフを下図に示す:

見ての通り、マージポイント(緑でハイライト)では、v2は整数1か空文字列""のどちらかになります。これはRPythonの制限に違反しています。
### 5.2 例外事項
Pythonの例外処理を完全にサポート
## 6.Modules (rlib)
RPythonの標準ライブラリ(rlibとも呼ばれる)には、一般的に便利なモジュールがたくさんあります。
### 6.1 listsort
RPython でリストをソートするには、Timsort ソートアルゴリズムを提供する rpython.rlib.listsort モジュールを使用することができます。
```python=
from rpython.rlib.listsort import TimSort
def listsort():
lst = [10, 1, 9, 2, 8, 3, 7, 4, 5, 6]
TimSort(lst).sort()
print lst
def entry_point(argv):
listsort()
return 0
def target(*args): return entry_point
```
```python=
vietanh@KU-info2019-031:~/Rpython$ ./list-sort-c
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```
### 6.2 rarithmetic
rarithmetic は,整数演算を表現するためのクラスと演算を提供します.
### 6.3 rbigint
rbigint は,数学関数,型変換,比較など,大きな整数を扱う関数を含んでいます.
----
## 7. JIT
Simple Example:
The following example is a interpreter of brainfuck language with JIT. There is a detailed tutorial (part1, part2) on how to write a simple interpreter with RPython. Here, we briefly introduce some JIT-related data structures, functions, and decorators.
----
## 8. Foreign Function Interface (rffi) 外部関数インターフェース
RPythonの外部関数インターフェースはrffiと呼ばれています。rffiを使うと、低レベルの外部C関数の宣言、関数の外部登録、Cの型定義などができます。
----
### 8.1 Call Functions in libc (libc の関数の呼び出し)
次の例は、rffiを使ってlibcのmath関数を呼び出す方法です。
```python=
from rpython.rtyper.lltypesystem import rffi
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.rlib.rarithmetic import r_int32, r_int64, r_longlong
eci = ExternalCompilationInfo(
includes=['stdlib.h'],
)
# int abs(int j);
c_abs = rffi.llexternal('abs', # function name
[rffi.INT], # 引数型
rffi.INT, # return type リターンタイプ
compilation_info=eci)
def rffi_example():
int_number = r_int32(-10)
print c_abs(int_number)
def entry_point(argv): rffi_example(); return 0
def target(*args): return entry_point
```
```python=
vietanh@KU-info2019-031:~/Rpython$ ./rffi-c
10
```
----
### 8.2 Separate Module Source(セパレートモジュールソース)
また、C関数を直接書くこともできます。RPython translatorのCビルドツールが自動的にC関数をコンパイルしてリンクしてくれます。
```python=
from rpython.rtyper.lltypesystem import rffi
from rpython.rtyper.lltypesystem.lltype import Signed
from rpython.translator.tool.cbuild import ExternalCompilationInfo
c_source = """
int someexternalfunction(int x)
{
return (x + 3);
}
"""
eci = ExternalCompilationInfo(separate_module_sources=[c_source])
c_someexternalfunction = rffi.llexternal('someexternalfunction',
[Signed],
Signed,
compilation_info=eci)
def rffi_example():
print c_someexternalfunction(1)
def entry_point(argv): rffi_example(); return 0
def target(*args): return entry_point
```
```python=
vietanh@KU-info2019-031:~/Rpython$ ./rffi-2-c
4
```
----
### 8.3 String Operations (文字列操作)
ASCIIやunicodeエンコーディングなど、文字列変換を行うための関数がいくつか用意されています。
----
### 8.4 List Conversion(リスト変換)
文字列のリストはよく使われるので、"liststr2charpp"と"charpp2liststr"という2つの関数のペアが用意されています。"free_charpp" を使ってリストを解放します。
ここでは、文字列のリストの合計の長さを計算する例を示します。
```python=
* liststr2charpp: list[str] -> char**, NULL terminated
* free_charpp: frees list of char**, NULL terminated
* charpp2liststr: char** NULL terminated -> list[str]. No freeing is done.
```
```python=
from rpython.rtyper.lltypesystem import rffi
from rpython.translator.tool.cbuild import ExternalCompilationInfo
# Calculate the total length of strings in the list
c_source = """
#include <string.h>
int strlen_list(char *args[]) {
char **p = args; // CHARPP in RPython
int l = 0;
while (*p) {
l += strlen(*p);
p++;
}
return l;
}
"""
eci = ExternalCompilationInfo(separate_module_sources=[c_source])
c_strlen_list = rffi.llexternal('strlen_list', # function name
[rffi.CCHARPP], # parameters
rffi.SIGNED, # return value
compilation_info=eci)
def rffi_list_example():
l = ["Hello", ",", "World", "!"]
l_charpp = rffi.liststr2charpp(l) #char 型返し
r = c_strlen_list(l_charpp)
rffi.free_charpp(l_charpp) #リストを解放する
print r
def entry_point(argv): rffi_list_example(); return 0
def target(*args): return entry_point
```
----
## 9. Compilation Errors コンパイルエラー
### 9.1 コンパイルエラー
このエラーメッセージは、異なるタイプのものが混在しているために起こる非常に一般的なエラーメッセージです。
5章にあります・
```python=
def compiler_error(arg):
v = 0 # v is an integer
if arg:
v = 1 # assign integer 1 to v
else:
v = "" # assign string "" to v
return v
def entry_point(argv): compiler_error(argv[1]); return 0
def target(*args): return entry_point
```
----
### 9.2 AnnotatorError
"AnnotatorError" は、RPythonのプログラムにアノテーションを付ける際に発生するコンパイラエラーのクラスです。このクラスには、いくつかの具体的なエラーがあります。
- UnionError (annotator/module.py): アノテーターが型の競合で文句を言う。
- TooLateForChange (annotator/listdef.py): ドキュメント化されていません。
- ListChangeUnallowed (annotator/listdef.py): ドキュメントはありません。
- NoSuchAttrError (annotator/classdesc.py): クラスに属性が存在しない。
- SignatureError (annotator/signature.py): 関数宣言のシグネチャが異なる。
----
例えば、これはよくあるアノテーションのミスです:
```python=
def func(): return 0
def compiler_error(arg):
func(1)
def entry_point(argv): compiler_error(argv[1]); return 0
def target(*args): return entry_point
```
```python=
[translation:ERROR] AnnotatorError:
signature mismatch: func() takes no arguments (1 given)
Occurred processing the following simple_call:
function func <test.py, line 1> returning
v0 = simple_call((function func), (1))
In <FunctionGraph of (test:2)compiler_error at 0x7f1b16d3db90>:
Happened at file test.py line 3
==> func(1)
Known variable annotations:
Processing block:
block@6[arg_0] is a <class 'rpython.flowspace.flowcontext.SpamBlock'>
in (test:2)compiler_error
containing the following operations:
v0 = simple_call((function func), (1))
--end--
```
3行目で、 署名の不一致があります。具体的には、この関数は引数を取らず、3行目で与えられた1つの引数を取ります。
----
### 9.3 FlowingError
FlowingErrorは、RPythonコンパイラがコントロールフロー・グラフを構築する際のエラーに関するものです。以下はFlowingErrorが発生する一般的な原因です。
- unsupported operations
- invalid exception class
- cannot import modules
- local variable referenced before assignment
- global name is not defined
----
例:
```python=
def compiler_error(arg):
return v
def entry_point(argv): compiler_error(argv[1]); return 0
def target(*args): return entry_point
```
```python=
[translation:ERROR] FlowingError:
global name 'v' is not defined
In <FunctionGraph of (test:1)compiler_error at 0x7f322cd80b90>:
Happened at file test.py line 2
return v
Processing block:
block@9[argv_0] is a <class 'rpython.flowspace.flowcontext.SpamBlock'>
in (test:4)entry_point
containing the following operations:
v0 = getitem(argv_0, (1))
v1 = simple_call((function compiler_error), v0)
--end--
```
'v' が定義されていない.
----
## 10 Rewriting Python Benchmarks (Pythonベンチマークの書き換え)
いくつかの例(フィボナッチ数)を使って、RPythonでPythonコードを書き換えるためのヒントを説明します。
次のコードは、フィボナッチ数を再帰的に計算するものである。
```python=
def fib(n):
if n == 0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
def main():
fib(40)
if __name__ == '__main__':
main()
```
このベンチマークを実行するには、RPythonにエントリポイントを伝えるためのターゲット関数を追加する必要があります。
```python=
def fib(n):
if n == 0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
# nは定数であってはならない
def main(n):
ret = fib(n)
# output the result, otherwise RPython will optimize out the above call
# (the `transform_dead_op_vars` pass)
print ret
def target(*args): # we need a target function
return entry_point, None
def entry_point(argv):
main(int(argv[1])) # get n from the arguments
return 0
```
ここでは、GNU timeコマンドを用いて、プロセスのライフタイム中の実行時間と最大レジデントセットサイズを計測するだけである。
```python=
import time
start_time = time.time()
def fib(n):
if n == 0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
def main():
fib(40)
if __name__ == '__main__':
main()
print("--- %s seconds ---" % (time.time() - start_time))
C:\Users\r201902979ij\Downloads\4年生\test>python fib.py
--- 62.72197651863098 seconds ---
```