# Python interpreter > Author: 堇姬Naup ## 定義 先定義一下何謂python interpreter,就是用來解釋code object的 ![image](https://hackmd.io/_uploads/SyVmFu7jC.png) ## tiny interpreter 首先要建立一個解釋器,需要給他一個指令集 這邊先要求實現一件是,就是做加法 需要有以下指令 - LOAD_VALUE -> 載入數字至stack上、參數是要載入到stack上的數字 - ADD_TWO_VALUES -> 將stack最上層跟最上層-1 pop然後相加,將結果放到stack上、無參數 - PRINT_ANSWER -> 印出stack最上方層並pop掉、無參數 先將要加的兩個數字載入至stack上,相加後印出 ![image](https://hackmd.io/_uploads/H1uRoOQoA.png) 並且加入 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掉 ![image](https://hackmd.io/_uploads/SJkRif4iR.png) 由於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 中傳遞的返回值 ![image](https://hackmd.io/_uploads/H1Yh-X4i0.png) 編譯後的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/