Макроси

6 декември 2018

Макроси

Макроси

Макроси

try!

Това вече сме го виждали

1 2 3 4 5 6 7 8
macro_rules! try {
    ($expr:expr) => {
        match $expr {
            Ok(value) => value,
            Err(e) => return Err(e.into()),
        }
    }
}
#![allow(unused_macros)]
fn main () {}
macro_rules! try {
    ($expr:expr) => {
        match $expr {
            Ok(value) => value,
            Err(e) => return Err(e.into()),
        }
    }
}

add!

Общата схема

1 2 3 4 5 6 7 8 9 10
macro_rules! add {
    ($var1:expr, $var2:expr) => {
        $var1 + $var2;
    }
}

fn main() {
    println!("{}", add!(1, 1));
    println!("{}", add!("foo".to_string(), "bar"));
}
2 foobar
macro_rules! add {
    ($var1:expr, $var2:expr) => {
        $var1 + $var2;
    }
}

fn main() {
    println!("{}", add!(1, 1));
    println!("{}", add!("foo".to_string(), "bar"));
}

add!

Общата схема

add!

Общата схема

add!

Общата схема

add!

Общата схема

add!

Общата схема

add!

Общата схема

add!

Защо не "променливи"? Защото в кръглите скоби се прави pattern-matching на ниво token-и:

1 2 3 4 5 6 7 8 9 10
macro_rules! add {
    (Чш, я събери ($var1:expr) и ($var2:expr)) => {
        $var1 + $var2;
    }
}

fn main() {
    println!("{}", add!(Чш, я събери (1) и (1)));
    println!("{}", add!(Чш, я събери ("foo".to_string()) и ("bar")));
}
2 foobar
macro_rules! add {
    (Чш, я събери ($var1:expr) и ($var2:expr)) => {
        $var1 + $var2;
    }
}

fn main() {
    println!("{}", add!(Чш, я събери (1) и (1)));
    println!("{}", add!(Чш, я събери ("foo".to_string()) и ("bar")));
}

add!

Защо има скоби? За да се знае къде свършва expression/израз.

1 2 3 4 5
macro_rules! add {
    (Чш, я събери $var1:expr и $var2:expr) => {
        $var1 + $var2;
    }
}
error: `$var1:expr` is followed by `и`, which is not allowed for `expr` fragments --> /main_be9c85d3e58c64ae0ab6a9978ece02a78cdf0563.rs:5:30 | 5 | (Чш, я събери $var1:expr и $var2:expr) => { | ^ not allowed after `expr` fragments | = note: allowed there are: `=>`, `,` or `;`
#![allow(unused_macros)]
fn main () {}
macro_rules! add {
    (Чш, я събери $var1:expr и $var2:expr) => {
        $var1 + $var2;
    }
}

add!

Непосредствено след expr са позволени само (=>), (,) и (;), ако expr не е в скоби

1 2 3 4 5 6 7 8 9 10
macro_rules! add {
    (Чш, я събери $var1:expr, $var2:expr) => {
        $var1 + $var2;
    }
}

fn main() {
    println!("{}", add!(Чш, я събери 1, 1));
    println!("{}", add!(Чш, я събери "foo".to_string(), "bar"));
}
2 foobar
macro_rules! add {
    (Чш, я събери $var1:expr, $var2:expr) => {
        $var1 + $var2;
    }
}

fn main() {
    println!("{}", add!(Чш, я събери 1, 1));
    println!("{}", add!(Чш, я събери "foo".to_string(), "bar"));
}

map!

Нещо малко по-практично

1 2 3 4 5 6 7 8 9 10 11 12
macro_rules! map {
    {
        $( $key: expr => $value: expr ),*
    } => {
        // Забележете блока
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    }
}

map!

Какво прави $( ... ),* ?

map!

Какво прави $( ... ),* ?

map!

Какво прави $( ... ),* ?

map!

Какво прави $( ... ),* ?

map!

Какво прави $( ... ),* ?

map!

Какво прави $( ... ),* ?

map!

Какво прави $( ... ),* ?

map!

Какво прави $( ... ),* ?

map!

Ок, нека да компилираме

1 2 3 4 5 6 7 8 9 10 11
macro_rules! map {
    {
        $( $key: expr : $value: expr ),*
    } => {
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    }
}

map!

Ок, нека да компилираме

1 2 3 4 5 6 7 8 9 10 11
macro_rules! map {
    {
        $( $key: expr : $value: expr ),*
    } => {
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    }
}
error: `$key:expr` is followed by `:`, which is not allowed for `expr` fragments --> /main_48aecfc760527ed5f683faae57a6895a4507076a.rs:6:23 | 6 | $( $key: expr : $value: expr ),* | ^ not allowed after `expr` fragments | = note: allowed there are: `=>`, `,` or `;`
#![allow(unused_macros)]
fn main() {}
macro_rules! map {
    {
        $( $key: expr : $value: expr ),*
    } => {
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    }
}

map!

Правилата са си правила.. Ще ги разгледаме подробно по-късно

1 2 3 4 5 6 7 8 9 10 11
macro_rules! map {
    {
        $( $key: expr => $value: expr ),*
    } => {
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    }
}
#![allow(unused_macros)]
fn main() {}
macro_rules! map {
    {
        $( $key: expr => $value: expr ),*
    } => {
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    }
}

map!

1 2 3 4 5 6
let m = map! {
    "a" => 1,
    "b" => 2
};

println!("{:?}", m);
{"b": 2, "a": 1}
macro_rules! map {
{
$( $key: expr => $value: expr ),*
} => {
{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $value); )*
map
}
}
}
fn main() {
let m = map! {
    "a" => 1,
    "b" => 2
};

println!("{:?}", m);
}

