Как создать язык программирования. Часть 1. Начало и создание виртуальной машины
Invite pending
Здравствуйте, уважаемые хабрачитатели! В этом цикле уроков я буду рассказывать как создать язык программмирования и делится личным опытом. Эта статья написана для тех, кто уже знает, что такое парсинг, лексер, выражения и прочие штучки, без знаний которых невозможно объяснить как работают языки программирования и как их создавать.
Итак приступим. Первым делом мы придумываем название языку. Лучше не называйте язык OwnLang, OneLang, ...Lang и уж тем более World of Legends, потому что я запатентовал такое название (хотя у вас наверное и в голову не придёт назвать яп World of Legends). У меня язык называется, как вы уже поняли, World of Legends, в честь компании, которую я придумал, однако которая из-за моего несовершеннолетия не создана. Далее мы определим, какой у нас будет язык: компилируемый или интерпретируемый, со статической типизацией или с динамической, с виртуальной машиной или без, регистрозависимый или нет и т.д. Мой язык это будет скриптовый объектно-ориентированный (поддерживает функциональную парадигму) компилируемый статически-типизированный регистрозависимый язык работающий под виртуальную машину, которую мне пришлось создавать вместе с языком и которую я назвал сокращённо WoL VM. Затем нам надо выбрать язык программирования на котором мы хотим писать свой яп. Я отклонил C++, C и Ассемблер из-за сложности, однако если вы их хорошо знаете, то лучше всего писать на них, потому что они одни из самых быстрых языков. Я выбрал не Python и не Java из-за того, что они медленные и пользователю, чтобы работал мой язык придётся устанавливать pvm и JVM соответственно. Другие языки имеют преимущественно только одно направление, например веб-разработка (JavaScript, PHP и прочие) или iOS или MacOS разработка (Swift, Objective-C). Так что самым оптимальным решением я считаю C#. Благодаря технологии .NET Core и фреймворку Mono, C# стал кроссплатформенным, так что вопрос кроссплатформенности нас смущать не должен. Также это лёгкий для изучения и быстрый язык. Насчёт использования виртуальной машины, компилируемости и прочего. Это значительно ускоряет работу языка и сокращает использование памяти. Для того, чтобы всё только что перечисленное работало, нам надо сделать у виртуальной машины исполняемые (промежуточные) файлы, написанные на низкоуровневом яп, который придумаем мы сами. В моём случае таким языком будет являться WoL Virtual. Итак теперь нужно разобраться как будет происходить компиляция.

