1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use proc_macro::TokenStream;
use cpclib_asm::preamble::*;
use quote::ToTokens;
use syn::{parse_macro_input, Result, token, Error};
use syn::parse::Parse;
use syn::parse::ParseStream;
use syn::parse::Parser;
mod tokens;


/// Structure that contains the input f the macro.
/// Will be updated once we'll have additional parameters
struct AssemblyMacroInput {
    /// Code provided by the user of the macro
    code: String
}

mod kw {
    syn::custom_keyword!(fname);
}

/// Obtain the z80 code from:
/// - the direct string if any
/// - a file if "fname:" is provided
impl Parse for AssemblyMacroInput {
    fn parse(input: ParseStream) -> Result<Self> {


        let lookahead = input.lookahead1();
        if lookahead.peek(kw::fname) {
            input.parse::<kw::fname>()?;
            input.parse::<syn::Token![:]>()?;
            let fname = (input.parse::<syn::LitStr>()?).value();
            let content = std::fs::read_to_string(fname)
                .or_else(|e|{
                    Err(syn::Error::new(proc_macro2::Span::call_site(), e.to_string()))
                })?;

            Ok(
                AssemblyMacroInput{
                    code: content
                }
            )
        } else if lookahead.peek(syn::LitStr) {
            Ok(AssemblyMacroInput{
                code: (input.parse::<syn::LitStr>()?).value()
            })
        } else {
            Err(lookahead.error())
        }
    }

}



#[proc_macro]
/// Parse an assembly code and produce the appropriate Listing while compiling the rust code.
/// No more parsing is done at execution.
/// input can be:
/// - a string literal
/// - a path
pub fn parse_z80(tokens: TokenStream) -> TokenStream {
    let input = parse_macro_input!(tokens as AssemblyMacroInput);
    let listing = get_listing(input);

    match listing {
        Ok(listing) => {
            use tokens::*;
            let mut stream = proc_macro2::TokenStream::new();
            listing.to_tokens(&mut stream);
            stream.into()
        },
        Err(e) => {
            panic!("[ERROR] {:?}", e);
        }
    }
}

fn get_listing(input: AssemblyMacroInput) -> std::result::Result<Listing, cpclib_asm::error::AssemblerError> {
    Listing::from_str(&input.code)
}

/// Generte the bytes of asssembled data
#[proc_macro]
pub fn assemble(tokens: TokenStream) -> TokenStream
{

    let input = parse_macro_input!(tokens as AssemblyMacroInput);
    let listing = get_listing(input);

    match listing {
        Ok(listing) => {
            match listing.to_bytes() {
                Ok(ref bytes) => {
                    let mut tokens = proc_macro2::TokenStream::default();
                    proc_macro2::Literal::byte_string(&bytes).to_tokens(&mut tokens);
                    return tokens.into();
                },

                Err(e) => {
                    panic!("Unable to assemble the provided code. {}", e);
                }
            }
        },
        Err(e) => {
            panic!("[ERROR] {:?}", e);
        }
    }
}