math/
math.rs

1use std::env;
2use std::error;
3use std::io::{self, Read, Write};
4
5use text_scanner::Scanner;
6
7#[derive(PartialEq, Clone, Debug)]
8enum Token<'text> {
9    Ident(&'text str),
10    Int(i32),
11    Float(f32),
12    Sym(Sym),
13}
14
15#[derive(PartialEq, Clone, Copy, Debug)]
16enum Sym {
17    Plus,
18    Minus,
19    Star,
20    Slash,
21    LParen,
22    RParen,
23}
24
25impl<'text> Token<'text> {
26    fn parse_token(scanner: &mut Scanner<'text>) -> Result<Option<Self>, Box<dyn error::Error>> {
27        scanner.skip_whitespace();
28
29        if let Ok((first, _c)) = scanner.accept_if(|c| c.is_alphabetic() || (c == '_')) {
30            let (last, _s) = scanner.skip_while(|c| c.is_alphanumeric() || (c == '_'));
31            return Ok(Some(Self::Ident(&scanner.text()[first.start..last.end])));
32        }
33
34        if let Ok((first, _c)) = scanner.accept_if(|c| c.is_ascii_digit()) {
35            let (last, _s) = scanner.skip_while(|c| c.is_ascii_digit());
36
37            if scanner.accept_char('.').is_ok() {
38                let (last, _s) = scanner.skip_while(|c| c.is_ascii_digit());
39                let text = &scanner.text()[first.start..last.end];
40                let f = text.parse()?;
41                return Ok(Some(Self::Float(f)));
42            } else {
43                let text = &scanner.text()[first.start..last.end];
44                let f = text.parse()?;
45                return Ok(Some(Self::Int(f)));
46            }
47        }
48
49        if let Some(sym) = Sym::parse_token(scanner) {
50            return Ok(Some(Self::Sym(sym)));
51        }
52
53        Ok(None)
54    }
55}
56
57impl<'text> Sym {
58    fn parse_token(scanner: &mut Scanner<'text>) -> Option<Self> {
59        let (_r, c) = scanner
60            .accept_char_any(&['+', '-', '*', '/', '(', ')'])
61            .ok()?;
62        match c {
63            '+' => Some(Self::Plus),
64            '-' => Some(Self::Minus),
65            '*' => Some(Self::Star),
66            '/' => Some(Self::Slash),
67            '(' => Some(Self::LParen),
68            ')' => Some(Self::RParen),
69            _ => unreachable!(),
70        }
71    }
72}
73
74#[derive(Debug)]
75enum Expr<'text> {
76    Ident(&'text str),
77    Int(i32),
78    Float(f32),
79    Add(Box<Self>, Box<Self>),
80    Sub(Box<Self>, Box<Self>),
81    Mul(Box<Self>, Box<Self>),
82    Div(Box<Self>, Box<Self>),
83    Pos(Box<Self>),
84    Neg(Box<Self>),
85}
86
87impl<'text> Expr<'text> {
88    fn parse(text: &'text str) -> Result<Self, Box<dyn error::Error>> {
89        ExprParser::new(text).parse()
90    }
91}
92
93struct ExprParser<'text> {
94    scanner: Scanner<'text>,
95    next: Option<Token<'text>>,
96}
97
98impl<'text> ExprParser<'text> {
99    fn new(text: &'text str) -> Self {
100        Self {
101            scanner: Scanner::new(text),
102            next: None,
103        }
104    }
105
106    fn next_token(&mut self) -> Result<Option<Token<'text>>, Box<dyn error::Error>> {
107        if let Some(tok) = self.next.take() {
108            return Ok(Some(tok));
109        }
110        Token::parse_token(&mut self.scanner)
111    }
112
113    fn peek_token(&mut self) -> Result<Option<&Token<'text>>, Box<dyn error::Error>> {
114        if self.next.is_none() {
115            self.next = self.next_token()?;
116        }
117        Ok(self.next.as_ref())
118    }
119
120    fn parse(&mut self) -> Result<Expr<'text>, Box<dyn error::Error>> {
121        let expr = self.parse_expr()?;
122
123        if let Some(tok) = self.next_token()? {
124            return Err(format!("expected end of input, received {:?}", tok).into());
125        }
126
127        Ok(expr)
128    }
129
130    fn parse_expr(&mut self) -> Result<Expr<'text>, Box<dyn error::Error>> {
131        self.parse_expr_additive()
132    }
133
134    fn parse_expr_additive(&mut self) -> Result<Expr<'text>, Box<dyn error::Error>> {
135        let mut expr = self.parse_expr_multiplicative()?;
136
137        while let Some(&Token::Sym(op @ (Sym::Plus | Sym::Minus))) = self.peek_token()? {
138            _ = self.next_token(); // Eat `Token::Sym`
139
140            let rhs = self.parse_expr_multiplicative()?;
141
142            expr = match op {
143                Sym::Plus => Expr::Add(Box::new(expr), Box::new(rhs)),
144                Sym::Minus => Expr::Sub(Box::new(expr), Box::new(rhs)),
145                _ => unreachable!(),
146            };
147        }
148
149        Ok(expr)
150    }
151
152    fn parse_expr_multiplicative(&mut self) -> Result<Expr<'text>, Box<dyn error::Error>> {
153        let mut expr = self.parse_expr_unary()?;
154
155        while let Some(&Token::Sym(op @ (Sym::Star | Sym::Slash))) = self.peek_token()? {
156            _ = self.next_token(); // Eat `Token::Sym`
157
158            let rhs = self.parse_expr_multiplicative()?;
159
160            expr = match op {
161                Sym::Star => Expr::Mul(Box::new(expr), Box::new(rhs)),
162                Sym::Slash => Expr::Div(Box::new(expr), Box::new(rhs)),
163                _ => unreachable!(),
164            };
165        }
166
167        Ok(expr)
168    }
169
170    fn parse_expr_unary(&mut self) -> Result<Expr<'text>, Box<dyn error::Error>> {
171        if let Some(&Token::Sym(op @ (Sym::Plus | Sym::Minus))) = self.peek_token()? {
172            _ = self.next_token(); // Eat `Token::Sym`
173
174            let expr = self.parse_expr_unary()?;
175
176            match op {
177                Sym::Plus => Ok(Expr::Pos(Box::new(expr))),
178                Sym::Minus => Ok(Expr::Neg(Box::new(expr))),
179                _ => unreachable!(),
180            }
181        } else {
182            self.parse_expr_value()
183        }
184    }
185
186    fn parse_expr_value(&mut self) -> Result<Expr<'text>, Box<dyn error::Error>> {
187        let tok = self
188            .next_token()?
189            .ok_or_else(|| "unexpected end of input")?;
190        match tok {
191            Token::Ident(ident) => Ok(Expr::Ident(ident)),
192            Token::Int(i) => Ok(Expr::Int(i)),
193            Token::Float(f) => Ok(Expr::Float(f)),
194            Token::Sym(Sym::LParen) => {
195                let expr = self.parse_expr()?;
196
197                let tok = self
198                    .next_token()?
199                    .ok_or_else(|| "unexpected end of input")?;
200                if tok != Token::Sym(Sym::RParen) {
201                    return Err(format!("expected `)` found {:?}", tok).into());
202                }
203
204                Ok(expr)
205            }
206            _ => Err(format!("unexpected token {:?}", tok).into()),
207        }
208    }
209}
210
211impl<'text> Expr<'text> {
212    fn eval(&self) -> Result<f32, Box<dyn error::Error>> {
213        match self {
214            &Self::Ident(ident) => match ident {
215                "pi" | "PI" => Ok(std::f32::consts::PI),
216                "tau" | "TAU" => Ok(std::f32::consts::TAU),
217                _ => Err(format!("unknown ident `{}`, expected `pi` or `tau`", ident).into()),
218            },
219            &Self::Int(i) => Ok(i as f32),
220            &Self::Float(f) => Ok(f),
221            Self::Add(lhs, rhs) => Ok(lhs.eval()? + rhs.eval()?),
222            Self::Sub(lhs, rhs) => Ok(lhs.eval()? - rhs.eval()?),
223            Self::Mul(lhs, rhs) => Ok(lhs.eval()? * rhs.eval()?),
224            Self::Div(lhs, rhs) => Ok(lhs.eval()? / rhs.eval()?),
225            Self::Pos(operand) => Ok(operand.eval()?),
226            Self::Neg(operand) => Ok(-operand.eval()?),
227        }
228    }
229}
230
231fn main() {
232    eval_print_input("2 + 3");
233    eval_print_input("-(2 + 3)");
234    eval_print_input("-(2 + 3) * -5");
235    eval_print_input("3 * pi - tau");
236
237    let mut read_stdin = false;
238    let mut repl = false;
239
240    for arg in env::args().skip(1) {
241        if arg == "-i" {
242            repl = true;
243        } else if arg == "-" {
244            read_stdin = true;
245        } else {
246            eval_print_input(&arg);
247        }
248    }
249
250    if read_stdin {
251        println!();
252
253        let mut buf = String::new();
254        io::stdin().read_to_string(&mut buf).unwrap();
255        eval_print_input(&buf);
256    } else if repl {
257        println!();
258        println!("Enter `exit` or press Ctrl+C to exit repl");
259
260        let mut input = String::new();
261
262        loop {
263            print!("> ");
264            _ = io::stdout().flush();
265            input.clear();
266            io::stdin().read_line(&mut input).unwrap();
267            let input = input.trim();
268
269            if input.is_empty() {
270                continue;
271            } else if input == "exit" {
272                break;
273            }
274
275            eval(&input);
276        }
277    }
278}
279
280fn eval_print_input(expr: &str) {
281    println!("{}", expr);
282    eval(expr);
283}
284
285fn eval(expr: &str) {
286    match Expr::parse(expr) {
287        Ok(expr) => match expr.eval() {
288            Ok(val) => println!("= {}", val),
289            Err(err) => eprintln!("eval error: {}", err),
290        },
291        Err(err) => eprintln!("parse error: {}", err),
292    }
293}