Как реализован форматтер кода в Turtle Graphics
4 ноября 2022 г.Всем привет, в прошлой статье я представил проект приложения Turtle Graphics для Android с деталями реализации и ресурсами, касающимися языка сценариев, редактора, создания документации и т. д., после того как я опубликовал приложение и получил более 2000 загрузок несколько раз и хорошие оценки и отзывы, я решил добавить поддержку форматирования кода и в этой статье я подробно расскажу как работает простой форматировщик кода и как я реализовал его в приложении Turtle Graphics.
n Как программисты, форматировщики кода являются важным инструментом в нашей повседневной работе. Они облегчают чтение кода, если он отформатирован, но задавались ли вы вопросом, как это работает?
Прежде чем говорить о средстве форматирования кода, давайте сначала поговорим о том, как компиляторы представляют ваш код из текста в структуру данных, чтобы выполнять над ним такие процессы, как проверка типов.
n Давайте начнем наш рассказ с вашего файла, который содержит простой пример hello world n
fun main() {
print("Hello, World!")
}
Первый шаг — прочитать этот текстовый файл и преобразовать его в список токенов. Токен — это класс, представляющий ключевое слово, число, квадратную скобку, строку и т. д. с этой позицией в исходном коде, например, n
data class Token (
val kind : TokenKind,
val literal : String,
val line : Int,
)
Мы также можем сохранить имя файла, начало и конец столбца, поэтому, когда мы хотим сообщить об ошибке, мы можем предоставить полезную информацию, например, о позиции
Error in File Main Line 10: Missing semicolon :D
Этот шаг называется сканером, лексером или токенизатором, и в конце мы получим список токенов, например, n
{ FUN_KEYWORD, "fun", 1 }
{ IDENTIFIER, "main", 1 }
{ LEFT_PAREN, "(", 1 }
{ RRIGHT_PAREN, ")", 1 }
{ LEFT_BRACE, "{", 1 }
{ IDENTIFIER, "print", 2 }
{ LEFT_PAREN, "(", 2 }
{ STRING, "Hello, World!", 2 }
{ RRIGHT_PAREN, ")", 2 }
{ RIGHT_BRACE, "}", 3 }
Результатом является список токенов n
val tokens : List<Token> = tokenizer(input)
Обратите внимание, что на этом шаге мы можем проверить наличие некоторых ошибок, таких как незавершенная строка или символ, неподдерживаемые символы и т. д.
n После этого шага вы забудете свой текстовый файл и будете иметь дело с этим списком токенов, и теперь мы должны преобразовать некоторые токены в узлы в зависимости от грамматики нашего языка, когда мы увидели FUN_KEYWORD, что означает, что мы создадим узел объявления функции. и мы ожидаем имя, парен, параметры… и т.д.
n На этом шаге нам нужна структура данных для представления программы таким образом, чтобы мы могли пройти и проверить ее позже, и она называется абстрактным синтаксическим деревом (AST). Каждый узел в AST представляет такие операторы, как If, While, Объявление функции, объявление var и т. д. или выражения, такие как присваивания, унарные и т. д., каждый узел хранит необходимую информацию, чтобы использовать ее позже, например, на следующих шагах
n Объявление функции n
data class Function (
var name : String,
var arguments : List<Argument>,
var body : List<Statement>
)
Объявление переменной n
data class Var (
var name : String
var value : Expression
)
Этот шаг называется синтаксическим анализом, и в итоге мы получим объект AST, который мы сможем использовать позже для обхода всех узлов.
var astNode = parse(tokens)
Если язык имеет статические типы, такие как Java, C, Go и т. д., мы перейдем к шагу проверки типов, целью которого является проверка того, что пользователь правильно использует тип, например, если пользователь объявляет переменную с типом int. должен хранить только целые числа, условие if должно быть логическим типом или целым числом на языке, подобном C … и т. д.
n После этого шага мы получим тот же узел AST, но теперь мы знаем, что он действителен, и теперь мы можем скомпилировать его для любой цели или оценить его. Но также мы можем выполнить форматирование, статический анализ , оптимизация, проверка стиля кода и т. д.
n Например, предположим, что мы хотим, чтобы все разработчики объявляли переменные без использования _ внутри имени, чтобы убедиться, что мы пройдем через наш узел AST, чтобы найти все узлы Var и проверить их n
fun checkVarDeclaration(node : Var) {
if (node.name.contains("_") {
reportError("Ops your variable name ${node.name} contains _")
}
}
Но теперь нам нужно отформатировать его, так как это сделать? Это то же самое, что мы проходим через наш AST, и для каждого узла мы запишем его обратно в текст, но отформатированный, например, n
fun formatVarDeclaration(node : Var) : String {
var builder = StringBuilder()
builder.append(indentation)
builder.append("var ")
builder.append(node.name)
builder.append(" = ")
builder.append(formatValue(node.value))
builder.append("n")
return builder.toString()
}
В этом простом методе мы переписываем узел в строку, но с правильными отступами и добавляем новую строку после него, так что теперь 2 переменные объявляются в одной строке, значение также форматируется с помощью другой функции, которую вы можете использовать шаблон проектирования Посетитель, чтобы сделать это легко обрабатывать все узлы.
n В конце этого шага мы получим строку, представляющую тот же входной файл, но отформатированную, а затем запишем ее обратно в файл.
n Это базовая реализация средства форматирования кода, реальное средство форматирования производственного кода должно обрабатывать больше случаев, например, что, если код недействителен? Должен ли я форматировать только допустимый код? должны ли мы читать всю программу каждый раз, когда хотим отформатировать или скомпилировать код?
n Теперь вернемся к графике черепах. В этом проекте я уже сделал все необходимые шаги и получил готовый AST, поэтому я просто переписываю его с кодом, как вы видели выше ^_^ я прочитал его из формата пользовательского интерфейса и записать это обратно в пользовательский интерфейс в моем случае
n Если вам интересно и вы хотите узнать больше, я предлагаю
- Прочитайте хотя бы одну книгу по компиляторам, например "Создание интерпретаторов".
- Подробнее о протоколе языкового сервера (LSP)
- Посмотрите объяснение компилятора Typescript от автора Андерса Хейлсберга
- Подумайте, если у вас есть Ваша программа как AST, что еще вы можете с ней сделать
Надеюсь, вам понравилась моя статья, и вы можете найти меня на
Наслаждайтесь программированием 😋.
Оригинал