Skip to content

Instantly share code, notes, and snippets.

@bend-n
Created March 21, 2025 08:12
Show Gist options
  • Save bend-n/592552af3d74e2b4889dca22470f78f4 to your computer and use it in GitHub Desktop.
Save bend-n/592552af3d74e2b4889dca22470f78f4 to your computer and use it in GitHub Desktop.
use itertools::Itertools;
use std::{collections::VecDeque, fmt, io::Read};
struct P(Vec<Token>);
impl fmt::Display for P {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[rustfmt::skip]
macro_rules! w { ($x:expr) => {
match $x {
N(x) => x.fmt(f),
O(x) => write!(f, "{x}"),
}
}}
w!(*self.0.first().unwrap())?;
for token in &self.0 {
write!(f, " ")?;
w!(*token)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum Token {
N(f64),
O(char),
}
use Token::*;
#[derive(Debug)]
struct Lexer(VecDeque<Token>);
impl Lexer {
fn new(input: &str) -> Self {
Self(
input
.chars()
.filter(|x| !x.is_whitespace())
.chunk_by(|x| matches!(x, '0'..='9' | '.'))
.into_iter()
.flat_map(|(x, g)| match x {
true => vec![N(g.into_iter().collect::<String>().parse::<f64>().unwrap())],
false => g.into_iter().map(O).collect(),
})
.collect::<VecDeque<_>>(),
)
}
fn next(&mut self) -> Option<Token> {
self.0.pop_front()
}
fn peek(&mut self) -> Option<Token> {
self.0.front().copied()
}
}
// with matklads pratt
fn expr_bp(lexer: &mut Lexer, min_bp: u8) -> Vec<Token> {
let mut o = vec![];
match lexer.next() {
Some(x @ N(_)) => o.push(x),
Some(O('(')) => {
let lhs = expr_bp(lexer, 0);
assert_eq!(lexer.next(), Some(O(')')));
o.extend(lhs);
}
Some(O('-')) => {
let rhs = expr_bp(lexer, 5);
o.extend(rhs);
o.push(O('¯'));
}
t => panic!("bad token: {:?}", t),
};
loop {
let op = match lexer.peek() {
None => break,
Some(O(op)) => op,
t => panic!("bad token: {:?}", t),
};
let (l_bp, r_bp) = match op {
'+' | '-' => (1, 2),
'×' | '*' | '/' => (3, 4),
'^' => (6, 5),
_ => break,
};
if l_bp < min_bp {
break;
}
lexer.next();
let rhs = expr_bp(lexer, r_bp);
o.extend(rhs);
o.push(O(op))
}
o
}
fn exec(x: Vec<Token>) -> Option<f64> {
let mut stack = Vec::new();
macro_rules! apply {
($op:tt) => {{
let a = stack.pop()?;
let b = stack.pop()?;
stack.push(b $op a);
}};
}
for element in x {
match element {
N(x) => stack.push(x),
O('-') => apply!(-),
O('+') => apply!(+),
O('/') => apply!(/),
O('×' | '*') => apply!(*),
O('^') => {
let a = stack.pop()?;
let b = stack.pop()?;
stack.push(b.powf(a));
}
O('¯') => {
let x = stack.pop()?;
stack.push(-x)
}
_ => unreachable!(),
}
}
stack.pop()
}
fn main() {
println!("please input an expression. press ctrl + d when done.");
let mut x = String::with_capacity(50);
std::io::stdin().read_to_string(&mut x).unwrap();
let mut lexer = Lexer::new(&x);
println!("lex: {}", P(lexer.0.iter().copied().collect()));
let parsed = expr_bp(&mut lexer, 0);
println!("parse: {}", P(parsed.clone()));
println!("result: {}", exec(parsed).unwrap());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment