diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9d4873c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*.egg-info/
+.hypothesis/
+.mypy_cache/
+.pytest_cache/
+.vscode/
+__pycache__/
+build/
+dist/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..e22d5ed
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,70 @@
+# https://github.com/brandtbucher/travis-python-matrix
+
+# CPython 3.7.4 / CPython 3.6.8
+# Ubuntu 16.04 (Xenial Xerus) / Windows 10 (64-bit) / macOS 10.14 (Mojave)
+
+matrix:
+
+ include:
+
+ - name: CPython 3.7.4 on Ubuntu 16.04 (Xenial Xerus)
+ language: python
+ os: linux
+ dist: xenial
+ python: 3.7.4
+
+ - name: CPython 3.6.8 on Ubuntu 16.04 (Xenial Xerus)
+ language: python
+ os: linux
+ dist: xenial
+ python: 3.6.8
+
+ - name: CPython 3.7.4 on Windows 10 (64-bit)
+ language: shell
+ os: windows
+ before_install:
+ - export PATH=/c/Python37:/c/Python37/Scripts:$PATH
+ - choco install python --version 3.7.4
+
+ - name: CPython 3.6.8 on Windows 10 (64-bit)
+ language: shell
+ os: windows
+ before_install:
+ - export PATH=/c/Python36:/c/Python36/Scripts:$PATH
+ - choco install python --version 3.6.8
+
+ - name: CPython 3.7.4 on macOS 10.14 (Mojave)
+ language: shell
+ os: osx
+ osx_image: xcode10.2
+ before_install:
+ - export PATH=/Users/travis/.pyenv/shims:$PATH PYENV_VERSION=3.7.4
+ - travis_wait brew upgrade pyenv && pyenv install $PYENV_VERSION
+
+ - name: CPython 3.6.8 on macOS 10.14 (Mojave)
+ language: shell
+ os: osx
+ osx_image: xcode10.2
+ before_install:
+ - export PATH=/Users/travis/.pyenv/shims:$PATH PYENV_VERSION=3.6.8
+ - CFLAGS="-I$(xcrun --show-sdk-path)/usr/include" pyenv install $PYENV_VERSION
+
+ - name: mypy
+ language: python
+ os: linux
+ dist: xenial
+ python: 3.8
+ script: mypy
+
+ - name: Black
+ language: python
+ os: linux
+ dist: xenial
+ python: 3.8
+ script: black --check .
+
+# Python and pip exectuables are just named "python" and "pip" on all platforms!
+
+install: pip install -r requirements.txt
+
+script: pytest
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..2154299
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,30 @@
+
+
+The MIT License
+===============
+
+### Copyright © 2019 Gary Brandt Bucher, II
+
+
+
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..78c8664
--- /dev/null
+++ b/README.md
@@ -0,0 +1,99 @@
+
+
+HAX
+===
+
+[](https://github.com/brandtbucher/hax/releases)[](https://travis-ci.com/brandtbucher/hax/branches)[](https://github.com/brandtbucher/hax/issues)
+
+
+
+
+
+
+
+HAX lets you write compiled bytecode inline with standard Python syntax.
+
+Installation
+------------
+
+HAX supports CPython 3.6–3.9.
+
+To install, just run:
+
+```sh
+$ pip install hax
+```
+
+Example
+-------
+
+Consider the following function; it accepts a sequence of items, and returns a
+list with each item repeated twice:
+
+```py
+from typing import List, Sequence, TypeVar
+
+T = TypeVar("T")
+
+def double(items: Sequence[T]) -> List[T]:
+ out = []
+ for item in items:
+ out += item, item
+ return out
+```
+
+For example, `(0, 1, 2)` becomes `[0, 0, 1, 1, 2, 2]`.
+
+We can make this function faster by keeping `out` on the stack (instead of in a
+local variable) and using the `LIST_APPEND` op to build it. HAX makes it
+simple to inline these instructions:
+
+```py
+from hax import *
+
+@hax
+def double(items: Sequence[T]) -> List[T]:
+
+ BUILD_LIST(0)
+
+ for item in items:
+
+ LOAD_FAST("item")
+ DUP_TOP()
+ LIST_APPEND(3)
+ LIST_APPEND(2)
+
+ RETURN_VALUE()
+```
+
+If you're up to the challenge of computing jump targets, the function can be
+further sped up by rewriting the for-loop in bytecode, removing _all_ temporary
+variables, and operating **entirely on the stack**:
+
+```py
+@hax
+def double(items: Sequence[T]) -> List[T]:
+
+ BUILD_LIST(0)
+
+ LOAD_FAST("items")
+ GET_ITER()
+ FOR_ITER(34) # When done, jump forward to RETURN_VALUE().
+
+ DUP_TOP()
+ LIST_APPEND(3)
+ LIST_APPEND(2)
+ JUMP_ABSOLUTE(28) # Jump back to FOR_ITER(34).
+
+ RETURN_VALUE()
+```
+
+It's important to realize that the functions HAX provides (`BUILD_LIST`,
+`LOAD_FAST`, ...) aren't just "emulating" their respective bytecode
+instructions; the `@hax` decorator detects them, and completely recompiles
+`double`'s code to use the _actual_ ops that we've specified here!
+
+These performance improvements are impossible to get from CPython's compiler and
+optimizer alone.
+
+
diff --git a/hax.py b/hax.py
new file mode 100644
index 0000000..89955e6
--- /dev/null
+++ b/hax.py
@@ -0,0 +1,771 @@
+import dis
+import os
+import sys
+import types
+import typing
+
+
+if sys.version_info < (3, 6):
+ raise RuntimeError("HAX only supports Python 3.6+!")
+
+
+if sys.implementation.name != "cpython":
+ raise RuntimeError("HAX only supports CPython!")
+
+
+__version__ = "0.0.0"
+
+
+_F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
+
+
+_USAGE_MESSAGE = "HAX inline bytecode functions are not meant to be used directly; you must decorate any functions that use them with @hax."
+
+
+_CALL_FUNCTION = dis.opmap["CALL_FUNCTION"]
+_EXTENDED_ARG = dis.opmap["EXTENDED_ARG"]
+_LOAD_CONST = dis.opmap["LOAD_CONST"]
+_NOP = dis.opmap["NOP"]
+_POP_TOP = dis.opmap["POP_TOP"]
+
+
+class HaxCompileError(SyntaxError):
+ pass
+
+
+class HaxUsageError(RuntimeError):
+ pass
+
+
+def _raise_hax_error(
+ message: str, filename: str, line: int, op: dis.Instruction
+) -> None:
+
+ if os.path.isfile(filename):
+ with open(filename) as file:
+ for line_number, text in enumerate(file, 1):
+ if line_number != line:
+ continue
+ source: typing.Optional[str] = text
+ if op.starts_line:
+ column: typing.Optional[int] = text.find(str(op.argval))
+ if column == -1:
+ column = None
+ break
+ else:
+ source = column = None
+
+ raise HaxCompileError(message, (filename, line, column, source))
+
+
+def _instructions_with_lines(
+ code: types.CodeType
+) -> typing.Iterator[typing.Tuple[dis.Instruction, int]]:
+ line = code.co_firstlineno
+ for instruction in dis.get_instructions(code):
+ line = instruction.starts_line or line
+ yield instruction, line
+
+
+def hax(function: _F) -> _F:
+
+ ops = _instructions_with_lines(function.__code__)
+
+ code = bytearray(function.__code__.co_code)
+ names = list(function.__code__.co_names)
+ stacksize = function.__code__.co_stacksize
+ varnames = list(function.__code__.co_varnames)
+
+ start = 0
+
+ for _ in range(len(code) // 2 + 1):
+
+ for op, line in ops:
+
+ if op.opcode != _EXTENDED_ARG:
+ break
+
+ else:
+
+ break
+
+ if op.argval not in dis.opmap:
+ start = op.offset + 2
+ continue
+
+ if op.opname not in {
+ "LOAD_CLASSDEREF",
+ "LOAD_CLOSURE",
+ "LOAD_DEREF",
+ "LOAD_FAST",
+ "LOAD_GLOBAL",
+ "LOAD_NAME",
+ }:
+ message = "Ops must consist of a simple call."
+ _raise_hax_error(message, function.__code__.co_filename, line, op)
+
+ new_op = dis.opmap[op.argval]
+
+ has_arg = dis.HAVE_ARGUMENT <= new_op
+
+ args = 0
+
+ for following, _ in ops:
+
+ if following.opcode == _EXTENDED_ARG:
+ continue
+
+ if following.opcode == _LOAD_CONST:
+ arg = following.argval
+ args += 1
+ continue
+
+ break
+
+ else:
+ message = "Ops must consist of a simple call."
+ _raise_hax_error(message, function.__code__.co_filename, line, op)
+
+ if following.opcode != _CALL_FUNCTION:
+ message = "Ops must consist of a simple call."
+ _raise_hax_error(message, function.__code__.co_filename, line, op)
+
+ if args != has_arg:
+ message = (
+ f"Number of arguments is wrong (expected {int(has_arg)}, got {args})."
+ )
+ _raise_hax_error(message, function.__code__.co_filename, line, op)
+
+ following, _ = next(ops)
+
+ if following.opcode != _POP_TOP:
+ message = "Ops must be standalone statements."
+ _raise_hax_error(message, function.__code__.co_filename, line, op)
+
+ line = following.starts_line or line
+
+ if new_op in dis.hasname:
+ try:
+ arg = names.index(arg)
+ except ValueError:
+ names.append(arg)
+ arg = len(names) - 1
+ elif new_op in dis.hasconst:
+ arg = function.__code__.co_consts.index(arg)
+ elif new_op in dis.hascompare:
+ try:
+ arg = dis.cmp_op.index(arg)
+ except ValueError:
+ message = f"Bad comparision operator {arg!r}; expected one of {' / '.join(map(repr, dis.cmp_op))}!"
+ _raise_hax_error(
+ message, function.__code__.co_filename, line, following
+ )
+ elif new_op in dis.haslocal:
+ try:
+ arg = varnames.index(arg)
+ except ValueError:
+ varnames.append(arg)
+ arg = len(varnames) - 1
+ elif new_op in dis.hasfree:
+ try:
+ arg = (
+ function.__code__.co_cellvars + function.__code__.co_freevars
+ ).index(arg)
+ except ValueError:
+ message = f'No free/cell variable {arg!r}; maybe use "nonlocal" in the inner scope to compile correctly?'
+ _raise_hax_error(
+ message, function.__code__.co_filename, line, following
+ )
+ elif not isinstance(arg, int):
+ message = f"Expected integer argument, got {arg!r}."
+ _raise_hax_error(message, function.__code__.co_filename, line, following)
+
+ if arg > (1 << 32) - 1:
+ message = (
+ f"Args greater than {(1 << 32) - 1:,} aren't supported (got {arg:,})!"
+ )
+ _raise_hax_error(message, function.__code__.co_filename, line, following)
+
+ if arg < 0:
+ message = f"Args less than 0 aren't supported (got {arg:,})!"
+ _raise_hax_error(message, function.__code__.co_filename, line, following)
+
+ for offset in range(start, following.offset, 2):
+ code[offset : offset + 2] = _NOP, 0
+
+ start = following.offset + 2
+
+ code[following.offset : following.offset + 2] = new_op, arg & 255
+ arg >>= 8
+
+ for lookback in range(2, 8, 2):
+ if not arg:
+ break
+ code[following.offset - lookback : following.offset - lookback + 2] = (
+ _EXTENDED_ARG,
+ arg & 255,
+ )
+ arg >>= 8
+
+ assert not arg, f"Leftover bytes in arg ({arg:,})!"
+
+ # This is the worst-case stack size... but we can probably be more exact.
+ stacksize = max(
+ stacksize, stacksize + dis.stack_effect(new_op, arg if has_arg else None)
+ )
+
+ else:
+
+ assert False, "Main loop exited prematurely!"
+
+ assert len(function.__code__.co_code) == len(code), "Code changed size!"
+
+ function.__code__ = types.CodeType(
+ function.__code__.co_argcount,
+ function.__code__.co_kwonlyargcount,
+ len(varnames),
+ stacksize,
+ function.__code__.co_flags,
+ bytes(code),
+ function.__code__.co_consts,
+ tuple(names),
+ tuple(varnames),
+ function.__code__.co_filename,
+ function.__code__.co_name,
+ function.__code__.co_firstlineno,
+ function.__code__.co_lnotab,
+ function.__code__.co_freevars,
+ function.__code__.co_cellvars,
+ )
+
+ return function
+
+
+def BEFORE_ASYNC_WITH() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 8) <= sys.version_info:
+
+ def BEGIN_FINALLY() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_ADD() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_AND() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_FLOOR_DIVIDE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_LSHIFT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_MATRIX_MULTIPLY() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_MODULO() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_MULTIPLY() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_OR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_POWER() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_RSHIFT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_SUBSCR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_SUBTRACT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_TRUE_DIVIDE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BINARY_XOR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if sys.version_info < (3, 8):
+
+ def BREAK_LOOP() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_CONST_KEY_MAP(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_LIST(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_LIST_UNPACK(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_MAP(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_MAP_UNPACK(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_MAP_UNPACK_WITH_CALL(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_SET(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_SET_UNPACK(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_SLICE(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_STRING(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_TUPLE(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_TUPLE_UNPACK(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def BUILD_TUPLE_UNPACK_WITH_CALL(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 8) <= sys.version_info:
+
+ def CALL_FINALLY(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def CALL_FUNCTION(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def CALL_FUNCTION_EX(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def CALL_FUNCTION_KW(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 7) <= sys.version_info:
+
+ def CALL_METHOD(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def COMPARE_OP(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if sys.version_info < (3, 8):
+
+ def CONTINUE_LOOP(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DELETE_ATTR(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DELETE_DEREF(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DELETE_FAST(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DELETE_GLOBAL(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DELETE_NAME(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DELETE_SUBSCR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DUP_TOP() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def DUP_TOP_TWO() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 8) <= sys.version_info:
+
+ def END_ASYNC_FOR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def END_FINALLY() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def EXTENDED_ARG(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def FORMAT_VALUE(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def FOR_ITER(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def GET_AITER() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def GET_ANEXT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def GET_AWAITABLE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def GET_ITER() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def GET_YIELD_FROM_ITER() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def IMPORT_FROM(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def IMPORT_NAME(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def IMPORT_STAR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_ADD() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_AND() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_FLOOR_DIVIDE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_LSHIFT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_MATRIX_MULTIPLY() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_MODULO() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_MULTIPLY() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_OR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_POWER() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_RSHIFT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_SUBTRACT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_TRUE_DIVIDE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def INPLACE_XOR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def JUMP_ABSOLUTE(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def JUMP_FORWARD(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def JUMP_IF_FALSE_OR_POP(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def JUMP_IF_TRUE_OR_POP(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LIST_APPEND(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 9) <= sys.version_info:
+
+ def LOAD_ASSERTION_ERROR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_ATTR(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_BUILD_CLASS() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_CLASSDEREF(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_CLOSURE(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_CONST(arg: typing.Hashable) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_DEREF(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_FAST(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_GLOBAL(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 7) <= sys.version_info:
+
+ def LOAD_METHOD(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def LOAD_NAME(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def MAKE_FUNCTION(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def MAP_ADD(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def NOP() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def POP_BLOCK() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def POP_EXCEPT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 8) <= sys.version_info:
+
+ def POP_FINALLY(arg: bool) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def POP_JUMP_IF_FALSE(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def POP_JUMP_IF_TRUE(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def POP_TOP() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def PRINT_EXPR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def RAISE_VARARGS(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def RETURN_VALUE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if (3, 8) <= sys.version_info:
+
+ def ROT_FOUR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def ROT_THREE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def ROT_TWO() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def SETUP_ANNOTATIONS() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def SETUP_ASYNC_WITH(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if sys.version_info < (3, 8):
+
+ def SETUP_EXCEPT(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def SETUP_FINALLY(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if sys.version_info < (3, 8):
+
+ def SETUP_LOOP(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def SETUP_WITH(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def SET_ADD(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def STORE_ATTR(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+if sys.version_info < (3, 7):
+
+ def STORE_ANNOTATION(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def STORE_DEREF(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def STORE_FAST(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def STORE_GLOBAL(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def STORE_NAME(arg: str) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def STORE_SUBSCR() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def UNARY_INVERT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def UNARY_NEGATIVE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def UNARY_NOT() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def UNARY_POSITIVE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def UNPACK_EX(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def UNPACK_SEQUENCE(arg: int) -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def WITH_CLEANUP_FINISH() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def WITH_CLEANUP_START() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def YIELD_FROM() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
+
+
+def YIELD_VALUE() -> None:
+ raise HaxUsageError(_USAGE_MESSAGE)
diff --git a/mypy.ini b/mypy.ini
new file mode 100644
index 0000000..5857706
--- /dev/null
+++ b/mypy.ini
@@ -0,0 +1,26 @@
+[mypy]
+
+files = **/*.py
+
+check_untyped_defs = True
+disallow_any_generics = True
+disallow_incomplete_defs = True
+disallow_subclassing_any = True
+disallow_untyped_calls = True
+disallow_untyped_decorators = True
+disallow_untyped_defs = True
+no_implicit_optional = True
+no_implicit_reexport = True
+warn_redundant_casts = True
+warn_return_any = True
+warn_unused_configs = True
+warn_unused_ignores = True
+
+[mypy-pytest]
+
+ignore_missing_imports = True
+
+[mypy-test]
+
+allow_untyped_decorators = True
+
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..606a6f4
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,4 @@
+[pytest]
+
+addopts = --forked --verbose --numprocesses auto
+python_files = test.py
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..f9fb0f8
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+black
+hypothesis
+mypy
+pytest
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..ed527fa
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,22 @@
+import setuptools # type: ignore
+
+import hax
+
+
+with open("README.md") as readme:
+ long_description = readme.read()
+
+
+setuptools.setup(
+ author="Brandt Bucher",
+ author_email="brandtbucher@gmail.com",
+ description="Write compiled bytecode inline with standard Python syntax.",
+ keywords="bytecode",
+ license="MIT",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ name="hax",
+ py_modules=["hax"],
+ url="https://github.com/brandtbucher/hax",
+ version=hax.__version__,
+)
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..12d0d52
--- /dev/null
+++ b/test.py
@@ -0,0 +1,45 @@
+import dis
+import itertools
+import re
+import typing
+
+import hypothesis
+import pytest
+
+import hax
+
+
+def get_examples() -> typing.Iterator[str]:
+
+ with open("README.md") as readme:
+ examples = re.findall(r"\n```py(\n[^`]+\n)```\n", readme.read())
+
+ for i, example in enumerate(itertools.accumulate(examples)):
+ yield pytest.param(example, id=f"{i}")
+
+
+@hypothesis.given(items=hypothesis.infer)
+@pytest.mark.parametrize("code", get_examples())
+def test_readme(code: str, items: typing.Sequence[object]) -> None:
+
+ namespace: typing.Dict[str, typing.Any] = {"__name__": "__main__"}
+ exec(code, namespace)
+
+ actual = namespace["double"](items)
+ expected = [*itertools.chain.from_iterable(zip(items, items))]
+
+ assert actual == expected
+
+
+@pytest.mark.parametrize("opname, opcode", dis.opmap.items())
+def test_opcodes(opname: str, opcode: int) -> None:
+
+ arg = dis.HAVE_ARGUMENT <= opcode
+
+ assert hasattr(hax, opname)
+
+ with pytest.raises(hax.HaxUsageError if arg else TypeError):
+ getattr(hax, opname)(...)
+
+ with pytest.raises(TypeError if arg else hax.HaxUsageError):
+ getattr(hax, opname)()