c64_assembler_macro/
lib.rs1use proc_macro::{TokenStream, TokenTree};
2
3#[proc_macro]
4pub fn application(input: TokenStream) -> TokenStream {
5 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 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 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 lines.join("\n").parse().unwrap()
328}
329
330#[proc_macro]
331pub fn function(input: TokenStream) -> TokenStream {
332 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 lines.join("\n").parse().unwrap()
340}
341
342#[proc_macro]
343pub fn instructions(input: TokenStream) -> TokenStream {
344 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 lines.join("\n").parse().unwrap()
352}