Вступление

Это не гайд, не туториал и не исследование. Эта статья - просто история о том, как я решил создать свой язык для низкоуровневых штучек, отличного от ассемблера и С. Приятного прочтения!

Предыстория

Как вы знаете, я обожал (и до сих пор обожаю) язык ассемблера, а именно - fasm. Я уже давно его изучил и пользуюсь им, но постепенно стал давать слабину. Я устал писать код на ассемблере, так как это долго и довольно таки тяжело (рано, или поздно, это всё равно произошло бы). Понять мою любовь к этому языку можно прочитав мои предыдущие статьи: раз, два, три, четыре. Почему же я не стал использовать С? Он мне просто не нравится. Неудобно мне. Вот и всё.

Концепт

Концепт заключается в том, что человек указывает все данные для компиляции в главном файле с помощью директивы, например, format. Так же, есть два типа библиотек: встроенные (стандартные) и кастомные (пользовательские). Иначе говоря, либы и модули. Для использования shared object, dll и пр. будет отдельная директива. Указателей в языке не будет, а для разыменывания будет использоваться макрос OFFSET. Для работы с железом на все случаи жизни будут стдлибы. Синтаксис будет чем-то средним между Java, TS, fasm (для некоторых выражений) и Python.

Синтаксис

Это, пожалуй, первое, с чего я начал. Для начала, я написал пример кода, который устроит меня своим синтаксисом. Вышло как-то так:

format mbr.x86.16.executable;

use Bios;
use Console;

fn main() -> Void
{
    Bios.set_mode(3);
    Console.log("Hello, World!");
    return 0;
}

Я решил не сильно париться, поэтому использовал библиотеку parglare. Она очень легкая и удобная, всем рекомендую. Для описания синтаксиса парсер принимает строку в соответствующем формате, использует регулярные выражения (не надо осуждать регулярки, они всесильны!). Итак, вот такое описание синтаксиса у меня получилось:

sroot: sany*;

sany: sformat
    | suse
    | sfn
    | sdefine;

sformat: "format" tname ";";
suse: "using" tname ";";
sfn: "fn" tname "(" sargs? ")" "->" tname sbody;
sargs: sarg ("," sarg)*;
sarg: tname ":" tname;
sbody: "{" sline* "}";
sline: (scall | sequal | sdefine | sreturn) ";";
scall: tname "(" scallargs? ")";
scallargs: sexpr ("," sexpr)*;
sequal: tname "=" sexpr ";";
sdefine: sarg ("=" sexpr)* ";";
sreturn: "return" sexpr?;
sexpr:
     "(" sexpr ")"
    |(ssequence | tname) "[" tinteger "]"
    |sexpr "*" "*" sexpr
    |"-" sexpr
    |sexpr "*" sexpr
    |sexpr "/" sexpr
    |sexpr "+" sexpr
    |sexpr "-" sexpr
    |"!" sexpr
    |sexpr "&" sexpr
    |sexpr "|" sexpr
    |sexpr "^" sexpr
    |tinteger
    |tstring
    |tname
    |ssequence;
ssequence:
    "<" scallargs ">";

terminals

tstring: /"[^"]*"/;
tname: /([A-Za-z_]\w*\.)*[A-Za-z_]\w*/;
tinteger: /00?|[1-9]\d*/;

Вышло кратко. А краткость - сестра таланта. Да и к тому же, так будет проще.

Компиляция в язык ассемблера (fasm)

Я долго ломал голову, как это полаконичней сделать, и никак не мог придумать решение. Через пару дней я наткнулся на статью про pyast64 (на медиум, по-моему). Она раскрыла мне глаза. В коде этого скрипта было все что мне нужно: и работа со стеком, и соглашение о вызовах, и пр и тп. Я очень шустро накидал константы:

SIMPLE_BINOPS = (
	"add",
	"sub",
	"or",
	"and",
	"xor",
	"mul",
	"div"
)

SIMPLE_UNOPS = (
	"neg",
	"not"
)

SIMPLE_TYPES = (
	"integer",
	"string",
	"label"
)

COMPLEX_BINOPS = (
	"pow",
	"ind"
)

MUTABLE = (
    "integer"
)

ASM_BINOP = """
    pop qword rdx
    pop qword rax
    %s qword rax, rdx
    push qword rax"""

ASM_DEF = """
    %s d_%s %s"""

ASM_EQU = """
    pop qword rdx
    mov [%s], rdx"""

ASM_LDR = """
    push qword [%s]"""

ASM_LEA = """
    push qword %s"""

ASM_PTR = """
    push qword %s"""

ASM_RET = "\n   ret"

ASM_SEQ = """
    push %s
"""

Дописать транслятор в ассемблер я ещё не успел, но до этого не долго осталось. Весь исходный код опубликован здесь: группа Telegram, здесь же можно обсудить и/или поддержать проект. Спасибо за внимание!

Комментарии (43)


  1. AspisVipera
    00.00.0000 00:00
    +8

    1. TalismanChet Автор
      00.00.0000 00:00
      -8

      This tutorial assumes you know C++, ...


    1. TalismanChet Автор
      00.00.0000 00:00
      -8

      да и мне не нужен язык из темплейтов и пр.


      1. 4Droidek
        00.00.0000 00:00
        +2

        Орфографические ошибки исправить бы. Задумка очень интересная. ЯП подразумевается удобный для автора или есть радикальное отличие от сиподобных?


        1. TalismanChet Автор
          00.00.0000 00:00

          во-первых, удобный для автора, во-вторых, да, отличия есть. Например, больше никаких указателей, ссылок и прочего! если же какой-то либе или хардваре нужен именно адрес, то его можно взять макросом-функцией OFFSET. Планируется ещё много деталей, но пока что это всё, что успел.


          1. 4Droidek
            00.00.0000 00:00

            Желаю Вам не бросить на полпути. Успехов!


            1. TalismanChet Автор
              00.00.0000 00:00

              Спасибо!


  1. NeoCode
    00.00.0000 00:00
    +3

    Вы бы рассказали что ваш язык умеет, какие языковые конструкции поддерживает. Например массивы, структуры, перечисления, функции и будут ли функции first-class объектами (т.е. лямбды, замыкания и прочее), кортежи (нетипизированные составные литералы), рефлексия, метапрограммирование и т.д. Какие будут операции? Из того что я понял только совсем стандартные, но если у вас низкоуровневый язык то может имеет смысл подумать о более специфических битовых операциях? Например в системе команд x86 есть битовое вращение, битовое сканирование, есть установка, сброс и получение бита по номеру, эти операции в Си не представлены.


    1. TalismanChet Автор
      00.00.0000 00:00
      +1

      Я расскажу в следующей части, сейчас пишу indev-версию. когда закончу 1.1, буду добавлять больше специфики и деталей. А так, спасибо за подробный разбор потенциальных возможностей!


      1. BigBeaver
        00.00.0000 00:00
        +2

        Просто немного странно сначала рассказать как вы это сделали, и только потом — что


        1. TalismanChet Автор
          00.00.0000 00:00

          Несомненно, вы правы. Но этот язык разрабатывается в версии indev, следственно, мало что в нем пока есть (из тех вещей, которые потом будут) и мало что вообще определяет его будущее.


  1. GermanAizek
    00.00.0000 00:00

    @TalismanChet, чем UASM не угодил? Ассемблер настолько сильно упрощается и превращается в C/Pascal подобный язык, пишите себе макросами целые оптимизированные конструкции. Если хочется своей реализации, можете встроить свой участок кода. При этом трансляция переходит в нативный для архитектуры MASM код.
    http://www.terraspace.co.uk/uasm.html


    1. TalismanChet Автор
      00.00.0000 00:00

      1. я не знал что оное существует

      2. я жестко привязан к специфике препроцессора fasm, поэтому реализовать что-то на базе uasm не могу (хотя бы метаязык на макросах)

      3. уже давно горю идеей создания своего языка


      1. perfect_genius
        00.00.0000 00:00
        +2

        я не знал что оное существует

        А знаете ли вы о сайте compiler.su?


        1. TalismanChet Автор
          00.00.0000 00:00

          Нет, не знал, но приму к сведению существование оного.


  1. perfect_genius
    00.00.0000 00:00
    +23

    Плох тот программист, который не хочет создать свой язык программирования.
    Но иногда описание своей мечты выглядит как рассказывание бредового сна — для самого рассказчика сон был очень волнующим и важным, но окружающим неловко и непонятно зачем такое рассказывать.
    Рекомендую в следующих статьях сразу же отмечать какую задачу/проблему призван решать ваш язык.


  1. wormball
    00.00.0000 00:00
    +2

    Эта статья — просто история о том, как я решил создать свой язык для низкоуровневых штучек, отличного от ассемблера и С.

    Могу посоветовать вам изучить язык Forth и написать его компилятор/интерпретатор. Пишется элементарно, даже у меня это заняло две недели. Ну или retroforth посмотрите. Книжки — Лео Броуди "Способ мышления Форт" и "Начальный курс программирования на языке Форт".


    1. TalismanChet Автор
      00.00.0000 00:00

      спасибо за совет, но это скорей проект ради проекта, ведь я могу продолжить писать на fasm/C, или даже на Forth/uasm, но мне хочется своё детище, которое может кому-нибудь принести пользу.


  1. SaemonZixel
    00.00.0000 00:00
    +2

    Мой стандартный вопрос свой-языко-придумывателям: как обстоят дела с отладкой? Как программисты будут отлаживать свои программы на вашем языке? Вы что-то придумали на эту тему уже?

    Отладка, всё таки, занимает большую часть времени и нервов в процессе программирования.


    1. TalismanChet Автор
      00.00.0000 00:00

      Хороший вопрос, саажу я вам. На этот счет у меня тоже есть идея, хоть она ещё не реализована. Я планирую создать ВМ для отладки приложений на моем языке. То есть, будет специальный бинарник (honey-dbg.*), который будет разворачивать собственную виртуальную среду выполнения программ (VPEE), которая будет абсолютно управляема (даже через гуй). Например, мы хотим отладить прогу для Windows 7. Тогда, мы пишем $ honey-dbg *.hny --gui --osname=nt osver=6.1 . В итоге, запустится программа с гуем, где можно будет пошагово (и не только) смотреть за поведением проги, делать подстановку путей файловой системы, подстановлять контент файлов, смотреть, какие возбуждаются исключения и тд и тп. Это не так сложно сделать в ВМ, чем в специальном отладчике через порты, ведь в таком слючае можно полностью контролировать как код, так и условия его выполнения.


  1. VitGo
    00.00.0000 00:00

    приходите в гости, похоже мы заняты одним и тем же :-)

    кстати для арм ассемблера у меня уже есть редактор, и я думаю вы много там найдете полезного (коль пишите (писали) на асме - то знаете все грабли которые обычно в этом процессе поджидают)

    https://t.me/ArmAsmEditor

    поставил вам плюса, но они не помогут, здесь за асм хейтят (больше всего те кто в асме нифига не понимают :-) последний пример в конце комментариев к моей последней статье)


    1. TalismanChet Автор
      00.00.0000 00:00

      Уже состою в этой группе! Попал в нее из вашей статьи, очень интересный проект, был бы рад помочь, но компа (тем более с виндой) пока что нет, делаю всё с телефона. Честно, я бы сделал компилер в арм асм с синтаксисом intel x86, а потом уже можно в sublime поставить подсветку. Но не в коем случае не умаляю ценность вашего проекта! Как я понял, вы с WinAPI пишите, так что снимаю шляпу - это очень непросто. У меня тоже есть своя группа про создание этого языка, пока что не такая активная, но тем не менее: https://t.me/honey_devdot. Если интересно, можете принять участие в развитии проекта!


      1. VitGo
        00.00.0000 00:00
        +1

        ну в сублиме только подсветка токенов есть... контекст не разбирается


        1. TalismanChet Автор
          00.00.0000 00:00

          Сравнительно недавно и контекст добавили. Так что 1:1 :)


          1. VitGo
            00.00.0000 00:00
            +1

            ух ты !!! есть ссыль как это работает ? мне это очень интересно !!


            1. TalismanChet Автор
              00.00.0000 00:00

              ссыль сейчас не найду, но в новый способ описания синтаксиса (yaml) добавили поддержку контекста!


    1. tyomitch
      00.00.0000 00:00

      здесь за асм хейтят (больше всего те кто в асме нифига не понимают :-)

      Ну например у https://habr.com/ru/post/540136/ (про FizzBuzz на SSE) рейтинг +369

      А туториалы для 16-битного реального режима х86, устаревшего ещё до нашего рождения, -- действительно поднадоели.


  1. pinbraerts
    00.00.0000 00:00
    -1

    Судя, например, по наличию точки с запятой и отсутствию механизма замыкающей запятой в грамматике, язык такой же кривой и неудобный, как и большинство остальных мейнстрим языков и особо от них не отличается в плане грамматики. Непонятно тогда, чем вам не угодил C++ (шире -- llvm), на нём можно полностью проэмулировать ваш фронтенд с помощью дефайнов (максимум простенький транслятор), вместе с кучей возможностей, библиотек и оптимизаций


    1. TalismanChet Автор
      00.00.0000 00:00
      -1

      ещё раз, почитайте комменты. Я хочу СВОЙ язык. И можете сказать, что такое замыкающая запятая?


      1. pinbraerts
        00.00.0000 00:00

        https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas

        По моему, с точки зрения грамматики, большинство токенов вообще лишние и не несут никакой дополнительной информации.

        Вы же используете какие-то инструменты для написания фронтенда, почему бы не использовать уже существующую реализацию фронтенда, если уж он у вас не сильно отличается и сконцентрировать своё внимание на бекэнде. Либо наоборот, если хотите изучить процесс написания языка полностью, то почему пользуетесь инструментами, а не пишете, например, свой парсер. СВОЙ очень размытое понятие. Я бы использовал как стартовую точку какое-то отличие от языков, то есть начал бы с концепта. Например, новое соглашение о вызовах на вашем любимом ассемблере или максимально выразительная грамматика, революционный механизм обработки ошибок какой-нибудь. Соответственно тогда нет смысла лезть в области, не связанные непосредственно с задачей.


        1. TalismanChet Автор
          00.00.0000 00:00
          -1

          ещё раз, я ЦЕЛЕНАПРАВЛЕННО изобретаю колесо, надеясь, что оное может кому-то пригодиться. И в первую очередь, я делаю этот проект для себя, и ПРю его для поиска единомышленников, не хейтеров. Можно предлагать фичи, или что вырезать, но тогда желательно делать пулл реквест в репозиторий гитхаб или же прилагать код вместе с предложением в группе devdot. Спасибо за понимание!


          1. pinbraerts
            00.00.0000 00:00
            +2

            Какими фичами ваше колесо уникально и может кого-то заинтересовать? Я как хейтер ваш самый большой единомышленник, потому что пытаюсь найти (вдохновить) изюминку, в процессе разработки которой можно изучить много языков и их принципов работы. Если запариться, то работа может потянуть, наверное, на диплом бакалавра. Как вы сами могли убедиться, сваять каркас по готовым инструментам довольно просто, дальше открывается дорога творчеству, а у вас, грубо говоря, весь манифест состоит из одной строки: "свой язык". Что если бы вы наткнулись на бэкенд либу с уже реализованным дебаггером? Вы бы тогда скорее всего не стали задумываться о его реализации. То же самое с грамматикой. Раз уж начинать свой язык, то зачем тащить в него всякие архаизмы типа точки с запятой? Или вы просто скопировали BNF откуда-то?

            Извиняюсь за прямолинейную форму, удачи в ваших начинаниях!


            1. TalismanChet Автор
              00.00.0000 00:00

              Если хотите принять участие, можете зайти в группу в телеграм (ссылка в статье)!

              Вообще, я собираюсь сделать все самостоятельно, и единственное, что я себе позволил позаимствовать - парсер из библиотеки parglare. Остальное я делаю целиком и полностью сам :)


          1. tyomitch
            00.00.0000 00:00
            +2

            Вот пример статьи, как надо описывать самодельный язык, чтобы привлечь единомышленников, а не хейтеров: https://habr.com/ru/company/ruvds/blog/535904/

            Начать с того, зачем этот язык нужен и какие новые возможности даст; добавить несколько простых примеров кода на новом языке, которые на существующих языках писать неудобно; а портянки с формальными грамматиками оставить на гитхабе, и не тащить в статью вообще.


    1. TalismanChet Автор
      00.00.0000 00:00

      все, нашел что такое эта ваша запятая. Не думаю, что эта фича так полезна, но добавить её не сложно.


      1. VitGo
        00.00.0000 00:00

        да уж.. жесть какая то.. кто то видать написал кривой лексер и потом вот придумал "гениальную запятую" :-) я бы еще понял если бы речь о точке шла....

        в общем нуб я, и ничего в колбасных обрезках не понимаю :-(


        1. TalismanChet Автор
          00.00.0000 00:00

          Вообще, не вижу смысла в такой запятой, но просто, чтобы было, добавил её в синтаксис. Для этого я изменил 3 строчки кода! И зачем это нужно, если, по сути, никакой функции это не несет?


          1. pinbraerts
            00.00.0000 00:00
            +2

            Дифф меньше, когда добавляешь новый элемент в список, не надо редактировать предыдущую строку, чтобы просто поставить запятую, если она там уже стоит. Для меня это такая мелочь, которая постоянно сидит в глазу


        1. TalismanChet Автор
          00.00.0000 00:00
          -1

          вот, кстати, ещё доказательство её бесполезности:

          В JSON нельзя использовать замыкающие запятые. Попытаясь спарсить JSON с такой запятой, вы получите синтаксическую ошибку


          1. pinbraerts
            00.00.0000 00:00
            +2

            Это доказательство бесполезности JSON, там вообще грамматика очень жёсткая и для описания данных изобилует излишествами. Лишние килобайты на передачу всех этих скобок и кавычек при общении на текстовом JSON. Иногда достаточно просто поменять местами операнды в правиле грамматики (заодно удаляются тривиальные правила), и пользователи языка смогут сделать глубокий вдох в полную грудь


            1. TalismanChet Автор
              00.00.0000 00:00

              как вариант решения данной проблемы можно использовать yaml. Например, передавать в json единственный элемент - массив из строк кода yaml. А со стороны клиента уже парсить. Но никто не станет этим заниматься


              1. pinbraerts
                00.00.0000 00:00
                +1

                В yaml грамматика более гибкая, но тут возникает другая проблема: полноценная грамматика yaml очень сложная, рекурсивная и неоднозначная (до сих пор не реализована), вроде как сложнее даже чем и так перегруженная грамматика C++, если парсер JSON можно наваять на коленке, а готовые либы обычно не очень большие, то yaml я даже не возьмусь разбирать, разве что его какое-нибудь сильно упрощённое подмножество, поэтому никто и не станет этим заниматься


  1. himysay
    00.00.0000 00:00
    +1

    Я больше практик, но если уж разрабатывать компилятор, надо делать статический анализатор,который будет разбирать код на части и проверять синтаксис на прекомпиляции, и не просто регулярками а формируя стэк вызова тех или иных функций и смотреть их порядок вызовов, тоже касается конструкций ты, элсе и так далее, тоже касается типов. В любом случае всё это надо закладывать изначально ибо в будущем придёшь к Си подобному языку, ибо JS, TS то же синтаксически похож на си++