PDA

Просмотр полной версии : [Урок] Препроцессорные парсеры



VVWVV
10.12.2016, 05:59
В данной статье рассмотрим препроцессорные парсеры. Кроме того, в статье приведены большое количество примеров с пояснениями, каждая строка которых полностью аргументирована. Статья рассчитана на уже более-менее опытных программистов.

Введение


Препроцессорные парсеры – это конструкции из препроцессорных директив define, которые необходимы для определения отрывка кода по заданному шаблону. Подобные конструкции можно рассматривать, как более продвинутые манипуляции с компилятором.

Именно данные парсеры послужили основой для множества лучших библиотек. Например, всеми любимая библиотека foreach, которая образует новый псевдо-оператор foreach со своим синтаксисом и т.д.

Про алгоритмы, использованные в библиотеках, можно говорить очень долго, но суть всего этого одна – они анализируют код, отсеивают ненужные части, а потом переопределяют уже существующий код на этапе пре-компиляции. Это чем-то похоже на метапрограммирование.

Рассмотрим пример:


#define SOME_PARSE: _:SOME_MACRO_TAG_0:SOME_MACRO_TAG_1:
#define SOME_MACRO_TAG_0:SOME_MACRO_TAG_1:%1<%2>%0[%3] %1,%2,%3
#define SOME_MACRO_TAG_1:%1[%3] %1,cellmax,%3

main()
{
printf("%d %d %d %d", SOME_PARSE:5<8>[10]);
printf("%d %d %d %d", SOME_PARSE:5[10]);
}


Теперь рассмотрим каждую строчку более подробнее. Начнём.

Первая строчка необходима для упрощения вызова данного парсера.
Вторая строчка необходима для определения шаблона %1<%2>%0[%3] в строке. Заметим, что между символом ">" и "[" стоит нуль. Данный нуль необходим для определения пробелов между данными символами, т.е. для написания подобным образом:


SOME_PARSE:5 <8> [10]

Третья - альтернатива. То есть, если вторая строка не подходит по шаблону то значит, что альтернативная для этого только третья.


При использовании флага -l при компиляции, создаётся файл с расширение ".lst", который содержит результат работы препроцессора. Давайте рассмотрим результат выше приведенного кода:


printf("%d %d %d %d", _:5,8,10);
printf("%d %d %d %d", _:SOME_MACRO_TAG_0:5,cellmax,10);


Заметим то, что остался тег SOME_MACRO_TAG_0, но мы его обезвредили "нулевым" тегом, иначе была бы ошибка из-за несовпадения тегов. Таким образом, вы можем вместо символа "_" подставлять совершенно любые теги, однако главное - чтобы функция поддерживала данный тег.



Hash


Теперь давайте рассмотрим алгоритм хеширования, построенный полностью на этапе пре-компиляции. Однако большой минус такой реализации - разные регистры.


#define hash_alg(%0) h@(%0,END,END)(0)
#define h@(%0,%1)(%8) h@%0(%1)(%8)

#define h@END(%1)(%8) %8
#define h@a(%1)(%8) h@(%1)(%8 + 158)
#define h@b(%1)(%8) h@(%1)(%8 + 682)
#define h@c(%1)(%8) h@(%1)(%8 + 333)


Итак, давайте рассмотрим каждую строчку по отдельности:

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

#define h@b(%1)(%8) h@(%1)(%8 + 682)
Примечание: все цифры - 158, 682, 333 - придуманы, вместо них может стоять какой-либо алгоритм, либо цифра.

Теперь необходимо вызвать макро-функцию, например, в printf для вывода результата:


main()
{
printf("%d", hash_alg(a,b,c,c,b,a));
}


Результатом будет число 2346, т.к. 0 + 158 + 682 + 333 + 333 + 682 + 158 = 2346



Сложные алгоритмы


Отличие минимальной сложности от максимальной является более уникальный и сложный алгоритм, которые конструируется только для определённой библиотеки. Такие алгоритмы немного напоминают обфускацию (https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%84%D1%83%D1%81%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D0%BE%D0%B5_%D0%BE%D0%B1%D0%B5%D1%81%D0%BF%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5)), что сильно уменьшает читаемость кода.

Пример:


// Точка входа. <> необходим при последующего анализа кода.
#define dialog%1(%2) DPARSE(%1<>%2)

// Извлекаем содержимое из <>, если оно там присутствует.
#define DPARSE(%1<%3>%2) forward pdR_%1(playerid,response,listitem,inputtext[]);public pdR_%1(playerid,response,listitem,inputtext[]){return g@R(%3,%1,response)(%2)

// Тоже самое, что и первый пример в этой статье, т.е. это отсеивание. Разделитель "$", в других библиотеках может быть "|||" или "||||" и т.п.
#define g@R(%3,%1,%4)(%2) _:D@A0:D@A1:%1$%2$%3$%4$

