# Python interpreter
> Author: 堇姬Naup
## 定義
先定義一下何謂python interpreter,就是用來解釋code object的

## tiny interpreter
首先要建立一個解釋器,需要給他一個指令集
這邊先要求實現一件是,就是做加法
需要有以下指令
- LOAD_VALUE -> 載入數字至stack上、參數是要載入到stack上的數字
- ADD_TWO_VALUES -> 將stack最上層跟最上層-1 pop然後相加,將結果放到stack上、無參數
- PRINT_ANSWER -> 印出stack最上方層並pop掉、無參數
先將要加的兩個數字載入至stack上,相加後印出

並且加入
run_code()
循環執行每條指令及參數
```python
what_to_execute = {
"instructions": [("LOAD_VALUE", 0), # the first number
("LOAD_VALUE", 1), # the second number
("ADD_TWO_VALUES", None),
("PRINT_ANSWER", None)],
"numbers": [7, 5] }
class Interpreter:
def __init__(self):
self.stack = []
def LOAD_VALUE(self, number):
self.stack.append(number)
def PRINT_ANSWER(self):
answer = self.stack.pop()
print(answer)
def ADD_TWO_VALUES(self):
first_num = self.stack.pop()
second_num = self.stack.pop()
total = first_num + second_num
self.stack.append(total)
def run_code(self, what_to_execute):
instructions = what_to_execute["instructions"]
numbers = what_to_execute["numbers"]
for each_step in instructions:
instruction, argument = each_step
if instruction == "LOAD_VALUE":
number = numbers[argument]
self.LOAD_VALUE(number)
elif instruction == "ADD_TWO_VALUES":
self.ADD_TWO_VALUES()
elif instruction == "PRINT_ANSWER":
self.PRINT_ANSWER()
interpreter = Interpreter()
interpreter.run_code(what_to_execute)
```
透過這樣來實現加法
## 變數
接下來引入一些簡單的mmap關係
LOAD_NAME -> 將變數值放到stack上
STORE_NAME -> 將變數mmap到stack最上層的值並pop
除了const表以外,還需要加入變數表
並新增environment來建立mmap關係
```python=
what_to_execute = {
"instructions": [("LOAD_VALUE", 0),
("STORE_NAME", 0),
("LOAD_VALUE", 1),
("STORE_NAME", 1),
("LOAD_NAME", 0),
("LOAD_NAME", 1),
("ADD_TWO_VALUES", None),
("PRINT_ANSWER", None)],
"numbers": [1, 2], # 數值列表,用於 LOAD_VALUE 指令
"names": ["a", "b"] # 變數名稱列表,用於 STORE_NAME 和 LOAD_NAME 指令
}
class Interpreter:
def __init__(self):
self.stack = [] # 用來儲存運算過程中的數值堆疊
self.environment = {} # 用來儲存變數名稱和值的映射環境
def LOAD_VALUE(self, number):
self.stack.append(number) # 將數值推入堆疊
def PRINT_ANSWER(self):
answer = self.stack.pop() # 從堆疊中取出結果
print(answer) # 輸出結果
def ADD_TWO_VALUES(self):
first_num = self.stack.pop() # 從堆疊中取出第一個數值
second_num = self.stack.pop() # 從堆疊中取出第二個數值
total = first_num + second_num # 將兩個數值相加
self.stack.append(total) # 將總和推回堆疊
def STORE_NAME(self, name):
val = self.stack.pop() # 從堆疊中取出數值
self.environment[name] = val # 將數值儲存到指定的變數名稱中
def LOAD_NAME(self, name):
val = self.environment[name] # 從環境中取出變數的值
self.stack.append(val) # 將該值推入堆疊
def parse_argument(self, instruction, argument, what_to_execute):
"""解析指令的參數,並根據指令類型返回對應的值或名稱。"""
numbers = ["LOAD_VALUE"] # 對應 LOAD_VALUE 的指令
names = ["LOAD_NAME", "STORE_NAME"] # 對應變數名稱的指令
if instruction in numbers:
argument = what_to_execute["numbers"][argument] # 根據索引返回對應的數值
elif instruction in names:
argument = what_to_execute["names"][argument] # 根據索引返回對應的變數名稱
return argument
def run_code(self, what_to_execute):
instructions = what_to_execute["instructions"] # 獲取執行指令的列表
for each_step in instructions:
instruction, argument = each_step
argument = self.parse_argument(instruction, argument, what_to_execute) # 解析指令參數
# 根據指令類型執行相應的操作
if instruction == "LOAD_VALUE":
self.LOAD_VALUE(argument)
elif instruction == "ADD_TWO_VALUES":
self.ADD_TWO_VALUES()
elif instruction == "PRINT_ANSWER":
self.PRINT_ANSWER()
elif instruction == "STORE_NAME":
self.STORE_NAME(argument)
elif instruction == "LOAD_NAME":
self.LOAD_NAME(argument)
# 建立解釋器並執行代碼
interpreter = Interpreter()
interpreter.run_code(what_to_execute)
```
## 優化run_code
這邊可以觀察到,使用if分支來建立指令集已經開始變得冗長,這邊我們可以使用getattr來優化動態查找
可以參考這篇
https://www.runoob.com/python/python-func-getattr.html
```python=
def run_code(self, what_to_execute):
instructions = what_to_execute["instructions"]
for each_step in instructions:
instruction, argument = each_step
argument = self.parse_argument(instruction, argument, what_to_execute)
# 用getattr優化
bytecode_method = getattr(self, instruction)
if argument is None:
bytecode_method()
else:
bytecode_method(argument)
```
## Real Python Bytecode
以上簡單的了解了指令集的建立及該如何跑他,現在回歸原本的python bytecode來看看
```python=
def f():
x = 3
if x < 5:
return 'yes'
else:
return 'no'
```
可以用__code__看一下
### __code__
```python
def f():
x = 3
if x < 5:
return 'yes'
else:
return 'no'
for attr in dir(f.__code__):
if attr.startswith('co_'):
print(f"{attr}:\t{getattr(f.__code__, attr)}")
```
觀察一下它裡面到底存了甚麼資訊
```
co_argcount: 0
co_cellvars: ()
co_code: b'd\x01}\x00|\x00d\x02k\x00r\x08d\x03S\x00d\x04S\x00'
co_consts: (None, 3, 5, 'yes', 'no')
co_filename: d:\Users\user\Downloads\dist (10)\tst.py
co_firstlineno: 1
co_flags: 67
co_freevars: ()
co_kwonlyargcount: 0
co_lines: <built-in method co_lines of code object at 0x000001CCAFC9FCB0>
co_linetable: b'\x04\x01\x08\x01\x04\x01\x04\x02'
co_lnotab: b'\x00\x01\x04\x01\x08\x01\x04\x02'
co_name: f
co_names: ()
co_nlocals: 1
co_posonlyargcount: 0
co_stacksize: 2
co_varnames: ('x',)
```
包含了許多函數相關bytecode以及執行資訊
.co_code下的就是他的bytecode
```
b'd\x01}\x00|\x00d\x02k\x00r\x08d\x03S\x00d\x04S\x00'
```
可以把他轉成list來看
```python
def f():
x = 3
if x < 5:
return 'yes'
else:
return 'no'
print(list(f.__code__.co_code))
```
```
[100, 1, 125, 0, 124, 0, 100, 2, 107, 0, 114, 8, 100, 3, 83, 0, 100, 4, 83, 0]
```
這邊搭配dis來看
```
3 0 LOAD_CONST 1 (3)
2 STORE_FAST 0 (x)
4 4 LOAD_FAST 0 (x)
6 LOAD_CONST 2 (5)
8 COMPARE_OP 0 (<)
10 POP_JUMP_IF_FALSE 8 (to 16)
5 12 LOAD_CONST 3 ('yes')
14 RETURN_VALUE
7 >> 16 LOAD_CONST 4 ('no')
18 RETURN_VALUE
None
```
一個bytecode對應一個指令
```python
for i in list(f.__code__.co_code):
print(i,"=",dis.opname[i])
```
對應關係
```
100 = LOAD_CONST
1
125 = STORE_FAST
0
124 = LOAD_FAST
0
100 = LOAD_CONST
2
107 = COMPARE_OP
0
114 = POP_JUMP_IF_FALSE
8
100 = LOAD_CONST
3
83 = RETURN_VALUE
0
100 = LOAD_CONST
4
83 = RETURN_VALUE
0
```
LOAD_CONST 就相當於上方的 LOAD_FAST
LOAD_VALUE 就相當於上方的 LOAD_NAME
## 比較和循環
通常一個程式當然不可能只有單純的一行行執行,一定有條件判斷跟迴圈,而python bytecode也有類似的goto可以做跳轉
```
if x < 5
```
被編譯成了
```
4 4 LOAD_FAST 0 (x)
6 LOAD_CONST 2 (5)
8 COMPARE_OP 0 (<)
10 POP_JUMP_IF_FALSE 8 (to 16)
```
如果是false就跳到16行的no
對的話往下執行
POP_JUMP_IF_FALSE -> 將stack頂部的值pop掉看是true or false
```python
def f():
x = 1
while x < 5:
x = x + 1
return x
```
迴圈也是一樣的概念
```
3 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (x)
4 4 LOAD_FAST 0 (x)
6 LOAD_CONST 2 (5)
8 COMPARE_OP 0 (<)
10 POP_JUMP_IF_FALSE 14 (to 28)
5 >> 12 LOAD_FAST 0 (x)
14 LOAD_CONST 1 (1)
16 BINARY_ADD
18 STORE_FAST 0 (x)
4 20 LOAD_FAST 0 (x)
22 LOAD_CONST 2 (5)
24 COMPARE_OP 0 (<)
26 POP_JUMP_IF_TRUE 6 (to 12)
6 >> 28 LOAD_FAST 0 (x)
30 RETURN_VALUE
```
看有沒有達成compare條件,並進行跳轉
## Frame
接下來要介紹的是Frame,觀察一下可以發現 RETURN_VALUE會return一個值,那他究竟return到哪裡呢?
可以想像,一個bytecode object有很多的Frame,return的value會被return到main frame,可以簡單想像他是作用域的概念
如果一個遞迴函式調用自己十次,會有十一個frame
```python
def bar(y):
z = y + 3
return z
def foo():
a = 1
b = 2
return a + bar(b)
foo()
```
現在共有三個frame,一但bar return就會把bar frame 從call stack pop掉

