cpclib_macros/
lib.rs

1use cpclib_asm::preamble::*;
2use proc_macro::TokenStream;
3use quote::ToTokens;
4use syn::parse::{Parse, ParseStream};
5use syn::{Result, parse_macro_input};
6mod tokens;
7
8/// Structure that contains the input of the macro.
9/// Will be updated once we'll have additional parameters
10struct AssemblyMacroInput {
11    /// Code provided by the user of the macro
12    code: String
13}
14
15mod kw {
16    syn::custom_keyword!(fname);
17}
18
19/// Obtain the z80 code from:
20/// - the direct string if any
21/// - a file if "fname:" is provided
22impl Parse for AssemblyMacroInput {
23    fn parse(input: ParseStream) -> Result<Self> {
24        let lookahead = input.lookahead1();
25        if lookahead.peek(kw::fname) {
26            input.parse::<kw::fname>()?;
27            input.parse::<syn::Token![:]>()?;
28            let fname = (input.parse::<syn::LitStr>()?).value();
29            let content = std::fs::read_to_string(&fname).map_err(|e| {
30                syn::Error::new(
31                    proc_macro2::Span::call_site(),
32                    format!("Unable to load {fname}.\n{e}")
33                )
34            })?;
35
36            Ok(AssemblyMacroInput { code: content })
37        }
38        else if lookahead.peek(syn::LitStr) {
39            Ok(AssemblyMacroInput {
40                code: (input.parse::<syn::LitStr>()?).value()
41            })
42        }
43        else {
44            Err(lookahead.error())
45        }
46    }
47}
48
49#[proc_macro]
50/// Parse an assembly code and produce the appropriate Listing while compiling the rust code.
51/// No more parsing is done at execution.
52/// input can be:
53/// - a string literal
54/// - a path
55pub fn parse_z80(tokens: TokenStream) -> TokenStream {
56    let input = parse_macro_input!(tokens as AssemblyMacroInput);
57    let listing = get_listing(input);
58
59    match listing {
60        Ok(listing) => {
61            use tokens::*;
62            let mut stream = proc_macro2::TokenStream::new();
63            listing.to_tokens(&mut stream);
64            stream.into()
65        },
66        Err(e) => {
67            panic!("[ERROR] {e:?}");
68        }
69    }
70}
71
72fn get_listing(
73    input: AssemblyMacroInput
74) -> std::result::Result<Listing, cpclib_asm::error::AssemblerError> {
75    Listing::from_str(&input.code)
76}
77
78/// Generte the bytes of asssembled data
79#[proc_macro]
80pub fn assemble(tokens: TokenStream) -> TokenStream {
81    let input = parse_macro_input!(tokens as AssemblyMacroInput);
82    let listing = get_listing(input);
83
84    match listing {
85        Ok(listing) => {
86            match listing.to_bytes() {
87                Ok(ref bytes) => {
88                    let mut tokens = proc_macro2::TokenStream::default();
89                    proc_macro2::Literal::byte_string(bytes).to_tokens(&mut tokens);
90                    tokens.into()
91                },
92
93                Err(e) => {
94                    panic!("Unable to assemble the provided code. {e:?}");
95                }
96            }
97        },
98        Err(e) => {
99            panic!("[ERROR] {e:?}");
100        }
101    }
102}