rs-asm6805 1.0.0

6805 Datalink Assembler in Rust
Documentation
use crate::utils::*;
use std::fmt;

#[derive(Debug, Clone, PartialEq)]
pub enum Token {
    Invalid,
    Const(i32),
    Symbol(String),
    Star,
    String(String),
    RShift,
    LShift,
    Plus,
    Minus,
    LParen,
    RParen,
    Not,
    Immed,
    Xor,
    And,
    Divide,
    Or,
    Comma,
    Colon,
    End,
}

impl fmt::Display for Token {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Token::Invalid => write!(f, "INVALID"),
            Token::Const(val) => write!(f, "CONST({})", val),
            Token::Symbol(name) => write!(f, "SYMBOL({})", name),
            Token::Star => write!(f, "STAR"),
            Token::String(s) => write!(f, "STRING(\"{}\")", s),
            Token::RShift => write!(f, "RSHIFT"),
            Token::LShift => write!(f, "LSHIFT"),
            Token::Plus => write!(f, "PLUS"),
            Token::Minus => write!(f, "MINUS"),
            Token::LParen => write!(f, "LPAREN"),
            Token::RParen => write!(f, "RPAREN"),
            Token::Not => write!(f, "NOT"),
            Token::Immed => write!(f, "IMMED"),
            Token::Xor => write!(f, "XOR"),
            Token::And => write!(f, "AND"),
            Token::Divide => write!(f, "DIVIDE"),
            Token::Or => write!(f, "OR"),
            Token::Comma => write!(f, "COMMA"),
            Token::Colon => write!(f, "COLON"),
            Token::End => write!(f, "END"),
        }
    }
}

pub struct Lexer {
    line: String,
    pos: usize,
    start: usize,
}

impl Lexer {
    pub fn new(line: String) -> Self {
        Self {
            line,
            pos: 0,
            start: 0,
        }
    }

    pub fn get_token(&mut self) -> Token {
        // Skip whitespace
        while self.pos < self.line.len() && is_space(self.current_char()) {
            self.pos += 1;
        }

        if self.pos >= self.line.len() {
            return Token::End;
        }

        self.start = self.pos;

        match self.current_char() {
            '\0' => Token::End,
            ';' => Token::End,
            ':' => {
                self.pos += 1;
                Token::Colon
            }
            '+' => {
                self.pos += 1;
                Token::Plus
            }
            '-' => {
                self.pos += 1;
                Token::Minus
            }
            '(' => {
                self.pos += 1;
                Token::LParen
            }
            ')' => {
                self.pos += 1;
                Token::RParen
            }
            '!' => {
                self.pos += 1;
                Token::Not
            }
            '#' => {
                self.pos += 1;
                Token::Immed
            }
            '^' => {
                self.pos += 1;
                Token::Xor
            }
            '&' => {
                self.pos += 1;
                Token::And
            }
            '/' => {
                self.pos += 1;
                Token::Divide
            }
            '|' => {
                self.pos += 1;
                Token::Or
            }
            ',' => {
                self.pos += 1;
                Token::Comma
            }
            '*' => {
                self.pos += 1;
                Token::Star
            }
            '>' => {
                if self.pos + 1 < self.line.len() && self.peek_char() == '>' {
                    self.pos += 2;
                    Token::RShift
                } else {
                    Token::Invalid
                }
            }
            '<' => {
                if self.pos + 1 < self.line.len() && self.peek_char() == '<' {
                    self.pos += 2;
                    Token::LShift
                } else {
                    Token::Invalid
                }
            }
            '\'' | '"' => self.parse_string(),
            '$' => self.parse_hex(),
            '0' => self.parse_number_or_hex(),
            '1'..='9' => self.parse_decimal(),
            c if is_csymf(c) => self.parse_symbol(),
            _ => Token::Invalid,
        }
    }

    fn current_char(&self) -> char {
        self.line.chars().nth(self.pos).unwrap_or('\0')
    }

    fn peek_char(&self) -> char {
        self.line.chars().nth(self.pos + 1).unwrap_or('\0')
    }

    fn parse_string(&mut self) -> Token {
        let quote_char = self.current_char();
        self.pos += 1;
        let mut result = String::new();

        loop {
            if self.pos >= self.line.len() {
                break;
            }

            let ch = self.current_char();
            if ch == quote_char {
                self.pos += 1;
                if self.pos < self.line.len() && self.current_char() == quote_char {
                    // Escaped quote
                    result.push(quote_char);
                    self.pos += 1;
                } else {
                    break;
                }
            } else {
                result.push(ch);
                self.pos += 1;
            }
        }

        Token::String(result)
    }

    fn parse_hex(&mut self) -> Token {
        self.pos += 1; // Skip '$'
        
        if self.pos >= self.line.len() || !is_xdigit(self.current_char()) {
            return Token::Invalid;
        }

        let mut value = 0i32;
        while self.pos < self.line.len() && is_xdigit(self.current_char()) {
            let digit_val = hex_digit_to_val(self.current_char());
            value = (value << 4) + digit_val as i32;
            self.pos += 1;
        }
        
        Token::Const(value)
    }

    fn parse_number_or_hex(&mut self) -> Token {
        if self.pos + 1 < self.line.len() {
            let next_char = self.peek_char();
            if next_char == 'x' || next_char == 'X' {
                self.pos += 2; // Skip '0x'
                if self.pos >= self.line.len() || !is_xdigit(self.current_char()) {
                    return Token::Invalid;
                }

                let mut value = 0i32;
                while self.pos < self.line.len() && is_xdigit(self.current_char()) {
                    let digit_val = hex_digit_to_val(self.current_char());
                    value = (value << 4) + digit_val as i32;
                    self.pos += 1;
                }
                return Token::Const(value);
            }
            if next_char == 'd' || next_char == 'D' {
                self.pos += 2; // Skip '0d'
            }
        }

        self.parse_decimal()
    }

    fn parse_decimal(&mut self) -> Token {
        let mut value = 0i32;
        while self.pos < self.line.len() && is_digit(self.current_char()) {
            let digit_val = hex_digit_to_val(self.current_char());
            value = value * 10 + digit_val as i32;
            self.pos += 1;
        }
        Token::Const(value)
    }

    fn parse_symbol(&mut self) -> Token {
        let mut result = String::new();
        while self.pos < self.line.len() && is_csym(self.current_char()) {
            result.push(self.current_char());
            self.pos += 1;
        }
        Token::Symbol(result)
    }
}