Макросы¶
В Ü есть механизм расширения синтаксиса программы, с использованием конструкций, определяемых программистом. Данный механизм реализуется через макросы.
Программист может объявить макрос, начинающийся с некоторого имени. При встрече этого имени в тексте программы синтаксический анализатор компилятора применит правила, заданные в макросе, для последующего разбора текста и произведёт замену на синтаксические конструкции, указанные в макросе.
Простейший пример:
?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;
// }
}