由於python在frame被pop掉幾乎都會清掉data stack,所以其實所有frame共用一個data stack也是可以的(非生成器的情況)
- 生成器的特殊性: 生成器的特性在於它可以暫停一個frame的執行,並在之後恢復到相同的狀態繼續執行,這要求每個frame必須有獨立的data stack
https://www.runoob.com/python3/python3-iterator-generator.html
## Byterun
上述就是基礎的python interpreter的知識,開始看byterun吧
byterun有四種對象
- VirtualMachine
- 負責整體結構管理(frame call stack、指令到操作的映射)
- 處理指令到操作的映射
- 較複雜的管理邏輯
- Frame
- 關聯 code object
- 管理全局、局部命名空間
- 跟踪call stack和執行狀態
- Function
- 調用函數時,會創建一個新的 Frame
- 控制新 Frame 的創建和管理
- Block
https://github.com/nedbat/byterun
## VirtualMachine
python interpreter就是一個VirtualMachine
VirtualMachine 負責保存call stack、異常狀態,以及在 frame 中傳遞的返回值

編譯後的code object為參數,並且創建一個frame,這個frame可能會在增加或刪減更多的frame,call stack也隨之伸縮,當第一個frame return時就結束(跟之前學的幾乎一樣)
```python
class VirtualMachine(object):
def __init__(self):
self.frames = [] # 儲存調用棧的列表。
self.frame = None # 當前正在執行的 frame。
self.return_value = None # 儲存當前 frame 的返回值。
self.last_exception = None # 儲存最後一個異常狀態。
def run_code(self, code, global_names=None, local_names=None):
""" An entry point to execute code using the virtual machine."""
frame = self.make_frame(code, global_names=global_names,
local_names=local_names)
self.run_frame(frame)
```
## frame
就是一個屬性的集合,包含了code object、global name、local name、內建命名空間、前一個frame、data stack、block stack
https://docs.python.org/zh-tw/3/library/builtins.html
https://www.cnblogs.com/goldsunshine/p/15085333.html
```python
class Frame(object):
def __init__(self, code_obj, global_names, local_names, prev_frame):
self.code_obj = code_obj # 儲存當前的程式碼物件
self.global_names = global_names # 儲存全域名稱空間
self.local_names = local_names # 儲存區域名稱空間
self.prev_frame = prev_frame # 儲存前一個執行框架(如果有的話)
self.stack = [] # 初始化操作堆疊為空
if prev_frame:
# 如果有前一個框架,繼承其內建函式名稱空間
self.builtin_names = prev_frame.builtin_names
else:
# 否則,使用當前區域名稱空間中的 '__builtins__' 來設置內建函式名稱空間
self.builtin_names = local_names['__builtins__']
if hasattr(self.builtin_names, '__dict__'):
# 如果內建函式名稱有 __dict__ 屬性,則使用其字典
self.builtin_names = self.builtin_names.__dict__
self.last_instruction = 0 # 記錄最後執行的指令索引,初始為 0
self.block_stack = [] # 初始化區塊堆疊為空,用於處理控制流(如 try/except 等)
```
剛剛的virtual machine並沒有實現對於frame的操作,這邊加入
```python
class VirtualMachine(object):
[... snip ...]
# 處理執行框架的相關方法
def make_frame(self, code, callargs={}, global_names=None, local_names=None):
# 建立新的執行框架
if global_names is not None and local_names is not None:
# 如果提供了全域名稱和區域名稱,將區域名稱設為全域名稱
local_names = global_names
elif self.frames:
# 如果已經有現存的框架,繼承目前框架的全域名稱空間,並初始化一個空的區域名稱空間
global_names = self.frame.global_names
local_names = {}
else:
# 如果沒有現存的框架,初始化全域和區域名稱空間
global_names = local_names = {
'__builtins__': __builtins__, # 預設的內建函式集合
'__name__': '__main__', # 預設的模組名稱
'__doc__': None, # 預設的文件字串
'__package__': None, # 預設的包名稱
}
local_names.update(callargs) # 將函式的引數更新至區域名稱空間
frame = Frame(code, global_names, local_names, self.frame) # 創建新的 Frame 物件
return frame # 返回新的執行框架
def push_frame(self, frame):
# 將新的執行框架推入堆疊並設為當前框架
self.frames.append(frame)
self.frame = frame
def pop_frame(self):
# 將當前框架從堆疊中彈出,並更新當前框架為堆疊頂端的框架
self.frames.pop()
if self.frames:
self.frame = self.frames[-1]
else:
self.frame = None
def run_frame(self):
# 執行當前框架的程式邏輯 (此部分尚未實作)
pass
```
## Function
重要的只有創建一個frame並執行他的__call__
```python
class Function(object):
"""
創建一個現實的函式物件,定義了解譯器所需的功能。
"""
__slots__ = [
'func_code', 'func_name', 'func_defaults', 'func_globals',
'func_locals', 'func_dict', 'func_closure',
'__name__', '__dict__', '__doc__',
'_vm', '_func',
]
def __init__(self, name, code, globs, defaults, closure, vm):
"""此部分細節對於理解解譯器來說並不是特別重要。"""
self._vm = vm # 儲存虛擬機器實例
self.func_code = code # 儲存函式的程式碼物件
self.func_name = self.__name__ = name or code.co_name # 儲存函式名稱
self.func_defaults = tuple(defaults) # 儲存函式的預設引數
self.func_globals = globs # 儲存全域名稱空間
self.func_locals = self._vm.frame.f_locals # 儲存當前區域名稱空間
self.__dict__ = {} # 初始化函式的屬性字典
self.func_closure = closure # 儲存閉包(如果有的話)
self.__doc__ = code.co_consts[0] if code.co_consts else None # 儲存函式的文檔字串
# 有時需要一個真正的 Python 函式,這裡是為了這個目的
kw = {
'argdefs': self.func_defaults, # 設置預設引數
}
if closure:
# 如果有閉包,創建對應的 cell
kw['closure'] = tuple(make_cell(0) for _ in closure)
self._func = types.FunctionType(code, globs, **kw) # 創建真正的 Python 函式物件
def __call__(self, *args, **kwargs):
"""當調用函式時,創建一個新的框架並執行它。"""
callargs = inspect.getcallargs(self._func, *args, **kwargs)
# 使用 callargs 來提供引數與其值的映射,以便傳遞到新的框架中
frame = self._vm.make_frame(
self.func_code, callargs, self.func_globals, {}
)
return self._vm.run_frame(frame) # 執行新的框架並返回結果
def make_cell(value):
"""創建一個真正的 Python 閉包並抓取一個 cell。"""
# 感謝 Alex Gaynor 這部分的幫助
fn = (lambda x: lambda: x)(value)
return fn.__closure__[0] # 返回閉包中的 cell
```
virtual machine也加入了管理stack更多方法
```python
class VirtualMachine(object):
[... snip ...]
# 資料堆疊操作
def top(self):
"""返回堆疊頂端的值。"""
return self.frame.stack[-1]
def pop(self):
"""彈出並返回堆疊頂端的值。"""
return self.frame.stack.pop()
def push(self, *vals):
"""將一個或多個值推入堆疊。"""
self.frame.stack.extend(vals)
def popn(self, n):
"""從堆疊中彈出 `n` 個值。
返回一個 `n` 個值的列表,最深的值在前。
"""
if n:
ret = self.frame.stack[-n:] # 取出最上面的 `n` 個值
self.frame.stack[-n:] = [] # 清空這些值
return ret
else:
return [] # 如果 `n` 為 0,返回空列表
```
並且要加入解析指令是否有參數跟更新最後一行執行
```python
class VirtualMachine(object):
[... snip ...]
def parse_byte_and_args(self):
f = self.frame
opoffset = f.last_instruction
byteCode = f.code_obj.co_code[opoffset]
f.last_instruction += 1
byte_name = dis.opname[byteCode]
if byteCode >= dis.HAVE_ARGUMENT:
# index into the bytecode
arg = f.code_obj.co_code[f.last_instruction:f.last_instruction+2]
f.last_instruction += 2 # advance the instruction pointer
arg_val = arg[0] + (arg[1] * 256)
if byteCode in dis.hasconst: # Look up a constant
arg = f.code_obj.co_consts[arg_val]
elif byteCode in dis.hasname: # Look up a name
arg = f.code_obj.co_names[arg_val]
elif byteCode in dis.haslocal: # Look up a local name
arg = f.code_obj.co_varnames[arg_val]
elif byteCode in dis.hasjrel: # Calculate a relative jump
arg = f.last_instruction + arg_val
else:
arg = arg_val
argument = [arg]
else:
argument = []
return byte_name, argument
```
## block(沒有很重要,看看就好了)
block在控制流程中扮演重要角色,尤其是在處理異常和迴圈時。它確保在操作完成後,數據堆疊(data stack)的狀態是正確
在迴圈中,一個特殊的迭代器會存在於堆疊中,當迴圈完成時,它會從堆疊中彈出。解釋器需要檢查迴圈是否仍在進行中,或者已經停止
為了跟蹤這些額外的信息,解釋器設置了一個標誌來指示它的狀態。我們用一個變量 why 來實現這個標誌,它可以是 None 或以下幾個字符串之一:"continue"、"break"、"exception"、"return"。這些標誌指示如何對區塊堆疊和數據堆疊進行操作。例如,在迴圈的例子中,如果區塊堆疊的堆疊頂部是 loop 區塊,並且 why 是 "continue",那麼迭代器應該保留在數據堆疊上;如果 why 是 "break",那麼迭代器會被彈出。
(原文解釋得很好了)
```python
import collections
# 定義區塊的數據結構
Block = collections.namedtuple("Block", "type, handler, stack_height")
class VirtualMachine(object):
[... snip ...]
# 推入區塊到區塊堆疊
def push_block(self, b_type, handler=None):
level = len(self.frame.stack) # 獲取當前堆疊的深度
self.frame.block_stack.append(Block(b_type, handler, level)) # 將區塊推入堆疊
# 彈出區塊
def pop_block(self):
return self.frame.block_stack.pop()
# 清理與特定區塊相關的數據堆疊值
def unwind_block(self, block):
""" 根據給定的區塊清理數據堆疊上的值。 """
if block.type == 'except-handler':
# 異常本身在堆疊中,包括類型、值和回溯
offset = 3
else:
offset = 0
while len(self.frame.stack) > block.stack_height + offset:
self.pop() # 彈出堆疊中的值
if block.type == 'except-handler':
traceback, value, exctype = self.popn(3) # 彈出異常相關的三個值
self.last_exception = exctype, value, traceback # 記錄最後的異常
# 根據給定的標誌處理區塊堆疊
def manage_block_stack(self, why):
""" 管理區塊堆疊。 """
frame = self.frame
block = frame.block_stack[-1] # 獲取區塊堆疊的最上層區塊
if block.type == 'loop' and why == 'continue':
self.jump(self.return_value) # 繼續執行循環
why = None
return why
self.pop_block() # 彈出區塊
self.unwind_block(block) # 清理數據堆疊中的值
if block.type == 'loop' and why == 'break':
why = None
self.jump(block.handler) # 跳轉到處理器
return why
if (block.type in ['setup-except', 'finally'] and why == 'exception'):
self.push_block('except-handler') # 推入異常處理區塊
exctype, value, tb = self.last_exception # 獲取最後的異常信息
self.push(tb, value, exctype) # 推入異常信息
self.push(tb, value, exctype) # 再次推入異常信息
why = None
self.jump(block.handler) # 跳轉到異常處理器
return why
elif block.type == 'finally':
if why in ('return', 'continue'):
self.push(self.return_value) # 推入返回值
self.push(why) # 推入標誌
why = None
self.jump(block.handler) # 跳轉到最終處理器
return why
return why
```
## 實現指令集
基本上跟上面都差不多
```python
class VirtualMachine(object):
[... snip ...]
## Stack manipulation
def byte_LOAD_CONST(self, const):
self.push(const)
def byte_POP_TOP(self):
self.pop()
## Names
def byte_LOAD_NAME(self, name):
frame = self.frame
if name in frame.f_locals:
val = frame.f_locals[name]
elif name in frame.f_globals:
val = frame.f_globals[name]
elif name in frame.f_builtins:
val = frame.f_builtins[name]
else:
raise NameError("name '%s' is not defined" % name)
self.push(val)
def byte_STORE_NAME(self, name):
self.frame.f_locals[name] = self.pop()
def byte_LOAD_FAST(self, name):
if name in self.frame.f_locals:
val = self.frame.f_locals[name]
else:
raise UnboundLocalError(
"local variable '%s' referenced before assignment" % name
)
self.push(val)
def byte_STORE_FAST(self, name):
self.frame.f_locals[name] = self.pop()
def byte_LOAD_GLOBAL(self, name):
f = self.frame
if name in f.f_globals:
val = f.f_globals[name]
elif name in f.f_builtins:
val = f.f_builtins[name]
else:
raise NameError("global name '%s' is not defined" % name)
self.push(val)
## Operators
BINARY_OPERATORS = {
'POWER': pow,
'MULTIPLY': operator.mul,
'FLOOR_DIVIDE': operator.floordiv,
'TRUE_DIVIDE': operator.truediv,
'MODULO': operator.mod,
'ADD': operator.add,
'SUBTRACT': operator.sub,
'SUBSCR': operator.getitem,
'LSHIFT': operator.lshift,
'RSHIFT': operator.rshift,
'AND': operator.and_,
'XOR': operator.xor,
'OR': operator.or_,
}
def binaryOperator(self, op):
x, y = self.popn(2)
self.push(self.BINARY_OPERATORS[op](x, y))
COMPARE_OPERATORS = [
operator.lt,
operator.le,
operator.eq,
operator.ne,
operator.gt,
operator.ge,
lambda x, y: x in y,
lambda x, y: x not in y,
lambda x, y: x is y,
lambda x, y: x is not y,
lambda x, y: issubclass(x, Exception) and issubclass(x, y),
]
def byte_COMPARE_OP(self, opnum):
x, y = self.popn(2)
self.push(self.COMPARE_OPERATORS[opnum](x, y))
## Attributes and indexing
def byte_LOAD_ATTR(self, attr):
obj = self.pop()
val = getattr(obj, attr)
self.push(val)
def byte_STORE_ATTR(self, name):
val, obj = self.popn(2)
setattr(obj, name, val)
## Building
def byte_BUILD_LIST(self, count):
elts = self.popn(count)
self.push(elts)
def byte_BUILD_MAP(self, size):
self.push({})
def byte_STORE_MAP(self):
the_map, val, key = self.popn(3)
the_map[key] = val
self.push(the_map)
def byte_LIST_APPEND(self, count):
val = self.pop()
the_list = self.frame.stack[-count] # peek
the_list.append(val)
## Jumps
def byte_JUMP_FORWARD(self, jump):
self.jump(jump)
def byte_JUMP_ABSOLUTE(self, jump):
self.jump(jump)
def byte_POP_JUMP_IF_TRUE(self, jump):
val = self.pop()
if val:
self.jump(jump)
def byte_POP_JUMP_IF_FALSE(self, jump):
val = self.pop()
if not val:
self.jump(jump)
## Blocks
def byte_SETUP_LOOP(self, dest):
self.push_block('loop', dest)
def byte_GET_ITER(self):
self.push(iter(self.pop()))
def byte_FOR_ITER(self, jump):
iterobj = self.top()
try:
v = next(iterobj)
self.push(v)
except StopIteration:
self.pop()
self.jump(jump)
def byte_BREAK_LOOP(self):
return 'break'
def byte_POP_BLOCK(self):
self.pop_block()
## Functions
def byte_MAKE_FUNCTION(self, argc):
name = self.pop()
code = self.pop()
defaults = self.popn(argc)
globs = self.frame.f_globals
fn = Function(name, code, globs, defaults, None, self)
self.push(fn)
def byte_CALL_FUNCTION(self, arg):
lenKw, lenPos = divmod(arg, 256) # KWargs not supported here
posargs = self.popn(lenPos)
func = self.pop()
frame = self.frame
retval = func(*posargs)
self.push(retval)
def byte_RETURN_VALUE(self):
self.return_value = self.pop()
return "return"
```
## 詳細實現參考
https://github.com/nedbat/byterun
## ref
https://docs.python.org/3/library/dis.html
https://qingyunha.github.io/taotao/