Skip to main content

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}