Рефал-05

Язык Рефал-05, встроенные функции и его отличия от Рефала-5

Синтаксис Рефала-05

Синтаксис Рефала-05 является точным подмножеством синтаксиса Рефала-5 (версия PZ Oct 29 2004) — если программма компилируется без ошибок Рефалом-05, то она будет без ошибок скомпилирована и Рефалом-5 тоже.

Обратное неверно — Рефал-05 может выдавать синтаксические ошибки на некоторые корректные программы Рефала-5. Это следующие ошибки:

Так сделано намеренно. Рефал-05 — это не Рефал-5, это другой язык, у него могут быть другие правила.

Объявления и определения

Будем говорить, что функция объявлена, если имя этой функции присутствует в списке $EXTERN.

Будем говорить, что функция определена, если в файле присутствует тело этой функции.

Избыточные внешние объявления

С точки зрения Рефала-05 каждая функция может быть либо встроенной, либо объявленной, либо определённой. Причём, и объявлена, и определена она может только однократно.

Отсюда следует, что

Неиспользуемые функции

Рефал-05 считает синтаксической ошибкой объявления и определения, которые не используются в программе.

Рефал-05 использует раздельную компиляцию, он за раз анализирует только один файл исходного текста, поэтому он не может понять, что в конкретной программе какая-либо функция, помеченная $ENTRY не используется. Более того, наличие таких фактически неиспользуемых функций скорее правило, ведь пользователь может подключать к программе библиотеку (например, LibraryEx), из которой использовать далеко не все функции. Поэтому функции, помеченные $ENTRY, являются используемыми по определению.

Определим формально, что такое используемые функции и используемые определения.

Метафункция — встроенная функция Рефала-05, способная вызвать функцию по её имени (заданным составным символом и/или цепочкой литер). В Рефале-5 встроенными функциями являются функции Mu, Residue, Up и Ev-met, актуальная версия Рефала-05 поддерживает только Mu и Residue.

Множество используемых функций — это минимальное множество, определённое следующими правилами:

Множество используемых объявлений — это минимальное множество, определённое следующими правилами:

Соответственно, неиспользуемые функции и объявления — это функции и объявления, которые не входят во множества используемых. На них компилятор Рефала-05 выдаёт синтаксические ошибки.

Правила вида «если функция F используемая, её тело содержит составной символ G…» связаны с семантикой нагруженных идентификаторов — см. раздел про них и функцию Mu ниже.

Отличия в семантике Рефала-05 и Рефала-5

Стартовая функция только GO

В Рефале-5 стартовая функция может называться Go или GO, причём вторая имеет приоритет (при наличии в программе обеих функций будет вызываться вторая). В Рефале-05 стартовая функция всегда должна называться GO.

Для запуска программ Рефала-5, стартовая точка которых называется Go, можно воспользоваться библиотекой Go.c. Она содержит функцию $ENTRY GO, которая просто вызывает функцию Go.

Нагруженные идентификаторы и функция Mu

Комментарии ниже про функцию Mu актуальны и для других метафункций: функции Residue (которая в актуальной реализации эквивалентна Mu) и функций Up и Ev-met, если они впоследствии будут добавлены в Рефал-05.

Функция Mu в Рефале-5 и Рефале-05: общее

В языке Рефал-5 (и в Рефале-05 тоже) есть функция Mu, осуществляющая косвенный вызов — имея идентификатор, совпадающий с именем функции, вызвать её:

Hello {
  = <Prout 'Hello!'>
}

$ENTRY GO {
  /* пусто */
    = <Mu Hello>                           /* напечатает «Hello!» */
      <Prout <Mu Add 2 3>>                 /* напечатает «5» */
      <Prout <Mu Chr 72 101 108 108 111>>  /* напечатает «Hello» */
      <Mu Bye>                             /* напечатает «Bye» */
}

Bye {
  = <Prout 'Bye!'>
}

В первом и последнем вызовах функции GO вызываются функции, определённые в том же файле. Во втором и третьем — вызовы встроенных функций.

Её формат условно можно описать так (обозначения будут описаны в разделе про встроенные функции):

<Mu s.WORD e.Arg> ≈≈ <s.WORD e.Arg>

Обозначение условное, т.к. запись <s.WORD …> синтаксически некорректная в Рефале-5 и в Рефале-05.

С функцией Mu всё просто и понятно, пока программа состоит из одного файла: если идентификатор является именем функции, определённой пользователем или встроенной функции — вызывается соответствующая функция, иначе — ошибка.

В программах из нескольких файлов уже могут появиться одноимённые функции, т.к. в файле можно определить функцию без пометки $ENTRY с любым именем, не совпадающем с именем встроенной функции — конфликта имён не будет.

И для таких программ становится неочевидно, какую из одноимённых функций вызовет Mu, если мы укажем её имя. Рассмотрим пример:

Файл main.ref:

*$FROM lib
$EXTERN Greeting;

$ENTRY GO {
  = <Greeting Hello 'Mazdaywik'> <Greeting Bye 'Mazdaywik>
}

Hello {
  = 'Hello'
}

Bye {
  = 'Good bye'
}

Файл lib.ref:

$ENTRY Greeting {
  s.Greeting e.Name
    = <Prout <Mu s.Greeting> ', ' e.Name '!'>
}

Hello {
  = 'Hi'
}

Bye {
  = 'Bye'
}

В обоих исходных файлах есть и функция Hello, и функция Bye. Спрашивается, какую вызывать?

Особенности функции Mu в Рефале-5

Рассмотрим вызов

 <Mu s.Name e.Arg>

Функция с именем s.Name ищется в три этапа:

  1. Все функции того файла, где записан вызов функции Mu,
  2. все функции программы с пометками $ENTRY,
  3. все встроенные функции.

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

Второй этап — поиск среди функций с пометками $ENTRY — официально незадокументирован в учебнике Турчина, однако, описан в приложении к книге.

Поиск среди функций с пометками $ENTRY позволяет записывать библиотеки функций высшего порядка, такие как функции Map, Reduce и MapAccum библиотеки LibraryEx. Чтобы функция, например, Map, могла вызвать функцию пользователя, последняя должна быть написана с пометкой $ENTRY. Без поиска по $ENTRY-функциям библиотеки функций высшего порядка в Рефале-5 были бы невозможны.

В примере с Greeting программа на Рефале-5 напечатает

Hi, Mazdaywik!
Bye, Mazdaywik!

т.к. поиск имён Hi и Bye будет осуществляться среди функций файла lib.ref, в котором записан вызов <Mu s.Greeting>.

