From 0ecee9a71b9d2c7ed3a54a0a4df366c65280cb01 Mon Sep 17 00:00:00 2001 From: Cormac Shannon Date: Wed, 9 Apr 2025 16:54:52 +0100 Subject: [PATCH] First working state --- .gitignore | 10 +++ .python-version | 1 + README.md | 0 examples/GameOfLife.bf | 48 ++++++++++++++ examples/HelloWorld.bf | 1 + hello.py | 6 ++ pyproject.toml | 13 ++++ src/brainfuck/__brainfuck.py | 106 +++++++++++++++++++++++++++++++ src/brainfuck/__init__.py | 0 src/brainfuck/__main__.py | 0 src/enterpreter/__enterpreter.py | 72 +++++++++++++++++++++ src/enterpreter/__init__.py | 3 + src/enterpreter/__main__.py | 0 src/enterpreter/util/__init__.py | 3 + src/enterpreter/util/hexdump.py | 45 +++++++++++++ uv.lock | 66 +++++++++++++++++++ 16 files changed, 374 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 examples/GameOfLife.bf create mode 100644 examples/HelloWorld.bf create mode 100644 hello.py create mode 100644 pyproject.toml create mode 100644 src/brainfuck/__brainfuck.py create mode 100644 src/brainfuck/__init__.py create mode 100644 src/brainfuck/__main__.py create mode 100644 src/enterpreter/__enterpreter.py create mode 100644 src/enterpreter/__init__.py create mode 100644 src/enterpreter/__main__.py create mode 100644 src/enterpreter/util/__init__.py create mode 100644 src/enterpreter/util/hexdump.py create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..505a3b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..ff4a82c --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +pypy@3.10 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/GameOfLife.bf b/examples/GameOfLife.bf new file mode 100644 index 0000000..82790eb --- /dev/null +++ b/examples/GameOfLife.bf @@ -0,0 +1,48 @@ + Linus Akesson presents: + The Game Of Life implemented in Brainfuck + + +>>++++[<++++>-]<[<++++++>-]+[<[>>>>+<<<<-]>>>>[<<<<+>>>>>>+<<-]<+ + +++[>++++++++<-]>.[-]<+++[>+++<-]>+[>>.+<<-]>>[-]<<<++[<+++++>-]<.<<[>>>>+ + <<<<-]>>>>[<<<<+>>>>>>+<<-]<<[>>>>.+<<<++++++++++[<[>>+<<-]>>[<<+>>>>>++++++++ + +++<<<-]<[>+<-]>[<+>>>>+<<<-]>>>[>>>>>>>>>>>>+>+<< <<<<<<<<<<<-]>>>>>>>>>> +>>[-[>>>>+<<<<-]>[>>>>+<<<<-]>>>]> >>[<<<+>> >- ]<<<[>>+>+<<<-]>[->[<<< +<+>>>>-]<[<<< <+> >>>-]<<<< ]< ++++++ ++ +[>+++++<-]>>[<<+>>-]< +<[>---<-]>.[- ] <<<<<<<<< < <<<<<< < -]++++++++++.[-]<-]>>> +>[-]<[-]+++++ +++[>++++ ++++< - ]>--.[-]<,----------[<+ +>-]>>>>>>+<<<<< < <[>+>>>>>+>[ -]<<< << <<-]>++++++++++>>>>>[[-] +<<,<<<<<<<->>>> > >>[<<<<+>>>>-]<<<<[>>>>+ >+<<<<<-]>>>>>----------[<<<< +<<<<+<[>>>>+<<< <-]>>>>[<<<<+>>>>>>+<<- ]>[>-<-]>++++++++++[>+++++++++ +++<-]<<<<<<[>>> >+<<<<-]>>>>[<<<<+>>>>> >+<<-]>>>>[<<->>-]<<++++++++++ +[>+<-]>[>>>>>>> >>>>>+>+<<<< <<<<< <<<<-]>>> >> >>>>>>>[-[>>> +>+<<<<-]>[>>>> +<<<<-]>> > ]>> > [<< < +>>>-]+<<<[> +>>-<<<-]>[->[< <<<+>>>>-] <[ < < < <+>>>>-]<<< +<]<<<<<<<<<<<, [ -]]>]>[-+++ ++ + +++ ++[>+++++++ +++++>+++++++++ + +<<-]>[-[>>> +<<<- ]>>>[ < <<+ >>>>>>>+>+< +<<<<-]>>>>[-[> > >>+<<<<-]>[> >>>+< < <<-]> > >]> >>[<<<+>>>- +]<<<[>>+>+<<< - ]>[->[<<<<+> >>>-] < [<<< < +>> >>-]<<<<]<< +<<<<<<[>>>+<< < -]>>>[<<<+>> >>>>> + >+<< < <<-]<<[>>+<< +-]>>[<<+>>>>> >+>+<<<<<-]>> >>[-[ > >>>+ < <<<-]>[>>>>+< +<<<-]>[>>>>+< <<<-]>>]>>>[ - ]<[>+< - ]<[ - [<<<<+>>>>-]<<< +<]<<<<<<<<]<< <<<<<<<<++++ + +++++ [ >+++ + ++++++[<[>>+<<-]>>[<<+ +>>>>>++++++++ + ++<<< -] < [>+<- ] >[<+ > >>>+<<<-]>>>[<<<+>>>-] +<<<[>>>+>>>> > +<<<< << <<-]> > >>>> >>>[>>+<<-]>>[<<+<+>> +>-]<<<------ - -----[ >> >+<<< - ]>>> [<<<+> > >>>>>+>+<<<< +<-]>>>>[-[>> > >+<<<< -] > [>>>> + <<<<- ]>>> ] >>>[<<<+>>>- +]<<<[>>+>+<< < -]>>> >> > > [<<<+ >>>-]<<<[>>> ++<<<<<+>>- ]> > >>>>>[< <<+>>>-]<<<[> +>>+<<<<<<< <<+ > >>>>>-]< <<<<<<[->[<<<<+ +>>>>-]<[<<<<+>>>>-]<<<<]>[<<<<<< <+>>> >>>>-]<<<< <<<<<+++++++++++[> +>>+<<<-]>>>[<<<+>>>>>>>+>+<<<<<-]>>>>[-[> >>>+<<<<-]>[>>>>+<<<<-]>>>]>>>[<<< ++>>>-]<<<[>>+>+<<<-]>>>>>>>[<<<+>>>-]<<<[ >>>+<<<<<+>>-]>>>>>>>[<<<+>>>-]<<< +[>>>+<<<<<<<<<+>>>>>>-]<<<<<<<[->[< < < <+>>>>-]<[<<<<+>>>>-]<<<<]>[<<<<<<< ++>>>>>>>-]<<<<<<<<<+++++++++++[>>> > >>>+>+<<<<<<<<-]>>>>>>>[-[>>>>+<<<<- +]>[>>>>+<<<<-]>>>]>>>[<<<+>>>-]<<< [ >>+>+<<<-]>>>>>>>[<<<+>>>-]<<<[>>>+<< +<<<+>>-]>>>>>>>[<<<+>>>-]<<<[>>>+< <<<<<<<<+>>>>>>-]<<<<<<<[->[<<<<+>>>>- + ]<[<<<<+>>>>-]<<<<]>[<<<<<<<+>>>>> >>-]<<<<<<<----[>>>>>>>+<<<<<<<+[>>>>> + >>-<<<<<<<[-]]<<<<<<<[>>>>>>>>>>>>+>+<<<<<<<<<<<<<-][ lft@df.lth.se ]>>>>> + >>>>>>>[-[>>>>+<<<<-]>[>>>>+<<<<-]>[>>>>+<<<<-]>>]>>>[-]<[>+<-]<[-[<<<<+>> + >>-]<<<<]<<<<<<[-]]<<<<<<<[-]<<<<-]<-]>>>>>>>>>>>[-]<<]<<<<<<<<<<] + + Type for instance "fg" to toggle the cell at row f and column g + Hit enter to calculate the next generation + Type q to quit \ No newline at end of file diff --git a/examples/HelloWorld.bf b/examples/HelloWorld.bf new file mode 100644 index 0000000..ea2b641 --- /dev/null +++ b/examples/HelloWorld.bf @@ -0,0 +1 @@ +++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. \ No newline at end of file diff --git a/hello.py b/hello.py new file mode 100644 index 0000000..a79ef70 --- /dev/null +++ b/hello.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from enterpreter!") + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..159d86c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "enterpreter" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + "colorama>=0.4.6", + "cython>=3.0.12", +] + +[tool.uv] +package = true diff --git a/src/brainfuck/__brainfuck.py b/src/brainfuck/__brainfuck.py new file mode 100644 index 0000000..b107665 --- /dev/null +++ b/src/brainfuck/__brainfuck.py @@ -0,0 +1,106 @@ +from sys import stdin, stdout +from os import PathLike +from enterpreter import Enterpreter, opcode + + +BrainFuckByteCode = { + ">": 1, + "<": 2, + "+": 3, + "-": 4, + ".": 5, + ",": 6, + "[": 7, + "]": 8, +} + + +class BrainFuckByteCodeCompiler: + def load_file(self, file: PathLike): + self.source_file: PathLike = file + self.load(bytearray(Path(file).read_bytes())) + + def load(self, data: bytearray): + self.source_data: bytearray = data + + def compile(self) -> bytearray: + # whitespace_b = whitespace.encode() + valid_b = set(ord(s) for s in BrainFuckByteCode.keys()) + self.source_data = bytearray(b for b in self.source_data if b in valid_b) + + out = bytearray() + for byte in self.source_data: + out.append(BrainFuckByteCode[chr(byte)]) + return out + + +class BrainFuckEnterpreter(Enterpreter): + @opcode(BrainFuckByteCode[">"]) + def a_(self): + self._memory_pointer += 1 + + @opcode(BrainFuckByteCode["<"]) + def b_(self): + self._memory_pointer -= 1 + + @opcode(BrainFuckByteCode["+"]) + def c_(self): + val = self._memory[self._memory_pointer] + self._memory[self._memory_pointer] = (val + 1) % 256 + + @opcode(BrainFuckByteCode["-"]) + def d_(self): + val = self._memory[self._memory_pointer] + self._memory[self._memory_pointer] = (val - 1) & 255 + + @opcode(BrainFuckByteCode["."]) + def e_(self): + stdout.write(chr(self._memory[self._memory_pointer])) + + @opcode(BrainFuckByteCode[","]) + def f_(self): + stdout.flush() + self._memory[self._memory_pointer] = ord(stdin.read(1)) + + @opcode(BrainFuckByteCode["["]) + def g_(self): + # Ignore opcode if mem_pointer == 0 + if self._memory[self._memory_pointer] > 0: + pass + elif self._memory[self._memory_pointer] == 0: + self._program_counter = self.findNext() - 1 + + @opcode(BrainFuckByteCode["]"]) + def h_(self): + self._program_counter = self.findNext(-1) + + def findNext(self, incrementValue: int = 1) -> int: + search_pointer = self._program_counter + incrementValue + bracket_counter = 1 + while bracket_counter != 0: + opcode = self._memory[search_pointer] + if opcode == BrainFuckByteCode["["]: + bracket_counter += incrementValue + elif opcode == BrainFuckByteCode["]"]: + bracket_counter -= incrementValue + search_pointer += incrementValue + return search_pointer + + +def bf(program_path: PathLike, bits=8, mem=2**8) -> BrainFuckEnterpreter: + bf_c = BrainFuckByteCodeCompiler() + bf_c.load_file(program_path) + program = bf_c.compile() + + bf_e = BrainFuckEnterpreter(bits=bits, memsize=mem) + bf_e.load_program(program) + bf_e.run() + + return bf_e + + +if __name__ == "__main__": + from pathlib import Path + + bf(Path("examples", "HelloWorld.bf")) + bf(Path("examples", "GameOfLife.bf"), bits=16, mem=2**16) diff --git a/src/brainfuck/__init__.py b/src/brainfuck/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/brainfuck/__main__.py b/src/brainfuck/__main__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/enterpreter/__enterpreter.py b/src/enterpreter/__enterpreter.py new file mode 100644 index 0000000..728a3fb --- /dev/null +++ b/src/enterpreter/__enterpreter.py @@ -0,0 +1,72 @@ +from collections.abc import Callable +from inspect import getmembers +from typing import TypeAlias +from traceback import print_exception +from pprint import pprint + +from .util import hexdump + +Operation: TypeAlias = str | int +Subroutine: TypeAlias = Callable + +OPCODE_HEADER = "__enterpreter__opcode" + + +def opcode(opcode: Operation): + def _helper(func: Callable): + setattr(func, OPCODE_HEADER, opcode) + return func + + return _helper + + +class Enterpreter: + def __init__(self, bits=8, memsize=256) -> None: + self.__bits = bits + self.__memory_max_size = 2**self.__bits + self.__memory_size = min(memsize, self.__memory_max_size) + + self._memory = bytearray(self.__memory_size) + self._memory_pointer: int = 0 + + self._program_counter: int = 0 + + self.__operations: dict[Operation, Subroutine] = dict() + self.__init_opcodes() + + def __init_opcodes(self): + for func_name, func in getmembers( + self, lambda obj: hasattr(obj, OPCODE_HEADER) + ): + self._register_opcode(getattr(func, OPCODE_HEADER), func) + + def _register_opcode(self, opcode: Operation, callback: Subroutine): + self.__operations[opcode] = callback + + def is_running(self) -> bool: + return self._memory[self._program_counter] != 0 + + def run(self) -> None: + try: + # Keep running until you reach a null byte + while self.is_running(): + opcode = self._memory[self._program_counter] + self.__operations[opcode]() + self._program_counter += 1 + except Exception as e: + self.dump() + print_exception(e) + + def dump(self): + hexdump( + data=self._memory, + program_counter=self._program_counter, + memory_pointer=self._memory_pointer, + ) + pprint(self.__operations) + + def load_program(self, program: bytearray) -> None: + self._memory[: len(program)] = program[:] + + # Set new pointer immediately after code + self._memory_pointer = len(program) + 1 diff --git a/src/enterpreter/__init__.py b/src/enterpreter/__init__.py new file mode 100644 index 0000000..06c68aa --- /dev/null +++ b/src/enterpreter/__init__.py @@ -0,0 +1,3 @@ +from .__enterpreter import Enterpreter, opcode + +__all__ = ["Enterpreter", "opcode"] \ No newline at end of file diff --git a/src/enterpreter/__main__.py b/src/enterpreter/__main__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/enterpreter/util/__init__.py b/src/enterpreter/util/__init__.py new file mode 100644 index 0000000..6b728f5 --- /dev/null +++ b/src/enterpreter/util/__init__.py @@ -0,0 +1,3 @@ +from .hexdump import hexdump + +__all__ = ["hexdump"] \ No newline at end of file diff --git a/src/enterpreter/util/hexdump.py b/src/enterpreter/util/hexdump.py new file mode 100644 index 0000000..60f9788 --- /dev/null +++ b/src/enterpreter/util/hexdump.py @@ -0,0 +1,45 @@ +from colorama import Fore, Style, init + +# Initialize colorama (especially important on Windows) +init() + + +def hexdump( + data: bytearray, + width: int = 16, + program_counter: int = None, + memory_pointer: int = None, +): + """ + Print a hex+ASCII memory dump of the given bytearray. + Highlights: + - Program counter (PC) in bright red + - Memory pointer (MP) in bright blue + """ + for i in range(0, len(data), width): + chunk = data[i : i + width] + hex_parts = [] + ascii_parts = [] + + for j, byte in enumerate(chunk): + addr = i + j + + # Apply color based on PC or MP + if addr == program_counter: + style = Style.BRIGHT + Fore.RED + elif addr == memory_pointer: + style = Style.BRIGHT + Fore.BLUE + else: + style = "" + reset = Style.RESET_ALL if style else "" + + # Hex and ASCII views + hex_parts.append(f"{style}{byte:02x}{reset}") + ascii_parts.append( + f"{style}{chr(byte) if 32 <= byte <= 126 else '.'}{reset}" + ) + + # Format line: address, hex bytes, ASCII chars + hex_field = " ".join(hex_parts).ljust(width * 3 - 1) + ascii_field = "".join(ascii_parts) + print(f"{i:08x}: {hex_field} {ascii_field}") diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..ecc3dc1 --- /dev/null +++ b/uv.lock @@ -0,0 +1,66 @@ +version = 1 +requires-python = ">=3.10" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "cython" +version = "3.0.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/25/886e197c97a4b8e254173002cdc141441e878ff29aaa7d9ba560cd6e4866/cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc", size = 2757617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/78/3bcb8ee7b6f5956dbd6bebf85818e075d863419db3661f25189c64cd6c70/Cython-3.0.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba67eee9413b66dd9fbacd33f0bc2e028a2a120991d77b5fd4b19d0b1e4039b9", size = 3271523 }, + { url = "https://files.pythonhosted.org/packages/dc/21/5b700dac60cc7af4261c7fa2e91f55fe5f38f6c183e1201ced7cc932201b/Cython-3.0.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee2717e5b5f7d966d0c6e27d2efe3698c357aa4d61bb3201997c7a4f9fe485a", size = 3630244 }, + { url = "https://files.pythonhosted.org/packages/a3/db/a42b8905bde467599927765ba12147f9d6ae3cd10fb33c4cda02011d7be0/Cython-3.0.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cffc3464f641c8d0dda942c7c53015291beea11ec4d32421bed2f13b386b819", size = 3685089 }, + { url = "https://files.pythonhosted.org/packages/1a/0f/64be4bbdf26679f44fe96e19078c4382800eb8ba9b265373cae73ac94493/Cython-3.0.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d3a8f81980ffbd74e52f9186d8f1654e347d0c44bfea6b5997028977f481a179", size = 3501991 }, + { url = "https://files.pythonhosted.org/packages/d4/7d/e2a48882a4cbb16f0dfbc406879b2a1ea6b5d27c7cbef080e8eb8234a96c/Cython-3.0.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8d32856716c369d01f2385ad9177cdd1a11079ac89ea0932dc4882de1aa19174", size = 3713604 }, + { url = "https://files.pythonhosted.org/packages/3b/df/ca69aab33ffd2a184269335f6240b042c21d41dad335d4abb4ead9f22687/Cython-3.0.12-cp310-cp310-win32.whl", hash = "sha256:712c3f31adec140dc60d064a7f84741f50e2c25a8edd7ae746d5eb4d3ef7072a", size = 2578290 }, + { url = "https://files.pythonhosted.org/packages/1f/4c/4f79129407a1e0d540c961835960d811356aa3a666f621aa07cd7a979b0a/Cython-3.0.12-cp310-cp310-win_amd64.whl", hash = "sha256:d6945694c5b9170cfbd5f2c0d00ef7487a2de7aba83713a64ee4ebce7fad9e05", size = 2778456 }, + { url = "https://files.pythonhosted.org/packages/7e/60/3d27abd940f7b80a6aeb69dc093a892f04828e1dd0b243dd81ff87d7b0e9/Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3", size = 3277430 }, + { url = "https://files.pythonhosted.org/packages/c7/49/f17b0541b317d11f1d021a580643ee2481685157cded92efb32e2fb4daef/Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8", size = 3444055 }, + { url = "https://files.pythonhosted.org/packages/6b/7f/c57791ba6a1c934b6f1ab51371e894e3b4bfde0bc35e50046c8754a9d215/Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92", size = 3597874 }, + { url = "https://files.pythonhosted.org/packages/23/24/803a0db3681b3a2ef65a4bebab201e5ae4aef5e6127ae03683476a573aa9/Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128", size = 3644129 }, + { url = "https://files.pythonhosted.org/packages/27/13/9b53ba8336e083ece441af8d6d182b8ca83ad523e87c07b3190af379ebc3/Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73", size = 3504936 }, + { url = "https://files.pythonhosted.org/packages/a9/d2/d11104be6992a9fe256860cae6d1a79f7dcf3bdb12ae00116fac591f677d/Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec", size = 3713066 }, + { url = "https://files.pythonhosted.org/packages/d9/8c/1fe49135296efa3f460c760a4297f6a5b387f3e69ac5c9dcdbd620295ab3/Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b", size = 2579935 }, + { url = "https://files.pythonhosted.org/packages/02/4e/5ac0b5b9a239cd3fdae187dda8ff06b0b812f671e2501bf253712278f0ac/Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784", size = 2787337 }, + { url = "https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310", size = 3311717 }, + { url = "https://files.pythonhosted.org/packages/ee/ab/adfeb22c85491de18ae10932165edd5b6f01e4c5e3e363638759d1235015/Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47", size = 3344337 }, + { url = "https://files.pythonhosted.org/packages/0d/72/743730d7c46b4c85abefb93187cbbcb7aae8de288d7722b990db3d13499e/Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e", size = 3517692 }, + { url = "https://files.pythonhosted.org/packages/09/a1/29a4759a02661f8c8e6b703f62bfbc8285337e6918cc90f55dc0fadb5eb3/Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7", size = 3577057 }, + { url = "https://files.pythonhosted.org/packages/d6/f8/03d74e98901a7cc2f21f95231b07dd54ec2f69477319bac268b3816fc3a8/Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8", size = 3396493 }, + { url = "https://files.pythonhosted.org/packages/50/ea/ac33c5f54f980dbc23dd8f1d5c51afeef26e15ac1a66388e4b8195af83b7/Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba", size = 3603859 }, + { url = "https://files.pythonhosted.org/packages/a2/4e/91fc1d6b5e678dcf2d1ecd8dce45b014b4b60d2044d376355c605831c873/Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a", size = 2610428 }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a7fdec227b9f0bb07edbeb016c7b18ed6a8e6ce884d08b2e397cda2c0168/Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3", size = 2794755 }, + { url = "https://files.pythonhosted.org/packages/67/ad/550ddcb8b5a5d9949fe6606595cce36984c1d42309f1e04af98f5933a7ea/Cython-3.0.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4ee6f1ea1bead8e6cbc4e64571505b5d8dbdb3b58e679d31f3a84160cebf1a1a", size = 3393574 }, + { url = "https://files.pythonhosted.org/packages/34/de/ade0a80bea17197662e23d39d3d3fbf89e9e99e6ad91fd95ab87120edb3a/Cython-3.0.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57aefa6d3341109e46ec1a13e3a763aaa2cbeb14e82af2485b318194be1d9170", size = 3367198 }, + { url = "https://files.pythonhosted.org/packages/a8/30/7f48207ea13dab46604db0dd388e807d53513ba6ad1c34462892072f8f8c/Cython-3.0.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:879ae9023958d63c0675015369384642d0afb9c9d1f3473df9186c42f7a9d265", size = 3535849 }, + { url = "https://files.pythonhosted.org/packages/81/ab/f61c79fa14bd433a7dfd1548c5e00d9bd18b557c2f836aaece4fb1b22f34/Cython-3.0.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36fcd584dae547de6f095500a380f4a0cce72b7a7e409e9ff03cb9beed6ac7a1", size = 3559079 }, + { url = "https://files.pythonhosted.org/packages/d0/d1/1dbf17061229ccd35d5c0eed659fab60c2e50d2eadfa2a5729e753b6f4d0/Cython-3.0.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62b79dcc0de49efe9e84b9d0e2ae0a6fc9b14691a65565da727aa2e2e63c6a28", size = 3436649 }, + { url = "https://files.pythonhosted.org/packages/2d/d4/9ce42fff6de5550f870cdde9a1482d69ea66a1249a88fa0d0df9adebfb1a/Cython-3.0.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4aa255781b093a8401109d8f2104bbb2e52de7639d5896aefafddc85c30e0894", size = 3644025 }, + { url = "https://files.pythonhosted.org/packages/e3/89/b0c847f9df92af3ef11281b6811c000bd6f8ce0db02e4374397f8d67f829/Cython-3.0.12-cp313-cp313-win32.whl", hash = "sha256:77d48f2d4bab9fe1236eb753d18f03e8b2619af5b6f05d51df0532a92dfb38ab", size = 2604911 }, + { url = "https://files.pythonhosted.org/packages/a6/5f/bbfaf2b5f7bf78854ecbc82f8473a3892ae5580e0c5bd0d4a82580b39ed3/Cython-3.0.12-cp313-cp313-win_amd64.whl", hash = "sha256:86c304b20bd57c727c7357e90d5ba1a2b6f1c45492de2373814d7745ef2e63b4", size = 2786786 }, + { url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640 }, +] + +[[package]] +name = "enterpreter" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "colorama" }, + { name = "cython" }, +] + +[package.metadata] +requires-dist = [ + { name = "colorama", specifier = ">=0.4.6" }, + { name = "cython", specifier = ">=3.0.12" }, +]