Косвенное обращение к макрофункции

Я пытаюсь написать макрос для своего рода библиотеки форматирования. Идея состоит в том, что вы можете использовать сочетание строковых литералов, обычных функций и (здесь я застрял) выведенных/ограниченных функций. Я подозреваю, что я мог бы заставить это работать с макросами proc, но это сложнее, и я бы хотел, чтобы код макроса был проще для чтения, если это возможно.

Пример

Вот урезанная версия кода, который я пишу.

enum TemplateEntry<T> {
    Text(String),
    Typed(T, String),
}

enum ColorType {
    Red(String),
    Blue(String),
}

impl ColorType {
    pub fn red(s: &str) -> Self {
        Self::Red(s.to_string())
    
    pub fn blue(s: &str) -> Self {
        Self::Blue(s.to_string())
    }
}

impl Into<TemplateEntry<ColorType>> for ColorType {
    fn into(&self) -> TemplateEntry<Self> {
        match self {
            Self::Blue(text) => TemplateEntry::Typed(self.clone(), text.clone()),
            Self::Red(text) => TemplateEntry::Typed(self.clone(), text.clone()),
        }
    }
}

macro_rules! markup_helper {
    ($temp_vec:ident,  $type_id:tt, @ $x:expr) => {{
        $temp_vec.push($type_id::$x);
    }};
    ( $temp_vec:ident, $type_id:tt, $x:expr) => {{
        $temp_vec.push($x.into());
    }};
}

#[macro_export]
macro_rules! markup {
    ( $type_id:tt, $( $($opt:literal)? $rest:expr ),* ) => {
        {
            let mut temp_vec: Vec<TemplateEntry<$type_id>> = Vec::new();
            $(
                markup_helper!(temp_vec, $type_id, $($opt)? $rest);
            )*
            temp_vec
        }
    };
}

fn example_call() {
    let _example = markup!(ColorType, "regular text", @blue("other text"), "more regular text", regular_func_returning_text());
    // What I want it to expand to
    let _example = {
        let mut temp_vec: Vec<TemplateEntry<ColorType>> = Vec::new();
        temp_vec.push("regular text".into());
        temp_vec.push(ColorType::blue("other text"));
        temp_vec.push("more regular text".into());
        temp_vec.push(regular_func_returning_text().into());
        temp_vec
    };
}

Поиск @ не работает должным образом, поскольку компилятор не может определить, являются ли строковые литералы необязательными литералами или выражением. Кроме того, похоже, он не пересылает @ помощнику, если я пытаюсь проанализировать его с помощью рекурсивно определенного макроса.

Переформулированные вопросы

Есть ли способ использовать use, чтобы попытаться найти связанные функции базового типа, возможно, без индикатора @?

Есть ли что-то очевидное, чего мне не хватает для того, чтобы заставить работать @ или любой другой удобный символ индикатора, который позволил бы работать такому макросу?

Мне не ясно, чего вы хотите/ожидаете, что вызов макроса расширится?

eggyal 09.06.2024 21:08

Основная проблема в том, что вы не сопоставляете @, blue или круглые скобки в @blue("other text"), также неясно, что вы хотите, чтобы это значило.

cafce25 09.06.2024 23:00

Я обновил вопрос, указав, во что я хочу расширить макрос. Я ожидаю, что $rest:expr будет соответствовать части blue("other text"), которая кажется при написании менее сложных макросов для экспериментов.

quittle 10.06.2024 03:05

Пожалуйста, отредактируйте свой вопрос, включив в него несколько примеров входных данных и то, чего вы ожидаете от них.

PitaJ 10.06.2024 06:22
@blue не является буквальным. Это символ @, за которым следует ident, поэтому его следует анализировать как $(@ $opt:ident)?
Jmb 10.06.2024 09:23

То, что вы указали как желаемое расширение макроса, не скомпилируется по ряду причин. Я бы попросил вас сначала это исправить, чтобы мы могли быть уверены, что понимаем, чего вы хотите. Однако, возможно, что-то вроде этого — это то, что вам нужно?

eggyal 11.06.2024 11:00

Пример @eggyal делает то, что я хочу. Спасибо, что собрали все вместе и проработали мой плохо объясненный вопрос. Синтаксис макроса кажется слишком сложным, поэтому я попробую сократить его и добавить в него ответ. Если вы захотите разместить это сами, я буду рад это принять.

quittle 12.06.2024 02:15

Одним из упрощений может быть изменение правила @ так, чтобы оно только расширяло этот символ: см. пример. В противном случае очень рад, что вы упростите дальше по своему усмотрению и опубликуете в качестве ответа.

eggyal 12.06.2024 06:08
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
8
109
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Благодаря @eggyal, вот окончательная версия, к которой я пришел, и она почти полностью соответствует тому, что они предоставили, только что переработанному с учетом моих личных предпочтений.

macro_rules! markup_internal {
    // Expand leading `@` into `$type_id::`
    (($temp_vec:ident:$type_id:ident): [@ $($rest:tt)*]) => {
        markup_internal!(($temp_vec:$type_id): [$type_id::$($rest)*])
    };

    // Handle all other entries
    (($temp_vec:ident:$type_id:ident): [$expr:expr$(, $($rest:tt)*)?]) => {
        // Actually append the entries
        $temp_vec.push($expr.into());
        // Recursively expand
        $(markup_internal!(($temp_vec:$type_id): [$($rest)*]))?
    };
    
    // Supports trailing commas
    (($temp_vec:ident:$type_id:ident): []) => {}
}

#[macro_export]
macro_rules! markup {
    // set up the array and push each list item into it by recursively invoking this macro
    ($type_id:ident: [$($rest:tt)*]) => {{
        let mut temp_vec = Vec::<TemplateEntry<$type_id>>::new();
        markup_internal!((temp_vec:$type_id): [$($rest)*]);
        temp_vec
    }};
}

Другие вопросы по теме