Git Product home page Git Product logo

mrefal's Introduction

Компилятор Модульного Рефала

Модульный Рефал — ещё один диалект Рефала. Отличается от других диалектов системой модулей в духе Оберона, возможностью расширения внешними функциями на C++ и временной неразвитостью выразительных возможностей :-).

Цели проекта

Цели написаны в произвольном порядке. Каждую цель сопровождает список решений дизайна, проистекающих из неё.

  1. Язык Рефал — язык символьных вычислений. Язык должен продолжать классическую линейку реализаций Рефала, языка основанном на конкатенации цепочек, сопоставлении с образцом и семантике поля зрения. Но при этом язык должен быть не менее выразительным, чем другие диалекты. Следствия:
    • на текущем этапе используется классическое списковое представление данных, в дальнейшем планируется использование других представлений, допускающих эффективную конкатенацию и семантику поля зрения,
    • используется синтаксис, схожий с синтаксисом РЕФАЛа-5,
    • указатели на глобальные функции, планируется реализация и вложенных функций,
    • планируется синтаксис, подобный Refal-7, но явно выделяющий функции, генерирующие неуспехи и недетерминированные/с побочными эффектами,
    • одно из приложений символьных вычислений — написание компиляторов, поэтому компилятор самоприменим.
  2. Максимум контроля на наиболее ранней стадии:
    • разрешение импортируемых функций на стадии компиляции — исключение ошибок компоновки типа «unresolved external reference»,
    • абстрактные типы данных = именованные скобки — реализуют инкапсуляцию данных на уровне модулей,
    • множество идентификаторов фиксируется на момент компиляции — отсутствие функций типа Implode РЕФАЛа-5,
    • указатели на функции вместо функции Mu РЕФАЛа-5.
  3. Наличие FFI с другими языками программирования:
    • компиляция в язык C++ (напрямую и косвенно через Простой Рефал), благодаря чему можно написать внешние функции на целевом языке и подключить при компоновке.
  4. Эффективность — компилятор должен выполняться и его разработка должна вестись относительно комфортно даже на древних машинах (например, Pentium I):
    • компиляция в язык C++ вместо интерпретации RASL’а,
    • система модулей, потенциально позволяющая на стадии компиляции иметь информацию об импортируемых модулях, дабы использовать её при оптимизации (например, могут появиться форматы функций),
    • оптимизации (они сейчас ведутся в родственном проекте — Простом Рефале) Надо признать, что эта цель пока далека от достижения.
  5. Модульная архитектура:
    • архитектура компилятора допускает использование нескольких различных back-end’ов и front-end’ов,
    • сейчас уже реализованы 3 разных back-end’а (в РЕФАЛ-5, в Простой Рефал и в C++, использующий Простой Рефал),
    • в библиотеке заложена (пока не реализованная) возможность переносимости на другие платформы.
  6. Переносимость — пока не реализована.

Приоритета среди целей нет, если цели противоречат друг другу, конфликт разрешается ситуативно.

mrefal's People

Contributors

mazdaywik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

mrefal's Issues

Нативные вставки в Модульном Рефале

Эта задача в некотором смысле подзадача #9.

Мотивация их внедрения довольно прозрачна — упрощение написания примитивных нативных функций на Модульном Рефале. Но сейчас к их реализации подталкивает суровая необходимость. В рамках задачи #9 требуется синхронизировать Модульный Рефал с рантаймом Простого Рефала версии 1.λ.7, а он уже требует генерации двоичного кода. Вернее, 1.λ.7 пока требует генерации инициализированного байтового массива на C++, но уже 1.λ.9 использует уже двоичный код.

Но суть одна — в новых версиях Простого Рефала используется машиногенерируемый RASL, который писать вручную крайне утомительно.

Один из подходов — написание специальной утилиты его генерации для самописных нативных модулей. Но он не масштабируется, является решением ad hoc, т.е. по сути является костылём.

