solveq/src/parse.rs
Florian Stecker 0b8bdf3da6 cleaned up
2024-08-28 12:31:28 -04:00

104 lines
2.6 KiB
Rust

use std::error::Error;
use egg::{RecExpr, Id, FromOp, FromOpError};
use crate::language::EquationLanguage;
pub fn parse_equation(input: &str) -> Result<RecExpr<EquationLanguage>, ParseError> {
let mut result: RecExpr<EquationLanguage> = Default::default();
parse_equation_inner(&input.replace(" ", ""), &mut result)?;
Ok(result)
}
// this is a very simple parser essentially copied from the technical interview
fn parse_equation_inner(input: &str, expr: &mut RecExpr<EquationLanguage>) -> Result<Id, ParseError> {
let mut level = 0;
let mut precedence = 1000; // 0 = '=', 1 = '+-', 2 = '*/', 3 = '^'
let mut operator_position: Option<usize> = None;
for (i,c) in input.chars().enumerate() {
if c == '(' {
level += 1;
} else if c == ')' {
level -= 1;
}
if level > 0 {
continue;
}
match c {
'^' if precedence >= 3 => {
operator_position = Some(i);
precedence = 3;
},
'*' | '/' if precedence >= 2 => {
operator_position = Some(i);
precedence = 2;
},
'-' | '+' if precedence >= 1 => {
operator_position = Some(i);
precedence = 1;
},
'=' => {
operator_position = Some(i);
precedence = 0;
},
_ => {},
}
}
// no top level operator => either primitive item or in parantheses
if let Some(operator_position) = operator_position {
if operator_position == 0 && input.starts_with("-") {
let inner = parse_equation_inner(&input[1 .. input.len()], expr)?;
let id = expr.add(EquationLanguage::from_op("-", vec![inner])?);
return Ok(id);
}
let left = parse_equation_inner(&input[0 .. operator_position], expr)?;
let right = parse_equation_inner(&input[operator_position+1 .. input.len()], expr)?;
let id = expr.add(EquationLanguage::from_op(
&input[operator_position .. operator_position + 1],
vec![left, right]
)?);
Ok(id)
} else {
if input.starts_with("(") && input.ends_with(")") {
// expression in parentheses
parse_equation_inner(&input[1..input.len()-1], expr)
} else {
// standalone integer
if input == "x" {
let id = expr.add(EquationLanguage::Unknown);
Ok(id)
} else {
input.parse::<i64>()
.map_err(|_|ParseError(format!("Failed conversion to i64: {}", &input)))?;
let id = expr.add(EquationLanguage::from_op(
input, vec![]
)?);
Ok(id)
}
}
}
}
#[derive(Debug)]
pub struct ParseError(String);
impl Error for ParseError {}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl From<FromOpError> for ParseError {
fn from(value: FromOpError) -> Self {
ParseError(format!("Error parsing {}", &value))
}
}