1use lalrpop_util::lalrpop_mod;
2use num_runtime_fmt::NumFmt;
3
4use crate::{
5 types::{Calcable, CalcableError},
6 Context,
7};
8
9lalrpop_mod!(#[allow(clippy::all)] pub parser);
11
12#[derive(Debug, thiserror::Error)]
14pub enum ParseError {
15 #[error("index must fit into usize")]
16 Index(#[source] std::num::ParseIntError),
17 #[error("failed to parse format string")]
18 Format(#[from] num_runtime_fmt::parse::Error),
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum PrefixOperator {
24 Negation,
25 Not,
26}
27
28impl PrefixOperator {
29 fn evaluate<N: Calcable>(&self, operand: N) -> Result<N, <N as Calcable>::Err> {
30 match self {
31 Self::Negation => operand.neg().ok_or_else(|| N::Err::unimplemented("-")),
32 Self::Not => operand.not().ok_or_else(|| N::Err::unimplemented("!")),
33 }
34 }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum InfixOperator {
40 Add,
41 Sub,
42 Mul,
43 Div,
44 TruncDiv,
45 Pow,
46 Rem,
47 Lshift,
48 Rshift,
49 RotateL,
50 RotateR,
51 BitAnd,
52 BitOr,
53 BitXor,
54}
55
56impl InfixOperator {
57 fn evaluate<N: Calcable>(&self, left: N, right: N) -> Result<N, <N as Calcable>::Err> {
58 match self {
59 Self::Add => <N as Calcable>::add(left, right),
60 Self::Sub => <N as Calcable>::sub(left, right),
61 Self::Mul => <N as Calcable>::mul(left, right),
62 Self::Div => <N as Calcable>::div(left, right),
63 Self::TruncDiv => left.trunc_div(right),
64 Self::Pow => left.pow(right),
65 Self::Rem => left.rem(right),
66 Self::Lshift => left.shl(right),
67 Self::Rshift => left.shr(right),
68 Self::RotateL => left.rotate_left(right),
69 Self::RotateR => left.rotate_right(right),
70 Self::BitAnd => left
71 .bit_and(right)
72 .ok_or_else(|| N::Err::unimplemented("&")),
73 Self::BitOr => left.bit_or(right).ok_or_else(|| N::Err::unimplemented("|")),
74 Self::BitXor => left
75 .bit_xor(right)
76 .ok_or_else(|| N::Err::unimplemented("^")),
77 }
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum Function {
84 Abs,
85 Ceil,
86 Floor,
87 Round,
88 Sin,
89 Cos,
90 Tan,
91 Sinh,
92 Cosh,
93 Tanh,
94 Asin,
95 Acos,
96 Atan,
97 Asinh,
98 Acosh,
99 Atanh,
100 Rad,
101 Deg,
102 Sqrt,
103 Cbrt,
104 Log,
105 Lg,
106 Ln,
107 Exp,
108}
109
110impl Function {
111 fn evaluate<N: Calcable>(&self, operand: N) -> Result<N, <N as Calcable>::Err> {
112 let (result, symbol) = match self {
113 Self::Abs => (operand.abs(), "abs"),
114 Self::Ceil => (operand.ceil(), "ceil"),
115 Self::Floor => (operand.floor(), "floor"),
116 Self::Round => (operand.round(), "round"),
117 Self::Sin => (operand.sin(), "sin"),
118 Self::Cos => (operand.cos(), "cos"),
119 Self::Tan => (operand.tan(), "tan"),
120 Self::Sinh => (operand.sinh(), "sinh"),
121 Self::Cosh => (operand.cosh(), "cosh"),
122 Self::Tanh => (operand.tanh(), "tanh"),
123 Self::Asin => (operand.asin(), "asin"),
124 Self::Acos => (operand.acos(), "acos"),
125 Self::Atan => (operand.atan(), "atan"),
126 Self::Asinh => (operand.asinh(), "asinh"),
127 Self::Acosh => (operand.acosh(), "acosh"),
128 Self::Atanh => (operand.atanh(), "atanh"),
129 Self::Rad => (operand.rad(), "rad"),
130 Self::Deg => (operand.deg(), "deg"),
131 Self::Sqrt => (operand.sqrt(), "sqrt"),
132 Self::Cbrt => (operand.cbrt(), "cbrt"),
133 Self::Log => (operand.log(), "log"),
134 Self::Lg => (operand.lg(), "lg"),
135 Self::Ln => (operand.ln(), "ln"),
136 Self::Exp => (operand.exp(), "exp"),
137 };
138 result.ok_or_else(|| N::Err::unimplemented(symbol))
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub enum Constant {
145 E,
146 Pi,
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum HistoryIndexKind {
155 Relative,
156 Absolute,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum Term<'input> {
162 Literal(&'input str),
163 HexLiteral(&'input str),
164 OctLiteral(&'input str),
165 BinLiteral(&'input str),
166 Constant(Constant),
167 History(HistoryIndexKind, usize),
168}
169
170impl<'input> Term<'input> {
171 fn evaluate<N: Calcable>(&self, ctx: &Context<N>) -> Result<N, <N as Calcable>::Err> {
172 match self {
173 Self::Literal(s) => N::parse_decimal(s),
174 Self::HexLiteral(s) => N::parse_hex(s),
175 Self::OctLiteral(s) => N::parse_octal(s),
176 Self::BinLiteral(s) => N::parse_binary(s),
177 Self::Constant(Constant::E) => N::E.ok_or_else(|| N::Err::unimplemented("e")),
178 Self::Constant(Constant::Pi) => N::PI.ok_or_else(|| N::Err::unimplemented("pi")),
179 Self::History(kind, idx) => {
180 let real_idx = match kind {
181 HistoryIndexKind::Absolute => *idx,
182 HistoryIndexKind::Relative => {
183 ctx.history.len().checked_sub(*idx).ok_or_else(|| {
184 N::Err::history_out_of_bounds(*kind, *idx, ctx.history.len())
185 })?
186 }
187 };
188 match ctx.history.get(real_idx) {
189 Some(n) => Ok(n.clone()),
190 None => Err(N::Err::history_out_of_bounds(
191 *kind,
192 *idx,
193 ctx.history.len(),
194 )),
195 }
196 }
197 }
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
203pub enum Expr<'input> {
204 Term(Term<'input>),
205 Prefix(PrefixOperator, Box<Expr<'input>>),
206 Infix(Box<Expr<'input>>, InfixOperator, Box<Expr<'input>>),
207 Func(Function, Box<Expr<'input>>),
208 Group(Box<Expr<'input>>),
209}
210
211impl<'input> Expr<'input> {
212 pub(crate) fn evaluate<N: Calcable>(
214 &self,
215 ctx: &Context<N>,
216 ) -> Result<N, <N as Calcable>::Err> {
217 match self {
218 Self::Term(term) => term.evaluate(ctx),
219 Self::Prefix(prefix, expr) => prefix.evaluate(expr.evaluate(ctx)?),
220 Self::Infix(left, infix, right) => {
221 infix.evaluate(left.evaluate(ctx)?, right.evaluate(ctx)?)
222 }
223 Self::Func(func, expr) => func.evaluate(expr.evaluate(ctx)?),
224 Self::Group(expr) => expr.evaluate(ctx),
225 }
226 }
227}
228
229#[derive(Debug, thiserror::Error)]
231pub enum AnnotatedError<N>
232where
233 N: std::fmt::Debug + Calcable,
234 <N as Calcable>::Err: 'static,
235{
236 #[error(transparent)]
237 Calculation(<N as Calcable>::Err),
238 #[error("failed to render calculation result in desired format")]
239 Format(#[from] num_runtime_fmt::Error),
240}
241
242pub struct AnnotatedExpr<'input> {
244 pub expr: Expr<'input>,
245 pub format: NumFmt,
246}
247
248impl<'input> AnnotatedExpr<'input> {
249 pub fn evaluate<N>(&self, ctx: &Context<N>) -> Result<(N, String), AnnotatedError<N>>
254 where
255 N: std::fmt::Debug + Calcable + num_runtime_fmt::Numeric,
256 <N as Calcable>::Err: 'static,
257 {
258 let value = self
259 .expr
260 .evaluate(ctx)
261 .map_err(AnnotatedError::Calculation)?;
262 let formatted = self.format.fmt(value.clone())?;
263 Ok((value, formatted))
264 }
265}