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
123fn build_address_mode(
124    line: &mut Vec<String>,
125    tokens: &[TokenTree],
126    allow_immediate: bool,
127    allow_accumulator: bool,
128    allow_absolute: bool,
129    allow_indirect: bool,
130) -> usize {
131    match tokens.first().unwrap() {
132        TokenTree::Punct(punct) => {
133            if punct.to_string() == "#" {
134                assert!(allow_immediate);
135                return build_address_mode_imm(line, &tokens[1..]) + 1;
136            }
137            if punct.to_string() == "(" {
138                assert!(allow_indirect);
139                return build_address_mode_indirect(line, &tokens[1..]) + 1;
140            }
141        }
142        TokenTree::Ident(ident) => {
143            if ident.to_string() == *"a" {
144                assert!(allow_accumulator);
145                return build_address_mode_accumulator(line, tokens);
146            }
147            assert!(allow_absolute);
148            return build_address_mode_absolute(line, tokens);
149        }
150        _ => todo!(),
151    }
152    0
153}
154
155fn build_address_mode_accumulator(line: &mut Vec<String>, _tokens: &[TokenTree]) -> usize {
156    line.push("_acc()".to_string());
157    1
158}
159
160fn build_address_mode_absolute(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
161    let mut num_tokens = 1;
162    let address = if let Some(TokenTree::Ident(ident)) = tokens.first() {
163        ident.to_string()
164    } else {
165        "".to_string()
166    };
167    line.push("_addr".to_string());
168    let mut offset = 0;
169    let mut index = "".to_string();
170    if let Some(TokenTree::Punct(p)) = tokens.get(1) {
171        if p.as_char() == '+' {
172            if let Some(TokenTree::Literal(l)) = tokens.get(2) {
173                offset = l.to_string().parse::<u16>().unwrap();
174            }
175            num_tokens = 3;
176        }
177        if p.as_char() == ',' {
178            if let Some(TokenTree::Ident(identifier)) = tokens.get(2) {
179                index = identifier.to_string();
180            }
181            num_tokens = 3;
182        }
183    }
184    if (offset) > 0 {
185        line.push("_offs".to_string());
186    }
187    if !index.is_empty() {
188        line.push(format!("_{}", index));
189    }
190
191    line.push("(".to_string());
192    line.push(format!("\"{}\"", address));
193    if offset > 0 {
194        line.push(format!(", {}", offset));
195    }
196    line.push(")".to_string());
197    num_tokens
198}
199
200fn build_address_mode_indirect(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
201    0
202}
203
204fn build_address_mode_imm(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
205    let mut num_tokens = 0;
206    line.push("_imm".to_string());
207    let mut is_hex = false;
208    let mut is_low = false;
209    let mut is_high = false;
210    for token in tokens {
211        num_tokens += 1;
212        match token {
213            TokenTree::Punct(punct) => {
214                if punct.to_string() == "$" {
215                    is_hex = true;
216                }
217                if punct.to_string() == "<" {
218                    is_low = true;
219                }
220                if punct.to_string() == ">" {
221                    is_high = true;
222                }
223            }
224            TokenTree::Literal(value) => {
225                if is_hex {
226                    line.push(format!("(0x{})", value));
227                } else {
228                    line.push(format!("({})", value));
229                }
230                break;
231            }
232            TokenTree::Ident(value) => {
233                if is_low {
234                    line.push(format!("_low(\"{}\")", value));
235                } else if is_high {
236                    line.push(format!("_high(\"{}\")", value));
237                } else if is_hex {
238                    line.push(format!("(0x{})", value));
239                } else {
240                    line.push(format!("(\"{}\")", value));
241                }
242                break;
243            }
244            _ => todo!(),
245        }
246    }
247    num_tokens
248}
249
250fn build_instructions(input: TokenStream) -> String {
251    let mut lines = Vec::<String>::default();
252    lines.push("  InstructionBuilder::default()".to_string());
253    let tokens = input.into_iter().collect::<Vec<TokenTree>>();
254    let mut sub_start = 0;
255    for i in 0..tokens.len() {
256        if i < sub_start {
257            continue;
258        }
259        let token = &tokens[i];
260        if let TokenTree::Ident(identifier) = token {
261            let name = identifier.to_string();
262            match name.as_str() {
263                "include_basic_header" => {
264                    lines.push("    .add_basic_header()".to_string());
265                    sub_start = i + 1;
266                }
267
268                "asl" | "lsr" | "ror" | "rol" => {
269                    let mut line = Vec::default();
270                    line.push(format!("    .{name}"));
271                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, true, true, false);
272                    lines.push(line.join(""));
273                    sub_start = i + 1 + add_tokens_parsed;
274                }
275                "adc" | "and" | "cmp" | "cpx" | "cpy" | "eor" | "lda" | "ldx" | "ldy" | "ora" | "sbc" => {
276                    let mut line = Vec::default();
277                    line.push(format!("    .{name}"));
278                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], true, false, true, true);
279                    lines.push(line.join(""));
280                    sub_start = i + 1 + add_tokens_parsed;
281                }
282
283                "bcc" | "bcs" | "beq" | "bmi" | "bne" | "bpl" | "bvc" | "bvs" | "jsr" | "bit" | "dec" | "inc" => {
284                    let mut line = Vec::default();
285                    line.push(format!("    .{name}"));
286                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, false, true, false);
287                    lines.push(line.join(""));
288                    sub_start = i + 1 + add_tokens_parsed;
289                }
290
291                "sta" | "stx" | "sty" | "jmp" => {
292                    let mut line = Vec::default();
293                    line.push(format!("    .{name}"));
294                    let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, false, true, true);
295                    lines.push(line.join(""));
296                    sub_start = i + 1 + add_tokens_parsed;
297                }
298
299                "brk" | "cld" | "cli" | "clv" | "dex" | "dey" | "inx" | "iny" | "nop" | "pha" | "psr" | "pla"
300                | "php" | "plp" | "rti" | "sec" | "sed" | "sei" | "tax" | "tay" | "tsx" | "txa" | "txs" | "tya"
301                | "clc" | "rts" => {
302                    lines.push(format!("    .{name}()"));
303                    sub_start = i + 1;
304                }
305
306                &_ => {
307                    lines.push(format!("    .label(\"{name}\")"));
308                    sub_start = i + 2;
309                }
310            }
311        }
312    }
313
314    lines.push("    .build()".to_string());
315    lines.join("\n")
316}
317
318#[proc_macro]
319pub fn module(input: TokenStream) -> TokenStream {
320    //dbg!(input.clone());
321    let mut lines = Vec::<String>::default();
322    lines.push("{".to_string());
323    lines.push("  use c64_assembler::builder::{*};".to_string());
324    lines.push(build_module(input));
325    lines.push("}".to_string());
326    //println!("{:#?}", lines.join("\n"));
327    lines.join("\n").parse().unwrap()
328}
329
330#[proc_macro]
331pub fn function(input: TokenStream) -> TokenStream {
332    //dbg!(input.clone());
333    let mut lines = Vec::<String>::default();
334    lines.push("{".to_string());
335    lines.push("  use c64_assembler::builder::{*};".to_string());
336    lines.push(build_function(input));
337    lines.push("}".to_string());
338    //println!("{:#?}", lines.join("\n"));
339    lines.join("\n").parse().unwrap()
340}
341
342#[proc_macro]
343pub fn instructions(input: TokenStream) -> TokenStream {
344    //dbg!(input.clone());
345    let mut lines = Vec::<String>::default();
346    lines.push("{".to_string());
347    lines.push("  use c64_assembler::builder::{*};".to_string());
348    lines.push(build_instructions(input));
349    lines.push("}".to_string());
350    //println!("{:#?}", lines.join("\n"));
351    lines.join("\n").parse().unwrap()
352}