map!

А какво става, ако искаме да поддържаме trailing comma 🤔

1 2 3 4 5 6
let m = map! {
    "a" => 1,
    "b" => 2,
};

println!("{:?}", m);

map!

А какво става, ако искаме да поддържаме trailing comma 🤔

1 2 3 4 5 6
let m = map! {
    "a" => 1,
    "b" => 2,
};

println!("{:?}", m);
error: unexpected end of macro invocation --> /main_5c32cc6affa79f7fd4a16843f01c100a2bb3f65e.rs:15:13 | 15 | "b" => 2, | ^
macro_rules! map {
{
$( $key: expr => $value: expr ),*
} => {
{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $value); )*
map
}
}
}
fn main() {
let m = map! {
    "a" => 1,
    "b" => 2,
};

println!("{:?}", m);
}

map!

Не точно каквото очаквахме..

map!

Може би така?

1 2 3 4 5 6 7 8 9 10 11 12
macro_rules! map {
    {
        $( $key: expr => $value: expr ),*,
    } => {
        /* ... */
    }
}

let m = map! {
    "a" => 1,
    "b" => 2
};

map!

Може би така?

1 2 3 4 5 6 7 8 9 10 11 12
macro_rules! map {
    {
        $( $key: expr => $value: expr ),*,
    } => {
        /* ... */
    }
}

let m = map! {
    "a" => 1,
    "b" => 2
};
error: unexpected end of macro invocation --> /main_f40bbb578c57a886c2648a112fa47f0c587a7594.rs:28:12 | 28 | "b" => 2 | ^
macro_rules! map {
{
$( $key: expr => $value: expr ),*
} => {
{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $value); )*
map
}
}
}
macro_rules! map {
    {
        $( $key: expr => $value: expr ),*,
    } => {
        /* ... */
{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $value); )*
map
}
    }
}

fn main() {
let m = map! {
    "a" => 1,
    "b" => 2
};
}

map!

Не..

map!

Не бойте се, има си трик за това

1 2 3 4 5 6 7
macro_rules! map {
    {
        $( $key: expr => $value: expr ),* $(,)*
    } => {
        /* ... */
    }
}

map!

Недостатъка е, че може да match-нем нещо такова. Ще покажем по-късно и друг начин

1 2 3 4
let m = map! {
    "a" => 1,
    "b" => 2,,,,,,,,,,,,
};
macro_rules! map {
{
$( $key: expr => $value: expr ),* $(,)*
} => {
{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $value); )*
map
}
}
}
fn main() {
let m = map! {
    "a" => 1,
    "b" => 2,,,,,,,,,,,,
};
}

map!

Guess macro_rules! ¯\_(ツ)_/¯

macros 2.0

В момента макросите са в процес на преработка към macros 2.0, където ще има ?

Хигиена

Макросите в Rust са хигиенични

