LINUX.ORG.RU

Написание собственного высокоуровневого ЯП

 , ,


0

2

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

Собственно, наткнулся я на функционально-распределенные Erlang и Elixir, и это оказались реально крутые языки. Однако мне кажется, что я смогу лучше - а если не получится лучше, то только прибавится уважение к тому, что есть, то есть везде одни плюсы.

Собственно, как с наименьшими потерями написать высокоуровневый язык? Я посмотрел на LLVM, QBE, FASM, GNU Assembler и другие крутые, продуманные вещи. И понял, что всё не то. После долгого поиска я задался правильным (на мой взгляд) вопросом - а на каком уровне абстракции ПО происходит вся высокоуровневая магия? И понял - уровень абстракции, интересующий меня - это уровень системных вызовов. Структура исполняемого файла, кодировка машинных инструкций, экспорт символов и релокации - это всё мне не интересно, интересна сама манипуляция данными, без раскуривания мануалов по AMD64 ISA. А с помощью какого инструмента проще, естественней всего взаимодействовать с системными вызовами? Да с помощью того, на котором и под который они были написаны - язык Си.

Итак, почему не генерить код на языке Си, а потом кормить компилятору? Как минимум, потому что хоть в Си и нет рантайма (параллельно запущенной системы, выполняющей служебные функции, например сборщик мусора), есть его подобие - стандартная библиотека. В ней аллокатор, в ней многопоточность, в ней много всего того, чего я бы хотел переписать по-своему. Так вот, почему не выдернуть с корнями libc и не написать свою, со стандартами Си не совместимую? Таким образом получаем полный контроль над системными вызовами, без нужды париться о низкоуровневых вещах и переносимости - язык Си всё делает за нас.

Вопрос - какие подводные камни? Насколько будут Си компиляторы терпеть мою LibC, функций стандартов Си не реализующую? И как бы вы подошли/уже подходили к задаче написания высокоуровневого ЯП?

Вопрос - какие подводные камни?

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

vbcnthfkmnth123 ★★★★★
()

Итак, почему не генерить код на языке Си, а потом кормить компилятору?

Можно. Так иногда и делают.

Так вот, почему не выдернуть с корнями libc и не написать свою, со стандартами Си не совместимую? Таким образом получаем полный контроль над системными вызовами, без нужды париться о низкоуровневых вещах и переносимости - язык Си всё делает за нас.

Не понял, зачем выдирать libc? Как раз libc — способ относительно не париться о совсем уж низкоуровневых вещах.

Насколько будут Си компиляторы терпеть мою LibC, функций стандартов Си не реализующую?

Будут. Писать на C можно и без libc вообще. Терпеть не будет софт, который написан под libc, но это очевидно.

Waterlaz ★★★★★
()
Ответ на: комментарий от Waterlaz

libc — способ относительно не париться о совсем уж низкоуровневых вещах

Насколько нискоуровневых? :) Тут вопрос опять же, насколько глубоко хочешь копнуть. Я бы хотел побаловаться с управлением памятью и асинхронностью - тут как раз libc и мешает. Вернее, задает направление, к которому, возможно, я в итоге и приду, но не без попытки сделать по-своему перед этим :) А вот система типов, calling conventions и т.п. - это уже слишком низкоуровнево, тут пускай компилятор делает как ему удобно.

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

lostghost1
() автор топика

В ней аллокатор, в ней многопоточность, в ней много всего того, чего я бы хотел переписать по-своему.

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

alysnix ★★★
()
Ответ на: комментарий от alysnix

Ну как зачем. Именно эта сторона разработки ЯП мне и интересна. Цель же в первую очередь образовательная. Или ты сомневаешься в реализуемости, нежели в мотивации?

lostghost1
() автор топика
Ответ на: комментарий от lostghost1

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

но когда выбирают готовый си и начинают ему отрывать лапки..то какой смысл его выбирать?

что не устраивает в рантайме си? там все вылизано, и его использование неопасно, ты же генереишь код на си сам, и можешь избегать опасных на нем конструкций.

alysnix ★★★
()

А за что борьба-то? Одно дело если за удобство программирования, то есть скорость написания, языковые конструкции, синтаксический сахар и т.д., а другое - если за эффективность.

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

И понял - уровень абстракции, интересующий меня - это уровень системных вызовов

Системных вызовов какой ОС? Они разные бывают. А затачивать высокоуровневый язык под конкретное окружение совсем плохая идея.

COKPOWEHEU
()
Ответ на: комментарий от alysnix

«Рантайм» - не часть языка. Хоть всякие графоманы-стандартописатели и старательно пытаются смешать эти два понятия. Ему нужно удобное кроссплатформенное апи для скармливания кодогенератору инструкций, рантайм тут ни при чём.

