(Дамп потока сознания.)
Декларированные цели
Недавно я задумался: в Рефал-05 сравнительно несложно внедрить условия и блоки (upd: #35). Для этого потребуются совсем минимальные изменения рантайма (рекурсивный вызов рефал-машины и псевдофункции FuncName$n) плюс некоторое количество кода на Рефале (парсер, дерево, генератор). Т.е. технически проблем никаких нет. Но есть идеологические проблемы — противоречие целям, заявленным в README и документации.
Я затрудняюсь сказать, останется ли в этом случае Рефал-05 минималистичным. Возможно, останется, возможно, нет, нужно смотреть, на сколько объём кода увеличится.
Но в любом случае пришлось задуматься над целями и пересмотреть их. Исходно цели декларировались так:
Рефал-05 — минималистичный самоприменимый компилятор минималистичного
диалекта Рефала, имеющий общее подмножество с классическим Рефалом-5.
На этом подмножестве он и написан.
Но получился язык, в котором в отличие от Рефала-5, идентификаторы являются именами функций (отсюда костыль с псевдокомментариями и $ENUM) и есть синтаксис для нативных вставок. Обе вещи несколько противоречат минималистичности…
В процессе разработки спонтанно родилась новая цель: компилятор как фреймворк для разработки инструментальных средств, и частично она достигнута (однако, не доведена до ума — см. #27).
Критика концепции
Получился язык, не полностью минималистичный, но частично совместимый с Рефалом-5. Важнейшие отличия от Рефала-5
- Вместо символов-слов символы-функции (что лишает пользователя составных символов в двойных кавычках).
- Объявления пустых функций в псевдокомментариях
*$ENUM и *$EENUM. Без псевдокомментариев исходники невозможно было бы собрать Рефалом-5.
- Нативные вставки.
Можно ли было бы, сохранив стремление к минимализму, получить другой результат?
Можно было бы генерировать интерпретируемый код по диссертации Романенко, код прозрачен, компактен и эффективен. Тогда бы «встроенные функции» пришлось бы делать встроенными. Расширяемости не было. Но при этом получилось бы подмножество Рефала-5. Точное подмножество без геморроя с объявлением пустых функций.
Можно было бы пожертвовать раздельной трансляцией — всю программу транслировать в один исходник на Си. В этом случае тоже символы-слова были бы символами-словами.
Можно было бы оставить C++ и трюк с генерацией идентификаторов.
Можно было бы добавить отдельную фазу линковки, которая строит глобальную таблицу идентификаторов. Это, конечно, несколько усложнило бы компилятор.
Можно было бы пожертвовать эффективностью, генерируя таблицу идентификаторов во время выполнения. Например, в начало каждой функции добавлять вызов функции отложенной инициализации:
...
struct Ident **idents_table = NULL;
const char *ident_names = "True\0False\0Success\0Fails\0\0";
...
void r05f_funcname(…) {
if (idents_table == 0) {
idents_table = r05_init_idents_table(ident_names);
}
...
/* ссылка на Success */
... idents_table[2] ...
...
}
...
В этом случае тоже получили бы классический Рефал-5 или хотя бы его точное подмножество. Но это тоже несколько усложнило бы компилятор.
Почему так вышло? Потому что неявной подразумеваемой целью была другая:
Сделать Простой Рефал совместимым с Рефалом-5, эффективным и компилирующимся в Си.
Отсюда раздельная компиляция, как в Простом Рефале. Отсюда странная реализация символов-слов, поскольку в чистом Си их по-другому не сделаешь, а проверки времени выполнения снижают эффективность. Отсюда желание сохранить расширяемость языка — каждая функция компилируется в одноимённую функцию на Си.
В любом случае, компиляция в Си не вытекала из цели минимализма, а сама была неявно декларируемой целью. И было не декларируемое явно стремление сохранить свойства Простого Рефала — эффективность, раздельную компиляцию и удобный интерфейс с Си. Поэтому получилось то, что получилось.
Но Рефал-05 как язык обладает тем же недостатком, что и Простой Рефал: он не нужен. Не нужен новый диалект Рефала, который не совместим с кодовой базой и не привносит ничего принципиально нового.
Чтобы программу на Рефале-5 можно было откомпилировать Рефалом-05, нужно, чтобы в ней не было копилки (она выкинута), не было символов-слов в двойных кавычках, остальные символы-слова были именами функций в текущей области видимости, в ней не использовались условия и блоки (для этого её можно пропустить через 5-to-basis). Слишком много возни.
Отсюда следует и ненужность фреймворка-компилятора. Инструментальные средства, разрабатываемые на нём, будут столь же бесполезны.
Кроме того, наличие нативных вставок затрудняет реализацию инструментальных средств. Один из подпунктов в #27 гласит:
- Альтернативный компилятор — объединяет все файлы без нативных вставок в один файл с необходимым переименованием локальных функций.
Я выделил ключевую в этом контексте фразу: без нативных вставок. Потому что если в файле есть хотя бы одна нативная вставка, то никакие преобразования кода мы сделать не можем. Мы не можем, например, переименовать или удалить локальную функцию — мы не знаем, использует ли её нативная вставка. Мы не можем переставлять нативные вставки и функции с ними местами. Кроме того, нативные вставки гвоздями прибивают нас к языку реализации.
Нативные вставки безусловно удобны при изменениях представления данных, например, при описаниях функций через дескрипторы. Пример такого изменения — #14, 5dc0c8e — в коммите не менялись объявления функций, см. также и Library.sref в bmstu-iu9/refal-5-lambda@6f1a366. Но того же эффекта можно было достичь и аккуратным использованием макросов.
Нативные вставки позволяют смешивать в исходном файле код на Рефале и код на целевом языке, но это преимущество немного сомнительно. Оно было актуально раньше в Простом Рефале/Рефале-5λ, поскольку позволяло смешивать оба языка в Library.sref, сохраняя при этом единый файл. Сейчас это не так актуально и там (поскольку если уж рантайм рассыпался на кучу файлов, можно рассыпать и Library), и здесь (встроенные функции Рефала-05 несложно описать и на Си).
Что же делать?
Менять концепцию.
Хороший фреймворк для Рефала-5, безусловно, нужен (по моему мнению, prefal — плохой фреймворк). Фреймворк для странного языка Рефала-05 не нужен. Компилятор странного Рефала в Си пускай будет.
Поэтому есть предложение отказаться от идеи делать фреймворк на базе Рефала-05, вместо этого развивать 5-to-basis как подобный фреймворк. Парсер в том проекте полностью поддерживает классический Рефал-5.
(Либо, вообще можно подумать о фреймворке на базе Рефала-5λ.)
Задачу #27 перенести в 5-to-basis и решить её там.
В Рефал-05 можно добавить условия и блоки, фронт-энд сделать общим с 5-to-basis. Соответственно, здесь останется только рантайм, кодогенератор и расширения парсера (разбор псевдокомментариев *$ENUM, проверка имён-идентификаторов).
Как унифицировать фронт-энд между обоими проектами — отдельный вопрос.
Из Рефала-05 выкинуть нативные вставки, Library.ref переписать на Си. В ней на Рефале написаны только Mu и ListOfBuiltin, их переписать на Си несложно. Можно добавить дополнительные встроенные функции для большей совместимости с классическим Рефалом-5.
5-to-basis видоизменить так, чтобы он мог компилироваться Рефалом-05.
Ревизия 2019-03-31 — подзадачи
В комментарий этот список выносить нельзя, поскольку тогда интерфейс GitHub его не увидит.
(Дамп потока сознания.)
Декларированные цели
Недавно я задумался: в Рефал-05 сравнительно несложно внедрить условия и блоки (upd: #35). Для этого потребуются совсем минимальные изменения рантайма (рекурсивный вызов рефал-машины и псевдофункции
FuncName$n) плюс некоторое количество кода на Рефале (парсер, дерево, генератор). Т.е. технически проблем никаких нет. Но есть идеологические проблемы — противоречие целям, заявленным в README и документации.Я затрудняюсь сказать, останется ли в этом случае Рефал-05 минималистичным. Возможно, останется, возможно, нет, нужно смотреть, на сколько объём кода увеличится.
Но в любом случае пришлось задуматься над целями и пересмотреть их. Исходно цели декларировались так:
Но получился язык, в котором в отличие от Рефала-5, идентификаторы являются именами функций (отсюда костыль с псевдокомментариями и
$ENUM) и есть синтаксис для нативных вставок. Обе вещи несколько противоречат минималистичности…В процессе разработки спонтанно родилась новая цель: компилятор как фреймворк для разработки инструментальных средств, и частично она достигнута (однако, не доведена до ума — см. #27).
Критика концепции
Получился язык, не полностью минималистичный, но частично совместимый с Рефалом-5. Важнейшие отличия от Рефала-5
*$ENUMи*$EENUM. Без псевдокомментариев исходники невозможно было бы собрать Рефалом-5.Можно ли было бы, сохранив стремление к минимализму, получить другой результат?
Можно было бы генерировать интерпретируемый код по диссертации Романенко, код прозрачен, компактен и эффективен. Тогда бы «встроенные функции» пришлось бы делать встроенными. Расширяемости не было. Но при этом получилось бы подмножество Рефала-5. Точное подмножество без геморроя с объявлением пустых функций.
Можно было бы пожертвовать раздельной трансляцией — всю программу транслировать в один исходник на Си. В этом случае тоже символы-слова были бы символами-словами.
Можно было бы оставить C++ и трюк с генерацией идентификаторов.
Можно было бы добавить отдельную фазу линковки, которая строит глобальную таблицу идентификаторов. Это, конечно, несколько усложнило бы компилятор.
Можно было бы пожертвовать эффективностью, генерируя таблицу идентификаторов во время выполнения. Например, в начало каждой функции добавлять вызов функции отложенной инициализации:
В этом случае тоже получили бы классический Рефал-5 или хотя бы его точное подмножество. Но это тоже несколько усложнило бы компилятор.
Почему так вышло? Потому что неявной подразумеваемой целью была другая:
Сделать Простой Рефал совместимым с Рефалом-5, эффективным и компилирующимся в Си.
Отсюда раздельная компиляция, как в Простом Рефале. Отсюда странная реализация символов-слов, поскольку в чистом Си их по-другому не сделаешь, а проверки времени выполнения снижают эффективность. Отсюда желание сохранить расширяемость языка — каждая функция компилируется в одноимённую функцию на Си.
В любом случае, компиляция в Си не вытекала из цели минимализма, а сама была неявно декларируемой целью. И было не декларируемое явно стремление сохранить свойства Простого Рефала — эффективность, раздельную компиляцию и удобный интерфейс с Си. Поэтому получилось то, что получилось.
Но Рефал-05 как язык обладает тем же недостатком, что и Простой Рефал: он не нужен. Не нужен новый диалект Рефала, который не совместим с кодовой базой и не привносит ничего принципиально нового.
Чтобы программу на Рефале-5 можно было откомпилировать Рефалом-05, нужно, чтобы в ней не было копилки (она выкинута), не было символов-слов в двойных кавычках, остальные символы-слова были именами функций в текущей области видимости, в ней не использовались условия и блоки (для этого её можно пропустить через 5-to-basis). Слишком много возни.
Отсюда следует и ненужность фреймворка-компилятора. Инструментальные средства, разрабатываемые на нём, будут столь же бесполезны.
Кроме того, наличие нативных вставок затрудняет реализацию инструментальных средств. Один из подпунктов в #27 гласит:
Я выделил ключевую в этом контексте фразу: без нативных вставок. Потому что если в файле есть хотя бы одна нативная вставка, то никакие преобразования кода мы сделать не можем. Мы не можем, например, переименовать или удалить локальную функцию — мы не знаем, использует ли её нативная вставка. Мы не можем переставлять нативные вставки и функции с ними местами. Кроме того, нативные вставки гвоздями прибивают нас к языку реализации.
Нативные вставки безусловно удобны при изменениях представления данных, например, при описаниях функций через дескрипторы. Пример такого изменения — #14, 5dc0c8e — в коммите не менялись объявления функций, см. также и
Library.srefв bmstu-iu9/refal-5-lambda@6f1a366. Но того же эффекта можно было достичь и аккуратным использованием макросов.Нативные вставки позволяют смешивать в исходном файле код на Рефале и код на целевом языке, но это преимущество немного сомнительно. Оно было актуально раньше в Простом Рефале/Рефале-5λ, поскольку позволяло смешивать оба языка в
Library.sref, сохраняя при этом единый файл. Сейчас это не так актуально и там (поскольку если уж рантайм рассыпался на кучу файлов, можно рассыпать иLibrary), и здесь (встроенные функции Рефала-05 несложно описать и на Си).Что же делать?
Менять концепцию.
Хороший фреймворк для Рефала-5, безусловно, нужен (по моему мнению,
prefal— плохой фреймворк). Фреймворк для странного языка Рефала-05 не нужен. Компилятор странного Рефала в Си пускай будет.Поэтому есть предложение отказаться от идеи делать фреймворк на базе Рефала-05, вместо этого развивать 5-to-basis как подобный фреймворк. Парсер в том проекте полностью поддерживает классический Рефал-5.
(Либо, вообще можно подумать о фреймворке на базе Рефала-5λ.)
Задачу #27 перенести в 5-to-basis и решить её там.
В Рефал-05 можно добавить условия и блоки, фронт-энд сделать общим с 5-to-basis. Соответственно, здесь останется только рантайм, кодогенератор и расширения парсера (разбор псевдокомментариев
*$ENUM, проверка имён-идентификаторов).Как унифицировать фронт-энд между обоими проектами — отдельный вопрос.
Из Рефала-05 выкинуть нативные вставки,
Library.refпереписать на Си. В ней на Рефале написаны толькоMuиListOfBuiltin, их переписать на Си несложно. Можно добавить дополнительные встроенные функции для большей совместимости с классическим Рефалом-5.5-to-basis видоизменить так, чтобы он мог компилироваться Рефалом-05.
Ревизия 2019-03-31 — подзадачи
#27в 5-to-basis (перенесена: Уточнение API и примеры использования фреймворка refal-5-framework#4).ArgСмена (уточнение) концепции #33 (comment).В комментарий этот список выносить нельзя, поскольку тогда интерфейс GitHub его не увидит.