1use crate::{
14 aim::{ContextLike, Writer, WriterLike},
15 expressions::{
16 ExpressionLike, Multiplicative, Primary, evaluate_and_promote, parse_multiplicative,
17 },
18 literals::Literal,
19 values::Value,
20};
21use anyhow::{Result, anyhow};
22use nom::{
23 IResult, Parser,
24 branch::alt,
25 character::complete::{char, multispace0},
26 multi::many0,
27};
28use std::{
29 fmt,
30 sync::Arc,
31};
32
33#[derive(Debug, Clone, PartialEq)]
40pub enum Additive {
41 Add(Box<Additive>, Multiplicative),
42 Subtract(Box<Additive>, Multiplicative),
43 Primary(Box<Primary>),
45}
46
47impl Additive {
48 pub fn print(&self, writer: &mut Writer) {
49 match self {
50 Additive::Add(left, right) => {
51 writer.write_binary_op(left.as_ref(), " + ", right);
52 }
53 Additive::Subtract(left, right) => {
54 writer.write_binary_op(left.as_ref(), " - ", right);
55 }
56 Additive::Primary(primary) => primary.print(writer),
57 }
58 }
59}
60
61pub fn parse_additive(input: &str) -> IResult<&str, Additive> {
68 let (input, first) = parse_multiplicative(input)?;
69 let (input, rest) = many0((
70 multispace0,
71 alt((char('+'), char('-'))),
72 multispace0,
73 parse_multiplicative,
74 ))
75 .parse(input)?;
76
77 let result = rest.into_iter().fold(
79 match first {
80 Multiplicative::Primary(primary) => Additive::Primary(primary),
81 _ => Additive::Primary(Box::new(Primary::Multiplicative(first))),
82 },
83 |acc, (_, op, _, next)| match op {
84 '+' => Additive::Add(Box::new(acc), next),
85 '-' => Additive::Subtract(Box::new(acc), next),
86 _ => unreachable!(),
87 },
88 );
89
90 Ok((input, result))
91}
92
93impl ExpressionLike for Additive {
94 fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
95 match self {
96 Additive::Add(left, right) => {
97 let (left_val, right_val) = evaluate_and_promote(context, left.as_ref(), right)?;
98 if left_val.is_error() {
99 return Ok(left_val);
100 }
101 if right_val.is_error() {
102 return Ok(right_val);
103 }
104 match (&*left_val, &*right_val) {
106 (Value::Literal(Literal::Bool(l)), Value::Literal(Literal::Bool(r))) => {
107 Ok(Arc::new(Value::Literal(Literal::Bool(l ^ r))))
108 }
109 (Value::Literal(Literal::Number(l)), Value::Literal(Literal::Number(r))) => {
110 Ok(Arc::new(Value::Literal(Literal::Number(l + r))))
111 }
112 (Value::Literal(Literal::Text(l)), Value::Literal(Literal::Text(r))) => {
113 Ok(Arc::new(Value::Literal(Literal::Text(Arc::from(format!("{}{}", l, r))))))
114 }
115 _ => Err(anyhow!(
116 "Expected Bool, Number, or Text, found {} + {}~{}",
117 left_val.type_as_string(),
118 right_val.type_as_string(),
119 self.to_formula(),
120 )),
121 }
122 }
123 Additive::Subtract(left, right) => {
124 let (left_val, right_val) = evaluate_and_promote(context, left.as_ref(), right)?;
125 if left_val.is_error() {
126 return Ok(left_val);
127 }
128 if right_val.is_error() {
129 return Ok(right_val);
130 }
131 match (&*left_val, &*right_val) {
133 (Value::Literal(Literal::Bool(l)), Value::Literal(Literal::Bool(r))) => {
134 Ok(Arc::new(Value::Literal(Literal::Bool(!(l ^ r)))))
135 }
136 (Value::Literal(Literal::Number(l)), Value::Literal(Literal::Number(r))) => {
137 Ok(Arc::new(Value::Literal(Literal::Number(l - r))))
138 }
139 (Value::Literal(Literal::Text(l)), Value::Literal(Literal::Text(r))) => {
140 if r.is_empty() {
141 return Ok(Arc::new(Value::Literal(Literal::Text(l.clone()))));
142 }
143 if let Some(pos) = l.rfind(r.as_ref()) {
144 let mut result = String::with_capacity(l.len() - r.len());
145 result.push_str(&l[..pos]);
146 result.push_str(&l[pos + r.len()..]);
147 Ok(Arc::new(Value::Literal(Literal::Text(Arc::from(result)))))
148 } else {
149 Ok(Arc::new(Value::Literal(Literal::Text(l.clone()))))
150 }
151 }
152 _ => Err(anyhow!(
153 "Expected Bool, Number, or Text, found {} - {}~{}",
154 left_val.type_as_string(),
155 right_val.type_as_string(),
156 self.to_formula(),
157 )),
158 }
159 }
160 Additive::Primary(primary) => primary.evaluate(context),
161 }
162 }
163
164 fn to_formula(&self) -> String {
166 let mut writer = Writer::formulizer();
167 self.print(&mut writer);
168 writer.finish()
169 }
170}
171
172impl WriterLike for Additive {
173 fn write(&self, writer: &mut Writer) {
174 self.print(writer);
175 }
176}
177
178impl fmt::Display for Additive {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 write!(f, "{}", self.to_stringized())
181 }
182}