Reworked parsing

master
Mark 2024-03-04 15:49:24 -08:00
parent a0ffc73333
commit a2a264163a
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
1 changed files with 275 additions and 54 deletions

View File

@ -1,26 +1,167 @@
use std::fmt::Display; use std::fmt::{Debug, Display};
use termion::color; use termion::color;
use crate::{Player, Symb}; use crate::{Player, Symb};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
enum Token { pub enum Token {
Number(f32), Value(String),
OpAdd, OpAdd,
OpSub, OpSub,
OpMult, OpMult,
OpDiv, OpDiv,
} }
impl Token { #[derive(PartialEq, Clone)]
fn val(&self) -> f32 { pub enum TreeElement {
Partial(String),
Number(f32),
Add {
l: Box<TreeElement>,
r: Box<TreeElement>,
},
Sub {
l: Box<TreeElement>,
r: Box<TreeElement>,
},
Mul {
l: Box<TreeElement>,
r: Box<TreeElement>,
},
Div {
l: Box<TreeElement>,
r: Box<TreeElement>,
},
Neg {
r: Box<TreeElement>,
},
}
impl Display for TreeElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Number(x) => *x, Self::Partial(s) => write!(f, "{s}")?,
_ => unreachable!(), Self::Number(n) => write!(f, "{n}")?,
Self::Add { l, r } => write!(f, "({l}+{r})")?,
Self::Div { l, r } => write!(f, "({l}÷{r})")?,
Self::Mul { l, r } => write!(f, "({l}×{r})")?,
Self::Sub { l, r } => write!(f, "({l}-{r})")?,
Self::Neg { r } => write!(f, "(-{r})")?,
}
Ok(())
}
}
impl Debug for TreeElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self, f)
}
}
enum InterTreeElement {
Unprocessed(Token),
Processed(TreeElement),
}
#[allow(dead_code)]
impl TreeElement {
pub fn left(&self) -> Option<&TreeElement> {
match self {
Self::Add { l, .. }
| Self::Sub { l, .. }
| Self::Mul { l, .. }
| Self::Div { l, .. } => Some(&**l),
_ => None,
}
}
pub fn right(&self) -> Option<&TreeElement> {
match self {
Self::Add { r, .. }
| Self::Neg { r, .. }
| Self::Sub { r, .. }
| Self::Mul { r, .. }
| Self::Div { r, .. } => Some(&**r),
_ => None,
}
}
pub fn left_mut(&mut self) -> Option<&mut TreeElement> {
match self {
Self::Add { l, .. }
| Self::Sub { l, .. }
| Self::Mul { l, .. }
| Self::Div { l, .. } => Some(&mut **l),
_ => None,
}
}
pub fn right_mut(&mut self) -> Option<&mut TreeElement> {
match self {
Self::Add { r, .. }
| Self::Neg { r, .. }
| Self::Sub { r, .. }
| Self::Mul { r, .. }
| Self::Div { r, .. } => Some(&mut **r),
_ => None,
} }
} }
} }
impl TreeElement {
pub fn evaluate(&self) -> Option<f32> {
match self {
Self::Number(x) => Some(*x),
Self::Partial(_) => None,
Self::Add { l, r } => {
let l = l.evaluate();
let r = r.evaluate();
if let (Some(l), Some(r)) = (l, r) {
Some(l + r)
} else {
None
}
}
Self::Mul { l, r } => {
let l = l.evaluate();
let r = r.evaluate();
if let (Some(l), Some(r)) = (l, r) {
Some(l * r)
} else {
None
}
}
Self::Div { l, r } => {
let l = l.evaluate();
let r = r.evaluate();
if let (Some(l), Some(r)) = (l, r) {
Some(l / r)
} else {
None
}
}
Self::Sub { l, r } => {
let l = l.evaluate();
let r = r.evaluate();
if let (Some(l), Some(r)) = (l, r) {
Some(l - r)
} else {
None
}
}
Self::Neg { r } => {
let r = r.evaluate();
if let Some(r) = r {
Some(-r)
} else {
None
}
}
}
}
}
#[derive(Clone)]
pub struct Board { pub struct Board {
board: [Option<(Symb, Player)>; 11], board: [Option<(Symb, Player)>; 11],
free_spots: usize, free_spots: usize,
@ -152,88 +293,159 @@ impl Board {
true true
} }
pub fn evaluate(&self) -> Option<f32> { pub fn tokenize(&self) -> Vec<Token> {
if !self.is_done() {
return None;
}
let mut tokens = Vec::new(); let mut tokens = Vec::new();
let mut is_neg = true; // if true, - is negative. if false, subtract. let mut is_neg = true; // if true, - is negative. if false, subtract.
let mut current_num = 0f32; let mut current_num = String::new();
let mut current_sgn = 1f32;
for (s, _) in self.board.iter().map(|x| x.unwrap()) { for s in self.board.iter().map(|x| x.map(|(s, _)| s)) {
match s { match s {
Symb::Div => { Some(Symb::Div) => {
tokens.push(Token::Number(current_num * current_sgn)); tokens.push(Token::Value(current_num.clone()));
current_num = 0.0; current_num.clear();
current_sgn = 1.0;
tokens.push(Token::OpDiv); tokens.push(Token::OpDiv);
is_neg = true; is_neg = true;
} }
Symb::Minus => { Some(Symb::Minus) => {
if is_neg { if is_neg {
current_sgn = -1.0; current_num = format!("-{}", current_num);
} else { } else {
tokens.push(Token::Number(current_num * current_sgn)); tokens.push(Token::Value(current_num.clone()));
current_num = 0.0; current_num.clear();
current_sgn = 1.0;
tokens.push(Token::OpSub); tokens.push(Token::OpSub);
is_neg = true; is_neg = true;
} }
} }
Symb::Plus => { Some(Symb::Plus) => {
tokens.push(Token::Number(current_num * current_sgn)); tokens.push(Token::Value(current_num.clone()));
current_num = 0.0; current_num.clear();
current_sgn = 1.0;
tokens.push(Token::OpAdd); tokens.push(Token::OpAdd);
is_neg = true; is_neg = true;
} }
Symb::Times => { Some(Symb::Times) => {
tokens.push(Token::Number(current_num * current_sgn)); tokens.push(Token::Value(current_num.clone()));
current_num = 0.0; current_num.clear();
current_sgn = 1.0;
tokens.push(Token::OpMult); tokens.push(Token::OpMult);
is_neg = true; is_neg = true;
} }
Symb::Zero => { Some(Symb::Zero) => {
current_num = current_num * 10.0; current_num.push('0');
is_neg = false; is_neg = false;
} }
Symb::Number(x) => { Some(Symb::Number(x)) => {
current_num = current_num * 10.0 + (x.get() as f32); current_num.push_str(&x.to_string());
is_neg = false;
}
None => {
current_num.push('_');
is_neg = false; is_neg = false;
} }
} }
} }
tokens.push(Token::Number(current_num * current_sgn)); tokens.push(Token::Value(current_num));
tokens
}
pub fn treeify(tokens: &Vec<Token>) -> TreeElement {
let mut tree: Vec<_> = tokens
.iter()
.map(|x| InterTreeElement::Unprocessed(x.clone()))
.collect();
let mut priority_level = 0; let mut priority_level = 0;
let mut did_something; let mut did_something;
while tokens.len() > 1 { while tree.len() > 1 {
did_something = false; did_something = false;
for i in 0..tokens.len() { for i in 0..tree.len() {
if match priority_level { if match priority_level {
0 => matches!(tokens[i], Token::OpMult | Token::OpDiv), 0 => matches!(
1 => matches!(tokens[i], Token::OpAdd | Token::OpSub), tree[i],
InterTreeElement::Unprocessed(Token::OpMult)
| InterTreeElement::Unprocessed(Token::OpDiv)
),
1 => matches!(
tree[i],
InterTreeElement::Unprocessed(Token::OpAdd)
| InterTreeElement::Unprocessed(Token::OpSub)
),
_ => false, _ => false,
} { } {
did_something = true; did_something = true;
let l = &tokens[i - 1]; let l = &tree[i - 1];
let r = &tokens[i + 1]; let r = &tree[i + 1];
let v = match tokens[i] { let l = match l {
Token::OpAdd => l.val() + r.val(), InterTreeElement::Processed(x) => x.clone(),
Token::OpDiv => l.val() / r.val(), InterTreeElement::Unprocessed(Token::Value(s)) => {
Token::OpSub => l.val() - r.val(), if s.starts_with('-') {
Token::OpMult => l.val() * r.val(), TreeElement::Neg {
r: {
if s.contains('_') {
Box::new(TreeElement::Partial(s[1..].to_string()))
} else {
Box::new(TreeElement::Number(s[1..].parse().unwrap()))
}
},
}
} else {
if s.contains('_') {
TreeElement::Partial(s.to_string())
} else {
TreeElement::Number(s.parse().unwrap())
}
}
}
_ => unreachable!(), _ => unreachable!(),
}; };
tokens.remove(i - 1); let r = match r {
tokens.remove(i - 1); InterTreeElement::Processed(x) => x.clone(),
tokens[i - 1] = Token::Number(v); InterTreeElement::Unprocessed(Token::Value(s)) => {
if s.starts_with('-') {
TreeElement::Neg {
r: {
if s.contains('_') {
Box::new(TreeElement::Partial(s[1..].to_string()))
} else {
Box::new(TreeElement::Number(s[1..].parse().unwrap()))
}
},
}
} else {
if s.contains('_') {
TreeElement::Partial(s.to_string())
} else {
TreeElement::Number(s.parse().unwrap())
}
}
}
_ => unreachable!(),
};
let v = match tree[i] {
InterTreeElement::Unprocessed(Token::OpAdd) => TreeElement::Add {
l: Box::new(l),
r: Box::new(r),
},
InterTreeElement::Unprocessed(Token::OpDiv) => TreeElement::Div {
l: Box::new(l),
r: Box::new(r),
},
InterTreeElement::Unprocessed(Token::OpMult) => TreeElement::Mul {
l: Box::new(l),
r: Box::new(r),
},
InterTreeElement::Unprocessed(Token::OpSub) => TreeElement::Sub {
l: Box::new(l),
r: Box::new(r),
},
_ => unreachable!(),
};
tree.remove(i - 1);
tree.remove(i - 1);
tree[i - 1] = InterTreeElement::Processed(v);
break; break;
} }
} }
@ -243,7 +455,14 @@ impl Board {
} }
} }
Some(tokens[0].val()) match tree.into_iter().next().unwrap() {
InterTreeElement::Processed(x) => x,
_ => unreachable!(),
}
}
pub fn evaluate(&self) -> Option<f32> {
Self::treeify(&self.tokenize()).evaluate()
} }
/// Hacky method to parse a board from a string /// Hacky method to parse a board from a string
@ -255,7 +474,9 @@ impl Board {
let x = s let x = s
.chars() .chars()
.filter_map(|c| { .filter_map(|c| {
if let Some(symb) = Symb::from_char(&c) { if c == '_' {
Some(None)
} else if let Some(symb) = Symb::from_char(&c) {
Some(Some((symb, current_player))) Some(Some((symb, current_player)))
} else { } else {
None None