Pretty-print Lisp используя Python
Есть ли способ напечатать красивую строку кода в стиле Лисп (другими словами, набор сбалансированных скобок и текст внутри) в Python, не изобретая колесо?
3 ответа
Короткий ответ
Я думаю, что разумным подходом, если вы можете, является создание списков Python или пользовательских объектов вместо строк и использование модуля pprint, как это предлагается @saulspatz.
Длинный ответ
Весь вопрос выглядит как пример проблемы XY. Зачем? потому что вы используете Python (почему не Lisp?) для манипулирования строками (почему не структуры данных?), представляющими сгенерированный код в стиле Lisp, где стиль Lisp определяется как "набор скобок и текста внутри". На вопрос "как красиво печатать?" Я бы ответил: "Я бы не стал отсюда!", Лучший способ не изобретать колесо в вашем случае, кроме использования существующих колес, это придерживаться простого выходного формата.
Но, прежде всего, зачем вам красиво печатать? кто посмотрит на полученный код?
В зависимости от того, какой диалект Lisp вы используете, и предполагаемого использования кода, вы можете отформатировать свой код очень по-разному. Например, подумайте о переводе строки, отступе и максимальной ширине текста. Обычный принтер Common Lisp особенно развит, и я сомневаюсь, что вы хотите иметь тот же уровень конфигурируемости. Если вы использовали Lisp, простой вызов pprint
решит вашу проблему, но вы используете Python, так что придерживайтесь наиболее разумного вывода на данный момент, потому что симпатичная печать - это червячная банка.
Если ваш код предназначен для читателей, пожалуйста:
- не ставьте закрывающую скобку в своих строках
- не выравнивайте по вертикали открытые и закрывающие скобки
- не добавляйте пробелы между открывающими скобками
Это безобразно
( * ( + 3 x )
(f
x
y
)
)
Это лучше:
(* (+ 3 x)
(f x y))
Или просто:
(* (+ 3 x) (f x y))
Смотрите здесь для более подробной информации.
Но перед печатью вам необходимо проанализировать входную строку и убедиться, что она правильно сформирована. Возможно, вы уверены, что он правильно сформирован из-за того, как вы генерируете свои формы, но я бы сказал, что принтер должен игнорировать это и не делать слишком много предположений. Если вы передадите симпатичному принтеру AST, представленный объектами Python, а не просто строками, это будет проще, как это предлагается в комментариях. Вы можете создать структуру данных или пользовательские классы и использовать модуль pprint (python). Это, как уже было сказано выше, похоже, подходит для вашего случая, если вы можете изменить способ генерации кода в стиле Lisp.
Со строками вы должны обрабатывать любые возможные входные данные и отклонять неверные. Это означает проверку сбалансированности круглых скобок и кавычек (остерегайтесь escape-символов) и т. Д. На самом деле, вам не нужно создавать промежуточное дерево для печати (хотя это, вероятно, поможет другим частям вашей программы), потому что Lisp- Код стиля состоит из форм, которые легко вкладываются и используют префиксную нотацию: вы можете отсканировать введенную строку слева направо и распечатать ее при необходимости, когда видите круглые скобки (открывающая скобка: recurse; закрывающая скобка, возврат из рекурсии). Когда вы впервые сталкиваетесь с двойными кавычками без экранирования "
читать до следующего "
, ... Это, в сочетании с простым методом печати, может быть достаточно для ваших нужд.
Я думаю, что самый простой способ - использовать тройные кавычки. Если вы говорите:
print """
(((This is some lisp code))) """
Он должен работать. Вы можете отформатировать свой код в тройных кавычках так, как вам нравится, и он получится так, как вы хотите.
Желаем удачи и счастливого кодирования!
Однажды я сделал этот рудиментарный симпатичный принтер для приукрашивания CLIPS, основанного на Lisp. Может помочь:
def clips_pprint(clips_str: str) -> str:
"""Pretty-prints a CLIPS string.
Indents a CLIPS string for easier visual confirmation during development
and verification.
Assumes the CLIPS string is valid CLIPS, i.e. braces are paired.
"""
LB = "("
RB = ")"
TAB = " " * 4
formatted_clips_str = ""
tab_count = 0
for c in clips_str:
if c == LB:
formatted_clips_str += os.linesep
for _i in range(tab_count):
formatted_clips_str += TAB
tab_count += 1
elif c == RB:
tab_count -= 1
formatted_clips_str += c
return formatted_clips_str.strip()