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 {
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 {
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;
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; 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; }
}
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)
}
}