Синтаксис Рефала-05 является точным подмножеством синтаксиса Рефала-5 (версия
PZ Oct 29 2004
) — если программма компилируется без ошибок Рефалом-05, то она
будет без ошибок скомпилирована и Рефалом-5 тоже.
Обратное неверно — Рефал-05 может выдавать синтаксические ошибки на некоторые корректные программы Рефала-5. Это следующие ошибки:
Так сделано намеренно. Рефал-05 — это не Рефал-5, это другой язык, у него могут быть другие правила.
Будем говорить, что функция объявлена, если имя этой функции присутствует
в списке $EXTERN
.
Будем говорить, что функция определена, если в файле присутствует тело этой функции.
С точки зрения Рефала-05 каждая функция может быть либо встроенной, либо объявленной, либо определённой. Причём, и объявлена, и определена она может только однократно.
Отсюда следует, что
$EXTERN
и не может быть записано
в нескольких разных списках $EXTERN
,$EXTERN
.Рефал-05 считает синтаксической ошибкой объявления и определения, которые не используются в программе.
Рефал-05 использует раздельную компиляцию, он за раз анализирует только один
файл исходного текста, поэтому он не может понять, что в конкретной программе
какая-либо функция, помеченная $ENTRY
не используется. Более того, наличие
таких фактически неиспользуемых функций скорее правило, ведь пользователь
может подключать к программе библиотеку (например, LibraryEx
), из которой
использовать далеко не все функции. Поэтому функции, помеченные $ENTRY
,
являются используемыми по определению.
Определим формально, что такое используемые функции и используемые определения.
Метафункция — встроенная функция Рефала-05, способная вызвать функцию
по её имени (заданным составным символом и/или цепочкой литер). В Рефале-5
встроенными функциями являются функции Mu
, Residue
, Up
и Ev-met
,
актуальная версия Рефала-05 поддерживает только Mu
и Residue
.
Множество используемых функций — это минимальное множество, определённое следующими правилами:
$ENTRY
, она используемая,F
используемая и её тело содержит вызов <G …>
, то функция
G
тоже используемая,F
используемая и её тело содержит вызов <M …>
, где M
—
метафункция, то все функции, определённые в файле, являются используемыми
(обоснование: к примеру, вызов <Mu (<Card>)>
может вызвать любую функцию).Множество используемых объявлений — это минимальное множество, определённое следующими правилами:
F
используемая, её тело содержит вызов <G …>
и программа
содержит имя G
в списке $EXTERN
, то это имя в списке $EXTERN
используемое.F
используемая, её тело содержит составной символ G
и программа содержит имя G
в списке $EXTERN
, то это имя в списке
$EXTERN
используемое,F
используемая и её тело содержит вызов <M …>
, где M
—
метафункция, то все объявления являются используемыми.Соответственно, неиспользуемые функции и объявления — это функции и объявления, которые не входят во множества используемых. На них компилятор Рефала-05 выдаёт синтаксические ошибки.
Правила вида «если функция F
используемая, её тело содержит составной
символ G
…» связаны с семантикой нагруженных идентификаторов —
см. раздел про них и функцию Mu
ниже.
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
ищется в три этапа:
Mu
,$ENTRY
,Первая найденная функция вызывается. Если функцию найти не удалось, то программа завершается аварийно.
Второй этап — поиск среди функций с пометками $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:
Mu
,$ENTRY
,несложно реализовать только первый и последний, а именно, генерировать
в каждом файле свой экземпляр функции Mu
, осуществляющий поиск в локальной
области видимости.
Осуществлять поиск среди entry-функций по имени непросто:
Поэтому ради создания простой переносимой реализации с раздельной трансляцией пришлось пожертвовать простотой семантики и полной совместимостью с Рефалом-5. Были введены т.н. нагруженные идентификаторы.
Идентификаторы в Рефале-05 могут быть нагруженными и ненагруженными.
Если в файле, где был записан идентификатор ‹имя›
функция с именем ‹имя›
видима и имеет глобальную область видимости (т.е. или определена с $ENTRY
,
или объявлена), то такой идентификатор называется нагруженным. Такой
идентификатор в поле зрения несёт «нагрузку» — ссылку на соответствующую
функцию. В противном случае идентификатор ненагруженный, никакой нагрузки
он не несёт.
Функция Mu
в Рефале-05 работает следующим образом:
Mu
присутствует функция
(объявленная или определённая) с данным именем, то вызывается она,Указанные правила гарантируют, что семантика функции 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
его вызовет.
В предыдущем разделе определялось понятие используемых функций и используемых объявлений — в определениях говорилось:
- если функция
F
используемая, её тело содержит составной символG
и программа содержит имяG
в списке$EXTERN
, то это имя в списке$EXTERN
используемое.
Теперь должно быть понятно, откуда эта оговорка про «тело содержит составной
символ 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
):
Dgall
,Last
,Step
,Sysfun
,Imp$$
,Stop$$
,Freeze
,Freezer
,Dn
,GetCurrentDirectory
,DeSysfun
,XMLParse
,SizeOf
,GetPID
,int4fab_1
,GetPPID
.Функции 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+
<Mu s.FUNCTION e.AnyExpr> == e.AnyExpr
<Mu (s.CHAR+) e.AnyExpr> == e.AnyExpr
<Mu s.SugarName e.AnyExpr> == e.AnyExpr
s.SugarName ::= '%' | '*' | '+' | '-' | '/' | '?'
Семантика:
s.FUNCTION
является именем функции в области видимости вызова
функции Mu
, то вызывается одноимённая функция, иначе если s.FUNCTION
—
нагруженный идентификатор, вызывается нагрузка, иначе — ошибка;s.CHAR+
образует имя функции в области видимости вызова функции Mu
,
вызывается соответствующая функция, иначе — ошибка,<Mu '%' …>
, <Mu '*' …>
и т.д. вызывают, соответственно, <% …>
,
<* …>
и т.д.Пояснение: можно считать, в области видимости всегда присутствуют все встроенные
функции, включая функции с именами "%"
, "*"
, "+"
и т.д. В Рефале-5 запись
<% …>
является синтаксическим сахаром для <Mod …>
, в Рефале-05 — это вызов
встроенной функции "%"
(которая разделяет реализацию с Mod
), в чём можно
убедиться, сделав отладочный дамп (например, вызвав <% 1 0>
).
Совместимость с Рефалом-5 См. раздел «Отличия от Рефала-5».
<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 допускает наличие в аргументах только одной макроцифры.
<Arg s.ArgNo> == e.Argument
s.ArgNo ::= s.NUMBER
e.Argument ::= s.CHAR*
Семантика: возвращает аргумент командной строки с указанным номером.
Нулевой аргумент — имя вызываемой программы. Если запрашиваемый аргумент
не существует — фактическое их число меньше, чем s.ArgNo
, возвращается
пустая строка.
Совместимость с Рефалом-5. Интерпретатор Рефала-5 пропускает все аргументы, начинающиеся на знак минус, поэтому в переносимых программах не рекомендуется использовать ключи командной строки, начинающиеся на минус.
<Br e.Key '=' e.Value> == пусто
e.Key, e.Value ::= e.AnyExpr
Семантика: Сохраняет в копилке соответствующую пару «ключ-значение».
Совместимость с Рефалом-5: полная.
<Card> == s.CHAR* 0?
Семантика. Считывает перфокарту строчку со стандартного ввода. Если
встречен символ конца файла, в конец прочитанной строки добавляется число 0
.
Совместимость с Рефалом-5. Встроенные функции Card
и Get
некорректно
считывают строки, содержащие внутри себя символ с кодом нуля \x00
, поэтому
переносимые программы не должны читать двоичные файлы.
<Chr e.AnyExpr> == e.AnyExprChr
e.AnyExprChr ::= t.AnyTermChr*
t.AnyTermChr ::= s.CHAR | s.NUMBER | (e.AnyExprChr)
Семантика: функция заменяет в своём аргументе все числа на символы-литеры с соответствующим ASCII-кодом (по модулю 256).
Совместимость с Рефалом-5: полностью совместима.
<Cp e.Key> == e.Value
Семантика: Копирует из копилки последнее сохранённое значение с заданным ключом. Если с заданным ключом ничего не сохранялось, возвращается пустое выражение.
Совместимость с Рефалом-5: полная. Включая следующую ошибку:
$ENTRY GO {
= <Cp 'A=B=C'> <Prout <Cp 'A=B'>>
}
И Рефал-5, и Рефал-05 распечатают C
.
<Dg e.Key> == e.Value
Семантика: Извлекает из копилки последнее сохранённое значение с заданным ключом. Если с заданным ключом ничего не сохранялось, возвращается пустое выражение.
Совместимость с Рефалом-5: полная. Включая следующую ошибку:
$ENTRY GO {
= <Br 'A=B=C'> <Prout <Dg 'A=B'>>
}
И Рефал-5, и Рефал-05 распечатают C
.
<Div e.ArithmArg> == '-'? s.NUMBER
Семантика. Выполняет целочисленное деление. Если делитель равен нулю, программа аварийно останавливается выдачей дампа поля зрения и ошибки «деление на ноль». Если аргументы разных знаков, результат будет отрицательным.
Совместимость с Рефалом-5: см. Add
.
<Divmod e.ArithmArg> == ('-'? s.NUMBER) '-'? s.NUMBER
Семантика. Вычисляет частное и остаток от деления, частное заключает в скобки. Если делитель равен нулю, программа аварийно останавливается выдачей дампа поля зрения и ошибки «деление на ноль».
Если аргументы разных знаков, частное будет отрицательным. Знак остатка совпадает со знаком делимого.
Совместимость с Рефалом-5: см. Add
.
<Explode s.FUNCTION> == s.CHAR+
Семантика. Для символа-функции возвращает её имя как последовательность литер. Пример:
<Explode R05-Parse-File> → 'R05-Parse-File'
<Explode findfile_AnalyzeFile-ByFolders> → 'findfile_AnalyzeFile-ByFolders'
Совместимость с Рефалом-5: полная.
<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: полностью совместима.
<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
.
<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 эта особенность недокументированная.
<Lenw e.Expr> == s.Len e.Expr
|e.Expr| == s.Len
Семантика: возвращает длину своего аргумента в термах и сам аргумент.
Совместимость с Рефалом-5: полная.
<Lower e.Expr> == e.Expr′
Семантика: заменяет в своём аргументе все литеры заглавных букв на строчные, остальные символы не меняет.
Совместимость с Рефалом-5: полная.
<Mod e.ArithmArg> == '-'? s.NUMBER
Семантика: вычисляет остаток от деления. При делении на нуль — см. Div
.
Знак остатка совпадает со знаком делимого.
Совместимость с Рефалом-5: см. Add
.
<Mul e.ArithmArg> == '-'? s.Number? s.NUMBER
Семантика: вычисляет произведение двух чисел. Если ответ превышает 2³²−1, то ответ представляется в виде двух макроцифр X и Y, что следует трактовать как X × 2³² + Y.
Совместимость с Рефалом-5: см. Add
.
<Numb s.Sign? s.CHAR*> == '-'? s.NUMBER
Семантика. Если аргумент начинается с последовательности цифр,
то возвращается соответствующее число. В противном случае возвращается 0
.
В случае, если значение не вмещается в одну макроцифру, функция аварийно
завершается с сообщением integer overflow
.
Совместимость с Рефалом. В Рефале-5 поддерживаются длинная арифметика, поэтому результат может содержать несколько макроцифр. В Рефале-5 ни при каком аргументе функция аварийно не завершается.
<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).
Режимы:
'r'
, r
— открытие файла для чтения. Если файл не существует, программа
аварийно останавливается с выдачей соответствующего сообщения об ошибке.'w'
, w
— открытие файла для (пере)записи — если файл существует,
то усекается до нулевой длины, если файл не существует, то будет создан.'a'
, a
— открытие файла для дозаписи. Если файл существует, то запись
будет осуществляться в конец, если не существует — будет создан.rb
, wb
, ab
— двоичный ввод-вывод (зависит от платформы).Файл с номером 39 используется в LibraryEx
функциями LoadFile
и SaveFile
.
Совместимость с Рефалом-5. Функция полностью совместима.
<Ord e.AnyExpr> ::= AnyExprOrd
e.AnyExprOrd ::= t.AnyTermOrd*
t.AnyTermord ::= s.NUMBER | s.FUNCTION | (e.AnyExprOrd)
Семантика: заменяет в своём аргументе все литеры на их ASCII-коды.
Совместимость с Рефалом-5: полностью совместима.
<Print e.AnyExpr> == e.AnyExpr
Семантика. Распечатывает объектное выражение и возвращает свой аргумент.
Преобразование в цепочку литер осуществляет точно также, как и Prout
.
Совместимость с Рефалом-5: полностью совместима.
<Prout e.AnyExpr> == пусто
Семантика. Распечатывает объектное выражение. Литеры выводятся как есть,
числа выводятся в десятичном виде, для идентификаторов выводятся их имена,
структурные скобки распечатываются как (
и )
(при печати
неотличимы от '('
, ')'
). После чисел и идентификаторов добавляется пробел,
чтобы при выводе нескольких чисел или функций подряд их образы не слипались.
Совместимость с Рефалом-5: полностью совместима.
<Put s.FileNo e.Expr> == e.Expr
Семантика. Распечатывает объектное выражение в файл с указанным номером
и возвращает распечатанное выражение неизменным. В остальном эквивалента
Putout
.
Совместимость с Рефалом-5: полностью совместима.
<Putout s.FileNo e.Expr> == пусто
Семантика. Распечатывает объектное выражение в файл с указанным номером.
Преобразование в цепочку литер осуществляет точно также, как и Prout
. Номер
файла определяется по формуле:
file_no = s.FileNo % 40
Если файл с номером file_no
не открыт, то открывается файл с именем
REFAL<file_no>.DAT
, где <file_no>
— десятичная запись file_no
в режиме
(пере)записи (см. 'w'
у функции Open
).
Совместимость с Рефалом-5: полностью совместима.
<Rp e.Key '=' e.Value> == пусто
e.Key, e.Value ::= e.AnyExpr
Семантика: Заменяет в копилке значение, ассоциированное с данным ключом. Если значения не было, оно добавляется.
Совместимость с Рефалом-5: полная.
<Sub e.ArithmArg> == '-'? 1? s.NUMBER
Семантика: вычисляет разность двух чисел. Если первое число меньше второго,
возвращается литера '-'
и значением модуля их разности. Иначе просто
возвращается их разность. В случае, если аргументы разных знаков, возможно
арифметическое переполнение, в этом случае к ответу добавляется макроцифра 1
,
вторая макроцифра есть сумма абсолютных значений аргументов по модулю 2³².
Совместимость с Рефалом-5: см. Add
.
<Symb e.Sign s.NUMBER> == e.Sign s.CHAR+
e.Sign ::= '+' | '-' | пусто
Семантика. Преобразует число в его десятичную запись. Если числу
предшествовала литера '+'
или '-'
, та же литера будет предшествовать
и результату. Поддержка знака была добавлена для совместимости с функцией
System
.
Совместимость с Рефалом-5. Рефал-5 поддерживает длинную арифметику, поэтому
в аргументе может быть указано несколько чисел-макроцифр, таким образом,
функция Symb
Рефала-05 обрабатывает помножество области определения функции
Рефала-5.
<Time> == s.CHAR+
Семантика: возвращает текущее время в формате функции ctime()
языка Си
(однако, без знака \n
на конце результата).
Совместимость с Рефалом-5: полная.
<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'
. Остальные типы и подтипы:
'L'
— литера, латинская буква. 'Lu'
— заглавная, 'Ll'
— строчная.'D'
— литера, десятичная цифра. Подтип всегда '0'
.'W'
— функция. Подтип 'i'
, если имя функции может быть записано без
кавычек, 'q'
, если только в кавычках. Для подтипа 'q'
функция всегда
будет пустой.'N'
— число. Подтип всегда '0'
.'P'
— литера, печатный знак. Реализация повторяет семантику Рефала-5
PZ Oct 29 2004
: подтип 'u'
, если функция isupper()
вернула истину,
'l'
в противном случае. Для локали "C"
функция isupper()
возвращает
истину только для латинских букв, значит, для любого печатного знака, значит
подтип всегда будет 'l'
.'O'
— любая другая литера. Подтип — см. замечание к 'P'
.'B'
— скобочный терм. Подтип всегда '0'
.Работа функции зависит от установленной локали (которая по умолчанию вроде
"C"
), если локаль изменена (конфигурацией операционной системы или внешней функцией
на Си), то функция будет работать иначе. Лексический анализатор полагается
на эту функцию.
Совместимость с Рефалом-5: полная.
<Upper e.Expr> == e.Expr′
Семантика: заменяет в своём аргументе все литеры строчных букв на заглавные, остальные символы не меняет.
Совместимость с Рефалом-5: полная.
Метафункция Up
не реализована, при вызове аварийно завершает программу.
Добавлена для совместимости при раскрутке Рефалом-5.
Метафункция Ev-met
не реализована, при вызове аварийно завершает программу.
Добавлена для совместимости при раскрутке Рефалом-5.
В текущей реализации в точности эквивалентна Mu
— см. описание функции Mu
.
Доступен синтаксический сахар <? …>
.
Совместимость с Рефалом-5. В Рефале-5 различия между Mu
и Residue
возникают при вычислении в метакоде (в Ev-met
). В актуальной реализации
Ev-met
не поддерживается.
<GetEnv e.EnvName> == e.EnvValue
e.EnvName, e.EnvValue ::= s.CHAR*
Семантика: возвращает значение переменной среды с заданным именем. Если переменная среды не установлена, возвращает пустую строку.
Совместимость с Рефалом-5: полная.
<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: полная.
<Exit e.RetCode>
e.RetCode ::= '-'? s.NUMBER
Семантика. Завершает программу с заданным кодом возврата. Отрицательное
значение записывается как литера '-'
с последующим числом.
Совместимость с Рефалом-5: полная.
<Close s.FileNo> == пусто
Семантика. Закрывает открытый файл с номером s.FileNo % 40
. Если файл
с этим номером не был открыт, функция ничего не делает.
Совместимость с Рефалом-5: полная.
<ExistFile e.FileName> == True | False
e.FileName ::= s.CHAR*
Семантика. Функция пытается открыть файл с указанным именем для чтения
при помощи fopen()
. Если удаётся — закрывает открытый файл и возвращает
True
, в противном случае возвращает False
. Функции True
и False
не являются встроенными функциями, это значит, что их нужно явно подключать
при помощи $EXTERN
. Так сделано из соображений простоты и переносимости.
Совместимость с Рефалом-5. Рефал-5 действует честнее — использует средства
операционной системы, чтобы понять, существует файл или нет. А это значит,
что если файл существует, но недоступен для чтения, функции ExistFile
Рефала-5
и Рефала-05 увидят его по-разному.
<RemoveFile e.FileName> == True () | False (e.Message)
e.Message ::= s.CHAR*
Семантика: функция удаляет файл с заданным именем. При неудаче возвращает сообщение об ошибке, содержимое которого зависит от ОС и реализации языка Си.
Совместимость с Рефалом-5: вроде полная.
<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 такое расширение не поддерживает.
В остальном совместимость полная.
<Explode_Ext s.FUNCTION> == s.CHAR*
Семантика: функция в точности эквивалентна функции Explode
.
Совместимость с Рефалом-5: полная.
<TimeElapsed 0?> == s.CHAR+
Семантика. Функция возвращает число секунд, прошедших с момента
предыдущего вызова <TimeElapsed 0>
, либо с начала программы, если
<TimeElapsed>
ни разу не вызывалась. Число секунд возвращается в виде
десятичной записи дробного числа вида '12.345'
.
Совместимость с Рефалом-5. Полная.
<Compare e.ArithmArg> == '-' | '0' | '+'
Семантика. Возвращает '-'
, если первое число меньше второго, '0'
— если
они равны и '+'
, если первое больше второго. Иначе говоря, возвращает знак
разности этих двух чисел.
Совместимость с Рефалом-5. Рефал-5 поддерживает длинную арифметику, в том
числе и для функции Compare
(см. Add
).
<Random s.Len> == e.RandomDigits
e.RandomDigits ::= s.NUMBER+
|e.RandomDigits| == ((s.Len != 0) ? s.Len : 1)
Семантика: генерирует случайное количество (не более s.Len
) случайных
макроцифр.
Совместимость с Рефалом-5: полная.
<RandomDigit s.Max> == s.RandomDigit
s.RandomDigit, s.Max ::= s.NUMBER
s.RandomDigit <= s.Max
Семантика: генерирует случайное число от 0
до s.Max
.
Совместимость с Рефалом-5: полная.
<Write s.FileNo e.Expr> == пусто
Семантика: функция аналогична Putout
, только после вывода текста
не печатает '\n'
.
Совместимость с Рефалом-5: полная.
<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.
Рефал-05 проектировался как точное подмножество Рефала-5: любая корректная программа на Рефале-05 будет корректной программой на Рефале-5, причём выполняться она будет точно также, с несколькими исключениями:
XMLParse
, Рефалом-5 программа не сможет
скомпилироваться.Arg
в Рефале-5 пропускает аргументы, начинающиеся на дефис —
так передаются ключи, управляющие интерпретатором, в Рефале-05 функция
Arg
возвращает все аргументы.Возможно, в последующих версиях эти ограничения будут ослаблены или сняты.
Рефал-05 является подмножеством, а это значит, что существуют программы на Рефале-5, которые Рефалом-05 поддерживаться не будут (не будут компилироваться или корректно выполняться).
Как нужно писать программы на Рефале-5, чтобы они работали в Рефале-05:
Go
, то при компиляции Рефалом-05
следует подключать библиотечный файл Go.c
(в каталоге lib
репозитория).Mu
) должен подчиняться ограничениям,
описанным в подразделе после следующего.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…
).
Вообще, это подход можно развить до идиоматической реализации ООП (без наследования). Каждый объект — скобочный терм, начинающийся с символического имени. Функция символического имени является виртуальной таблицей — может обрабатывать различные сообщения. Методы эти сообщения посылают.
Выглядит это так. Фигуры «Прямоугольник» (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 не объектно-ориентированный язык 😉?
Mu
Рассуждения в этом разделе в равной степени относятся и к другим метафункциям,
поэтому далее будем говорить только о Mu
.
Напомним семантику вызова <Mu ‹символ-слово› …>
в обоих реализациях Рефала:
Mu
:
В случае, если косвенный вызов успешно разрешается на первом этапе в одной из двух реализаций, то он успешно разрешится и в другой. Рассмотрим подробнее:
Mu
. Соответственно,
в Рефале-05 этот вызов тоже должен разрешиться.Mu
. Если
она была определена, то разрешится вызов и в Рефале-5. Если была объявлена,
значит программа скомпилировалась (иначе получили бы ошибку на этапе
компоновки, ведь таблица функции Mu
содержит на неё ссылку), следовательно,
в программе присутствует entry-функция с этим именем и Рефал-5 разрешит
её вызов на втором этапе.А вот если дело дошло до второго этапа, то в Рефале-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).
Таким образом имена точек входа и имена вспомогательных функций заметно
различаются и тем самым минимизируется конфликт.