uxn_tal/parser.rs
1//! Parser for TAL assembly language
2
3use crate::error::{AssemblerError, Result};
4use crate::lexer::{Token, TokenWithPos};
5use crate::opcodes::Opcodes;
6use crate::runes::Rune;
7
8/// Represents a parsed instruction with modes
9#[derive(Debug, Clone)]
10pub struct Instruction {
11 pub opcode: String,
12 pub short_mode: bool,
13 pub return_mode: bool,
14 pub keep_mode: bool,
15}
16
17/// AST node types
18#[derive(Debug, Clone)]
19pub enum AstNode {
20 /// Raw byte value
21 Byte(u8),
22 /// 16-bit short value
23 Short(u16),
24 /// Literal byte value (prefixed with #)
25 LiteralByte(u8),
26 /// Literal short value (prefixed with #)
27 LiteralShort(u16),
28 /// Instruction with mode flags
29 Instruction(Instruction),
30 /// Label definition
31 LabelDef(Rune, String),
32 /// Label reference
33 LabelRef {
34 label: String,
35 rune: Rune,
36 token: TokenWithPos,
37 },
38 /// Sublabel definition
39 SublabelDef(TokenWithPos),
40 /// Sublabel reference
41 SublabelRef(TokenWithPos),
42 /// Relative address reference
43 RelativeRef(TokenWithPos),
44 /// Conditional jump reference
45 ConditionalRef(TokenWithPos),
46 /// Raw address reference
47 RawAddressRef(TokenWithPos),
48 /// JSR call reference
49 JSRRef(TokenWithPos),
50 /// Hyphen address reference
51 HyphenRef(TokenWithPos),
52 /// Padding to specific address
53 Padding(u16),
54 PaddingLabel(TokenWithPos),
55 /// Relative padding by hex count ($1234)
56 RelativePadding(u16),
57 /// Relative padding to label ($label)
58 RelativePaddingLabel(TokenWithPos),
59 /// Macro definition
60 MacroDef(String, Vec<AstNode>), // name, body
61 /// Macro call (name, line, position)
62 MacroCall(String, usize, usize),
63 /// Raw string data
64 RawString(Vec<u8>),
65 /// Include directive
66 Include(TokenWithPos),
67 /// Dot reference - generates LIT + 8-bit address (like uxnasm's '.' rune)
68 DotRef(TokenWithPos),
69 /// Semicolon reference - generates LIT2 + 16-bit address (like uxnasm's ';' rune)
70 SemicolonRef(TokenWithPos),
71 /// Equals reference - generates 16-bit address directly (like uxnasm's '=' rune)
72 EqualsRef(TokenWithPos),
73 /// Comma reference - generates LIT + relative 8-bit address (like uxnasm's ',' rune)
74 CommaRef(TokenWithPos),
75 /// Underscore reference - generates relative 8-bit address (like uxnasm's '_' rune)
76 UnderscoreRef(TokenWithPos),
77 /// Question reference - generates conditional jump (like uxnasm's '?' rune)
78 QuestionRef(TokenWithPos),
79 /// Exclamation reference - generates JSR call (like uxnasm's '!' rune)
80 ExclamationRef(TokenWithPos),
81 /// Conditional block start (e.g., ?{) - lambda id for auto label
82 ConditionalBlockStart(TokenWithPos),
83 /// Conditional block end (e.g., }) - lambda id for auto label
84 ConditionalBlockEnd(TokenWithPos),
85 /// Lambda block start '{' (standalone, not '?{')
86 LambdaStart(TokenWithPos),
87 /// Lambda block end '}' corresponding to LambdaStart
88 LambdaEnd(TokenWithPos),
89 Eof,
90 Ignored, // Used for stray '}' after macro body or block
91}
92
93/// Parser for TAL assembly
94pub struct Parser {
95 tokens: Vec<TokenWithPos>,
96 position: usize,
97 line: usize,
98 position_in_line: usize,
99 path: String,
100 source: String,
101 brace_stack: Vec<BraceKind>, // track lambda vs conditional braces
102 macro_table: std::collections::HashSet<String>, // <-- Add macro table
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106enum BraceKind {
107 Conditional,
108 Lambda,
109}
110
111impl Parser {
112 pub fn new_with_source(tokens: Vec<TokenWithPos>, path: String, source: String) -> Self {
113 let line = tokens.first().map(|t| t.line).unwrap_or(1);
114 let position_in_line = tokens.first().map(|t| t.start_pos).unwrap_or(1);
115 // Build macro table from tokens
116 let mut macro_table = std::collections::HashSet::new();
117 for t in &tokens {
118 if let Token::MacroDef(ref name) = t.token {
119 macro_table.insert(name.clone());
120 }
121 }
122 Self {
123 tokens,
124 position: 0,
125 line,
126 position_in_line,
127 path,
128 source,
129 brace_stack: Vec::new(),
130 macro_table,
131 }
132 }
133
134 /// Parse tokens into AST nodes
135 pub fn parse(&mut self) -> Result<Vec<AstNode>> {
136 let mut nodes = Vec::new();
137
138 while !self.is_at_end() {
139 let tok_with_pos = self.current_token();
140 // Print path:line:start_pos-end_pos for every token
141 // println!(
142 // "{}:{}:{}-{} {:?}",
143 // self.path,
144 // tok_with_pos.line,
145 // tok_with_pos.start_pos,
146 // tok_with_pos.end_pos,
147 // tok_with_pos.token
148 // );
149 let token = &tok_with_pos.token;
150 match token {
151 Token::Newline => {
152 self.advance();
153 continue;
154 }
155 Token::Comment(_comment) => {
156 // println!("Comment({:?})", comment);
157 self.advance();
158 continue;
159 }
160 Token::BracketOpen | Token::BracketClose => {
161 // Completely ignore brackets - they're just ignored in uxnasm.c
162 self.advance();
163 continue;
164 }
165 _ => {
166 let node = self.parse_node()?;
167 // println!("{:?}", node);
168 nodes.push(node);
169 }
170 }
171 }
172
173 Ok(nodes)
174 }
175
176 fn parse_node(&mut self) -> Result<AstNode> {
177 // Robustly skip newlines before parsing a node
178 // Robustly skip newlines and comments before parsing a node
179 loop {
180 let token = &self.current_token().token;
181 match token {
182 Token::Newline | Token::Comment(_) | Token::BracketOpen | Token::BracketClose => {
183 self.advance();
184 continue;
185 }
186 _ => break,
187 }
188 }
189 let token = self.current_token().token.clone();
190 let path = if self.path.is_empty() {
191 "(input)".to_string()
192 } else {
193 self.path.clone()
194 };
195 let line = self.current_token().line;
196 let position = self.current_token().start_pos;
197 match token {
198 Token::Comment(_) => {
199 // Comments are already skipped in the main loop
200 self.advance();
201 Ok(AstNode::Ignored)
202 }
203 Token::Word(name) => {
204 // If the word is a macro name, treat it as a macro call
205
206 if self.is_macro_defined(&name) {
207 self.advance();
208 return Ok(AstNode::MacroCall(name.clone(), line, position));
209 }
210 // Otherwise, treat as a label reference (bare word)
211 let tok = self.current_token().clone();
212 self.advance();
213 // Check if label is defined in tokens
214 let label_defined = self.tokens.iter().any(|t| match &t.token {
215 Token::LabelDef(_, def_name) => def_name == &name,
216 _ => false,
217 });
218 if label_defined {
219 Ok(AstNode::LabelRef {
220 label: name.clone(),
221 rune: Rune::from(' '),
222 token: tok,
223 })
224 } else {
225 Err(AssemblerError::SyntaxError {
226 path: path.clone(),
227 line,
228 position,
229 message: format!("Label reference '{}' is not defined", name),
230 source_line: self.get_source_line(line),
231 })
232 }
233 }
234 Token::HexLiteral(hex) => {
235 let hex = hex.clone();
236 let value = self.parse_hex_literal(&hex)?;
237 let hex_len = hex.len();
238 self.advance();
239 if hex_len <= 2 {
240 Ok(AstNode::LiteralByte(value as u8))
241 } else {
242 Ok(AstNode::LiteralShort(value))
243 }
244 }
245 Token::RawHex(hex) => {
246 // Match uxnasm: 1–2 hex digits => byte, 3–4 hex digits => short.
247 let value = self.parse_hex_literal(&hex)?;
248 let hex_len = hex.len();
249 self.advance();
250 if hex_len <= 2 {
251 Ok(AstNode::Byte(value as u8))
252 } else {
253 // PATCH: always mask to 16 bits for >2 digits
254 Ok(AstNode::Short(value))
255 }
256 }
257 Token::DecLiteral(dec) => {
258 let line = self.current_token().line;
259 let position = self.current_token().start_pos;
260 let value = dec
261 .parse::<u16>()
262 .map_err(|_| AssemblerError::SyntaxError {
263 path: self.path.clone(),
264 line,
265 position,
266 message: format!("Invalid decimal literal: {}", dec),
267 source_line: self.get_source_line(line),
268 })?;
269 self.advance();
270 if value <= 255 {
271 Ok(AstNode::LiteralByte(value as u8))
272 } else {
273 Ok(AstNode::LiteralShort(value))
274 }
275 }
276 Token::BinLiteral(bin) => {
277 let line = self.current_token().line;
278 let position = self.current_token().start_pos;
279 let value =
280 u16::from_str_radix(&bin, 2).map_err(|_| AssemblerError::SyntaxError {
281 path: self.path.clone(),
282 line,
283 position,
284 message: format!("Invalid binary literal: {}", bin),
285 source_line: self.get_source_line(line),
286 })?;
287 self.advance();
288 if value <= 255 {
289 Ok(AstNode::LiteralByte(value as u8))
290 } else {
291 Ok(AstNode::LiteralShort(value))
292 }
293 }
294 Token::CharLiteral(ch) => {
295 let value = ch as u8;
296 self.advance();
297 Ok(AstNode::Byte(value))
298 }
299 Token::Instruction(inst) => {
300 let inst = inst.clone();
301 self.advance();
302 let ast_node = self.parse_instruction(inst)?;
303 if let AstNode::Instruction(instr) = ast_node {
304 Ok(AstNode::Instruction(instr))
305 } else {
306 Err(AssemblerError::SyntaxError {
307 path: self.path.clone(),
308 line: self.line,
309 position: self.position_in_line,
310 message: "Expected instruction node".to_string(),
311 source_line: self
312 .source
313 .lines()
314 .nth(self.line.saturating_sub(1))
315 .unwrap_or("")
316 .to_string(),
317 })
318 }
319 }
320 Token::LabelDef(rune, label) => {
321 // Avoid holding a reference to self across self.advance()
322 let label = label.clone();
323 self.advance();
324 // println!("DEBUG: Parsed label definition: @{}", label);
325 Ok(AstNode::LabelDef(rune, label))
326 }
327 Token::LabelRef(rune, label) => {
328 // Clone the values before advancing to avoid borrow checker issues
329 let label = label.to_string();
330 let tok = self.current_token().clone();
331 self.advance();
332 // println!(
333 // "LabelRef: label='{}', rune={:?}, token=({}:{}:{})",
334 // label, rune, self.path, tok.line, tok.start_pos
335 // );
336 Ok(AstNode::LabelRef {
337 label,
338 rune,
339 token: tok,
340 })
341 }
342 Token::SublabelDef(_) => {
343 let tok = self.current_token().clone();
344 self.advance();
345 Ok(AstNode::SublabelDef(tok))
346 }
347 Token::SublabelRef(_) => {
348 let tok = self.current_token().clone();
349 self.advance();
350 Ok(AstNode::SublabelRef(tok))
351 }
352 Token::RelativeRef(_) => {
353 let tok = self.current_token().clone();
354 self.advance();
355 Ok(AstNode::RelativeRef(tok))
356 }
357 Token::ConditionalRef(_) => {
358 let tok = self.current_token().clone();
359 self.advance();
360 Ok(AstNode::ConditionalRef(tok))
361 }
362
363 Token::DotRef(_) => {
364 let tok = self.current_token().clone();
365 self.advance();
366 Ok(AstNode::DotRef(tok))
367 }
368 Token::EqualsRef(label) => {
369 let tok = self.current_token().clone();
370 if label == "{" {
371 // Anonymous lambda: ={ ... }
372 self.advance();
373 self.brace_stack.push(BraceKind::Lambda);
374 Ok(AstNode::EqualsRef(tok))
375 } else {
376 self.advance();
377 Ok(AstNode::EqualsRef(tok))
378 }
379 }
380 Token::SemicolonRef(label) => {
381 let tok = self.current_token().clone();
382 if label == "{" {
383 // Anonymous lambda: ;{ ... }
384 self.advance();
385 self.brace_stack.push(BraceKind::Lambda);
386 Ok(AstNode::SemicolonRef(tok))
387 } else {
388 self.advance();
389 Ok(AstNode::SemicolonRef(tok))
390 }
391 }
392 Token::CommaRef(_) => {
393 let tok = self.current_token().clone();
394 self.advance();
395 Ok(AstNode::CommaRef(tok))
396 }
397 Token::UnderscoreRef(label) => {
398 let tok = self.current_token().clone();
399 if label == "{" {
400 // Anonymous lambda: _{ ... }
401 self.advance();
402 self.brace_stack.push(BraceKind::Lambda);
403 Ok(AstNode::UnderscoreRef(tok))
404 } else {
405 self.advance();
406 Ok(AstNode::UnderscoreRef(tok))
407 }
408 }
409 Token::QuestionRef(_) => {
410 let tok = self.current_token().clone();
411 self.advance();
412 Ok(AstNode::QuestionRef(tok))
413 }
414 Token::ExclamationRef(_) => {
415 let tok = self.current_token().clone();
416 self.advance();
417 Ok(AstNode::ExclamationRef(tok))
418 }
419 // Token::ConditionalOperator => {
420 // self.advance();
421 // // Skip any newlines or comments after '?'
422 // while matches!(
423 // &self.current_token().token,
424 // Token::Newline | Token::Comment(_)
425 // ) {
426 // self.advance();
427 // }
428 // &self.current_token().token
429 // // let next_token = &self.current_token().token;
430 // // match next_token {
431 // // _ => {
432 // // let line = self.current_token().line;
433 // // let position = self.current_token().start_pos;
434 // // Err(AssemblerError::SyntaxError {
435 // // path: self.path.clone(),
436 // // line,
437 // // position,
438 // // message: "Conditional operator '?' must be followed by a block"
439 // // .to_string(),
440 // // source_line: self.get_source_line(line),
441 // // })
442 // // }
443 // // }
444 // }
445 Token::RawAddressRef(_) => {
446 let tok = self.current_token().clone();
447 self.advance();
448 Ok(AstNode::RawAddressRef(tok))
449 }
450 Token::JSRRef(_) => {
451 let tok = self.current_token().clone();
452 self.advance();
453 Ok(AstNode::JSRRef(tok))
454 }
455 Token::HyphenRef(_) => {
456 let tok = self.current_token().clone();
457 self.advance();
458 Ok(AstNode::HyphenRef(tok))
459 }
460 Token::Padding(addr) => {
461 self.advance();
462 Ok(AstNode::Padding(addr))
463 }
464 Token::PaddingLabel(_) => {
465 let tok = self.current_token().clone();
466 // Check for empty or invalid label
467 if let Token::PaddingLabel(ref label) = tok.token {
468 if label.trim().is_empty() {
469 let line = tok.line;
470 let position = tok.start_pos;
471 return Err(AssemblerError::SyntaxError {
472 path: self.path.clone(),
473 line,
474 position,
475 message: "Expected identifier after '|' for padding label".to_string(),
476 source_line: self.get_source_line(line),
477 });
478 }
479 }
480 self.advance();
481 Ok(AstNode::PaddingLabel(tok))
482 }
483 Token::RelativePadding(count) => {
484 let c = count;
485 self.advance();
486 Ok(AstNode::RelativePadding(c))
487 }
488 Token::RelativePaddingLabel(_) => {
489 let tok = self.current_token().clone();
490 self.advance();
491 Ok(AstNode::RelativePaddingLabel(tok))
492 }
493 Token::MacroDef(name) => {
494 // Accept macro names like '=' for ={ ... } blocks
495 let name = name.clone();
496 // println!(
497 // "MacroDef: {} {}:{}:{}:{} {:?}",
498 // name,
499 // self.path,
500 // self.current_token().line,
501 // self.current_token().start_pos,
502 // self.current_token().end_pos,
503 // self.current_token().token
504 // );
505
506 self.advance();
507
508 while matches!(
509 &self.current_token().token,
510 Token::Comment(_) | Token::Newline
511 ) {
512 self.advance();
513 }
514 // println!(
515 // "MacroDef: {} {}:{}:{}:{} {:?}",
516 // name,
517 // self.path,
518 // self.current_token().line,
519 // self.current_token().start_pos,
520 // self.current_token().end_pos,
521 // self.current_token().token
522 // );
523
524 let mut body = Vec::new();
525 let mut depth = 1;
526 match &self.current_token().token {
527 Token::BraceOpen | Token::ConditionalBlockStart => {
528 // Accept either '{' or '?{' as block openers
529 self.advance();
530 while matches!(
531 &self.current_token().token,
532 Token::Comment(_) | Token::Newline
533 ) {
534 self.advance();
535 }
536 // println!(
537 // "DEBUG: Macro body starts at token: {:?}",
538 // self.current_token()
539 // );
540 while !self.is_at_end() && depth > 0 {
541 match &self.current_token().token {
542 Token::Comment(_)
543 | Token::Newline
544 | Token::BracketClose
545 | Token::BracketOpen => {
546 self.advance();
547 }
548 Token::BraceOpen | Token::ConditionalBlockStart => {
549 depth += 1;
550 // println!(
551 // "BraceOpen Inside macro '{}', depth = {}, token = {:?}",
552 // name,
553 // depth,
554 // self.current_token()
555 // );
556 body.push(self.parse_node()?);
557 // println!(
558 // "BraceOpen Inside macro '{}', depth = {}, token = {:?}",
559 // name,
560 // depth,
561 // self.current_token()
562 // );
563 }
564 Token::BraceClose => {
565 depth -= 1;
566 if depth == 0 {
567 // println!("BraceClose Inside macro '{}', depth = {}, token = {:?}", name, depth, self.current_token());
568 self.advance();
569 break;
570 } else {
571 // println!("BraceClose Inside macro '{}', depth = {}, token = {:?}", name, depth, self.current_token());
572 let n = self.parse_node()?;
573 // println!("BraceClose Inside macro '{}', depth = {}, token = {:?}", name, depth, self.current_token());
574 body.push(n);
575 }
576 }
577 Token::Eof => {
578 // println!(
579 // "Eof Inside macro '{}', depth = {}, token = {:?}",
580 // name,
581 // depth,
582 // self.current_token()
583 // );
584 break;
585 }
586 _ => {
587 // if matches!(self.current_token().token, Token::Eof) {
588 // break;
589 // }
590 // println!("Unexpected token inside macro '{}', depth = {}, token = {:?}", name, depth, self.current_token());
591 // Only parse and push if not a macro name
592 if let Token::Word(ref w) = self.current_token().token {
593 if self.is_macro_defined(w) {
594 // Don't expand macro calls inside macro definitions
595 self.advance();
596 continue;
597 }
598 }
599 body.push(self.parse_node()?);
600 }
601 }
602 }
603 if depth != 0 && self.current_token().token != Token::Eof {
604 let line = self.current_token().line;
605 let position = self.current_token().start_pos;
606 return Err(AssemblerError::SyntaxError {
607 path: self.path.clone(),
608 line,
609 position,
610 message: format!("Expected '}}' after macro body for macro '{}', depth={} token={:?}", name, depth, self.current_token()),
611 source_line: self.get_source_line(line),
612 });
613 }
614 Ok(AstNode::MacroDef(name, body))
615 }
616 Token::Eof => Ok(AstNode::Ignored),
617 _ => {
618 // println!(
619 // "DEBUG: Unexpected token after macro name: {:?}",
620 // self.current_token().token
621 // );
622 let line = self.current_token().line;
623 let position = self.current_token().start_pos;
624 Err(AssemblerError::SyntaxError {
625 path: self.path.clone(),
626 line,
627 position,
628 message: "Expected '{' after macro name".to_string(),
629 source_line: self.get_source_line(line),
630 })
631 }
632 }
633 }
634 Token::RawString(string) => {
635 let bytes = string.as_bytes().to_vec();
636 self.advance();
637 Ok(AstNode::RawString(bytes))
638 }
639 Token::Include(_) => {
640 println!(
641 "DEBUG: Include directive found: {:?}",
642 self.current_token().token
643 );
644 let tok = self.current_token().clone();
645 self.advance();
646 Ok(AstNode::Include(tok))
647 }
648 Token::ConditionalBlockStart => {
649 let tok = self.current_token().clone();
650 self.advance();
651 self.brace_stack.push(BraceKind::Conditional);
652 Ok(AstNode::ConditionalBlockStart(tok))
653 }
654 Token::BraceOpen => {
655 let tok = self.current_token().clone();
656 self.advance();
657 self.brace_stack.push(BraceKind::Lambda);
658 Ok(AstNode::LambdaStart(tok))
659 }
660 Token::BraceClose => {
661 let tok = self.current_token().clone();
662 self.advance();
663
664 // if self.brace_stack.is_empty() {
665 // // Ignore stray '}' after macro body or after macro block just closed
666 // return Ok(AstNode::Ignored);
667 // }
668 // if self.brace_stack.is_empty() {
669
670 // // Ignore stray '}' after macro body or after macro block just closed
671 // return Ok(AstNode::Ignored);
672
673 // eprintln!("Unmatched '}}' at line {}. Current macro table:", tok.line);
674 // for name in &self.macro_table {
675 // eprintln!("Macro '{}'", name);
676 // }
677 // eprintln!("Brace stack: {:?}", self.brace_stack);
678 // return Err(AssemblerError::SyntaxError {
679 // path: self.path.clone(),
680 // line: tok.line,
681 // position: tok.start_pos,
682 // message: "Unmatched '}' (no open '{' or '?{')".to_string(),
683 // source_line: self.get_source_line(tok.line),
684 // });
685 // }
686 Ok(match self.brace_stack.pop().unwrap_or(BraceKind::Lambda) {
687 BraceKind::Conditional => AstNode::ConditionalBlockEnd(tok),
688 BraceKind::Lambda => AstNode::LambdaEnd(tok),
689 })
690 }
691 Token::Eof => Ok(AstNode::Eof),
692 _ => {
693 let line = self.current_token().line;
694 let position = self.current_token().start_pos;
695 Err(AssemblerError::SyntaxError {
696 path: path.clone(),
697 line,
698 position,
699 message: format!("Unexpected token: {:?}", self.current_token().token),
700 source_line: self.get_source_line(line),
701 })
702 }
703 }
704 }
705
706 fn parse_instruction(&mut self, name: String) -> Result<AstNode> {
707 let opcodes = Opcodes::new();
708 let mut opcode;
709 let mut short_mode = false;
710 let mut return_mode = false;
711 let mut keep_mode = false;
712
713 // Debug output for tracing
714 // eprintln!("DEBUG: parse_instruction input name: '{}'", name);
715
716 // Always parse mode flags for all instructions, including LIT/LIT2/LIT2r/LITr
717 let mut base = name.as_str();
718 let mut mode_chars = String::new();
719 while let Some(last) = base.chars().last() {
720 if last == 'k' || last == 'r' || last == '2' {
721 mode_chars.insert(0, last);
722 base = &base[..base.len() - 1];
723 } else {
724 break;
725 }
726 }
727 // eprintln!("DEBUG: base after stripping flags: '{}', mode_chars: '{}'", base, mode_chars);
728
729 if opcodes.get_opcode(base).is_ok() {
730 opcode = base.to_string();
731 for c in mode_chars.chars() {
732 match c {
733 'k' => {
734 keep_mode = true;
735 // eprintln!("DEBUG: found 'k' flag, keep_mode = true");
736 }
737 'r' => {
738 return_mode = true;
739 // eprintln!("DEBUG: found 'r' flag, return_mode = true");
740 }
741 '2' => {
742 short_mode = true;
743 // eprintln!("DEBUG: found '2' flag, short_mode = true");
744 }
745 _ => {}
746 }
747 }
748 } else {
749 opcode = name.clone();
750 // eprintln!("DEBUG: base '{}' not found in opcode table, using original name '{}'", base, name);
751 }
752
753 // For LIT/LIT2/LITr/LIT2r, always use base "LIT" and set flags accordingly
754 if opcode == "LIT" || name.starts_with("LIT") {
755 // eprintln!("DEBUG: opcode is 'LIT' or starts with 'LIT', checking for '2' and 'r' in name '{}'", name);
756 if name.contains('2') {
757 short_mode = true;
758 // eprintln!("DEBUG: name contains '2', short_mode = true");
759 }
760 if name.contains('r') {
761 return_mode = true;
762 // eprintln!("DEBUG: name contains 'r', return_mode = true");
763 }
764 keep_mode = true;
765 // eprintln!("DEBUG: LIT always sets keep_mode = true");
766 opcode = "LIT".to_string();
767 }
768
769 // eprintln!(
770 // "DEBUG: parse_instruction result: opcode='{}', short_mode={}, return_mode={}, keep_mode={}",
771 // opcode, short_mode, return_mode, keep_mode
772 // );
773
774 Ok(AstNode::Instruction(Instruction {
775 opcode,
776 short_mode,
777 return_mode,
778 keep_mode,
779 }))
780 }
781
782 fn parse_hex_literal(&self, hex: &str) -> Result<u16> {
783 u16::from_str_radix(hex, 16).map_err(|_| AssemblerError::SyntaxError {
784 path: self.path.clone(),
785 line: self.line,
786 position: self.position_in_line,
787 message: format!("Invalid hexadecimal literal: {}", hex),
788 source_line: self
789 .source
790 .lines()
791 .nth(self.line - 1)
792 .unwrap_or("")
793 .to_string(),
794 })
795 }
796
797 fn current_token(&self) -> TokenWithPos {
798 self.tokens
799 .get(self.position)
800 .cloned()
801 .unwrap_or(TokenWithPos {
802 token: Token::Eof,
803 line: self.line,
804 start_pos: self.position_in_line,
805 end_pos: self.position_in_line,
806 scope: None, // <-- Add default scope
807 })
808 }
809
810 fn advance(&mut self) {
811 self.position += 1;
812 if let Some(tok) = self.tokens.get(self.position) {
813 self.line = tok.line;
814 self.position_in_line = tok.start_pos;
815 }
816 }
817
818 fn is_at_end(&self) -> bool {
819 self.position >= self.tokens.len()
820 }
821
822 fn get_source_line(&self, line: usize) -> String {
823 self.source
824 .lines()
825 .nth(line.saturating_sub(1))
826 .unwrap_or("")
827 .to_string()
828 }
829
830 /// Returns true if the macro name is defined in the macro table
831 fn is_macro_defined(&self, name: &str) -> bool {
832 self.macro_table.contains(name)
833 }
834}