Модули и пакеты
В NewLang реализована концепция программных модулей и пакетов - которая повторяет идею иерархического расположения файлов в каталогах файловой системы, примерно так же, как это сделано в языке Python, но разделителем имен выступает не точка, а символ “\” (как разделитель каталогов в Windows).
Имя модуля может содержать только строчные английские буквы, цифры или символ подчерка (кроме первой и последеней позиции). Данное ограничение связано с прямым отображением имен модулей на объекты в файловой системе, так как у разных файловых систем могут быть различные возможности с поддержкой кодировок и разные требования к преобразованию регистров символов.
Под модулем в NewLang понимается файл с исходным кодом (с расширением *.src). Модули предназначены для того, чтобы в них хранить часто используемые функции, классы, константы и т.п. Их можно условно разделить на модули и программы: программы предназначены для непосредственного запуска, а модули для импортирования их в другие программы, но функционально модули и программы ничем между собой не отличаются.
Объекты модуля
Все объекты, определенные внутри одного модуля без указания глобального простанства имен, видны только в рамках текущего файла и из других модулей, которые были подключенны после его определения.
Время жизни статических и локальных переменных самого верхнего уровня модуля одинаковые и ограничены временем жизни самого модуля, но статическая и локальная переменные модуля отличаются с точки зрения многопоточности.
Статическая переменная модуля всегда будет в единственном экземпляре для всех потоков, тогда как локальная переменная модуля будет для каждого потока своя (аналог thread_local (C++11)).
Как импортировать модули?
Относительное имя программного модуля начинается на один символ “\”, и в этом случае расположение файла программного модуля указывается относительно текущего файла.
Абсолютное имя программного модуля начинается на два символа “\\” и указывает на файл модуля относительно каталога текущего исполняемого файла (или указанного в списке каталогов для поиска программных модулей, который можно переопределить, например, с помощью аргументов командной строки).
Самый простой способ импортировать модуль, это записать его имя со скобками как при вызове функции. Причем в скобках можно передать аргументы для инициализации модуля, список импортируемых функций и т.д.
Если перед именем импортируемого молуя стоит символ препроцессора, т.е. имя модуля начинается на ‘@\’ или ‘@\\’, это означает, что модуль импортирует не только код, но и все препроцессорые макросы.
Так как NewLang разрабатывается как компилируемый язык, то загрузка модулей возможна как статически, так и динамически (очень похоже на статическую и динамическую ликновку с dll библиотеками).
- \dir\module() - статическая загрузка модуля по относительному пути
- @\dir\module() - статическая загрузка модуля и его макросов по относительному пути
- \\root\dir\module() - статическая загрузка модуля по абсолютному пути
- \\("каталог\файл") - динамическая загрузка модуля во время выполнения программы
- @\\( " каталог\файл " ) - При динамической линковке модуля, макросы импортировать нельзя.
При динамической линковке, компиляция исходного кода модуля и все проверки будут выполнятся только во время выполнения приложения, тогда как статическая загрузка модуля позволяет выявить возможные ошибки еще на этапе компиляции программы.
Что такое пакет в NewLang?
Пакет в NewLang – это имя каталога, который включает в себя другие каталоги и модули и при этом содержит дополнительный файл __init__.src.
Пакеты используются как дополнение к пространству имен, что позволяет работать с модулями через указание уровня вложенности (через символ \). Но в отличии от Python и Java, где модули и пакеты заменяют собой пространство имен (namespace), в NewLang модульная структура и области имен используются одновременно и при указании полного имени объекта, программные модули и пространства имен можно объединять.
Например, полное имя переменой можно записать с указанием программного модуля \root\dir\module::ns::name::var
,
где root и dir это каталоги в файловой системе относительно текущего моделя, а module — имя файла, т.е. root/dir/module.src
Для импортирования пакетов используется тот же синтаксис, что и для работы с модулями.
Использование пакетов
Рассмотрим следующую структуру пакета:
fincalc
|-- __init__.src
|-- simper.src
|-- compper.src
|-- annuity.src
Пакет fincalc содержит в себе модули для работы с простыми процентами (simper.src), сложными процентами (compper.src) и аннуитетами (annuity.src). Файл __init__.src в отличии от Python, не может быть пустым, а должен в явном виде содержать команды загрузки модулей, входящих в пакет
Например для нашего случая содержимое __init__.src может быть вот таким:
\simper();
\compper();
\annuity();
Для использования функции fp из модуля для работы с простыми процентами, можно использовать один из следующих вариантов (для сравнения приведен аналогичный код на Python, когда это возможно):
Импорт одного модуля без указания псевдонима:
Python:
import fincalc.simper
fv = fincalc.simper.fv(pv, i, n)
NewLang:
\fincalc\simper();
fv := \fincalc\simper::fv(pv, i, n);
С указанием псевдонима имени модуля:
Python:
import fincalc.simper as sp
fv = sp.fv(pv, i, n)
NewLang:
sp := \fincalc\simper();
fv := sp.fv(pv, i, n);
Импорт одной конкретной функции:
Python:
from fincalc import fv
result = fv(pv, i, n)
NewLang:
\fincalc\simper(__import__="fv");
result := fv(pv, i, n);
Импорт всего пакета:
NewLang:
\fincalc();
result := fv(pv, i, n);