1use std::fmt;
18use lalrpop_util::{lalrpop_mod, ParseError};
19use libm::tgamma;
20
21pub mod context;
22use context::*;
23
24mod ast;
25use ast::*;
26
27lalrpop_mod!(grammar);
29
30pub fn calculate(input_str: &str, ctx: &mut Context) -> Result<f64, CalcError> {
46
47 let input_str = if let Some(stripped) = input_str.strip_suffix('\n') { stripped } else { input_str };
48
49 let parser = grammar::targetParser::new();
51 let (tree, assignment) = match parser.parse(input_str) {
52 Ok(res) => { res }
53 Err(e) => {
54
55 let msg = match &e {
56 ParseError::InvalidToken { location } => {
57 let pad = std::iter::repeat(" ").take(*location).collect::<String>();
58 format!("Invalid token\n| {input_str}\n| {pad}└── here")
59 },
60 ParseError::UnrecognizedEof { location: _, expected: _ } => {
61 String::from("Unexpected EOI")
62 },
63 ParseError::UnrecognizedToken { token, expected: _ } => {
64 let pad = std::iter::repeat(" ").take(token.0).collect::<String>();
65 format!("Unexpected token\n| {input_str}\n| {pad}└── here")
66 },
67 ParseError::ExtraToken { token} => {
68 let pad = std::iter::repeat(" ").take(token.0).collect::<String>();
69 format!("Extra token\n| {input_str}\n| {pad}└── here")
70 },
71 _ => String::from("Parser error"),
72 };
73 return Err(CalcError {
74 error_type: CalcErrorType::ParserError,
75 msg,
76 });
77 }
78 };
79 let res = evaluate_ast(*tree, ctx);
82
83 if res.is_ok() {
84 let solution = res.clone().unwrap();
85
86
87 if assignment.is_some() {
88 let assign_var = assignment.unwrap();
90 if let Err(e) = ctx.assign_var(&assign_var, solution) {
91 return Err(e);
92 }
93 } else {
94 ctx.prev_ans = Some(solution);
96 }
97 }
98
99 return res;
100}
101
102fn evaluate_ast(root: Expr, ctx: &Context) -> Result<f64, CalcError> {
105 match root {
106 Expr::Num(n) => {
107 Ok(n)
108 }
109 Expr::Op(left_e, op, right_e) => {
110 let lhs = match evaluate_ast(*left_e, ctx) {
112 Ok(n) => n,
113 Err(e) => { return Err(e) },
114 };
115 let rhs = match evaluate_ast(*right_e, ctx) {
116 Ok(n) => n,
117 Err(e) => { return Err(e) },
118 };
119 let res = match op {
121 Operation::Add => { lhs + rhs }
122 Operation::Sub => { lhs - rhs }
123 Operation::Mul => { lhs * rhs }
124 Operation::Div => { lhs / rhs }
125 Operation::FloorDiv => { f64::floor(lhs / rhs) }
126 Operation::Mod => { lhs % rhs }
127 Operation::Exp => { lhs.powf(rhs) }
128 };
129 Ok(res)
130 }
131 Expr::Func(name, arg_list) => {
132 let mut args: Vec<f64> = Vec::new();
133 for arg in arg_list {
134 let val = match evaluate_ast(*arg, ctx) {
135 Ok(n) => n,
136 Err(e) => { return Err(e) },
137 };
138 args.push(val);
139 }
140 if let Some(res) = ctx.try_function(&name, args) {
141 return res;
142 }
143 return Err(CalcError {
144 error_type: CalcErrorType::UndefinedIdentifier,
145 msg: format!("Unknown function \"{name}()\""),
146 })
147 }
148 Expr::Var(name) => {
149 if let Some(res) = ctx.lookup_var(&name) {
150 return res;
151 }
152 return Err(CalcError {
153 error_type: CalcErrorType::UndefinedIdentifier,
154 msg: format!("Unknown variable \"{name}\""),
155 })
156 }
157 Expr::Fac(e) => {
158 let num = match evaluate_ast(*e, ctx) {
159 Ok(n) => n,
160 Err(e) => { return Err(e) },
161 };
162 return Ok(tgamma(num + 1.0));
163 }
164 }
165}
166
167#[derive(Debug, Clone, PartialEq)]
169pub struct CalcError {
170 pub error_type: CalcErrorType,
172 pub msg: String,
174}
175impl fmt::Display for CalcError {
176 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
177 write!(formatter, "{}: {}\n", self.error_type, self.msg)
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq)]
183pub enum CalcErrorType {
184 ParserError,
186 UndefinedIdentifier,
188 AssignmentError,
190 ArgumentError,
192 CalculationError,
194}
195impl fmt::Display for CalcErrorType {
196 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
197 write!(formatter, "{}", match *self {
198 Self::ParserError => { "Parser error" },
199 Self::UndefinedIdentifier => { "Undefined identifier" },
200 Self::AssignmentError => { "Assignment error" },
201 Self::ArgumentError => { "Argument error" },
202 Self::CalculationError => { "Calculation error" },
203 })
204 }
205}