1 2 3 4 5
macro_rules! five_times {
    ($x:expr) => (5 * $x);
}

println!("{}", five_times!(2 + 3));

Хигиена

Макросите в Rust са хигиенични

1 2 3 4 5
macro_rules! five_times {
    ($x:expr) => (5 * $x);
}

println!("{}", five_times!(2 + 3));
25
macro_rules! five_times {
    ($x:expr) => (5 * $x);
}

fn main() {
println!("{}", five_times!(2 + 3));
}

Хигиена

Макросите в Rust са хигиенични

1 2 3 4 5
macro_rules! five_times {
    ($x:expr) => (5 * $x);
}

println!("{}", five_times!(2 + 3));
25
macro_rules! five_times {
    ($x:expr) => (5 * $x);
}

fn main() {
println!("{}", five_times!(2 + 3));
}

Нещо подобно в C/C++ би изчислило 13

Хигиена

В този пример отново заради хигиена двата state-а не се shadow-ват взаимно

1 2 3 4 5 6 7 8 9 10 11
macro_rules! log {
    ($msg:expr) => {{
        let state: i32 = get_log_state();
        if state > 0 {
            println!("log({}): {}", state, $msg);
        }
    }};
}

let state: &str = "reticulating splines";
log!(state);
log(1): reticulating splines
fn get_log_state() -> i32 { 1 }
macro_rules! log {
    ($msg:expr) => {{
        let state: i32 = get_log_state();
        if state > 0 {
            println!("log({}): {}", state, $msg);
        }
    }};
}

fn main() {
let state: &str = "reticulating splines";
log!(state);
}

Хигиена

Всяко разгъване на макрос се случва в различен синтактичен контекст.
В този случай може да го мислите все едно двете променливи имат различен цвят който ги разграничава.

Хигиена

По тази причина не може да представяме нови променливи чрез макрос по следния начин

1 2 3 4 5 6
macro_rules! foo {
    () => (let x = 3;);
}

foo!();
println!("{}", x);
error[E0425]: cannot find value `x` in this scope --> /main_f77bccafcffc077ab7e68cffcdade87e2b6d1674.rs:7:16 | 7 | println!("{}", x); | ^ not found in this scope
macro_rules! foo {
    () => (let x = 3;);
}

fn main() {
foo!();
println!("{}", x);
}

Хигиена

Ще трябва да подадем името на променлива на макроса за да се получи

1 2 3 4 5 6
macro_rules! foo {
    ($v:ident) => (let $v = 3;);
}

foo!(x);
println!("{}", x);
3
macro_rules! foo {
    ($v:ident) => (let $v = 3;);
}

fn main() {
foo!(x);
println!("{}", x);
}

Хигиена

Правило важи за let и цикли като loop while for, но не и за item-и, което значи, че следното ще се компилира

1 2 3 4 5 6
macro_rules! foo {
    () => (fn x() { println!("macros!") });
}

foo!();
x();
macros!
macro_rules! foo {
    () => (fn x() { println!("macros!") });
}

fn main() {
foo!();
x();
}

Синтаксис

Извикване на макроси

Макросите следват същите правила както останалата част от синтаксиса на Rust

Синтаксис

Извикване на макроси

Макросите следват същите правила както останалата част от синтаксиса на Rust

Синтаксис

Извикване на макроси

Макросите следват същите правила както останалата част от синтаксиса на Rust

Синтаксис

Извикване на макроси

Макросите следват същите правила както останалата част от синтаксиса на Rust

Синтаксис

Синтаксис

Синтаксис

Синтаксис

Синтаксис

Формално извикването на макрос се състои от поредица от token trees които са

Синтаксис

Формално извикването на макрос се състои от поредица от token trees които са

Синтаксис

Формално извикването на макрос се състои от поредица от token trees които са

Синтаксис

Затова Rust макросите винаги приоритизират затварянето на скобите пред match-ването, което е полезно при някои подходи за match-ване

Синтаксис

Metavariables & Fragment specifiers

Tиповете на метапроменливите са

Синтаксис

Metavariables & Fragment specifiers

Tиповете на метапроменливите са

Синтаксис

Metavariables & Fragment specifiers

Ограниченията за типовете са

Ръкави

Макросите могат да имат повече от един ръкав за matching разделени с ;

