├── birp ├── __init__.py ├── __main__.py ├── __version__.py ├── argparser.birp ├── argparser.py ├── translate │ ├── __main__.birp │ ├── __main__.py │ └── birp.lark ├── reverse │ ├── __main__.birp │ ├── __main__.py │ └── birp-rev.lark ├── translated.py ├── codegen_utils.py └── codegen_utils.birp ├── MANIFEST.in ├── examples ├── fib.birp ├── fib.py ├── fizzbuzz.py └── fizzbuzz.birp ├── LICENSE ├── setup.py ├── README.md └── .gitignore /birp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /birp/__main__.py: -------------------------------------------------------------------------------- 1 | from .translate.__main__ import main 2 | main() -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include . *.birp 2 | recursive-include . *.lark 3 | -------------------------------------------------------------------------------- /examples/fib.birp: -------------------------------------------------------------------------------- 1 | из functools подключить cache как кэш 2 | 3 | @кэш 4 | объявить фибоначчи(номер): 5 | если номер == 0: 6 | вернуть 0 7 | если номер в [1, 2]: 8 | вернуть 1 9 | вернуть фибоначчи(номер - 1) + фибоначчи(номер - 2) -------------------------------------------------------------------------------- /examples/fib.py: -------------------------------------------------------------------------------- 1 | from functools import cache as кэш 2 | 3 | 4 | @кэш 5 | def фибоначчи(номер): 6 | if номер == 0: 7 | return 0 8 | if номер in [1, 2]: 9 | return 1 10 | return фибоначчи(номер - 1) + фибоначчи(номер - 2) 11 | -------------------------------------------------------------------------------- /examples/fizzbuzz.py: -------------------------------------------------------------------------------- 1 | def шипение_жужжание(номер): 2 | if not номер % 15: 3 | return "ШипениеЖужжание" 4 | if not номер % 3: 5 | return "Шипение" 6 | if not номер % 5: 7 | return "Жужжание" 8 | return str(номер) 9 | 10 | 11 | for номер in range(100): 12 | print(шипение_жужжание(номер)) 13 | -------------------------------------------------------------------------------- /examples/fizzbuzz.birp: -------------------------------------------------------------------------------- 1 | объявить шипение_жужжание(номер): 2 | если не номер % 15: 3 | вернуть "ШипениеЖужжание" 4 | если не номер % 3: 5 | вернуть "Шипение" 6 | если не номер % 5: 7 | вернуть "Жужжание" 8 | вернуть строка(номер) 9 | 10 | для номер в диапазон(100): 11 | вывод(шипение_жужжание(номер)) -------------------------------------------------------------------------------- /birp/__version__.py: -------------------------------------------------------------------------------- 1 | """ 2 | __version__.py 3 | ~~~~~~~~~~~~~~ 4 | 5 | Information about the current version of birp package. 6 | """ 7 | 8 | __title__ = "birp" 9 | __description__ = "birp — большой русский питон (BIg Russian Python)" 10 | __version__ = "0.2.0" 11 | __author__ = "evtn" 12 | __author_email__ = "g@evtn.ru" 13 | __license__ = "MIT" 14 | __url__ = "https://github.com/evtn/birp" 15 | -------------------------------------------------------------------------------- /birp/argparser.birp: -------------------------------------------------------------------------------- 1 | из argparse подключить ArgumentParser как ПарсерАргументов 2 | 3 | аргпарсер = ПарсерАргументов(описание="birp - большой русский питон. Позволяет писать программы на русском языке, как настоящий патриот!!!!", prog="birp[.reverse]") 4 | 5 | 6 | аргпарсер.add_argument( 7 | "-ф", "--файлы", "-f", "--files" 8 | nargs="+", 9 | help="Список файлов, которые необъодимо перевести" 10 | ) 11 | 12 | аргпарсер.add_argument( 13 | "-п", "--переводы", 14 | помощь="ЯСНО*-файл с дополнительными переводами имён (должен содержать объект вида {русское_имя: английское_имя})\n*ЯваСценарная Нотация Объектов", 15 | ) 16 | 17 | аргпарсер.add_argument( 18 | "-д", "--дерево", 19 | dest="tree", 20 | action="store_true", 21 | помощь="Вывод дерева в отдельный файл" 22 | ) -------------------------------------------------------------------------------- /birp/argparser.py: -------------------------------------------------------------------------------- 1 | from argparse import ArgumentParser as ПарсерАргументов 2 | 3 | аргпарсер = ПарсерАргументов( 4 | description="birp - большой русский питон. Позволяет писать программы на русском языке, как настоящий патриот!!!!", 5 | prog="birp[.reverse]" 6 | ) 7 | аргпарсер.add_argument( 8 | "-ф", "--файлы", "-f", "--files", nargs="+", help="Список файлов, которые необъодимо перевести" 9 | ) 10 | аргпарсер.add_argument( 11 | "-п", 12 | "--переводы", 13 | help="ЯСНО*-файл с дополнительными переводами имён (должен содержать объект вида {русское_имя: английское_имя})\n*ЯваСценарная Нотация Объектов", 14 | ) 15 | аргпарсер.add_argument( 16 | "-д", 17 | "--дерево", 18 | dest="tree", 19 | action="store_true", 20 | help="Вывод дерева в отдельный файл", 21 | ) 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dmitry Gritsenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | from setuptools import setup 5 | 6 | 7 | # get key package details from birp/__version__.py 8 | about = {} # type: ignore 9 | here = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | with open(os.path.join(here, 'birp', '__version__.py')) as f: 12 | exec(f.read(), about) 13 | 14 | # load the README file and use it as the long_description for PyPI 15 | with open('README.md', 'r', encoding="utf-8") as f: 16 | readme = f.read() 17 | 18 | # package configuration - for reference see: 19 | # https://setuptools.readthedocs.io/en/latest/setuptools.html#id9 20 | setup( 21 | name=about['__title__'], 22 | description=about['__description__'], 23 | long_description=readme, 24 | long_description_content_type='text/markdown', 25 | version=about['__version__'], 26 | author=about['__author__'], 27 | author_email=about['__author_email__'], 28 | url=about['__url__'], 29 | packages=['birp', 'birp.reverse', 'birp.translate'], 30 | install_requires=["lark"], 31 | package_data={"": ["*.lark", "*.birp"]}, 32 | include_package_data=True, 33 | python_requires=">=3.6.*", 34 | license=about['__license__'], 35 | download_url=f"https://github.com/evtn/birp/archive/v{about['__version__']}.tar.gz", 36 | zip_safe=False, 37 | # https://pypi.org/classifiers/ 38 | classifiers=[ 39 | 'Intended Audience :: Developers', 40 | 'Programming Language :: Python :: 3', 41 | 'Programming Language :: Python :: 3.6', 42 | 'Programming Language :: Python :: 3.7', 43 | 'Programming Language :: Python :: 3.8', 44 | 'Programming Language :: Python :: 3.9' 45 | ], 46 | keywords='python russian translation' 47 | ) 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # birp (борп) - большой русский питон 2 | 3 | Почему большой? [Потому что](https://ru.wikipedia.org/wiki/%D0%91%D0%BE%D0%BB%D1%8C%D1%88%D0%BE%D0%B9_%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9_%D0%BA%D0%BE%D1%82). 4 | Почему русский? Потому что на русском. 5 | Почему питон? [Потому что](https://ru.wikipedia.org/wiki/Python). 6 | 7 | ## Что? 8 | 9 | С помощью данного модуля вы сможете писать код на русском, но всё ещё пользоваться всеми преимуществами Питона! (пожалуйста, не надо) 10 | Транслятор переводит не только ключевые слова, но и встроенные функции, типы, магические методы, а так же некоторые другие слова 11 | 12 | Пример кода: 13 | ```birp 14 | граница = целое(ввод("Введите верхнюю границу: ")) 15 | 16 | для число в диапазон(граница): 17 | вывод(число) 18 | ``` 19 | 20 | Пример транслируется в: 21 | 22 | ```python 23 | bound = int(input("Введите верхнюю границу: ")) 24 | 25 | for number in range(bound): 26 | print(number) 27 | ``` 28 | 29 | Транслятор Борп в том числе написан на Борп (см. файлы \*.birp в репозитории) 30 | 31 | ## Установка 32 | 33 | `python -m pip install birp` 34 | 35 | ## Использование 36 | 37 | Напишите ваш код в файле с расширением .birp и запустите простую команду: 38 | 39 | `python -m birp -f файл1 файл2...` 40 | 41 | Более подробно об использовании аргументов расскажет `python -m birp -h` 42 | 43 | ## Документация 44 | 45 | Когда-нибудь - обязательно. Пока смотрите примеры (папка `examples`) и используйте обратную трансляцию (см. ниже) 46 | 47 | ## Обратная трансляция 48 | Для тех, кому очень лениво переписывать тысячи строчек кода, сделан модуль обратной трансляции (из обычного Питона в Борп): 49 | 50 | `python -m birp.reverse -f файл1 файл2...` 51 | 52 | ### Очень хороший пример обратной трансляции 53 | 54 | ``` 55 | git clone https://github.com/keon/algorithms 56 | python -m birp.reverse -f algorithms/algorithms/*/*.py 57 | ``` 58 | 59 | ## Интерактивный режим 60 | 61 | Если не передать файлы, то запустится интерактивный режим. 62 | Работает он ужасно, потому что так и задумано (нет). -------------------------------------------------------------------------------- /birp/translate/__main__.birp: -------------------------------------------------------------------------------- 1 | из ..translated подключить translated как переводы 2 | из ..argparser подключить аргпарсер 3 | подключить os 4 | из ..codegen_utils подключить БазовыйГенератор, получить_парсеры, загрузить, преобразовать 5 | 6 | здесь = os.path.abspath(os.path.dirname(__файл__)) 7 | парсер, вычислительный_парсер = получить_парсеры(os.path.join(здесь, "birp.lark")) 8 | 9 | класс Кодогенератор(БазовыйГенератор): 10 | вычислительный_парсер = вычислительный_парсер 11 | переводы = переводы 12 | 13 | 14 | преобразователь = Кодогенератор() 15 | 16 | 17 | объявить главный(): 18 | аргументы = аргпарсер.parse_args() 19 | если аргументы.переводы: 20 | с открыть(аргументы.переводы) как файл: 21 | переводы_пользователя = загрузить(файл) 22 | переводы.обновить(переводы_пользователя) 23 | 24 | если аргументы.файлы: 25 | попробовать: 26 | подключить black как чёрный 27 | кроме ОшибкаПодключения: 28 | чёрный = Ничего 29 | 30 | для аргумент в аргументы.файлы: 31 | имя_ввода = аргумент 32 | с открыть(имя_ввода) как файл: 33 | код = файл.читать() 34 | имя_вывода = имя_ввода.заменить(".birp", ".py") 35 | результат, дерево = преобразовать(код, парсер, преобразователь, имя_ввода) 36 | если результат: 37 | если чёрный: 38 | результат = чёрный.format_str(результат, mode=чёрный.Mode()) 39 | если аргументы.дерево: 40 | с открыть(имя_вывода + ".tree", "w") как файл: 41 | файл.писать(дерево.приятный()) 42 | с открыть(имя_вывода, "w") как файл: 43 | файл.писать(результат) 44 | вывод(ф"{имя_ввода} -> {имя_вывода}") 45 | иначе: 46 | вывод(ф"{имя_ввода} невозможно преобразовать в {имя_вывода}") 47 | 48 | иначе: 49 | пока Да: 50 | вывод("Интерактивный Борп!") 51 | код = ввод(">>> ") 52 | результат, дерево = преобразовать(код, вычислительный_парсер, преобразователь, "<ввод>") 53 | вывод(ф"<<< {результат}") 54 | вывод(ф"= {представление(вычислить(результат))}") 55 | 56 | если __имя__ == "__main__": 57 | main() -------------------------------------------------------------------------------- /birp/translate/__main__.py: -------------------------------------------------------------------------------- 1 | from ..translated import translated as переводы 2 | from ..argparser import аргпарсер 3 | import os 4 | from ..codegen_utils import БазовыйГенератор, получить_парсеры, загрузить, transform 5 | 6 | здесь = os.path.abspath(os.path.dirname(__file__)) 7 | ( 8 | parser, 9 | вычислительный_парсер, 10 | ) = получить_парсеры(os.path.join(здесь, "birp.lark")) 11 | 12 | 13 | class Кодогенератор(БазовыйГенератор): 14 | вычислительный_парсер = вычислительный_парсер 15 | переводы = переводы 16 | 17 | 18 | преобразователь = Кодогенератор() 19 | 20 | 21 | def main(): 22 | args = аргпарсер.parse_args() 23 | if args.переводы: 24 | with open(args.переводы) as file: 25 | переводы_пользователя = загрузить(file) 26 | 27 | переводы.update(переводы_пользователя) 28 | if args.файлы: 29 | try: 30 | import black as чёрный 31 | except ImportError: 32 | чёрный = None 33 | 34 | for arg in args.файлы: 35 | имя_ввода = arg 36 | with open(имя_ввода) as file: 37 | code = file.read() 38 | 39 | имя_вывода = имя_ввода.replace(".birp", ".py") 40 | ( 41 | result, 42 | tree, 43 | ) = transform(code, parser, преобразователь, имя_ввода) 44 | if result: 45 | if чёрный: 46 | result = чёрный.format_str(result, mode=чёрный.Mode()) 47 | if args.tree: 48 | with open(имя_вывода + ".tree", "w") as file: 49 | file.write(tree.pretty()) 50 | 51 | with open(имя_вывода, "w") as file: 52 | file.write(result) 53 | 54 | print(f"{имя_ввода} -> {имя_вывода}") 55 | else: 56 | print(f"{имя_ввода} невозможно преобразовать в {имя_вывода}") 57 | else: 58 | while True: 59 | print("Интерактивный Борп!") 60 | code = input(">>> ") 61 | ( 62 | result, 63 | tree, 64 | ) = transform(code, вычислительный_парсер, преобразователь, "<ввод>") 65 | print(f"<<< {result}") 66 | print(f"= {repr(eval(result))}") 67 | 68 | 69 | if __name__ == "__main__": 70 | main() 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ -------------------------------------------------------------------------------- /birp/reverse/__main__.birp: -------------------------------------------------------------------------------- 1 | из ..translated подключить translated как переводы 2 | из ..argparser подключить аргпарсер 3 | подключить os 4 | из ..codegen_utils подключить БазовыйГенератор, получить_парсеры, загрузить, преобразовать 5 | 6 | здесь = os.path.abspath(os.path.dirname(__файл__)) 7 | парсер, вычислительный_парсер = получить_парсеры(os.path.join(здесь, "birp-rev.lark")) 8 | 9 | переводы = {значение: ключ для ключ, значение в переводы.элементы()} 10 | 11 | класс Кодогенератор(БазовыйГенератор): 12 | вычислительный_парсер = вычислительный_парсер 13 | переводы = переводы 14 | ключи = { 15 | "if": "если", 16 | "else": "иначе", 17 | "elif": "иначеесли", 18 | "while": "пока", 19 | "for": "для", 20 | "try": "попробовать", 21 | "except": "кроме", 22 | "finally": "финально", 23 | "from": "из", 24 | "in": "в", 25 | "import": "подключить", 26 | "as": "как", 27 | "pass": "пропустить", 28 | "continue": "продолжить", 29 | "break": "остановить", 30 | "return": "вернуть", 31 | "def": "объявить", 32 | "raise": "бросить", 33 | "assert": "убедиться", 34 | "class": "класс", 35 | "yield": "выдать", 36 | "await": "ожидать", 37 | "async": "асинхронно", 38 | "lambda": "лямбда", 39 | "None": "Ничего", 40 | "True": "Да", 41 | "False": "Нет", 42 | "and": "и", 43 | "or": "или", 44 | "not": "не", 45 | "is": "есть", 46 | "del": "удалить", 47 | "match": "соответствие", 48 | "case": "случай", 49 | "global": "глобально", 50 | "nonlocal": "нелокально", 51 | "with": "с", 52 | "orig_in": "in", 53 | "orig_is": "is", 54 | "orig_not": "not" 55 | } 56 | 57 | 58 | преобразователь = Кодогенератор() 59 | 60 | 61 | объявить главный(): 62 | аргументы = аргпарсер.parse_args() 63 | если аргументы.переводы: 64 | с открыть(аргументы.переводы) как файл: 65 | переводы_пользователя = загрузить(файл) 66 | переводы.обновить(переводы_пользователя) 67 | 68 | если аргументы.файлы: 69 | для аргумент в аргументы.файлы: 70 | имя_ввода = аргумент 71 | с открыть(имя_ввода) как файл: 72 | код = файл.читать() 73 | имя_вывода = имя_ввода.заменить(".py", ".birp") 74 | результат, дерево = преобразовать(код, парсер, преобразователь, имя_ввода) 75 | если результат: 76 | если аргументы.дерево: 77 | с открыть(имя_вывода + ".tree", "w") как файл: 78 | файл.писать(дерево.приятный()) 79 | с открыть(имя_вывода, "w") как файл: 80 | файл.писать(результат) 81 | вывод(ф"{имя_ввода} -> {имя_вывода}") 82 | иначе: 83 | вывод(ф"{имя_ввода} невозможно преобразовать в {имя_вывода}") 84 | 85 | иначе: 86 | пока Да: 87 | вывод("Интерактивный Реверсивный Борп!") 88 | код = ввод(">>> ") 89 | результат = строка(преобразовать(код, вычислительный_парсер, преобразователь, "<ввод>")) 90 | вывод(ф"=> {результат}") 91 | вывод(ф"= {представление(вычислить(результат))}") 92 | 93 | если __имя__ == "__main__": 94 | main() -------------------------------------------------------------------------------- /birp/reverse/__main__.py: -------------------------------------------------------------------------------- 1 | from ..translated import translated as переводы 2 | from ..argparser import аргпарсер 3 | import os 4 | from ..codegen_utils import БазовыйГенератор, получить_парсеры, загрузить, transform 5 | 6 | здесь = os.path.abspath(os.path.dirname(__file__)) 7 | ( 8 | parser, 9 | вычислительный_парсер, 10 | ) = получить_парсеры(os.path.join(здесь, "birp-rev.lark")) 11 | переводы = {value: key for key, value in переводы.items()} 12 | 13 | 14 | class Кодогенератор(БазовыйГенератор): 15 | вычислительный_парсер = вычислительный_парсер 16 | переводы = переводы 17 | keys = { 18 | "if": "если", 19 | "else": "иначе", 20 | "elif": "иначеесли", 21 | "while": "пока", 22 | "for": "для", 23 | "try": "попробовать", 24 | "except": "кроме", 25 | "finally": "финально", 26 | "from": "из", 27 | "in": "в", 28 | "import": "подключить", 29 | "as": "как", 30 | "pass": "пропустить", 31 | "continue": "продолжить", 32 | "break": "остановить", 33 | "return": "вернуть", 34 | "def": "объявить", 35 | "raise": "бросить", 36 | "assert": "убедиться", 37 | "class": "класс", 38 | "yield": "выдать", 39 | "await": "ожидать", 40 | "async": "асинхронно", 41 | "lambda": "лямбда", 42 | "None": "Ничего", 43 | "True": "Да", 44 | "False": "Нет", 45 | "and": "и", 46 | "or": "или", 47 | "not": "не", 48 | "is": "есть", 49 | "del": "удалить", 50 | "match": "соответствие", 51 | "case": "случай", 52 | "global": "глобально", 53 | "nonlocal": "нелокально", 54 | "with": "с", 55 | "orig_in": "in", 56 | "orig_is": "is", 57 | "orig_not": "not", 58 | } 59 | 60 | 61 | преобразователь = Кодогенератор() 62 | 63 | 64 | def main(): 65 | args = аргпарсер.parse_args() 66 | if args.переводы: 67 | with open(args.переводы) as file: 68 | переводы_пользователя = загрузить(file) 69 | 70 | переводы.update(переводы_пользователя) 71 | if args.файлы: 72 | for arg in args.файлы: 73 | имя_ввода = arg 74 | with open(имя_ввода) as file: 75 | code = file.read() 76 | 77 | имя_вывода = имя_ввода.replace(".py", ".birp") 78 | ( 79 | result, 80 | tree, 81 | ) = transform(code, parser, преобразователь, имя_ввода) 82 | if result: 83 | if args.tree: 84 | with open(имя_вывода + ".tree", "w") as file: 85 | file.write(tree.pretty()) 86 | 87 | with open(имя_вывода, "w") as file: 88 | file.write(result) 89 | 90 | print(f"{имя_ввода} -> {имя_вывода}") 91 | else: 92 | print(f"{имя_ввода} невозможно преобразовать в {имя_вывода}") 93 | else: 94 | while True: 95 | print("Интерактивный Реверсивный Борп!") 96 | code = input(">>> ") 97 | result = str( 98 | transform(code, вычислительный_парсер, преобразователь, "<ввод>") 99 | ) 100 | print(f"=> {result}") 101 | print(f"= {repr(eval(result))}") 102 | 103 | 104 | if __name__ == "__main__": 105 | main() 106 | -------------------------------------------------------------------------------- /birp/reverse/birp-rev.lark: -------------------------------------------------------------------------------- 1 | // Python 3 grammar for Lark 2 | 3 | // This grammar should parse all python 3.x code successfully. 4 | 5 | // Adapted from: https://docs.python.org/3/reference/grammar.html 6 | 7 | // Start symbols for the grammar: 8 | // single_input is a single interactive statement; 9 | // file_input is a module or sequence of commands read from an input file; 10 | // eval_input is the input for the eval() functions. 11 | // NB: compound_stmt in single_input is followed by extra NEWLINE! 12 | // 13 | 14 | single_input: _NEWLINE | simple_stmt | compound_stmt _NEWLINE 15 | file_input: (_NEWLINE | stmt)* 16 | eval_input: testlist _NEWLINE* 17 | 18 | decorator: "@" dotted_name [ "(" [arguments] ")" ] _NEWLINE 19 | decorators: decorator+ 20 | decorated: decorators (classdef | funcdef | async_funcdef) 21 | 22 | async_funcdef: "async" funcdef 23 | funcdef: "def" name "(" [parameters] ")" ["->" test] ":" suite 24 | 25 | parameters: paramvalue ("," paramvalue)* ["," SLASH] ["," [starparams | kwparams]] 26 | | starparams 27 | | kwparams 28 | 29 | SLASH: "/" // Otherwise the it will completely disappear and it will be undisguisable in the result 30 | starparams: "*" typedparam? ("," paramvalue)* ["," kwparams] 31 | kwparams: "**" typedparam ","? 32 | 33 | ?paramvalue: typedparam ("=" test)? 34 | ?typedparam: name (":" test)? 35 | 36 | 37 | lambdef: "lambda" [lambda_params] ":" test 38 | lambdef_nocond: "lambda" [lambda_params] ":" test_nocond 39 | lambda_params: lambda_paramvalue ("," lambda_paramvalue)* ["," [lambda_starparams | lambda_kwparams]] 40 | | lambda_starparams 41 | | lambda_kwparams 42 | ?lambda_paramvalue: name ("=" test)? 43 | lambda_starparams: "*" [name] ("," lambda_paramvalue)* ["," [lambda_kwparams]] 44 | lambda_kwparams: "**" name ","? 45 | 46 | 47 | ?stmt: simple_stmt | compound_stmt 48 | ?simple_stmt: small_stmt (";" small_stmt)* [";"] _NEWLINE 49 | ?small_stmt: (expr_stmt | assign_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 50 | ?expr_stmt: testlist_star_expr 51 | ?assign_stmt: annassign | augassign | assign | "(" assigment_expression ")" -> assigment_expression_top_level 52 | assigment_expression: name ":=" test 53 | 54 | annassign: testlist_star_expr ":" test ["=" test] 55 | assign: testlist_star_expr ("=" (yield_expr|testlist_star_expr))+ 56 | augassign: testlist_star_expr augassign_op (yield_expr|testlist) 57 | !augassign_op: "+=" | "-=" | "*=" | "@=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "**=" | "//=" 58 | ?testlist_star_expr: test_or_star_expr 59 | | test_or_star_expr ("," test_or_star_expr)+ ","? -> tuple 60 | | test_or_star_expr "," -> tuple 61 | 62 | // For normal and annotated assignments, additional restrictions enforced by the interpreter 63 | del_stmt: "del" exprlist 64 | pass_stmt: "pass" 65 | ?flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 66 | break_stmt: "break" 67 | continue_stmt: "continue" 68 | return_stmt: "return" [testlist] 69 | ?yield_stmt: yield_expr 70 | raise_stmt: "raise" [test ["from" test]] 71 | ?import_stmt: import_name | import_from 72 | import_name: "import" dotted_as_names 73 | // note below: the ("." | "...") is necessary because "..." is tokenized as ELLIPSIS 74 | IMPORT_STAR: "*" 75 | import_from: "from" (dots? dotted_name | dots) "import" (IMPORT_STAR | "(" import_as_names ")" | import_as_names) 76 | !dots: "."+ 77 | import_as_name: name ["as" name] 78 | dotted_as_name: dotted_name ["as" name] 79 | import_as_names: import_as_name ("," import_as_name)* [","] 80 | dotted_as_names: dotted_as_name ("," dotted_as_name)* 81 | dotted_name: name ("." name)* 82 | global_stmt: "global" name ("," name)* 83 | nonlocal_stmt: "nonlocal" name ("," name)* 84 | assert_stmt: "assert" test ["," test] 85 | 86 | ?compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt | match_stmt 87 | async_stmt: "async" (funcdef | with_stmt | for_stmt) 88 | if_stmt: "if" test ":" suite elifs ["else" ":" suite] 89 | elifs: elif_* 90 | elif_: "elif" test ":" suite 91 | while_stmt: "while" test ":" suite ["else" ":" suite] 92 | for_stmt: "for" exprlist "in" testlist ":" suite ["else" ":" suite] 93 | try_stmt: "try" ":" suite except_clauses ["else" ":" suite] [finally_] 94 | | "try" ":" suite finally_ -> try_finally 95 | finally_: "finally" ":" suite 96 | except_clauses: except_clause+ 97 | except_clause: "except" [test ["as" name]] ":" suite 98 | 99 | with_stmt: "with" with_items ":" suite 100 | with_items: with_item ("," with_item)* 101 | with_item: test ["as" expr] 102 | // NB compile.c makes sure that the default except clause is last 103 | suite: simple_stmt -> one_line_suite 104 | | _NEWLINE _INDENT stmt+ _DEDENT 105 | 106 | ?test: or_test ("if" or_test "else" test)? 107 | | lambdef 108 | ?test_nocond: or_test | lambdef_nocond 109 | 110 | ?or_test: and_test ("or" and_test)* 111 | ?and_test: not_test_ ("and" not_test_)* 112 | ?not_test_: "not" not_test_ -> not_test 113 | | comparison 114 | ?comparison: expr (comp_op expr)* 115 | star_expr: "*" expr 116 | 117 | ?star_named_expr: star_expr | named_expression 118 | ?star_named_exprs: star_named_expr ("," star_named_expr)* ","? 119 | ?named_expression: assigment_expression | test 120 | 121 | ?expr: or_expr 122 | ?or_expr: xor_expr ("|" xor_expr)* 123 | ?xor_expr: and_expr ("^" and_expr)* 124 | ?and_expr: shift_expr ("&" shift_expr)* 125 | ?shift_expr: arith_expr (_shift_op arith_expr)* 126 | ?arith_expr: term (_add_op term)* 127 | ?term: factor (_mul_op factor)* 128 | ?factor: _unary_op factor | power 129 | 130 | !_unary_op: "+"|"-"|"~" 131 | !_add_op: "+"|"-" 132 | !_shift_op: "<<"|">>" 133 | !_mul_op: "*"|"@"|"/"|"%"|"//" 134 | // <> isn't actually a valid comparison operator in Python. It's here for the 135 | // sake of a __future__ import described in PEP 401 (which really works :-) 136 | !comp_op: "<"|">"|"=="|">="|"<="|"<>"|"!="|"in"|"not" "in"|"is"|"is" "not" 137 | 138 | ?power: await_expr ("**" factor)? 139 | ?await_expr: AWAIT? atom_expr 140 | AWAIT: "await" 141 | 142 | match_stmt: "match" test ":" _NEWLINE _INDENT case+ _DEDENT 143 | 144 | case: "case" pattern ["if" test] ":" suite 145 | 146 | ?pattern: sequence_item_pattern "," _sequence_pattern -> sequence_pattern 147 | | as_pattern 148 | ?as_pattern: or_pattern ("as" name)? 149 | ?or_pattern: closed_pattern ("|" closed_pattern)* 150 | ?closed_pattern: literal_pattern 151 | | name -> capture_pattern 152 | | "_" -> any_pattern 153 | | attr_pattern 154 | | "(" as_pattern ")" 155 | | "[" _sequence_pattern "]" -> sequence_pattern 156 | | "(" (sequence_item_pattern "," _sequence_pattern)? ")" -> sequence_pattern 157 | | "{" (mapping_item_pattern ("," mapping_item_pattern)* ","?)?"}" -> mapping_pattern 158 | | "{" (mapping_item_pattern ("," mapping_item_pattern)* ",")? "**" name ","? "}" -> mapping_star_pattern 159 | | class_pattern 160 | 161 | literal_pattern: inner_literal_pattern 162 | 163 | ?inner_literal_pattern: "None" -> const_none 164 | | "True" -> const_true 165 | | "False" -> const_false 166 | | STRING -> string 167 | | number 168 | 169 | attr_pattern: name ("." name)+ -> value 170 | 171 | name_or_attr_pattern: name ("." name)* -> value 172 | 173 | mapping_item_pattern: (literal_pattern|attr_pattern) ":" as_pattern 174 | 175 | _sequence_pattern: (sequence_item_pattern ("," sequence_item_pattern)* ","?)? 176 | ?sequence_item_pattern: as_pattern 177 | | "*" name -> star_pattern 178 | 179 | class_pattern: name_or_attr_pattern "(" [arguments_pattern ","?] ")" 180 | arguments_pattern: pos_arg_pattern ["," keyws_arg_pattern] 181 | | keyws_arg_pattern -> no_pos_arguments 182 | 183 | pos_arg_pattern: as_pattern ("," as_pattern)* 184 | keyws_arg_pattern: keyw_arg_pattern ("," keyw_arg_pattern)* 185 | keyw_arg_pattern: name "=" as_pattern 186 | 187 | ?atom_expr: atom_expr "(" [arguments] ")" -> funccall 188 | | atom_expr "[" subscriptlist "]" -> getitem 189 | | atom_expr "." name -> getattr 190 | | atom 191 | 192 | ?atom: "(" yield_expr ")" -> parens 193 | | "(" _tuple_inner? ")" -> tuple 194 | | "(" comprehension{test_or_star_expr} ")" -> tuple_comprehension 195 | | "[" _testlist_comp? "]" -> list 196 | | "[" comprehension{test_or_star_expr} "]" -> list_comprehension 197 | | "{" _dict_exprlist? "}" -> dict 198 | | "{" comprehension{key_value} "}" -> dict_comprehension 199 | | "{" _set_exprlist "}" -> set 200 | | "{" comprehension{test} "}" -> set_comprehension 201 | | name -> var 202 | | number 203 | | string_concat 204 | | "(" test ")" -> parens 205 | | "..." -> ellipsis 206 | | "None" -> const_none 207 | | "True" -> const_true 208 | | "False" -> const_false 209 | 210 | 211 | ?string_concat: string+ 212 | 213 | _testlist_comp: test | _tuple_inner 214 | _tuple_inner: test_or_star_expr (("," test_or_star_expr)+ [","] | ",") 215 | 216 | 217 | ?test_or_star_expr: star_named_expr 218 | 219 | ?subscriptlist: subscript 220 | | subscript (("," subscript)+ [","] | ",") -> subscript_tuple 221 | ?subscript: test | ([test] ":" [test] [sliceop]) -> slice 222 | sliceop: ":" [test] 223 | ?exprlist: (expr|star_expr) 224 | | (expr|star_expr) (("," (expr|star_expr))+ [","]|",") 225 | ?testlist: test | testlist_tuple 226 | testlist_tuple: test (("," test)+ [","] | ",") 227 | du_dct: "**" expr 228 | _dict_exprlist: (key_value | du_dct) ("," (key_value | du_dct))* [","] 229 | 230 | key_value: test ":" test 231 | 232 | _set_exprlist: test_or_star_expr ("," test_or_star_expr)* [","] 233 | 234 | classdef: "class" name ["(" [arguments] ")"] ":" suite 235 | 236 | arguments: argvalue ("," argvalue)* ("," [ starargs | kwargs])? 237 | | starargs 238 | | kwargs 239 | | comprehension{test} 240 | 241 | starargs: stararg ("," stararg)* ("," argvalue)* ["," kwargs] 242 | stararg: "*" test 243 | kwargs: "**" test [","] 244 | 245 | ?argvalue: test ("=" test)? 246 | 247 | 248 | comprehension{comp_result}: comp_result comp_fors [comp_if] 249 | comp_fors: comp_for+ 250 | comp_for: [ASYNC] "for" exprlist "in" or_test 251 | ASYNC: "async" 252 | ?comp_if: "if" test_nocond 253 | 254 | // not used in grammar, but may appear in "node" passed from Parser to Compiler 255 | encoding_decl: name 256 | 257 | yield_expr: "yield" [testlist] 258 | | "yield" "from" test -> yield_from 259 | 260 | number: DEC_NUMBER -> integer 261 | | HEX_NUMBER -> integer 262 | | BIN_NUMBER -> integer 263 | | OCT_NUMBER -> integer 264 | | FLOAT_NUMBER -> float 265 | | IMAG_NUMBER -> complex 266 | string: STRING | LONG_STRING 267 | 268 | // Other terminals 269 | 270 | _NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+ 271 | 272 | %ignore /[\t \f]+/ // WS 273 | %ignore /\\[\t \f]*\r?\n/ // LINE_CONT 274 | %ignore COMMENT 275 | %declare _INDENT _DEDENT 276 | 277 | 278 | // Python terminals 279 | 280 | !name: NAME | "match" | "case" 281 | NAME: /[^\W\d]\w*/ 282 | COMMENT: /#[^\n]*/ 283 | 284 | STRING : /[ubfюбф]?r?("(?!"").*?(?" test] ":" suite 24 | 25 | parameters: paramvalue ("," paramvalue)* ["," SLASH] ["," [starparams | kwparams]] 26 | | starparams 27 | | kwparams 28 | 29 | SLASH: "/" // Otherwise the it will completely disappear and it will be undisguisable in the result 30 | starparams: "*" typedparam? ("," paramvalue)* ["," kwparams] 31 | kwparams: "**" typedparam ","? 32 | 33 | ?paramvalue: typedparam ("=" test)? 34 | ?typedparam: NAME (":" test)? 35 | 36 | 37 | lambdef: "лямбда" [lambda_params] ":" test 38 | lambdef_nocond: "лямбда" [lambda_params] ":" test_nocond 39 | lambda_params: lambda_paramvalue ("," lambda_paramvalue)* ["," [lambda_starparams | lambda_kwparams]] 40 | | lambda_starparams 41 | | lambda_kwparams 42 | ?lambda_paramvalue: NAME ("=" test)? 43 | lambda_starparams: "*" [NAME] ("," lambda_paramvalue)* ["," [lambda_kwparams]] 44 | lambda_kwparams: "**" NAME ","? 45 | 46 | 47 | ?stmt: simple_stmt | compound_stmt 48 | ?simple_stmt: small_stmt (";" small_stmt)* [";"] _NEWLINE 49 | ?small_stmt: (expr_stmt | assign_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 50 | ?expr_stmt: testlist_star_expr 51 | ?assign_stmt: annassign | augassign | assign | "(" assigment_expression ")" -> assigment_expression_top_level 52 | assigment_expression: NAME ":=" test 53 | 54 | annassign: testlist_star_expr ":" test ["=" test] 55 | assign: testlist_star_expr ("=" (yield_expr|testlist_star_expr))+ 56 | augassign: testlist_star_expr augassign_op (yield_expr|testlist) 57 | !augassign_op: "+=" | "-=" | "*=" | "@=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "**=" | "//=" 58 | ?testlist_star_expr: test_or_star_expr 59 | | test_or_star_expr ("," test_or_star_expr)+ ","? -> tuple 60 | | test_or_star_expr "," -> tuple 61 | 62 | // For normal and annotated assignments, additional restrictions enforced by the interpreter 63 | del_stmt: "удалить" exprlist 64 | pass_stmt: "пропустить" 65 | ?flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 66 | break_stmt: "остановить" 67 | continue_stmt: "продолжить" 68 | return_stmt: "вернуть" [testlist] 69 | ?yield_stmt: yield_expr 70 | raise_stmt: "бросить" [test ["из" test]] 71 | ?import_stmt: import_name | import_from 72 | import_name: "подключить" dotted_as_names 73 | // note below: the ("." | "...") is necessary because "..." is tokenized as ELLIPSIS 74 | IMPORT_STAR: "*" 75 | import_from: "из" (dots? dotted_name | dots) "подключить" (IMPORT_STAR | "(" import_as_names ")" | import_as_names) 76 | !dots: "."+ 77 | import_as_name: name ["как" name] 78 | dotted_as_name: dotted_name ["как" name] 79 | import_as_names: import_as_name ("," import_as_name)* [","] 80 | dotted_as_names: dotted_as_name ("," dotted_as_name)* 81 | dotted_name: name ("." name)* 82 | global_stmt: "глобально" name ("," name)* 83 | nonlocal_stmt: "нелокально" name ("," name)* 84 | assert_stmt: "убедиться" test ["," test] 85 | 86 | ?compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt | match_stmt 87 | async_stmt: "асинхронно" (funcdef | with_stmt | for_stmt) 88 | if_stmt: "если" test ":" suite elifs ["иначе" ":" suite] 89 | elifs: elif_* 90 | elif_: "иначеесли" test ":" suite 91 | while_stmt: "пока" test ":" suite ["иначе" ":" suite] 92 | for_stmt: "для" exprlist "в" testlist ":" suite ["иначе" ":" suite] 93 | try_stmt: "попробовать" ":" suite except_clauses ["иначе" ":" suite] [finally_] 94 | | "попробовать" ":" suite finally_ -> try_finally 95 | finally_: "финально" ":" suite 96 | except_clauses: except_clause+ 97 | except_clause: "кроме" [test ["как" name]] ":" suite 98 | 99 | with_stmt: "с" with_items ":" suite 100 | with_items: with_item ("," with_item)* 101 | with_item: test ["как" expr] 102 | // NB compile.c makes sure that the default except clause is last 103 | suite: simple_stmt -> one_line_suite 104 | | _NEWLINE _INDENT stmt+ _DEDENT 105 | 106 | ?test: or_test ("если" or_test "иначе" test)? 107 | | lambdef 108 | ?test_nocond: or_test | lambdef_nocond 109 | 110 | ?or_test: and_test ("или" and_test)* 111 | ?and_test: not_test_ ("и" not_test_)* 112 | ?not_test_: "не" not_test_ -> not_test 113 | | comparison 114 | ?comparison: expr (comp_op expr)* 115 | star_expr: "*" expr 116 | 117 | ?star_named_expr: star_expr | named_expression 118 | ?star_named_exprs: star_named_expr ("," star_named_expr)* ","? 119 | ?named_expression: assigment_expression | test 120 | 121 | ?expr: or_expr 122 | ?or_expr: xor_expr ("|" xor_expr)* 123 | ?xor_expr: and_expr ("^" and_expr)* 124 | ?and_expr: shift_expr ("&" shift_expr)* 125 | ?shift_expr: arith_expr (_shift_op arith_expr)* 126 | ?arith_expr: term (_add_op term)* 127 | ?term: factor (_mul_op factor)* 128 | ?factor: _unary_op factor | power 129 | 130 | !_unary_op: "+"|"-"|"~" 131 | !_add_op: "+"|"-" 132 | !_shift_op: "<<"|">>" 133 | !_mul_op: "*"|"@"|"/"|"%"|"//" 134 | // <> isn't actually a valid comparison operator in Python. It's here for the 135 | // sake of a __future__ import described in PEP 401 (which really works :-) 136 | !comp_op: "<"|">"|"=="|">="|"<="|"<>"|"!="|"в"|"не" "в"|"есть"|"есть" "не" 137 | 138 | ?power: await_expr ("**" factor)? 139 | ?await_expr: AWAIT? atom_expr 140 | AWAIT: "ожидать" 141 | 142 | match_stmt: "соответствие" test ":" _NEWLINE _INDENT case+ _DEDENT 143 | 144 | case: "случай" pattern ["если" test] ":" suite 145 | 146 | ?pattern: sequence_item_pattern "," _sequence_pattern -> sequence_pattern 147 | | as_pattern 148 | ?as_pattern: or_pattern ("как" name)? 149 | ?or_pattern: closed_pattern ("|" closed_pattern)* 150 | ?closed_pattern: literal_pattern 151 | | name -> capture_pattern 152 | | "_" -> any_pattern 153 | | attr_pattern 154 | | "(" as_pattern ")" 155 | | "[" _sequence_pattern "]" -> sequence_pattern 156 | | "(" (sequence_item_pattern "," _sequence_pattern)? ")" -> sequence_pattern 157 | | "{" (mapping_item_pattern ("," mapping_item_pattern)* ","?)?"}" -> mapping_pattern 158 | | "{" (mapping_item_pattern ("," mapping_item_pattern)* ",")? "**" NAME ","? "}" -> mapping_star_pattern 159 | | class_pattern 160 | 161 | literal_pattern: inner_literal_pattern 162 | 163 | ?inner_literal_pattern: "Ничего" -> const_none 164 | | "Да" -> const_true 165 | | "Нет" -> const_false 166 | | STRING -> string 167 | | number 168 | 169 | attr_pattern: name ("." name)+ -> value 170 | 171 | name_or_attr_pattern: name ("." name)* -> value 172 | 173 | mapping_item_pattern: (literal_pattern | attr_pattern) ":" as_pattern 174 | 175 | _sequence_pattern: (sequence_item_pattern ("," sequence_item_pattern)* ","?)? 176 | ?sequence_item_pattern: as_pattern 177 | | "*" name -> star_pattern 178 | 179 | class_pattern: name_or_attr_pattern "(" [arguments_pattern ","?] ")" 180 | arguments_pattern: pos_arg_pattern ["," keyws_arg_pattern] 181 | | keyws_arg_pattern -> no_pos_arguments 182 | 183 | pos_arg_pattern: as_pattern ("," as_pattern)* 184 | keyws_arg_pattern: keyw_arg_pattern ("," keyw_arg_pattern)* 185 | keyw_arg_pattern: name "=" as_pattern 186 | 187 | 188 | ?atom_expr: atom_expr "(" [arguments] ")" -> funccall 189 | | atom_expr "[" subscriptlist "]" -> getitem 190 | | atom_expr "." name -> getattr 191 | | atom 192 | 193 | ?atom: "(" yield_expr ")" -> parens 194 | | "(" _tuple_inner? ")" -> tuple 195 | | "(" comprehension{test_or_star_expr} ")" -> tuple_comprehension 196 | | "[" _testlist_comp? "]" -> list 197 | | "[" comprehension{test_or_star_expr} "]" -> list_comprehension 198 | | "{" _dict_exprlist? "}" -> dict 199 | | "{" comprehension{key_value} "}" -> dict_comprehension 200 | | "{" _set_exprlist "}" -> set 201 | | "{" comprehension{test} "}" -> set_comprehension 202 | | name -> var 203 | | number 204 | | string_concat 205 | | "(" test ")" -> parens 206 | | "..." -> ellipsis 207 | | "Ничего" -> const_none 208 | | "Да" -> const_true 209 | | "Нет" -> const_false 210 | 211 | 212 | ?string_concat: string+ 213 | 214 | _testlist_comp: test | _tuple_inner 215 | _tuple_inner: test_or_star_expr (("," test_or_star_expr)+ [","] | ",") 216 | 217 | 218 | ?test_or_star_expr: star_named_expr 219 | 220 | ?subscriptlist: subscript 221 | | subscript (("," subscript)+ [","] | ",") -> subscript_tuple 222 | ?subscript: test | ([test] ":" [test] [sliceop]) -> slice 223 | sliceop: ":" [test] 224 | ?exprlist: (expr|star_expr) 225 | | (expr|star_expr) (("," (expr|star_expr))+ [","]|",") 226 | ?testlist: test | testlist_tuple 227 | testlist_tuple: test (("," test)+ [","] | ",") 228 | du_dct: "**" expr 229 | _dict_exprlist: (key_value | du_dct) ("," (key_value | du_dct))* [","] 230 | 231 | key_value: test ":" test 232 | 233 | _set_exprlist: test_or_star_expr ("," test_or_star_expr)* [","] 234 | 235 | classdef: "класс" name ["(" [arguments] ")"] ":" suite 236 | 237 | arguments: argvalue ("," argvalue)* ("," [ starargs | kwargs])? 238 | | starargs 239 | | kwargs 240 | | comprehension{test} 241 | 242 | starargs: stararg ("," stararg)* ("," argvalue)* ["," kwargs] 243 | stararg: "*" test 244 | kwargs: "**" test [","] 245 | 246 | ?argvalue: test ("=" test)? 247 | 248 | comprehension{comp_result}: comp_result comp_fors [comp_if] 249 | comp_fors: comp_for+ 250 | comp_for: [ASYNC] "для" exprlist "в" or_test 251 | ASYNC: "асинхронно" 252 | ?comp_if: "если" test_nocond 253 | 254 | // not used in grammar, but may appear in "node" passed from Parser to Compiler 255 | encoding_decl: name 256 | 257 | yield_expr: "выдать" [testlist] 258 | | "выдать" "из" test -> yield_from 259 | 260 | number: DEC_NUMBER -> integer 261 | | HEX_NUMBER -> integer 262 | | BIN_NUMBER -> integer 263 | | OCT_NUMBER -> integer 264 | | FLOAT_NUMBER -> float 265 | | IMAG_NUMBER -> complex 266 | string: STRING | LONG_STRING 267 | 268 | // Other terminals 269 | 270 | _NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+ 271 | 272 | %ignore /[\t \f]+/ // WS 273 | %ignore /\\[\t \f]*\r?\n/ // LINE_CONT 274 | %ignore COMMENT 275 | %declare _INDENT _DEDENT 276 | 277 | 278 | // Python terminals 279 | 280 | !name: NAME | "соответствие" | "случай" 281 | NAME: /[^\W\d]\w*/ 282 | COMMENT: /#[^\n]*/ 283 | 284 | STRING : /[ubfюбф]?[рr]?("(?!"").*?(? str: 26 | if text.strip(): 27 | return text.replace("\n", "\n" + символ_отступа) 28 | return "" 29 | 30 | 31 | def с_пробелом(text): 32 | return " ".join(["", text]) 33 | 34 | 35 | def пропустить_пустые(tokens): 36 | return filter(None, tokens) 37 | 38 | 39 | def проверка_интерполяции(token, курсор): 40 | вложенность = 0 41 | for смещение in range(курсор, len(token)): 42 | if token[смещение] == "{": 43 | вложенность += 1 44 | elif token[смещение] == "}": 45 | вложенность -= 1 46 | if вложенность < 0: 47 | return смещение 48 | else: 49 | raise ValueError("Неверная интерполяция") 50 | 51 | 52 | def получить_парсеры(файл_грамматики): 53 | with open(файл_грамматики) as file: 54 | грамматика = file.read() 55 | 56 | args = dict(parser="lalr", postlex=ОтступникПитона(), maybe_placeholders=True) 57 | parser = Жаворонок(грамматика, start="file_input", **args) 58 | вычислительный_парсер = Жаворонок(грамматика, start="eval_input", **args) 59 | return parser, вычислительный_парсер 60 | 61 | 62 | class БазовыйГенератор(Преобразователь): 63 | keys = { 64 | "if": "if", 65 | "else": "else", 66 | "elif": "elif", 67 | "while": "while", 68 | "for": "for", 69 | "try": "try", 70 | "except": "except", 71 | "finally": "finally", 72 | "from": "from", 73 | "in": "in", 74 | "import": "import", 75 | "as": "as", 76 | "pass": "pass", 77 | "continue": "continue", 78 | "break": "break", 79 | "return": "return", 80 | "def": "def", 81 | "raise": "raise", 82 | "assert": "assert", 83 | "class": "class", 84 | "yield": "yield", 85 | "await": "await", 86 | "async": "async", 87 | "lambda": "lambda", 88 | "None": "None", 89 | "True": "True", 90 | "False": "False", 91 | "and": "and", 92 | "or": "or", 93 | "not": "not", 94 | "is": "is", 95 | "del": "del", 96 | "match": "match", 97 | "case": "case", 98 | "global": "global", 99 | "nonlocal": "nonlocal", 100 | "with": "with", 101 | "orig_in": "в", 102 | "orig_is": "есть", 103 | "orig_not": "не", 104 | } 105 | 106 | def file_input(self, tokens): 107 | return "\n".join(tokens) 108 | 109 | def eval_input(self, tokens): 110 | return tokens[0] 111 | 112 | def __getattr__(self, attr): 113 | if attr.isupper(): 114 | return lambda token: token 115 | print(f"!!! {attr} не преобразован !!!") 116 | return lambda tokens: " ".join(tokens) 117 | 118 | def one_line_suite(self, tokens): 119 | return tokens[0] 120 | 121 | def suite(self, tokens): 122 | return добавить_отступ("\n" + "\n".join(tokens)) 123 | 124 | def if_stmt(self, tokens): 125 | иначе_ = (f"\n{self.keys['else']}:" + tokens[3]) if tokens[3] else "" 126 | return "".join( 127 | [ 128 | self.keys["if"], 129 | " ", 130 | tokens[0], 131 | ":", 132 | tokens[1], 133 | *(["\n", tokens[2]] if tokens[2] else ""), 134 | иначе_, 135 | ] 136 | ) 137 | 138 | def elifs(self, tokens): 139 | return "\n".join(tokens) 140 | 141 | def elif_(self, tokens): 142 | return "".join([self.keys["elif"], " ", tokens[0], ":", tokens[1]]) 143 | 144 | def import_from(self, tokens): 145 | return " ".join( 146 | [self.keys["from"], "".join(tokens[:-1]), self.keys["import"], tokens[-1]] 147 | ) 148 | 149 | def import_name(self, tokens): 150 | return " ".join([self.keys["import"], tokens[0]]) 151 | 152 | def dotted_as_name(self, tokens): 153 | return f" {self.keys['as']} ".join(пропустить_пустые(tokens)) 154 | 155 | def import_as_name(self, tokens): 156 | return f" {self.keys['as']} ".join(пропустить_пустые(tokens)) 157 | 158 | def dotted_as_names(self, tokens): 159 | return ", ".join(tokens) 160 | 161 | def import_as_names(self, tokens): 162 | return ", ".join(tokens) 163 | 164 | def dotted_name(self, tokens): 165 | return ".".join(tokens) 166 | 167 | def dots(self, tokens): 168 | return "".join(tokens) 169 | 170 | def global_stmt(self, tokens): 171 | return " ".join([self.keys["global"], ", ".join(tokens)]) 172 | 173 | def nonlocal_stmt(self, tokens): 174 | return " ".join([self.keys["nonlocal"], ", ".join(tokens)]) 175 | 176 | def assert_stmt(self, tokens): 177 | return " ".join([self.keys["assert"], ", ".join(пропустить_пустые(tokens))]) 178 | 179 | def raise_stmt(self, tokens): 180 | return " ".join( 181 | [ 182 | self.keys["raise"], 183 | f" {self.keys['from']} ".join(пропустить_пустые(tokens)), 184 | ] 185 | ) 186 | 187 | def while_stmt(self, tokens): 188 | иначе_ = (f"\n{self.keys['else']}:" + tokens[2]) if tokens[2] else "" 189 | return "".join([self.keys["while"], " ", tokens[0], ":", tokens[1], иначе_]) 190 | 191 | def continue_stmt(self, tokens): 192 | return self.keys["continue"] 193 | 194 | def break_stmt(self, tokens): 195 | return self.keys["break"] 196 | 197 | def assign(self, tokens): 198 | return " = ".join(tokens) 199 | 200 | def slice(self, tokens): 201 | tokens = [x or "" for x in tokens] 202 | return "{}:{}{}".format(*tokens) 203 | 204 | def sliceop(self, tokens): 205 | if tokens[0]: 206 | return f":{tokens[0]}" 207 | 208 | def var(self, tokens): 209 | return tokens[0] 210 | 211 | def test(self, tokens): 212 | return "{0} {3} {1} {4} {2}".format(*tokens, self.keys["if"], self.keys["else"]) 213 | 214 | def star_expr(self, tokens): 215 | return f"*{tokens[0]}" 216 | 217 | def funccall(self, tokens): 218 | return f"{tokens[0]}({tokens[1] or ''})" 219 | 220 | def arguments(self, tokens): 221 | return ", ".join(пропустить_пустые(tokens)) 222 | 223 | def argvalue(self, tokens): 224 | return "=".join(tokens) 225 | 226 | def starargs(self, tokens): 227 | return ", ".join(пропустить_пустые(tokens)) 228 | 229 | def stararg(self, tokens): 230 | return f"*{tokens[0]}" 231 | 232 | def kwargs(self, tokens): 233 | return f"**{tokens[0]}" 234 | 235 | def subscript_tuple(self, tokens): 236 | return ", ".join(tokens) 237 | 238 | def parens(self, tokens): 239 | return f"({tokens[0]})" 240 | 241 | def getitem(self, tokens): 242 | return f"{tokens[0]}[{tokens[1]}]" 243 | 244 | def getattr(self, tokens): 245 | return ".".join(tokens) 246 | 247 | def name(self, tokens): 248 | return self.переводы.get(tokens[0], tokens[0]) 249 | 250 | def annassign(self, tokens): 251 | if tokens[2]: 252 | return f"{tokens[0]}: {tokens[1]} = {tokens[2]}" 253 | return f"{tokens[0]}: {tokens[1]}" 254 | 255 | def augassign(self, tokens): 256 | return " ".join(tokens) 257 | 258 | def augassign_op(self, tokens): 259 | return tokens[0] 260 | 261 | def float(self, tokens): 262 | return tokens[0] 263 | 264 | def integer(self, tokens): 265 | return tokens[0] 266 | 267 | def complex(self, tokens): 268 | return tokens[0] 269 | 270 | def string(self, tokens): 271 | return tokens[0] 272 | 273 | def with_stmt(self, tokens): 274 | return "{2} {0}:{1}\n".format(*tokens, self.keys["with"]) 275 | 276 | def with_items(self, tokens): 277 | return ", ".join(tokens) 278 | 279 | def with_item(self, tokens): 280 | return f" {self.keys['as']} ".join(пропустить_пустые(tokens)) 281 | 282 | def STRING(self, token): 283 | if token[0] in "юбфрЮБФР": 284 | token = "ubfrUBFR"["юбфрЮБФР".index(token[0])] + token[1:] 285 | if token.lower().startswith("f"): 286 | result = [] 287 | интерполяции = [0] 288 | скобка = False 289 | index = 0 290 | while index < len(token): 291 | if скобка: 292 | скобка = False 293 | index += 1 294 | continue 295 | if token[index] == "{": 296 | if token[index + 1 : index + 2] == "{": 297 | скобка = True 298 | continue 299 | if not скобка: 300 | интерполяции.append(index + 1) 301 | index = проверка_интерполяции(token, index + 1) 302 | интерполяции.append(index) 303 | скобка = False 304 | index += 1 305 | интерполяции.append(len(token)) 306 | for index in range(len(интерполяции[:-1])): 307 | часть = token[интерполяции[index] : интерполяции[index + 1]] 308 | if index % 2: 309 | часть_ = часть.split(":") 310 | if len(часть_) > 1: 311 | ( 312 | *часть, 313 | фмт, 314 | ) = часть_ 315 | else: 316 | часть = часть_ 317 | фмт = None 318 | часть = self.transform( 319 | self.вычислительный_парсер.parse(":".join(часть)) 320 | ) 321 | result.append(":".join(пропустить_пустые([часть, фмт]))) 322 | else: 323 | result.append(часть) 324 | return "".join(result) 325 | return token 326 | 327 | def funcdef(self, tokens): 328 | return f"\n{self.keys['def']} {tokens[0]}({tokens[1] or ''}){' -> '.join(['', *пропустить_пустые([tokens[2]])])}: {tokens[3]}" 329 | 330 | def parameters(self, tokens): 331 | return ", ".join(пропустить_пустые(tokens)) 332 | 333 | def paramvalue(self, tokens): 334 | return " = ".join(tokens) 335 | 336 | def const_true(self, tokens): 337 | return self.keys["True"] 338 | 339 | def const_false(self, tokens): 340 | return self.keys["False"] 341 | 342 | def const_none(self, tokens): 343 | return self.keys["None"] 344 | 345 | def starparams(self, tokens): 346 | return "".join(["*", ", ".join(пропустить_пустые(tokens))]) 347 | 348 | def kwparams(self, tokens): 349 | return f"**{tokens[0]}" 350 | 351 | def typedparam(self, tokens): 352 | return ": ".join(tokens) 353 | 354 | def return_stmt(self, tokens): 355 | return " ".join([self.keys["return"], *пропустить_пустые(tokens)]) 356 | 357 | def lambdef(self, tokens): 358 | return "".join( 359 | [self.keys["lambda"], с_пробелом(tokens[0] or ""), ": ", tokens[1]] 360 | ) 361 | 362 | def lambda_params(self, tokens): 363 | return ", ".join(пропустить_пустые(tokens)) 364 | 365 | def lambda_paramvalue(self, tokens): 366 | return "=".join(пропустить_пустые(tokens)) 367 | 368 | def lambda_starparams(self, tokens): 369 | return ", ".join(пропустить_пустые(tokens)) 370 | 371 | def lambda_kwparams(self, tokens): 372 | return f"**{tokens[0]}" 373 | 374 | def or_test(self, tokens): 375 | return f" {self.keys['or']} ".join(tokens) 376 | 377 | def and_test(self, tokens): 378 | return f" {self.keys['and']} ".join(tokens) 379 | 380 | def not_test(self, tokens): 381 | return f"{self.keys['not']} {tokens[0]}" 382 | 383 | def or_expr(self, tokens): 384 | return " | ".join(tokens) 385 | 386 | def xor_expr(self, tokens): 387 | return " ^ ".join(tokens) 388 | 389 | def and_expr(self, tokens): 390 | return " & ".join(tokens) 391 | 392 | def power(self, tokens): 393 | return " ** ".join(tokens) 394 | 395 | def comparison(self, tokens): 396 | return " ".join(tokens) 397 | 398 | def comp_op(self, tokens): 399 | return ( 400 | tokens[0] 401 | .replace(self.keys["orig_not"], self.keys["not"]) 402 | .replace(self.keys["orig_is"], self.keys["is"]) 403 | .replace(self.keys["orig_in"], self.keys["in"]) 404 | ) 405 | 406 | def многострочный_список(self, tokens): 407 | return добавить_отступ("\n" + ",\n".join(tokens)) 408 | 409 | def list(self, tokens): 410 | if len(tokens) > 5 or any("\n" in token for token in tokens): 411 | return "".join(["[", self.многострочный_список(tokens), "\n]"]) 412 | return f"[{', '.join(tokens)}]" 413 | 414 | def dict(self, tokens): 415 | if len(tokens) > 5 or any("\n" in token for token in tokens): 416 | return "".join(["{", self.многострочный_список(tokens), "\n}"]) 417 | return f"{{{', '.join(tokens)}}}" 418 | 419 | def set(self, tokens): 420 | if len(tokens) > 5 or any("\n" in token for token in tokens): 421 | return "".join(["{", self.многострочный_список(tokens), "\n}"]) 422 | return f"{{{', '.join(tokens)}}}" 423 | 424 | def dict_comprehension(self, tokens): 425 | return f"{{{tokens[0]}}}" 426 | 427 | def shift_expr(self, tokens): 428 | return " ".join(tokens) 429 | 430 | def set_comprehension(self, tokens): 431 | return f"{{{tokens[0]}}}" 432 | 433 | def list_comprehension(self, tokens): 434 | return f"[{tokens[0]}]" 435 | 436 | def classdef(self, tokens): 437 | return "".join( 438 | [ 439 | f"\n{self.keys['class']} ", 440 | tokens[0], 441 | f"({tokens[1]})" if tokens[1] else "", 442 | ":", 443 | tokens[2], 444 | "\n", 445 | ] 446 | ) 447 | 448 | def comprehension(self, tokens): 449 | return " ".join(пропустить_пустые(tokens)) 450 | 451 | def comp_fors(self, tokens): 452 | return " ".join(tokens) 453 | 454 | def comp_for(self, tokens): 455 | return " ".join( 456 | [ 457 | *пропустить_пустые([f"{self.keys['async']} " if tokens[0] else ""]), 458 | self.keys["for"], 459 | tokens[1], 460 | self.keys["in"], 461 | tokens[2], 462 | ] 463 | ) 464 | 465 | def comp_if(self, tokens): 466 | return f"{self.keys['if']} {tokens[0]}" 467 | 468 | def exprlist(self, tokens): 469 | return ", ".join(tokens) 470 | 471 | def yield_expr(self, tokens): 472 | return " ".join([self.keys["yield"], *пропустить_пустые(tokens)]) 473 | 474 | def yield_from(self, tokens): 475 | return " ".join([self.keys["yield"], self.keys["from"], tokens[0]]) 476 | 477 | def arith_expr(self, tokens): 478 | return " ".join(пропустить_пустые(tokens)) 479 | 480 | def key_value(self, tokens): 481 | return ": ".join(tokens) 482 | 483 | def decorator(self, tokens): 484 | return "".join(["@", tokens[0], f"({tokens[1]})" if tokens[1] else ""]) 485 | 486 | def decorators(self, tokens): 487 | return "\n".join(tokens) 488 | 489 | def decorated(self, tokens): 490 | return "".join(["\n", *tokens]) 491 | 492 | def tuple(self, tokens): 493 | if tokens: 494 | if len(tokens) > 5 or all("\n" in token for token in tokens): 495 | return "".join(["(", self.многострочный_список(tokens), ",\n)"]) 496 | return f"({', '.join(tokens)}, )" 497 | return "()" 498 | 499 | def testlist_tuple(self, tokens): 500 | return ", ".join(tokens) 501 | 502 | def simple_stmt(self, tokens): 503 | return "; ".join(tokens) 504 | 505 | def for_stmt(self, tokens): 506 | else_ = (f"\n{self.keys['else']}:" + tokens[3]) if tokens[3] else "" 507 | return "".join( 508 | [ 509 | f"{self.keys['for']} ", 510 | tokens[0], 511 | f" {self.keys['in']} ", 512 | tokens[1], 513 | ":", 514 | tokens[2], 515 | else_, 516 | ] 517 | ) 518 | 519 | def term(self, tokens): 520 | return " ".join(tokens) 521 | 522 | def factor(self, tokens): 523 | return " ".join(tokens) 524 | 525 | def async_funcdef(self, tokens): 526 | return " ".join([f"\n{self.keys['async']}", tokens[0][1:]]) 527 | 528 | def del_stmt(self, tokens): 529 | return f"{self.keys['del']} {tokens[0]}" 530 | 531 | def await_expr(self, tokens): 532 | return f"{self.keys['await']} {tokens[1]}" 533 | 534 | def try_stmt(self, tokens): 535 | else_ = (f"{self.keys['else']}:" + tokens[2]) if tokens[2] else "" 536 | return "\n".join( 537 | пропустить_пустые( 538 | [ 539 | f"{self.keys['try']}: {tokens[0]}", 540 | tokens[1], 541 | else_ if tokens[2] else "", 542 | tokens[3], 543 | " ", 544 | ] 545 | ) 546 | ) 547 | 548 | def try_finally(self, tokens): 549 | return f"\n{self.keys['try']}: {tokens[0]}\n{tokens[1]}" 550 | 551 | def except_clauses(self, tokens): 552 | return "\n".join(tokens) 553 | 554 | def except_clause(self, tokens): 555 | if len(tokens) == 2: 556 | tokens.insert(1, None) 557 | return "".join( 558 | [ 559 | self.keys["except"], 560 | с_пробелом( 561 | f" {self.keys['as']} ".join(пропустить_пустые(tokens[:2])) 562 | ).rstrip(), 563 | ":", 564 | tokens[2], 565 | ] 566 | ) 567 | 568 | def finally_(self, tokens): 569 | return f"{self.keys['finally']}: {tokens[0]}" 570 | 571 | def du_dct(self, tokens): 572 | return f"**{tokens[0]}" 573 | 574 | def pass_stmt(self, tokens): 575 | return self.keys["pass"] 576 | 577 | def async_stmt(self, tokens): 578 | nl = "\n" 579 | return f"{self.keys['async']} {tokens[0].lstrip(nl)}" 580 | 581 | def match_stmt(self, tokens): 582 | ветки = добавить_отступ("\n" + "\n".join(tokens[1:])) 583 | return f"{self.keys['match']} {tokens[0]}:{ветки}" 584 | 585 | def case(self, tokens): 586 | ( 587 | шаблон, 588 | условие, 589 | блок, 590 | ) = tokens 591 | if условие: 592 | условие = f" {self.keys['if']} {условие}" 593 | else: 594 | условие = "" 595 | блок = добавить_отступ(f"\n{блок}") 596 | return f"{self.keys['case']} {шаблон}{условие}:{блок}" 597 | 598 | def sequence_pattern(self, tokens): 599 | return f"({', '.join(tokens)})" 600 | 601 | def as_pattern(self, tokens): 602 | return f"{tokens[0]} {self.keys['as']} {tokens[1]}" 603 | 604 | def or_pattern(self, tokens): 605 | return " | ".join(tokens) 606 | 607 | def literal_pattern(self, tokens): 608 | return tokens[0] 609 | 610 | def mapping_pattern(self, tokens): 611 | return f"{{{', '.join(tokens)}}}" 612 | 613 | def mapping_pattern_item(self, tokens): 614 | return ":".join(tokens) 615 | 616 | def star_pattern(self, tokens): 617 | return f"*{tokens}" 618 | 619 | def mapping_star_pattern(self, tokens): 620 | if len(tokens) == 1: 621 | tokens.insert(0, "") 622 | return f"{{{' ** '.join(tokens)}}}" 623 | 624 | def attr_pattern(self, tokens): 625 | return ".".join(tokens) 626 | 627 | def pos_arg_pattern(self, tokens): 628 | return ", ".join(tokens) 629 | 630 | def keyws_arg_pattern(self, tokens): 631 | return ", ".join(tokens) 632 | 633 | def keyw_arg_pattern(self, tokens): 634 | return " = ".join(tokens) 635 | 636 | def arguments_pattern(self, tokens): 637 | return ", ".join(tokens) 638 | 639 | def no_pos_arguments(self, tokens): 640 | return tokens[0] 641 | 642 | 643 | def репорт(code, error, имя_файла="модуль"): 644 | номер_строки = error.line 645 | размер = 3 646 | верхний_размер = min(номер_строки - 1, размер) 647 | строки = code.split("\n")[номер_строки - верхний_размер - 1 : номер_строки + размер] 648 | отступ = error.column 649 | ширина_номера_строки = len( 650 | str(max(номер_строки - верхний_размер, номер_строки + размер)) 651 | ) 652 | return "\n".join( 653 | [ 654 | *[ 655 | f"{str(i + номер_строки - верхний_размер):>{ширина_номера_строки}} | {line}" 656 | for i, line in enumerate(строки[: верхний_размер + 1]) 657 | ], 658 | f"{'':>{ширина_номера_строки + 1}}:{'':>{отступ}}^ Ошибка: Непредвиденный {error.token.type}", 659 | *[ 660 | f"{str(i + 1 + номер_строки):>{ширина_номера_строки}} | {line}" 661 | for i, line in enumerate(строки[верхний_размер + 1 :]) 662 | ], 663 | ] 664 | ) 665 | 666 | 667 | def transform(code, parser, преобразователь, имя_файла="модуль"): 668 | try: 669 | tree = parser.parse(code + "\n") 670 | except НепредвиденныйТокен as error: 671 | print(репорт(code, error, имя_файла)) 672 | return None, None 673 | 674 | try: 675 | result = преобразователь.transform(tree) 676 | except Exception: 677 | print(format_exc()) 678 | return None, tree 679 | 680 | return result, tree 681 | -------------------------------------------------------------------------------- /birp/codegen_utils.birp: -------------------------------------------------------------------------------- 1 | из lark подключить ( 2 | Lark как Жаворонок, 3 | Transformer как Преобразователь, 4 | UnexpectedToken как НепредвиденныйТокен, 5 | UnexpectedCharacters как НепредвиденныеСимволы, 6 | UnexpectedEOF как НепредвиденныйКФ 7 | ) 8 | из lark.indenter подключить Indenter как Отступник 9 | из json подключить load как загрузить 10 | из traceback подключить format_exc 11 | 12 | 13 | класс ОтступникПитона(Отступник): 14 | NL_type = "_NEWLINE" 15 | OPEN_PAREN_types = ["LPAR", "LSQB", "LBRACE"] 16 | CLOSE_PAREN_types = ["RPAR", "RSQB", "RBRACE"] 17 | INDENT_type = "_INDENT" 18 | DEDENT_type = "_DEDENT" 19 | tab_len = 8 20 | 21 | 22 | символ_отступа: строка = " " 23 | 24 | 25 | объявить добавить_отступ(текст: строка) -> строка: 26 | если текст.обрезать(): 27 | вернуть текст.заменить("\n", "\n" + символ_отступа) 28 | вернуть "" 29 | 30 | 31 | объявить с_пробелом(текст): 32 | вернуть " ".соединить(["", текст]) 33 | 34 | 35 | объявить пропустить_пустые(токены): 36 | вернуть фильтр(Ничего, токены) 37 | 38 | 39 | объявить проверка_интерполяции(токен, курсор): 40 | вложенность = 0 41 | для смещение в диапазон(курсор, длина(токен)): 42 | если токен[смещение] == "{": 43 | вложенность += 1 44 | иначеесли токен[смещение] == "}": 45 | вложенность -= 1 46 | если вложенность < 0: 47 | вернуть смещение 48 | иначе: 49 | бросить ОшибкаЗначения("Неверная интерполяция") 50 | 51 | 52 | объявить получить_парсеры(файл_грамматики): 53 | с открыть(файл_грамматики) как файл: 54 | грамматика = файл.читать() 55 | 56 | аргументы = словарь( 57 | парсер="lalr", 58 | postlex=ОтступникПитона(), 59 | maybe_placeholders=Да, 60 | ) 61 | 62 | парсер = Жаворонок( 63 | грамматика, 64 | start="file_input", 65 | **аргументы, 66 | ) 67 | 68 | вычислительный_парсер = Жаворонок( 69 | грамматика, 70 | start="eval_input", 71 | **аргументы, 72 | ) 73 | вернуть парсер, вычислительный_парсер 74 | 75 | 76 | класс БазовыйГенератор(Преобразователь): 77 | ключи = { 78 | "if": "if", 79 | "else": "else", 80 | "elif": "elif", 81 | "while": "while", 82 | "for": "for", 83 | "try": "try", 84 | "except": "except", 85 | "finally": "finally", 86 | "from": "from", 87 | "in": "in", 88 | "import": "import", 89 | "as": "as", 90 | "pass": "pass", 91 | "continue": "continue", 92 | "break": "break", 93 | "return": "return", 94 | "def": "def", 95 | "raise": "raise", 96 | "assert": "assert", 97 | "class": "class", 98 | "yield": "yield", 99 | "await": "await", 100 | "async": "async", 101 | "lambda": "lambda", 102 | "None": "None", 103 | "True": "True", 104 | "False": "False", 105 | "and": "and", 106 | "or": "or", 107 | "not": "not", 108 | "is": "is", 109 | "del": "del", 110 | "match": "match", 111 | "case": "case", 112 | "global": "global", 113 | "nonlocal": "nonlocal", 114 | "with": "with", 115 | "orig_in": "в", 116 | "orig_is": "есть", 117 | "orig_not": "не" 118 | } 119 | 120 | объявить file_input(сам, токены): 121 | вернуть "\n".соединить(токены) 122 | 123 | объявить eval_input(сам, токены): 124 | вернуть токены[0] 125 | 126 | объявить __полатр__(сам, атрибут): 127 | если атрибут.есть_верхний(): 128 | вернуть лямбда токен: токен 129 | 130 | вывод(ф"!!! {атрибут} не преобразован !!!") 131 | вернуть лямбда токены: " ".соединить(токены) 132 | 133 | объявить one_line_suite(сам, токены): 134 | вернуть токены[0] 135 | 136 | объявить suite(сам, токены): 137 | вернуть добавить_отступ("\n" + "\n".соединить(токены)) 138 | 139 | объявить if_stmt(сам, токены): 140 | иначе_ = (ф"\n{сам.ключи['else']}:" + токены[3]) если токены[3] иначе "" 141 | 142 | вернуть "".соединить( 143 | [ 144 | сам.ключи['if'], " ", 145 | токены[0], 146 | ":", 147 | токены[1], 148 | *(["\n", токены[2]] если токены[2] иначе ""), 149 | иначе_, 150 | ] 151 | ) 152 | 153 | объявить elifs(сам, токены): 154 | вернуть "\n".соединить(токены) 155 | 156 | объявить elif_(сам, токены): 157 | вернуть "".соединить([сам.ключи['elif'], " ", токены[0], ":", токены[1]]) 158 | 159 | объявить import_from(сам, токены): 160 | вернуть " ".соединить([ 161 | сам.ключи['from'], 162 | "".соединить(токены[:-1]), 163 | сам.ключи['import'], 164 | токены[-1] 165 | ]) 166 | 167 | объявить import_name(сам, токены): 168 | вернуть " ".соединить([сам.ключи['import'], токены[0]]) 169 | 170 | объявить dotted_as_name(сам, токены): 171 | вернуть f" {сам.ключи['as']} ".соединить(пропустить_пустые(токены)) 172 | 173 | объявить import_as_name(сам, токены): 174 | вернуть f" {сам.ключи['as']} ".соединить(пропустить_пустые(токены)) 175 | 176 | объявить dotted_as_names(сам, токены): 177 | вернуть ", ".соединить(токены) 178 | 179 | объявить import_as_names(сам, токены): 180 | вернуть ", ".соединить(токены) 181 | 182 | объявить dotted_name(сам, токены): 183 | вернуть ".".соединить(токены) 184 | 185 | объявить dots(сам, токены): 186 | вернуть "".соединить(токены) 187 | 188 | объявить global_stmt(сам, токены): 189 | вернуть " ".соединить([сам.ключи['global'], ", ".соединить(токены)]) 190 | 191 | объявить nonlocal_stmt(сам, токены): 192 | вернуть " ".соединить([сам.ключи['nonlocal'], ", ".соединить(токены)]) 193 | 194 | объявить assert_stmt(сам, токены): 195 | вернуть " ".соединить([сам.ключи['assert'], ", ".соединить(пропустить_пустые(токены))]) 196 | 197 | объявить raise_stmt(сам, токены): 198 | вернуть " ".соединить([сам.ключи['raise'], f" {сам.ключи['from']} ".join(пропустить_пустые(токены))]) 199 | 200 | объявить while_stmt(сам, токены): 201 | иначе_ = (ф"\n{сам.ключи['else']}:" + токены[2]) если токены[2] иначе "" 202 | 203 | вернуть "".соединить( 204 | [ 205 | сам.ключи['while'], 206 | " ", 207 | токены[0], 208 | ":", 209 | токены[1], 210 | иначе_, 211 | ] 212 | ) 213 | 214 | объявить continue_stmt(сам, токены): 215 | вернуть сам.ключи['continue'] 216 | 217 | объявить break_stmt(сам, токены): 218 | вернуть сам.ключи['break'] 219 | 220 | объявить assign(сам, токены): 221 | вернуть " = ".соединить(токены) 222 | 223 | объявить slice(сам, токены): 224 | токены = [x или '' для x в токены] 225 | вернуть "{}:{}{}".формат(*токены) 226 | 227 | объявить sliceop(сам, токены): 228 | если токены[0]: 229 | вернуть ф":{токены[0]}" 230 | 231 | объявить var(сам, токены): 232 | вернуть токены[0] 233 | 234 | объявить test(сам, токены): 235 | вернуть "{0} {3} {1} {4} {2}".формат(*токены, сам.ключи['if'], сам.ключи['else']) 236 | 237 | объявить star_expr(сам, токены): 238 | вернуть ф"*{токены[0]}" 239 | 240 | объявить funccall(сам, токены): 241 | вернуть ф"{токены[0]}({токены[1] или ''})" 242 | 243 | объявить arguments(сам, токены): 244 | вернуть ", ".соединить(пропустить_пустые(токены)) 245 | 246 | объявить argvalue(сам, токены): 247 | вернуть "=".соединить(токены) 248 | 249 | объявить starargs(сам, токены): 250 | вернуть ", ".соединить(пропустить_пустые(токены)) 251 | 252 | объявить stararg(сам, токены): 253 | вернуть ф"*{токены[0]}" 254 | 255 | объявить kwargs(сам, токены): 256 | вернуть ф"**{токены[0]}" 257 | 258 | объявить subscript_tuple(сам, токены): 259 | вернуть ", ".соединить(токены) 260 | 261 | объявить parens(self, tokens): 262 | вернуть ф"({tokens[0]})" 263 | 264 | объявить getitem(сам, токены): 265 | вернуть ф"{токены[0]}[{токены[1]}]" 266 | 267 | объявить getattr(сам, токены): 268 | вернуть ".".соединить(токены) 269 | 270 | объявить name(сам, токены): 271 | вернуть сам.переводы.получить(токены[0], токены[0]) 272 | 273 | объявить annassign(сам, токены): 274 | если токены[2]: 275 | вернуть ф"{токены[0]}: {токены[1]} = {токены[2]}" 276 | вернуть ф"{токены[0]}: {токены[1]}" 277 | 278 | объявить augassign(сам, токены): 279 | вернуть " ".соединить(токены) 280 | 281 | объявить augassign_op(сам, токены): 282 | вернуть токены[0] 283 | 284 | объявить float(сам, токены): 285 | вернуть токены[0] 286 | 287 | объявить integer(сам, токены): 288 | вернуть токены[0] 289 | 290 | объявить complex(сам, токены): 291 | вернуть токены[0] 292 | 293 | объявить string(сам, токены): 294 | вернуть токены[0] 295 | 296 | объявить with_stmt(сам, токены): 297 | вернуть "{2} {0}:{1}\n".формат(*токены, сам.ключи['with']) 298 | 299 | объявить with_items(сам, токены): 300 | вернуть ", ".соединить(токены) 301 | 302 | объявить with_item(сам, токены): 303 | вернуть ф" {сам.ключи['as']} ".соединить(пропустить_пустые(токены)) 304 | 305 | объявить STRING(сам, токен): 306 | если токен[0] в "юбфрЮБФР": 307 | токен = "ubfrUBFR"["юбфрЮБФР".индекс(токен[0])] + токен[1:] 308 | 309 | если токен.нижний().начинаетсяс("f"): 310 | результат = [] 311 | интерполяции = [0] 312 | скобка = Нет 313 | индекс = 0 314 | 315 | # выделяем части, которые нужно преобразовать 316 | пока индекс < длина(токен): 317 | если скобка: 318 | скобка = Нет 319 | индекс += 1 320 | продолжить 321 | если токен[индекс] == "{": 322 | если токен[индекс + 1:индекс + 2] == "{": 323 | скобка = Да 324 | продолжить 325 | если не скобка: 326 | интерполяции.добавить(индекс + 1) 327 | индекс = проверка_интерполяции(токен, индекс + 1) 328 | интерполяции.добавить(индекс) 329 | скобка = Нет 330 | индекс += 1 331 | интерполяции.добавить(длина(токен)) 332 | 333 | для индекс в диапазон(длина(интерполяции[:-1])): 334 | часть = токен[интерполяции[индекс]: интерполяции[индекс + 1]] 335 | если индекс % 2: 336 | часть_ = часть.разделить(":") 337 | если длина(часть_) > 1: 338 | *часть, фмт = часть_ 339 | иначе: 340 | часть = часть_ 341 | фмт = Ничего 342 | часть = сам.преобразовать(сам.вычислительный_парсер.парсить(":".соединить(часть))) 343 | результат.добавить( 344 | ":".соединить( 345 | пропустить_пустые([часть, фмт]) 346 | ) 347 | ) 348 | иначе: 349 | результат.добавить(часть) 350 | вернуть "".соединить(результат) 351 | 352 | вернуть токен 353 | 354 | объявить funcdef(сам, токены): 355 | вернуть ф"\n{сам.ключи['def']} {токены[0]}({токены[1] или ''}){' -> '.соединить(['', *пропустить_пустые([токены[2]])])}: {токены[3]}" 356 | 357 | объявить parameters(сам, токены): 358 | вернуть ", ".соединить(пропустить_пустые(токены)) 359 | 360 | объявить paramvalue(сам, токены): 361 | вернуть " = ".соединить(токены) 362 | 363 | объявить const_true(сам, токены): 364 | вернуть сам.ключи['True'] 365 | 366 | объявить const_false(сам, токены): 367 | вернуть сам.ключи['False'] 368 | 369 | объявить const_none(сам, токены): 370 | вернуть сам.ключи['None'] 371 | 372 | объявить starparams(сам, токены): 373 | вернуть "".соединить(["*", ", ".соединить(пропустить_пустые(токены))]) 374 | 375 | объявить kwparams(сам, токены): 376 | вернуть ф"**{токены[0]}" 377 | 378 | объявить typedparam(сам, токены): 379 | вернуть ": ".соединить(токены) 380 | 381 | объявить return_stmt(сам, токены): 382 | вернуть " ".соединить([сам.ключи['return'], *пропустить_пустые(токены)]) 383 | 384 | объявить lambdef(сам, токены): 385 | вернуть "".соединить([сам.ключи['lambda'], с_пробелом(токены[0] или ""), ": ", токены[1]]) 386 | 387 | объявить lambda_params(сам, токены): 388 | вернуть ", ".соединить(пропустить_пустые(токены)) 389 | 390 | объявить lambda_paramvalue(сам, токены): 391 | вернуть "=".соединить(пропустить_пустые(токены)) 392 | 393 | объявить lambda_starparams(сам, токены): 394 | вернуть ", ".соединить(пропустить_пустые(токены)) 395 | 396 | объявить lambda_kwparams(сам, токены): 397 | вернуть ф"**{токены[0]}" 398 | 399 | объявить or_test(сам, токены): 400 | вернуть ф" {сам.ключи['or']} ".соединить(токены) 401 | 402 | объявить and_test(сам, токены): 403 | вернуть ф" {сам.ключи['and']} ".соединить(токены) 404 | 405 | объявить not_test(сам, токены): 406 | вернуть ф"{сам.ключи['not']} {токены[0]}" 407 | 408 | объявить or_expr(сам, токены): 409 | вернуть " | ".соединить(токены) 410 | 411 | объявить xor_expr(сам, токены): 412 | вернуть " ^ ".соединить(токены) 413 | 414 | объявить and_expr(сам, токены): 415 | вернуть " & ".соединить(токены) 416 | 417 | объявить power(сам, токены): 418 | вернуть " ** ".соединить(токены) 419 | 420 | объявить comparison(сам, токены): 421 | вернуть " ".соединить(токены) 422 | 423 | объявить comp_op(сам, токены): 424 | вернуть токены[0].replace( 425 | сам.ключи['orig_not'], сам.ключи['not'] 426 | ).replace( 427 | сам.ключи['orig_is'], сам.ключи['is'] 428 | ).replace( 429 | сам.ключи['orig_in'], сам.ключи['in'] 430 | ) 431 | 432 | объявить многострочный_список(сам, токены): 433 | вернуть добавить_отступ("\n" + ",\n".соединить(токены)) 434 | 435 | объявить list(сам, токены): 436 | если длина(токены) > 5 или любой("\n" в токен для токен в токены): 437 | вернуть "".соединить(["[", сам.многострочный_список(токены), "\n]"]) 438 | вернуть ф"[{', '.соединить(токены)}]" 439 | 440 | объявить dict(сам, токены): 441 | если длина(токены) > 5 или любой("\n" в токен для токен в токены): 442 | вернуть "".соединить(["{", сам.многострочный_список(токены), "\n}"]) 443 | вернуть ф"{{{', '.соединить(токены)}}}" 444 | 445 | объявить set(сам, токены): 446 | если длина(токены) > 5 или любой("\n" в токен для токен в токены): 447 | вернуть "".соединить(["{", сам.многострочный_список(токены), "\n}"]) 448 | вернуть ф"{{{', '.соединить(токены)}}}" 449 | 450 | объявить dict_comprehension(сам, токены): 451 | вернуть ф"{{{токены[0]}}}" 452 | 453 | объявить shift_expr(сам, токены): 454 | вернуть " ".соединить(токены) 455 | 456 | объявить set_comprehension(сам, токены): 457 | вернуть ф"{{{токены[0]}}}" 458 | 459 | объявить list_comprehension(сам, токены): 460 | вернуть ф"[{токены[0]}]" 461 | 462 | объявить classdef(сам, токены): 463 | вернуть "".соединить( 464 | [ 465 | f"\n{сам.ключи['class']} ", 466 | токены[0], 467 | ф"({токены[1]})" если токены[1] иначе "", 468 | ":", 469 | токены[2], 470 | "\n", 471 | ] 472 | ) 473 | 474 | объявить comprehension(сам, токены): 475 | вернуть " ".соединить(пропустить_пустые(токены)) 476 | 477 | объявить comp_fors(сам, токены): 478 | вернуть " ".соединить(токены) 479 | 480 | объявить comp_for(сам, токены): 481 | вернуть " ".соединить( 482 | [ 483 | *пропустить_пустые([f"{сам.ключи['async']} " если токены[0] иначе ""]), 484 | сам.ключи['for'], 485 | токены[1], 486 | сам.ключи['in'], 487 | токены[2], 488 | ] 489 | ) 490 | 491 | объявить comp_if(сам, токены): 492 | вернуть ф"{сам.ключи['if']} {токены[0]}" 493 | 494 | объявить exprlist(сам, токены): 495 | вернуть ", ".соединить(токены) 496 | 497 | объявить yield_expr(сам, токены): 498 | вернуть " ".соединить([сам.ключи['yield'], *пропустить_пустые(токены)]) 499 | 500 | объявить yield_from(сам, токены): 501 | вернуть " ".соединить([сам.ключи['yield'], сам.ключи['from'], токены[0]]) 502 | 503 | объявить arith_expr(сам, токены): 504 | вернуть " ".соединить(пропустить_пустые(токены)) 505 | 506 | объявить key_value(сам, токены): 507 | вернуть ": ".соединить(токены) 508 | 509 | объявить decorator(сам, токены): 510 | вернуть "".соединить(["@", токены[0], ф"({токены[1]})" если токены[1] иначе ""]) 511 | 512 | объявить decorators(сам, токены): 513 | вернуть "\n".соединить(токены) 514 | 515 | объявить decorated(сам, токены): 516 | вернуть "".соединить(["\n", *токены]) 517 | 518 | объявить tuple(сам, токены): 519 | если токены: 520 | если длина(токены) > 5 или все("\n" в токен для токен в токены): 521 | вернуть "".соединить(["(", сам.многострочный_список(токены), ",\n)"]) 522 | вернуть ф"({', '.соединить(токены)}, )" 523 | вернуть "()" 524 | 525 | объявить testlist_tuple(сам, токены): 526 | вернуть ", ".соединить(токены) 527 | 528 | объявить simple_stmt(сам, токены): 529 | вернуть "; ".соединить(токены) 530 | 531 | объявить for_stmt(сам, токены): 532 | else_ = (f"\n{сам.ключи['else']}:" + токены[3]) если токены[3] иначе "" 533 | 534 | вернуть "".соединить([f"{сам.ключи['for']} ", токены[0], f" {сам.ключи['in']} ", токены[1], ":", токены[2], else_]) 535 | 536 | объявить term(сам, токены): 537 | вернуть " ".соединить(токены) 538 | 539 | объявить factor(сам, токены): 540 | вернуть " ".соединить(токены) 541 | 542 | объявить async_funcdef(сам, токены): 543 | вернуть " ".соединить([f"\n{сам.ключи['async']}", токены[0][1:]]) 544 | 545 | объявить del_stmt(сам, токены): 546 | вернуть ф"{сам.ключи['del']} {токены[0]}" 547 | 548 | объявить await_expr(сам, токены): 549 | вернуть ф"{сам.ключи['await']} {токены[1]}" 550 | 551 | объявить try_stmt(сам, токены): 552 | else_ = (ф"{сам.ключи['else']}:" + токены[2]) если токены[2] иначе "" 553 | вернуть "\n".соединить(пропустить_пустые([ф"{сам.ключи['try']}: {токены[0]}", токены[1], else_ если токены[2] иначе "", токены[3], " "])) 554 | 555 | объявить try_finally(сам, токены): 556 | вернуть ф"\n{сам.ключи['try']}: {токены[0]}\n{токены[1]}" 557 | 558 | объявить except_clauses(сам, токены): 559 | вернуть "\n".соединить(токены) 560 | 561 | объявить except_clause(сам, токены): 562 | если длина(токены) == 2: 563 | токены.вставить(1, Ничего) 564 | вернуть "".соединить([ 565 | сам.ключи['except'], 566 | с_пробелом( 567 | f" {сам.ключи['as']} ".соединить( 568 | пропустить_пустые(токены[:2]) 569 | ) 570 | ).побрезать(), ":", токены[2]]) 571 | 572 | объявить finally_(сам, токены): 573 | вернуть ф"{сам.ключи['finally']}: {токены[0]}" 574 | 575 | объявить du_dct(сам, токены): 576 | вернуть ф"**{токены[0]}" 577 | 578 | объявить pass_stmt(сам, токены): 579 | вернуть сам.ключи['pass'] 580 | 581 | объявить async_stmt(сам, токены): 582 | nl = "\n" 583 | вернуть ф"{сам.ключи['async']} {токены[0].лобрезать(nl)}" 584 | 585 | объявить match_stmt(сам, токены): 586 | ветки = добавить_отступ("\n" + "\n".соединить(токены[1:])) 587 | вернуть ф"{сам.ключи['match']} {токены[0]}:{ветки}" 588 | 589 | объявить case(сам, токены): 590 | шаблон, условие, блок = токены 591 | 592 | если условие: 593 | условие = ф" {сам.ключи['if']} {условие}" 594 | иначе: 595 | условие = "" 596 | 597 | блок = добавить_отступ(f"\n{блок}") 598 | вернуть ф"{сам.ключи['case']} {шаблон}{условие}:{блок}" 599 | 600 | объявить sequence_pattern(сам, токены): 601 | вернуть ф"({', '.соединить(токены)})" 602 | 603 | объявить as_pattern(сам, токены): 604 | вернуть ф"{токены[0]} {сам.ключи['as']} {токены[1]}" 605 | 606 | объявить or_pattern(сам, токены): 607 | вернуть " | ".соединить(токены) 608 | 609 | объявить literal_pattern(сам, токены): 610 | вернуть токены[0] 611 | 612 | объявить mapping_pattern(сам, токены): 613 | вернуть ф"{{{', '.соединить(токены)}}}" 614 | 615 | объявить mapping_pattern_item(сам, токены): 616 | вернуть ":".соединить(токены) 617 | 618 | объявить star_pattern(сам, токены): 619 | вернуть ф"*{токены}" 620 | 621 | объявить mapping_star_pattern(сам, токены): 622 | если длина(токены) == 1: 623 | токены.вставить(0, "") 624 | вернуть ф"{{{' ** '.соединить(токены)}}}" 625 | 626 | объявить attr_pattern(сам, токены): 627 | вернуть ".".соединить(токены) 628 | 629 | объявить pos_arg_pattern(сам, токены): 630 | вернуть ", ".соединить(токены) 631 | 632 | объявить keyws_arg_pattern(сам, токены): 633 | вернуть ", ".соединить(токены) 634 | 635 | объявить keyw_arg_pattern(сам, токены): 636 | вернуть " = ".соединить(токены) 637 | 638 | объявить arguments_pattern(сам, токены): 639 | вернуть ", ".соединить(токены) 640 | 641 | объявить no_pos_arguments(сам, токены): 642 | вернуть токены[0] 643 | 644 | 645 | объявить репорт(код, ошибка, имя_файла="модуль"): 646 | номер_строки = ошибка.line 647 | размер = 3 648 | верхний_размер = минимум(номер_строки - 1, размер) 649 | 650 | строки = код.разделить("\n")[номер_строки - верхний_размер - 1: номер_строки + размер] 651 | отступ = ошибка.column 652 | ширина_номера_строки = длина( 653 | строка( 654 | максимум( 655 | номер_строки - верхний_размер, 656 | номер_строки + размер 657 | ) 658 | ) 659 | ) 660 | вернуть ( 661 | "\n".соединить([ 662 | *[ 663 | f"{строка(i + номер_строки - верхний_размер):>{ширина_номера_строки}} | {line}" 664 | для i, line в пронумеровать(строки[:верхний_размер + 1]) 665 | ], 666 | f"{'':>{ширина_номера_строки + 1}}:{'':>{отступ}}^ Ошибка: Непредвиденный {ошибка.токен.тип}", 667 | *[ 668 | f"{строка(i + 1 + номер_строки):>{ширина_номера_строки}} | {line}" 669 | для i, line в пронумеровать(строки[верхний_размер + 1:]) 670 | ], 671 | ]) 672 | ) 673 | 674 | объявить преобразовать(код, парсер, преобразователь, имя_файла="модуль"): 675 | попробовать: 676 | дерево = парсер.парсить(код + "\n") 677 | кроме НепредвиденныйТокен как ошибка: 678 | вывод(репорт(код, ошибка, имя_файла)) 679 | вернуть Ничего, Ничего 680 | 681 | попробовать: 682 | результат = преобразователь.преобразовать(дерево) 683 | кроме Исключение: 684 | вывод(format_exc()) 685 | вернуть Ничего, дерево 686 | 687 | вернуть результат, дерево --------------------------------------------------------------------------------