7318fea4-6106-4edc-99fa-d96d515f2133
use std::env;
/*
For amplify [amplify.connorcode.com]
github.com/basicprogrammer10/amplify
* Tokenize
* Parse AST
* Evaluate
# EX 1
5*6+3-1
|
/-\
/+\ 1
/*\ 3
5 6
# EX 2
7*2+9*5-6
|
/-\
/+\ 6
/ \
/*\ /*\
7 2 9 5
*/*/*/*/
// 61+21-1218+72-7274
fn main() {
for i in env::args().skip(1) {
let tokens = tokenize(&i);
let tree = create_tree(tokens);
let result = evaluate(tree);
println!("{result}");
}
}
#[derive(Debug, Clone)]
enum Token {
Number(i32),
Op(Ops),
Group(Vec<Token>),
Tree(Ops, Box<Token>, Box<Token>),
}
#[derive(Debug, Clone, Copy)]
enum Ops {
Add,
Sub,
Mul,
}
fn evaluate(tree: Token) -> i32 {
match tree {
Token::Tree(op, left, right) => {
let left = evaluate(*left);
let right = evaluate(*right);
match op {
Ops::Add => left + right,
Ops::Sub => left - right,
Ops::Mul => left * right,
}
}
Token::Number(n) => n,
_ => panic!("Invalid token"),
}
}
fn create_tree(mut tokens: Vec<Token>) -> Token {
fn get_max_prio(tokens: &[Token]) -> usize {
tokens
.iter()
.filter_map(|x| match x {
Token::Op(i) => Some(i.prio()),
_ => None,
})
.max()
.unwrap()
}
fn contains_non_tree(tokens: &[Token]) -> bool {
tokens
.iter()
.filter(|x| !matches!(x, Token::Tree(_, _, _)))
.count()
> 0
}
while contains_non_tree(&tokens) {
let max_prio = get_max_prio(&tokens);
let mut i = 0;
while i < tokens.len() {
if let Token::Op(op) = tokens[i] {
if op.prio() < max_prio {
i += 1;
continue;
}
let left = tokens.remove(i - 1).make_tree();
let right = tokens.remove(i).make_tree();
tokens[i - 1] = Token::Tree(op, Box::new(left), Box::new(right));
break;
}
i += 1;
}
}
debug_assert!(tokens.len() == 1);
tokens[0].clone()
}
fn tokenize(inp: &str) -> Vec<Token> {
let mut out = Vec::new();
let mut working = String::new();
let mut in_group = false;
for i in inp.chars() {
if !i.is_ascii_digit() && !working.is_empty() && !in_group {
out.push(Token::Number(working.parse().unwrap()));
working.clear();
}
match i {
// Groups
'(' => in_group = true,
')' => {
in_group = false;
out.push(Token::Group(tokenize(&working)));
working.clear();
}
i if in_group => working.push(i),
// Operations
'+' => out.push(Token::Op(Ops::Add)),
'-' => out.push(Token::Op(Ops::Sub)),
'*' => out.push(Token::Op(Ops::Mul)),
// Numbers
i if i.is_ascii_digit() => working.push(i),
_ => panic!("INVALID CHAR: {}", i),
}
}
if !working.is_empty() {
out.push(Token::Number(working.parse().unwrap()));
}
out
}
impl Ops {
fn prio(&self) -> usize {
match self {
Ops::Add | Ops::Sub => 1,
Ops::Mul => 2,
}
}
}
impl Token {
fn make_tree(self) -> Token {
match self {
Token::Group(tokens) => create_tree(tokens),
_ => self,
}
}
}