Можно условно считать, что компилятор Рефала-5 в каждый файл неявно добавляет экземпляр функции Mu без пометки $ENTRY следующего вида

Mu {
  …
  ‹имя-функции› e.Arg = <‹имя-функции› e.Arg>;
  ('‹имя-функции›') e.Arg = <‹имя-функции› e.Arg>;
  …
  t.Unknown e.Arg = ‹поиск среди $ENTRY и встроенных›;
}

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

В файл lib.ref, можно считать, что неявным образом добавляется следующая функция Mu:

$ENTRY Greeting {
  s.Greeting e.Name
    = <Prout <Mu s.Greeting> ', ' e.Name '!'>
}

Hello {
  = 'Hi'
}

Bye {
  = 'Bye'
}

Mu {
  Greeting e.Arg = <Greeting e.Arg>;
  Hello e.Arg = <Hello e.Arg>;
  Bye e.Arg = <Bye e.Arg>;
  Mu e.Arg = <Mu e.Arg>;

  s.Unknown e.Arg = ‹поиск среди $ENTRY и встроенных›;
}

В реальности компилятор Рефала-5 действительно в каждый скомпилированный модуль неявно добавляет функцию Mu, тело которой состоит из одной инструкции RASL’а (байткода Рефала-5) BUILT_IN1 с двумя аргументами: указателем на таблицу функций текущего файла и номером встроенной функции Mu — 1 (эти номера возвращает ListOfBuiltin).

Особенности функции Mu в Рефале-05 и нагруженные идентификаторы

Рефал-05 компилирует каждый файл с исходным текстом на Рефале в файл с исходным текстом на Си, каждую функцию Рефала в функцию на Си. Entry-функции компилируются в функции (вернее, в описатели функций) внешней компоновки, локальные — в функции с модификатором static (детали в пятой части).

Из трёх этапов поиска имени Рефала-5:

  1. Все функции того файла, где записан вызов функции Mu,
  2. все функции программы с пометками $ENTRY,
  3. все встроенные функции.

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

Осуществлять поиск среди entry-функций по имени непросто:

Поэтому ради создания простой переносимой реализации с раздельной трансляцией пришлось пожертвовать простотой семантики и полной совместимостью с Рефалом-5. Были введены т.н. нагруженные идентификаторы.

Идентификаторы в Рефале-05 могут быть нагруженными и ненагруженными.

Если в файле, где был записан идентификатор ‹имя› функция с именем ‹имя› видима и имеет глобальную область видимости (т.е. или определена с $ENTRY, или объявлена), то такой идентификатор называется нагруженным. Такой идентификатор в поле зрения несёт «нагрузку» — ссылку на соответствующую функцию. В противном случае идентификатор ненагруженный, никакой нагрузки он не несёт.

Функция Mu в Рефале-05 работает следующим образом:

  1. Если в области видимости вызова функции Mu присутствует функция (объявленная или определённая) с данным именем, то вызывается она,
  2. иначе, если идентификатор нагруженный, то вызывается функция из «нагрузки»,
  3. иначе аварийно завершает программу с ошибкой отождествления.

Указанные правила гарантируют, что семантика функции Mu в Рефале-05 является точным подмножеством семантики Mu Рефала-5: если вызов в Рефале-05 работает, то точно также будет работать вызов и в Рефале-5. Обратное неверно — Рефал-5 может вызвать entry-функцию по имени, в то время как данное имя в Рефале-05 будет записано ненагруженным идентификатором.

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

Нагрузка идентификаторов влияет только на поведение функции Mu, другими средствами Рефала-05 её заметить невозможно. В частности, при сравнении значений на равенство кратными вхождениями переменных образца нагрузка никак не учитывается, учитываются только имена; функции вывода нагрузку не печатают; функция Type нагрузку также не учитывает.

Если переменная в левой части встречается один раз, в правой части — несколько и значение переменной содержит идентификаторы, то нагрузка копируется.

Если переменная несколько раз встречается в образце, то при сравнении на равенство нагрузка идентификаторов не учитывается — значения различных вхождений переменной могут нести разную нагрузку. Если такая кратная переменная входит в результатное выражение, то нагрузка идентификаторов у её значения в результате не определена. Рассмотрим пример:

Файл fileA.ref:

$EXTERN GetX;

$ENTRY GO {
  /* пусто */
    = <Test <GetX> X>
      <Test X <GetX>>
}

Test {
  s.X s.X = <Prout <Mu s.X> <Mu s.X> <Mu s.X>>
}

Файл fileB.ref:

$ENTRY GetX { = X }

$ENTRY X { = 'X' }

На момент написания этих строк данная программа печатает

XXX

И аварийно завершается с ошибкой в вызове <Mu X>. Дело в том, что GetX возвращает нагруженный идентификатор, идентификатор X в fileA.ref — ненагруженный, функция Test размножает первый терм аргумента. Во втором вызове Test будет размножен ненагруженный идентификатор.

Если в функции Test заменить s.X на t.X, то программа будет аварийно завершаться, ничего не напечатав. В других версиях Рефала-05 поведение может оказаться другим.

Если же эту программу откомпилировать и запустить (refgo fileA+fileB) Рефалом-5, то увидим

XXX
XXX

В Рефале-5 нет понятия нагруженных идентификаторов. Функция Mu для имени X всегда будет вызывать функцию X из файла fileB.ref.

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

В предыдущем разделе определялось понятие используемых функций и используемых объявлений — в определениях говорилось:

Теперь должно быть понятно, откуда эта оговорка про «тело содержит составной символ G» — соответствующий символ становится нагруженным идентификатором и несёт в себе ссылку на соответствующую объявленную функцию.

Заметим также, что если в некотором исходном файле есть имя функции F в списке $EXTERN, функция в файле нигде не вызывается, но имеется составной символ F в теле одной из используемых функций, но при этом нигде в программе нет функции F с пометкой $ENTRY, то такая программа не скомпилируется. Компоновщик языка Си выдаст ошибку. В скомпилированном файле ссылка на эту внешнюю функцию будет, т.к. составной символ F будет нагруженным и будет на эту функцию ссылаться. В Рефале-5 в этом случае ошибки не будет — объявления функций, которые не вызываются, ошибкой не являются и молча игнорируются.

Знаковая «полудлинная» арифметика

В отличие от Рефала-5, в Рефале-05 арифметика «полудлинная» — аргументы арифметических операций не могут быть длинными числами (состоящими из нескольких макроцифр), но результат (у Add, Mul, Sub) может. Эти три функции в случае переполнения возвращают две макроцифры.

Формат арифметических функций может быть описан так (см. про обозначения далее):