что не устраивает в рантайме си? там все вылизано

Не знаю не знаю, у меня иногда возникало желание даже без смены языка (т.е. продолжая писать на си) заменить libc на свою несовместимую реализацию, без тонн всякого легаси и просто ненужного функционала. Останавливала оценка трудозатрат.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)

Вопрос поставлен странно и задача не ясна. Непонятно какой у тебя опыт. Поэтому каждый отвечает соответственно.

Я, например свой небольшой ЯП (скорее даже не ЯП, а ПОЯ) транслирую даже не в C, а в C++. При том без всяких рекурсивных спусков, генераторов анализаторов и т.д. Я просто построчно транслирую. Хотя БНФ я для ясности всё-таки написал. Но лексический и синтаксический анализатор велосипедил вручную. Синтаксис разработан специально, чтобы такой примитивный построчный подход работал. Возможно, и при более хитром синтаксисе построчный метод работает.

Nim вроде бы не построчно транслирует. У них всё по науке. Сначала транслируют в какой-то свой промежуточный код, а уже потом в целевой язык. Соответственно, результирующий код абсолютно не похож на исходный.

Kogrom
()
Ответ на: комментарий от firkax

«Рантайм» - не часть языка. Хоть всякие графоманы-стандартописатели и старательно пытаются смешать эти два понятия. Ему нужно удобное кроссплатформенное апи для скармливания кодогенератору инструкций, рантайм тут ни при чём.

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

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

вот и вопрос. он выбрал си. но чем ему не нравится его рантайм? в чем претензии хоть?..боюсь он сам не до конца понимает, что ему надо.

без тонн всякого легаси и просто ненужного функционала/

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

alysnix ★★★
()
Ответ на: комментарий от firkax

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

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

alysnix ★★★
()

Без libc ты не сможешь использовать любые библиотеки что от него зависят, а это почти что все, сразу забывай про любую графику, про icu, svg, png, jpeg библиотеки и прочее.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от lostghost1

Я бы хотел побаловаться с управлением памятью и асинхронностью - тут как раз libc и мешает.

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

MOPKOBKA ★★★★★
()

не-не-не, Девид Блейн (с)

есть только Truffle, за него и держись.

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

крч счастье есть, ваяй.

olelookoe ★★★
()
Ответ на: комментарий от alysnix

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

Да, в Си (у имеющихся компиляторов) есть несколько случаев, когда они генерируют вызов какой-то функции там, где в исходном коде его не было, но очень мало где. В остальном Си тупо транслируется в ассемблер построчно, ну или с предварительными оптимизациями. Всякие printf и подобное - это совсем не то, без чего не запустится программа.

боюсь он сам не до конца понимает, что ему надо.

Прекрасно понимает, он же писал об этом: учебное мероприятие. Написание собственного высокоуровневого ЯП (комментарий)

все что не используется, все равно не влинкуется. что там заменять?

Так уж и не влинкуется? Попробуй статически слинковать хелловорлд, удивись размеру бинарника. Там явно больше, чем нужно для вывода строки в консоль.

firkax ★★★★★
()
Ответ на: комментарий от alysnix

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

Но вообще автор писал про аллокатор например, а это просто библиотечные функции безо всякого особого места в компиляторе.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Там явно больше, чем нужно для вывода строки в консоль.

а сколько надо чтобы вывести в консоль через принтф? принтф - сложная функция, и пишет в файл через какой-нить putc, с залочкой/разлочкой его. и это не три строчки кода.

а линкер все равно выкидывает лишнее.

alysnix ★★★
()

LLVM — проект программной инфраструктуры для создания компиляторов и сопутствующих им утилит. Состоит из набора компиляторов из языков высокого уровня, системы оптимизации, интерпретации и компиляции в машинный код.

https://ru.wikipedia.org/wiki/LLVM

пример - https://habr.com/ru/companies/jugru/articles/503146/

Unixson
()

… Собственно, наткнулся я на функционально-распределенные Erlang и Elixir, и это оказались реально крутые языки.

Erlang - это на котором ублюдочный RabbitMQ реализован? )))) Ну-ну… ) Может, с точки зрения «кодировщика», которому по-фиг, как живется потом пользователям ПО, это и верх крутизны.

vinvlad ★★
()

словно надрессированные из одного языка в другой тянут вот это

 ==
я думал, что хоть математики в их матиматическом ЯП неопустятся до унизительного буздумного копирования попринципу «как у всех» и оставят равенство знаком равенства, но нет....

Assembler
()
Ответ на: комментарий от firkax

Так уж и не влинкуется? Попробуй статически слинковать хелловорлд, удивись размеру бинарника. Там явно больше, чем нужно для вывода строки в консоль.

$ cat main.c
#include <stdio.h>

int main() {
    fwrite("hello world\n", 1, sizeof("hello world\n")-1, stdout);
}
$ musl-gcc -Os -static main.c -o test
$ strip ./test
$ file ./test
./test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
$ ./test
hello world
$ ls -l ./test
-rwxr-xr-x 1 fsb4000 fsb4000 17848 апр 23 11:59 ./test
fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Для сравнения программа на ассемблере.

$ cat hello.s
.section .data
    msg:
        .ascii "Hello, world!\n\0"
        len = . - msg

.section .text
    .globl _start
    _start:
        mov $4, %eax    # системный вызов для вывода строки
        mov $1, %ebx    # stdout
        lea msg(%rip), %ecx  # адрес строки
        mov $len, %edx  # длина строки
        int $0x80       # вызов системного вызова

        mov $1, %eax    # системный вызов для завершения программы
        xor %ebx, %ebx  # код возврата 0
        int $0x80       # вызов системного вызова
$ as -o hello.o hello.s && ld -o hello hello.o
$ strip ./hello
$ ./hello
Hello, world!
$ file ./hello
./hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
$ ls -l ./hello
-rwxr-xr-x 1 fsb4000 fsb4000 8488 апр 23 12:14 ./hello
fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

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

Assembler
()

Да нормально он отнесется. Есть же пример linux, в котором нет никакой libc, написан на C. Аналогично с freebsd kernel и всякими микроконтроллерами

cobold ★★★★★
()
Последнее исправление: cobold (всего исправлений: 1)
Ответ на: комментарий от vM

Не знаю, так подсказал ChatGPT когда я спросил как вывести hello world на ассебмлере на Linux.

Переписал так:

$ cat hello.s
.section .text
    msg:
        .ascii "Hello, world!\n\0"
        len = . - msg
    .globl _start
    _start:
        mov $4, %eax    # системный вызов для вывода строки
        mov $1, %ebx    # stdout
        lea msg(%rip), %ecx  # адрес строки
        mov $len, %edx  # длина строки
        int $0x80       # вызов системного вызова

        mov $1, %eax    # системный вызов для завершения программы
        xor %ebx, %ebx  # код возврата 0
        int $0x80       # вызов системного вызова

Размер стал меньше

$ ls -l ./hello
-rwxr-xr-x 1 fsb4000 fsb4000 4352 апр 23 14:05 ./hello
fsb4000 ★★★★★
()

ЯННП, ты хочешь использовать Си как бэкенд для твоего языка и чтобы этот бэкенд использовал твою библиотеку вместо libc? Это не проблема, хотя есть нюансы (недавно тут было обсуждение).

Таким образом получаем полный контроль над системными вызовами, без нужды париться о низкоуровневых вещах и переносимости

Париться всё-таки придётся, т.к. в разных системах вызовы разные и делаются по-разному. Без libc будешь весь этот зоопарк поддерживать сам.

Тебя ведь никто не заставляет тащить всю libc и плотно её использовать. Можешь взять только то, что тебе нужно и написать остальное своё.

no-such-file ★★★★★
()
Ответ на: комментарий от Assembler

я не знаю паскаль , поэтому , к сожалению, ничего не могу сказать

это довольно легко исправляется. Достаточно поставить документацию info по bison/flex и там внутри есть микро-паскаль.

Любители писать собственные язычки могут начать оттуда, это нормальная точка старта.

а так, выше указали SICP и lisp-подобные, имплементация/реализация, всё разобрано до косточек;

Или, моя личная рекомендация топик-стартеру, всё-таки написать форт-подобное. Это реально выворачивает мозг из привычной зоны комфорта, и в меру просветляет

MKuznetsov ★★★★★
()
Ответ на: комментарий от Assembler

нак равенства в вашем языке программирования был знаком равенства, а не знаком присваивания…

Non-programming language…

signal r_TOGGLE_100HZ : std_logic := '0';
  signal r_TOGGLE_50HZ  : std_logic := '0';
  signal r_TOGGLE_10HZ  : std_logic := '0';
  signal r_TOGGLE_1HZ   : std_logic := '0';
-- -- -- -- --   
begin
  p_100_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_100HZ = c_CNT_100HZ-1 then  -- -1, since counter starts at 0
        r_TOGGLE_100HZ <= not r_TOGGLE_100HZ;
        r_CNT_100HZ    <= 0;
      else
        r_CNT_100HZ <= r_CNT_100HZ + 1;
      end if;
    end if;
  end process p_100_HZ;

