NewLang — это язык программирования высокого уровня общего назначения. Основной особенностью языка является простой, логичный и не противоречивый синтаксис, который основан на строгой системе грамматических правил. За счет использования макросов, синтаксис языка легко расширяется до более привычного вида на основе ключевых слов.
NewLang находится в процессе активного развития. Это третий публичный релиз в который добавлены новые существенные возможности по сравнению с предыдущей версией.
- Возможность работы как в режиме интерпретатора, так и компилятора*.
- Динамическая и статическая типизация с возможностью указания типов в явном виде.
- Статическая типизация является условно строгой (автоматическое приведение типов отсутствует, но допускается преобразование между некоторыми типами данных, например, целое число может быть автоматически преобразовано в вещественное или рациональное, но не наоборот).
- Автоматическое управление памятью.
- ООП в виде явного наследования классов и «утиная» типизация.
- На уровне синтаксиса поддержка нескольких типов функций (обычные и чистые функции без побочных эффектов).
- Необязательные и именованные параметры функций.
- Простая интеграция с уже существующими программными библиотеками (в том числе импорт нативных переменных и функций из С/С++).
- Имеется REPL read-eval-print loop — «цикл: чтение — вычисление — вывод».
Зачем нужен NewLang?
У всех современных языков программирования происходит постоянное развитие (усложнение) синтаксиса по мере выхода новых версий. Это является своего рода платой за появление новых возможностей и воспринимается пользователями как естественное явление.
Но одновременно является и серьезной проблемой, так как с выходом версий добавляются новые ключевые слова и синтаксические конструкции, что неизбежно повышает порог входа для новых пользователей. Еще одним следствием этого процесса становится постоянное повышение сложности разработки и поддержки уже созданных программных продуктов, когда старый код дорабатывается с применением уже новых стандартов.
У NewLang сложность языковых конструкций ограничена естественным образом за счет разделения синтаксиса языка на две части — это упрощает его изучение и использование.
Основной синтаксис — для написания программ в объектно-ориентированном (императивном) и декларативном стилях, который основан не на зарезервированных ключевых словах, а на системе строгих грамматических правил. Имеется возможность расширения основного синтаксиса за счет использования макросов. Расширенный синтаксис — программные вставки на языке реализации (С/С++), когда основного синтаксиса становится недостаточно.
Еще одно неудобство современных языков в том, что большинство из них были созданы до начала эпохи машинного обучения, поэтому тензорные вычисления у них выполнены в виде отдельных библиотек. Это же касается и вычислений с неограниченной точностью, которые так же требуют применения дополнительных библиотечных функций.
У NewLang тензорные вычисления и рациональные числа неограниченной точности доступны «из коробки». Они поддерживаются на уровне синтаксиса для записи литералов соответствующих типов, а простые арифметические типы данных являются скалярами (тензорами нулевой размерности). Реализация тензорных вычислений сделана на базе библиотеки libtorch, а рациональные числа с использованием OpenSSL.
Новые глобальные фичи и изменения в синтаксисе:
Простые чистые функции удалены
Раньше я пытался использовать синтаксис предикатов из Пролога, в котором проверки условий записываются через запятую. Но эта идея оказалась провальной, а сами операторы просто ужасными (тестировал разные варианты, но каждый раз получал много отрицательных отзывов), поэтому сейчас решил полностью от них отказаться.
Они получались не только сложными для восприятия, но и по факту не нужными, так как легко заменялись обычной функцией с комбинации обычных логических операций.
Зафиксирован синтаксис операторов проверки условия и циклов.
Изменен и упрощен синтаксис операторов проверки условия (импликации) и цикла, для которых оставлено только по одному варианту. Проверка условия записывается в виде оператора математического следования -->, а цикл с помощью оператора <->.
Но для упрощения записи можно пользоваться макросами, тогда в этом случае операции проверки условий и циклы превращаются в классические
\if(...){
...
} \elif(...) {
...
} \else {
...
};
или
\while( ... ){
...
};
Конструкция else у операторов цикла
Теперь оператор цикла while поддерживает конструкцию else, которая выполняется если условие входа в цикл не было выполнено. Это поведение отличается от аналогичных конструкций в языке Python, у которого секция else выполняется всегда, кроме прерывания цикла по break.
Ветка else у оператора цикла записывается так же как и ветка иначе в условном операторе, т. е.
[ cond ] <-> {
...
}, [_] --> {
...
};
Или тоже самое, но с использованием макросов:
\while(cond) {
...
} \else {
...
};
Пространства имен
В синтаксис NewLang добавлены пространства имен, в стиле очень похожим на С++. Имена разделяются двойным двоеточием, а для определение пространства имен, его нужно указать перед открывающейся фигурной скобкой.
ns {
name {
var := 0; # Имя переменной будет ns::name::var
::var := 1; # Переменная из глобального пространства имен
}
}
Программные модули
Реализована концепция программных модулей — которая повторяет концепцию иерархического расположения файлов в структуре каталогов файловой системы, как в языках Python и Java.
Имя модуля начинается на префикс @, а структура каталогов указывается через точку. Причем концепции программных модулей и пространства имен объединены, и полное имя переменой из предыдущего абзаца будет @root.dir.module::ns::name::var
, где root и dir это каталоги в файловой системе, а module — имя файла.
Объектно ориентированное программирование
Реализована часть концепции ООП и добавлена поддержка определения классов и их наследование, которая сейчас выглядит следующим образом:
:NewClass := :Class() { # Новый тип (класс)
field := 1;
method() := {};
};
obj := :NewClass(); # Экземпляр класса
Неожиданно для самого себя понял, что имея полный набор вариантов проверок при создании объектов (::= — создать новый объект, := — создать новый или присвоить значение существующему, = — только присвоить значение, а если объект не существует будет ошибка), концепция переопределения наследуемых функций не требует вообще никаких ключевых слов:
:NewClass2 := :NewClass() { # Новый класс на базе существующего
field ::= 2; # Будет ошибка, т. к. поле field уже есть в базовом классе
method() = {}; # Аналог override, т.к. method должен существовать в базовом классе
};
Прерывания, возврат и обработка ошибок
Изменена, а точнее полностью переделана идеология возвратов из функций и обработки ошибок. Теперь она чем-то похожа на подход, примененный в Ruby. Любая последовательность команд заключенные в фигурные скобки (в том числе тело функции), рассматривается как блок кода у которого нет специального оператора аналога return, который возвращает какое либо значение. Просто любой блок кода всегда возвращает последнее вычисленное значение (это чем то похоже на оператор «запятая» в языках C/C++).
Для того, чтобы прервать выполнение кода используются две разные команды — прерывания, которые условно можно назвать положительным и отрицательным результатом. Что примерно соответствует семантике их записи. "Отрицательное" прерывание записывается в виде двух минусов, а "положительное" прерывание в виде двух плюсов, т.е. -- или ++.
По умолчанию данные операции возвращают пустое значение. Чтобы прерывание вернуло результат, возвращаемые данные нужно записывать между парой соответствующих символов, т.е. -- 100 --, что является близким аналогом оператора return 100;
в других языках программирования, а ++«Строка»++ — аналогом return «Строка»;
.
Хотя более точным аналогом этих операторов будет все таки не return, а throw, т.к. эти команды не только прерывают выполнение последовательности команд в блоке, но их еще можно «ловить». Для этого используется блок кода с соответствующей семантикой, {+ … +} — блок кода, который перехватывает положительные прерывания и {- … -} — блок кода, который перехватывает прерывания, созданные операторами --.
Подобная концепция (в явном виде не разделять возвраты из функций и обработку исключений), хоть и выглядит немного необычной, но позволяет реализовывать несколько очень полезных финтов, которые сложно реализуемые в обычных языках программирования.
Например, возврат из нескольких вложенных функций без необходимости обрабатывать результат возврата каждой из них. В этом примере функция Test перехватывает "положительные" прерывания из вложенных функций:
Test0(arg) := { \if($arg==0) \return("DONE - 0"); «FAIL» };
Test1(arg) := { \if($arg==1) \return("DONE - 1"); Test0($arg); };
Test(arg) := {+ \if($arg >= 0) Test1($arg); $arg; +};
Test(0); # Вернет «DONE — 0» возврат из вложенной функции Test0
Test(1); # Вернет «DONE — 1» возврат из вложенной функции Test1
Test(2); # Вернет «FAIL» возврат из вложенной функции Test0
Test(-2); # Вернет -2 — возврат из функции Test
Есть еще блок {* … *}
, который перехватывает оба типа прерываний. Такой блок кода поддерживает типизацию возвращаемого значения, что позволяет в явном виде указывать типы данных, которые нужно перехватывать. Например, {* ... *} :Type1
— будет перехвачено прерывание вида ++ :Type1 ++
или --:Type1--
, что позволяет очень гибко формировать логику работы программы.
Блоки кода с перехватом исключений также поддерживают оператор иначе (\else) который, по аналогии с оператором \else в циклах, выполняется только тогда, если прерывания не произошло.
Можно указать сразу несколько типов, которые нужно перехватывать:
{*
....
*} <:Type1, :Type2, :Type3>;
Бинарная сборка под Windws:
В текущем релизе версия clang повышена 15, а вызовы нативных функций опять реализованы с помощью libffi, что в итоге позволило собрать бинарную сборку не только под Linux, но и под Windows. И теперь чтобы поиграться с REPL read-eval-print loop можно скачать уже готовый бинарник.
Подробная информация о языке:
- Синтаксис NewLang
- Подробное описание типов данных
- Операторы и управляющие конструкции
- Вся документация по NewLang на одной странице
Обратная связь
Если у вас появятся предложения по развитию нового или улучшению уже существующего функционала NewLang, пишите.
Примеры кода
#!../output/nlc --eval
# Определение функции hello
hello(str) := {
# Импорт стандартной C функции
printf := :Pointer('printf(format:FmtChar, ...):Int32');
# Вызов C функции с проверкой типов аргументов по строке формата
printf('%s', $str);
# Возврат значения из функции hello
$str;
};
hello('Привет, мир!'); # Вызвать функцию</code>
Вывод (первая строка выводится с помощью printf, а вторая — возвращаемое значение функции hello):
Привет, мир!
Привет, мир!
Пример скрипта для вычисления факториала 1000
#!../output/nlc --eval
fact := 1\1; # Рациональное число без ограничения точности
mult := 1000..1..-1?; # Сделать из диапазона итератор для множителей от 1000 до 2
[mult ?!] <-> { # Цикл, пока не закончатся данные итератора
# Получить текущий множитель и перейти на следующий элемент итератора
fact *= mult !;
};
fact # Вывести итоговый результат
Вывод:
402387260077093773543702433923003985719374864210714632543799910429938512398629
020592044208486969404800479988610197196058631666872994808558901323829669944590
997424504087073759918823627727188732519779505950995276120874975462497043601418
278094646496291056393887437886487337119181045825783647849977012476632889835955
735432513185323958463075557409114262417474349347553428646576611667797396668820
291207379143853719588249808126867838374559731746136085379534524221586593201928
090878297308431392844403281231558611036976801357304216168747609675871348312025
478589320767169132448426236131412508780208000261683151027341827977704784635868
170164365024153691398281264810213092761244896359928705114964975419909342221566
832572080821333186116811553615836546984046708975602900950537616475847728421889
679646244945160765353408198901385442487984959953319101723355556602139450399736
280750137837615307127761926849034352625200015888535147331611702103968175921510
907788019393178114194545257223865541461062892187960223838971476088506276862967
146674697562911234082439208160153780889893964518263243671616762179168909779911
903754031274622289988005195444414282012187361745992642956581746628302955570299
024324153181617210465832036786906117260158783520751516284225540265170483304226
143974286933061690897968482590125458327168226458066526769958652682272807075781
391858178889652208164348344825993266043367660176999612831860788386150279465955
131156552036093988180612138558600301435694527224206344631797460594682573103790
084024432438465657245014402821885252470935190620929023136493273497565513958720
559654228749774011413346962715422845862377387538230483865688976461927383814900
140767310446640259899490222221765904339901886018566526485061799702356193897017
860040811889729918311021171229845901641921068884387121855646124960798722908519
296819372388642614839657382291123125024186649353143970137428531926649875337218
940694281434118520158014123344828015051399694290153483077644569099073152433278
288269864602789864321139083506217095002597389863554277196742822248757586765752
344220207573630569498825087968928162753848863396909959826280956121450994871701
244516461260379029309120889086942028510640182154399457156805941872748998094254
742173582401063677404595741785160829230135358081840096996372524230560855903700
624271243416909004153690105933983835777939410970027753472000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000\1
Как посмотреть?
- Бинарная сборка и тесты под Windows
- Бинарная сборка и тесты под Ubuntu
- Для запуска бинарных сборок потребуются разделяемые библиотеки libLLVM-15 и libtorch (архив с библиотеками для Windows), (архив с библиотеками для Ubuntu).
- Так же все можно собрать напрямую из исходников по инструкции в репозитории
Комментарии (49)
dynamicult
07.11.2022 17:27+4Это уже, кажется, третий пост от автора о его языке, который я читаю. А перед этим еще помню тему в комментариях к которой, за год до первого прототипа, автор высказывал намерение этот язык создать.
Я этот язык использовать не стану. И даже изучать его и пробовать. Причина проста - в языке слишком много соли. Вполне возможно, что в языке есть и иные проблемы, или причины по которым он бы мне не подошёл - но я о них даже не узнаю, потому что пересоленный синтаксис не даст никакой мотивации обращать на этот язык внимания.rsashka Автор
07.11.2022 17:31Причина проста - в языке слишком много соли.
А что это, можно какой нибудь пример? И можете привести пример языка, в которых "соли" мало?
dynamicult
07.11.2022 17:37+2На хабре когда-то был хороший перевод статьи про соль и чем она вредна https://habr.com/ru/post/348282/
можете привести пример языка, в которых "соли" мало
JavaScript
rsashka Автор
07.11.2022 17:55Ага, кажется речь идет о противопоставлении термину "синтаксический сахар".
И если это так, то я не понял, в чем заключается его соленость. Наоборот, синтаксис языка не использует какие либо термины вовсе, что позволяет реализовать вообще любой вариант DSL и добавлять "сахар" в каких угодно местах (если оно кажется слишком соленым).
tolik_anabolik
08.11.2022 05:41+2Думаю, dynamicult имел ввиду мусор в синтаксисе языка – все эти $ :: \ := и т.п. В этом я с ним соглашусь.
Хороший синтаксис ЯП не должен обременять разработчика ненужными символами, не должен заставлять решать в голове ребусы, отвечая на вопрос "А что же тут происходит".
Вам как разработчику своего детища все эти хитровыдуманные символы и подходы очевидны и понятны. Но для других людей, которые вдруг внезапно захотят применить этот ЯП, это серьезный отталкивающий фактор.
rsashka Автор
08.11.2022 06:09-1Вообще то, все эти символы так или иначе используются в подавляющем количестве языков и нет смысла отказываться от уже существующих и проверенных временем решений.
iCpu
08.11.2022 07:08+1Есть небольшая разница, нужно ли мне писать этот символ иногда, чтобы подчеркнуть какую-нибудь особенность типа
double ** matrix_p
или специальную операциюSingleton::instance()->foo()
- или ::мне \нужно писать $их \чуть-\ли не в \каждом :слове.Вы, конечно, можете возразить, что тут перемешаны переменные, макросы, типы и области видимости. Но мне не просто всё равно, мне абсолютно всё равно. Зато кому не всё равно, так это авторам более зрелых языков, которые у себя засахарили всякие
auto
,using
и прочие&
, чтобы исходник из адского адаfor (std::vector<MahType<Foo, Bar::Buz<BufferType, std::allocator<BufferType>>*>::iterator i = foos.begin(); i != foos.end(); ++i) { (*i)->foobar(); /*...*/ }
превратился в абсолютно эквивалентный и куда более читаемый
using BarBuzBuffer = Bar::Buz<BufferType, std::allocator<BufferType>>; using MahFooType = MahType<Foo, BarBuzBuffer>; std::vector <MahFooType*> foos; /*...*/ for (auto _i = foos.begin(); _i != foos.end(); ++_i) { MahFooType * i = (*_i); i->foobar(); /*...*/ }
или менее совместимый
for (auto * i : foos) {}
rsashka Автор
08.11.2022 07:36Вообще то в приведенных вами примерах, косая черта перед термином обязательно будет всего в одном месте, перед for, точнее \while(){ }
Если говорить про префиксы вообще, то он должен быть при объявлении
$foos и $i, как локальных переменных.
Конечно, при желании можно через макросы определить \BarBuzBuffer и \MahFooType в конструкции using, но лучше это сделать с помощью создания новых типов.
Что же касается аналога цикла foreach в NewLang, то его можно сделать двумя способами, оператором раскрытия списка:
$summa := 0; $dict := (1,2,3,4,5,); \while( dict ) { # Первый элемент словаря перемещается в item $item, dict := ... dict; summa += item; };
Или с помощью итератора:
$fact := 1\1; $mult := 1000..1..-1?; # Итератор от 1000 до 2 \while( mult ?! ) { fact *= mult !; }; fact; # Вывести итоговый результат
Причем, если вас смутили знаки вопроса, то можно сделать и так:
$mult2 := \iter(1000..1..-1); \while( \curr(mult2) ) { fact *= \next(mult2); };
iCpu
08.11.2022 09:30Меня смущает, что в вашем "примере покороче без знаков вопроса" пять слешей. Которые не нужны.
Кстати, а могут быть одноимённые функции, макросы и переменные?
$next := \next(next(foobar))
А может ли макрос возвращать макрос, который можно разыменовать слешем?\\findMacroFor(situation)
rsashka Автор
08.11.2022 09:41Слеш перед термином однозначно указывает на имя макроса, а использование префиксов для них является обязательным.
Функция с именем макроса тоже может быть как и такой же объект в другом модуле или пространстве имен.
Макросы не возвращаются и не разименовываются, так как обрабатываются отдельным парсером ДО анализа компилятором языка.Именно этот момент позволяет использовать в макросах не только обычные операторы, но и полностью менять сам синтаксис (при большом извращенном желании).
iCpu
08.11.2022 09:47+2Слеш перед термином однозначно указывает на имя макроса, а использование префиксов для них является обязательным. Функция с именем макроса тоже может быть как и такой же объект в другом модуле или пространстве имен.
Помню я те прекрасные времена, когда ошибка в программе на питоне была из-за двух пробелов. Теперь ещё и слеши...
Макросы не возвращаются и не разименовываются, так как обрабатываются отдельным парсером ДО анализа компилятором языка.Именно этот момент позволяет использовать в макросах не только топовые операторы, но и полностью менять сам синтаксис (при большом извращенном желании).
То есть параметризировать сборку через параметризацию подстановки макросов нельзя? Как-то странно.
rsashka Автор
08.11.2022 09:54Помню я те прекрасные времена, когда ошибка в программе на питоне была из-за двух пробелов. Теперь ещё и слеши...
Слеши хотя бы видать, в отличии от пробелов :-)
То есть параметризировать сборку через параметризацию подстановки макросов нельзя?
Если честно, то я пока про это не задумывался, поэтому спасибо за идею (Хабр — ума палата работатет!). Может быть это действительно имеет смысл, но буду про это думать при разработке компилятора.
iCpu
08.11.2022 11:36+1Слеши хотя бы видать, в отличии от пробелов :-)
PVS-Studioне даст соврать, видимость символа ни капельки не уменьшает вероятность опечатки или пропуска правки во время копипасты.
Если честно, то я пока про это не задумывался, поэтому спасибо за идею
Вопрос с подвохом, потому что оба варианта хуже. Если позволить многоуровневые макросы вида
\\\foo() -> \\bar() -> \buz()
получается нечитаемая фигня без контроля вложенности, ломающая всё на своём пути. Если не позволить, параметризация макросов должна проходить через ветвления аля#ifdef #elif #endif
. Что, в общем-то, неплохо, но ничем не лучше уже существующих решений, как по читаемости, так и по удобству. Вам бы макросы сделать, как код, выполняющийся над синтаксическим деревом языка в месте подстановки. Но в этом месте можно и порваться.
rsashka Автор
08.11.2022 12:03Из-за особенностей синтаксиса NewLang, его компилятор (читай парсер) получается очень простой, так как для анализа ему не требуется AST со списком определенных ранее идентификаторов и типов данных.
И когда я писал про идею с параметризацией макросов, то имел ввиду, что это можно сделать преобразовав набор макросов в обычный словарь, доступный даже внутри исполняемого файла.
Тогда его можно будет экспортировать (пересоздать набор макросов, с которыми собирался и выполнялся исходник), так и выполнить загрузку модуля с переопределением макросов.
Еще раз, большое спасибо за саму идею. Тут нужно подумать, что и как это можно сделать. Ведь еще одним следствием строгого синтаксиса является доступность компилятора в рантайме, поэтому и трансформация кода за счет переопределения набора макросов вполне живая идея.
Вам бы макросы сделать, как код, выполняющийся над синтаксическим деревом языка в месте подстановки. Но в этом месте можно и порваться.
Цитата из статьи:
Но, давайте скажем простыми словами, что такое «интерфейс» — это такой именованный набор публичных чисто-виртуальных методов, к которому в то же время не привязаны никакие приватные методы или члены данных. Всё! ...
На самом деле у меня сейчас методы классов именно так и сделаны. Они не принадлежат конкретному классу, а являются обычными функциями в определенном пространстве имен. И при их создании встроены различные проверки.
Например, в NewLang лямбда функция не имеет специального синтаксиса, а при создании проверяется имя создаваемой функции, и если оно отсутствует (вместо имени используется подчерк), что объект не регистрируется (точнее регистрируется, но без имени).
Поэтому идея в статье не только здравая, но и вполне рабочая, сам проверял :-)
iCpu
08.11.2022 13:33+2Не, идею с подменой макросов лучше оставить за бортом. На плюсах уже сколько десятилетий блюют от заголовочника <windows.h> и его глобально переопределения макросов min и max, перекрывающее даже стандартную библиотеку std. Вы НЕ хотите такого в своём ньюланге. Как минимум, импорт-экспорт нужно реализовывать, как в питоне, контролируя пространство. в которое попадают импортированные символы.
А вообще, цельтесь на AST. Так хотя бы будет оправдание, зачем нужен новый язык с кучей слешей. Пока что лучший аргумент "за" звучал просто "пусть будет". Сильно, но не очень.
rsashka Автор
08.11.2022 13:49Как минимум, импорт-экспорт нужно реализовывать, как в питоне, контролируя пространство. в которое попадают импортированные символы.
Вау, большое спасибо уже за вторую идею! Это очень важный момент, который я упустил из виду, когда делал импорт функций из модулей!
rsashka Автор
08.11.2022 15:49А если делать импорт только в конкретную область имен, то что мешает область имен использовать и в макросах? Это нужно обмозговать. Еще раз спасибо!
k0stu
08.11.2022 11:54превратился в абсолютно эквивалентный и куда более читаемый
Вы серьезно называете это читаемым?
iCpu
08.11.2022 13:04+1"Куда более читаемым" - да. Я плюсы в пример привожу не столько потому, что на них пишу, сколько потому, что на них можно писать:
1) нечитаемо
2) ужасно
3) страшно
4) не по принятым стандартам
ShefEr
07.11.2022 18:52+7Интересует какую проблему решает этот язык, которую не смогли решить уже существующие
perfect_genius
08.11.2022 01:13+1У NewLang тензорные вычисления и рациональные числа неограниченной точности доступны «из коробки».
Похоже, проблему неудобности существующих языков для машинного обучения.
s_f1
07.11.2022 19:18+3Ну наконец-то новый язык! А то всё старые. Мы заждались!
PS серьёзно – что за название? Это как автомобиль NewCar, или отель NewHotel.rsashka Автор
07.11.2022 20:04-2Этот проект очень долго был без собственного названия и в публикациях
назывался просто и абстрактно «новый язык». Но после нескольких статей,
временное название «новый язык» постепенно превратилось в имя
собственное NewLang, которое я и решил в конечном итоге оставить
(что еще раз подтверждает поговорку, что нет ничего более постоянного,
чем что-то временное).
dyadyaSerezha
07.11.2022 23:44+8Да, неудачное название. Предлагаю такое, покруче:
/::New<->/:Lang:
rsashka Автор
08.11.2022 06:04Тогда уж HabrLang, так как он делается в том числе и с вашей помощью (Хабр — ума палата)
BugM
07.11.2022 21:18+2А зачем он? В 21 веке принято языки делать для какой-то цели. Go - простой для перекладывания джейсонов. Rust - более безопасная замена плюсам. Kotlin - джава с кучей сахара. И тому подобное.
А ваш язык зачем нужен?
rsashka Автор
07.11.2022 21:22-2Вы не заметили самую первую главу с названием "Зачем нужен NewLang?" или что-то в ней не поняли?
BugM
07.11.2022 21:28+2Вы в дальнейшем тексте про это полностью забываете и пишете про что угодно другое.
Тогда где подходящие примеры из области для которой этот язык предназначен? Где сравнение с тем что там сейчас используют массово?
rsashka Автор
07.11.2022 21:39Дальше я пишу про новые фичи текущего релиза.
А ниже есть ссылки на полную документацию, в том числе и с примерами, преобразования типов и data comprehensions
BugM
07.11.2022 21:50+4Простите, но нет. Это однострочники которые непонятно зачем.
Примеры и сравнение с другими языками должны быть комплексными. Чтобы по ним можно было понять какая задача решена и оценить почему она на вашем языке решается лучше или удобнее.
rsashka Автор
07.11.2022 22:08+1Примеры и сравнение с другими языками должны быть комплексными. Чтобы по ним можно было понять какая задача решена и оценить почему она на вашем языке решается лучше или удобнее.
А вот тут я с вами полностью согласен! Это обязательно нужно будет делать! Надеюсь, что в следующей версии я доделаю ООП (сейчас нет конструкторов и деструкторов) и получится сделать импорт нативных классов C++.
Когда это заработает, тогда и можно будет делать полноценные примеры и сравнения, т.к. сейчас не достаточно функционала для серьезных примеров (я даже сеточку натренировать не могу, т.к. не доделан импорт С++ функций с учетом манглинга имен).
newpy
07.11.2022 21:20+2Перегруженный синтаксис - это просто невозможно использовать.
Такое ощущение, что другого способа парсить синтаксические конструкции языка при реализации компилятора/интерпретатора - просто не нашлось.
Или, что автор печатает не на обычной клавиатуре, а на очень специфической, в которой дотянуться до всего этого многообразия довольно проблематично. Таким вероятно язык и задумывался - под специфичную клавиатуру, восприятие, сложный набор и ментальность.
KISS.
P.S. Я уж молчу что напоминает какую-то адскую смесь всех языков в мире. Говорят откровенно, глаза текут. Не хватает чистоты. Но наверное это только мне и только кажется. Простите меня за это.rsashka Автор
07.11.2022 21:33Мне очень хотелось сделать синтаксис языка, который бы имел возможность свободной модификации по любому желанию разработчика. Это было возможно только путем отказа от ключевых слов и разработки синтаксиса исключительно на правилах.
Ritan
07.11.2022 22:08+2Проблема такого синтаксиса в том, что у каждого разработчика будет своё мнение о том, каким он должен быть. Тут иной раз договориться о пробелы vs табы не могут, а вы предлагаете ключевые слова выбирать.
В результате никакая команда из более чем 3-х человек просто не станет такой язык использовать
rsashka Автор
07.11.2022 22:17-2А вот тут и получается самое вкусное и интересное! Если декларировать автоматическое форматирование как обязательное требование к оформлению кода, тогда можно вообще не договариваться, а использовать всегда свое привычное.
Ведь синтаксис языка универсальный и имея набор макросов для определения DSL, исходный код можно конвертировать в любую сторону!
Получил код, преобразовал в свой набор ключевых слов, отправил его на ревью, например китайцу, а он его преобразовал в свой вариант DSL для проверки, а потом вернул назад. Как вам такая фишка? Это даже круче чем в 1С :-)
Ritan
08.11.2022 00:21+2Увы. Подозреваю это хорошо звучит только в теории. Это будет ад для поддержки любых средств ревью кода( gitlab, github итд ). Плюс такая система будет наверняка приводить к грязным дифам в любой VCS. Файлы проектов unity в виде автогенерированных xml не дадут соврать
rsashka Автор
08.11.2022 06:02Если синтаксис основан на однозначных правилах, то он будет форматироваться всегда одним и тем же способом без грязных дифов. С этой задачей любая современная IDE справится даже для обычного языка.
NN1
08.11.2022 00:46Может стоит посмотреть на примеры других тактх языков?
Например: https://github.com/rsdn/nitra
rsashka Автор
08.11.2022 05:57Посмотрел ваш пример языка:
using Nemerle.Extensions; class EventRaiser { public event Ev : Action; } class Cls { public Raiser : EventRaiser = EventRaiser() <-{ Ev += () => Handler() } Handler() : void { } }
Или другой пример из репозитория:
Hidden text
using Nitra; using Nitra.Runtime; using Nemerle; using Nemerle.Collections; using System.Collections.Generic; language SimpleCalc { syntax module SimpleCalcSyntax start rule Start; } syntax module SimpleCalcSyntax { using Nitra.Core; [StartRule] syntax Start = Expr !Any { [Cached] Value() : double = Expr.Value(); } regex Digits = ['0'..'9']+; regex Id = ['a' .. 'z', 'A' .. 'Z']+; [StartRule] syntax Expr { Value() : double; missing Value = double.NaN; | [SpanClass(Number)] Num = Digits { override Value = double.Parse(GetText(this.Digits)); } | Call = Id '(' Id Id ')' { override Value = 42.0; } | Rounds = '(' Expr ')' { override Value = Expr.Value(); } precedence Additive: | Add = Expr sm '+' sm Expr { override Value = Expr1.Value() + Expr2.Value(); } | Sub = Expr sm '-' sm Expr { override Value = Expr1.Value() - Expr2.Value(); } precedence Multiplicative: | Mul = Expr sm '*' sm Expr { override Value = Expr1.Value() * Expr2.Value(); } | Div = Expr sm '/' sm Expr { override Value = Expr1.Value() / Expr2.Value(); } | Mod = Expr sm '%' sm Expr { override Value = Expr1.Value() % Expr2.Value(); } precedence Power: | Pow = Expr sm '^' sm Expr right-associative { override Value = System.Math.Pow(Expr1.Value(), Expr2.Value()); } precedence Unary: | Neg = '-' Expr { override Value = -Expr.Value(); } } }
И не могу согласиться, что он может быть примером для подражания в плане легкости и понятности синтаксиса. Возможно этот язык действительно решает нужную задачу правильным образом. Но его синтаксис отнюдь не эталон.
NN1
08.11.2022 08:55Это синтаксис описание языка, а сам язык можете хоть в стиле C сделать:
https://github.com/rsdn/Nitra-Mini-C/blob/master/Samples/Fib/Fib/Fib.test
rsashka Автор
08.11.2022 09:20Кхм... дело в том, что в данном случае аналогом NewLang будет сам(а?) Nitra и его синтаксис описание языка, а не результирующий абстрактный языка "для примера".
NN1
08.11.2022 09:26Моё дело показать похожую идею.
Чтобы сделать язык, который умеет расширяться за счёт макросов как Nemerle, нужно вложить немало времени и сил.
А сам синтаксис может быть каким-угодно.
rsashka Автор
08.11.2022 09:32Спасибо за ссылку. Синтаксис Nemerle очень похож на описание БНФ формы (а наверно она и есть, я не разбирался). И этаже форма записи используется и в других лексерах - парсерах.
И вы наверно удивитесь, но NewLang уже заимствовал из этой формы записи операторы определения терминов (::=).
yurec_bond
OMG. /Слеши /перед /ключевыми /словами /в /разы /понижают /читаемость /языка.
rsashka Автор
Не только слеши, а еще двоеточия, амперсанд, знак доллара и собачка.
А так как слеш перед словом, это макрос определенный пользователем, то и сами слова могут быть о-го-го какими :-)
Tangeman
Хоть по бэкслэшам уже не раз прошлись чуть ранее, есть гораздо менее приятный момент для тех у кого немецкая раскладка, где бэкслэш - это необходимость нажимать две клавиши одновременно (причём вторая клавиша это не shift а правый alt, он же alt gr). А нужен он довольно часто, судя по всему.
rsashka Автор
Нет ничего совершенного :(
Я на русской раскладке тоже мучаюсь, но не беклешем, а с решеткой "#"