e.ArithmArg ::=
    (s.Sign? s.NUMBER) s.Sign? s.NUMBER
  | s.Sign? s.NUMBER s.Sign? s.NUMBER
s.Sign ::= '+' | '-'

<Add e.ArithmArg> == '-'? 1? s.NUMBER
<Sub e.ArithmArg> == '-'? 1? s.NUMBER
<Mul e.ArithmArg> == '-'? s.NUMBER? s.NUMBER
<Div e.ArithmArg> == '-'? s.NUMBER
<Mod e.ArithmArg> == '-'? s.NUMBER
<Divmod e.ArithmArg> == ('-'? s.NUMBER) '-'? s.NUMBER
<Compare e.ArithmArg> == '-' | '0' | '+'

Аргументы арифметических функций — числа с необязательным предшествующим знаком '+' или '-', первый аргумент функции может быть заключён в скобки. Результат — одна или две макроцифры, которым может предшествовать знак '-'. У функций Add и Sub предпоследняя макроцифра, если есть, всегда имеет значение 1, у функции Mul может иметь почти любое значение. У функций Div и Mod в результате всегда только одна макроцифра по очевидным причинам.

Семантика функции Divmod может быть условно описана как

Divmod {
  e.Arg = (<Div e.Arg>) <Mod e.Arg>
}

Не все встроенные функции поддерживаются

В актуальной реализации отсутствуют следующие встроенные функции (из тех, которые перечисляет ListOfBuiltin):

Функции Up и Ev-met определены для возможности раскрутки Рефалом-5, однако при запуске они аварийно прерывают программу с выдачей сообщения, что они не реализованы.

Возможно, в дальнейшем этот список будет сокращаться.

Библиотека встроенных функций

Далее мы перечислим встроенные функции в том порядке и с теми номерами, в каком их перечисляет встроенная функция ListOfBuiltin.

Нотация для записи типов функций

Для описания типов функций будем использовать следующие обозначения.

Тип функции:

<ИмяФункции тип-аргумента>
  == тип-результата

Здесь тип-аргумента — описание области определения функции, тип-результата — описание области значений. Оба типа — типы выражений

Именованный тип:

переменная-типа ::= тип-выражения1 | тип-выражения2 | … | тип-выраженияN

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

Несколько именованных типов могут иметь одинаковое описание:

перем1, перем2, перем3 ::= тип-выражения

Тип выражения записывается как образцовое выражение, где после термов (включая переменные) могут использоваться квантификаторы * (0 и более раз), + (1 и более раз) и ? (0 или 1 раз).

Переменные s.CHAR, s.NUMBER и s.FUNCTION описывают, соответственно, произвольную литеру, число и функцию.

Примеры. Произвольное выражение, произвольный терм и произвольный символ:

e.AnyExpr ::= t.AnyTerm*
t.AnyTerm ::= s.AnySymbol | (e.AnyExpr)
s.AnySymbol ::= s.CHAR | s.NUMBER | s.FUNCTION

Входная точка лексического анализатора (длинный список лексем сокращён):

<R05-LexScan-File e.SourceName>
  == e.Tokens
e.Tokens ::= (s.TokType t.SrcPos e.Info)
t.SrcPos ::= (s.Row s.Col)

s.TokType e.Info ::=
    TkChar s.CHAR
  | TkClose s.Bracket
  | TkCloseBlock
    …
  | TkError e.Message
  | TkExtern
  | TkName e.Name
  | TkNative (e.SourceName s.LineNo) (s.CHAR*)*
  | TkNumber s.NUMBER
  | TkOpen s.Bracket
  | TkOpenBlock
  | TkReplace
  | TkSemicolon
  | TkUnexpected e.BadCharacters
  | TkVariable s.Mode e.Index

s.Bracket ::= Bracket | CallBracket
e.Message, e.Name, e.SourceName, e.BadCharacters, e.Index ::= s.CHAR+
s.LineNo ::= s.NUMBER
s.Mode ::= 's' | 't' | 'e'

Входная точка синтаксического анализатора:

<R05-Parse-File e.SourceFile>
  == Success e.Tree
  == Fails e.Errors

e.Errors ::= ((s.Row s.Col) e.Message)*

Часть описания дерева:

e.Tree ::= t.TreeItem*
t.TreeItem ::=
    (Extern e.Name)
  | (Function s.Scope (e.Name) e.Body)
  | (Native e.Native)

e.Name ::= s.CHAR+
s.Scope ::= Entry | Local
e.Body ::= Sentences t.Sentence* | Native e.Native
e.Native ::= (e.SourceName s.Line) (s.CHAR*)*
s.Line ::= s.NUMBER
e.SourceName ::= s.CHAR+

1. Mu

<Mu s.FUNCTION e.AnyExpr> == e.AnyExpr
<Mu (s.CHAR+) e.AnyExpr> == e.AnyExpr
<Mu s.SugarName e.AnyExpr> == e.AnyExpr

s.SugarName ::= '%' | '*' | '+' | '-' | '/' | '?'

Семантика:

  1. если s.FUNCTION является именем функции в области видимости вызова функции Mu, то вызывается одноимённая функция, иначе если s.FUNCTION — нагруженный идентификатор, вызывается нагрузка, иначе — ошибка;
  2. если s.CHAR+ образует имя функции в области видимости вызова функции Mu, вызывается соответствующая функция, иначе — ошибка,
  3. вызовы <Mu '%' …>, <Mu '*' …> и т.д. вызывают, соответственно, <% …>, <* …> и т.д.

Пояснение: можно считать, в области видимости всегда присутствуют все встроенные функции, включая функции с именами "%", "*", "+" и т.д. В Рефале-5 запись <% …> является синтаксическим сахаром для <Mod …>, в Рефале-05 — это вызов встроенной функции "%" (которая разделяет реализацию с Mod), в чём можно убедиться, сделав отладочный дамп (например, вызвав <% 1 0>).

Совместимость с Рефалом-5 См. раздел «Отличия от Рефала-5».

2. Add

<Add e.ArithmArg> == '-'? 1? s.NUMBER

e.ArithmArg ::=
    (s.Sign? s.NUMBER) s.Sign? s.NUMBER
  | s.Sign? s.NUMBER s.Sign? s.NUMBER
s.Sign ::= '+' | '-'

Семантика. Вычисляет сумму двух чисел. Если среди аргументов есть отрицательные слагаемые, то результат может быть отрицательным. В случае арифметического переполнения к результату добавляется макроцифра 1, вторая макроцифра равна сумме аргументов по модулю 2³².

