Pull to refresh

Как создать язык программирования. Часть 1. Начало и создание виртуальной машины

Здравствуйте, уважаемые хабрачитатели! В этом цикле уроков я буду рассказывать как создать язык программмирования и делится личным опытом. Эта статья написана для тех, кто уже знает, что такое парсинг, лексер, выражения и прочие штучки, без знаний которых невозможно объяснить как работают языки программирования и как их создавать.
Как я пытался создать свой
Однажды я захотел создать свой язык программирования. Первым делом я конечно же стал искать литературу. Однако такую, подробную и на русском я нашёл спустя 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. Итак теперь нужно разобраться как будет происходить компиляция.
схема яп 1
Файл (например с расширением .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, который будет воспроизводить исполняемые файлы виртуальной машины, но так как компилятор мы будем создавать в следующих уроках на этом пока всё. Ждите или если уже есть следующих уроков. Спасибо за внимание!
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.