2015-07-02 12 views
7

Tôi cố gắng để viết một macro cho phép tôi chuyển đổi (a, b, c, d)-(a, a + b, a + b + c, a + b + c + d), vv Đây là những gì tôi đã có cho đến nay:dấu phẩy Thoát sản lượng vĩ mô

macro_rules! pascal_next { 
    ($x: expr) => ($x); 
    ($x: expr, $y: expr) => (
     ($x, $x + $y) 
    ); 
    ($x: expr, $y: expr, $($rest: expr),+) => (
     ($x, pascal_next!(
       $x + $y, $($rest),+ 
      ) 
     ) 
    ); 
} 

Tuy nhiên, có một vấn đề mà nó thực sự sẽ xuất (a, (a + b, (a + b + c, a + b + c + d))). Nguồn gốc là quy tắc kết hợp thứ hai ($x: expr, $y: expr) => (($x, $x + $y));, tạo ra một khung phụ, do đó sẽ có các dấu ngoặc lồng nhau. Nếu tôi không đặt một khung bên ngoài, tôi sẽ nhận được lỗi lỗi:

unexpected token: ,

Vậy là nó có thể để ra một dấu phẩy , trong macro Rust?

Trả lời

10

Không; kết quả của một macro phải là một cấu trúc ngữ pháp hoàn chỉnh như một biểu thức hoặc một mục. Bạn hoàn toàn không thể có các bit cú pháp ngẫu nhiên như dấu phẩy hoặc dấu ngoặc đóng.

Bạn có thể giải quyết vấn đề này bằng cách không xuất ra bất kỳ thứ gì cho đến khi bạn có biểu thức hoàn chỉnh, cuối cùng. Kìa!

#![feature(trace_macros)] 

macro_rules! pascal_impl { 
    /* 
    The input to this macro takes the following form: 

    ```ignore 
    (
     // The current output accumulator. 
     ($($out:tt)*); 

     // The current additive prefix. 
     $prefix:expr; 

     // The remaining, comma-terminated elements. 
     ... 
    ) 
    ``` 
    */ 

    /* 
    Termination condition: there is no input left. As 
    such, dump the output. 
    */ 
    (
     $out:expr; 
     $_prefix:expr; 
    ) => { 
     $out 
    }; 

    /* 
    Otherwise, we have more to scrape! 
    */ 
    (
     ($($out:tt)*); 
     $prefix:expr; 
     $e:expr, $($rest:tt)* 
    ) => { 
     pascal_impl!(
      ($($out)* $prefix+$e,); 
      $prefix+$e; 
      $($rest)* 
     ) 
    }; 
} 

macro_rules! pascal { 
    ($($es:expr),+) => { pascal_impl!((); 0; $($es),+,) }; 
} 

trace_macros!(true); 

fn main() { 
    println!("{:?}", pascal!(1, 2, 3, 4)); 
} 

Note: Để sử dụng này trên một trình biên dịch ổn định, bạn sẽ cần phải xoá #![feature(trace_macros)]trace_macros!(true); dòng. Mọi thứ khác sẽ ổn thôi.

Điều này không là nó đệ quy munches đi tại đầu vào, đi qua các phần (và có khả năng ngữ nghĩa không hợp lệ) đầu ra như đầu vào lên tầm cao mới của đệ quy. Điều này cho phép chúng tôi xây dựng một "danh sách mở", mà chúng tôi không thể làm khác.

Sau đó, khi chúng tôi không có dữ liệu đầu vào, chúng tôi chỉ diễn giải lại phần đầu ra một phần của chúng tôi dưới dạng biểu thức hoàn chỉnh và ... xong.

Lý do tôi bao gồm các công cụ truy tìm là để tôi có thể hiển thị cho bạn những gì nó trông giống như khi nó chạy:

pascal! { 1 , 2 , 3 , 4 } 
pascal_impl! { () ; 0 ; 1 , 2 , 3 , 4 , } 
pascal_impl! { (0 + 1 ,) ; 0 + 1 ; 2 , 3 , 4 , } 
pascal_impl! { (0 + 1 , 0 + 1 + 2 ,) ; 0 + 1 + 2 ; 3 , 4 , } 
pascal_impl! { (0 + 1 , 0 + 1 + 2 , 0 + 1 + 2 + 3 ,) ; 0 + 1 + 2 + 3 ; 4 , } 
pascal_impl! { (0 + 1 , 0 + 1 + 2 , 0 + 1 + 2 + 3 , 0 + 1 + 2 + 3 + 4 ,) ; 0 + 1 + 2 + 3 + 4 ; } 

Và kết quả là:

(1, 3, 6, 10) 

Một điều cần phải nhận thức của : số lượng lớn các số nguyên nguyên không chú thích có thể gây ra sự gia tăng số lần biên dịch ấn tượng. Nếu điều này xảy ra, bạn có thể giải quyết nó bằng cách chỉ cần chú thích tất cả của các chữ số nguyên của bạn (như 1i32).

Các vấn đề liên quan