Совместимость с Рефалом-5. В Рефале-5 поддерживаются длинная арифметика, аргументы могут содержать несколько макроцифр. Рефал-05 допускает наличие в аргументах только одной макроцифры.

3. Arg

<Arg s.ArgNo> == e.Argument

s.ArgNo ::= s.NUMBER
e.Argument ::= s.CHAR*

Семантика: возвращает аргумент командной строки с указанным номером. Нулевой аргумент — имя вызываемой программы. Если запрашиваемый аргумент не существует — фактическое их число меньше, чем s.ArgNo, возвращается пустая строка.

Совместимость с Рефалом-5. Интерпретатор Рефала-5 пропускает все аргументы, начинающиеся на знак минус, поэтому в переносимых программах не рекомендуется использовать ключи командной строки, начинающиеся на минус.

4. Br

<Br e.Key '=' e.Value> == пусто

e.Key, e.Value ::= e.AnyExpr

Семантика: Сохраняет в копилке соответствующую пару «ключ-значение».

Совместимость с Рефалом-5: полная.

5. Card

<Card> == s.CHAR* 0?

Семантика. Считывает перфокарту строчку со стандартного ввода. Если встречен символ конца файла, в конец прочитанной строки добавляется число 0.

Совместимость с Рефалом-5. Встроенные функции Card и Get некорректно считывают строки, содержащие внутри себя символ с кодом нуля \x00, поэтому переносимые программы не должны читать двоичные файлы.

6. Chr

<Chr e.AnyExpr> == e.AnyExprChr

e.AnyExprChr ::= t.AnyTermChr*
t.AnyTermChr ::= s.CHAR | s.NUMBER | (e.AnyExprChr)

Семантика: функция заменяет в своём аргументе все числа на символы-литеры с соответствующим ASCII-кодом (по модулю 256).

Совместимость с Рефалом-5: полностью совместима.

7. Cp

<Cp e.Key> == e.Value

Семантика: Копирует из копилки последнее сохранённое значение с заданным ключом. Если с заданным ключом ничего не сохранялось, возвращается пустое выражение.

Совместимость с Рефалом-5: полная. Включая следующую ошибку:

$ENTRY GO {
  = <Cp 'A=B=C'> <Prout <Cp 'A=B'>>
}

И Рефал-5, и Рефал-05 распечатают C.

8. Dg

<Dg e.Key> == e.Value

Семантика: Извлекает из копилки последнее сохранённое значение с заданным ключом. Если с заданным ключом ничего не сохранялось, возвращается пустое выражение.

Совместимость с Рефалом-5: полная. Включая следующую ошибку:

$ENTRY GO {
  = <Br 'A=B=C'> <Prout <Dg 'A=B'>>
}

И Рефал-5, и Рефал-05 распечатают C.

10. Div

<Div e.ArithmArg> == '-'? s.NUMBER

Семантика. Выполняет целочисленное деление. Если делитель равен нулю, программа аварийно останавливается выдачей дампа поля зрения и ошибки «деление на ноль». Если аргументы разных знаков, результат будет отрицательным.

Совместимость с Рефалом-5: см. Add.

11. Divmod

<Divmod e.ArithmArg> == ('-'? s.NUMBER) '-'? s.NUMBER

Семантика. Вычисляет частное и остаток от деления, частное заключает в скобки. Если делитель равен нулю, программа аварийно останавливается выдачей дампа поля зрения и ошибки «деление на ноль».

Если аргументы разных знаков, частное будет отрицательным. Знак остатка совпадает со знаком делимого.

Совместимость с Рефалом-5: см. Add.

12. Explode

<Explode s.FUNCTION> == s.CHAR+

Семантика. Для символа-функции возвращает её имя как последовательность литер. Пример:

<Explode R05-Parse-File> → 'R05-Parse-File'
<Explode findfile_AnalyzeFile-ByFolders> → 'findfile_AnalyzeFile-ByFolders'

Совместимость с Рефалом-5: полная.

13. First

<First s.Len e.Items> == (e.Prefix) e.Suffix

e.Items : e.Prefix e.Suffix
|e.Prefix| == s.Len || { |e.Prefix| < s.Len && |e.Suffix| == 0 }

Семантика. Отделяет от строки префикс указанной длины. Если строка короче, то она вся возвращается как префикс.

Совместимость с Рефалом-5: полностью совместима.

14. Get

<Get s.FileNo> == s.CHAR* 0?

s.FileNo ::= s.NUMBER

Семантика. Функция читает из файла с заданным номером. Номер файла вычисляется как остаток от деления s.FileNo на 40:

file_no = s.FileNo % 40

Если величина file_no равна нулю, то читается стандартный ввод, т.е. вызов <Get 0> (или <Get 40>, <Get 80> и т.д.) будет эквивалентен вызову <Card>.

Если файл с указанным номером не был открыт при помощи функции Open, то открывается файл с именем REFAL<file_no>.DAT в режиме «для чтения», где вместо <file_no> означает запись file_no в десятичном виде. Например, если файл с номером 33 не был открыт, то вызов <Get 143> откроет для чтения файл REFAL33.DAT.

Точно также, как и функция Card, при достижении конца файла функция загружает в поле зрения число 0.

Совместимость с Рефалом-5. См. Card.

15. Implode

<Implode e.ValidPrefix e.Suffix> == s.FUNCTION e.Suffix
<Implode e.Suffix> == 0 e.Suffix

e.ValidPrefix ::= s.Lettern { s.Letter | s.Digit | '_' | '-' | '$' }
s.Letter ::= 'A' | … | 'Z' | 'a' | … | 'z'
s.Digit ::= '0' | … | '9'

Семантика. У аргумента выделяется префикс максимальной длины, являющийся записью корректного имени функции в Рефале-5. Если этот префикс не пустой, функция Implode возвращает функцию, имя которой записано в префиксе и суффикс. Если же префикс пустой, то функция возвращает число 0 и суффикс (фактически, совпадающий со всем аргументом).

Особенность Рефала-05 — если указано имя встроенной функции, то возвращается встроенная функция (нагруженный идентификатор), если другое имя — пустая функция (ненагруженный идентификатор):

<Mu <Implode 'Add' 12 34>>  →  46

Совместимость с Рефалом-5. Полная. В частности, и Рефал-5, и Рефал-05 допускают в идентификаторе, создаваемом Implode, знак $, однако, в Рефале-5 эта особенность недокументированная.

17. Lenw

<Lenw e.Expr> == s.Len e.Expr

|e.Expr| == s.Len

Семантика: возвращает длину своего аргумента в термах и сам аргумент.

