feat(lexer): add lexer

added basic lexer
This commit is contained in:
ozzy committed 2026-09-25 19:16:52 -05:00
1 parent acd053dfb5
commit 26e85652af
6 files changed
+650 -2

No files matched your search

+29 -2
View File
@@ -1,3 +1,30 @@
fn main() {
println!("Hello, world!");
use std::io::{self, IsTerminal, Write};
mod math;
fn main() -> io::Result<()> {
prompt()?;
for line in io::stdin().lines() {
if let Err(err) = line {
eprintln!("error reading line: {}", err);
break;
}
let line = line.unwrap();
let parsed = math::parse(line.trim());
println!("{:?}", parsed);
prompt()?;
}
Ok(())
}
fn prompt() -> io::Result<()> {
if io::stdin().is_terminal() {
print!("> ");
io::stdout().flush()?;
}
Ok(())
}
+106
View File
@@ -0,0 +1,106 @@
use std::str::FromStr;
use crate::math::types::Operator;
use rust_decimal::Decimal;
#[derive(Debug)]
pub enum Token {
Number(Decimal),
Identifier(String),
Operator(Operator),
OpenParen,
CloseParen,
Eof,
}
pub fn from(source: &str) -> Lexer<'_> {
Lexer::new(source)
}
pub struct Lexer<'a> {
chars: std::iter::Peekable<std::str::Chars<'a>>,
}
impl<'a> Lexer<'a> {
fn new(source: &'a str) -> Self {
Self {
chars: source.chars().peekable(),
}
}
fn skip_whitespace(&mut self) {
while let Some(&ch) = self.chars.peek() {
if ch.is_whitespace() {
self.chars.next();
continue;
}
break;
}
}
fn parse_number(&mut self, initial: char) -> Token {
let mut number = String::from(initial);
while let Some(&ch) = self.chars.peek() {
if ch.is_numeric() || ch == '.' {
number.push(ch);
self.chars.next();
continue;
}
break;
}
let number = Decimal::from_str(&number).expect("unable to parse number");
Token::Number(number)
}
fn parse_identifier(&mut self, initial: char) -> Token {
let mut identifier = String::from(initial);
while let Some(&ch) = self.chars.peek() {
if ch.is_alphabetic() {
identifier.push(ch);
self.chars.next();
continue;
}
break;
}
Token::Identifier(identifier)
}
pub fn next_token(&mut self) -> Token {
self.skip_whitespace();
match self.chars.next() {
Some('(') => Token::OpenParen,
Some(')') => Token::CloseParen,
Some('+') => Token::Operator(Operator::Add),
Some('-') => Token::Operator(Operator::Subtract),
Some('*') => Token::Operator(Operator::Multiply),
Some('/') => Token::Operator(Operator::Divide),
Some('%') => Token::Operator(Operator::Modulo),
Some('^') => Token::Operator(Operator::Power),
None => Token::Eof,
Some(ch) => {
if ch.is_numeric() || ch == '.' {
self.parse_number(ch)
} else if ch.is_alphabetic() {
self.parse_identifier(ch)
} else {
panic!("unexpected character: {}", ch);
}
}
}
}
}
impl<'a> Iterator for Lexer<'a> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
let token = self.next_token();
match token {
Token::Eof => None,
_ => Some(token),
}
}
}
+8
View File
@@ -0,0 +1,8 @@
mod lexer;
pub mod types;
pub fn parse(source: &str) -> Vec<lexer::Token> {
let lexer = lexer::from(source);
lexer.collect()
}
+21
View File
@@ -0,0 +1,21 @@
use rust_decimal::Decimal;
#[derive(Debug)]
pub enum Expression {
Number(Decimal),
Fraction(Box<Expression>, Box<Expression>),
Variable(String),
Operation(Operator, Box<Expression>, Box<Expression>),
Negative(Box<Expression>),
Function(String, Vec<Expression>),
}
#[derive(Debug)]
pub enum Operator {
Add,
Subtract,
Multiply,
Divide,
Power,
Modulo,
}