1 2 3 4 5
macro_rules! my_macro {
    ($e: expr) => (...);
    ($i: ident) => (...);
    (for $i: ident in $e: expr) => (...);
}

Ръкави

Има и конвенция за private ръкави @text, които да се викат чрез рекурсия

1 2 3 4 5
macro_rules! my_macro {
    (for $i: ident in $e: expr) => (...);
    (@private1 $e: expr) => (...);
    (@private2 $i: ident) => (...);
}

Рекурсия

Макросите могат да извикват други макроси и дори себе си както този прост html shorthand

1 2 3 4 5 6 7 8 9 10 11 12
macro_rules! write_html {
    ($w: expr, ) => (());

    ($w: expr, $e: tt) => (write!($w, "{}", $e)?);

    ($w: expr, $tag: ident [ $( $inner: tt )* ] $( $rest: tt )*) => {{
        write!($w, "<{}>", stringify!($tag))?;
        write_html!($w, $($inner)*);
        write!($w, "</{}>", stringify!($tag))?;
        write_html!($w, $($rest)*);
    }};
}
#![allow(unused_macros)]
fn main() {}
macro_rules! write_html {
    ($w: expr, ) => (());

    ($w: expr, $e: tt) => (write!($w, "{}", $e)?);

    ($w: expr, $tag: ident [ $( $inner: tt )* ] $( $rest: tt )*) => {{
        write!($w, "<{}>", stringify!($tag))?;
        write_html!($w, $($inner)*);
        write!($w, "", stringify!($tag))?;
        write_html!($w, $($rest)*);
    }};
}

Рекурсия

1 2 3 4 5 6 7 8 9 10 11 12 13
use std::fmt::Write;

let mut out = String::new();

write_html! {
    &mut out,
    html[
        head[title["Macros guide"]]
        body[h1["Macros are the best!"]]
    ]
}

println!("{}", out);
<html><head><title>Macros guide</title></head><body><h1>Macros are the best!</h1></body></html>
macro_rules! write_html {
($w: expr, ) => (());
($w: expr, $e: tt) => (write!($w, "{}", $e)?);
($w: expr, $tag: ident [ $( $inner: tt )* ] $( $rest: tt )*) => {{
write!($w, "<{}>", stringify!($tag))?;
write_html!($w, $($inner)*);
write!($w, "", stringify!($tag))?;
write_html!($w, $($rest)*);
}};
}
fn main() -> Result<(), ::std::fmt::Error> {
use std::fmt::Write;

let mut out = String::new();

write_html! {
    &mut out,
    html[
        head[title["Macros guide"]]
        body[h1["Macros are the best!"]]
    ]
}

println!("{}", out);
Ok(())
}

Рекурсия

Нека направим онзи хак за trailing comma по-малко хак с тези познания