Совместимость с Рефалом-5: полная.

18. Lower

<Lower e.Expr> == e.Expr′

Семантика: заменяет в своём аргументе все литеры заглавных букв на строчные, остальные символы не меняет.

Совместимость с Рефалом-5: полная.

19. Mod

<Mod e.ArithmArg> == '-'? s.NUMBER

Семантика: вычисляет остаток от деления. При делении на нуль — см. Div.

Знак остатка совпадает со знаком делимого.

Совместимость с Рефалом-5: см. Add.

20. Mul

<Mul e.ArithmArg> == '-'? s.Number? s.NUMBER

Семантика: вычисляет произведение двух чисел. Если ответ превышает 2³²−1, то ответ представляется в виде двух макроцифр X и Y, что следует трактовать как X × 2³² + Y.

Совместимость с Рефалом-5: см. Add.

21. Numb

<Numb s.Sign? s.CHAR*> == '-'? s.NUMBER

Семантика. Если аргумент начинается с последовательности цифр, то возвращается соответствующее число. В противном случае возвращается 0. В случае, если значение не вмещается в одну макроцифру, функция аварийно завершается с сообщением integer overflow.

Совместимость с Рефалом. В Рефале-5 поддерживаются длинная арифметика, поэтому результат может содержать несколько макроцифр. В Рефале-5 ни при каком аргументе функция аварийно не завершается.

22. Open

<Open s.Mode s.FileNo e.FileName?> == пусто

s.Mode ::=
    'r' | 'w' | 'a'
  |  r  |  w  |  a
  |  rb |  wb |  ab
e.FileName ::= s.CHAR+

Семантика. Функция открывает файл с заданным номером в заданном режиме. Номер файла вычисляется по формуле

file_no = s.FileNo % 40

Если файл с номером file_no был открыт ранее, он закрывается. Если имя файла не задано, то открывается файл с именем REFAL<file_no>.DAT, где <file_no> — десятичная запись file_no.

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

Режимы:

Файл с номером 39 используется в LibraryEx функциями LoadFile и SaveFile.

Совместимость с Рефалом-5. Функция полностью совместима.

23. Ord

<Ord e.AnyExpr> ::= AnyExprOrd

e.AnyExprOrd ::= t.AnyTermOrd*
t.AnyTermord ::= s.NUMBER | s.FUNCTION | (e.AnyExprOrd)

Семантика: заменяет в своём аргументе все литеры на их ASCII-коды.

Совместимость с Рефалом-5: полностью совместима.

24. Print

<Print e.AnyExpr> == e.AnyExpr

Семантика. Распечатывает объектное выражение и возвращает свой аргумент. Преобразование в цепочку литер осуществляет точно также, как и Prout.

Совместимость с Рефалом-5: полностью совместима.

25. Prout

<Prout e.AnyExpr> == пусто

Семантика. Распечатывает объектное выражение. Литеры выводятся как есть, числа выводятся в десятичном виде, для идентификаторов выводятся их имена, структурные скобки распечатываются как ( и ) (при печати неотличимы от '(', ')'). После чисел и идентификаторов добавляется пробел, чтобы при выводе нескольких чисел или функций подряд их образы не слипались.

Совместимость с Рефалом-5: полностью совместима.

26. Put

<Put s.FileNo e.Expr> == e.Expr

Семантика. Распечатывает объектное выражение в файл с указанным номером и возвращает распечатанное выражение неизменным. В остальном эквивалента Putout.

Совместимость с Рефалом-5: полностью совместима.

27. Putout

<Putout s.FileNo e.Expr> == пусто

Семантика. Распечатывает объектное выражение в файл с указанным номером. Преобразование в цепочку литер осуществляет точно также, как и Prout. Номер файла определяется по формуле:

file_no = s.FileNo % 40

Если файл с номером file_no не открыт, то открывается файл с именем REFAL<file_no>.DAT, где <file_no> — десятичная запись file_no в режиме (пере)записи (см. 'w' у функции Open).

Совместимость с Рефалом-5: полностью совместима.

28. Rp

<Rp e.Key '=' e.Value> == пусто

e.Key, e.Value ::= e.AnyExpr

Семантика: Заменяет в копилке значение, ассоциированное с данным ключом. Если значения не было, оно добавляется.

Совместимость с Рефалом-5: полная.

30. Sub

<Sub e.ArithmArg> == '-'? 1? s.NUMBER

Семантика: вычисляет разность двух чисел. Если первое число меньше второго, возвращается литера '-' и значением модуля их разности. Иначе просто возвращается их разность. В случае, если аргументы разных знаков, возможно арифметическое переполнение, в этом случае к ответу добавляется макроцифра 1, вторая макроцифра есть сумма абсолютных значений аргументов по модулю 2³².

Совместимость с Рефалом-5: см. Add.

31. Symb

<Symb e.Sign s.NUMBER> == e.Sign s.CHAR+
e.Sign ::= '+' | '-' | пусто

Семантика. Преобразует число в его десятичную запись. Если числу предшествовала литера '+' или '-', та же литера будет предшествовать и результату. Поддержка знака была добавлена для совместимости с функцией System.

Совместимость с Рефалом-5. Рефал-5 поддерживает длинную арифметику, поэтому в аргументе может быть указано несколько чисел-макроцифр, таким образом, функция Symb Рефала-05 обрабатывает помножество области определения функции Рефала-5.

32. Time

<Time> == s.CHAR+

Семантика: возвращает текущее время в формате функции ctime() языка Си (однако, без знака \n на конце результата).

Совместимость с Рефалом-5: полная.

33. Type

<Type e.AnyExpr> == s.Type s.SubType e.AnyExpr

s.Type s.SubType ::=
    'Lu' — uppercase latin letter
  | 'Ll' — lowercase latin letter
  | 'D0' — decimal digit
  | 'Wi' — identifier (function)
  | 'N0' — number
  | 'Pu' — isprint() && isupper()
  | 'Pl' — isprint() && ! isupper()
  | 'Ou' — other && isupper()
  | 'Ol' — other && ! isupper()
  | 'B0' — brackets
  | '*0' — empty expression

Семантика: возвращает тип первого терма аргумента. Если аргумент пустой, возвращает '*0'. Остальные типы и подтипы:

Работа функции зависит от установленной локали (которая по умолчанию вроде "C"), если локаль изменена (конфигурацией операционной системы или внешней функцией на Си), то функция будет работать иначе. Лексический анализатор полагается на эту функцию.

Совместимость с Рефалом-5: полная.

34. Upper

<Upper e.Expr> == e.Expr′

