use crate::token::Token;
use num_bigint::BigInt;
use num_rational::BigRational;
pub struct Lexer {
input: Vec<char>,
position: usize,
pub line: usize,
}
impl Lexer {
pub fn new(input: String) -> Self {
Self {
input: input.chars().collect(),
position: 0,
line: 1,
}
}
pub fn next_token(&mut self) -> Token {
self.skip_whitespace();
if self.position >= self.input.len() {
return Token::EOF;
}
let ch = self.input[self.position];
self.position += 1;
match ch {
'+' => Token::Plus,
'-' => Token::Minus,
'*' => Token::Star,
'/' => Token::Slash,
'>' => Token::GreaterThan,
'<' => Token::LessThan,
'=' => Token::Assign,
'{' => Token::LBrace,
'}' => Token::RBrace,
'(' => Token::LParen,
')' => Token::RParen,
'"' => self.read_string_literal(),
'0'..='9' | '.' => self.read_number(ch),
'a'..='z' | 'A'..='Z' | '_' => self.read_identifier(ch),
',' => Token::Comma,
'#' => {
while self.position < self.input.len() && self.input[self.position] != '\n' {
self.position += 1;
}
self.next_token()
}
'\n' => {
self.line += 1;
self.next_token()
}
_ => panic!("Unexpected character '{}' on line {}.", ch, self.line),
}
}
pub fn skip_whitespace(&mut self) {
while self.position < self.input.len() && self.input[self.position].is_whitespace() {
if self.input[self.position] == '\n' {
self.line += 1;
}
self.position += 1;
}
}
fn read_number(&mut self, first_char: char) -> Token {
let mut number = first_char.to_string();
let mut is_float = false;
while self.position < self.input.len() && (self.input[self.position].is_digit(10) || self.input[self.position] == '.') {
if self.input[self.position] == '.' {
is_float = true;
}
number.push(self.input[self.position]);
self.position += 1;
}
if is_float {
Token::Float(BigRational::from_float(number.parse::<f64>().unwrap()).unwrap())
} else {
Token::Float(BigRational::from_integer(number.parse::<BigInt>().unwrap()))
}
}
pub fn read_identifier(&mut self, first_char: char) -> Token {
let mut identifier = first_char.to_string();
while self.position < self.input.len() && (self.input[self.position].is_alphanumeric() || self.input[self.position] == '_') {
identifier.push(self.input[self.position]);
self.position += 1;
}
match identifier.as_str() {
"call" => Token::Call,
"print" => Token::Print,
"if" => Token::If,
"else" => Token::Else,
"dewpoint" => Token::DewPoint,
"ftoc" => Token::FToC,
"ctof" => Token::CToF,
"ctok" => Token::CToK,
"ktoc" => Token::KToC,
"ftok" => Token::FToK,
"ktof" => Token::KToF,
"pauli_x" => Token::PauliX,
"pauli_y" => Token::PauliY,
"pauli_z" => Token::PauliZ,
"hadamard" => Token::Hadamard,
"cnot" => Token::CNot,
"qubit" => Token::Qubit,
"toffoli" => Token::Toffoli,
"phase" => Token::Phase,
"t_gate" => Token::TGate,
"s_gate" => Token::SGate,
"fredkin" => Token::Fredkin,
"swap_qubits" => Token::SWAP,
"reset_qubit" => Token::ResetQubit,
"measure" => Token::MeasureQubit,
"fn" => Token::Function,
"import" => Token::Import,
"_pi_" => Token::Pi,
"_kelvin_" => Token::Kelvin,
"_rd_" => Token::RD,
"_cp_" => Token::CP,
"_p0_" => Token::P0,
"_lv_" => Token::LV,
"_cw_" => Token::CW,
"_rho_air_" => Token::RhoAir,
"_rho_water_" => Token::RhoWater,
"_g_" => Token::G,
_ => Token::Identifier(identifier),
}
}
pub fn read_string_literal(&mut self) -> Token {
let mut string = String::new();
while self.position < self.input.len() && self.input[self.position] != '"' {
string.push(self.input[self.position]);
self.position += 1;
}
self.position += 1; Token::StringLiteral(string)
}
}