c64_assembler_macro/
lib.rs

1use proc_macro::{TokenStream, TokenTree};
2
3#[proc_macro]
4pub fn application(input: TokenStream) -> TokenStream {
5    //dbg!(input.clone());
6    let mut lines = Vec::<String>::default();
7    lines.push("{".to_string());
8    lines.push("  use c64_assembler::builder::{*};".to_string());
9    lines.push("  ApplicationBuilder::default()".to_string());
10    let mut iter = input.into_iter();
11    while let Some(tree) = iter.next() {
12        if let TokenTree::Ident(identifier) = tree {
13            let name = identifier.to_string();
14            if name == "name" {
15                let mut line = Vec::<String>::default();
16                line.push("    .name(".to_string());
17                let _eq = iter.next().unwrap();
18                if let Some(TokenTree::Literal(application_name)) = iter.next() {
19                    line.push(format!("{application_name})"));
20                    lines.push(line.join(""));
21                }
22            }
23            if name == "entry_point" {
24                let mut line = Vec::<String>::default();
25                line.push("    .entry_point(".to_string());
26                let _eq = iter.next().unwrap();
27                if let Some(TokenTree::Literal(entry_point)) = iter.next() {
28                    line.push(format!("{entry_point})"));
29                    lines.push(line.join(""));
30                }
31            }
32            if name == "include_vic2_defines" {
33                lines.push("    .include_vic2_defines()".to_string());
34            }
35            if name == "include_sid_defines" {
36                lines.push("    .include_sid_defines()".to_string());
37            }
38            if name == "module" {
39                let _eq = iter.next().unwrap();
40                if let Some(TokenTree::Group(sub_tree)) = iter.next() {
41                    lines.push("    .module(".to_string());
42                    lines.push(build_module(sub_tree.stream()));
43                    lines.push("    )".to_string());
44                }
45            }
46        }
47    }
48    lines.push("    .build()".to_string());
49    lines.push("}".to_string());
50
51    //println!("{:#?}", lines.join("\n"));
52    lines.join("\n").parse().unwrap()
53}
54
55fn build_module(input: TokenStream) -> String {
56    let mut lines = Vec::<String>::default();
57    lines.push("  ModuleBuilder::default()".to_string());
58    let mut iter = input.into_iter();
59    while let Some(tree) = iter.next() {
60        if let TokenTree::Ident(identifier) = tree {
61            let name = identifier.to_string();
62            if name == "name" {
63                let mut line = Vec::<String>::default();
64                line.push("    .name(".to_string());
65                let _eq = iter.next().unwrap();
66                if let Some(TokenTree::Literal(module_name)) = iter.next() {
67                    line.push(format!("{module_name})"));
68                    lines.push(line.join(""));
69                }
70            }
71            if name == "instructions" {
72                let _eq = iter.next().unwrap();
73                if let Some(TokenTree::Group(sub_tree)) = iter.next() {
74                    lines.push("    .instructions(".to_string());
75                    lines.push(build_instructions(sub_tree.stream()));
76                    lines.push("    )".to_string());
77                }
78            }
79            if name == "function" {
80                let _eq = iter.next().unwrap();
81                if let Some(TokenTree::Group(sub_tree)) = iter.next() {
82                    lines.push("    .function(".to_string());
83                    lines.push(build_function(sub_tree.stream()));
84                    lines.push("    )".to_string());
85                }
86            }
87        }
88    }
89    lines.push("    .build()".to_string());
90    lines.join("\n")
91}
92
93fn build_function(input: TokenStream) -> String {
94    let mut lines = Vec::<String>::default();
95    lines.push("  FunctionBuilder::default()".to_string());
96    let mut iter = input.into_iter();
97    while let Some(tree) = iter.next() {
98        if let TokenTree::Ident(identifier) = tree {
99            let name = identifier.to_string();
100            if name == "name" {
101                let mut line = Vec::<String>::default();
102                line.push("    .name(".to_string());
103                let _eq = iter.next().unwrap();
104                if let Some(TokenTree::Literal(function_name)) = iter.next() {
105                    line.push(format!("{function_name})"));
106                    lines.push(line.join(""));
107                }
108            }
109            if name == "instructions" {
110                let _eq = iter.next().unwrap();
111                if let Some(TokenTree::Group(sub_tree)) = iter.next() {
112                    lines.push("    .instructions(".to_string());
113                    lines.push(build_instructions(sub_tree.stream()));
114                    lines.push("    )".to_string());
115                }
116            }
117        }
118    }
119    lines.push("    .build()".to_string());
120    lines.join("\n")
121}
122/*
123Ident {
124    ident: "jmp",
125    span: #0 bytes(20193..20196),
126},
127Group {
128    delimiter: Parenthesis,
129    stream: TokenStream [
130        Ident {
131            ident: "test",
132            span: #0 bytes(20198..20202),
133        },
134        ],
135        span: #0 bytes(20197..20203),
136    },
137`*/
138fn build_address_mode(
139    line: &mut Vec<String>,
140    tokens: &[TokenTree],
141    allow_immediate: bool,
142    allow_accumulator: bool,
143    allow_absolute: bool,
144    allow_indirect: bool,
145) -> usize {
146    match tokens.first().unwrap() {
147        TokenTree::Punct(punct) => {
148            if punct.to_string() == "#" {
149                assert!(allow_immediate);
150                return build_address_mode_imm(line, &tokens[1..]) + 1;
151            }
152        }
153        TokenTree::Ident(ident) => {
154            if ident.to_string() == *"a" {
155                assert!(allow_accumulator);
156                return build_address_mode_accumulator(line, tokens);
157            }
158            assert!(allow_absolute);
159            return build_address_mode_absolute(line, tokens);
160        }
161        TokenTree::Group(_group) => {
162            assert!(allow_indirect);
163            return build_address_mode_indirect(line, &tokens[0..]);
164        }
165        _ => todo!("HUH!"),
166    }
167    0
168}
169
170fn build_address_mode_accumulator(line: &mut Vec<String>, _tokens: &[TokenTree]) -> usize {
171    line.push("_acc()".to_string());
172    1
173}
174
175fn build_address_mode_absolute(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
176    let mut num_tokens = 1;
177    let address = if let Some(TokenTree::Ident(ident)) = tokens.first() {
178        ident.to_string()
179    } else {
180        "".to_string()
181    };
182    line.push("_addr".to_string());
183    let mut offset = 0;
184    let mut index = "".to_string();
185    if let Some(TokenTree::Punct(p)) = tokens.get(1) {
186        if p.as_char() == '+' {
187            if let Some(TokenTree::Literal(l)) = tokens.get(2) {
188                offset = l.to_string().parse::<u16>().unwrap();
189            }
190            num_tokens = 3;
191        }
192        if p.as_char() == ',' {
193            if let Some(TokenTree::Ident(identifier)) = tokens.get(2) {
194                index = identifier.to_string();
195            }
196            num_tokens = 3;
197        }
198    }
199    if (offset) > 0 {
200        line.push("_offs".to_string());
201    }
202    if !index.is_empty() {
203        line.push(format!("_{}", index));
204    }
205
206    line.push("(".to_string());
207    line.push(format!("\"{}\"", address));
208    if offset > 0 {
209        line.push(format!(", {}", offset));
210    }
211    line.push(")".to_string());
212    num_tokens
213}
214
215fn build_address_mode_indirect(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
216    let mut is_indirect_indexed = false;
217    let mut is_indexed_indirect = false;
218    let mut address = String::new();
219    if let TokenTree::Group(group) = &tokens[0] {
220        for token in group.stream() {
221            if let TokenTree::Ident(identifier) = &token {
222                address = identifier.to_string();
223            }
224            if let TokenTree::Punct(punct) = &token {
225                if punct.as_char() == ',' {
226                    is_indexed_indirect = true;
227                    break;
228                }
229            }
230        }
231    }
232    if let Some(TokenTree::Punct(punct)) = &tokens.get(1) {
233        if punct.as_char() == ',' {
234            is_indirect_indexed = true;
235        }
236    }
237
238    if is_indexed_indirect {
239        line.push(format!("_ind_x(\"{address}\")"));
240        1
241    } else if is_indirect_indexed {
242        line.push(format!("_ind_y(\"{address}\")"));
243        3
244    } else {
245        line.push(format!("_ind(\"{address}\")"));
246        1
247    }
248}
249
250fn build_address_mode_imm(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
251    let mut num_tokens = 0;
252    line.push("_imm".to_string());
253    let mut is_hex = false;
254    let mut is_low = false;
255    let mut is_high = false;
256    for token in tokens {
257        num_tokens += 1;
258        match token {
259            TokenTree::Punct(punct) => {
260                if punct.to_string() == "$" {
261                    is_hex = true;
262                }
263                if punct.to_string() == "<" {
264                    is_low = true;
265                }
266                if punct.to_string() == ">" {
267                    is_high = true;
268                }
269            }
270            TokenTree::Literal(value) => {
271                if is_hex {
272                    line.push(format!("(0x{})", value));
273                } else {
274                    line.push(format!("({})", value));
275                }
276                break;
277            }
278            TokenTree::Ident(value) => {
279                if is_low {
280                    line.push(format!("_low(\"{}\")", value));
281                } else if is_high {
282                    line.push(format!("_high(\"{}\")", value));
283                } else if is_hex {
284                    line.push(format!("(0x{})", value));
285                } else {
286                    line.push(format!("(\"{}\")", value));
287                }
288                break;
289            }
290            _ => todo!(),
291        }
292    }
293    num_tokens
294}
295
296fn build_instructions(input: TokenStream) -> String {
297    let mut lines = Vec::<String>::default();
298    lines.push("  InstructionBuilder::default()".to_string());
299    let tokens = input.into_iter().collect::<Vec<TokenTree>>();
300    let mut sub_start = 0;
301    for i in 0..tokens.len() {
302        if i < sub_start {
303            continue;
304        }
305        let token = &tokens[i];
306        if let TokenTree::Ident(identifier) = token {
307            let name = identifier.to_string();
308            match name.as_str() {
309                "include_basic_header" => {
310                    lines.push("    .add_basic_header()".to_string());
311                    sub_start = i + 1;
312                }
313
314                "asl" | "lsr" | "ror" | "rol" => {
315                    let mut line = Vec::default();
316                    line.push(format!("    .{name}"));
317                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, true, true, false);
318                    lines.push(line.join(""));
319                    sub_start = i + 1 + add_tokens_parsed;
320                }
321                "adc" | "and" | "cmp" | "cpx" | "cpy" | "eor" | "lda" | "ldx" | "ldy" | "ora" | "sbc" => {
322                    let mut line = Vec::default();
323                    line.push(format!("    .{name}"));
324                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], true, false, true, true);
325                    lines.push(line.join(""));
326                    sub_start = i + 1 + add_tokens_parsed;
327                }
328
329                "bcc" | "bcs" | "beq" | "bmi" | "bne" | "bpl" | "bvc" | "bvs" | "jsr" | "bit" | "dec" | "inc" => {
330                    let mut line = Vec::default();
331                    line.push(format!("    .{name}"));
332                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, false, true, false);
333                    lines.push(line.join(""));
334                    sub_start = i + 1 + add_tokens_parsed;
335                }
336
337                "sta" | "stx" | "sty" | "jmp" => {
338                    let mut line = Vec::default();
339                    line.push(format!("    .{name}"));
340                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, false, true, true);
341                    lines.push(line.join(""));
342                    sub_start = i + 1 + add_tokens_parsed;
343                }
344
345                "brk" | "cld" | "cli" | "clv" | "dex" | "dey" | "inx" | "iny" | "nop" | "pha" | "psr" | "pla"
346                | "php" | "plp" | "rti" | "sec" | "sed" | "sei" | "tax" | "tay" | "tsx" | "txa" | "txs" | "tya"
347                | "clc" | "rts" => {
348                    lines.push(format!("    .{name}()"));
349                    sub_start = i + 1;
350                }
351
352                &_ => {
353                    lines.push(format!("    .label(\"{name}\")"));
354                    sub_start = i + 2;
355                }
356            }
357        }
358    }
359
360    lines.push("    .build()".to_string());
361    lines.join("\n")
362}
363
364#[proc_macro]
365pub fn module(input: TokenStream) -> TokenStream {
366    //dbg!(input.clone());
367    let mut lines = Vec::<String>::default();
368    lines.push("{".to_string());
369    lines.push("  use c64_assembler::builder::{*};".to_string());
370    lines.push(build_module(input));
371    lines.push("}".to_string());
372    //println!("{:#?}", lines.join("\n"));
373    lines.join("\n").parse().unwrap()
374}
375
376#[proc_macro]
377pub fn function(input: TokenStream) -> TokenStream {
378    //dbg!(input.clone());
379    let mut lines = Vec::<String>::default();
380    lines.push("{".to_string());
381    lines.push("  use c64_assembler::builder::{*};".to_string());
382    lines.push(build_function(input));
383    lines.push("}".to_string());
384    //println!("{:#?}", lines.join("\n"));
385    lines.join("\n").parse().unwrap()
386}
387
388#[proc_macro]
389pub fn instructions(input: TokenStream) -> TokenStream {
390    //dbg!(input.clone());
391    let mut lines = Vec::<String>::default();
392    lines.push("{".to_string());
393    lines.push("  use c64_assembler::builder::{*};".to_string());
394    lines.push(build_instructions(input));
395    lines.push("}".to_string());
396    //println!("{:#?}", lines.join("\n"));
397    lines.join("\n").parse().unwrap()
398}