Семантика: заменяет в своём аргументе все литеры строчных букв на заглавные, остальные символы не меняет.

Совместимость с Рефалом-5: полная.

48. Up

Метафункция Up не реализована, при вызове аварийно завершает программу. Добавлена для совместимости при раскрутке Рефалом-5.

49. Ev-met

Метафункция Ev-met не реализована, при вызове аварийно завершает программу. Добавлена для совместимости при раскрутке Рефалом-5.

50. Residue

В текущей реализации в точности эквивалентна Mu — см. описание функции Mu.

Доступен синтаксический сахар <? …>.

Совместимость с Рефалом-5. В Рефале-5 различия между Mu и Residue возникают при вычислении в метакоде (в Ev-met). В актуальной реализации Ev-met не поддерживается.

51. GetEnv

<GetEnv e.EnvName> == e.EnvValue
e.EnvName, e.EnvValue ::= s.CHAR*

Семантика: возвращает значение переменной среды с заданным именем. Если переменная среды не установлена, возвращает пустую строку.

Совместимость с Рефалом-5: полная.

52. System

<System e.Command> == e.RetCode
e.Command ::= s.CHAR*
e.RetCode ::= '-'? s.NUMBER

Семантика. Выполняет команду e.Command при помощи функции system() языка Си.

На POSIX (если установлен ключ -DR05_POSIX компилятора Си), если запущенный процесс успешно завершился, возвращает код его возврата, иначе возвращает '-' 1. На Windows (т.е. когда не установлен -DR05_POSIX) возвращает то, что вернула system() как есть.

Отрицательный код возврата представляется как число с предшествующей литерой '-'.

Совместимость с Рефалом-5: полная.

53. Exit

<Exit e.RetCode>
e.RetCode ::= '-'? s.NUMBER

Семантика. Завершает программу с заданным кодом возврата. Отрицательное значение записывается как литера '-' с последующим числом.

Совместимость с Рефалом-5: полная.

54. Close

<Close s.FileNo> == пусто

Семантика. Закрывает открытый файл с номером s.FileNo % 40. Если файл с этим номером не был открыт, функция ничего не делает.

Совместимость с Рефалом-5: полная.

55. ExistFile

<ExistFile e.FileName> == True | False
e.FileName ::= s.CHAR*

Семантика. Функция пытается открыть файл с указанным именем для чтения при помощи fopen(). Если удаётся — закрывает открытый файл и возвращает True, в противном случае возвращает False. Функции True и False не являются встроенными функциями, это значит, что их нужно явно подключать при помощи $EXTERN. Так сделано из соображений простоты и переносимости.

Совместимость с Рефалом-5. Рефал-5 действует честнее — использует средства операционной системы, чтобы понять, существует файл или нет. А это значит, что если файл существует, но недоступен для чтения, функции ExistFile Рефала-5 и Рефала-05 увидят его по-разному.

57. RemoveFile

<RemoveFile e.FileName> == True () | False (e.Message)

e.Message ::= s.CHAR*

Семантика: функция удаляет файл с заданным именем. При неудаче возвращает сообщение об ошибке, содержимое которого зависит от ОС и реализации языка Си.

Совместимость с Рефалом-5: вроде полная.

58. Implode_Ext

<Implode_Ext s.CHAR*> == s.FUNCTION

Семантика. Строит составной символ из литер в аргументе. Точно также, как и в случае Implode, если составной символ образует имя встроенной функции, составной символ оказывается «нагруженным» указателем на эту функцию. Если не образует, то вызов такого составного символа при помощи Mu приведёт к ошибке отождествления.

В Рефале-05 символы "%", "*", "+", "-", "/" и "?" являются именами соответствующих встроенных функций (синонимы для Mod, Mul, Add, Sub, Div и Residue соответственно).

Совместимость с Рефалом-5. У Рефала-5 есть недокументированное расширение — фактический формат имеет вид:

<Implode_Ext s.CHAR* e.ANY> == s.FUNCTION

где e.ANY не начинается с литеры. Рефал-05 такое расширение не поддерживает.

В остальном совместимость полная.

59. Explode_Ext

<Explode_Ext s.FUNCTION> == s.CHAR*

Семантика: функция в точности эквивалентна функции Explode.

Совместимость с Рефалом-5: полная.

60. TimeElapsed

<TimeElapsed 0?> == s.CHAR+

Семантика. Функция возвращает число секунд, прошедших с момента предыдущего вызова <TimeElapsed 0>, либо с начала программы, если <TimeElapsed> ни разу не вызывалась. Число секунд возвращается в виде десятичной записи дробного числа вида '12.345'.

Совместимость с Рефалом-5. Полная.

61. Compare

<Compare e.ArithmArg> == '-' | '0' | '+'

Семантика. Возвращает '-', если первое число меньше второго, '0' — если они равны и '+', если первое больше второго. Иначе говоря, возвращает знак разности этих двух чисел.

Совместимость с Рефалом-5. Рефал-5 поддерживает длинную арифметику, в том числе и для функции Compare (см. Add).

64. Random

 <Random s.Len> == e.RandomDigits

 e.RandomDigits ::= s.NUMBER+
 |e.RandomDigits| == ((s.Len != 0) ? s.Len : 1)

Семантика: генерирует случайное количество (не более s.Len) случайных макроцифр.

Совместимость с Рефалом-5: полная.

65. RandomDigit

 <RandomDigit s.Max> == s.RandomDigit

 s.RandomDigit, s.Max ::= s.NUMBER
 s.RandomDigit <= s.Max

Семантика: генерирует случайное число от 0 до s.Max.

Совместимость с Рефалом-5: полная.

66. Write

<Write s.FileNo e.Expr> == пусто

Семантика: функция аналогична Putout, только после вывода текста не печатает '\n'.

Совместимость с Рефалом-5: полная.

67. ListOfBuiltin

<ListOfBuiltin> == (s.FuncNo s.Name s.BuiltinType)+

s.FuncNo ::= s.NUMBER
s.Name ::= s.FUNCTION
s.BuiltinType ::= special | regular

Семантика. Выводит список встроенных функций в указанном формате. Величина s.FuncNo в данной реализации смысла не имеет, добавлена для совместимости с Рефалом-5. s.BuiltinType равна special для метафункций, regular для обычных функций. Значения s.FuncNo и s.BuiltinType текущая реализация возвращает те же, что и Рефал-5 версии PZ Oct 29 2004.

Список имён, возвращаемых этой функцией, используется компилятором, чтобы неявно предобъявлять встроенные функции.

Совместимость с Рефалом-5. Эта функция в Рефале-5 перечисляет гораздо больше имён. Значения s.FuncNo и s.BuiltinType относятся ко внутренней реализации интерпретатора Рефала-5.

Отличия от Рефала-5

Рефал-05 проектировался как точное подмножество Рефала-5: любая корректная программа на Рефале-05 будет корректной программой на Рефале-5, причём выполняться она будет точно также, с несколькими исключениями:

Возможно, в последующих версиях эти ограничения будут ослаблены или сняты.

Рефал-05 является подмножеством, а это значит, что существуют программы на Рефале-5, которые Рефалом-05 поддерживаться не будут (не будут компилироваться или корректно выполняться).

Как нужно писать программы на Рефале-5, чтобы они работали в Рефале-05:

Диспетчеризация с использованием Mu

Ранее, когда Рефал-05 поддерживал пустые функции (как Рефал-2), был написан этот раздел — в нём говорилось, что функции для идентификаторов не обязательно делать пустыми, в них можно писать полезную логику, делая идентификаторы исполнимыми. Однако, эти «исполнимые идентификаторы» оказываются полезными и для актуального Рефала-05, и даже для Рефала-5. Поэтому данный раздел, немного офтопик, оставлен (но актуализирован) — он описывает полезный приём программирования.

Рефал — функциональный язык. Рефал-05 и Рефал-5 — функциональные языки с ограниченной поддержкой функций высшего порядка, предназначенные для символьных вычислений: преобразования формул, анализа и компиляции программ и т.д.

Символьные вычисления подразумевают использования древовидных данных, например, в случае компиляции — цепочек токенов, деревьев абстрактного синтаксиса, команд промежуточного кода и т.д. Зачастую узлы таких деревьев помечаются символами-словами — узел выглядит как (‹слово› ‹данные›…).

И с такими метками иногда можно связать какое-либо действие «по умолчанию». Например, каждый токен должен уметь себя выводить в текстовой форме для вывода сообщения об ошибке. Команда промежуточного кода компилируется в кусок целевого кода.

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

Например, пусть у нас есть синтаксическое дерево такого вида

t.Expr ::=
    (ExNumber s.Number)
  | (ExVariable e.Name)
  | (ExAdd t.Left t.Right)
  | (ExSub t.Left t.Right)
  | (ExMul t.Left t.Right)
  | (ExDiv t.Left t.Rigth)
  | (ExMinus t.Expr)

t.Left, t.Right ::= t.Expr

И есть функция вывода этого дерева в виде арифметического выражения:

StrFromExpr {
  (ExNumber s.Number) = <Symb s.Number>;
  (ExVariable e.Name) = e.Name;
  (ExAdd t.Left t.Right) =
     '(' <StrFromExpr t.Left> '+' <StrFromExpr t.Rigth ')';
  ...
}

Если выбрать действием по умолчанию печать, то имена узлов дерева можно определить как функции:

$ENTRY ExNumber {
  s.Number = <Symb s.Number>;
}

$ENTRY ExVariable {
  e.Name = e.Name;
}

$ENTRY ExAdd {
  (s.Left e.LeftVal) (s.Right e.RightVal) =
    '(' <Mu s.Left e.LeftVal> '+' <Mu s.Right e.RightVal> ')';
}

...

Теперь функции с символическими именами не пустые, а уже имеют некоторе осмысленное тело.

В самом компиляторе Рефала-05 таким образом представлены команды промежуточного кода — их вызов при помощи Mu приводит к генерации целевого кода (в исходном файле R05-Generator.ref можно поискать идентификаторы, начинающиеся на Cmd…).

Лирическое отступление: ООП в Рефале-05

Вообще, это подход можно развить до идиоматической реализации ООП (без наследования). Каждый объект — скобочный терм, начинающийся с символического имени. Функция символического имени является виртуальной таблицей — может обрабатывать различные сообщения. Методы эти сообщения посылают.

Выглядит это так. Фигуры «Прямоугольник» (ORect) и «Круг» (OCircle) могут распечатать себя в текстовом виде (метод MPrint), сдвинуться (метод MMove) и ответить, содержат ли они точку с заданными координатами (метод MHasPoint). Точка (OPoint) тоже может себя печатать и двигаться:

ORect {
  (ORect t.LeftTop t.RightBottom) MPrint =
    'Rectangle [' <MPrint t.LeftTop> '-' <MPrint t.RightBottom> ']';

  (ORect t.LeftTop t.RightBottm) MMove t.Offset =
    (ORect <MMove t.LeftTop t.Offset> <MMove t.RightBootom t.Offset>);

  (ORect (OPoint s.Left s.Top) (OPoint s.Right s.Bottom))
  MHasPoint (OPoint s.X s.Y)
    <And <InRange s.Left s.X s.Right> <InRange s.Bottom s.Y s.Top>>;
}

OCircle {
  (OCircle t.Center s.Radius) MPrint =
    'Circle [center: ' <MPrint t.Center> ', radius: ' <Symb s.Radius> ']';

  (OCircle t.Center s.Radius) MMove t.Offset =
    (OCircle <MMove t.Center> s.Radius);

  (OCircle (OPoint s.CX s.CY) s.Radius) MHasPoint (OPoint s.X s.Y) =
    <InRange
      0
      <Add <Square <Dist s.CX s.X>> <Square <Dist s.CY s.Y>>>
      <Square s.Radius>
    >;
}

OPoint {
  (OPoint s.X s.Y) MPrint = '(' <Symb s.X> ', ' <Symb s.Y> ')';

  (OPoint s.X s.Y) MMove (OPoint s.dX s.dY) =
    (OPoint <Add s.X s.dX> <Add s.Y s.dY>);
}

MPrint {
  t.Object = <Send t.Object MPrint>;
}

MMove {
  t.Object t.Offset = <Send t.Object MMove t.Offset>;
}

MHasPoint {
  t.Object t.Point = <Send t.Object MHasPoint t.Offset>;
}

Send {
  (s.VTable e.Data) s.Method e.Args =
    <Mu s.VTable (s.VTable e.Data) s.Method e.Args>;
}

(Функции InRange, Dist и Square имеют очевидную, но громоздкую реализацию, которая для краткости не приведена.)

И кто теперь скажет, что Рефал-05 не объектно-ориентированный язык 😉?

Косвенный вызов в Рефале-5 и Рефале-05, разная семантика Mu

Рассуждения в этом разделе в равной степени относятся и к другим метафункциям, поэтому далее будем говорить только о Mu.