Более правильный подход — переход от написания нативных модулей вручную к их генерации из кода на Модульном Рефале с нативными вставками, как это уже давно сделано в Простом Рефале (bmstu-iu9/refal-5-lambda#11).

Нерешёнными остаются вопросы, как их оформлять синтаксически.

Во-первых, иметь ли пометки back-end’а нативной вставки в исходном коде? Если не иметь, то исходный файл можно случайно откомпилировать не тем back-end’ом и получить некорректный вывод. Если иметь, то это усложняет синтаксис и дальнейшую проверку (нужна отдельная верификация). И возникнет вопрос: можно ли иметь несколько пометок разных типов в одном файле.

Во-вторых, как их оформлять. Если оформлять также, как в Простом Рефале, будет конфликт, когда мы будем описывать тело функции, которое в Простом Рефале тоже должно быть нативной вставкой (на C++). Как решение ad hoc — знаки %% экранировать внутри вставки при помощи \%%. Если оформить иным путём, то проблем не будет. Можно пойти путём языка Си и оформлять их как просто строковые константы — в лексическом анализе потребуется только дополнительное ключевое слово, например $NATIVE.

Наблюдение: для профиля SimRef нативные вставки будут содержать только нативные вставки C++ (в текущей версии), возможно, для них будет полезным отдельный синтаксис.

Задачи из Documentation/Журнал/Changes.txt

Ранее (когда не использовался GitHub) роль таск-трекера выполнял журнал проекта. Особым образом размеченные заголовки соответствовали открытию/дополнению/закрытию задач. Открытые на данный момент задачи я не буду дублировать в виде отдельных issues, вместо чего фиксации, их закрывающие буду привязывать к текущей задаче #2. Ниже перечислены открытые задачи в виде списка флажков:

  • [TODO] RASL и интерпретатор
  • [TOTHINK] Упрощение структуры Lexer'а и Parser'а
  • [TOTHINK] Двухуровневая структура синтаксического анализатора
    [TOTHINK] Рефакторинг интерфейса и реализации IModule
  • [TODO] Изменения в лексике и комментариях
  • [TODO] Блоки $INIT и $FINAL
  • [TOTHINK] Написание комплексных тестов для библиотеки
  • [TODO] Удалить исходники Простого Рефала из Модульного Рефала
  • [TOTHINK] Грамотная реализация библиотеки
  • [TODO] НД: Написание текста документации
  • [TOTHINK] Средство автоматической установки
  • [TODO] ВПВ: Репозиторий исходного кода
  • [TOTHINK] ВПВ: Opensource-лицензия
  • [TODO] Написание документации (НД)
  • [TODO] Выпуск публичной версии (ВПВ)
  • [TODO] Вложенные функции (ВФ)
  • [TOTHINK] Серьёзное расширение синтаксиса
  • [TODO] Актуализировать компилятор Простого Рефала и back-end C++/SR
  • [TODO] Ускорение работы компилятора на слабых машинах
  • [ERROR] Ошибка в генерации циклов удлинения e-переменных
  • [ERROR] Некорректная обработка двойных кавычек в компоновщике back-end’а РЕФАЛа-5
  • [TODO] Оптимизация совместного сопоставления с образцом

TODOs.txt

Добавить неполное ООП в Модульный Рефал

Мотивация

Модульный Рефал имеет встроенный механизм инкапсуляции данных — так называемые «абстрактные типы данных», АТД. Модуль может определить несколько «тегов типа» — имён АТД, которыми может помечать некоторые скобочные термы (собственно, АТД-термы). Непосредственный доступ к содержимому АДТ-термов возможен только в функциях модуля, в котором определены метки. Другие модули могут манипулировать с АТД-термами, либо импортируя модуль, где они определены и вызывая его entry-функции, либо вызывая функции-callback’и того модуля.

Механизм АТД решает свою задачу: предотвращает доступ извне к содержимому, но при этом налагает жёсткое ограничение: либо нужно импортировать модуль, либо передавать callback, либо доступа вообще нет. Таким образом, возникают затруднения при написании некоторых универсальных служб (служб, которые могут работать с любыми объектными выражениями): сортировка, преобразование к строке, сериализация.

Другое ограничение АТД (в широком смысле) в модульных языках (Модульный Рефал, Модула-2) — необходимость прямого или косвенного импорта модуля, реализующего тип данных в модулях, использующих экземпляры этого типа. В результате возникает слишком жёсткая связь между отдельными компонентами (например, в синтаксическом анализаторе захардкожена ссылка на лексический анализатор).

Решение обеих проблем хорошо известно: разделение типа и интерфейса. Первая проблема решается тем, что универсальные службы предлагают интерфейс (например, ToString), который должны реализовать все типы, совместимые со службой. Вторая проблема решается тем, что клиенты зависят только от описания интерфейса, экземпляр типа, реализующий требуемый интерфейс, передаётся в клиенты методами верхнего уровня.

Предлагаемое решение

Концепция

В Модульном Рефале единица инкапсуляции — модуль. Это свойство разумно сохранить. Модульный Рефал — функциональный язык, поэтому интерфейс опишем как набор переопределяемых функций. Как-то так.

Некоторые модули предоставляют виртуальные функции, набор этих функций формирует некоторый интерфейс. Другие модули импортируют модули с виртуальными функциями и расширяют последние дополнительными предложениями, обрабатывающими АТД, реализованные в данном модуле. После чего можно вызывать виртуальные функции, передавая в них экземпляры АТД, для которых эти функции расширены. Виртуальные функции (они всегда экспортируются, пишем $VIRTUAL, подразумеваем $ENTRY) могут вызываться как из модулей, где они определены, так и из модулей, которые импортируют модули с виртуальными функциями. При этом последние ничего не знают о модулях, которые расширяют виртуальные функции.

Синтаксис

Виртуальная функция помечается ключевым словом $VIRTUAL и имеет одно и только одно виртуальное предложение. Виртуальное предложение состоит из одного образца (для краткости далее виртуального образца), который (а) может быть только жёстким образцом (без повторных переменных и открытых e-переменных), (б) должен содержать хотя бы один терм вида $DATA — признак виртуального предложения. Перед и после виртуального предложения могут располагаться обычные предложения с обычной семантикой.

Расширение виртуальной функции может располагаться только в модуле, который импортирует модуль, определяющий виртуальную функцию. Расширение виртуальной функции записывается как функция, помеченная ключевым словом $EXTENDS и имеющая имя ИмяМодуля.ИмяВиртуальнойФункции, где ИмяМодуля — имя модуля, где виртуальная функция определена. При этом в модуле не может дважды переопределяться одна и та же виртуальная функция. Каждое предложения расширения должно уточнять виртуальный образец, при этом на месте терма $DATA должен располагаться АТД-терм. Соответствие образцовых частей расширения виртуальному образцу должно контролироваться на стадии компиляции.

Расширение виртуальной функции самостоятельной функцией не является, предложения, перечисленные в теле функции, можно вызвать только вызывав виртуальную функцию. Соответственно, ключевое слово $ENTRY к расширению не применимо ибо бессмысленно.

Семантика

Примечание. Здесь описывается идеализированная модель, реализация может (и будет) отличаться, но её поведение должно совпадать с идеализированной моделью.

После компиляции и компоновки формируется единая программа, в которой каждая виртуальная функция превращается в обычную функцию, в которой на месте виртуального предложения располагаются предложения всех расширений этой виртуальной функции. При этом относительный порядок предложений, описанных в расширении сохраняется, но относительный порядок предложений различных расширений не определён, ибо не важен (см. ниже).

Следовательно, исполнение виртуальной функции идёт в следующем порядке:

  1. Сопоставления с образцами предложений, расположенных до виртуального предложения. Если одно из них оказалось успешным, выполняется соответствующее предложение, виртуальная функция на этом завершается.
  2. Сопоставление аргумента с каждым из образцов расширений функции. Все эти образцы, уточняя виртуальный образец, различаются тегами АТД в позициях термов $DATA. В модуле, расширяющем виртуальную функцию, могут использоваться только теги данных, которые в нём определены, поэтому образцы предложений различных модулей не конфликтуют между собой. Если одно из сопоставлений завершилось успешно, выполняется соответствующее предложение расширения и виртуальная функция завершается.
  3. Сопоставление с образцами предложений, расположенных после виртуального предложения. Тут всё аналогично п. 1.
  4. Если не было успешного сопоставления, функция падает с ошибкой.

Пара слов о синтаксисе

  1. Ключевые слова $VIRTUAL и $EXTENDS избыточны: виртуальную функцию можно узнать по наличию виртуального предложения, расширение — по квалифицированному имени. Однако, добавление этих ключевых слов повышает ясность и читабельность программы.
  2. Возможное расширение синтаксиса: [$ENTRY] ИмяФункции $EXTENDS Модуль.ВиртуальнаяФункция — создаёт как обычную функцию (с именем ИмяФункции), так и расширение Модуль.ВиртуальнаяФункция с идентичным набором предложений.

Реализация

Back-end C++/SR

Непосредственная реализация семантики затруднена: модули транслируются независимо в исходные файлы на C++, которые затем уже транслируются компилятором C++. Поэтому будем действовать иначе.

Допустим, выполнение виртуальной функции дошло до виртуального предложения. Сопоставим аргумент с виртуальным образцом — допустим, оно прошло успешно, каждый из символов $DATA наложился на АТД-терм. Возьмём, к примеру, первое вхождение символа $DATA — зная тип АТД, мы можем узнать модуль, в котором он переопределён, и попытаться в этом модуле найти расширение виртуальной функции. Расширение нашли — можем попробовать сопоставить аргумент с каждой из левых частей предложений расширения. Если одно из предложений расширения выполнилось, то виртуальная функция на этом завершается, иначе происходит переход на предложения, следующие за виртуальным. Заметим, что другие расширения виртуальной функции проверять не нужно — они не могут содержать на месте $DATA чужой АТД.

Очевидно, что путь поиска расширения избыточен: из цепочки тег АТД → модуль → расширение можно выкинуть модуль, храня в теге непосредственно расширения для разных АТД, причём только те предложения из расширений, которые относятся к данному типу.

На данный момент тег типа для АТД представляет собой пустую функцию, поскольку его роль — только отличать друг от друга разные АТД. Поэтому его можно пополнить новой информацией, новым поведением без конфликтов с имеющимся кодом.

Виртуальная функция при компиляции представляется в виде пары: префикс — функция, состоящая из предложений по виртуальное включительно и суффикс — функция, состоящая из предложений, которые следуют за виртуальным. Суффикс может быть и пустой функцией. Для примера, пусть у нас компилируется функция Модуль.Гладить — имя префикса будет совпадать с именем функции, имя суффикса будет иметь вид Модуль.Гладить%суф. (Реализация может давать любое имя, как угодно декорированное, такое, что пользователь не сможет его перекрыть). Модуль:

$MODULE Модуль;

$VIRTUAL Гладить {
  Обычное = Предложение;
  Виртуальное $DATA;
  Предложение = Суффикса;
}

$END Модуль.

будет компилироваться во что-то вроде:

$MODULE Модуль;

$ENTRY Гладить {
  Обычное = Предложение;
  Виртуальное [s.Tag e.Info] = <s.Tag &Гладить%суф Виртуальное [s.Tag e.Info]>;
}

$ENTRY Гладить%суф {
  Предложение = Суффикса;
}

$END Модуль.

Здесь используется синтаксически невозможная [s.Tag e.Info], которая позволяет извлечь тег типа из АТД для осуществления косвенного вызова. Тег АТД является функцией, которая принимает указатель на суффикс и исходный аргумент, причём указатель на суффикс играет две роли: во-первых, служит тегом виртуальной функции, во-вторых, на него передаётся управление в случае невозможности сопоставления. На примере модуля-клиента всё будет понятно.

$MODULE Клиент;

$IMPORT Модуль;
$IMPORT Format;

$DATA Кот, Утюг, НеКласс;

$EXTENDS Модуль.Гладить {
  Виртуальное [Кот e.Кот] = Гладим кота;
  Виртуальное [Утюг e.Утюг] = Гладим бельё;
}

$EXTENDS Format.ToString {
  [Кот e.Кот] = 'Кота зовут ' e.Кот;
  [Утюг e.Утюг] = 'Утюг марки ' e.Утюг;
}

$END Клиент.

компилируется в

$MODULE Клиент;

$IMPORT Модуль;
$IMPORT Format;

Кот {
  & Модуль.Гладить%суф Виртуальное [Кот e.Кот] = Гладим кота;
  & Format.ToString%суф [Кот e.Кот] = 'Кота зовут ' e.Кот;
  s.Suf e.Arg = <s.Suf e.Arg>;
}

Утюг {
  & Модуль.Гладить%суф Виртуальное [Утюг e.Утюг] = Гладим бельё;
  & Format.ToString%суф [Утюг e.Утюг] = 'Утюг марки ' e.Утюг;
  s.Suf e.Arg = <s.Suf e.Arg>;
}

НеКласс {
  s.Suf e.Arg = <s.Suf e.Arg>;
}

$END Клиент.

Теги типов становятся уже таблицами виртуальных функций. Последнее предложение каждой виртуальной функции служит для передачи управления на суффикс, АТД, для которых расширения не определены, состоят только из одного такого предложения.

Back-end Простого Рефала

Реализуется аналогично back-end’у C++/SR с той лишь разницей, что конструкция [s.Tag e.Info] синтаксически недопустима, а значит, нужно идти в обход.

АТД, не участвующие в каком-либо расширении виртуальных функций, описываются как и раньше — пустая функция для тега и [Tag e.Info] для АТД-термов. Для типов, участвующих в расширении виртуальных функций тег описывается также, как и для предыдущего back-end’а, а терм — [Class Tag [Tag e.Info]] — таким образом, (а) по-прежнему обеспечивается инкапсуляция, но при этом есть доступ к тегу типа. Тег Class глобален на всю программу, объявляется в файле .main.sref как $EENUM.

Виртуальные предложения ожидают, что типы данных имеют вид [Class Tag [Tag e.Info]], поэтому при передаче в них АТД, не участвующих в расширениях виртуальных функций, выполнение сразу передаётся на суффикс. Пример выше может быть откомпилирован так:

$EXTERN Class;

$ENTRY МодульP_Гладить {
  #Обычное = #Предложение;
  #Виртуальное [Class s.Tag [s.Tag e.Info]] =
    <s.Tag МодульP_ГладитьX_суф #Виртуальное [Class s.Tag [s.Tag e.Info]]>;
  #Предложение = #Суффикса;
}

$ENTRY МодульP_ГладитьX_суф {
  #Предложение = #Суффикса;
}
$EXTERN Class;
$EXTERN МодульP_ГладитьX_суф;
$EXTERN FormatP_ToStringX_суф;

КлиентP_Кот {
  МодульP_ГладитьX_суф #Виртуальное [Class Кот [Кот e.Кот]] = Гладим кота;
  FormatP_ToStringX_суф [Class Кот [Кот e.Кот]] = 'Кота зовут ' e.Кот;
  s.Suf e.Arg = <s.Suf e.Arg>;
}

КлиентP_Утюг {
  МодульP_ГладитьX_суф #Виртуальное [Class Утюг [Утюг e.Утюг]] = Гладим бельё;
  FormatP_ToStringX_суф [Class Утюг [Утюг e.Утюг]] = 'Утюг марки ' e.Утюг;
  s.Suf e.Arg = <s.Suf e.Arg>;
}

$EMPTY КлиентP_НеКласс;

Back-end Рефала-5

Поскольку стадия компоновки является частью Модульного Рефала, здесь можно непосредственно реализовать семантику.

Заключение

Описанный механизм позволяет внедрить механизм полиморфизма в Модульный Рефал, причём минимальными синтаксическими расширениями. Механизм расширяет и дополняет имеющийся в языке механизм инкапсуляции. Если в дальнейшем в языке появится статическая типизация, то описанные синтаксис и семантика могут быть легко интегрированы в систему типов (в отличие от ООП на основе вложенных функций).

Как внедрить наследование реализации, а главное, стоит ли его внедрять — вопрос открытый.

Сырые строки для Модульного Рефала

Решение задачи #10 предполагает, что нативные вставки будут оформляться как в том числе и литеральные строки Модульного Рефала. Но часто такие вставки будут представлять собой длинные многострочные куски текста на C++. Очевидно, набирать их, учитывая escape-последовательности, завершая строки на \n (ведь хочется, чтобы в целевом коде они тоже были разбиты на строки, довольно утомительно и ненаглядно.

Поэтому предлагается внедрить в язык специальный синтаксис для сырых строк — строковых литералов, которые могут разбиваться на несколько строчек текста, не учитывают escape-последовательности и завершаются особым завершителем.

При этом завершитель должен быть таким, чтобы минимально ограничивать содержимое самой литеральной строки. Плохой пример — Python, где три кавычки запрещают наличие трёх кавычек внутри самой строки. Heredoc’и Perl’а позволяют пользователю самому указать вид завершителя, но при этом завершитель (ЕМНИП) должен начинаться с новой строки, да и сама строка начинается со следующей строки.

Очень неплохой подход демонстрирует Rust — в нём сырые строки имеют вид r"…", r#"…"#, r##"…"## и т.д. То есть, буква r, за которой следует ноль или более решёток, кавычка и сама строка. Завершается строка кавычкой и таким же числом решёток. При этом если мы хотим внутри сырой строки указать, скажем "##, мы можем для ограничителя указать три решётки и конфликта не будет.

Предлагается использовать подход Rust, при этом незначительно изменив его.

Сырые строки в Модульном Рефале будут выглядеть так: @'…'@, @@'…'@@ и т.д. — снаружи кавычек не менее одной собачки, число собачек в начале и конце должно быть одинаковым.

Front-end Рефала-5

У нас есть препроцессор, который может конвертировать подмножество Простого Рефала в исходный код на Модульном Рефале. При этом он написан на Модульном Рефале. Его можно аккуратно встроить в компилятор, получив при этом второй front-end. Следует заметить, что поддержка множественных front-end’ов в компиляторе уже имеется.

Частности:

  1. Самый простой вариант встраивания: для каждого входного файла на Простом Рефале вызывать препроцессор, который будет порождать временный файл. Последний скармливать парсеру Модульного Рефала. Препроцессор гарантирует, что номера строк сохранятся и будут правильно указывать на синтаксические ошибки. Разумеется, есть много более сложных, но более качественных вариантов.
  2. Текущая версия препроцессора сильно отстала, она не поддерживает идентификаторы и вложенные функции. Версию нужно актуализировать.
  3. Front-end будет принимать только подмножество Простого Рефала, возможно, расширенное подмножество.
  4. Уже есть back-end Простого Рефала. Поскольку front-end принимает подмножество, он не обязан обрабатывать выхлоп back-end’а.

Актуализировать профили C++/SR и SimRef в соответствии с актуальной версией Рефала-5λ

Эта задача никогда не закроется. Вернее, будет существовать, пока существуют Простой Рефал (Рефал-5λ) и указанные профили в компиляторе Модульного Рефала. Либо может закрываться-открываться-закрываться-открываться…

Задача создана для возможности ссылок на неё из других подзадач и в комментариях коммитов.

На момент создания задачи актуальной является интеграция с версией 1.λ.7.

Не работает самоприменение с back-end’ом РЕФАЛа-5 и в режиме +make

Вылетает со следующей ошибкой:

REFAL ERROR:  RECOGNITION IMPOSSIBLE
Refal system Error: Recognition impossible.
*** Active function: LSV
*** Active expression:

<LSV (T9 (T 'MRefal' ))
 (TL (TK (T )(T )(T ))
  (TJ 
   (TB 
    (T (T (T 'mrefal' ))(T W1Q W2S )(T W21 W1U )(T W1Z W48 )(T W9P W2Q )
     (T W9Q W9R 
     )(T W9N (T WK )(T '..' )(T 'Bin' )(T 'MRefal.r5' ))(T W9J W1R )
     (T W9H W1C 
     )(T W9O W1U )
     (T WF (T WM '~ROut\\MRefal.rout' )(T WL '~Defs\\MRefal.rsym' )
     )(T W77 W7A 'MRefal.mref' )
    )
   )
  )(TI )
 )W4Q W4Q W4Q 
>
Elapsed system time: 0.399 seconds

После декодирования:

REFA*MRefal.Profiling-Go ERROR:  RECOGNITION IMPOSSIB*Config.MLoad.HelpHandler
Refal system Error: Recognition impossible.
*** Active function: *Driver.MClusters.GetLastTime-OutTime
*** Active expression:

<Driver.MClusters.GetLastTime-OutTime [MQualifiedName.QName ('MRefal' ))
 [Driver.MTables.Tables [Driver.Tables.MLookup.LookupTable ()()())
  [Driver.Tables.MModules.Context 
   [MSymTable.SymTable 
    ((('mrefal' ))(Kind Module )(RealName Unknown )(ModuleType Main )(CachedSym None )
     (CachedModuleIF NotCached 
     )(Target (Relative )('..' )('Bin' )('MRefal.r5' ))(Status Found )
     (Type Updated 
     )(MinMaxTime Unknown )
     (BackEnds (BE-Refal5 '~ROut\\MRefal.rout' )(BE-ModuleIF '~Defs\\MRefal.rsym' )
     )(FrontEnd MRefal_FrontEnd 'MRefal.mref' )
    )
   )
  )[Driver.Tables.MModuleTree.CaseTable )
 )FileNotFound FileNotFound FileNotFound 
>
Elapsed system time: 0.399 seconds

Тестировалось на текущем master’е: 2e4043b.

При этом режимы +build и +linkonly работают нормально. По всей видимости, проблема в определении атрибутов файла в библиотеке.

Обнаружилось в ходе тестирования Рефала-5λ (bmstu-iu9/refal-5-lambda#145).

Срастить профилировщик с рантаймом

Предлагается следующее. Рантайм (скомпилированный в особом режиме) при запуске открывает файл с заданным именем, содержащий имена функций, подлежащие профилировке. Затем, на каждом шаге рефал-машины сравнивает имя выполняемой функции с известными именами и, если имя находится, осуществляет профилировку заданной функции.

Детали. Дьявол кроется в деталях.

  • На каждом шаге сравнивать имя накладно. Но если в узлах поля зрения для функций хранить не указатель на функцию + указатель на имя, а указатель на дескриптор (который, в свою очередь, хранит указатель на функцию и имя), то выполнение можно организовать более эффективно. Дескриптор дополняется полем отладочных флагов, изначально равным нулю. Бит 0 означает, что функция уже известна, бит 1 — подлежит профилировке. На каждом шаге проверяется поле, если оно равно нулю, то (1) устанавливается бит 0, проверяется имя и, при необходимости, устанавливается бит 1. Затем профилировка осуществляется для функций с установленным битом 1.
  • Взаимодействие с Простым Рефалом — тут одни вопросы. При компиляции в него все имена декорируются, поэтому вопрос, как выписывать профилируемые функции в файл. Рантайм привязывается к профилировщику — надо ли переносить профилировщик в Простой Рефал.
  • Имя файла со списком функций можно указывать в виде макроса препроцессора (как имя файла дампа сейчас). Тогда, если этот макрос не определён, код, ответственный за профилирование даже не включается.

Отладочное поле флагов можно использовать и для других целей: трассировка, break-point’ы и т. д.

Рантайм, написанный на Модульном Рефале

Мотивация

Некоторые возможности рантайма проще выражать на Рефале, нежели на C++ или в сгенерированном коде. На данный момент — это вызов инициализаторов и финализаторов. Во всех трёх back-end’ах он реализован по-разному, с разной степенью кривизны. Наиболее красивая реализация инициализаторов и финализаторов — в Простом Рефале, наиболее кривая — в C++/SR. Если бы был глобальный модуль рантайма, написанный на Модульном Рефале, можно было бы определить в нём статический ящик, куда будут складываться финализаторы + функции регистрации и раскрутки финализаторов.

В дальнейшем (см. TODOs.txt) планируется добавить вложенные функции как подкласс некоторого встроенного класса. Как написано по предыдущей ссылке, такой класс можно описать и на Рефале:

  Первое, что мне пришло в голову — создать библиотечный модуль с определением виртуальной
функции вроде такого:

  $MODULE RUNTIME;

  $VIRTUAL CALL {
    s.Func e.Arg = <s.Func e.Arg>;
    $DATA e.Arg;
  }
  $END RUNTIME.

  Тогда, если модуль содержит вызовы <t.Func ...>, то он неявно импортирует модуль RUNTIME,
а сами вызовы заменяются на вызов <RUNTIME::CALL ...>. При этом пользователь может явно
импортировать этот модуль и расширить RUNTIME.CALL для своих АТД.

https://github.com/Mazdaywik/mrefal/blob/e6c206edc096506f3dd29e36b0463876da543d97/Documentation/Журнал/TODOs.txt#L640-L654

Возможно, в дальнейшем отыщутся и другие причины иметь рантайм, написанный на Модульном Рефале.

Реализация

Компилятор Рефала знает два модуля с предопределёнными именами: RUNTIME и RUNTIME-CORE. Оба модуля запрещено импортировать явно во всех остальных модулях, за исключением RUNTIME-CORE, который можно импортировать из RUNTIME. А может, и не запрещено. А просто не рекомендуется.

Первый модуль RUNTIME неявно импортируется из каждого модуля, где используется соответствующая функциональность рантайма (финализаторы, вложенные функции и <t.Func …>), либо импортируется неявно любыми модулями. Второй RUNTIME-CORE явно импортируется из RUNTIME.

Модуль RUNTIME содержит универсальные средства, не зависит от выбранного back-end’а. Второй содержит платформенно-зависимый код, поэтому для каждого back-end’а свой.

Комментарии

В TODOs.txt есть такое замечание:

  (-) Решение противоречит идеологии Модульного Рефала: встроенные средства языка не должны быть
привязаны к пользовательским определениям. Например, пользователь может переопределить модуль
RUNTIME в котором функция CALL может остутствовать, и вся механика разрушится.

https://github.com/Mazdaywik/mrefal/blob/e6c206edc096506f3dd29e36b0463876da543d97/Documentation/Журнал/TODOs.txt#L662-L664

Дескать, оно противоречит идеологии. Но это только на первый взгляд. Точно также, как пользователь может подменить RUNTIME.mref, он может подменить и refalrts.h/refalrts.cpp с тем же эффектом.

Удалить макрос MODULE_REFAL из профиля C++/SR

Профиль C++/SR изначально задумывался как профиль, генерирующий код на C++, совместимый с рантаймом Простого Рефала.

Однако, начиная с версии (вывод git describe --first-parent) 0.1.959-49-g9a96701 (фиксация 9a96701) пути этих профилей разошлись — имя функции внутри узла-функции стало задаваться не const char *, а как указатель на идентификатор. Замена предполагалась с целью оптимизации объёма сгенерированного файла: дублирование строк-имён избегаем при помощи идентификаторов. Эффект оказался незначительным.

В версии 0.1.959-115-g2e6be07 (фиксация 2e6be07) независимые рантаймы компилятора Простого Рефала и профиля C++/SR были объединены — выбор варианта для поля имени функции определялся макросом MODULE_REFAL. Макрос определялся при компиляции сгенерированных файлов на C++ + файлов рантайма и реализации библиотеки только при использовании профиля C++/SR.

Недавно, при реализации в Простом Рефале функций как дескрипторов (bmstu-iu9/refal-5-lambda#46) макрос MODULE_REFAL оказался очень кстати. Кодогенерация существенно изменилась, но при помощи макроса удалось сохранить старые фрагменты рантайма, необходимые для работы Модульного Рефала.

Таким образом, макрос на сегодняшний день имеет единственное преимущество:

  • возможность относительно независимо развивать Модульный и Простой Рефал.

Недостатков же несколько:

  • Усложняется понимание Простого Рефала (что противоречит его целям проекта: bmstu-iu9/refal-5-lambda#1).
  • Необходимость сопровождать оба варианта рантайма, проверять корректность как с включённым макросом, так и без.
  • Недостаток, вытекающий из преимущества — при возможном развитии Модульного Рефала не в ногу с Простым придётся править чужой файл в чужом репозитории.
  • Существование C++/SR противоречит исходным целям: генерировать код, совместимый с рантаймом Простого Рефала.

Цель

  • Генерировать код профилем C++/SR, полностью совместимый с рантаймом Простого Рефала без каких-либо костылей.
  • После чего удалить из рантайма Простого Рефала все #ifdef’ы с макросом MODULE_REFAL.
  • С этих пор профиль C++/SR оказывается в догоняющем положении по сравнению с Простым Рефалом — использует только представленные в нём возможности.

Эта задача блокирует задачу bmstu-iu9/refal-5-lambda#61

Относительные пути в конфигах считать относительно папки с конфигом

Файлы конфигурации допускают относительные пути, однако, они отсчитываются относительно текущего каталога. Следует сделать так, чтобы они отсчитывались от каталога с конфигурационным файлом. Это упростит написание скриптов, библиотек, обслуживание проектов и т. д.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.