NewLang — это язык программирования высокого уровня общего назначения cо строгим синтаксисом на основе грамматических правил, который с помощью макросов препроцессора легко превращается в традиционный на основе ключевых слов.
NewLang реализован как транспилер (source-to-source translator) в код C++ и совместим ним на низком уровне. В нем возможны непосредственные вызовы С++ функций и наоборот, из С++ можно вызвать функции NewLang. Разрешены вставки кода на языке С++ непосредственно в тексте программы и генерация выходного файла в виде обычного С++ кода.
Тензорные вычисления и рациональные числа не ограниченной точности реализованы как базовые типы данных и поддерживаются на уровне синтаксиса языка без подключения дополнительных библиотек. Тензорные вычисления сделаны на базе библиотеки libtorch, а рациональные числа неограниченной точности с помощью OpenSSL.
В NewLang реализовано автоматическое безопасное управление памятью без сборщика мусора и гарантируется отсуствие ошибок при управлени памятью еще на этапе компиляции исходного текста программы.
Основные свойства и особенности языка:
- возможность работы как в режиме интерпретатора, так и компилятора*
- динамическая и статическая типизация с возможностью указания типов в явном виде
- статическая типизация является условно-строгой (автоматическое приведение типов отсутствует, но допускается преобразование между некоторыми типами данных. Например, целое число может быть автоматически преобразовано в вещественное или рациональное, но не наоборот)
- автоматическое управление памятью без сборщика мусора на основе сильных и слабых указателей
- ООП* в виде явного наследования классов и «утиная типизация»
- на уровне синтаксиса поддерживается несколько типов функций (обычные и чистые функции без побочных эффектов)
- необязательные и именованные параметры функций
- простая интеграция с уже существующими программными библиотеками (в том числе импорт нативных переменных, функций и классов* из С/С++.)
- имеется REPL (read-eval-print loop — «цикл: чтение — вычисление — вывод»)
*) Данные возможности запланированы к реализации
Зачем нужен NewLang?
У всех современных языков программирования происходит постоянное развитие (усложнение) синтаксиса по мере выхода новых версий.
Это является своего рода платой за появление новых возможностей языка и воспринимается пользователями как естественное явление.
Но одновременно является и серьезной проблемой, так как с выходом новых версий языка в него добавляются новые ключевые слова и синтаксические конструкции, что неизбежно повышает порог входа для всех новых пользователей.
Еще одним следствием этого процесса становится постоянное увеличение сложности разработки и поддержки уже созданных программных продуктов, когда старый программный код дорабатывается с применением уже новых стандартов и постоянным увеличением старого легаси кода.
NewLang разработан как "надстройка" над языком C++ с сохранением всех его возможностей и характеристик, но лишенный его недостатков в виде очень сложной лексики, адресной арифметики, ручного управления памятью и ограниченностью встроенных типов данных.
Тензорные вычисления и рациональные числа неограниченной точности доступны «из коробки» и поддерживаются на уровне синтаксиса для удобной записи литералов. Тензорные вычисления сделаны на базе библиотеки libtorch, а рациональные числа неограниченной точности с помощью OpenSSL.
Пример скрипта Hello world! на NewLang
#!../output/nlc --eval
hello(str) := {
# Импорт и вызов функции printf стандартной библиотеки
printf(format:FmtChar, ...):Int32 := %printf...;
printf('call: %s', $str);
$str;
};
hello('Привет, мир!');
Вывод:
call: Привет, мир!
Привет, мир!
Пример вычисления факториала 40 на NewLang (базовый синтаксис)
#!../output/nlc --eval
fact := 1\1; # Рациональное число 1 (без ограничения точности)
mult := 40..1..-1?; # Итератор из диапазона для множителей от 40 до 2
[mult ?!] <-> { # Цикл, пока не закончатся данные итератора
fact *= mult !; # Получить текущий множитель и перейти на следующий элемент итератора
};
fact # Вывести итоговый результат
Вывод:
815915283247897734345611269596115894272000000000\1
Тот же код с использованием DSL синтаксиса
#!../output/nlc --eval
fact := 1\1;
mult := iter( 40..1..-1 );
while( curr(mult) ) {
fact *= next(mult);
};
fact;
Вывод:
815915283247897734345611269596115894272000000000\1