Каждый разработчик ПО использует язык программирования, и обычно даже несколько. Лишь немногие из нас создают языки программирования. Это логично, ведь работу, которую мы выполняем, чаще всего можно сделать при помощи уже существующих языков. И совершенствованием этих языков уже занимаются другие люди, а мы можем сосредоточиться на нашей непосредственной задаче.
Но из-за этого мы теряем возможность научиться чему-то новому. Я нашла это новое, когда создала язык, руководствуясь глупым принципом: поток управления должен осуществляться через исключения и ничего больше. Я создала его как шутку, но неожиданно для себя в процессе разработки получила новые знания.
В том, чтобы делать собственные инструменты, есть нечто особенное
Каждый серьёзный столяр создаёт часть своих инструментов самостоятельно. Кто-то изготавливает под себя верстак, или козлы, или зажимные приспособления под множество инструментов и условий работы. Всё это столяр может создать из дерева. Но у него не всегда есть доступ к станкам для изготовления всех используемых им инструментов: для производства деталей стамесок и рубанков, не говоря уже об электроинструменте.
У нас, программистов, ситуация иная. Мы имеем почти полный контроль над машиной и, теоретически, способны создать всё с нуля. Так как мы работаем с программными инструментами и сами пишем ПО, то можем создавать инструменты для себя, начиная с операционной системы и выше.
Это привилегия, недоступная для большинства областей деятельности.
▍ Чему можно научиться, создав язык
Один из инструментов, с которым мы взаимодействуем больше всего — это язык программирования. Мы используем его для выполнения задачи, и он формирует наш подход к её решению. Язык программирования становится нашим инструментом рассуждений, даже когда мы не сидим за клавиатурой; именно это и делает его подходящим для обучения. Создавая новый язык программирования, вы можете научиться многому.
Вы узнаете о грамматике и архитектуре языка.
Перед тем, как приступать к реализации языка программирования, вам необходимо решить, каким он вообще должен быть. Будет ли он императивным, функциональным или каким-то ещё? Он объектно-ориентированный? Он имеет традиционный синтаксис, позаимствованный у другого языка, или вы будете создавать что-то новое и причудливое? Это лишь некоторые из вопросов, на которые вам предстоит ответить при проектировании языка.
В процессе работы вы узнаете, почему другие языки проектировались именно таким образом. Возможно, вы даже поймёте это в процессе первоначального проектирования. Например, при работе над моим следующим языком программирования Lilac я узнала, почему так часто используется точка с запятой, потому что попробовала выбрать какой-то другой символ. Обсуждая это с моим другом, мы обнаружили множество потенциальных недостатков у всех других вариантов! Если же вам не повезёт, то вы узнаете всё это на этапе реализации, и подобные уроки уже никогда не забудете.
Вы узнаете о парсинге.
Это один из первых аспектов, с которым вы столкнётесь в начале реализации своего языка. Без парсинга языка невозможно сделать практически ничего другого. Перед тем, как приступать к написанию парсера, вам нужно выбрать его тип. Если вы новичок, то пока не стоит слишком вдаваться в подробности. Но если вам действительно интересны парсеры, то вы найдёте в них замечательную тему для глубокого исследования.
Вы узнаете о среде исполнения.
Для запуска кода вам нужно будет написать среду исполнения (или компилятор), то есть придётся серьёзно поразмыслить о том, как будет работать код.
Как на самом деле работает выбрасывание исключений?
Как узнать, в каком месте памяти искать переменную, когда на неё ссылаются?
При выполнении рекурсивной функции, насколько глубокой может быть рекурсия? И почему?
Это лишь некоторые из вопросов, на которые вам предстоит ответить. И список вопросов будет очень длинным.
Вы можете подстроить свой язык под то, чему хотите научиться. Мой первый язык, Hurl, научил меня основам создания интерпретатора, проектирования языка и написания грамматики. Мой второй язык, Lilac, позволит мне больше узнать о системах типов, средах исполнения и инструментарии управления.
В процессе создания языка вы будете глубже понимать другие языки. Когда я создавала Hurl и у меня возникали ошибки парсинга, язык выдавал мне только сырые имена токенов. Это напомнило мне ошибки, которые я иногда наблюдала в своей интеграции Neovim с Rust LSP, поэтому я начала работать над тем, чтобы эти ошибки было проще понимать. Каждое решение, принимаемое при проектировании и реализации языка, заставит вас углубить понимание используемых языков, повысив ваше качество как их пользователя.
▍ Язык будет плохим, но это нормально
При написании собственного языка для обучения здорово то, что с большой вероятностью он окажется плохим. Да, разумеется, возможно создавать новые хорошие языки, и это замечательно! Но по моему опыту, лучше отделять обучение чему-то от реализации этого с максимальным качеством.
Когда вы приступаете к делу, зная, что язык будет плохим, это даёт вам свободу! «Плохой» не означает, что он будет бесполезным для вас. По большей мере это означает нехватку совершенства и отточенности, свойственных «реальным» языкам, а также изъяны, препятствующие широкому распространению вашего проекта. Но вы можете разработать что-то, решающее ваши собственные задачи, позволяющее успешно участвовать в Advent of Code или добиться почёта среди друзей-ботанов. Всё это пойдёт вам на пользу.
Вы всё равно не создадите новый Python, так что можете сосредоточиться на том, что вам интересно и полезно для обучения. Можно избавиться от всего скучного, но необходимого для применения в реальном мире. При обучении можно выбрать чёткую цель и сделать процесс увлекательным, так вы повысите вероятность завершения проекта. Вполне допускается что-то намеренно ломать или принимать смешные архитектурные решения, которые просто вас забавляют. Язык всё равно будет плохим, так почему бы не расслабиться?
▍ Как приступить к созданию языка
Необходимость сесть за пустой экран редактора и приступить к «написанию нового языка» может быть пугающей. Долгое время я считала (даже в должности Principal Software Engineer), что в этом есть какая-то тёмная магия, недоступная моему пониманию. Это большой объём работы, но справиться с ним может любой из программистов. С каждым годом это становится всё проще, потому что появляется много новых ресурсов для обучения новичков.
Первым делом я бы порекомендовала воссоздать по руководствам чей-то чужой язык. Лично я для этого воспользовалась потрясающей книгой Crafting Interpreters. Хорошие отзывы я слышала об Writing An Interpreter In Go и Build Your Own Lisp. Все эти ресурсы позволят вам под руководством опытного наставника получить представление о том, как работают языки.
Однако стоит отметить, что мне показалось хорошей идеей выбрать реализацию не того языка, который использован в книге. В Crafting Interpreters рассказывается о Java и C, поэтому я использовала Rust. Выбрав другой язык, вы будете вынуждены глубже осваивать концепции, чтобы перенести их на иную платформу. Вы не сможете бездумно перепечатывать код, вам придётся изучить его глубже.
После этого вы уже можете сами выбирать путь своего развития. Я начала разрабатывать Hurl, просто экспериментируя и выбирая то, что понравится. Такой подход сработал и позволил мне подкрепить знания, полученные из Crafting Interpreters. Готовясь к созданию Lilac, я прочитала пока одну книгу и составила короткий список других для прочтения. Когда я спросила у друзей рекомендации, то они посоветовали мне следующие книги:
- Introduction to Compilers and Language Design, которую я прочитала и получила от неё большое удовольствие.
- Engineering a Compiler.
- Programming Languages: Application and Interpretation.
- Compilers: Principles, Techniques, and Tools, она же «книга с драконом».
Выбор чтения зависит от того, что вы хотите сделать дальше и чему хотите научиться.
▍ Вперёд, создавайте что-нибудь интересное
Я думаю, всем нам стоит создавать свои языки. Это отличный способ обучения и придумывания новых идей. В конце концов, это превосходная возможность экспериментировать и развлекаться с компьютером.
Автор: ru_vds