Препроцессинг кода в Python

Препроцессинг — предварительная обработка исходного файла перед передачей его компилятору. Чаще всего препроцессор вводит расширенный синтаксис, чтобы упростить или оптимизировать какие-то задачи, а на выходе генерирует код, понятный компилятору.

Если вы имели дело с фронтенд-разработкой, должно быть слышали о coffee, babel, sass, less, postcss — всем этом многообразии. Еще препроцессор в явном виде есть в C — он обеспечивает работу таких штук, как include, define, ifdef.

Надеюсь, у нас такие инструменты до масштабов современного фронтенда не дойдут. Однако, идея достойна внимания. Можно бесконечно расширять язык, вводить шорткаты и DSL на уровне приложения, причем совершенно прозрачно. Забавы ради я попробовал добавить в python простой аналог сишной директивы define.

# coding: сpreprocessor
# define True False

def main():
    if (1 > 0) is True:
        print("true branch")
    else:
        print("else branch")

Вся магия в первой строке. Она указывает интерпретатору на кодировку текущего файла. Чтобы это заработало, зарегистрируем новую кодировку с собственной функцией-декодером. Кроме, собственно, декодинга она заменит все упоминания True на False в исходном файле.

В точке входа в приложение, первым делом нужно импортнуть cpreprocessor — он зарегистрирует кодек-препроцессор. Теперь при импорте, файлы, которые начинаются с coding: cpreprocessor будут обрабатываться нашей функцией.

import cpreprocessor  # noqa
from app import main

if __name__ == '__main__':
    main()  # will print "false branch"

Воспринимайте это как proof of concept. Можно пойти дальше и заменить отступы на фигурные скобки или заменить self на @ прямо как в этом вашем ruby. Но тут уже не обойтись регулярками, придется сделать AST-парсер и может еще поддержку синтаксиса в IDE, чтобы та не сходила с ума.

На одном из последних Moscow.Python был доклад про PythonQL — DSL для запросов к SQL, NoSQL который именно так и работает. Там реально свой язык внутри generator expressions.

Ссылки по теме