Напомним семантику вызова <Mu ‹символ-слово› …> в обоих реализациях Рефала:

  1. Выполняется поиск символа-слова среди функций в том файле, где записан вызов функции Mu:
    • в случае Рефала-5 — среди функций, определённых в файле,
    • в случае Рефала-05 — среди функций, определённых и объявленных в файле.
  2. Если функция не нашлась на первом этапе:
    • в случае Рефала-5 осуществляется поиск среди всех entry-функций программы (встроенные функции также являются entry-функциями),
    • в случае Рефала-05 идентификатор должен быть нагруженным — литерал символа-слова должен находиться в области видимости объявления или определения одноимённой entry-функции (идентификаторы встроенных функций всегда нагруженные).

В случае, если косвенный вызов успешно разрешается на первом этапе в одной из двух реализаций, то он успешно разрешится и в другой. Рассмотрим подробнее:

А вот если дело дошло до второго этапа, то в Рефале-05 вызов может не разрешиться, в то время как в Рефале-5 он разрешится.

В Рефале-05 вызов разрешится, если в точке записи литерала имени функции присутствует определение entry-функции или объявление функции с заданным именем (считаем, что все встроенные функции неявно объявлены для однородности рассуждений) — только в этом случае идентификатор будет нагруженным. Если функция определена, то Рефал-5 успешно вызовет соответствующую entry-Функцию, найдя её по имени. Если функция объявлена, то Рефал-05 успешно скомпоновал программу, а значит, в программе эта функция присутствует и имеет модификатор $ENTRY — Рефал-5 её тоже успешно найдёт.

Проиллюстрируем рассуждения примером:

Файл go.ref:

$EXTERN Foo, Call;

$ENTRY GO {
  = <Call Foo> <Call Bar> <Call Baz>
}

$ENTRY Bar {
  = <Prout 'Bar'>
}

Файл caller.ref:

$EXTERN Baz;

$ENTRY Call {
  s.Func = <Mu s.Func>
}

Файл foo.ref:

$ENTRY Foo {
  = <Prout 'Foo'>
}

Файл baz.ref:

$ENTRY Baz {
  = <Prout 'Baz'>
}

В Рефале-5 все три вызова Mu успешно разрешатся, т.к. Foo, Bar и Baz определены как глобальные. В Рефале-05 тоже успешно, Foo и Bar — нагруженные идентификаторы (Foo ссылается на объявление функции, Bar — на определение глобальной функции), вызов Mu находится в области видимости объявления Baz.

Если мы исключим из программы файл foo.ref или baz.ref, то в случае Рефала-5 программа успешно загрузится, однако Mu не сможет найти соответствующую функцию, в случае Рефала-05 программа даже не скомпилируется (компоновщик языка Си сообщит о неразрешённых ссылках).

Рассмотрим теперь ситуацию, когда в Рефале-5 вызов разрешается, но вызов не разрешится в Рефале-05. Для этого entry-функция в программе должна присутствовать, однако она не должна быть видима ни в файле, где записан литерал, ни в файле, где осуществляется косвенный вызов при помощи Mu.

Пример программы, которая успешно выполнится Рефалом-5, но аварийно завершится при использовании Рефала-05:

Файл go.ref:

$ENTRY GO {
  = <Mu Hello>
}

Файл hello.ref:

$ENTRY Hello {
  = <Prout 'Hello!'>
}

При запуске refgo go+hello программа успешно печатает Hello!, однако будучи откомпилированной Рефалом-05, она ожидаемо сваливается с ошибкой.

В случае, если имя запускаемой функции задано в виде цепочки литер: <Mu (‹литеры›) …>, в Рефале-5 по-прежнему отрабатывают оба этапа, в Рефале-05 только первый этап, т.к. литеры нагруженными быть не могут. Соответственно, простейший пример программы, которая успешно выполняется в Рефале-5, но не в Рефале-05, следующий:

Файл go.ref:

$EXTERN Call;

$ENTRY GO {
  = <Call 'Hello'>
}

$ENTRY Hello {
  = <Prout 'Hello!'>
}

Файл caller.ref:

$ENTRY Call {
  e.Name = <Mu (e.Name)>
}

В Рефале-5 вызов Mu добирается до второго этапа (поиск среди entry-функций), в Рефале-05 для цепочки литер второго этапа нет, а в области видимости вызова Mu нет функции Hello. Проблему можно исправить, если модифицировать caller.ref следующим образом:

$EXTERN Hello;

$ENTRY Call {
  e.Name = <Mu (e.Name)>
}

В случае Рефала-5 разрешение по-прежнему выполняется на втором этапе, а в Рефале-05 — уже на первом.

Стилистические детали косвенного вызова

Здесь опять фрагмент старого варианта документации, который автору жалко удалять. Он здесь тоже немного не в тему, однако, был доработан в соответствии с актуальной версией.

Следует помнить о том, что на Рефале-5 функция Mu сначала просматривает функции файла со своим вызовом, а уже потом — глобальное пространство entry-функций. А это значит, что надо остерегаться конфликта имён с обеих сторон: подбирать имя entry-функции таким образом, чтобы оно не было похоже на локальные функции модуля с косвенным вызовом и подбирать имена локальным функциям, чтобы избежать конфликта с возможными entry-функциями.

Да, в Рефале-5 локальные функции не такие локальные: если в файле есть вызов Mu, можно извне вызвать любую из них.

Есть один важный стилевой момент. Основное предназначение ключевого слова $ENTRY — отделять интерфейс модуля (набор входных точек) от его реализации, внутренней кухни. Но в случае косвенного вызова в Рефале-5 и общем подмножестве программист вынужден делать входными точками те функции, которые по смыслу являются деталями реализации и в интерфейс не входят.

Как же избежать конфликта имён? В исходных текстах Рефала-05 принят такой подход. Имена всех функций по умолчанию пишутся с большой буквы. Функций, которые косвенно вызываются извне и должны быть entry, начинаются с префикса, представляющего собой имя файла с маленькой буквы и прочерк. Такое странное имя, во-первых, предотвращает возможный конфликт, если в разных файлах потребуется определить entry-Функции с одинаковыми именами, во-вторых, намекает, что эта entry-функция является деталью реализации и не входит в интерфейс модуля. Пример такого имени: generator_GenCommand, которая определена в файле R05-Generator.ref

С другой стороны — локальные функции часто являются частью алгоритма какой-то другой entry-функции. Имена таких вспомогательных функций строятся из имени entry-функции путём добавления либо префиксов Sw и Do, либо смысловых суффиков вида Функция-Подфункция (см. Приложение A). Таким образом имена точек входа и имена вспомогательных функций заметно различаются и тем самым минимизируется конфликт.