Open Mazdaywik opened 5 years ago
Согласно Mazdaywik/refal-5-framework#5, библиотечные файлы фреймворка должны находиться в переменной среды REF5RSL
. Соответственно, Рефал-05 должен поддерживать поиск в этой переменной
Помимо неявно декларируемой цели
Сделать Простой Рефал совместимым с Рефалом-5, эффективным и компилирующимся в Си.
была и другая неявная цель:
Написать прозрачный задокументированный компилятор Рефала, который мог бы служить учебным пособием по написанию компиляторов Рефала.
Отсюда, кстати, и минимализм — чем меньше объём, тем проще изучить и понять, и хорошая подробная документация.
Т.е. это должен быть как Minix, но компилятор Рефала.
Среди целей, описанных в документации, понятность и прозрачность исходников (следствие «учебного пособия») обозначены как второстепенная цель и граничное условие:
Разумно эту цель проявить и внести в документацию.
Старая концепция раскритикована, поставлены новые задачи, они, по большей части, выполнены, но новая цель (на которую направлены эти задачи) явно не артикулирована.
Попробуем её выявить, рассмотрев особенности актуального и предполагаемого (#35, #38) Рефала-05.
$ENUM
’ы вне псевдокомментариев), но теперь это так из-за использования refal-5-framework.$EXTERN
’ы являются синтаксической ошибкой,$EXTERN
’ы являются синтаксической ошибкой,/* … /* … */
тоже были синтаксической ошибкой.$ENTRY Go { = ; }
, т.к. библиотечные/встроенные функции ввода-вывода в обеих реализациях не пересекались.Mu
).Рефал-05 — эффективный и при этом минималистичный, совместимый с Рефалом-5 язык, компилирующийся в Си с раздельной трансляцией.
goto
.При решении задачи #38 уже почти любая программа на Рефале-5 будет компилироваться Рефалом-05 и, возможно, работать точно также. Компилироваться не будут те программы, в которых есть дублирующиеся внешние ссылки, неиспользуемые функции и внешние ссылки. Работать будут иначе программы, которые ссылаются на отсутствующие возможности встроенных функций (например, длинную арифметику) или используют Mu
несовместимым способом.
Таким образом, высока вероятность (после #38), что программа, написанная для Рефала-5, будет компилироваться Рефалом-05 и выполняться с тем же поведением без дополнительных правок, вроде добавления всяких '*$EENUM`’ов. Так что, можно говорить, что Рефал-05 — не только «совместим с Рефалом-5», но и является его альтернативной реализацией!
Неполностью совместимой альтернативной реализацией. А почему не полностью совместимой?
$EXTERN
’ы. Исторически нужна была проверка на избыточные *$E(E)NUM
’ы, а $EXTERN
’ы добавились заодно. Однако, crefal
также ругается на избыточные $EXTERN
’ы. refc
ругается на избыточные $EXTERN
’ы только в объявлениях вида $EXTERN F, F;
. Так что это, скорее, не прихоть, а, наоборот, недоработка refc
.<Add (e.X) e.Y>
) не реализованы из соображений минимализма.Mu
(#38) объяснимо целевым языком Си и раздельной компиляцией. Раздельная компиляция реализована из соображений минимализма.Отдельно остановлюсь на ошибках о неиспользуемых объявлениях и определениях. Конечно, можно их сделать и предупреждениями, и вообще убрать (заменив на молчаливое удаление таких узлов из синтаксического дерева). Но они полезны — и как воспитательная мера, и для упрощения рефакторинга (переписал код, компилятор напоминает удалить неиспользуемые). Кроме того, имена считаются используемыми (и будут считаться после #38!), если в тексте программы есть идентификатор с данным именем:
$ENTRY Go { = A С }
A { = } /* ✔ нет ошибки, используется в Go */
B { = } /* ❌ ошибка «Unused local function B» */
$EXTERN С; /* ✔ нет ошибки, используется в Go */
$EXTERD D; /* ❌ ошибка «Unused external declaration D» */
Можно возразить, что существуют корректные программы на Рефале-5, такие что (а) на них выдаёт ошибку Рефал-05, (б) после удаления «неиспользуемой» локальной функции программа перестанет работать или станет работать неправильно.
Например:
* файл main.ref
$EXTERN CallMu;
$ENTRY Go {
= <CallMu F> <CallMu G>
}
$ENTRY F { = <Prout 'F in main.ref'> }
* файл callmu.ref
$ENTRY CallMu {
s.F = <Mu s.F>
}
F { = <Prout 'F in callmu.ref'> }
G { = <Prout 'G in callmu.ref'> }
Эта программа, будучи откомпилированной и запущенной Рефалом-5, выдаст
F in callmu.ref
G in callmu.ref
Компилятор Рефала-05 выдаст ошибки на функции F
и G
в файле callmu.ref
. Удалим их и снова откомпилируем и запустим Рефалом-5 (да, опять им):
F in main.ref
‹ошибка RECOGNITION IMPOSSIBLE›
Таким образом, в одном месте поведение изменилось (программа стала работать иначе), в другом и вовсе упала.
Но ведь, даже если бы эта программа компилировалась Рефалом-05 молча или с выдачей предупреждения (вместо ошибки), то она и так работала бы иначе. В данном случае она тоже вывела бы
F in main.ref
‹ошибка RECOGNITION IMPOSSIBLE›
но это совпадение. Данная программа опирается на поведение функции Mu
, разное в обоих реализациях.
Вообще, можно показать, что если
то эта программа не работала бы правильно на Рефале-05, если бы он такую программу компилировал бы молча.
Так что данное предупреждение я оставлю.
Предлагаю определить Рефал-05 так:
Рефал-05 — альтернативная реализация Рефала-5, минималистичная, эффективная, прозрачная и компилируемая в Си. Реализация имеет отличия в семантике некоторых встроенных функций, сделанные ради минимализма и компиляции в Си.
Актуальная реализация сопоставления с образцом сложная, костыльная и неэффективная. Она унаследована от Простого Рефала, а как правильно компилировать сопоставления с образцом, я тогда не знал.
Костыли в ней, например, в необходимости запоминать диапазоны при сопоставлениях с открытыми переменными.
Предлагается сделать сопоставления с образцом в духе диссертации Романенко — каждый распознанный элемент кладётся на стек, диапазоны открытые — это просто края уже распознанных элементов. Система команд Романенко предлагает стек времени выполнения и два «регистра» границ — следствие того, что команды интерпретируются и ради уменьшения объёма байткода (у команд меньше аргументов). Очевидно, что положение сопоставленных элементов на стеке известно во время компиляции, так что вместо стека будет простой массив ячеек и команды будут ссылаться на ячейки явным образом.
Такое представление системы команд очевидным образом допускает выделение общих префиксов — оптимизацию совместного сопоставления с образцом в её простейшем варианте. Возможно, её реализую, если это не займёт много кода.
Сопоставление с образцом в духе диссертации Романенко будет гораздо проще задокументировать — фактически нужно пересказать своими словами соответствующий раздел учебника Турчина.
Тоже самое предполагается в дальнейшем сделать и в Рефале-5λ — https://github.com/bmstu-iu9/refal-5-lambda/issues/204.
P.S. Перенесено в отдельную заявку: #40.
$EXTERN
’ыВ рамках задачи Mazdaywik/refal-5-framework#9 предлагается реализовать проверку на избыточные $EXTERN
’ы в самом фреймворке.
Предлагается сообщать об ошибке в случаях вида
$EXTERN F;
F { … } /* может быть и $ENTRY, не важно */
и выдавать предупреждения в случаях вида
$EXTERN F;
$EXTERN F;
$EXTERN Prout;
Однако, поддержка предупреждений пока не реализована (Mazdaywik/refal-5-framework#11), поэтому вместо предупреждений фреймворк будет выдавать ошибки (но позже — предупреждения).
Оставить как есть — избыточные $EXTERN
’ы все ошибочны. Однако, в дальнейшем можно будет ожидать, что в корректном дереве от фреймворка не будут одновременно присутствовать и объявление, и определение некоторой функции, а значит, логику проверки соответствующей ошибки можно будет убрать. В случае же реализации предупреждений будет выдаваться и предупреждение на $EXTERN F, F;
от фреймворка, и ошибка от Рефала-05.
- Синтаксически Рефал-05 строже Рефала-5:
- неиспользуемые функции и
$EXTERN
’ы являются синтаксической ошибкой,- избыточные
$EXTERN
’ы являются синтаксической ошибкой,- когда у Рефала-05 был свой лексер, комментарии вида
/* … /* … */
тоже были синтаксической ошибкой.
Это не следует из других явно заявленных целей, но при этом соблюдается. Значит, это самостоятельная цель. Назовём её педантичность. Рефал-05 педантичный — в нём некоторые стилистические ошибки считаются ошибками синтаксиса. Имеет смысл это направление расширить и углубить.
была и другая неявная цель:
Написать прозрачный задокументированный компилятор Рефала, который мог бы служить учебным пособием по написанию компиляторов Рефала.
Спасибо. Именно благодаря Рефал-05 написал интерпретатор Рефала. При этом для меня, привыкшего к императивным языкам, наибольшую ценность представляли исходники на Си, как генерируемые, так и refal05rts.*.
Спасибо, @STrusov! Значит, Рефал-05 написан не зря!
(Дамп потока сознания.)
Декларированные цели
Недавно я задумался: в Рефал-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, 5dc0c8e65b79c476dcc9bd2fb5c29c94b6c8d0a2 — в коммите не менялись объявления функций, см. также и
Library.sref
в bmstu-iu9/refal-5-lambda@6f1a36670f903443577f37e9c27262e76cf1341b. Но того же эффекта можно было достичь и аккуратным использованием макросов.Нативные вставки позволяют смешивать в исходном файле код на Рефале и код на целевом языке, но это преимущество немного сомнительно. Оно было актуально раньше в Простом Рефале/Рефале-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 (перенесена: Mazdaywik/5-to-basis#4).В комментарий этот список выносить нельзя, поскольку тогда интерфейс GitHub его не увидит.