Переотправлю письмо через Яндекс, а то с Mail.Ru какие-то проблемы.
Добрый день всем!
Как многие в рассылке знают, я работаю над компилятором Рефала-5λ — точного
надмножества Рефала-5. Одним из полезных расширений языка являются нативные
вставки — вставки кода C++ в исходном коде на Рефале. Например,
/* это файл на Рефале */
%%
#include <stdio.h>
%%
$ENTRY Go {
= <Hello> <Hello>
}
Hello {
%%
printf("Hello!\n");
refalrts::splice_to_freelist(arg_begin, arg_end);
return refalrts::cSuccess;
%%
}
Как это работает. Компилятор Рефал-5λ создаёт исполнимые файлы операционной
системы (.exe-файлы для Windows, ELF для Linux, какие-то ещё для macOS).
Скомпилированный файл устроен на манер SFX-архива: он состоит из
префикса-интерпретатора, исполнимого файла операционной системы, и
суффикса-интерпретируемого кода — двоичного языка сборки. Как если бы в конец
интерпретатора refgo приписали бы несколько .rsl-файлов.
Когда компилятор транслирует файлы без нативных вставок, то он берёт уже
готовый префикс-интерпретатор (скомпилированный ранее) и в конец к нему
приписывает файлы языка сборки.
Если среди исходных файлов есть файлы с нативными вставками, то компилятор из
таких файлов создаёт исходники на C++, содержащие текст этих самых нативных
вставок. Потом вызывает компилятор C++, передавая ему исходники интерпретатора
языка сборки и эти самые файлы с текстом вставок. Получается исполнимый файл,
который становится префиксом-интерпретатором. Функции, описанные на C++ (вроде
функции Hello в примере выше) становятся своего рода встроенными в
интерпретатор функциями.
Кстати, все библиотечные функции (Prout, Add, Sub, Type) тоже описаны в файле
на Рефале примерно таким образом:
https://github.com/bmstu-iu9/simple-refal/blob/master/src/srlib/Library.sref
Но уже давно я планирую реализовать динамическую загрузку модулей на манер
.dll/.so. В этом случае компилятор сможет порождать не-.exe модули, которые
могут подгружаться как статически (если прописана соответствующая ссылка в
языке сборки), так и динамически (вызывается соответствующая библиотечная
функция для загрузки модуля и поиска функции в нём). Если исходные файлы модуля
не содержат нативных вставок, то создаётся динамически загружаемый модуль
только с одним языком сборки. Если содержат — вызывается компилятор C++,
который создаёт .dll/.so операционной системы с нативными функциями, и к нему в
хвост приписывает язык сборки как раньше.
На этом пути много технических проблем, я с ними справлюсь, скорее всего. Но
вот возникла одна архитектурная, про которую я не вижу красивого гладкого
решения.
Нужно (как и в случае .dll/.so) обеспечить корректную обработку повторной
загрузки модуля. Т.е. если некоторый файл был загружен, не грузить его
повторно, а только увеличивать у него счётчик ссылок.
Но вот как понять, что он уже был загружен? По пути к файлу это сделать сложно.
На UNIX-системах один и тот же файл может быть доступен как subdir/filename,
./subdir/./././filename /home/user/subdir/filename,
/home//user///subdir////filename,
/home/otheruser/../user/otherdir/../subdir/filename и т.д. На Windows ещё
веселее: добавляются тонкости с регистром символов в именах, синонимичность / и
\ и текущие папки на разных дисках. Эмулировать всё это слишком громоздко и тем
самым некрасиво.
Можно к каждому файлу добавлять хэш, таким образом обнаруживать идентичные
модули по их содержимому (даже если у них разные имена файлов). Но мне
почему-то это кажется костылём.
С уважением,
Александр Коновалов