LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
assumption_parse_atoms.cpp
浏览该文件的文档.
2#include "expr.hpp"
4#include <cctype>
5#include <vector>
6#include <cmath>
7
9
10std::string_view trim_ascii_whitespace(std::string_view text) noexcept {
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);
15}
16
17bool is_serialized_numeric_atom(std::string_view expression) noexcept {
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))) {
22 saw_digit = true;
23 continue;
24 }
25 if (c == '.' || c == '/' || c == 'e' || c == 'E') {
26 continue;
27 }
28 if ((c == '+' || c == '-') &&
29 (i == 0 || expression[i - 1] == 'e' ||
30 expression[i - 1] == 'E')) {
31 continue;
32 }
33 return false;
34 }
35 return saw_digit;
36}
37
38char matching_closer(char opener) noexcept {
39 switch (opener) {
40 case '(': return ')';
41 case '[': return ']';
42 case '{': return '}';
43 default: return '\0';
44 }
45}
46
47bool is_closing_delimiter(char value) noexcept {
48 return value == ')' || value == ']' || value == '}';
49}
50
52 int line,
53 const std::string& keyword) {
54 if (result) {
55 return;
56 }
57 if (result.error().code == CasErrc::InvalidArgument) {
58 throw std::invalid_argument(
59 "Line " + std::to_string(line) + ": " + keyword + ": " +
60 result.error().message);
61 }
62 throw result.error();
63}
64void require_line_end(std::istringstream& input,
65 int line_num,
66 const std::string& keyword) {
67 std::string trailing;
68 if (input >> trailing) {
69 throw std::invalid_argument(
70 "Line " + std::to_string(line_num) + ": " + keyword +
71 " has unexpected trailing field '" + trailing + "'");
72 }
73}
74
75std::shared_ptr<const SymbolicNode> parse_finite_number_node(
76 const std::string& token,
77 int line_num) {
78 const auto invalid = [&]() {
79 return std::invalid_argument(
80 "Line " + std::to_string(line_num) +
81 ": invalid finite number '" + token + "'");
82 };
83 if (token.empty()) {
84 throw invalid();
85 }
86 if (token.compare(0, 7, "approx(") == 0 && token.back() == ')') {
87 std::string_view decimal(token.data() + 7, token.size() - 8);
88 decimal = trim_ascii_whitespace(decimal);
90 if (!value) { throw invalid(); }
92 }
93
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) {
98 throw invalid();
99 }
101 Rational(
102 BigInt(token.substr(0, slash)),
103 BigInt(token.substr(slash + 1))));
104 }
105 if (token.find_first_of(".eE") == std::string::npos) {
106 return LMCAS::detail::make_node<NumberNode>(BigInt(token));
107 }
108
109 return LMCAS::detail::make_node<NumberNode>(Rational(token));
110}
111
112namespace {
113
114
116 const std::string& expression, int line_num) {
117 try {
119 parse_finite_number_node(expression, line_num));
120 } catch (const std::bad_alloc&) {
121 throw;
122 } catch (const std::invalid_argument&) {
123 throw;
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());
129 }
130}
131
133 const std::string& expression, int line_num,
135 auto parsed = parse_expr(expression, context);
136 if (parsed) {
137 auto normalized = LMCAS::simplify(parsed.value(), context);
138 if (normalized) {
139 return *normalized.value();
140 }
141 throw normalized.error();
142 }
143 if (parsed.error().code == CasErrc::ParseError ||
144 parsed.error().code == CasErrc::InvalidArgument) {
145 throw std::invalid_argument(
146 "Line " + std::to_string(line_num) + ": " +
147 parsed.error().message);
148 }
149 throw parsed.error();
150}
151
152}
153
155 const std::string& text, int line_num, ComputationContext& context) {
156 const auto trimmed = trim_ascii_whitespace(text);
157 if (trimmed.empty()) {
158 throw std::invalid_argument(
159 "Line " + std::to_string(line_num) + ": empty expression");
160 }
161 const std::string expression(trimmed);
162 if (is_serialized_numeric_atom(trimmed)) {
163 return parse_serialized_numeric_atom(expression, line_num);
164 }
165 return parse_general_expression(expression, line_num, context);
166}
167
168namespace {
169
170std::shared_ptr<SymbolicExpr> parse_interval_expression(
171 const std::string& text, int line_num, const std::string& interval,
173 try {
174 return std::make_shared<SymbolicExpr>(
175 parse_serialized_expression(text, line_num, context));
176 } catch (const std::invalid_argument&) {
177 throw std::invalid_argument(
178 "Line " + std::to_string(line_num) +
179 ": malformed interval '" + interval + "'");
180 }
181}
182
184 const std::string& text, bool open, int line_num,
185 const std::string& interval, ComputationContext& context) {
187 text, line_num, interval, context);
188 return open ? Endpoint::open(std::move(value))
189 : Endpoint::closed(std::move(value));
190}
191
192bool interval_delimiters_valid(const std::string& s) {
193 if (s.size() < 5) return false;
194 const bool lower = s.front() == '[' || s.front() == '(';
195 const bool upper = s.back() == ']' || s.back() == ')';
196 return lower && upper;
197}
198
199std::invalid_argument malformed_interval(
200 const std::string& text, int line_num) {
201 return std::invalid_argument(
202 "Line " + std::to_string(line_num) +
203 ": malformed interval '" + text + "'");
204}
205
206
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];
213 if (const char closer = matching_closer(c)) {
214 expected_closers.push_back(closer);
215 continue;
216 }
217 if (is_closing_delimiter(c)) {
218 if (expected_closers.empty() || expected_closers.back() != c) {
219 throw malformed_interval(interval, line_num);
220 }
221 expected_closers.pop_back();
222 continue;
223 }
224 if (c != ',' || !expected_closers.empty()) {
225 continue;
226 }
227 if (comma_pos != std::string::npos) {
228 throw malformed_interval(interval, line_num);
229 }
230 comma_pos = i;
231 }
232 if (!expected_closers.empty() || comma_pos == std::string::npos) {
233 throw malformed_interval(interval, line_num);
234 }
235 return comma_pos;
236}
237
239 std::string value, const std::string& interval, int line_num) {
240 const auto trimmed = trim_ascii_whitespace(value);
241 if (trimmed.empty()) {
242 throw malformed_interval(interval, line_num);
243 }
244 return std::string(trimmed);
245}
246
247bool is_infinity_token(const std::string& endpoint) {
248 return endpoint == "nan" || endpoint == "inf" ||
249 endpoint == "+inf" || endpoint == "-inf";
250}
251
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) ||
258 (is_infinity_token(lower) && lower != "-inf") ||
259 (is_infinity_token(upper) && upper != "+inf")) {
260 throw malformed_interval(interval, line_num);
261 }
262}
263
264}
265
267 const std::string& text, int line_num, ComputationContext& context) {
268 if (!interval_delimiters_valid(text)) {
269 throw malformed_interval(text, line_num);
270 }
271
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);
283
284 Interval interval;
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);
293 return interval;
294}
295
296}
CasError & error()
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)
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)
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 ExprPtr & lower
const VectorField const VectorField & c
std::string message
ExprResult simplify(const ExprPtr &expression, ComputationContext &context)
ExprResult parse_expr(const std::string &source)
const std::string const ExprPtr & value