Именование объектов
В качестве имен объектов и названий типов данных можно использовать буквы, цифры и знаки подчеркивания в любых комбинациях, при условии, что первый символ имени не является цифрой. Имя не может состоять из единственного знака подчеркивания, так как это специальный термин, который используется в служебных целях.
Для избежания коллизий имен можно использовать пространство имен и модульную структуру кода, которые NewLang поддерживает одновременно. Допускается переопределение и перегрузка функций.
При создании объектов имя идентификатора может содержать один или несколько специальных символов - квалификаторов (или сигилов), за которыми закрепелено определенное значение.
Квалификаторы имен:
- ‘@’ — префикс собачка используется для указания имени макроса, который обрабатыватся препроцессором до начала синтаксического анализа исходного текста программы.
- ‘$’ — знак доллара в начале имени объекта обозначает временное имя, время жизни которого ограничено семантикой языка
- ‘::’ — двойное двоеточие разделяет пространства имен и является признаком постоянного объекта, значение которого сохраняется после выхода из текущей области видимости. Если имя начинается на ‘::’, то область его видимости будет глобальной и он будет доступен из других модулей программы.
- ‘.’ — префикс точка используется при обращении к полю модуля или класса (ограничивает область видимости только текущим объектом). Префикс точка так же может быть использован при определении или вызове функции для указания имени аргумента, который нельзя перекрыть макросом препроцессора.
- ‘\’ — обратный слеш в начале имени обозначает имя программного модуля, а так же разделяет имена каталогов в иерархии размещения программных модулей в файловой системе.
- ‘:’ — двоеточие в начале термина обозначает имя типа данных или конструктор класса, которые всегда является постоянными
- ‘%’ — префикс знак процента указывается для нативных имен переменных и функций
Разрешение имен (name lookup)
Если имя объекта не содержит квалификатора, оно является простым.
И когда NewLang встречает простое имя объекта без квалификатора, то в дело вструпает алгоритм разрешения имен (name lookup), который связывает простое имя, встретившееся в исходном тексте программы, с его декларацией или созданным объектом по его внутреннему имени.
Разрешение простых имен без квалификатора (name lookup, или поиск имени функции/переменной) происходит всегда в строго определенном порядке:
- в первую очередь происходит поиск имени среди макросов
- в случае вызова функции выполняется статическое разрешение имен при перегрузке функций
- далее выполняется поиск имени среди локальных объектов до объектов текущего модуля
- в последнюю очередь выполняется поиск среди статических объектов с постепенным расширением пространства имен области поиска от текущей до глобальной
Такая последовательность разрешение имен всегда предоставляет возможность переопределить глобальные/локальные объекты или имена аргументов у функций для уже существующего кода без его серъезных изменений.
Например, для имени name
в области имен ns
поиск происходит в следующей последовательности:
@name
-> %name
-> $name
-> ns::name
-> ::ns::name
-> ::name
,
а для имени аргумента arg
проверяется только @arg
:
ns:: {
name(arg="value");
};
И в тоже время, всегда остатся возможность указать конкретный объект не зависимо от работы алгоритма разрешение простых имен. Достаточно указать квалификатор в имени объекта в явном виде.
Например, обратиться к глобальному объекту name из пространства имен ns из примера выше, нужно по полному имени объекта ::ns::name
,
а именованнй аргумент ’. arg’ не будет заменен макросом @arg
, если такой будет определен:
::ns::name(.arg="value");
Расширение поиска простанства имен
Для указания нескольких областей имен для расширенного поиска при разрешение простых имен без квалификатора,
используется синтаксическая конструкция ... = ns::name, ns::name2;
или @using(ns::name, ns::name2);
при использовании DSL.
Поиск в перечисленных областях имен произволится в порядке их указания до конца текущего блока кода,
до следущего оператора расширенного поиска или до оператора ... = _;
,
который отменяет расширенный поиск в пространствах имен до конца текущего блока кода (до конца модуля).
Предварительное объявление
В тексте программы можно ссылаться только на реально существующие (созданные) объекты. Но для тех случаев, когда требуется сослаться на объект, который создан в другом модуле или будет создан позже, можно сделать предварительное объявление, при котором компилятор регистриурет имя и тип объекта без его реального создания.
За счет предварительного объявления можно ссылаться только на статические объекты (типы данных), или локальные поля класса о которых компилятор ещё не знает, но которые будут определены в процессе компиляции позже.
Для предварительного объявления можно использовать внутреннее имя или полное квалифцированное имя, которое должно будет в точности совпадать с именем объекта при его последующем создании.
Для предварительного объявления используется точто такой же синтаксис, как и при реальном создании объекта, только с права от операторо создания должно быть указано многоточие.
Область видимости предварительного объявления соответствует области видимости его размещенея, а не реальной области видимости объекта, который будет в последствии создан.
# Предварительное определение переменной модуля
# Действует для всего модуля
var_module:Int32 := ...;
func() ::= {
# Предварительное объявление с помощью DSL
# (действует только внутри тела функции)
@declare( func2(arg:Int32):Int32 );
var_module = func2(var_module);
@return var_module;
};
func2(arg:Int32):Int32 ::= {
@return $arg*$arg;
}
var_module:Int32 := 1;
Имена аргументов, специальные и системные имена
Обозначение имен аргументов у функций очень похоже на обращение к аргументы в bash скриптах, где “$1” или “$name” — порядковый номер или имя соответствующего аргумента.
Зарезервированное имя “$0” обозначает текущий объект, а именем “$$” обозначается родительский объект.
Все аргументы функции собранны в одном словаре со специальным имеенм $*
Неизменяемая виртуальная переменная компилятора “$^” содержит результат выполнения последнего оператора или блока кода.
Полное имя текущего модуля содержится в переменной @\\, а текущая область имен в переменной @::, т.е.:
# Имя файла filename.src в каталоге dir
ns:: { # Использовать пространство имен ns
name:: {
# Команда препроцессора "@#" - преобразовать в символьную строку
ns_str := @# @::; # Строка с областью имен "::ns::name::"
mod_str := @# @\\; # Строка с именем модуля "\\dir\filename"
full_str := @# @\\ @## @# @::; # "\\dir\filename::ns::name::"
};
};
Неизменянемые объекты
NewLang реализует динамическую иммутабельность. Это свойство лексического объекта, а не свойство типа данных и означает, что объект может стать не изменяемым не только в момент создания, но и в будущем при одной из операций присвоения/измениения значения.
Для придания объекту свойства иммутабельности (невозможности изменять свое значение далее по тексту программы), используются символ ‘^’ карет (крышечка/домик) после имени объекта.
val ::= 0; # Create mutable variable
val = 1; # Set new value
val^ = 2; # Set unchangeable value
val = 3; # Error !!!
Своство иммутабельности у аргументов функций зависит от типа функции. У чистых функций аргументы передаваемые по ссылке (владеющие переменные) всегда иммутабельные. Свойство мутабельности у остальных аргументов указывется при определении функции на общих основаниях:
func( arg1^, arg2) ::= {
arg1 = 1; # Error (arg1 - immutable)
arg2 = 1; # OK
arg2^ = 2; # OK - set immutable value
arg2 = 3; # Already error
}
Свойство неизменяемости может относится не только к данным объекта, но и вызовам функций.
По умолчанию, вызовы функций не ограничены требованиями к неизменяемости объектов и могут вычисляться в любое время, в том числе и во время компиляции (если это в возможностях оптимизирующего компилятора), например, вычислить выражение чистой функции с аргументом-литералом.
Однако такое поведение компилятору можно указать явно (вычислять значение функций во время компиляции), если указать признак иммутабельности (символ ‘^’ карет) у стоящей в правой части выражения функции.
cube(arg) ::- {$arg * $arg * $arg}; # Pure func
val1 ::= cube(3); # Сalc at the discretion of the compiler
val2 ::= cube^(3); # Force calc in compile time
square(arg) ::= {$arg * $arg}; # Normal (non pure) function
val1 ::= square(3); # Calc in runtime
val2 ::= square^(3); # Error calc on compile time