Files
daisy/src/parser/expression/expression.rs
2023-08-21 13:24:49 -07:00

188 lines
5.1 KiB
Rust

use std::collections::VecDeque;
use crate::quantity::Quantity;
use crate::context::Context;
use super::Operator;
use super::Constant;
use super::super::LineLocation;
/// Expressions represent logical objects in an expession.
#[derive(Debug)]
#[derive(Clone)]
pub enum Expression {
// Meaning of `LineLocation`:
//
// For Variables, Constants, Quantities, Tuples:
// If this expression was parsed, LineLocation is what part of the prompt was parsed to get this expression
// If this expression is the result of a calculation, LineLocaion is the sum of the LineLocations of
// all expressions used to make it. In other words, it points to the part of the prompt that was evaluated
// to get this new expression.
//
// For Operators:
// Linelocation points to the operator's position in the prompt.
// If this is a function, it points to the function name.
// If this is `+`, `!`, or etc, it points to that character.
// Operator arguments are NOT included in this linelocation.
//
//
// All the above rules are implemented when parsing and evaluating expressions.
Variable(LineLocation, String),
Quantity(LineLocation, Quantity),
Constant(LineLocation, Constant),
Operator(LineLocation, Operator, VecDeque<Expression>),
Tuple(LineLocation, VecDeque<Expression>),
}
impl Expression {
pub fn display(&self, context: &Context) -> String {
match self {
Expression::Quantity(_, v) => v.display(context),
Expression::Constant(_, c) => c.to_string(),
Expression::Variable(_, s) => s.clone(),
Expression::Operator(_, o,a) => o.display(context, a),
Expression::Tuple(_, v) => {
format!("({})",
v.iter()
.map(|x| x.display(context))
.collect::<Vec<String>>()
.join(", ")
)
}
}
}
}
impl Expression {
// This is called only when this is the outermost Expression.
// This sometimes leads to different--usually more verbose--behavior.
pub fn display_outer(&self, context: &Context) -> String {
match self {
Expression::Quantity(_, v) => v.display_outer(context),
Expression::Constant(_, c) => c.to_string(),
Expression::Variable(_, s) => s.clone(),
Expression::Operator(_, o,a) => o.display(context, a),
Expression::Tuple(_, v) => {
format!("({})",
v.iter()
.map(|x| x.display(context))
.collect::<Vec<String>>()
.join(", ")
)
}
}
}
pub fn is_quantity(&self) -> bool {
match self {
Expression::Quantity(_,_) => true,
_ => false
}
}
// True if this is a unitless integer
pub fn is_unitless_integer(&self) -> bool {
match self {
Expression::Quantity(_, q) => {
return q.unitless() && q.fract().is_zero();
},
Expression::Operator(_, Operator::Negative, q) => {
assert!(q.len() == 1);
let Expression::Quantity(_, q) = &q[0] else { return false };
return q.unitless() && q.fract().is_zero();
}
_ => { return false; }
}
}
// True if this is a integer power operator applied to a constant or variable.
// Examples: pi^2, x ^ 3
pub fn is_poly_power(&self) -> bool {
match self {
Expression::Operator(_, Operator::Power, a) => {
// Assuming len(a) = 2, which should be true in this case
assert!(a.len() == 2);
let base = &a[0];
let power = &a[1];
// Make sure base is const or variable
match base {
Expression::Constant(_, _)
| Expression::Variable(_, _)
=> {},
_ => { return false; }
};
// Make sure power is an integer
return power.is_unitless_integer();
},
_ => { return false; }
};
}
#[inline(always)]
pub fn get_args_mut(&mut self) -> Option<&mut VecDeque<Expression>> {
match self {
Expression::Operator(_, _, ref mut a) => Some(a),
Expression::Tuple(_, ref mut a) => Some(a),
_ => None
}
}
#[inline(always)]
pub fn get_args(&self) -> Option<&VecDeque<Expression>> {
match self {
Expression::Operator(_, _, ref a) => Some(a),
Expression::Tuple(_, ref a) => Some(a),
_ => None
}
}
#[inline(always)]
pub fn get_at_coords<'a, 'b, I>(&'a self, coords: I) -> Option<&'a Expression>
where I: IntoIterator<Item = &'b usize> + Sized {
let mut g = self;
for t in coords.into_iter() {
let args = g.get_args();
let Some(args) = args else { return None; };
g = &args[*t];
}
return Some(g);
}
#[inline(always)]
pub fn get_at_coords_mut<'a, 'b, I>(&'a mut self, coords: I) -> Option<&'a mut Expression>
where I: IntoIterator<Item = &'b usize> + Sized {
let mut g = self;
for t in coords.into_iter() {
let args = g.get_args_mut();
let Some(args) = args else { return None; };
g = &mut args[*t];
}
return Some(g);
}
pub fn get_linelocation(&self) -> LineLocation {
match self {
Expression::Quantity(l, _)
| Expression::Constant(l, _)
| Expression::Variable(l, _)
| Expression::Operator(l, _,_)
| Expression::Tuple(l, _)
=> { *l }
}
}
pub fn set_linelocation(&mut self, loc: &LineLocation) {
match self {
Expression::Quantity(l, _) => { *l = *loc },
Expression::Constant(l, _) => { *l = *loc },
Expression::Variable(l, _) => { *l = *loc },
Expression::Operator(l, _,_) => { *l = *loc },
Expression::Tuple(l, _) => { *l = *loc },
}
}
}