-
Notifications
You must be signed in to change notification settings - Fork 69
/
Copy pathparser_body.hpp
236 lines (211 loc) · 7.41 KB
/
parser_body.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#pragma once
#include "quantities/parser.hpp"
#include <array>
#include <string>
#include "quantities/named_quantities.hpp"
#include "quantities/si.hpp"
namespace principia {
namespace quantities {
namespace internal_parser {
using RuntimeDimensions = std::array<std::int64_t, 8>;
template<typename Q>
struct ExtractDimensions {};
template<>
struct ExtractDimensions<double> {
static constexpr RuntimeDimensions dimensions() {
return {{0, 0, 0, 0, 0, 0, 0, 0}};
}
};
template<std::int64_t LengthExponent,
std::int64_t MassExponent,
std::int64_t TimeExponent,
std::int64_t CurrentExponent,
std::int64_t TemperatureExponent,
std::int64_t AmountExponent,
std::int64_t LuminousIntensityExponent,
std::int64_t AngleExponent>
struct ExtractDimensions<
Quantity<internal_quantities::Dimensions<LengthExponent,
MassExponent,
TimeExponent,
CurrentExponent,
TemperatureExponent,
AmountExponent,
LuminousIntensityExponent,
AngleExponent>>> {
static constexpr RuntimeDimensions dimensions() {
return {{LengthExponent,
MassExponent,
TimeExponent,
CurrentExponent,
TemperatureExponent,
AmountExponent,
LuminousIntensityExponent,
AngleExponent}};
}
};
struct Unit {
template<typename Q>
explicit Unit(Q const& quantity);
Unit(RuntimeDimensions&& dimensions, double scale);
RuntimeDimensions dimensions;
double scale;
};
template<typename Q>
Unit::Unit(Q const& quantity)
: dimensions(ExtractDimensions<Q>::dimensions()),
scale(quantity / SIUnit<Q>()) {}
inline Unit::Unit(RuntimeDimensions&& dimensions, double const scale)
: dimensions(dimensions),
scale(scale) {}
inline Unit operator*(Unit const& left, Unit const& right) {
RuntimeDimensions dimensions;
for (std::int64_t i = 0; i < dimensions.size(); ++i) {
dimensions[i] = left.dimensions[i] + right.dimensions[i];
}
return {std::move(dimensions), left.scale * right.scale};
}
inline Unit operator/(Unit const& left, Unit const& right) {
RuntimeDimensions dimensions;
for (std::int64_t i = 0; i < dimensions.size(); ++i) {
dimensions[i] = left.dimensions[i] - right.dimensions[i];
}
return {std::move(dimensions), left.scale / right.scale};
}
inline Unit operator^(Unit const& left, int const exponent) {
RuntimeDimensions dimensions;
for (std::int64_t i = 0; i < dimensions.size(); ++i) {
dimensions[i] = left.dimensions[i] * exponent;
}
return {std::move(dimensions), std::pow(left.scale, exponent)};
}
inline Unit ParseUnit(std::string const& s) {
// Unitless quantities.
if (s == "") {
return Unit(1.0);
// Units of length.
} else if (s == u8"μm") {
return Unit(si::Micro(si::Metre));
} else if (s == "mm") {
return Unit(si::Milli(si::Metre));
} else if (s == "cm") {
return Unit(si::Centi(si::Metre));
} else if (s == "m") {
return Unit(si::Metre);
} else if (s == "km") {
return Unit(si::Kilo(si::Metre));
} else if (s == "au") {
return Unit(si::AstronomicalUnit);
// Units of time.
} else if (s == "ms") {
return Unit(si::Milli(si::Second));
} else if (s == "s") {
return Unit(si::Second);
} else if (s == "min") {
return Unit(si::Minute);
} else if (s == "h") {
return Unit(si::Hour);
} else if (s == "d") {
return Unit(si::Day);
// Units of power.
} else if (s == "W") {
return Unit(si::Watt);
// Units of angle.
} else if (s == "deg" || s == u8"°") {
return Unit(si::Degree);
} else if (s == "rad") {
return Unit(si::Radian);
// Units of solid angle.
} else if (s == "sr") {
return Unit(si::Steradian);
} else {
LOG(FATAL) << "Unsupported unit " << s;
base::noreturn();
}
}
inline int ParseExponent(std::string const& s) {
// Parse an int.
char* interpreted_end;
char const* const c_string = s.c_str();
double const exponent = std::strtol(c_string, &interpreted_end, /*base=*/10);
int const interpreted = interpreted_end - c_string;
CHECK_LT(0, interpreted) << "invalid integer number " << s;
return exponent;
}
inline Unit ParseExponentiationUnit(std::string const& s) {
int const first_caret = s.find('^');
if (first_caret == std::string::npos) {
return ParseUnit(s);
} else {
int const first_nonblank = s.find_first_not_of(' ', first_caret + 1);
CHECK_NE(std::string::npos, first_nonblank);
int const last_nonblank = s.find_last_not_of(' ', first_caret - 1);
CHECK_NE(std::string::npos, last_nonblank);
auto const left = ParseUnit(s.substr(0, last_nonblank + 1));
auto const right = ParseExponent(s.substr(first_nonblank));
return left ^ right;
}
}
inline Unit ParseProductUnit(std::string const& s) {
// For a product we are looking for a blank character that is not next to a
// carret.
int first_blank;
int first_nonblank;
int last_nonblank;
for (int start = 0;; start = first_blank + 1) {
first_blank = s.find(' ', start);
if (first_blank == std::string::npos) {
return ParseExponentiationUnit(s);
} else {
first_nonblank = s.find_first_not_of(' ', first_blank + 1);
last_nonblank = s.find_last_not_of(' ', first_blank - 1);
if ((first_nonblank == std::string::npos || s[first_nonblank] != '^') &&
(last_nonblank == std::string::npos || s[last_nonblank] != '^')) {
break;
}
}
}
auto const left = ParseExponentiationUnit(s.substr(0, last_nonblank + 1));
auto const right = ParseProductUnit(s.substr(first_nonblank));
return left * right;
}
inline Unit ParseQuotientUnit(std::string const& s) {
// Look for the slash from the back to achieve proper associativity.
int const last_slash = s.rfind('/');
if (last_slash == std::string::npos) {
// Not a quotient.
return ParseProductUnit(s);
} else {
// A quotient. Parse each half.
int const first_nonblank = s.find_first_not_of(' ', last_slash + 1);
CHECK_NE(std::string::npos, first_nonblank);
int const last_nonblank = s.find_last_not_of(' ', last_slash - 1);
CHECK_NE(std::string::npos, last_nonblank);
auto const left = ParseQuotientUnit(s.substr(0, last_nonblank + 1));
auto const right = ParseExponentiationUnit(s.substr(first_nonblank));
return left / right;
}
}
template<typename Q>
Q ParseQuantity(std::string const& s) {
// Parse a double.
char* interpreted_end;
char const* const c_string = s.c_str();
double const magnitude = std::strtod(c_string, &interpreted_end);
int const interpreted = interpreted_end - c_string;
CHECK_LT(0, interpreted) << "invalid floating-point number " << s;
// Locate the unit. It may be empty for a double.
int const first_nonblank = s.find_first_not_of(' ', interpreted);
int const last_nonblank = s.find_last_not_of(' ');
std::string unit_string;
if (first_nonblank != std::string::npos) {
unit_string = s.substr(first_nonblank, last_nonblank - first_nonblank + 1);
}
Unit const unit = ParseQuotientUnit(unit_string);
CHECK(ExtractDimensions<Q>::dimensions() == unit.dimensions);
return magnitude * unit.scale * SIUnit<Q>();
}
} // namespace internal_parser
} // namespace quantities
} // namespace principia