#[macro_export]
macro_rules! print_ast {
    ([ $($ast:tt),* ]) => {
        $(
            print_ast!($ast);
        )*
    };

    ({
        type: class,
        name: $name:tt,
        meta: { pub: $pub:tt, reopen: $reopen:tt },
        struct: $struct:tt,
        methods: $methods:tt
    }) => {
        println!("\nCLASS\n=====\nclass {} (pub:{} reopen:{})", stringify!($name), stringify!($pub), stringify!($reopen));
        println!("{}", format_ast_class_struct!({ name: $name, tuple: $struct }));
        println!("\nMETHODS\n=======\n{}\n\n", format_ast_methods!($methods));
    };

    ($($ast:tt)*) => {
        println!("Unimplemented: {}", stringify!($($ast)*));
    };
}

#[macro_export]
macro_rules! format_ast_class_struct {
    ({
        name: $name:tt,
        tuple: { $( $field:tt : $fieldty:ty ),* }
    }) => {
        {
            let mut s = String::new();
            $(
                s.push_str(&format!("struct {} {{ {}: {} }}", stringify!($name), stringify!($field), stringify!($fieldty)));
            )*
            s
        }
    };

    ({
        name: $name:tt,
        tuple: ()
    }) => {
        "No Struct"
    };
}

#[macro_export]
macro_rules! format_ast_methods {
    ([
        $($ast:tt),*
    ]) => {
        {
            let mut v = Vec::new();
            $(
                v.push(format_ast_methods!($ast));
            )*
            v.join("\n")
        }
    };

    ({
        type: instance_method,
        name: $name:tt,
        self: $self:tt,
        args: $args:tt,
        ret: $ret:tt,
        body: $body:tt
    }) => {
        {
            let mut s = String::new();
            s.push_str(&format!("def {}", stringify!($name)));
            s.push_str(&format!("({})", format_ast_args!($args)));
            s.push_str(&format_ret!($ret));
            s.push_str(" ");
            s.push_str(&stringify!($body));
            s
        }
    };

    ({
        type: class_method,
        name: $name:tt,
        self: $self:tt,
        args: $args:tt,
        ret: $ret:ty,
        body: $body:tt
    }) => {
        {
            let mut s = String::new();
            s.push_str(&format!("def self.{}", stringify!($name)));
            s.push_str(&format!("({})", format_ast_args!($args)));
            s.push_str(&format_ret!($ret));
            s.push_str(" ");
            s.push_str(&stringify!($body));
            s
        }
    };


    ({
        type: initializer,
        name: $name:tt,
        self: $self:tt,
        args: $args:tt,
        ret: $ret:tt,
        body: $body:tt
    }) => {
        {
            let mut s = String::new();
            s.push_str(&format!("def initialize({})", format_ast_args!($args)));
            s.push_str(&format_ret!($ret));
            s.push_str(" ");
            s.push_str(&stringify!($body));
            s
        }
    };
}

#[macro_export]
macro_rules! format_ret {
    ($ret:ty) => { format!(" -> {}", stringify!($ret)) };
}

#[macro_export]
macro_rules! format_ast_args {
    ([ $($args:tt)* ]) => {
        format!("{}", stringify!($($args)*))
    }
}
