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