Reworked parsing
parent
a0ffc73333
commit
a2a264163a
329
src/board.rs
329
src/board.rs
|
@ -1,26 +1,167 @@
|
|||
use std::fmt::Display;
|
||||
use std::fmt::{Debug, Display};
|
||||
use termion::color;
|
||||
|
||||
use crate::{Player, Symb};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Token {
|
||||
Number(f32),
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Token {
|
||||
Value(String),
|
||||
OpAdd,
|
||||
OpSub,
|
||||
OpMult,
|
||||
OpDiv,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
fn val(&self) -> f32 {
|
||||
#[derive(PartialEq, Clone)]
|
||||
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 {
|
||||
Self::Number(x) => *x,
|
||||
_ => unreachable!(),
|
||||
Self::Partial(s) => write!(f, "{s}")?,
|
||||
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 {
|
||||
board: [Option<(Symb, Player)>; 11],
|
||||
free_spots: usize,
|
||||
|
@ -152,88 +293,159 @@ impl Board {
|
|||
true
|
||||
}
|
||||
|
||||
pub fn evaluate(&self) -> Option<f32> {
|
||||
if !self.is_done() {
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens = Vec::new();
|
||||
let mut is_neg = true; // if true, - is negative. if false, subtract.
|
||||
let mut current_num = 0f32;
|
||||
let mut current_sgn = 1f32;
|
||||
let mut current_num = String::new();
|
||||
|
||||
for (s, _) in self.board.iter().map(|x| x.unwrap()) {
|
||||
for s in self.board.iter().map(|x| x.map(|(s, _)| s)) {
|
||||
match s {
|
||||
Symb::Div => {
|
||||
tokens.push(Token::Number(current_num * current_sgn));
|
||||
current_num = 0.0;
|
||||
current_sgn = 1.0;
|
||||
Some(Symb::Div) => {
|
||||
tokens.push(Token::Value(current_num.clone()));
|
||||
current_num.clear();
|
||||
tokens.push(Token::OpDiv);
|
||||
is_neg = true;
|
||||
}
|
||||
Symb::Minus => {
|
||||
Some(Symb::Minus) => {
|
||||
if is_neg {
|
||||
current_sgn = -1.0;
|
||||
current_num = format!("-{}", current_num);
|
||||
} else {
|
||||
tokens.push(Token::Number(current_num * current_sgn));
|
||||
current_num = 0.0;
|
||||
current_sgn = 1.0;
|
||||
tokens.push(Token::Value(current_num.clone()));
|
||||
current_num.clear();
|
||||
tokens.push(Token::OpSub);
|
||||
is_neg = true;
|
||||
}
|
||||
}
|
||||
Symb::Plus => {
|
||||
tokens.push(Token::Number(current_num * current_sgn));
|
||||
current_num = 0.0;
|
||||
current_sgn = 1.0;
|
||||
Some(Symb::Plus) => {
|
||||
tokens.push(Token::Value(current_num.clone()));
|
||||
current_num.clear();
|
||||
tokens.push(Token::OpAdd);
|
||||
is_neg = true;
|
||||
}
|
||||
Symb::Times => {
|
||||
tokens.push(Token::Number(current_num * current_sgn));
|
||||
current_num = 0.0;
|
||||
current_sgn = 1.0;
|
||||
Some(Symb::Times) => {
|
||||
tokens.push(Token::Value(current_num.clone()));
|
||||
current_num.clear();
|
||||
tokens.push(Token::OpMult);
|
||||
is_neg = true;
|
||||
}
|
||||
Symb::Zero => {
|
||||
current_num = current_num * 10.0;
|
||||
Some(Symb::Zero) => {
|
||||
current_num.push('0');
|
||||
is_neg = false;
|
||||
}
|
||||
Symb::Number(x) => {
|
||||
current_num = current_num * 10.0 + (x.get() as f32);
|
||||
Some(Symb::Number(x)) => {
|
||||
current_num.push_str(&x.to_string());
|
||||
is_neg = false;
|
||||
}
|
||||
None => {
|
||||
current_num.push('_');
|
||||
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 did_something;
|
||||
while tokens.len() > 1 {
|
||||
while tree.len() > 1 {
|
||||
did_something = false;
|
||||
for i in 0..tokens.len() {
|
||||
for i in 0..tree.len() {
|
||||
if match priority_level {
|
||||
0 => matches!(tokens[i], Token::OpMult | Token::OpDiv),
|
||||
1 => matches!(tokens[i], Token::OpAdd | Token::OpSub),
|
||||
0 => matches!(
|
||||
tree[i],
|
||||
InterTreeElement::Unprocessed(Token::OpMult)
|
||||
| InterTreeElement::Unprocessed(Token::OpDiv)
|
||||
),
|
||||
1 => matches!(
|
||||
tree[i],
|
||||
InterTreeElement::Unprocessed(Token::OpAdd)
|
||||
| InterTreeElement::Unprocessed(Token::OpSub)
|
||||
),
|
||||
_ => false,
|
||||
} {
|
||||
did_something = true;
|
||||
let l = &tokens[i - 1];
|
||||
let r = &tokens[i + 1];
|
||||
let l = &tree[i - 1];
|
||||
let r = &tree[i + 1];
|
||||
|
||||
let v = match tokens[i] {
|
||||
Token::OpAdd => l.val() + r.val(),
|
||||
Token::OpDiv => l.val() / r.val(),
|
||||
Token::OpSub => l.val() - r.val(),
|
||||
Token::OpMult => l.val() * r.val(),
|
||||
let l = match l {
|
||||
InterTreeElement::Processed(x) => x.clone(),
|
||||
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!(),
|
||||
};
|
||||
|
||||
tokens.remove(i - 1);
|
||||
tokens.remove(i - 1);
|
||||
tokens[i - 1] = Token::Number(v);
|
||||
let r = match r {
|
||||
InterTreeElement::Processed(x) => x.clone(),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -255,7 +474,9 @@ impl Board {
|
|||
let x = s
|
||||
.chars()
|
||||
.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)))
|
||||
} else {
|
||||
None
|
||||
|
|
Loading…
Reference in New Issue