2020-06-13 20:06:21 +02:00
|
|
|
use crate::ast::{Expr, Stmt};
|
2020-06-13 19:01:33 +02:00
|
|
|
use crate::lexer::TokenKind;
|
2020-06-13 22:18:37 +02:00
|
|
|
use crate::parser::CalcError;
|
2020-06-14 19:23:02 +02:00
|
|
|
use crate::parser::DECL_UNIT;
|
2020-06-13 20:06:21 +02:00
|
|
|
use crate::symbol_table::SymbolTable;
|
2020-06-13 19:01:33 +02:00
|
|
|
|
|
|
|
impl Expr {
|
2020-06-13 22:18:37 +02:00
|
|
|
pub fn invert(&self, symbol_table: &mut SymbolTable) -> Result<Self, CalcError> {
|
2020-06-14 19:23:02 +02:00
|
|
|
let target_expr = Expr::Var(DECL_UNIT.into());
|
|
|
|
let result = invert(target_expr, symbol_table, self);
|
2020-06-14 21:35:56 +02:00
|
|
|
|
2020-06-14 19:23:02 +02:00
|
|
|
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)
|
|
|
|
}
|
2020-06-14 21:35:56 +02:00
|
|
|
Expr::Unary(op, expr) => invert_unary(target_expr, op, &expr),
|
2020-06-14 19:23:02 +02:00
|
|
|
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(target_expr, symbol_table, &identifier, arguments)
|
2020-06-13 19:01:33 +02:00
|
|
|
}
|
2020-06-14 19:23:02 +02:00
|
|
|
Expr::Literal(_) => Ok((target_expr, expr.clone())),
|
2020-06-13 19:01:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 20:06:21 +02:00
|
|
|
fn invert_binary(
|
2020-06-14 19:23:02 +02:00
|
|
|
target_expr: Expr,
|
2020-06-13 20:06:21 +02:00
|
|
|
symbol_table: &mut SymbolTable,
|
|
|
|
left: &Expr,
|
|
|
|
op: &TokenKind,
|
|
|
|
right: &Expr,
|
2020-06-14 19:23:02 +02:00
|
|
|
) -> Result<(Expr, Expr), CalcError> {
|
2020-06-13 19:01:33 +02:00
|
|
|
let op_inv = match op {
|
|
|
|
TokenKind::Plus => TokenKind::Minus,
|
2020-06-14 21:35:56 +02:00
|
|
|
TokenKind::Minus => {
|
2020-06-14 21:54:39 +02:00
|
|
|
// Eg. a-(b+c)
|
|
|
|
// Multiply "-1" into the group, resulting in it becoming a normal expression. Then invert it normally.
|
2020-06-14 21:35:56 +02:00
|
|
|
if let Expr::Group(inside_group) = right {
|
|
|
|
return invert_binary(
|
|
|
|
target_expr,
|
|
|
|
symbol_table,
|
|
|
|
left,
|
|
|
|
op,
|
|
|
|
&multiply_in(&Expr::Literal(String::from("-1")), inside_group)?,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
TokenKind::Plus
|
|
|
|
}
|
2020-06-14 19:23:02 +02:00
|
|
|
TokenKind::Star => {
|
2020-06-14 21:54:39 +02:00
|
|
|
// If the left expression is a group, multiply the right expression into it, dissolving the group.
|
|
|
|
// It can then be inverted normally.
|
2020-06-14 19:23:02 +02:00
|
|
|
if let Expr::Group(inside_group) = left {
|
|
|
|
return invert(
|
|
|
|
target_expr,
|
|
|
|
symbol_table,
|
|
|
|
&multiply_in(right, inside_group)?,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-06-14 21:54:39 +02:00
|
|
|
// Same as above but left/right switched.
|
2020-06-14 19:23:02 +02:00
|
|
|
if let Expr::Group(inside_group) = right {
|
|
|
|
return invert(target_expr, symbol_table, &multiply_in(left, inside_group)?);
|
|
|
|
}
|
|
|
|
|
|
|
|
TokenKind::Slash
|
|
|
|
}
|
|
|
|
TokenKind::Slash => {
|
2020-06-14 21:54:39 +02:00
|
|
|
// Eg. (a+b)/c
|
|
|
|
// Just dissolve the group. Nothing more needs to be done mathematically.
|
2020-06-14 19:23:02 +02:00
|
|
|
if let Expr::Group(inside_group) = left {
|
|
|
|
return invert(
|
|
|
|
target_expr,
|
|
|
|
symbol_table,
|
|
|
|
&Expr::Binary(inside_group.clone(), op.clone(), Box::new(right.clone())),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-06-14 21:54:39 +02:00
|
|
|
// Eg. a/(b+c)
|
|
|
|
// Same as above.
|
2020-06-14 19:23:02 +02:00
|
|
|
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
|
|
|
|
}
|
2020-06-13 19:01:33 +02:00
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
2020-06-14 21:54:39 +02:00
|
|
|
// If the left expression contains the unit, invert the right one instead,
|
|
|
|
// since the unit should not be moved.
|
2020-06-14 19:23:02 +02:00
|
|
|
if contains_the_unit(left) {
|
|
|
|
return Ok(invert(
|
|
|
|
Expr::Binary(Box::new(target_expr), op_inv, Box::new(right.clone())),
|
|
|
|
symbol_table,
|
|
|
|
left,
|
|
|
|
)?);
|
|
|
|
}
|
2020-06-13 19:01:33 +02:00
|
|
|
|
2020-06-14 21:54:39 +02:00
|
|
|
// Otherwise, invert the left side.
|
2020-06-14 21:35:56 +02:00
|
|
|
let final_target_expr = Expr::Binary(Box::new(target_expr), op_inv, Box::new(left.clone()));
|
2020-06-14 19:23:02 +02:00
|
|
|
Ok(invert(
|
2020-06-14 21:54:39 +02:00
|
|
|
// Eg. 2-a
|
|
|
|
// If the operator is minus (and the left expression is being inverted),
|
|
|
|
// make the target expression negative to keep balance.
|
|
|
|
if let TokenKind::Minus = op {
|
2020-06-14 21:35:56 +02:00
|
|
|
Expr::Unary(TokenKind::Minus, Box::new(final_target_expr))
|
|
|
|
} else {
|
|
|
|
final_target_expr
|
|
|
|
},
|
2020-06-14 19:23:02 +02:00
|
|
|
symbol_table,
|
2020-06-14 21:54:39 +02:00
|
|
|
right, // Then invert the right expression.
|
2020-06-14 19:23:02 +02:00
|
|
|
)?)
|
2020-06-13 19:01:33 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:35:56 +02:00
|
|
|
fn invert_unary(target_expr: Expr, op: &TokenKind, expr: &Expr) -> Result<(Expr, Expr), CalcError> {
|
|
|
|
match op {
|
|
|
|
TokenKind::Minus => Ok((
|
|
|
|
Expr::Unary(TokenKind::Minus, Box::new(target_expr)),
|
|
|
|
expr.clone(),
|
|
|
|
)),
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 20:06:21 +02:00
|
|
|
// Not necessary yet
|
2020-06-14 19:23:02 +02:00
|
|
|
fn invert_unit(
|
|
|
|
_target_expr: Expr,
|
|
|
|
_identifier: &str,
|
|
|
|
_expr: &Expr,
|
|
|
|
) -> Result<(Expr, Expr), CalcError> {
|
2020-06-13 19:01:33 +02:00
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
2020-06-13 22:18:37 +02:00
|
|
|
fn invert_fn_call(
|
2020-06-14 19:23:02 +02:00
|
|
|
target_expr: Expr,
|
2020-06-13 22:18:37 +02:00
|
|
|
symbol_table: &mut SymbolTable,
|
|
|
|
identifier: &str,
|
|
|
|
arguments: &Vec<Expr>,
|
2020-06-14 19:23:02 +02:00
|
|
|
) -> Result<(Expr, Expr), CalcError> {
|
2020-06-13 20:06:21 +02:00
|
|
|
let (parameters, body) =
|
|
|
|
if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() {
|
|
|
|
(parameters, body)
|
|
|
|
} else {
|
2020-06-13 22:18:37 +02:00
|
|
|
return Err(CalcError::UndefinedFn(identifier.into()));
|
2020-06-13 20:06:21 +02:00
|
|
|
};
|
|
|
|
|
2020-06-13 22:18:37 +02:00
|
|
|
if parameters.len() != arguments.len() {
|
|
|
|
return Err(CalcError::IncorrectAmountOfArguments(
|
|
|
|
parameters.len(),
|
|
|
|
identifier.into(),
|
|
|
|
arguments.len(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2020-06-13 20:06:21 +02:00
|
|
|
let mut parameters_iter = parameters.iter();
|
|
|
|
for argument in arguments {
|
|
|
|
symbol_table.insert(Stmt::VarDecl(
|
|
|
|
parameters_iter.next().unwrap().to_string(),
|
|
|
|
Box::new(argument.clone()),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2020-06-14 19:23:02 +02:00
|
|
|
invert(target_expr, symbol_table, &body)
|
2020-06-13 19:01:33 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 19:23:02 +02:00
|
|
|
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!(),
|
|
|
|
}
|
2020-06-13 19:01:33 +02:00
|
|
|
}
|