11 const auto first = text.find_first_not_of(
" \t\r\n");
12 if (first == std::string_view::npos)
return {};
13 const auto last = text.find_last_not_of(
" \t\r\n");
14 return text.substr(first, last - first + 1);
18 bool saw_digit =
false;
19 for (std::size_t i = 0; i < expression.size(); ++i) {
20 const char c = expression[i];
21 if (std::isdigit(
static_cast<unsigned char>(
c))) {
25 if (
c ==
'.' ||
c ==
'/' ||
c ==
'e' ||
c ==
'E') {
28 if ((
c ==
'+' ||
c ==
'-') &&
29 (i == 0 || expression[i - 1] ==
'e' ||
30 expression[i - 1] ==
'E')) {
53 const std::string& keyword) {
58 throw std::invalid_argument(
59 "Line " + std::to_string(line) +
": " + keyword +
": " +
66 const std::string& keyword) {
68 if (input >> trailing) {
69 throw std::invalid_argument(
70 "Line " + std::to_string(line_num) +
": " + keyword +
71 " has unexpected trailing field '" + trailing +
"'");
76 const std::string& token,
78 const auto invalid = [&]() {
79 return std::invalid_argument(
80 "Line " + std::to_string(line_num) +
81 ": invalid finite number '" + token +
"'");
86 if (token.compare(0, 7,
"approx(") == 0 && token.back() ==
')') {
87 std::string_view decimal(token.data() + 7, token.size() - 8);
90 if (!
value) {
throw invalid(); }
94 const auto slash = token.find(
'/');
95 if (slash != std::string::npos) {
96 if (slash == 0 || slash + 1 == token.size() ||
97 token.find(
'/', slash + 1) != std::string::npos) {
102 BigInt(token.substr(0, slash)),
103 BigInt(token.substr(slash + 1))));
105 if (token.find_first_of(
".eE") == std::string::npos) {
116 const std::string& expression,
int line_num) {
120 }
catch (
const std::bad_alloc&) {
122 }
catch (
const std::invalid_argument&) {
124 }
catch (
const std::exception& error) {
125 throw std::invalid_argument(
126 "Line " + std::to_string(line_num) +
127 ": invalid numeric expression '" + expression +
128 "': " + error.what());
133 const std::string& expression,
int line_num,
139 return *normalized.value();
141 throw normalized.error();
145 throw std::invalid_argument(
146 "Line " + std::to_string(line_num) +
": " +
147 parsed.error().message);
149 throw parsed.error();
157 if (trimmed.empty()) {
158 throw std::invalid_argument(
159 "Line " + std::to_string(line_num) +
": empty expression");
161 const std::string expression(trimmed);
163 return parse_serialized_numeric_atom(expression, line_num);
165 return parse_general_expression(expression, line_num,
context);
171 const std::string& text,
int line_num,
const std::string&
interval,
174 return std::make_shared<SymbolicExpr>(
176 }
catch (
const std::invalid_argument&) {
177 throw std::invalid_argument(
178 "Line " + std::to_string(line_num) +
179 ": malformed interval '" +
interval +
"'");
184 const std::string& text,
bool open,
int line_num,
188 return open ? Endpoint::open(std::move(
value))
189 : Endpoint::closed(std::move(
value));
193 if (s.size() < 5)
return false;
194 const bool lower = s.front() ==
'[' || s.front() ==
'(';
195 const bool upper = s.back() ==
']' || s.back() ==
')';
200 const std::string& text,
int line_num) {
201 return std::invalid_argument(
202 "Line " + std::to_string(line_num) +
203 ": malformed interval '" + text +
"'");
208 const std::string& inner,
const std::string&
interval,
int line_num) {
209 std::vector<char> expected_closers;
210 std::size_t comma_pos = std::string::npos;
211 for (std::size_t i = 0; i < inner.size(); ++i) {
212 const char c = inner[i];
214 expected_closers.push_back(closer);
218 if (expected_closers.empty() || expected_closers.back() !=
c) {
221 expected_closers.pop_back();
224 if (
c !=
',' || !expected_closers.empty()) {
227 if (comma_pos != std::string::npos) {
232 if (!expected_closers.empty() || comma_pos == std::string::npos) {
239 std::string
value,
const std::string&
interval,
int line_num) {
241 if (trimmed.empty()) {
244 return std::string(trimmed);
248 return endpoint ==
"nan" || endpoint ==
"inf" ||
249 endpoint ==
"+inf" || endpoint ==
"-inf";
253 const std::string&
lower,
const std::string&
upper,
254 bool lower_open,
bool upper_open,
255 const std::string&
interval,
int line_num) {
256 if ((
lower ==
"-inf" && !lower_open) ||
257 (
upper ==
"+inf" && !upper_open) ||
268 if (!interval_delimiters_valid(text)) {
269 throw malformed_interval(text, line_num);
272 const bool lower_open = text.front() ==
'(';
273 const bool upper_open = text.back() ==
')';
274 const std::string inner = text.substr(1, text.size() - 2);
275 const std::size_t comma_pos =
276 find_interval_comma(inner, text, line_num);
277 const std::string lower_text = trim_interval_endpoint(
278 inner.substr(0, comma_pos), text, line_num);
279 const std::string upper_text = trim_interval_endpoint(
280 inner.substr(comma_pos + 1), text, line_num);
281 validate_interval_endpoints(
282 lower_text, upper_text, lower_open, upper_open, text, line_num);
285 interval.lower = lower_text ==
"-inf"
286 ? Endpoint::neg_inf()
287 : parse_interval_endpoint(
288 lower_text, lower_open, line_num, text,
context);
289 interval.upper = upper_text ==
"+inf"
290 ? Endpoint::pos_inf()
291 : parse_interval_endpoint(
292 upper_text, upper_open, line_num, text,
context);
std::string trim_interval_endpoint(std::string value, const std::string &interval, int line_num)
SymbolicExpr parse_serialized_numeric_atom(const std::string &expression, int line_num)
std::invalid_argument malformed_interval(const std::string &text, int line_num)
bool is_infinity_token(const std::string &endpoint)
SymbolicExpr parse_general_expression(const std::string &expression, int line_num, ComputationContext &context)
Endpoint parse_interval_endpoint(const std::string &text, bool open, int line_num, const std::string &interval, ComputationContext &context)
bool interval_delimiters_valid(const std::string &s)
std::size_t find_interval_comma(const std::string &inner, const std::string &interval, int line_num)
void validate_interval_endpoints(const std::string &lower, const std::string &upper, bool lower_open, bool upper_open, const std::string &interval, int line_num)
std::shared_ptr< SymbolicExpr > parse_interval_expression(const std::string &text, int line_num, const std::string &interval, ComputationContext &context)
std::shared_ptr< const SymbolicNode > parse_finite_number_node(const std::string &token, int line_num)
void require_line_end(std::istringstream &input, int line_num, const std::string &keyword)
bool is_serialized_numeric_atom(std::string_view expression) noexcept
std::string_view trim_ascii_whitespace(std::string_view text) noexcept
void require_deserialization_update(const Result< void > &result, int line, const std::string &keyword)
SymbolicExpr parse_serialized_expression(const std::string &text, int line_num, ComputationContext &context)
bool is_closing_delimiter(char value) noexcept
char matching_closer(char opener) noexcept
Interval parse_interval(const std::string &text, int line_num, ComputationContext &context)
Result< double > parse_finite_decimal(std::string_view token)
inline ::LMCAS::SymbolicExpr expression_from_node(SymbolicNodePtr root)
std::shared_ptr< const Node > make_node(Args &&... args)
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
const VectorField const VectorField & c
ExprResult simplify(const ExprPtr &expression, ComputationContext &context)
ExprResult parse_expr(const std::string &source)
const std::string const ExprPtr & value