Changed inverter to instead move expressions to the LHS, like done when equations are solved in maths. Also made it multiply into parenthesis.

This commit is contained in:
PaddiM8 2020-06-14 19:23:02 +02:00
parent 1edec7d16e
commit 24771b3a5a
3 changed files with 131 additions and 41 deletions

View File

@ -2,6 +2,7 @@ use crate::ast::{Expr, Stmt};
use crate::lexer::TokenKind;
use crate::parser::CalcError;
use crate::parser::Unit;
use crate::parser::DECL_UNIT;
use crate::prelude;
use crate::symbol_table::SymbolTable;
use rug::ops::Pow;
@ -128,11 +129,11 @@ fn convert_unit(
to_unit: &str,
) -> Result<Float, CalcError> {
if let Some(Stmt::UnitDecl(_, _, unit_def)) =
context.symbol_table.get_unit(from_unit, to_unit).cloned()
context.symbol_table.get_unit(to_unit, from_unit).cloned()
{
context
.symbol_table
.insert(Stmt::VarDecl(String::from("u"), Box::new(expr.clone())));
.insert(Stmt::VarDecl(DECL_UNIT.into(), Box::new(expr.clone())));
eval_expr(context, &unit_def)
} else {

View File

@ -1,67 +1,114 @@
use crate::ast::{Expr, Stmt};
use crate::lexer::TokenKind;
use crate::parser::CalcError;
use crate::parser::DECL_UNIT;
use crate::symbol_table::SymbolTable;
impl Expr {
pub fn invert(&self, symbol_table: &mut SymbolTable) -> Result<Self, CalcError> {
match self {
Expr::Binary(left, op, right) => invert_binary(symbol_table, &left, op, &right),
Expr::Unary(op, expr) => invert_unary(op, &expr),
Expr::Unit(identifier, expr) => invert_unit(&identifier, &expr),
Expr::Var(_) => invert_value(self),
Expr::Group(expr) => invert_group(&expr),
let target_expr = Expr::Var(DECL_UNIT.into());
let result = invert(target_expr, symbol_table, self);
Ok(result?.0)
}
}
fn invert(
target_expr: Expr,
symbol_table: &mut SymbolTable,
expr: &Expr,
) -> Result<(Expr, Expr), CalcError> {
match expr {
Expr::Binary(left, op, right) => {
invert_binary(target_expr, symbol_table, &left, op, &right)
}
Expr::Unary(_, _) => Ok((target_expr, expr.clone())),
Expr::Unit(identifier, expr) => invert_unit(target_expr, &identifier, &expr),
Expr::Var(_) => Ok((target_expr, expr.clone())),
Expr::Group(expr) => Ok((target_expr, *expr.clone())),
Expr::FnCall(identifier, arguments) => {
invert_fn_call(symbol_table, &identifier, arguments)
}
Expr::Literal(_) => invert_value(self),
invert_fn_call(target_expr, symbol_table, &identifier, arguments)
}
Expr::Literal(_) => Ok((target_expr, expr.clone())),
}
}
fn invert_binary(
target_expr: Expr,
symbol_table: &mut SymbolTable,
left: &Expr,
op: &TokenKind,
right: &Expr,
) -> Result<Expr, CalcError> {
) -> Result<(Expr, Expr), CalcError> {
let op_inv = match op {
TokenKind::Plus => TokenKind::Minus,
TokenKind::Minus => TokenKind::Plus,
TokenKind::Star => TokenKind::Slash,
TokenKind::Slash => TokenKind::Star,
TokenKind::Star => {
if let Expr::Group(inside_group) = left {
return invert(
target_expr,
symbol_table,
&multiply_in(right, inside_group)?,
);
}
if let Expr::Group(inside_group) = right {
return invert(target_expr, symbol_table, &multiply_in(left, inside_group)?);
}
TokenKind::Slash
}
TokenKind::Slash => {
if let Expr::Group(inside_group) = left {
return invert(
target_expr,
symbol_table,
&Expr::Binary(inside_group.clone(), op.clone(), Box::new(right.clone())),
);
}
if let Expr::Group(inside_group) = right {
return invert(
target_expr,
symbol_table,
&Expr::Binary(Box::new(left.clone()), op.clone(), inside_group.clone()),
);
}
TokenKind::Star
}
_ => unreachable!(),
};
Ok(Expr::Binary(
Box::new(right.invert(symbol_table)?),
op_inv,
Box::new(left.invert(symbol_table)?),
))
if contains_the_unit(left) {
return Ok(invert(
Expr::Binary(Box::new(target_expr), op_inv, Box::new(right.clone())),
symbol_table,
left,
)?);
}
fn invert_unary(op: &TokenKind, expr: &Expr) -> Result<Expr, CalcError> {
Ok(match op {
TokenKind::Minus => expr.clone(),
TokenKind::Exclamation => unimplemented!(),
_ => unreachable!(),
})
Ok(invert(
Expr::Binary(Box::new(target_expr), op_inv, Box::new(left.clone())),
symbol_table,
right,
)?)
}
// Not necessary yet
fn invert_unit(_identifier: &str, _expr: &Expr) -> Result<Expr, CalcError> {
fn invert_unit(
_target_expr: Expr,
_identifier: &str,
_expr: &Expr,
) -> Result<(Expr, Expr), CalcError> {
unimplemented!()
}
fn invert_group(expr: &Expr) -> Result<Expr, CalcError> {
invert_value(expr)
}
fn invert_fn_call(
target_expr: Expr,
symbol_table: &mut SymbolTable,
identifier: &str,
arguments: &Vec<Expr>,
) -> Result<Expr, CalcError> {
) -> Result<(Expr, Expr), CalcError> {
let (parameters, body) =
if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() {
(parameters, body)
@ -85,9 +132,49 @@ fn invert_fn_call(
));
}
body.invert(symbol_table)
invert(target_expr, symbol_table, &body)
}
fn invert_value(expr: &Expr) -> Result<Expr, CalcError> {
Ok(Expr::Unary(TokenKind::Minus, Box::new(expr.clone())))
fn contains_the_unit(expr: &Expr) -> bool {
match expr {
Expr::Binary(left, _, right) => contains_the_unit(left) || contains_the_unit(right),
Expr::Unary(_, expr) => contains_the_unit(expr),
Expr::Unit(_, expr) => contains_the_unit(expr),
Expr::Var(identifier) => identifier == DECL_UNIT,
Expr::Group(expr) => contains_the_unit(expr),
Expr::FnCall(_, args) => {
for arg in args {
if contains_the_unit(arg) {
return true;
}
}
false
}
Expr::Literal(_) => false,
}
}
fn multiply_in(expr: &Expr, base_expr: &Expr) -> Result<Expr, CalcError> {
match base_expr {
Expr::Binary(left, op, right) => match op {
TokenKind::Plus | TokenKind::Minus => Ok(Expr::Binary(
Box::new(multiply_in(expr, &left)?),
op.clone(),
Box::new(multiply_in(expr, &right)?),
)),
TokenKind::Star | TokenKind::Slash => Ok(Expr::Binary(
Box::new(multiply_in(expr, &left)?),
op.clone(),
right.clone(),
)),
_ => unimplemented!(),
},
Expr::Literal(_) | Expr::Var(_) => Ok(Expr::Binary(
Box::new(expr.clone()),
TokenKind::Star,
Box::new(base_expr.clone()),
)),
_ => unimplemented!(),
}
}

View File

@ -6,6 +6,8 @@ use crate::{
};
use rug::Float;
pub const DECL_UNIT: &'static str = ".u";
/// Struct containing the current state of the parser. It stores user-defined functions and variables.
/// # Examples
/// ```
@ -20,7 +22,7 @@ pub struct Context {
symbol_table: SymbolTable,
angle_unit: Unit,
/// This is true whenever the parser is currently parsing a unit declaration.
/// It is necessary to keep track of this since you cannot use variables in unit declarations.
/// It is necessary to keep track of this in order to know when to find (figure out) units that haven't been defined yet.
/// Unit names are instead treated as variables.
parsing_unit_decl: bool,
/// When a unit declaration is being parsed, this value will be set
@ -346,11 +348,11 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
}
// Eg. x
if context.parsing_unit_decl {
context.unit_decl_base_unit = Some(identifier.value);
Ok(Expr::Var(String::from("u")))
} else if context.symbol_table.contains_var(&identifier.value) {
if context.symbol_table.contains_var(&identifier.value) {
Ok(Expr::Var(identifier.value))
} else if context.parsing_unit_decl {
context.unit_decl_base_unit = Some(identifier.value);
Ok(Expr::Var(DECL_UNIT.into()))
} else {
let mut chars = identifier.value.chars();
let mut left = Expr::Var(chars.next().unwrap().to_string());