r7 - 28 Oct 2004 - 05:30:19 - OrloVYou are here: TWiki >  Refaldevel Web > CppBackend

C++ backend

Выражения

Надо написать.

Функции

Определение рефал-функции в C++-программе имеет следующий вид:

RF_FUNC (FName, args, ress)
    ...
RF_END

args — входные параметры (аргументы) функции, ress — результаты. На месте многоточия идёт тело функции.

Параметры args и ress имеют следующий синтаксис:

args ::= RF_VOID | (RF_ARG var_names;;)
ress ::= RF_VOID | (RF_RES var_names;;)
var_names ::= ident | ident, var_names

ident — C++-идентификатор.

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

Есть два способа означивания выходных переменных:

  1. Присваивание выходной переменной выражения:
    • res = expr;
  2. Вызов функции (см. #RefalFunctionCalls), которая запишет свой результат в данную выходную переменную. Например:
    • RF_CALL (F, /*void*/, res);

Никаких других действий с выходными переменными, кроме их означивания каким-либо из приведённых двух способов, выполнять нельзя.
? Разумно ли это ограничение? Может быть разрешить делать с переменными, уже получившими значения, всё то же, что можно делать с обычными выражениями?

Штатный возврат (return) из функции осуществляется либо по достижении выражения RF_END, либо если встретится выражение return true; К моменту осуществления штатного возврата все выходные переменные должны быть определены.

Функция завершается неуспехом (возвращает $fail), если встретилось выражение RF_RETFAIL; В этом случае выходные переменные не обязаны быть определёнными.

Примеры:

RF_FUNC (Left, (RF_ARG s_Left, s_Len, e_Exp;;), (RF_RES e_SubExp;;))
    /*
     * Отбросить от e_Exp первые s_Left термов, а затем от того
     * что останется взять первые s_Len термов и записать в
     * e_SubExp.
     * (Для простоты корректность аргументов не проверяется.)
     */
     e_SubExp = Expr(e_Exp, s_Left.cast_to<Integer>(),
                             s_Len.cast_to<Integer>());
RF_END

RF_FUNC (_eq_, (RF_ARG e_Exp1, e_Exp2;;), RF_VOID)
    /*
     * Если e_Exp1 не равно e_Exp2, то выдать $fail.
     */
    if (Expr::compare(e_Exp1, e_Exp2) != 0)
        RF_RETFAIL;
RF_END

Вызов рефал-функции в C++-программе имеет одну из следующих форм:

  1. RF_CALL (FName, args, ress);
  2. RF_TAILCALL (FName, args, ress);

Аргументы args и результаты ress имеют следующий синтаксис:

args   ::= params
ress   ::= params
params ::= /*empty*/ | expr | (exprs)
exprs  ::= expr | expr, exprs

Для аргументов expr может быть любым рефал-выражением.

Для результатов expr обязан быть

  • либо C++-идентификатором, соответствующим выражению, определённому к моменту вызова функции,
  • либо одним из результатов (выходных переменных) текущей функции, перечисленных в RF_RES (см. #RefalFunctionDefs).

Результат вычисления выражения RF_CALL (FName, args, ress) может быть привёден к типу bool.

  • Результат true означает, что вызываемая функция завершила работу успешно, и все переменные из ress получили новые значения.
  • Результат false означает, что работа функции завершилась неуспехом ($fail). В этом случае значения переменных из ress неопределены.

Пример. Следующий фрагмент кода корректен, при условии, что выражения s_Left, s_Len и e_Exp определены должным образом.

Expr e_SubExp;
RF_CALL (Left, (s_Left, s_Len, e_Exp), e_SubExp);
RF_CALL (Left, (s_Left, s_Len, e_SubExp + e_Exp), (e_SubExp));
RF_CALL (Left, ((s_Left, s_Len, e_Exp)), (((e_SubExp))));
if (RF_CALL (_eq_, (e_Exp, e_SubExp), /*void*/))
    printf ("s_Left = 0, s_Len = %u\n", e_Exp.get_len ());

Пример. Следующие фрагменты кода некорректны:

  • RF_CALL (_eq_, (e_Exp, e_Exp), ())
    params не может быть пустыми скобками;
  • RF_CALL (Left, s_Left, s_Len, e_Exp, e_SubExp)
    если аргументов больше одного, они должны быть заключены в скобки; RF_CALL и RF_TAILCALL всегда имеют ровно 3 параметра (второй и третий могут быть пустыми).

RF_TAILCALL отличается от RF_CALL тем, что осуществляется хвостовой вызов. Т.е. возврат из вызываемой функции происходит не в место вызова, а в место ближайшего (по стеку вызовов) RF_CALL (минуя все RF_TAILCALL).

В случае RF_TAILCALL выходной формат (определённый, с помощью RF_RES, см. #RefalFunctionDefs) вызываемой функции должен совпадать с выходным форматом текущей функции. Значения выходных переменных, полученные при хвостовом вызове, будут использованы как результаты текущей функции.

ToDo Надо либо поддержать возможность менять порядок и количество выходных переменных при хвостовых вызовах, либо вообще убрать ress из RF_TAILCALL. На данный момент они просто игнорируются.

Пример. Приведём полноценную функцию на Рефале+ и её вид на С++.

$func Perm (e) (e) = ;

Perm (e1) (e2) =
{
    e2 : \{
        ea tx eb,
            <Perm (e1 tx) (ea eb)>,
            $fail;
        /*empty*/ = <PrintLN e1>;
    };;
};

Вычисление выражения

<Perm () (expr)>
приведёт к выводу всех возможных перестановок термов из expr.

На С++ эта функция может быть изображена следующим образом:

RF_FUNC (Perm, (RF_ARG _ve_1, _ve_2;;), RF_VOID)
    lsplit (_ve_2, 0, _ve_a, _v_lsplit__2);
    for ( ; ; iter(_ve_2)++)
    {
        uintptr_t _v_len1 = _v_lsplit__2.get_len ();
        if (_v_len1 < 1)
            goto exit_loop;
        Expr _vt_x (_v_lsplit__2, 0, 1);
        Expr _ve_b (_v_lsplit__2, 1, _v_len1 - 1);
        RF_CALL (Perm, (_ve_1 + _vt_x, _ve_a + _ve_b), /*void*/);
    }
exit_loop:
    if (_ve_2.get_len () == 0)
        RF_TAILCALL (StdIO::PrintLN, _ve_1, /*void*/);
RF_END

Объекты

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

В C++ объекты представляются производными классами от базового класса rfrt::Object, который реализует подсчёт ссылок на объект и его удаление в случае, когда соответствующий символ-ссылка больше не входит ни в какие объектные выражения.
ALERT! Внимание, сборка мусора в циклических структурах, образованных за счёт (прямых или косвенных) ссылок на объект из его содержимого, в данный момент не поддержана!

Подробнее о создании классов, представляющих новые типы объектов, см. Cpp User Object HowTo? .

В Рефале любому объектному выражению соответствует некоторое печатное представление. Для того чтобы сделать печать объектов более осмысленной, чем просто вывод динамически сгенерированного символа-ссылки, в классе rfrt::Object определён виртуальный метод to_string(), с помощью которого можно задавать разные печатные представления для объектов разных типов.

По умолчанию печатное представление объекта выглядит так: <Object|addr>, где addr — адрес памяти, по которому располагается объект.

Стандартные объекты Рефала+ имеют следующие печатные представления (addr — адрес объекта):

  • Ящики: <Box|addr|content>, где content — печатное представление содержимого ящика.
    ALERT! Так сделано исключительно в порядке эксперимента. Содержимое может быть очень большим, и даже снова содержать ссылку на печатуемый ящик. Поэтому стандартное представление должно включать только адрес содержимого. А функция, аналогичная Print, но распечатывающая содержимое ящиков, пишется на Рефале в две строчки.
  • Таблицы: <Table|addr>.
  • Каналы: <Channel|addr|fileno>, где fileno — дескриптор файла, с которым связан канал, либо -1, если канал не связан ни с каким файлом.
  • Векторы: <Vector|addr|data_addr>, где data_addr — адрес, по которому располагается массив выражений (содержимое вектора).
  • Строки: строка является своим печатным представлением.

Особенности реализиций для различных платформ

Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r7 < r6 < r5 < r4 < r3 | More topic actions
 
R+

This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback