1212
1313enum struct Operators { PLUS_SIGN, CLOSE_PAREN, OPEN_PAREN, MINUS_SIGN, DIVIDE_SIGN, MULTIPLY_SIGN };
1414
15+ // Evaluation errors with position information
1516struct EvaluationError {
1617 enum class ErrorType {
17- INVALID_NUMBER,
18- INVALID_EXPRESSION,
19- EMPTY_TOKEN,
20- STACK_EMPTY
18+ INVALID_NUMBER, // Invalid character in number
19+ INVALID_EXPRESSION, // General parsing error
20+ EMPTY_TOKEN, // Empty token found
21+ STACK_ERROR // Error with the stack operations
2122 };
2223
2324 ErrorType type;
24- std::string message;
25+ size_t position; // Position in the expression where the error occurred
2526
26- constexpr EvaluationError (ErrorType t, std::string_view msg ) : type(t), message(msg ) {}
27+ constexpr EvaluationError (ErrorType t, size_t pos = 0 ) : type(t), position(pos ) {}
2728};
2829
2930constexpr auto precedence (Operators input) noexcept -> int
@@ -73,16 +74,12 @@ constexpr auto evaluateStacks(Stack<RationalNumber> &numbers, Stack<Operators> &
7374 operators.pop ();
7475 auto operand2_result = numbers.pop ();
7576 if (!operand2_result) {
76- return std::unexpected (EvaluationError (
77- EvaluationError::ErrorType::STACK_EMPTY,
78- std::format (" Missing second operand for + operation: {}" , operand2_result.error ().message )));
77+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
7978 }
8079
8180 auto operand1_result = numbers.pop ();
8281 if (!operand1_result) {
83- return std::unexpected (EvaluationError (
84- EvaluationError::ErrorType::STACK_EMPTY,
85- std::format (" Missing first operand for + operation: {}" , operand1_result.error ().message )));
82+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
8683 }
8784
8885 numbers.push (*operand1_result + *operand2_result);
@@ -93,16 +90,12 @@ constexpr auto evaluateStacks(Stack<RationalNumber> &numbers, Stack<Operators> &
9390 operators.pop ();
9491 auto operand2_result = numbers.pop ();
9592 if (!operand2_result) {
96- return std::unexpected (EvaluationError (
97- EvaluationError::ErrorType::STACK_EMPTY,
98- std::format (" Missing second operand for - operation: {}" , operand2_result.error ().message )));
93+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
9994 }
10095
10196 auto operand1_result = numbers.pop ();
10297 if (!operand1_result) {
103- return std::unexpected (EvaluationError (
104- EvaluationError::ErrorType::STACK_EMPTY,
105- std::format (" Missing first operand for - operation: {}" , operand1_result.error ().message )));
98+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
10699 }
107100
108101 numbers.push (*operand1_result - *operand2_result);
@@ -113,16 +106,12 @@ constexpr auto evaluateStacks(Stack<RationalNumber> &numbers, Stack<Operators> &
113106 operators.pop ();
114107 auto operand2_result = numbers.pop ();
115108 if (!operand2_result) {
116- return std::unexpected (EvaluationError (
117- EvaluationError::ErrorType::STACK_EMPTY,
118- std::format (" Missing second operand for * operation: {}" , operand2_result.error ().message )));
109+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
119110 }
120111
121112 auto operand1_result = numbers.pop ();
122113 if (!operand1_result) {
123- return std::unexpected (EvaluationError (
124- EvaluationError::ErrorType::STACK_EMPTY,
125- std::format (" Missing first operand for * operation: {}" , operand1_result.error ().message )));
114+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
126115 }
127116
128117 numbers.push (*operand1_result * *operand2_result);
@@ -133,16 +122,12 @@ constexpr auto evaluateStacks(Stack<RationalNumber> &numbers, Stack<Operators> &
133122 operators.pop ();
134123 auto operand2_result = numbers.pop ();
135124 if (!operand2_result) {
136- return std::unexpected (EvaluationError (
137- EvaluationError::ErrorType::STACK_EMPTY,
138- std::format (" Missing second operand for / operation: {}" , operand2_result.error ().message )));
125+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
139126 }
140127
141128 auto operand1_result = numbers.pop ();
142129 if (!operand1_result) {
143- return std::unexpected (EvaluationError (
144- EvaluationError::ErrorType::STACK_EMPTY,
145- std::format (" Missing first operand for / operation: {}" , operand1_result.error ().message )));
130+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::STACK_ERROR));
146131 }
147132
148133 numbers.push (*operand1_result / *operand2_result);
@@ -163,15 +148,17 @@ template<std::integral Type>
163148{
164149 Type result{ 0 };
165150
166- for (const char digit : input) {
151+ for (size_t i = 0 ; i < input.size (); ++i) {
152+ const char digit = input[i];
167153 result *= 10 ;// NOLINT
168154
169155 if (digit >= ' 0' && digit <= ' 9' ) {
170156 result += static_cast <Type>(digit - ' 0' );
171157 } else {
158+ // Return position information with the error
172159 return std::unexpected (EvaluationError (
173160 EvaluationError::ErrorType::INVALID_NUMBER,
174- std::format ( " Invalid digit '{}' in number " , digit) ));
161+ i ));
175162 }
176163 }
177164
@@ -184,25 +171,15 @@ template<std::integral Type>
184171 Stack<Operators> operators;
185172 Stack<RationalNumber> numbers;
186173
187- const auto make_error = [&tokenizer](EvaluationError::ErrorType type, std::string_view msg = " " ) -> EvaluationError {
188- std::string contextual_message = std::format (
189- R"( Unable to evaluate expression
190- {}
191- {}^ unevaluated)" ,
192- tokenizer.input (),
193- std::string (tokenizer.offset (), ' ' ));
194-
195- if (!msg.empty ()) {
196- contextual_message = std::format (" {}: {}" , contextual_message, msg);
197- }
198-
199- return EvaluationError (type, contextual_message);
174+ // Creates an error with the current tokenizer position
175+ const auto make_error = [&tokenizer](EvaluationError::ErrorType type) -> EvaluationError {
176+ return EvaluationError (type, tokenizer.offset ());
200177 };
201178
202179 const auto evalStacks = [&]() -> std::expected<void , EvaluationError> {
203180 auto result = ::evaluateStacks (numbers, operators);
204181 if (!result) {
205- return std::unexpected (make_error (result.error ().type , result. error (). message ));
182+ return std::unexpected (make_error (result.error ().type ));
206183 }
207184 return {};
208185 };
@@ -211,7 +188,7 @@ template<std::integral Type>
211188 auto next = tokenizer.nextToken ();
212189
213190 if (next.empty ()) {
214- return std::unexpected (make_error (EvaluationError::ErrorType::EMPTY_TOKEN));
191+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::EMPTY_TOKEN, tokenizer. offset () ));
215192 }
216193
217194 auto value = Operators::PLUS_SIGN;
@@ -248,7 +225,7 @@ template<std::integral Type>
248225 operation = false ;
249226 auto parsed_result = from_chars<int >(next);
250227 if (!parsed_result) {
251- return std::unexpected (make_error ( parsed_result.error (). type , parsed_result. error (). message ));
228+ return std::unexpected (parsed_result.error ());
252229 }
253230 numbers.emplace (*parsed_result, 1 );
254231 break ;
@@ -294,14 +271,14 @@ template<std::integral Type>
294271 }
295272
296273 if (!operators.empty () || tokenizer.hasUnparsedInput ()) {
297- return std::unexpected (make_error (EvaluationError::ErrorType::INVALID_EXPRESSION));
274+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::INVALID_EXPRESSION, tokenizer. offset () ));
298275 }
299276
300277 if (numbers.peek () != nullptr ) {
301278 return *numbers.peek ();
302279 }
303280
304- return std::unexpected (make_error (EvaluationError::ErrorType::INVALID_EXPRESSION));
281+ return std::unexpected (EvaluationError (EvaluationError::ErrorType::INVALID_EXPRESSION, tokenizer. offset () ));
305282}
306283
307284[[nodiscard]] constexpr auto evaluate (std::string_view input) -> std::expected<RationalNumber, EvaluationError>
0 commit comments