Файл (например с расширением .wol) передаётся в компилятор, который будет парсить код находящийся внутри файла (а как конкретно разберёмся в следующем уроке) в исполняемый файл виртуальной машины (например с расширением .bld) который будет содержать три стека: с классами, переменными и функциями, и скрипт.
При выполнении bld файла вм создаст три стека, перекинет туда стеки из bld файла, а затем выполнит скрипт.
Для начала в созданном проекте VS в Program.cs или как вам удобно, создаём класс Runner (ну вы можете назвать по другому), который будет представлять виртуальную машину. Какие функции будет выполнять виртуальная машина на данном этапе? Выполнение bld файлов. И всё. Это значительно ускоряет язык и уменьшает используемую память, так как вся грязная работа перекладывается на компилятор. Что нужно для работы вм? Три стека в которых будут переносится три стека из исполняемого файла и словарь содержащий строка-выражение, для парсинга выражений в скрипте. Итак для всего этого нам нужно создать три класса и интерфейс: для обозначения класса в нашем языке, переменной/константы, функции и интерфейс для обозначения выражений. (Почему именно интерфейс, я расскажу в следующих уроках) Например у меня это будут wolObject, Value, wolFunction и Expression соответственно. Далее возвращаемся к классу виртуальной машины и объявляем там свойства, как показано в коде:
По созданию компилятора мы здесь создадим метод Run, который будет воспроизводить исполняемые файлы виртуальной машины, но так как компилятор мы будем создавать в следующих уроках на этом пока всё. Ждите или если уже есть следующих уроков. Спасибо за внимание!
Как я пытался создать свой
Однажды я захотел создать свой язык программирования. Первым делом я конечно же стал искать литературу. Однако такую, подробную и на русском я нашёл спустя 2 месяца упорных поисков. Это был плейлист с видео с уроками по созданию своего языка программирования -> www.youtube.com/playlist?list=PL81SFGIHr5IJFsoWX0qTeQ9_-MFBE552C. Посмотрев несколько уроков я попытался делать точь в точь как у автора курса (далее просто aNNiMON), только на C# и сразу вместе с IDE (делал с помощью WinForms), но у меня ничего не работало. Затем я ещё много пересматривал и понял, саму суть и то, как всё работает. И я решил сделать яп заново также вместе с ide, но работало со множеством костылей и в общем работало всё ооочень криво и неправильно. Затем у меня был длительный перерыв от программирования. Итак спустя месяц я снова приступил к разработке языка, но выбрал заместо C# — Java. Также я по ходу «перерыва» значительно изменил язык в синтаксисе и логике. Долгое время у меня было всё отлично (всё естественно шло через пот и множество ошибок и трудов). В то время я уже не пользовался уроками aNNiMON`a. Однако когда язык стал более-менее функционален и выполнять какие-либо действия. Но почему-то несмотря на то, что код одних выражений был похож на код других и имел одну и ту же семантику, у меня одни выражения работали, а другие нет. От полного недопонимания того, почему так происходит, мне пришлось писать яп заново. Ничего я особенно менять не стал. Язык разрабатывался полным ходом, но когда стал писать парсинг создания классов, у меня всё встряло. Я выбрал неправильный вид лексера для яп. Язык у меня был интерпретируемый и в синтаксисе не предусматривались точки с запятой. Лексер брал первое слово в строке (например для строки «var a» лексер бы взял «var») и контекст в виде оставшейся строки (в том же контексте это было бы «a»). Но при создании класса или функции, или других либо «блочных» элементов, нельзя было брать в виде контекста строку. Тогда я в панике стал пересматривать видео aNNiMON`a с блочными элементами. Но к сожалению, в языке, который он создавал по ходу уроков (OwnLang) была совсем другая структура лексера и парсера. Затем я перепробовал множество вариантов, но возникала проблема, которая возникала в предыдущем языке… Этой проблемой было впадание в бесконечный цикл по непонятным причинам. Я никак не мог пофиксить этот баг и в отчаянии всё забросил. Затем я очень долго думал и решил переписать язык под C#. Уже в тот период меня ждало множество (несколько) конкурсов, где я должен был рассказывать про мой яп. На тех конкурсах я рассказывал и показывал про прошлый вариант языка, в то время как делал новый. И на новой версии у меня было всё шикарно, но случилось одно но… Я потерял флешку с проектами, включая все версии языка. Две недели я искал флешку и за это время придумал новый вид языка, который значительно отличался от других версий. Список изменений я перечислять не буду, так как сделаю это в отдельной статье, посвящённой устройству моего языка. На данный момент это последняя и разрабатывающееся версия языка и других версий планировать не собираюсь, уж слишком замучился переписывать почти с нуля.
Начало
Итак приступим. Первым делом мы придумываем название языку. Лучше не называйте язык OwnLang, OneLang, ...Lang и уж тем более World of Legends, потому что я запатентовал такое название (хотя у вас наверное и в голову не придёт назвать яп World of Legends). У меня язык называется, как вы уже поняли, World of Legends, в честь компании, которую я придумал, однако которая из-за моего несовершеннолетия не создана. Далее мы определим, какой у нас будет язык: компилируемый или интерпретируемый, со статической типизацией или с динамической, с виртуальной машиной или без, регистрозависимый или нет и т.д. Мой язык это будет скриптовый объектно-ориентированный (поддерживает функциональную парадигму) компилируемый статически-типизированный регистрозависимый язык работающий под виртуальную машину, которую мне пришлось создавать вместе с языком и которую я назвал сокращённо WoL VM. Затем нам надо выбрать язык программирования на котором мы хотим писать свой яп. Я отклонил C++, C и Ассемблер из-за сложности, однако если вы их хорошо знаете, то лучше всего писать на них, потому что они одни из самых быстрых языков. Я выбрал не Python и не Java из-за того, что они медленные и пользователю, чтобы работал мой язык придётся устанавливать pvm и JVM соответственно. Другие языки имеют преимущественно только одно направление, например веб-разработка (JavaScript, PHP и прочие) или iOS или MacOS разработка (Swift, Objective-C). Так что самым оптимальным решением я считаю C#. Благодаря технологии .NET Core и фреймворку Mono, C# стал кроссплатформенным, так что вопрос кроссплатформенности нас смущать не должен. Также это лёгкий для изучения и быстрый язык. Насчёт использования виртуальной машины, компилируемости и прочего. Это значительно ускоряет работу языка и сокращает использование памяти. Для того, чтобы всё только что перечисленное работало, нам надо сделать у виртуальной машины исполняемые (промежуточные) файлы, написанные на низкоуровневом яп, который придумаем мы сами. В моём случае таким языком будет являться WoL Virtual. Итак теперь нужно разобраться как будет происходить компиляция.

Файл (например с расширением .wol) передаётся в компилятор, который будет парсить код находящийся внутри файла (а как конкретно разберёмся в следующем уроке) в исполняемый файл виртуальной машины (например с расширением .bld) который будет содержать три стека: с классами, переменными и функциями, и скрипт.
При выполнении bld файла вм создаст три стека, перекинет туда стеки из bld файла, а затем выполнит скрипт.
Как будет строится виртуальная машина
Для начала в созданном проекте VS в Program.cs или как вам удобно, создаём класс Runner (ну вы можете назвать по другому), который будет представлять виртуальную машину. Какие функции будет выполнять виртуальная машина на данном этапе? Выполнение bld файлов. И всё. Это значительно ускоряет язык и уменьшает используемую память, так как вся грязная работа перекладывается на компилятор. Что нужно для работы вм? Три стека в которых будут переносится три стека из исполняемого файла и словарь содержащий строка-выражение, для парсинга выражений в скрипте. Итак для всего этого нам нужно создать три класса и интерфейс: для обозначения класса в нашем языке, переменной/константы, функции и интерфейс для обозначения выражений. (Почему именно интерфейс, я расскажу в следующих уроках) Например у меня это будут wolObject, Value, wolFunction и Expression соответственно. Далее возвращаемся к классу виртуальной машины и объявляем там свойства, как показано в коде:
using System;
using System.Collections.Generic;
public class Runner
{
Stack<wolObject> classes; //стек с классами
Stack<Value> variables; //стек с переменными/константами
Stack<wolFunction> functions; //стек с функциями
Dictionary<string, Expression> tokens; //словарь с токенами
public Runner()
{
classes = new Stack<wolObject>();
variables = new Stack<Value>();
functions = new Stack<wolFunction>();
}
}
По созданию компилятора мы здесь создадим метод Run, который будет воспроизводить исполняемые файлы виртуальной машины, но так как компилятор мы будем создавать в следующих уроках на этом пока всё. Ждите или если уже есть следующих уроков. Спасибо за внимание!