1 2 3 4 5 6 7 8 9 10 11 12 13
macro_rules! map {
    { $( $key: expr => $value: expr ),*, } => {
        map!( $( $key => $value ),* );
    };

    { $( $key: expr => $value: expr ),* } => {
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    };
}
#![allow(unused_macros)]
fn main() {}
macro_rules! map {
    { $( $key: expr => $value: expr ),*, } => {
        map!( $( $key => $value ),* );
    };

    { $( $key: expr => $value: expr ),* } => {
        {
            let mut map = ::std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    };
}

Scoping

Компилатора разгъва макросите в ранна фаза на компилация, затова имат специфична видимост

Scoping

Компилатора разгъва макросите в ранна фаза на компилация, затова имат специфична видимост

Scoping

Компилатора разгъва макросите в ранна фаза на компилация, затова имат специфична видимост

Scoping

Компилатора разгъва макросите в ранна фаза на компилация, затова имат специфична видимост

Scoping

Имаме макроси дефинирани в macros и ще ги използваме в client

1 2 3
#[macro_use]
mod macros;
mod client; // ок

1 2 3
mod client; // компилационна грешка
#[macro_use]
mod macros;

Scoping

Имаме макроси дефинирани в macros и ще ги използваме в client

1 2 3 4 5 6 7 8
// crate macros

mod some_module {
    #[macro_export]
    macro_rules! hello {
        () => (println!("Hello!"))
    }
}
1 2 3 4 5 6 7 8
// crate client

#[macro_use]
extern crate macros;

fn main() {
    hello!();
}

Scoping

От Rust 1.30 може и ето така

1 2 3 4 5 6 7 8
// crate macros

mod some_module {
    #[macro_export]
    macro_rules! hello {
        () => (println!("Hello!"))
    }
}
1 2 3 4 5 6 7 8 9 10
// crate client

extern crate macros;

// notice top-level use
use macros::hello;

fn main() {
    hello!();
}

Scoping

Макроси дефинирани в блокове, функции или други подобни конструкции са видими само там

1 2 3
fn main() {
    macro_rules! map { ... }
}

Scoping

При работа на ниво crate

Scoping

При работа на ниво crate

Scoping

При работа на ниво crate

Scoping

При работа на ниво crate

Debugging

Дебъгването на макроси е сложно, но има някои полезни команди

Debugging

Дебъгването на макроси е сложно, но има някои полезни команди

Debugging

Дебъгването на макроси е сложно, но има някои полезни команди

Debugging

Дебъгването на макроси е сложно, но има някои полезни команди

Debugging

Има и удобни, но нестабилни макроси, които се ползват през feature gate на nightly

Debugging

Има и удобни, но нестабилни макроси, които се ползват през feature gate на nightly

Debugging

Има и удобни, но нестабилни макроси, които се ползват през feature gate на nightly

Debugging

Има и удобни, но нестабилни макроси, които се ползват през feature gate на nightly

Стандартни макроси

Стандартни макроси

Стандартни макроси

Стандартни макроси

Стандартни макроси

Advanced

TT Muncher

1 2 3 4 5 6 7 8 9 10 11 12
macro_rules! write_html {
    ($w: expr, ) => (());

    ($w: expr, $e: tt) => (write!($w, "{}", $e)?);

    ($w: expr, $tag: ident [ $( $inner: tt )* ] $( $rest: tt )*) => {{
        write!($w, "<{}>", stringify!($tag))?;
        write_html!($w, $($inner)*);
        write!($w, "</{}>", stringify!($tag))?;
        write_html!($w, $($rest)*);
    }};
}
#![allow(unused_macros)]
fn main() {}
macro_rules! write_html {
    ($w: expr, ) => (());

    ($w: expr, $e: tt) => (write!($w, "{}", $e)?);

    ($w: expr, $tag: ident [ $( $inner: tt )* ] $( $rest: tt )*) => {{
        write!($w, "<{}>", stringify!($tag))?;
        write_html!($w, $($inner)*);
        write!($w, "", stringify!($tag))?;
        write_html!($w, $($rest)*);
    }};
}

Advanced

TT Muncher

1 2 3 4 5 6 7
write_html! {
    &mut out,
    html[
        head[title["Macros guide"]]
        body[h1["Macros are the best!"]]
    ]
}

Advanced

Push-Down Accumulation

Макрос, който инизиализира масив до 3 елемента

1 2 3 4 5 6 7 8 9 10 11 12 13 14
macro_rules! init_array {
    [$e:expr; $n:tt] => {{
        let e = $e;
        init_array!(@accum ($n, e.clone()) -> ())
    }};
    (@accum (3, $e:expr) -> ($($body:tt)*)) => { init_array!(@accum (2, $e) -> ($($body)* $e,)) };
    (@accum (2, $e:expr) -> ($($body:tt)*)) => { init_array!(@accum (1, $e) -> ($($body)* $e,)) };
    (@accum (1, $e:expr) -> ($($body:tt)*)) => { init_array!(@accum (0, $e) -> ($($body)* $e,)) };
    (@accum (0, $_e:expr) -> ($($body:tt)*)) => { init_array!(@as_expr [$($body)*]) };
    (@as_expr $e:expr) => { $e };
}

let strings: [String; 3] = init_array![String::from("hi!"); 3];
println!("{:?}", strings);
["hi!", "hi!", "hi!"]
macro_rules! init_array {
    [$e:expr; $n:tt] => {{
        let e = $e;
        init_array!(@accum ($n, e.clone()) -> ())
    }};
    (@accum (3, $e:expr) -> ($($body:tt)*)) => { init_array!(@accum (2, $e) -> ($($body)* $e,)) };
    (@accum (2, $e:expr) -> ($($body:tt)*)) => { init_array!(@accum (1, $e) -> ($($body)* $e,)) };
    (@accum (1, $e:expr) -> ($($body:tt)*)) => { init_array!(@accum (0, $e) -> ($($body)* $e,)) };
    (@accum (0, $_e:expr) -> ($($body:tt)*)) => { init_array!(@as_expr [$($body)*]) };
    (@as_expr $e:expr) => { $e };
}

fn main() {
let strings: [String; 3] = init_array![String::from("hi!"); 3];
println!("{:?}", strings);
}

Advanced

Push-Down Accumulation

А не може ли да опростим нещата до това?

1 2 3 4 5 6 7 8 9 10 11 12
macro_rules! init_array {
    (@accum 0, $_e:expr) => {/* empty */};
    (@accum 1, $e:expr) => {$e};
    (@accum 2, $e:expr) => {$e, init_array!(@accum 1, $e)};
    (@accum 3, $e:expr) => {$e, init_array!(@accum 2, $e)};
    [$e:expr; $n:tt] => {
        {
            let e = $e;
            [ init_array!(@accum $n, e) ]
        }
    };
}

Advanced

Push-Down Accumulation

Не…

1 2 3 4 5 6 7 8 9 10 11 12 13 14
macro_rules! init_array {
    (@accum 0, $_e:expr) => {/* empty */};
    (@accum 1, $e:expr) => {$e};
    (@accum 2, $e:expr) => {$e, init_array!(@accum 1, $e)};
    (@accum 3, $e:expr) => {$e, init_array!(@accum 2, $e)};
    [$e:expr; $n:tt] => {
        {
            let e = $e;
            [ init_array!(@accum $n, e) ]
        }
    };
}

let strings: [String; 3] = init_array![String::from("hi!"); 3];
error: macro expansion ignores token `,` and any following --> /main_d7893d0d02408938f95722ad3842b5c665d42a1e.rs:5:31 | 5 | (@accum 3, $e:expr) => {$e, init_array!(@accum 2, $e)}; | ^ | note: caused by the macro expansion here; the usage of `init_array!` is likely invalid in expression context --> /main_d7893d0d02408938f95722ad3842b5c665d42a1e.rs:9:15 | 9 | [ init_array!(@accum $n, e) ] | ^^^^^^^^^^^^^^^^^^^^^^^^^ ... 15| let strings: [String; 3] = init_array![String::from("hi!"); 3]; | ----------------------------------- in this macro invocation
macro_rules! init_array {
    (@accum 0, $_e:expr) => {/* empty */};
    (@accum 1, $e:expr) => {$e};
    (@accum 2, $e:expr) => {$e, init_array!(@accum 1, $e)};
    (@accum 3, $e:expr) => {$e, init_array!(@accum 2, $e)};
    [$e:expr; $n:tt] => {
        {
            let e = $e;
            [ init_array!(@accum $n, e) ]
        }
    };
}

fn main() {
let strings: [String; 3] = init_array![String::from("hi!"); 3];
}

Advanced

Push-Down Accumulation

…защото това би довело до следното разгъване

1 2 3 4 5
init_array!(@accum 3, e)
e, init_array!(@accum 2, e)
e, e, init_array!(@accum 1, e)
e, e, e
[e, e, e]

Тук всяка помощна стъпка ще е невалиден Rust синтаксис и това не е позволено независимо от стъпките

Advanced

Push-Down Accumulation

Push-Down ни позволява да правим подобни констрикции чрез акумулиране на токени, без да се налага да имаме валиден синтаксис през цялото време.

Advanced

Push-Down Accumulation

Разгъвка на първия пример изглежда така

1 2 3 4 5 6
init_array! { String:: from ( "hi!" ) ; 3 }
init_array! { @ accum ( 3 , e . clone (  ) ) -> (  ) }
init_array! { @ accum ( 2 , e.clone() ) -> ( e.clone() , ) }
init_array! { @ accum ( 1 , e.clone() ) -> ( e.clone() , e.clone() , ) }
init_array! { @ accum ( 0 , e.clone() ) -> ( e.clone() , e.clone() , e.clone() , ) }
init_array! { @ as_expr [ e.clone() , e.clone() , e.clone() , ] }

Advanced

Push-Down Accumulation се използва в комбинация с TT Muncher, за да се парсват произволно сложни граматики

Материали

Въпроси