«При этом фрагменты образцов в результатных выражениях линзы не могут
повторяться и должны сохранять своё взаимное расположение. Другими словами:
линза всегда возвращает поддерево того упорядоченного дерева, которое
передаётся ей в качестве аргумента. Это приводит к тому, что все термы на
выходе линзы имеют ссылки на оригинальные термы, и эти ссылки назначаются
тривиальным образом.»
С этого и надо было начинать, это было не очевидно. И, согласен, это жёсткое
ограничение.
Я правильно понимаю, что относительный порядок сохраняться должен и в Purify, и
в Parse?
/* Линза преобразует список термов t.Token к списку t.PureToken */
Purify {
(t.Pos Let) e.Rest = Let <Purify e.Rest>;
(t.Pos Assign) e.Rest = Assign <Purify e.Rest>;
...
(t.Pos e.Data) e.Rest = (e.Data) <Purify e.Rest>;
}
/* Демонстрация вызова парсера с применением линзы */
CallParser {
e.Tokens = <Purify | Parse e.Tokens>;
/* Вызов функции с линзой -- единственное расширение синтаксиса */
}
/* Парсер принимает список термов t.PureToken */
Parse {
Let (Ident e.Name) Assign e.Expr
, <IsDeclared e.Name>: T
= ERROR (Ident e.Name) ': redefinition of variable';
/* Если парсер вызвали с линзой Purify, то на выходе
из функции Parse терм (Ident e.Name) преобразуется
обратно в (t.Pos Ident e.Name) */
...
}
«4.2. Хотелось бы передавать линзам гиперпараметры, управляющие их работой. Как
их передавать, в принципе, понятно. Вот так, например: <Purify SkipComments |
Parse e.tokens>. Но не совсем понятно, как отделить гиперпараметры от
обрабатываемого дерева в реализации линзы — мешает накладываемое ограничение.»
Не понял: SkipComments передаётся в Purify или в Parse?
From: Sergei Skorobogatov s.yu.skorobogatov_AT_gmail.com <[email protected]>
Sent: Thursday, November 14, 2024 7:50 PM
To: [email protected]
Subject: Re: Синтаксис словарей в Рефале!
1. Выбор термов, подвергающихся преобразованию на выходе из "линзированной"
функции
On Thu, Nov 14, 2024 at 5:59 PM Александр Коновалов a.v.konovalov87_AT_mail.ru
<http://a.v.konovalov87_AT_mail.ru> <[email protected] <mailto:[email protected]> >
wrote:
Вопрос по семантике примерно такой: если мы в результатной части линзированной
функции написали (Ident e.Name), то это имеется ввиду залинзированный терм из
аргумента или совершенно новый независимый терм (который разлинзировать не
нужно)? Ведь та же функция Parse может из цепочки термов порождать
синтаксическое дерево, в котором буквально присутствует узел (Ident e.Name) без
координат, и приписывание координат к нему будет ошибкой.
Если функция Parse добавит в дерево новый узел (Ident e.Name), то он при выходе
из функции Parse не будет преобразован. Это, в принципе, логично: функция Parse
взяла его с потолка, а не из входного потока токенов, и координат он не имеет.
Чтобы с такими деревьями, у которых часть узлов имеет координаты, а часть -- не
имеет, было удобнее работать, надо, наверное, переместить координаты в конец
выражения, описывающего токен, а имя взять в скобки: (Ident t.Name t.Pos).
2. Соответствие термов в образцах и результатных выражениях "линзированной"
функции
On Thu, Nov 14, 2024 at 5:59 PM Александр Коновалов a.v.konovalov87_AT_mail.ru
<http://a.v.konovalov87_AT_mail.ru> <[email protected] <mailto:[email protected]> >
wrote:
И второй вопрос, который лучше проиллюстрировать примером:
Parse {
…
Let (Ident e.Name) Assign e.Expr1 Let (Ident e.Name) Assign e.Expr2
= ERROR (Ident e.Name) ': double definition';
…
}
Вхождение (Ident e.Name) соответствует первому вхождению в образец или второму?
Для сопоставления слева направо логично будет взять первое вхождение, т.е. ваш
пример будет работать не так, как хочется. Чтобы он стал работать правильно,
его придётся переписать, заменив вхождения e.Name в образец на e.Name1 и
e.Name2, и добавив условие, сравнивающее e.Name1 с e.Name2.
3. Ограничения, накладываемые на функцию-линзу
Должен быть ещё и третий вопрос: какие термы на выходе линзы будут иметь ссылки
на оригинальные термы? А четвёртый вопрос должен уточнять, как именно термы на
выходе линзы ставятся в соответствие с оригинальными термами?
Ответ на первый вопрос -- все термы. Ответ на второй вопрос -- тривиально.
Дело в том, что на функцию-линзу должно быть наложено следующее ограничение:
результатные выражения в ней и во всех функциях, которые из неё вызываются,
должны быть составлены только из фрагментов соответствующих этим результатным
выражениям образцов. При этом фрагменты образцов в результатных выражениях
линзы не могут повторяться и должны сохранять своё взаимное расположение.
Другими словами: линза всегда возвращает поддерево того упорядоченного дерева,
которое передаётся ей в качестве аргумента. Это приводит к тому, что все термы
на выходе линзы имеют ссылки на оригинальные термы, и эти ссылки назначаются
тривиальным образом.
4. Направления развития линз
On Thu, Nov 14, 2024 at 5:59 PM Александр Коновалов a.v.konovalov87_AT_mail.ru
<http://a.v.konovalov87_AT_mail.ru> <[email protected] <mailto:[email protected]> >
wrote:
А как эту фичу предполагалось развивать?
4.1. Основное, что мне хотелось бы от линз, -- это ослабление указанного в
предыдущем пункте ограничения. Например, я хочу линзу, которое преобразует
арифметическое выражение из инфиксной записи в префиксную.
4.2. Хотелось бы передавать линзам гиперпараметры, управляющие их работой. Как
их передавать, в принципе, понятно. Вот так, например: <Purify SkipComments |
Parse e.tokens>. Но не совсем понятно, как отделить гиперпараметры от
обрабатываемого дерева в реализации линзы -- мешает накладываемое ограничение.
4.3. Хотелось бы уметь переходить к оригинальным версиям термов в функциях,
вызываемых из "линзированной" функции. Как это сделать красиво, я не придумал.
При этом всё, конечно, можно реализовать, если наплодить кучу нового
синтаксиса, но расширять синтаксис совершенно не хочется, потому что это
уменьшит соотношение "выразительность/сложность". Желательно ограничиться
расширением синтаксиса вызова функции: <линза | функция аргументы>.