https://nandland.com/your-first-vhdl-program-an-led-blinker/

vM ★★
()
Ответ на: комментарий от Assembler

А знак присвоения ты тогда как сделаешь, через ==? Ведь = будет занято. Реализация языков программирования, а ещё конкретнее синтаксиса языка к математике никакого отношения не имеет. Да и в целом языки к математике никакого отношения не имеют, у математики свой язык, к слову тот ещё упоротый сложившийся чисто исторически из награмождения всего и вся, но он сложился и им просто пользуются.

Единственное чего ты добьёшься заменив == на = это лолы кеки чебуреки. Вот у всех горит что в lua арифметическая индексация с 1 а не адресная с 0 как многие привыкли. Также вместо != там ~= что человек со стороны может охарактеризовать как «нуууу типа равно почти, но нет» аля мне нужно ~500 рублей == мне нужно примерно 500 рублей :D.

А присвоение это вообще самая частая операция в программировании и в 99% языках которое придумало человечество присвоение это = и сейчас для тебя будет ахтунг и открытие выражением a=b мы говорим что а равно b потому что это так и есть то что типы могут не совпадать и на деле будет не равно это уже другое понимать надо :D

Опять же выражения типа x=f(y) икс равно эфь оть игрек и if (x == f(y)) c="fuck"; как бы это разные вещи и как бы совсем не разные. И вообще не первый раз это от тебя читаю, только ты упорно игнорируешь одну так скать проблему, что делать с присвоениями то? У которых ты отнял ключевой символ?

Финализирую = это и есть равно что в твоём смысле что в языках. Это знак присвоения/создания равенства. А == это расширенный знак равенства без присвоения.

P.S. А вместо ещё используется a!=b факториал от a равен b 😱😱😱😱 что ж делается тооооо!!11! 😱😱😱😱

P.P.S. А так я понять чисто по человечески тебя могу, меня вот откровенно тошнить от фичи python в виде принудительных отступов, ну вот бесят они меня, ну вот не могу я такое терпеть. Ладно бы где то разок надо было выровнять, но нет надо везде. Но это чисто вкусовщина, такая же как и у тебя.

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

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

А знак присвоения ты тогда как сделаешь, через ==?

в С++ было бы круто если знак присвоение заменили на &. допустим что ты хочешь чтобы значение переменной «а» было 10. ну так свяжи переменную с этим значением. амперсант похож на узел, было бы мнемонически лучше запоминать начинающим. знак равенства должен быть знаком равенства и в математике и в ЯП. потому что они связаны неразрывно. и даже когда вы читаете код, вот такой знак присваивания

 
a=1; 
интуитивно читается как a равно единице. раз уж случилась со школы ассоциация = как знак равенства, то не нужно ее рушить в ЯП == вот такой фигней высосанной даже не из пальца а из кое чего другого.

Assembler
()
Ответ на: комментарий от LINUX-ORG-RU

сейчас для тебя будет ахтунг и открытие выражением a=b

не вижу тут противоречия. если вы в коде пишите, что a=b значит значение в переменной «а» равно значению в переменной «b». знак равенства используется в условных конструкциях и циклах, там этот знак будет понятен и уместен в отличии от ==

Assembler
()
Ответ на: комментарий от LINUX-ORG-RU

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

Assembler
()
Ответ на: комментарий от Assembler

в С++ было бы круто если знак присвоение заменили на &

Ты опять предлагаешь переназначить символ, но не предлагаешь что вместо него будет. Указатели закопать?

ну так свяжи переменную с этим значением.

Сввяжем, а извлечь/разыменовывать указатель то теперь каким символом?

амперсант похож на узел, было бы мнемонически лучше запоминать начинающим.

Большая проблема начинающим объяснить контекст происходящего чем как выглядит визуально всё.

знак равенства должен быть знаком равенства и в математике и в ЯП

Знак присвоения является и знаком равенства тоже.

a = 5
b = a -- б равно a

Это == не знак равенства, это операция сравнения и никакого отношения к математическому = не имеет. У == который ты хочешь заменить на = есть побочный эффект в виде результата операции, у = никакого эффекта нет, более того = это контатация факта a=b значит а равно б и точка никаких или. В математике если написали x=f(y) значит x РАВНО И ТОЧКА f(y) это утверждение, а не сравнение.

потому что они связаны неразрывно

Математика не имеет никакого отношения к программированию, её используют как инструмент для внутренних нужд и всё.

вот такой фигней высосанной даже не из пальца а из кое чего другого.

Ладно, допустим что делать с «факториалом» != и с ещё десятками и десятками несхождений с математикой и интуитивностью?

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

Решая одно ломаешь другое, надо чинить. Как?

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)