Макросы¶
В Ü есть механизм расширения синтаксиса программы, с использованием конструкций, определяемых программистом. Данный механизм реализуется через макросы.
Программист может объявить макрос, начинающийся с некоторого имени. При встрече этого имени в тексте программы синтаксический анализатор компилятора применит правила, заданные в макросе, для последующего разбора текста и произведёт замену на синтаксические конструкции, указанные в макросе.
Простейший пример:
?macro <? Double:expr ( ?e:expr ) ?>  ->  <? ?e * 2 ?>
var i32 x= Double(10);
В данном примере объявляется макрос с именем Double, который будет состоять из выражения в круглых скобках.
Данный макрос раскроется в выражение, умноженное на 2.
Объявление макросов¶
Макросы указываются в начале файла исходного кода, после списка импортируемых модулей, но до любого другого кода. Макросы импортируются из импортируемых модулей. Они глобальны - доступны везде в коде и расположены логически в общем пространстве имён (с разделением по контекстам).
Итак, из чего же состоят макросы?
Объявление макроса начинается с идентификатора ?macro, за которым следует пара скобок <? и ?>.
Первым элементов в этих скобках должно следовать имя макроса и его контекст после :.
Далее следует набор правил сопоставления макроса.
После следует блок правил раскрытия макроса в <? ?> скобках, следующий после лексемы ->.
Контексты использования макросов¶
У макроса обязательно указать контекст, в котором он будет использоваться. Существуют следующие контексты:
- namespace- контекст внутри пространств имён (в том числе в корневом пространстве имён).
- class- контекст внутри структуры или класса.
- block- контекст внутри блока.
- expr- контекст внутри выражения.
Все контексты макросов независимы друг от друга. В разных контекстах возможно объявления макросов с одним именем. Внутри же одного контекста объявление нескольких макросов с одним и тем же именем не допускается.
Элементы блока сопоставления¶
Самый простой элемент - это регулярные лексемы. Они включают в себя идентификаторы, различные скобки, символы операторов и т. д. Сопоставляются они напрямую.
Остальные элементы являются именованными сущностями.
Начинаются они с символа `?` и последующего идентификатора.
После идёт :, после которого указывается класс элемента.
Внутри одного блока сопоставления не допускается существование элементов с одинаковыми именами.
Но допускается использование элементов с одинаковыми именами в разных (в том числе вложенных) блоках сопоставления.
Элементы бывают простые и составные. Простые элементы включают в себя:
- ident- одиночный идентификатор.
- ty- имя типа.
- expr- выражение.
- block- блок.
Составные элементы это опциональные элементы и последовательности.
Опциональные элементы имеют класс opt.
После объявления опционального элемента идёт набор его вложенных элементов в <? ?> скобках.
Элементы последовательностей имеют класс rep.
После объявления элемента последовательности идёт набор его вложенных элементов в <? ?> скобках.
Также может быть указан опциональный разделитель элементов последовательности - одиночная лексема в <? ?> скобках.
Опциональные элементы и последовательности требуют чтобы в начале списка их внутренних элементов была указана фиксированная лексема, или же чтобы после них была указана фиксированная лексема. Это необходимо, чтобы при обработке сопоставления выбрать, перейти ли к элементу после опционального/последовательности или же сделать выборку опционального элемента/элемента последовательности. Фиксированная лексема начала опционального элемента/последователньости не должна совпадать с фиксированной лексемой после опционального/последовательности.
Примеры элементов:
?macro <? Skip:expr ?some_ident:ident ?>  ->  <? ?> // идентификатор
?macro <? Skip:namespace ?some_type:ty ?>  ->  <? ?> // имя типа
?macro <? Skip:expr ?some_expression:expr ?>  ->  <? ?> // выражение
?macro <? Skip:block ?some_block:block ?>  ->  <? ?> // блок
?macro <? Skip:expr ?some_opt:opt<? ?i:ident ?> ?>  ->  <? ?> // опциональный идентификатор
?macro <? Skip:expr ?some_opt:opt<? ( ?b:block ) ?> ?>  ->  <? ?> // опциональный блок в круглых скобках
?macro <? Skip:expr ( ?some_sequence:rep<? ?e:expr ?> ) ?>  ->  <? ?> // последовательность выражений в круглых скобках
?macro <? Skip:expr START ?some_sequence:rep<? ?e:expr ?><?,?> END ?>  ->  <? ?> // последовательность из нуля или более выражений, разделённых запятой, расположенная внутри пары слов START/END
Раскрытие макросов¶
Макросы раскрываются в соответствии с правилами, указанными в блоке раскрытия объявления макроса.
В этом блоке могут встречаться фиксированные лексемы, которые будут раскрыты напрямую.
Также в блоке могут встречаться макро-переменные, объявленные в блоке сопоставления.
Начинаются эти макро-переменные с символа ?.
При раскрытии макроса они заменяются аргументами, переданными в макрос.
Простые элементы раскрываются напрямую.
Составные элементы раскрываются вложенно.
Для них надо указывать макро-блок в <? ?> скобках, в которых уже указывать правила раскрытия внутренних элементов составных элементов.
Внутренний блок опционального элемента раскрывается в случае, если был передан входной опциональный элемент.
Внутренний блок последовательности раскрывается столько раз, сколько раз элемент полседовательности был передан в макрос. При этом для каждого раскрытия внутренние переменные блока раскрытия имеют свои значения, соотвествующие входным значениям.
Примеры раскрытия:
?macro <? DECLARE_VAR:namespace ?name:ident ?init:expr ?t:ty ?>  ->  <? var ?t ?name = ?init; ?>
DECLARE_VAR pi 3.14f f32
// Раскроется в
var i32 pi = 3.14f;
?macro <? DECLARE_VAR:namespace ?name:ident ?init:expr ?t:ty ?m:opt<?MUT?> ?>  ->  <? var ?t ?m<?mut?> ?name = ?init; ?>
DECLARE_VAR pi 3.14f f32
// Раскроется в
var i32 f32 = 3.14f;
DECLARE_VAR x 0 i32 MUT
// Раскроется в
var i32 mut pi = 0;
?macro <? DECLARE_FOO:namespace ( ?params:rep<? ?t:ty ?name:ident ?><?,?> ) ?>  ->  <? fn Foo( ?params<? ?t ?name ?><?,?> ); ?>
DECLARE_FOO()
// Раскроется в
fn Foo()
DECLARE_FOO(i32 x, f32 y)
// Раскроется в
fn Foo(i32 x, f32 y)
Уникальные макро-идентификаторы¶
В блоке раскрытия макроса можно указывать уникальные макро-идентификаторы, начинающиеся с ??.
Данные идентификаторы при раскрытии будут заменены на идентикфикаторы, уникальные в рамках данного раскрытия макроса и гарантированно не пересекающиеся с именами других идентификаторов.
Данные уникальные макро-идентификаторы позволяют производить раскрытие макроса без опасения совпадения имён, объявленных в раскрытии макроса, с именами, объявленными в других местах.
Пример:
?macro <? FOR:block ?count:expr ?b:block ?>  ->
<?
{
        var size_type mut ??counter= 0s;
        while( ??counter < size_type(?count) )
        {
            ?b
            ++??counter;
        }
}
?>
fn Foo();
fn Bar()
{
    FOR 32
    {
        var i32 counter= 0;
        Foo();
    }
    // Макрос будет раскрыт во что-то вроде
    // var size_type mut ??counter= 0s;
    // while( _macro_ident_counter_140734899778672_0 < size_type(32) )
    // {
    //     {
    //         var i32 counter= 0; // Пересечения имён не будет
    //         Foo();
    //     }
    //     ++_macro_ident_counter_140734899778672_0;
    // }
}