// Если содержимое в <> отсутствует, значит переопределяем по данному алгоритму:
#define D@A0:D@A1:%1$%2$$%4$ _dR_%1(D@AN:%2[]$); } stock _dR_%1(%2)

// Иначе, если %2 содержит <>, то в пользовательском коде <...> присутствует. Кроме того, это сделано для отделения <> от аргументов.
#define D@A1:%1$<>%2$%3$%4$ (_:(%3)==%4) ? _dR_%1(D@AN:%2[]$) : 0; } stock _dR_%1(%2)

// Данные строки нужны для избавления от пробела. Если бы этого не было, то мы бы получили ошибку.
#define pdR_%0\32;%1(%2) pdR_%0%1(%2)
#define _dR_%0\32;%1(%2) _dR_%0%1(%2)

// Данная строка уничтожает [] в строке для избежания ошибок, т.к. при вызове функции ненужно указывать квадратные скобки.
#define D@AN:%2[%3]%0$) %2)


Данный код переопределяет псевдо-оператор dialog в компилируемый код. Давайте рассмотрим результат выполнения данного кода:



forward pdR_PlayerReg(playerid,response,listitem,inputtext[]);
public pdR_PlayerReg(playerid,response,listitem,inputtext[])
{
return _:D@A0:(_:(true)==response)?_dR_PlayerReg(playerid, inputtext):0;
}
stock _dR_PlayerReg(playerid, inputtext[])
{
printf("Your text: %s", inputtext);
}


Даже, не компилируя, можно определить работоспособность кода.



Итог


Препроцессорные парсеры позволяют сделать код более гибким, менее затратным на написание, а также более читаемым. Таким образом, можно создать собственный синтаксис для библиотек.



Примеры лучших работ



code-parse.inc (https://github.com/Y-Less/code-parse.inc) (Автор: Y_less)
YSI (https://github.com/Misiur/YSI-Includes) (Автор: Y_less)
md-sort.inc (https://github.com/oscar-broman/md-sort/blob/master/md-sort.inc) (Автор: Slice)
SmartCMD (https://github.com/YashasSamaga/SmartCMD) (Автор: YashasSamaga)



Если у вас появились какие-то вопросы, или вы думаете, что я что-то забыл, напишите об этом.


Автор: VVWVV
Исключительно для pro-pawn.ru


Копирование данной статьи на других ресурсах без разрешения автора запрещено!

DeimoS
10.12.2016, 12:01
Расписано всё хорошо, но не хватает словесных примеров того, для чего могут пригодиться препроцессорные парсеры. И, как подсказывает опыт, большая часть из скриптеров просто не поймёт твою статью =)

VVWVV
10.12.2016, 14:02
Расписано всё хорошо, но не хватает словесных примеров того, для чего могут пригодиться препроцессорные парсеры. И, как подсказывает опыт, большая часть из скриптеров просто не поймёт твою статью =)

Как минимум, я всего-лишь показал, что это существует, а если будут вопросы - отвечу. Словесные примеры будут. Кроме того, буду пополнять каждый раз тему примерами, а также проводить анализ.

VVWVV
12.12.2016, 19:56
Переписал статью, читайте.

Anve
01.02.2018, 16:18
Есть идеи о том, как можно использовать больше, чем 2 парсера? Поскольку тогда получается тэги накладываются друг на друга.

VVWVV
01.02.2018, 18:24
Есть идеи о том, как можно использовать больше, чем 2 парсера? Поскольку тогда получается тэги накладываются друг на друга.

Можно пример? Можно же сделать уникальные теги.

Anve
01.02.2018, 18:29
Можно пример? Можно же сделать уникальные теги.

Допустим

#define TEST: TEST_0:TEST_1:TEST_2:
#define TEST_2: // В этом случае будет 2 тэга: TEST_0:TEST_1:

VVWVV
01.02.2018, 19:00
Допустим

#define TEST: TEST_0:TEST_1:TEST_2:
#define TEST_2: // В этом случае будет 2 тэга: TEST_0:TEST_1:

Можно сделать вот так:


#define test(%1) (_:TEST_0:TEST_1:TEST_2:$%1)
#define TEST_0:%8$%1)
#define TEST_1:%8$%1)
#define TEST_2:%8$%1)

Anve
01.02.2018, 19:07
Как это использовать? Я для примера сделал так

test(1)
И в листинге было следующее:

(_:

VVWVV
01.02.2018, 19:10
Как это использовать? Я для примера сделал так

test(1)
И в листинге было следующее:

(_:

Пример.

#define TEST_0:%8$%1[%2]) %8$%1+%2)

Anve
01.02.2018, 19:29
Хорошо, понял, но что на счёт создания переменных?

VVWVV
01.02.2018, 20:24
Хорошо, понял, но что на счёт создания переменных?

При чём тут переменные..

Anve
01.02.2018, 20:35
При чём тут переменные..

Просто я хотел с помощью парсеров создавать разные переменные.

VVWVV
01.02.2018, 20:39
Просто я хотел с помощью парсеров создавать разные переменные.

Можно. Обычное объявление переменных.

Anve
01.02.2018, 20:54
Можно. Обычное объявление переменных.

Я в курсе, я про то, как можно использовать больше 2-х парсеров для создания переменных, т.к. способ выше выдаёт ошибку(что логично).

VVWVV
01.02.2018, 21:03
Я в курсе, я про то, как можно использовать больше 2-х парсеров для создания переменных, т.к. способ выше выдаёт ошибку(что логично).

Никак.

pawnoholic
16.06.2018, 19:35
Подскажите как эту мощь интегрировать в мод, что то приглянулся мне такой вариант именования переменных, я вставлял это, как есть, в один из заголовочных файлов, но тут либо где то допущена ошибка, либо чего то еще не хватает, потому что скомпилировать мод не удается.

By Y-Less:

#define OO_TYPE(%6,%7)%0[%1]%2. (_:@vb:@va:%6:%7@%0[%1]%2[E_%6_%0:@vw:@vx:@vy:@vz:@vs:$E_%6_%0_]

// First move the ] out as far as required.
#define @vw:%9$%0]%1[ @vv:@vu:@vt:@vr:@vq$%0%1][
#define @vx:%9$%0]%1; @vv:@vu:@vt:@vr:@vq$%0%1];
#define @vy:%9$%0]%1) @vv:@vu:@vt:@vr:@vq$%0%1])
#define @vz:%9$%0]%1, @vv:@vu:@vt:@vr:@vq$%0%1],
#define @vs:%9$%0]%1: @vv:@vu:@vt:@vr:@vq$%0%1]:

// Second, move it in again to constrain the contents. We don't need to scan
// for `[` again since it was the first one looked for above and so can't
// possibly be within the square brackets. This is good, because scanning for
// `[` from within `[]` is problematic.

#define @vv:%9$%0;%1] %9$%0];%1
#define @vu:%9$%0)%1] %9$%0])%1
#define @vt:%9$%0,%1] %9$%0],%1
#define @vr:%9$%0:%1] %9$%0]:%1

#define @vq$

#define Player. OO_TYPE(PLAYER,Player)
#define Vehicle. OO_TYPE(VEHICLE,Vehicle)

Player.Vehicle[playerid].pos;more
Player.Vehicle[playerid].pos[0];more
Player.Vehicle[playerid].pos,more
Player.Vehicle[playerid].pos[0],more
Player.Vehicle[playerid].pos[0]more
Player.Vehicle[playerid].pos:more
Player.Vehicle[playerid].pos[0]:more

Player.Vehicle[playerid][vehicleid].pos;more
Player.Vehicle[playerid][vehicleid].pos[0];more
Player.Vehicle[playerid][vehicleid].pos,more
Player.Vehicle[playerid][vehicleid].pos[0],more
Player.Vehicle[playerid][vehicleid].pos[0]more
Player.Vehicle[playerid][vehicleid].pos:more
Player.Vehicle[playerid][vehicleid].pos[0]:more


Извините, что не в ту тему, думал тут про препроцессорные парсеры :)

VVWVV
16.06.2018, 19:42
Подскажите как эту мощь интегрировать в мод, что то приглянулся мне такой вариант именования переменных, я вставлял это, как есть, в один из заголовочных файлов, но тут либо где то допущена ошибка, либо чего то еще не хватает, потому что скомпилировать мод не удается.

By Y-Less:

#define OO_TYPE(%6,%7)%0[%1]%2. (_:@vb:@va:%6:%7@%0[%1]%2[E_%6_%0:@vw:@vx:@vy:@vz:@vs:$E_%6_%0_]

// First move the ] out as far as required.
#define @vw:%9$%0]%1[ @vv:@vu:@vt:@vr:@vq$%0%1][
#define @vx:%9$%0]%1; @vv:@vu:@vt:@vr:@vq$%0%1];
#define @vy:%9$%0]%1) @vv:@vu:@vt:@vr:@vq$%0%1])
#define @vz:%9$%0]%1, @vv:@vu:@vt:@vr:@vq$%0%1],
#define @vs:%9$%0]%1: @vv:@vu:@vt:@vr:@vq$%0%1]:

// Second, move it in again to constrain the contents. We don't need to scan
// for `[` again since it was the first one looked for above and so can't
// possibly be within the square brackets. This is good, because scanning for
// `[` from within `[]` is problematic.

#define @vv:%9$%0;%1] %9$%0];%1
#define @vu:%9$%0)%1] %9$%0])%1
#define @vt:%9$%0,%1] %9$%0],%1
#define @vr:%9$%0:%1] %9$%0]:%1

#define @vq$

#define Player. OO_TYPE(PLAYER,Player)
#define Vehicle. OO_TYPE(VEHICLE,Vehicle)

Player.Vehicle[playerid].pos;more
Player.Vehicle[playerid].pos[0];more
Player.Vehicle[playerid].pos,more
Player.Vehicle[playerid].pos[0],more
Player.Vehicle[playerid].pos[0]more
Player.Vehicle[playerid].pos:more
Player.Vehicle[playerid].pos[0]:more

Player.Vehicle[playerid][vehicleid].pos;more
Player.Vehicle[playerid][vehicleid].pos[0];more
Player.Vehicle[playerid][vehicleid].pos,more
Player.Vehicle[playerid][vehicleid].pos[0],more
Player.Vehicle[playerid][vehicleid].pos[0]more
Player.Vehicle[playerid][vehicleid].pos:more
Player.Vehicle[playerid][vehicleid].pos[0]:more


Покажите код полностью, ибо макросы правильные. Возможно, что проблема в объявлении массива.

pawnoholic
16.06.2018, 21:55
Покажите код полностью, ибо макросы правильные. Возможно, что проблема в объявлении массива.

Если мне не изменяет память, то:

enum E_PLAYER_Vehicle
{
Float:E_PLAYER_Vehicle_x,
Float:E_PLAYER_Vehicle_y,
Float:E_PLAYER_Vehicle_z
};
new PlayerVehicle[MAX_PLAYERS_VEHICLES][E_PLAYER_Vehicle];

Собственно, от куда все это произошло: ссылка (https://github.com/Y-samp/Y-Core/commit/ebf825bea72620acc77a2114c9fc715ce6175bc4#comments)

VVWVV
16.06.2018, 23:47
Если мне не изменяет память, то:

enum E_PLAYER_Vehicle
{
Float:E_PLAYER_Vehicle_x,
Float:E_PLAYER_Vehicle_y,
Float:E_PLAYER_Vehicle_z
};
new PlayerVehicle[MAX_PLAYERS_VEHICLES][E_PLAYER_Vehicle];

Собственно, от куда все это произошло: ссылка (https://github.com/Y-samp/Y-Core/commit/ebf825bea72620acc77a2114c9fc715ce6175bc4#comments)

Вот как это объявлено тут (https://github.com/Y-samp/Y-Core/blob/ebf825bea72620acc77a2114c9fc715ce6175bc4/gamemodes/Y/player/vehicle/header.inc#L14-L21).

pawnoholic
16.06.2018, 23:54
Вот как это объявлено тут (https://github.com/Y-samp/Y-Core/blob/ebf825bea72620acc77a2114c9fc715ce6175bc4/gamemodes/Y/player/vehicle/header.inc#L14-L21).

Я знаю, я видел это, но там другой вариант (https://github.com/Y-samp/Y-Core/blob/aca42da9a604dccc394b7dc7fe331b4198a7946e/gamemodes/Y/core/header.inc#L10-L12), более к классическому стилю.

Daniel_Cortez
17.06.2018, 18:07
Извините, что не в ту тему, думал тут про препроцессорные парсеры :)
Ничего страшного, переместил обсуждение в нужную тему.

pawnoholic
20.06.2018, 00:49
Пришло время снова попробовать.

Код:
#define Player. OO_TYPE(PLAYER,Player)

enum E_PLAYER_Vehicle
{
Float:E_PLAYER_Vehicle_x,
Float:E_PLAYER_Vehicle_y,
Float:E_PLAYER_Vehicle_z,
Float:E_PLAYER_Vehicle_a
};
new Player@Vehicle[MAX_PLAYERS][E_PLAYER_Vehicle];

main() {
Player.Vehicle[0].x = 10.0;
}
Результат:
(error) must be lvalue (non-constant)
(error) invalid expression, assumed zero
(warning) expression has no effect
Я думаю проблема скорее в отсутствии макросов для @vb: и @va:

Я их нашел, но не думаю что подходят к этому варианту, либо Y-Less забыл про них, либо они не нужны и я что то не так делаю.
#define OO_TYPE(%6,%7)%0[%1] (_:@vb:@va:%6:%7@%0[%1]
#define @va:%6:%7@%0[%1][@%2] %7%0[%1])[E_%6_%0_%2]
#define @vb:@va:%6:%7@%0[%1][%2][@%3] %7%0[%1])[%2][E_%6_%0_%3]

DeimoS
20.06.2018, 06:58
Скинь весь код, что у тебя получился (и макросы, и то, как ты пытаешься их использовать)

pawnoholic
20.06.2018, 13:46
// by Y-Less
#define OO_TYPE(%6,%7)%0[%1]%2. (_:@vb:@va:%6:%7@%0[%1]%2[E_%6_%0:@vw:@vx:@vy:@vz:@vs:$E_%6_%0_]
#define @va:%6:%7@%0[%1][@%2] %7%0[%1])[E_%6_%0_%2]
#define @vb:@va:%6:%7@%0[%1][%2][@%3] %7%0[%1])[%2][E_%6_%0_%3]

// First move the ] out as far as required.
#define @vw:%9$%0]%1[ @vv:@vu:@vt:@vr:@vq$%0%1][
#define @vx:%9$%0]%1; @vv:@vu:@vt:@vr:@vq$%0%1];
#define @vy:%9$%0]%1) @vv:@vu:@vt:@vr:@vq$%0%1])
#define @vz:%9$%0]%1, @vv:@vu:@vt:@vr:@vq$%0%1],
#define @vs:%9$%0]%1: @vv:@vu:@vt:@vr:@vq$%0%1]:

// Second, move it in again to constrain the contents. We don't need to scan
// for `[` again since it was the first one looked for above and so can't
// possibly be within the square brackets. This is good, because scanning for
// `[` from within `[]` is problematic.

#define @vv:%9$%0;%1] %9$%0];%1
#define @vu:%9$%0)%1] %9$%0])%1
#define @vt:%9$%0,%1] %9$%0],%1
#define @vr:%9$%0:%1] %9$%0]:%1

#define @vq$

#define Player. OO_TYPE(PLAYER,Player)

enum E_PLAYER_Vehicle
{
Float:E_PLAYER_Vehicle_x,
Float:E_PLAYER_Vehicle_y,
Float:E_PLAYER_Vehicle_z,
Float:E_PLAYER_Vehicle_a
};
new PlayerVehicle[MAX_PLAYERS][E_PLAYER_Vehicle];

public OnPlayerConnect(playerid)
{
Player.Vehicle[playerid].x = 0.0;
Player.Vehicle[playerid].y = 0.0;
Player.Vehicle[playerid].z = 0.0;
Player.Vehicle[playerid].a = 0.0;

return 1;
}

VVWVV
20.06.2018, 17:43
1) Массив должен быть назван так: Player@Vehicle.
2) Данный парсер не рассчитан на какие-либо операции, поскольку нет таковых условий, поэтому знак равно, да и в общем-то любые знаки записываются в квадратные скобки.

Вот результат работы препроцессора:

enum E_PLAYER_Vehicle
{
Float:E_PLAYER_Vehicle_x,
Float:E_PLAYER_Vehicle_y,
Float:E_PLAYER_Vehicle_z,
Float:E_PLAYER_Vehicle_a
};
new Player@Vehicle[MAX_PLAYERS][E_PLAYER_Vehicle];

public OnPlayerConnect(playerid)
{
(_:@vb:@va:PLAYER:Player@Vehicle[playerid][E_PLAYER_Vehicle:@vw:@vv:@vu:@vt:@vr:E_PLAYER_Vehicle_x = 0.0];
(_:@vb:@va:PLAYER:Player@Vehicle[playerid][E_PLAYER_Vehicle:@vw:@vv:@vu:@vt:@vr:E_PLAYER_Vehicle_y = 0.0];
(_:@vb:@va:PLAYER:Player@Vehicle[playerid][E_PLAYER_Vehicle:@vw:@vv:@vu:@vt:@vr:E_PLAYER_Vehicle_z = 0.0];
(_:@vb:@va:PLAYER:Player@Vehicle[playerid][E_PLAYER_Vehicle:@vw:@vv:@vu:@vt:@vr:E_PLAYER_Vehicle_a = 0.0];

return 1;
}

pawnoholic
15.07.2018, 22:09
Уже очень много времени не могу понять по какому принципу пишутся препроцессорные парсеры, подскажите как можно реализовать вот такой вызов этого макроса:
SendClientMessage(playerid, COLOR(RED,500), "Добро пожаловать!"); // Сейчас
SendClientMessage(playerid, COLOR(RED, 500), "Добро пожаловать!"); // Не возможно поставить пробел между аргументами
SendClientMessage(playerid, COLOR(RED), "Добро пожаловать!"); // Вызов функции без второго аргумента, который по умолчанию заменяется на COLOR@RED@500

Код:
#define COLOR@RED@100<%1,%2> (%1FFCDD2%2)
#define COLOR@RED@200<%1,%2> (%1EF9A9A%2)
#define COLOR@RED@300<%1,%2> (%1E57373%2)
#define COLOR@RED@400<%1,%2> (%1EF5350%2)
#define COLOR@RED@500<%1,%2> (%1F44336%2)
#define COLOR@RED@600<%1,%2> (%1E53935%2)
#define COLOR@RED@700<%1,%2> (%1D32F2F%2)
#define COLOR@RED@800<%1,%2> (%1C62828%2)
#define COLOR@RED@900<%1,%2> (%1B71C1C%2)

#define COLOR(%0,%1) (COLOR@%0@%1<0x,AA>)

VVWVV
15.07.2018, 22:24
Уже очень много времени не могу понять по какому принципу пишутся препроцессорные парсеры, подскажите как можно реализовать вот такой вызов этого макроса:
SendClientMessage(playerid, COLOR(RED,500), "Добро пожаловать!"); // Сейчас
SendClientMessage(playerid, COLOR(RED, 500), "Добро пожаловать!"); // Не возможно поставить пробел между аргументами
SendClientMessage(playerid, COLOR(RED), "Добро пожаловать!"); // Вызов функции без второго аргумента, который по умолчанию заменяется на COLOR@RED@500

Код:
#define COLOR@RED@100<%1,%2> (%1FFCDD2%2)
#define COLOR@RED@200<%1,%2> (%1EF9A9A%2)
#define COLOR@RED@300<%1,%2> (%1E57373%2)
#define COLOR@RED@400<%1,%2> (%1EF5350%2)
#define COLOR@RED@500<%1,%2> (%1F44336%2)
#define COLOR@RED@600<%1,%2> (%1E53935%2)
#define COLOR@RED@700<%1,%2> (%1D32F2F%2)
#define COLOR@RED@800<%1,%2> (%1C62828%2)
#define COLOR@RED@900<%1,%2> (%1B71C1C%2)

#define COLOR(%0,%1) (COLOR@%0@%1<0x,AA>)

Делаем две проверки на символ ','


#define COLOR@RED@100<%1,%2> (%1FFCDD2%2)
#define COLOR@RED@200<%1,%2> (%1EF9A9A%2)
#define COLOR@RED@300<%1,%2> (%1E57373%2)
#define COLOR@RED@400<%1,%2> (%1EF5350%2)
#define COLOR@RED@500<%1,%2> (%1F44336%2)
#define COLOR@RED@600<%1,%2> (%1E53935%2)
#define COLOR@RED@700<%1,%2> (%1D32F2F%2)
#define COLOR@RED@800<%1,%2> (%1C62828%2)
#define COLOR@RED@900<%1,%2> (%1B71C1C%2)

#define COLOR(%1) (_:clr@aM:clr@aN:$%1)
#define clr@aM:%8$%0,%1) clr@rS:COLOR@%0@%1<0x,AA>)
#define clr@aN:%8$%0) clr@rS:COLOR@%0@100<0x,AA>) // 100 - default value.
#define clr@rS:%1\32;%0) clr@rS:%1%0)

COLOR(RED, 800)

pawnoholic
15.07.2018, 23:11
SendClientMessage(playerid, COLOR(RED), "Добро пожаловать!");
undefined symbol "@COLOR@RED"
invalid expression, assumed zero
array must be indexed (variable "-unknown-")
too many error messages on one line

VVWVV
15.07.2018, 23:42
http://ihost.pro-pawn.ru/image.php?di=SMF6

pawnoholic
16.07.2018, 00:07
new color = COLOR(RED);
SendClientMessage(playerid, color, "Добро пожаловать!");
Да, таким методом компиляция проходит нормально.

SendClientMessage(playerid, COLOR(RED), "Добро пожаловать!");
А если вызывать вот так, то компиляция проходит с ошибкой

-l:
SendClientMessage(playerid, (_:clr@rS:@COLOR@RED)@"Добро пожаловать!"<0x,AA>);

VVWVV
16.07.2018, 00:44
new color = COLOR(RED);
SendClientMessage(playerid, color, "Добро пожаловать!");
Да, таким методом компиляция проходит нормально.

SendClientMessage(playerid, COLOR(RED), "Добро пожаловать!");
А если вызывать вот так, то компиляция проходит с ошибкой

-l:
SendClientMessage(playerid, (_:clr@rS:@COLOR@RED)@"Добро пожаловать!"<0x,AA>);


Опс.. Да...

Попробуй так:

#define COLOR@RED@100<%1,%2> (%1FFCDD2%2)
#define COLOR@RED@200<%1,%2> (%1EF9A9A%2)
#define COLOR@RED@300<%1,%2> (%1E57373%2)
#define COLOR@RED@400<%1,%2> (%1EF5350%2)
#define COLOR@RED@500<%1,%2> (%1F44336%2)
#define COLOR@RED@600<%1,%2> (%1E53935%2)
#define COLOR@RED@700<%1,%2> (%1D32F2F%2)
#define COLOR@RED@800<%1,%2> (%1C62828%2)
#define COLOR@RED@900<%1,%2> (%1B71C1C%2)

#define COLOR(%1) (_:clr@aM:clr@aN:$%1,)
#define clr@aM:%8$%0,%1,) clr@rS:COLOR@%0@%1<0x,AA>)
#define clr@aN:%8$%0,) clr@rS:COLOR@%0@100<0x,AA>) // 100 - default value.
#define clr@rS:%1\32;%0,) clr@rS:%1%0,)

COLOR(RED, 800)

pawnoholic
16.07.2018, 00:54
#define COLOR(%1) (_:clr@aM:clr@aN:$%1,)
#define clr@aM:%8$%0,%1,) clr@rS:COLOR@%0@%1<0x,AA>)
#define clr@aN:%8$%0,) clr@rS:COLOR@%0@100<0x,AA>) // 100 - default value.
#define clr@rS:%1\32;%0,) clr@rS:%1%0,)


Спасибо, это работает.

pawnoholic
16.07.2018, 02:48
Решил немного модифицировать и добавить третий аргумент, но теперь перед вторым и третьим параметром не убирается пробел, пробовал самостоятельно разрешить этот пробел, но так и не понял как это сделать:

#define COLOR(%1) (_:COLOR@A:COLOR@B:COLOR@C:$%1,)
#define COLOR@A:%8$%0,%1,%2,) COLOR@S:@COLOR@%0@%1<0x,%2>)
#define COLOR@B:%8$%0,%1,) COLOR@S:@COLOR@%0@%1<0x,AA>)
#define COLOR@C:%8$%0,) COLOR@S:@COLOR@%0@500<0x,AA>)
#define COLOR@S:%1\32;%0,) COLOR@S:%1%0,)

Результат:
(_:COLOR@S:@COLOR@RED@ 500<0x, AA>)

Необходимо:
(_:COLOR@S:@COLOR@RED@500<0x,AA>) -> (_:COLOR@S:(0xF44336AA))

VVWVV
16.07.2018, 03:06
Решил немного модифицировать и добавить третий аргумент, но теперь перед вторым и третьим параметром не убирается пробел, пробовал самостоятельно разрешить этот пробел, но так и не понял как это сделать:

#define COLOR(%1) (_:COLOR@A:COLOR@B:COLOR@C:$%1,)
#define COLOR@A:%8$%0,%1,%2,) COLOR@S:@COLOR@%0@%1<0x,%2>)
#define COLOR@B:%8$%0,%1,) COLOR@S:@COLOR@%0@%1<0x,AA>)
#define COLOR@C:%8$%0,) COLOR@S:@COLOR@%0@500<0x,AA>)
#define COLOR@S:%1\32;%0,) COLOR@S:%1%0,)

Результат:
(_:COLOR@S:@COLOR@RED@ 500<0x, AA>)

Необходимо:
(_:COLOR@S:@COLOR@RED@500<0x,AA>) -> (_:COLOR@S:(0xF44336AA))

%8$ забыли в COLOR@S.

pawnoholic
16.07.2018, 10:52
%8$ забыли в COLOR@S.

Как то без изменений, пробовал вот так
#define COLOR@S:%8$%1\32;%0,) COLOR@S:%1%0,)
И вот так
#define COLOR@S:%1\32;%8$%0,) COLOR@S:%1%0,)

VVWVV
16.07.2018, 16:39
Как то без изменений, пробовал вот так
#define COLOR@S:%8$%1\32;%0,) COLOR@S:%1%0,)
И вот так
#define COLOR@S:%1\32;%8$%0,) COLOR@S:%1%0,)


#define COLOR(%1) (_:COLOR@A:COLOR@B:COLOR@C:$%1,)
#define COLOR@A:%8$%0,%1,%2,) COLOR@S:@COLOR@%0@%1<0x,%2>)
#define COLOR@B:%8$%0,%1,) COLOR@S:@COLOR@%0@%1<0x,AA>)
#define COLOR@C:%8$%0,) COLOR@S:@COLOR@%0@500<0x,AA>)
#define COLOR@S:%1\32;%0,%6) COLOR@S:%1%0,%6)

pawnoholic
16.07.2018, 16:58
Спасибо, только еще пробел остался в таком варианте использования.
new color = (_:COLOR@S:(0xF44336 AA));
А можно еще как то модифицировать, так чтобы можно было и такой вариант вызвать или для такого варианта лучше новое правило сделать?
#define @COLOR@BLACK<%1,%2> (%1000000%2)
#define @COLOR@WHITE<%1,%2> (%1FFFFFF%2)

DeimoS
17.09.2019, 16:48
Допустим, есть макрос такого вида
#define SomeFunc1(%0,%1)
"%0" и "%1" - целочисленные переменные. Возможно ли убрать все пробелы между запятой и "%1", не добавляя, при этом, никаких символов или тегов?

В целом, задача заключается в том, чтоб переместить "%1" в начало
#define SomeFunc1(%0,%1) %1 = SomeFunc2(%0)
И если прописать в коде, например, так:
SomeFunc1(var1, var2);
То компилятор ругнётся на кривую табуляцию, так как "var2" перенесётся в начало вместе с пробелом. Собственно, от этого предупреждения и нужно избавиться.

vvw
17.09.2019, 17:19
Допустим, есть макрос такого вида
#define SomeFunc1(%0,%1)
"%0" и "%1" - целочисленные переменные. Возможно ли убрать все пробелы между запятой и "%1", не добавляя, при этом, никаких символов или тегов?

В целом, задача заключается в том, чтоб переместить "%1" в начало
#define SomeFunc1(%0,%1) %1 = SomeFunc2(%0)
И если прописать в коде, например, так:
SomeFunc1(var1, var2);
То компилятор ругнётся на кривую табуляцию, так как "var2" перенесётся в начало вместе с пробелом. Собственно, от этого предупреждения и нужно избавиться.

Почему бы не обернуть это в скобки (do - while)?

#define foo(%0,%1) (%1=Bar(%0))
//#define foo(%0,%1) do{%1=Bar(%0)}while(0)

DeimoS
17.09.2019, 23:14
Почему бы не обернуть это в скобки (do - while)?

#define foo(%0,%1) (%1=Bar(%0))
//#define foo(%0,%1) do{%1=Bar(%0)}while(0)


Точно, скобки... Совсем не подумал про них. Так и думал, что правильная реализация окажется совсем простой и очевидной. А я тут пытался чуть ли не в 10 строк алгоритмы писать ради избавления от этих пробелов. Спасибо.

Если что, просто решил сделать для себя парсер, который бы упрощал перевод с MySQL R-39 на MySQL R-40 и наоборот. А то с некоторыми заказчиками периодически бывают проблемы в плане определения нужной версии MySQL и либо приходилось один и тот же код дублировать, либо переписывать под нужную версию.

MassonNN
17.10.2020, 16:57
Хороший урок. Есть один вопрос. Я хочу найти в коде (довольно объемном коде с огромным количеством библиотек и т.д.) удаление конкретного объекта, удаляется он соответственно:
RemoveBuildingForPlayer(playerid, 17513
по этому примеру, я хочу с помощью макроса изменить данный код на какую - нибудь лабуду и искусственно вызвать ошибки для нахождения этой строчки во всех файлах одновременно. Но есть проблема: в данном макросе есть пробел, который я не знаю как учитывать. Просто подставить %0 не очень работает, так как дальше идут цифры, а если пробел оставить, то под дефайн попадут ВСЕ RemoveBuildingForPlayer(playerid,
а не только те, которые мне нужны.

DeimoS
17.10.2020, 19:28
Хороший урок. Есть один вопрос. Я хочу найти в коде (довольно объемном коде с огромным количеством библиотек и т.д.) удаление конкретного объекта, удаляется он соответственно:
RemoveBuildingForPlayer(playerid, 17513
по этому примеру, я хочу с помощью макроса изменить данный код на какую - нибудь лабуду и искусственно вызвать ошибки для нахождения этой строчки во всех файлах одновременно. Но есть проблема: в данном макросе есть пробел, который я не знаю как учитывать. Просто подставить %0 не очень работает, так как дальше идут цифры, а если пробел оставить, то под дефайн попадут ВСЕ RemoveBuildingForPlayer(playerid,
а не только те, которые мне нужны.

Качаешь Sublime Text 3 (можешь попробовать другой редактор, но там регулярки могут работать иначе), перетаскиваешь папку с модом в текущий проект (https://i.imgur.com/AvmpP43.png), жмёшь на папку правой кнопкой и выбираешь "Find & Replace".

Далее в открывшемся снизу меню жмёшь на кнопку " .* " и в поле "Find" добавляешь
RemoveBuildingForPlayer\s*\((\s*[a-zA-Z0-9_]+)\s*,\s*17513

Собственно, дальше можешь либо нажать на "Find" и получить все совпадения, либо можешь даже какие-то махинации с этим сделать, дописав в "Replace" текст, на который его нужно заменить.
Вот тут (https://sublimetext.ru/documentation/find-and-replace) можешь подробнее почитать про этот функционал. Собственно, если сейчас в "Replace" указать "\1", то на месте этих двух символов появится тот текст, что в RemoveBuildingForPlayer будет указан в качестве ID игрока ("playerid" и т.п.)