{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## CS 200: Python Virtual Machine (PVM)\n", "\n", "\n", "\n", "\n", "Video: A Bit about Bytes: Understanding Python Bytecode James Bennett, PyCon 2018, \n", "\n", "EBook: Inside The Python Virtual Machine Obi Ike-Nwosu \n", "\n", "Github: github.com/python/cpython source code for Python. ceval.c\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python source code is converted to python byte code, which is then executed by the python virtual machine. The .pyc files contain python byte code.\n", "\n", "Python source code goes through the following process:\n", "
    \n", "
  1. Lexical analysis. Break the code up into tokens. (See flex in UNIX, nee, lex)\n", "
  2. Parsing. Generate a syntactic parse tree. (Like diagramming sentences using parts of speech.) (See bison in UNIX, nee, yacc)\n", "
  3. Code generation. Python traverses the code tree and produces a byte code file - usually a .pyc file. (Resides in __pycache__ in Python 3. Take a look.)\n", "
  4. Execution. The Python Virtual Machine reads the byte code and executes the instructions.\n", "
\n", "\n", "\n", "Some of the modules we use to explore the byte code include:\n", "\n", "- dis the disassembler. It can convert python source code to byte code.\n", "- opcode which converts numeric byte codes to symbolic assembler code. For example, opcode 23 is the assembler instruction, BINARY_ADD\n", "- operator module provides function names for standard operators. For example, < is operator.lt(a,b)\n", "\n", "See the local files: bytecode.py and pvm.py" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import dis\n", "import operator\n", "import opcode" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define a simple function." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def s():\n", " a = 1\n", " b = 2\n", " return (a + b)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We check out the attributes of the function." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['__annotations__',\n", " '__call__',\n", " '__class__',\n", " '__closure__',\n", " '__code__',\n", " '__defaults__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__get__',\n", " '__getattribute__',\n", " '__globals__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__kwdefaults__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__name__',\n", " '__ne__',\n", " '__new__',\n", " '__qualname__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__']" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we drill down on the \\_\\_code\\_\\_ attribute." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " 'co_argcount',\n", " 'co_cellvars',\n", " 'co_code',\n", " 'co_consts',\n", " 'co_filename',\n", " 'co_firstlineno',\n", " 'co_flags',\n", " 'co_freevars',\n", " 'co_kwonlyargcount',\n", " 'co_lnotab',\n", " 'co_name',\n", " 'co_names',\n", " 'co_nlocals',\n", " 'co_posonlyargcount',\n", " 'co_stacksize',\n", " 'co_varnames',\n", " 'replace']" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(s.__code__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define a function, xf(), which will print out the value of the attributes of the \\_\\_code\\_\\_ property." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def xf(func, all=False):\n", " for x in (dir(func.__code__)):\n", " if all:\n", " print (\"{}:\\t{}\".format(x, func.__code__.__getattribute__(x)))\n", " elif x.startswith(\"co\"):\n", " print (\"{}:\\t{}\".format(x, func.__code__.__getattribute__(x)))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "co_argcount:\t0\n", "co_cellvars:\t()\n", "co_code:\tb'd\\x01}\\x00d\\x02}\\x01|\\x00|\\x01\\x17\\x00S\\x00'\n", "co_consts:\t(None, 1, 2)\n", "co_filename:\t/tmp/ipykernel_2260377/1904000924.py\n", "co_firstlineno:\t1\n", "co_flags:\t67\n", "co_freevars:\t()\n", "co_kwonlyargcount:\t0\n", "co_lnotab:\tb'\\x00\\x01\\x04\\x01\\x04\\x01'\n", "co_name:\ts\n", "co_names:\t()\n", "co_nlocals:\t2\n", "co_posonlyargcount:\t0\n", "co_stacksize:\t2\n", "co_varnames:\t('a', 'b')\n" ] } ], "source": [ "xf(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every time you define a function, Python creates these attributes. Some are obvious, and others are obscure. We will start by looking at the co\\_code attribute, which is the byte code instructions needed to execute the function.\n", "\n", "We define a simple function getbytes(f) to retrieve the bytecodes." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def getbytes(f):\n", " return f.__code__.co_code" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "b'd\\x01}\\x00d\\x02}\\x01|\\x00|\\x01\\x17\\x00S\\x00'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "getbytes(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See Hexadecimal.html for a discussion of hexadecimal byte strings." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We next define a function to print out the individual bytes." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def printbytes(f):\n", " for b in getbytes(f):\n", " print (b)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100\n", "1\n", "125\n", "0\n", "100\n", "2\n", "125\n", "1\n", "124\n", "0\n", "124\n", "1\n", "23\n", "0\n", "83\n", "0\n" ] } ], "source": [ "printbytes(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some of those bytes are opcodes - that is, assembly language instructions. The opcode module provides a mapping between bytes and opcodes." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['EXTENDED_ARG',\n", " 'HAVE_ARGUMENT',\n", " '__all__',\n", " '__builtins__',\n", " '__cached__',\n", " '__doc__',\n", " '__file__',\n", " '__loader__',\n", " '__name__',\n", " '__package__',\n", " '__spec__',\n", " 'cmp_op',\n", " 'hascompare',\n", " 'hasconst',\n", " 'hasfree',\n", " 'hasjabs',\n", " 'hasjrel',\n", " 'haslocal',\n", " 'hasname',\n", " 'hasnargs',\n", " 'opmap',\n", " 'opname',\n", " 'stack_effect']" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(opcode)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['<0>',\n", " 'POP_TOP',\n", " 'ROT_TWO',\n", " 'ROT_THREE',\n", " 'DUP_TOP',\n", " 'DUP_TOP_TWO',\n", " 'ROT_FOUR',\n", " '<7>',\n", " '<8>',\n", " 'NOP',\n", " 'UNARY_POSITIVE',\n", " 'UNARY_NEGATIVE',\n", " 'UNARY_NOT',\n", " '<13>',\n", " '<14>',\n", " 'UNARY_INVERT',\n", " 'BINARY_MATRIX_MULTIPLY',\n", " 'INPLACE_MATRIX_MULTIPLY',\n", " '<18>',\n", " 'BINARY_POWER',\n", " 'BINARY_MULTIPLY',\n", " '<21>',\n", " 'BINARY_MODULO',\n", " 'BINARY_ADD',\n", " 'BINARY_SUBTRACT',\n", " 'BINARY_SUBSCR',\n", " 'BINARY_FLOOR_DIVIDE',\n", " 'BINARY_TRUE_DIVIDE',\n", " 'INPLACE_FLOOR_DIVIDE',\n", " 'INPLACE_TRUE_DIVIDE',\n", " '<30>',\n", " '<31>',\n", " '<32>',\n", " '<33>',\n", " '<34>',\n", " '<35>',\n", " '<36>',\n", " '<37>',\n", " '<38>',\n", " '<39>',\n", " '<40>',\n", " '<41>',\n", " '<42>',\n", " '<43>',\n", " '<44>',\n", " '<45>',\n", " '<46>',\n", " '<47>',\n", " 'RERAISE',\n", " 'WITH_EXCEPT_START',\n", " 'GET_AITER',\n", " 'GET_ANEXT',\n", " 'BEFORE_ASYNC_WITH',\n", " '<53>',\n", " 'END_ASYNC_FOR',\n", " 'INPLACE_ADD',\n", " 'INPLACE_SUBTRACT',\n", " 'INPLACE_MULTIPLY',\n", " '<58>',\n", " 'INPLACE_MODULO',\n", " 'STORE_SUBSCR',\n", " 'DELETE_SUBSCR',\n", " 'BINARY_LSHIFT',\n", " 'BINARY_RSHIFT',\n", " 'BINARY_AND',\n", " 'BINARY_XOR',\n", " 'BINARY_OR',\n", " 'INPLACE_POWER',\n", " 'GET_ITER',\n", " 'GET_YIELD_FROM_ITER',\n", " 'PRINT_EXPR',\n", " 'LOAD_BUILD_CLASS',\n", " 'YIELD_FROM',\n", " 'GET_AWAITABLE',\n", " 'LOAD_ASSERTION_ERROR',\n", " 'INPLACE_LSHIFT',\n", " 'INPLACE_RSHIFT',\n", " 'INPLACE_AND',\n", " 'INPLACE_XOR',\n", " 'INPLACE_OR',\n", " '<80>',\n", " '<81>',\n", " 'LIST_TO_TUPLE',\n", " 'RETURN_VALUE',\n", " 'IMPORT_STAR',\n", " 'SETUP_ANNOTATIONS',\n", " 'YIELD_VALUE',\n", " 'POP_BLOCK',\n", " '<88>',\n", " 'POP_EXCEPT',\n", " 'STORE_NAME',\n", " 'DELETE_NAME',\n", " 'UNPACK_SEQUENCE',\n", " 'FOR_ITER',\n", " 'UNPACK_EX',\n", " 'STORE_ATTR',\n", " 'DELETE_ATTR',\n", " 'STORE_GLOBAL',\n", " 'DELETE_GLOBAL',\n", " '<99>',\n", " 'LOAD_CONST',\n", " 'LOAD_NAME',\n", " 'BUILD_TUPLE',\n", " 'BUILD_LIST',\n", " 'BUILD_SET',\n", " 'BUILD_MAP',\n", " 'LOAD_ATTR',\n", " 'COMPARE_OP',\n", " 'IMPORT_NAME',\n", " 'IMPORT_FROM',\n", " 'JUMP_FORWARD',\n", " 'JUMP_IF_FALSE_OR_POP',\n", " 'JUMP_IF_TRUE_OR_POP',\n", " 'JUMP_ABSOLUTE',\n", " 'POP_JUMP_IF_FALSE',\n", " 'POP_JUMP_IF_TRUE',\n", " 'LOAD_GLOBAL',\n", " 'IS_OP',\n", " 'CONTAINS_OP',\n", " '<119>',\n", " '<120>',\n", " 'JUMP_IF_NOT_EXC_MATCH',\n", " 'SETUP_FINALLY',\n", " '<123>',\n", " 'LOAD_FAST',\n", " 'STORE_FAST',\n", " 'DELETE_FAST',\n", " '<127>',\n", " '<128>',\n", " '<129>',\n", " 'RAISE_VARARGS',\n", " 'CALL_FUNCTION',\n", " 'MAKE_FUNCTION',\n", " 'BUILD_SLICE',\n", " '<134>',\n", " 'LOAD_CLOSURE',\n", " 'LOAD_DEREF',\n", " 'STORE_DEREF',\n", " 'DELETE_DEREF',\n", " '<139>',\n", " '<140>',\n", " 'CALL_FUNCTION_KW',\n", " 'CALL_FUNCTION_EX',\n", " 'SETUP_WITH',\n", " 'EXTENDED_ARG',\n", " 'LIST_APPEND',\n", " 'SET_ADD',\n", " 'MAP_ADD',\n", " 'LOAD_CLASSDEREF',\n", " '<149>',\n", " '<150>',\n", " '<151>',\n", " '<152>',\n", " '<153>',\n", " 'SETUP_ASYNC_WITH',\n", " 'FORMAT_VALUE',\n", " 'BUILD_CONST_KEY_MAP',\n", " 'BUILD_STRING',\n", " '<158>',\n", " '<159>',\n", " 'LOAD_METHOD',\n", " 'CALL_METHOD',\n", " 'LIST_EXTEND',\n", " 'SET_UPDATE',\n", " 'DICT_MERGE',\n", " 'DICT_UPDATE',\n", " '<166>',\n", " '<167>',\n", " '<168>',\n", " '<169>',\n", " '<170>',\n", " '<171>',\n", " '<172>',\n", " '<173>',\n", " '<174>',\n", " '<175>',\n", " '<176>',\n", " '<177>',\n", " '<178>',\n", " '<179>',\n", " '<180>',\n", " '<181>',\n", " '<182>',\n", " '<183>',\n", " '<184>',\n", " '<185>',\n", " '<186>',\n", " '<187>',\n", " '<188>',\n", " '<189>',\n", " '<190>',\n", " '<191>',\n", " '<192>',\n", " '<193>',\n", " '<194>',\n", " '<195>',\n", " '<196>',\n", " '<197>',\n", " '<198>',\n", " '<199>',\n", " '<200>',\n", " '<201>',\n", " '<202>',\n", " '<203>',\n", " '<204>',\n", " '<205>',\n", " '<206>',\n", " '<207>',\n", " '<208>',\n", " '<209>',\n", " '<210>',\n", " '<211>',\n", " '<212>',\n", " '<213>',\n", " '<214>',\n", " '<215>',\n", " '<216>',\n", " '<217>',\n", " '<218>',\n", " '<219>',\n", " '<220>',\n", " '<221>',\n", " '<222>',\n", " '<223>',\n", " '<224>',\n", " '<225>',\n", " '<226>',\n", " '<227>',\n", " '<228>',\n", " '<229>',\n", " '<230>',\n", " '<231>',\n", " '<232>',\n", " '<233>',\n", " '<234>',\n", " '<235>',\n", " '<236>',\n", " '<237>',\n", " '<238>',\n", " '<239>',\n", " '<240>',\n", " '<241>',\n", " '<242>',\n", " '<243>',\n", " '<244>',\n", " '<245>',\n", " '<246>',\n", " '<247>',\n", " '<248>',\n", " '<249>',\n", " '<250>',\n", " '<251>',\n", " '<252>',\n", " '<253>',\n", " '<254>',\n", " '<255>']" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opcode.opname" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "256" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(opcode.opname)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'BINARY_ADD'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opcode.opname[23]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The opcode.opname array maps numbers to opcodes. We define a function printopcodes(f) to map these codes for a given function." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def printopcodes(f):\n", " for b in getbytes(f):\n", " print (\"{}: {}\".format(b, opcode.opname[b]))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100: LOAD_CONST\n", "1: POP_TOP\n", "125: STORE_FAST\n", "0: <0>\n", "100: LOAD_CONST\n", "2: ROT_TWO\n", "125: STORE_FAST\n", "1: POP_TOP\n", "124: LOAD_FAST\n", "0: <0>\n", "124: LOAD_FAST\n", "1: POP_TOP\n", "23: BINARY_ADD\n", "0: <0>\n", "83: RETURN_VALUE\n", "0: <0>\n" ] } ], "source": [ "printopcodes(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dis module has a dis() function which performs this task, as well as the code\\_info() function which provides additional information. " ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'BINARY_ADD'" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opcode.opname[23]" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 2 0 LOAD_CONST 1 (1)\n", " 2 STORE_FAST 0 (a)\n", "\n", " 3 4 LOAD_CONST 2 (2)\n", " 6 STORE_FAST 1 (b)\n", "\n", " 4 8 LOAD_FAST 0 (a)\n", " 10 LOAD_FAST 1 (b)\n", " 12 BINARY_ADD\n", " 14 RETURN_VALUE\n" ] } ], "source": [ "dis.dis(s)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Name: s\n", "Filename: /tmp/ipykernel_886261/1904000924.py\n", "Argument count: 0\n", "Positional-only arguments: 0\n", "Kw-only arguments: 0\n", "Number of locals: 2\n", "Stack size: 2\n", "Flags: OPTIMIZED, NEWLOCALS, NOFREE\n", "Constants:\n", " 0: None\n", " 1: 1\n", " 2: 2\n", "Variable names:\n", " 0: a\n", " 1: b\n" ] } ], "source": [ "print( dis.code_info(s))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We combine these in the showme(f) function." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "def showme(f):\n", " dis.dis(f)\n", " print(dis.code_info(f)) " ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 2 0 LOAD_CONST 1 (1)\n", " 2 STORE_FAST 0 (a)\n", "\n", " 3 4 LOAD_CONST 2 (2)\n", " 6 STORE_FAST 1 (b)\n", "\n", " 4 8 LOAD_FAST 0 (a)\n", " 10 LOAD_FAST 1 (b)\n", " 12 BINARY_ADD\n", " 14 RETURN_VALUE\n", "Name: s\n", "Filename: /tmp/ipykernel_886261/1904000924.py\n", "Argument count: 0\n", "Positional-only arguments: 0\n", "Kw-only arguments: 0\n", "Number of locals: 2\n", "Stack size: 2\n", "Flags: OPTIMIZED, NEWLOCALS, NOFREE\n", "Constants:\n", " 0: None\n", " 1: 1\n", " 2: 2\n", "Variable names:\n", " 0: a\n", " 1: b\n" ] } ], "source": [ "showme(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PVM-lite\n", "\n", "See A Python Interpreter Written in Python\n", "\n", "Note that this article was written using an earlier version of Python, 3.4 or so. In that version, bytecodes used three bytes: one for the opcode and two for the arguments.\n", "After 3.4, Python generated bytecode that used only two bytes per opcode. That is what we use in this assignment. You should make the adjustment in the reading." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "what_to_execute = {\n", " \"instructions\": [(\"LOAD_VALUE\", 0),\n", " (\"LOAD_VALUE\", 1),\n", " (\"ADD_TWO_VALUES\", None),\n", " (\"PRINT_ANSWER\", None)],\n", " \"numbers\": [7, 5] }" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "def s():\n", " a = 1\n", " b = 2\n", " print (a + b)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "swhat_to_execute = {\n", " \"instructions\": [(\"LOAD_VALUE\", 0),\n", " (\"STORE_NAME\", 0),\n", " (\"LOAD_VALUE\", 1),\n", " (\"STORE_NAME\", 1),\n", " (\"LOAD_NAME\", 0),\n", " (\"LOAD_NAME\", 1),\n", " (\"ADD_TWO_VALUES\", None),\n", " (\"PRINT_ANSWER\", None)],\n", " \"numbers\": [1, 2],\n", " \"names\": [\"a\", \"b\"] }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The Interpreter\n", "\n", "We implement two interpreters in one. They both use a stack to execute the instructions.\n", "\n", "- run_code() - which is a simpler, more limited interpreter, with the instructions hard coded.\n", "- execute() - which is more general, allowing the instructions to be other methods in the class.\n", "\n", "The latter relies on the getattr(object, name) function, which returns the value of the named attribute of the object." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "class Interpreter0:\n", " def __init__(self):\n", " self.stack = []\n", " self.environment = {}\n", "\n", " def STORE_NAME(self, name):\n", " val = self.stack.pop()\n", " self.environment[name] = val\n", "\n", " def LOAD_NAME(self, name):\n", " val = self.environment[name]\n", " self.stack.append(val)\n", "\n", " def LOAD_VALUE(self, number):\n", " self.stack.append(number)\n", "\n", " def PRINT_ANSWER(self):\n", " answer = self.stack.pop()\n", " print (answer)\n", "\n", " def ADD_TWO_VALUES(self):\n", " first_num = self.stack.pop()\n", " second_num = self.stack.pop()\n", " total = first_num + second_num\n", " self.stack.append(total)\n", "\n", " def run_code(self, what_to_execute):\n", " instructions = what_to_execute[\"instructions\"]\n", " for each_step in instructions:\n", " instruction, argument = each_step\n", " argument = self.parse_argument(instruction, argument, what_to_execute)\n", " if instruction == \"LOAD_VALUE\":\n", " self.LOAD_VALUE(argument)\n", " elif instruction == \"ADD_TWO_VALUES\":\n", " self.ADD_TWO_VALUES()\n", " elif instruction == \"PRINT_ANSWER\":\n", " self.PRINT_ANSWER()\n", " elif instruction == \"STORE_NAME\":\n", " self.STORE_NAME(argument)\n", " elif instruction == \"LOAD_NAME\":\n", " self.LOAD_NAME(argument)\n", "\n", " def parse_argument(self, instruction, argument, what_to_execute):\n", " numbers = [\"LOAD_VALUE\"]\n", " names = [\"LOAD_NAME\", \"STORE_NAME\"]\n", "\n", " if instruction in numbers:\n", " argument = what_to_execute[\"numbers\"][argument]\n", " elif instruction in names:\n", " argument = what_to_execute[\"names\"][argument]\n", " return argument\n", "\n", " ## stage 2 version of PVM\n", " ## -------------------------------------------------------------\n", " def execute(self, what_to_execute):\n", " instructions = what_to_execute[\"instructions\"]\n", " for each_step in instructions:\n", " print (each_step)\n", " instruction, argument = each_step\n", " argument = self.parse_argument(instruction, argument, what_to_execute)\n", " bytecode_method = getattr(self, instruction)\n", " if argument is None:\n", " bytecode_method()\n", " else:\n", " bytecode_method(argument)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "def test():\n", " interpreter = Interpreter0()\n", " interpreter.run_code(what_to_execute)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'instructions': [('LOAD_VALUE', 0),\n", " ('LOAD_VALUE', 1),\n", " ('ADD_TWO_VALUES', None),\n", " ('PRINT_ANSWER', None)],\n", " 'numbers': [7, 5]}" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "what_to_execute" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "12\n" ] } ], "source": [ "test()" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'instructions': [('LOAD_VALUE', 0),\n", " ('STORE_NAME', 0),\n", " ('LOAD_VALUE', 1),\n", " ('STORE_NAME', 1),\n", " ('LOAD_NAME', 0),\n", " ('LOAD_NAME', 1),\n", " ('ADD_TWO_VALUES', None),\n", " ('PRINT_ANSWER', None)],\n", " 'numbers': [1, 2],\n", " 'names': ['a', 'b']}" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "swhat_to_execute" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "def test2():\n", " interpreter = Interpreter0()\n", " interpreter.run_code(swhat_to_execute)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] } ], "source": [ "test2()" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "def test3():\n", " interpreter = Interpreter0()\n", " interpreter.execute(swhat_to_execute)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('LOAD_VALUE', 0)\n", "('STORE_NAME', 0)\n", "('LOAD_VALUE', 1)\n", "('STORE_NAME', 1)\n", "('LOAD_NAME', 0)\n", "('LOAD_NAME', 1)\n", "('ADD_TWO_VALUES', None)\n", "('PRINT_ANSWER', None)\n", "3\n" ] } ], "source": [ "test3()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### hw5: Interpreter: PVM-lite\n", "\n", "Following the description in the reading, you will implement \n", "PVM-lite: A Python Virtual Machine for a subset of Python. \n", "\n", "Included in the subset are:\n", "\n", "- constants\n", "- local variables\n", "- arithmetic operators\n", "- comparison operators\n", "- if/then/else\n", "- lists, tuples, sets, dictionaries (maps)\n", "- slices\n", "- unary operators\n", "- inplace operators\n", "\n", "\n", "Not included are:\n", "\n", "- for, while loops\n", "- function calls\n", "- function arguments\n", "- classes\n", "- list comprehensions \n", "- generators\n", "- global variables\n", "- lambda expressions\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dis module can disassemble a Python function, printing out \n", "the byte codes for the function. For example, given:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "def s1():\n", " a = 1\n", " return a" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 2 0 LOAD_CONST 1 (1)\n", " 2 STORE_FAST 0 (a)\n", "\n", " 3 4 LOAD_FAST 0 (a)\n", " 6 RETURN_VALUE\n" ] } ], "source": [ "import dis\n", "dis.dis(s1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Write your own version of dis.dis() that produces a dictionary object \n", "containing the following values:\n", " \n", "- names: a list of the function's variable names (the .co_varnames attribute)\n", "- consts: a list of the function's constants (the .co_consts attribute)\n", "- code: a list of the function's bytecode (the .co_code attribute)\n", "- instructions: a list of the sequence of instructions for the function. \n", "\n", "Each instruction list has the following five components:\n", "\n", "- adjusted line number: index position in the bytecode list\n", "- instruction opcode number: opcode from the bytecode list\n", "- opname: symbolic name for the opcode\n", "- index: if the opcode takes an argument, the numeric index value, else None\n", "- argument: if the opcode takes an argument, the value of the argument, else None" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'names': ('a',),\n", " 'consts': (None, 1),\n", " 'code': b'd\\x01}\\x00|\\x00S\\x00',\n", " 'instructions': [(0, 100, 'LOAD_CONST', 1, 1),\n", " (2, 125, 'STORE_FAST', 0, 'a'),\n", " (4, 124, 'LOAD_FAST', 0, 'a'),\n", " (6, 83, 'RETURN_VALUE', None, None)]}" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import hw5a\n", "hw5a.makeobj(s1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that makeobj is similar to dis.dis(). You may use dis.dis() \n", "to help test your implementation of makeobj().\n", "\n", "You may also want to use dis.HAVE_ARGUMENT to identify those\n", "opcodes that do and do not take an argument and dis.hascompare to process comparison operators. dis.get_instructions(f) is very useful." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=1, argrepr='1', offset=0, starts_line=2, is_jump_target=False),\n", " Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),\n", " Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='a', argrepr='a', offset=4, starts_line=3, is_jump_target=False),\n", " Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=6, starts_line=None, is_jump_target=False)]" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(dis.get_instructions(s1))" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=1, argrepr='1', offset=0, starts_line=2, is_jump_target=False)\n", "Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False)\n", "Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='a', argrepr='a', offset=4, starts_line=3, is_jump_target=False)\n", "Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=6, starts_line=None, is_jump_target=False)\n" ] } ], "source": [ "for x in dis.get_instructions(s1):\n", " print (x)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "x = list(dis.get_instructions(s1))" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'LOAD_CONST'" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[0][0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The Interpreter Class\n", "\n", "We shall now implement an Interpreter class, along the lines\n", "of the reading, which describes a full implementation of the PVM\n", "and has the source code available on github. We encourage you to\n", "avail yourself of that resource. Our implementation is more modest.\n", "Nevertheless, you should try to borrow as much as possible from\n", "\"byterun\" implementation. Yes, I am telling you to copy / adapt\n", "code from the byterun github file.\n", "\n", "We first define a test function that will execute a Python function\n", "using the Interpreter class." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "def doit(func, debug=True):\n", " interpreter = Interpreter(debug)\n", " return interpreter.execute(func)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 3 - binary arithmetic\n", "\n", "Next we define a bunch of test functions for the Interpreter" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "def s3():\n", " a = 1\n", " return a + 1" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 1, 1) \n", "(8, 23, 'BINARY_ADD', None, None) \n", "(10, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "def s3a():\n", " a = 1\n", " b = 2\n", " return (a + b)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, 2) \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 23, 'BINARY_ADD', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "3" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3a)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "def s3b():\n", " a = 1\n", " b = 2\n", " return (a - b)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, 2) \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 24, 'BINARY_SUBTRACT', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3b)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "def s3c():\n", " a = 2\n", " b = 3\n", " return (a ** b)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 2) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, 3) \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 19, 'BINARY_POWER', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "8" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3c)" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [], "source": [ "def s3d():\n", " a = 2\n", " b = 3\n", " return (a * b)" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 2) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, 3) \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 20, 'BINARY_MULTIPLY', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "6" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3d)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [], "source": [ "def s3e():\n", " a = 6\n", " b = 3\n", " return (a / b)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 6) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, 3) \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 27, 'BINARY_TRUE_DIVIDE', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "2.0" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3e)" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [], "source": [ "def s3f():\n", " a = 7\n", " b = 3\n", " return (a // b)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 7) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, 3) \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 26, 'BINARY_FLOOR_DIVIDE', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3f)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [], "source": [ "def s3g():\n", " a = 7\n", " b = 3\n", " return (a % b)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 7) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, 3) \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 22, 'BINARY_MODULO', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3g)" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [], "source": [ "def s3h():\n", " ''' returns string append '''\n", " a = \"hello\"\n", " b = \" world\"\n", " return a + b" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 'hello') \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 100, 'LOAD_CONST', 2, ' world') \n", "(6, 125, 'STORE_FAST', 1, 'b') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 124, 'LOAD_FAST', 1, 'b') \n", "(12, 23, 'BINARY_ADD', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "'hello world'" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s3h)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 4 - comparison operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "def s4():\n", " return 1 > 2" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 100, 'LOAD_CONST', 2, 2) \n", "(4, 107, 'COMPARE_OP', 4, 4) >\n", "(6, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s4)" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [], "source": [ "def s4a():\n", " return 1 < 2" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 100, 'LOAD_CONST', 2, 2) \n", "(4, 107, 'COMPARE_OP', 0, 0) <\n", "(6, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s4a)" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [], "source": [ "def s4b():\n", " return 1 <= 2" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 100, 'LOAD_CONST', 2, 2) \n", "(4, 107, 'COMPARE_OP', 1, 1) <=\n", "(6, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s4b)" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [], "source": [ "def s4c():\n", " return 1 == 2" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 100, 'LOAD_CONST', 2, 2) \n", "(4, 107, 'COMPARE_OP', 2, 2) ==\n", "(6, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s4c)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [], "source": [ "def s4d():\n", " return 1 != 2" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 100, 'LOAD_CONST', 2, 2) \n", "(4, 107, 'COMPARE_OP', 3, 3) !=\n", "(6, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s4d)" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [], "source": [ "def s4e():\n", " return 1 >= 2" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 100, 'LOAD_CONST', 2, 2) \n", "(4, 107, 'COMPARE_OP', 5, 5) >=\n", "(6, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s4e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 5 - jump operators" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [], "source": [ "def s5():\n", " a = 1\n", " if a > 2:\n", " return 1\n", " else:\n", " return 2" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 2) \n", "(8, 107, 'COMPARE_OP', 4, 4) >\n", "(10, 114, 'POP_JUMP_IF_FALSE', 16, 16) \n", "(16, 100, 'LOAD_CONST', 2, 2) \n", "(18, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s5)" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [], "source": [ "def s5a():\n", " a = 1\n", " if a == 2:\n", " return 1\n", " else:\n", " return 2" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 2) \n", "(8, 107, 'COMPARE_OP', 2, 2) ==\n", "(10, 114, 'POP_JUMP_IF_FALSE', 16, 16) \n", "(16, 100, 'LOAD_CONST', 2, 2) \n", "(18, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s5a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 6 - lists, tuples, sets, subscript" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [], "source": [ "def s6():\n", " ''' returns tuple '''\n", " a = 2\n", " b = a << a\n", " return b, b % 2" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 2) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 124, 'LOAD_FAST', 0, 'a') \n", "(8, 62, 'BINARY_LSHIFT', None, None) \n", "(10, 125, 'STORE_FAST', 1, 'b') \n", "(12, 124, 'LOAD_FAST', 1, 'b') \n", "(14, 124, 'LOAD_FAST', 1, 'b') \n", "(16, 100, 'LOAD_CONST', 1, 2) \n", "(18, 22, 'BINARY_MODULO', None, None) \n", "(20, 102, 'BUILD_TUPLE', 2, 2) \n", "(22, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "(8, 0)" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s6)" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [], "source": [ "def s6a():\n", " ''' returns list '''\n", " b = 5\n", " return [b, b % 2]\n" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 5) \n", "(2, 125, 'STORE_FAST', 0, 'b') \n", "(4, 124, 'LOAD_FAST', 0, 'b') \n", "(6, 124, 'LOAD_FAST', 0, 'b') \n", "(8, 100, 'LOAD_CONST', 2, 2) \n", "(10, 22, 'BINARY_MODULO', None, None) \n", "(12, 103, 'BUILD_LIST', 2, 2) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "[5, 1]" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s6a)" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [], "source": [ "def s6b():\n", " ''' returns set '''\n", " b = 9\n", " return {b, b % 2}" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 9) \n", "(2, 125, 'STORE_FAST', 0, 'b') \n", "(4, 124, 'LOAD_FAST', 0, 'b') \n", "(6, 124, 'LOAD_FAST', 0, 'b') \n", "(8, 100, 'LOAD_CONST', 2, 2) \n", "(10, 22, 'BINARY_MODULO', None, None) \n", "(12, 104, 'BUILD_SET', 2, 2) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "{1, 9}" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s6b)" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [], "source": [ "def s6c():\n", " ''' returns list subscript '''\n", " a = [1,2,3]\n", " return a[1]" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 103, 'BUILD_LIST', 0, 0) \n", "(2, 100, 'LOAD_CONST', 1, (1, 2, 3)) \n", "(4, 162, 'LIST_EXTEND', 1, 1) \n", "(6, 125, 'STORE_FAST', 0, 'a') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 100, 'LOAD_CONST', 2, 1) \n", "(12, 25, 'BINARY_SUBSCR', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s6c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 7 - slices" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [], "source": [ "def s7():\n", " ''' returns list slice '''\n", " a = [1,2,3,4,5,6]\n", " return a[2:-1], a[2:4]" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 103, 'BUILD_LIST', 0, 0) \n", "(2, 100, 'LOAD_CONST', 1, (1, 2, 3, 4, 5, 6)) \n", "(4, 162, 'LIST_EXTEND', 1, 1) \n", "(6, 125, 'STORE_FAST', 0, 'a') \n", "(8, 124, 'LOAD_FAST', 0, 'a') \n", "(10, 100, 'LOAD_CONST', 2, 2) \n", "(12, 100, 'LOAD_CONST', 3, -1) \n", "(14, 133, 'BUILD_SLICE', 2, 2) \n", "(16, 25, 'BINARY_SUBSCR', None, None) \n", "(18, 124, 'LOAD_FAST', 0, 'a') \n", "(20, 100, 'LOAD_CONST', 2, 2) \n", "(22, 100, 'LOAD_CONST', 4, 4) \n", "(24, 133, 'BUILD_SLICE', 2, 2) \n", "(26, 25, 'BINARY_SUBSCR', None, None) \n", "(28, 102, 'BUILD_TUPLE', 2, 2) \n", "(30, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "((3, 4, 5), (3, 4))" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Error: should return lists, not tuples." ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [], "source": [ "def s7a():\n", " ''' returns string slice '''\n", " a = \"hello world\"\n", " return a[3:-1]" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 'hello world') \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 100, 'LOAD_CONST', 3, -1) \n", "(10, 133, 'BUILD_SLICE', 2, 2) \n", "(12, 25, 'BINARY_SUBSCR', None, None) \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "'lo worl'" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s7a)" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [], "source": [ "def s7b():\n", " ''' returns dictionary '''\n", " a = 2\n", " b = a ** 2\n", " d = {}\n", " d[a] = b\n", " return d" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 2) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 1, 2) \n", "(8, 19, 'BINARY_POWER', None, None) \n", "(10, 125, 'STORE_FAST', 1, 'b') \n", "(12, 105, 'BUILD_MAP', 0, 0) \n", "(14, 125, 'STORE_FAST', 2, 'd') \n", "(16, 124, 'LOAD_FAST', 1, 'b') \n", "(18, 124, 'LOAD_FAST', 2, 'd') \n", "(20, 124, 'LOAD_FAST', 0, 'a') \n", "(22, 60, 'STORE_SUBSCR', None, None) \n", "(24, 124, 'LOAD_FAST', 2, 'd') \n", "(26, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "{2: 4}" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s7b)" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [], "source": [ "def s7c():\n", " ''' dict access '''\n", " x = {}\n", " x['a'] = 1\n", " y = x['a']\n", " return y" ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 105, 'BUILD_MAP', 0, 0) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 100, 'LOAD_CONST', 1, 1) \n", "(6, 124, 'LOAD_FAST', 0, 'x') \n", "(8, 100, 'LOAD_CONST', 2, 'a') \n", "(10, 60, 'STORE_SUBSCR', None, None) \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 100, 'LOAD_CONST', 2, 'a') \n", "(16, 25, 'BINARY_SUBSCR', None, None) \n", "(18, 125, 'STORE_FAST', 1, 'y') \n", "(20, 124, 'LOAD_FAST', 1, 'y') \n", "(22, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s7c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 8 - unary and inplace operators" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [], "source": [ "def s8b():\n", " x = 1\n", " return -x" ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 11, 'UNARY_NEGATIVE', None, None) \n", "(8, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8b)" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [], "source": [ "def s8c():\n", " x = -1\n", " return +x" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, -1) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 10, 'UNARY_POSITIVE', None, None) \n", "(8, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8c)" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [], "source": [ "def s8d():\n", " x = 1\n", " return not x" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 12, 'UNARY_NOT', None, None) \n", "(8, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 125, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8d)" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [], "source": [ "def s8e():\n", " x = 1\n", " return ~x" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 15, 'UNARY_INVERT', None, None) \n", "(8, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "-2" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8e)" ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [], "source": [ "def s8f():\n", " x = 1\n", " x += 10\n", " return x" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 100, 'LOAD_CONST', 2, 10) \n", "(8, 55, 'INPLACE_ADD', None, None) \n", "(10, 125, 'STORE_FAST', 0, 'x') \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "11" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8f)" ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [], "source": [ "def s8g():\n", " x = 1\n", " x -= 10\n", " return x" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 100, 'LOAD_CONST', 2, 10) \n", "(8, 56, 'INPLACE_SUBTRACT', None, None) \n", "(10, 125, 'STORE_FAST', 0, 'x') \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "-9" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8g)" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [], "source": [ "def s8h():\n", " x = 1\n", " x *= 10\n", " return x" ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 1) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 100, 'LOAD_CONST', 2, 10) \n", "(8, 57, 'INPLACE_MULTIPLY', None, None) \n", "(10, 125, 'STORE_FAST', 0, 'x') \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "10" ] }, "execution_count": 133, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8h)" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [], "source": [ "def s8i():\n", " x = 10\n", " x %= 3\n", " return x" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 10) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 59, 'INPLACE_MODULO', None, None) \n", "(10, 125, 'STORE_FAST', 0, 'x') \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8i)" ] }, { "cell_type": "code", "execution_count": 136, "metadata": {}, "outputs": [], "source": [ "def s8j():\n", " x = 10\n", " x /= 3\n", " return x" ] }, { "cell_type": "code", "execution_count": 137, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 10) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 29, 'INPLACE_TRUE_DIVIDE', None, None) \n", "(10, 125, 'STORE_FAST', 0, 'x') \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "3.3333333333333335" ] }, "execution_count": 137, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8j)" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [], "source": [ "def s8k():\n", " x = 32\n", " x <<= 2\n", " return x" ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 32) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 100, 'LOAD_CONST', 2, 2) \n", "(8, 75, 'INPLACE_LSHIFT', None, None) \n", "(10, 125, 'STORE_FAST', 0, 'x') \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "128" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8k)" ] }, { "cell_type": "code", "execution_count": 140, "metadata": {}, "outputs": [], "source": [ "def s8l():\n", " x = 32\n", " x >>= 2\n", " return x" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 32) \n", "(2, 125, 'STORE_FAST', 0, 'x') \n", "(4, 124, 'LOAD_FAST', 0, 'x') \n", "(6, 100, 'LOAD_CONST', 2, 2) \n", "(8, 76, 'INPLACE_RSHIFT', None, None) \n", "(10, 125, 'STORE_FAST', 0, 'x') \n", "(12, 124, 'LOAD_FAST', 0, 'x') \n", "(14, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "8" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s8l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 9 - logical binary operators" ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [], "source": [ "def s9():\n", " a = 2\n", " return a << 3" ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 2) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 62, 'BINARY_LSHIFT', None, None) \n", "(10, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "16" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s9)" ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [], "source": [ "def s9a():\n", " a = 19\n", " return a >> 3" ] }, { "cell_type": "code", "execution_count": 145, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 19) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 63, 'BINARY_RSHIFT', None, None) \n", "(10, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 145, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s9a)" ] }, { "cell_type": "code", "execution_count": 146, "metadata": {}, "outputs": [], "source": [ "def s9b():\n", " a = 19\n", " return a & 3" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 19) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 64, 'BINARY_AND', None, None) \n", "(10, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "3" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s9b)" ] }, { "cell_type": "code", "execution_count": 148, "metadata": {}, "outputs": [], "source": [ "def s9c():\n", " a = 19\n", " return a | 3" ] }, { "cell_type": "code", "execution_count": 149, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 19) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 66, 'BINARY_OR', None, None) \n", "(10, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "19" ] }, "execution_count": 149, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s9c)" ] }, { "cell_type": "code", "execution_count": 150, "metadata": {}, "outputs": [], "source": [ "def s9d():\n", " a = 19\n", " return a ^ 3" ] }, { "cell_type": "code", "execution_count": 151, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 100, 'LOAD_CONST', 1, 19) \n", "(2, 125, 'STORE_FAST', 0, 'a') \n", "(4, 124, 'LOAD_FAST', 0, 'a') \n", "(6, 100, 'LOAD_CONST', 2, 3) \n", "(8, 65, 'BINARY_XOR', None, None) \n", "(10, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "16" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s9d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extra binary operators" ] }, { "cell_type": "code", "execution_count": 152, "metadata": {}, "outputs": [], "source": [ "# binary in\n", "def s10():\n", " a = [1,2,3,4,5,6]\n", " b = 4\n", " if b in a:\n", " return \"yes\"\n", " else:\n", " return \"no\"" ] }, { "cell_type": "code", "execution_count": 153, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 103, 'BUILD_LIST', 0, 0) \n", "(2, 100, 'LOAD_CONST', 1, (1, 2, 3, 4, 5, 6)) \n", "(4, 162, 'LIST_EXTEND', 1, 1) \n", "(6, 125, 'STORE_FAST', 0, 'a') \n", "(8, 100, 'LOAD_CONST', 2, 4) \n", "(10, 125, 'STORE_FAST', 1, 'b') \n", "(12, 124, 'LOAD_FAST', 1, 'b') \n", "(14, 124, 'LOAD_FAST', 0, 'a') \n", "(16, 118, 'CONTAINS_OP', 0, 0) \n", "(18, 114, 'POP_JUMP_IF_FALSE', 24, 24) \n", "(20, 100, 'LOAD_CONST', 3, 'yes') \n", "(22, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "'yes'" ] }, "execution_count": 153, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s10)" ] }, { "cell_type": "code", "execution_count": 154, "metadata": {}, "outputs": [], "source": [ "def s10a():\n", " a = [1,2,3,4,5,6]\n", " b = 4\n", " if b not in a:\n", " return \"yes\"\n", " else:\n", " return \"no\"" ] }, { "cell_type": "code", "execution_count": 155, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 103, 'BUILD_LIST', 0, 0) \n", "(2, 100, 'LOAD_CONST', 1, (1, 2, 3, 4, 5, 6)) \n", "(4, 162, 'LIST_EXTEND', 1, 1) \n", "(6, 125, 'STORE_FAST', 0, 'a') \n", "(8, 100, 'LOAD_CONST', 2, 4) \n", "(10, 125, 'STORE_FAST', 1, 'b') \n", "(12, 124, 'LOAD_FAST', 1, 'b') \n", "(14, 124, 'LOAD_FAST', 0, 'a') \n", "(16, 118, 'CONTAINS_OP', 1, 1) \n", "(18, 114, 'POP_JUMP_IF_FALSE', 24, 24) \n", "(24, 100, 'LOAD_CONST', 4, 'no') \n", "(26, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "'no'" ] }, "execution_count": 155, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s10a)" ] }, { "cell_type": "code", "execution_count": 156, "metadata": {}, "outputs": [], "source": [ "# binary is\n", "def s10b():\n", " a = [1,2,3,4,5,6]\n", " b = 4\n", " if b is a:\n", " return \"yes\"\n", " else:\n", " return \"no\"" ] }, { "cell_type": "code", "execution_count": 157, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 103, 'BUILD_LIST', 0, 0) \n", "(2, 100, 'LOAD_CONST', 1, (1, 2, 3, 4, 5, 6)) \n", "(4, 162, 'LIST_EXTEND', 1, 1) \n", "(6, 125, 'STORE_FAST', 0, 'a') \n", "(8, 100, 'LOAD_CONST', 2, 4) \n", "(10, 125, 'STORE_FAST', 1, 'b') \n", "(12, 124, 'LOAD_FAST', 1, 'b') \n", "(14, 124, 'LOAD_FAST', 0, 'a') \n", "(16, 117, 'IS_OP', 0, 0) \n", "(18, 114, 'POP_JUMP_IF_FALSE', 24, 24) \n", "(24, 100, 'LOAD_CONST', 4, 'no') \n", "(26, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "'no'" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s10b)" ] }, { "cell_type": "code", "execution_count": 158, "metadata": {}, "outputs": [], "source": [ "def s10c():\n", " a = [1,2,3,4,5,6]\n", " b = 4\n", " if b is not a:\n", " return \"yes\"\n", " else:\n", " return \"no\"" ] }, { "cell_type": "code", "execution_count": 159, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(0, 103, 'BUILD_LIST', 0, 0) \n", "(2, 100, 'LOAD_CONST', 1, (1, 2, 3, 4, 5, 6)) \n", "(4, 162, 'LIST_EXTEND', 1, 1) \n", "(6, 125, 'STORE_FAST', 0, 'a') \n", "(8, 100, 'LOAD_CONST', 2, 4) \n", "(10, 125, 'STORE_FAST', 1, 'b') \n", "(12, 124, 'LOAD_FAST', 1, 'b') \n", "(14, 124, 'LOAD_FAST', 0, 'a') \n", "(16, 117, 'IS_OP', 1, 1) \n", "(18, 114, 'POP_JUMP_IF_FALSE', 24, 24) \n", "(20, 100, 'LOAD_CONST', 3, 'yes') \n", "(22, 83, 'RETURN_VALUE', None, None) \n" ] }, { "data": { "text/plain": [ "'yes'" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hw5a.doit(s10c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define the Interpreter Class\n", "\n", "Now we start to define the Interpreter itself.\n", "\n", "The PVM uses a stack to control execution and pass arguments to functions\n", "and operators.\n", "- The result instance variable will contain the value returned by the function.\n", "- The debug flag is used to turn on debugging output.\n", "- The environment instance variable is a dictionary \n", "containing variable bindings.\n", "- The pc instance variable is the program counter." ] }, { "cell_type": "code", "execution_count": 160, "metadata": {}, "outputs": [], "source": [ "class Interpreter:\n", " def __init__(self, debug = True):\n", " self.stack = []\n", " self.result = None\n", " self.debug = debug\n", " self.environment = {}\n", " self.pc = 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The execute method controls the action.\n", "It first decodes the function using makeobj() which we defined above.\n", "It sets the program counter (pc) to 0. If the pc becomes\n", "greater than the number of instructions, the program halts.\n", "\n", "execute cycles through the instructions, printing them out if debug is True.\n", "Each PVM opcode/opname is defined as a method of the Interpreter class.\n", "This means that each opname is an attribute of the class and \n", "can be accessed using getattr(self, instruction). The \n", "opcode method is called with or without an argument, as appropriate.\n", "\n", "When the while() loop finishes, execute returns the result." ] }, { "cell_type": "code", "execution_count": 161, "metadata": {}, "outputs": [], "source": [ "\n", " def execute(self, func):\n", " codedict = makeobj(func)\n", " self.instructions = codedict[\"instructions\"]\n", " self.pc = 0\n", " while (self.pc < len(self.instructions)):\n", " each_step = self.instructions[self.pc]\n", " ## if debug, print out symbol for comparison operator\n", " if self.debug: \n", " suffix = ''\n", " if each_step[2] == 'COMPARE_OP':\n", " suffix = self.COMPARE_OPERATORS_SYMBOLS[each_step[3]]\n", " print (each_step, suffix)\n", "\n", " self.pc += 1\n", " lineno, inst, instruction, index, argument = each_step\n", " bytecode_method = getattr(self, instruction)\n", " if argument is None:\n", " bytecode_method()\n", " else:\n", " bytecode_method(argument)\n", " return self.result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that execute calls makeobj() defined above.\n", "If your makeobj function is not working correctly, you can still\n", "debug your execute function by importing the bytecode version of \n", "makeobj from hw5a.pyc:\n", "
\n",
    "from hw5a import makeobj\n",
    "
\n", "We define a few stack operations that are used in implementing \n", "the opcode methods." ] }, { "cell_type": "code", "execution_count": 162, "metadata": {}, "outputs": [], "source": [ "\n", " def top(self):\n", " return self.stack[-1]\n", "\n", " def pop(self):\n", " return self.stack.pop()\n", "\n", " def push(self, *vals):\n", " self.stack.extend(vals)\n", "\n", " def popn(self, n):\n", " \"\"\"Pop a number of values from the value stack.\n", " A list of `n` values is returned, the deepest value first.\n", " \"\"\"\n", " if n:\n", " ret = self.stack[-n:]\n", " self.stack[-n:] = []\n", " return ret\n", " else:\n", " return []\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first implement the opcodes needed to execute function s1().\n", "Write the following methods:" ] }, { "cell_type": "code", "execution_count": 163, "metadata": {}, "outputs": [], "source": [ "\n", " def STORE_FAST(self, name):\n", " val = self.stack.pop()\n", " self.environment[name] = val\n", "\n", " def LOAD_FAST(self, name):\n", " pass\n", "\n", " def LOAD_CONST(self, number):\n", " pass\n", "\n", " ## set result from stack\n", " ## set pc to be out of range and thereby halt.\n", " def RETURN_VALUE(self):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** problem 3 ** (10 points)\n", "\n", "Define the following set of binary operators needed to \n", "execute the s3 set of functions." ] }, { "cell_type": "code", "execution_count": 164, "metadata": {}, "outputs": [], "source": [ "\n", " def BINARY_ADD(self):\n", " pass\n", "\n", " def BINARY_SUBTRACT(self):\n", " pass\n", "\n", " def BINARY_POWER(self):\n", " pass\n", "\n", " def BINARY_MULTIPLY(self):\n", " pass\n", "\n", " def BINARY_DIVIDE(self):\n", " pass\n", "\n", " ## same as divide for Python 3, ich glaube.\n", " def BINARY_TRUE_DIVIDE(self):\n", " pass\n", "\n", " def BINARY_FLOOR_DIVIDE(self):\n", " pass\n", "\n", " def BINARY_MODULO(self):\n", " pass\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** problem 4 ** (10 points)\n", "\n", "Define the set of comparison operators needed to \n", "execute the s4 set of functions." ] }, { "cell_type": "code", "execution_count": 165, "metadata": {}, "outputs": [], "source": [ "\n", " ## comparison operators\n", " ## -----------------------------------\n", " COMPARE_OPERATORS_SYMBOLS = [\n", " '<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'subclass']\n", "\n", "\n", " COMPARE_OPERATORS = [\n", " operator.lt,\n", " operator.le,\n", " operator.eq,\n", " operator.ne,\n", " operator.gt,\n", " operator.ge,\n", " lambda x, y: x in y,\n", " lambda x, y: x not in y,\n", " lambda x, y: x is y,\n", " lambda x, y: x is not y,\n", " lambda x, y: issubclass(x, Exception) and issubclass(x, y),\n", " ]\n", "\n", " def COMPARE_OP(self, opnum):\n", " pass\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** problem 5 ** (10 points)\n", "\n", "Define the set of jump operators needed to \n", "execute the s5 set of functions. Some are given." ] }, { "cell_type": "code", "execution_count": 166, "metadata": {}, "outputs": [], "source": [ "\n", " ## jumps\n", " ## -----------------------------------\n", " ## This method is called by the other jump methods.\n", " ## It causes the given address to be the next \n", " ## instruction to execute\n", " def jump(self, address):\n", " pass\n", "\n", " def JUMP_FORWARD(self, jump):\n", " self.jump(jump)\n", "\n", " def JUMP_ABSOLUTE(self, jump):\n", " self.jump(jump)\n", "\n", " def JUMP_IF_TRUE(self, jump):\n", " pass\n", "\n", " def JUMP_IF_FALSE(self, jump):\n", " pass\n", "\n", " def POP_JUMP_IF_FALSE(self, jump):\n", " pass\n", "\n", " def JUMP_IF_TRUE_OR_POP(self, jump):\n", " pass\n", "\n", " def JUMP_IF_FALSE_OR_POP(self, jump):\n", " pass\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** problem 6 ** (10 points)\n", "\n", "Define the set of operators needed to build and index\n", "lists, tuples, and sets, and execute the s6 set of functions." ] }, { "cell_type": "code", "execution_count": 167, "metadata": {}, "outputs": [], "source": [ "\n", " def BUILD_TUPLE(self, count):\n", " pass\n", "\n", " def BUILD_LIST(self, count):\n", " pass\n", "\n", " def BUILD_SET(self, count):\n", " pass\n", "\n", " def BINARY_SUBSCR(self):\n", " pass\n", "\n", " ### new bytecode in 3.9\n", " def LIST_EXTEND(self,count):\n", " elts = self.popn(2)\n", " self.push(elts[count])\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** problem 7 ** (10 points)\n", "\n", "Define the set of operators needed to implement the slice function\n", "and define the set of operators needed to implement dictionaries\n", "and execute the s7 set of functions. " ] }, { "cell_type": "code", "execution_count": 168, "metadata": {}, "outputs": [], "source": [ " def BUILD_SLICE(self, count):\n", " pass\n", "\n", " def BUILD_MAP(self, size):\n", " pass\n", "\n", " def STORE_MAP(self):\n", " pass\n", "\n", " def STORE_SUBSCR(self):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** problem 8 ** (10 points)\n", "\n", "Define the set of operators to implement unary and inplace functions\n", "and execute the s8 set of functions." ] }, { "cell_type": "code", "execution_count": 169, "metadata": {}, "outputs": [], "source": [ "\n", " def UNARY_POSITIVE(self):\n", " pass\n", "\n", " def UNARY_NEGATIVE(self):\n", " pass\n", "\n", " def UNARY_NOT(self):\n", " pass\n", "\n", " def UNARY_INVERT(self):\n", " pass\n", "\n", " def INPLACE_ADD(self):\n", " pass\n", "\n", " def INPLACE_SUBTRACT(self):\n", " pass\n", "\n", " def INPLACE_MULTIPLY(self):\n", " pass\n", "\n", " def INPLACE_TRUE_DIVIDE(self):\n", " pass\n", "\n", " def INPLACE_MODULO(self):\n", " pass\n", "\n", " def INPLACE_LSHIFT(self):\n", " pass\n", "\n", " def INPLACE_RSHIFT(self):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** problem 9 ** (10 points)\n", "\n", "Define the set of operators needed to implement the logical binary\n", "operators and execute the s9 set of functions." ] }, { "cell_type": "code", "execution_count": 172, "metadata": {}, "outputs": [], "source": [ "\n", "\n", " ## binary operators\n", " ## -----------------------------------\n", "\n", " def BINARY_LSHIFT(self):\n", " pass\n", "\n", " def BINARY_RSHIFT(self):\n", " pass\n", "\n", " def BINARY_AND(self):\n", " pass\n", "\n", " def BINARY_XOR(self):\n", " pass\n", "\n", " def BINARY_OR(self):\n", " pass\n", " \n", " ### problem 10\n", "### new bytecode in 3.9\n", " def CONTAINS_OP(self,flag):\n", " if flag:\n", " self.COMPARE_OP(7)\n", " else:\n", " self.COMPARE_OP(6)\n", " \n", "### new bytecode in 3.9\n", " def IS_OP(self,flag):\n", " if flag:\n", " self.COMPARE_OP(9)\n", " else:\n", " self.COMPARE_OP(8)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" } }, "nbformat": 4, "nbformat_minor": 4 }