@@ -81,28 +81,10 @@ inline std::vector<std::string> parselist(const std::string& str) {
8181 return result;
8282}
8383
84- /* *
85- * Parses a double-valued list like "[0.0, 1.5, 2.3]" into a vector<double>.
86- * Used by concore.hpp's read/write which work with numeric data.
87- * Now delegates to the full literal parser to handle mixed-type payloads
88- * (strings, booleans, nested lists, tuples) without crashing.
89- * See Issue #389.
90- */
91- inline std::vector<double > parselist_double (const std::string& str); // forward decl; defined after ConcoreValue
92-
93- // ===================================================================
94- // Python-Literal-Compatible Value Type and Parser (Issue #389)
95- // ===================================================================
84+ inline std::vector<double > parselist_double (const std::string& str);
9685
97- /* *
98- * Tag for ConcoreValue discriminated union.
99- */
10086enum class ConcoreValueType { NUMBER , BOOL , STRING , ARRAY };
10187
102- /* *
103- * A recursive value type that mirrors Python's ast.literal_eval output.
104- * Supported: numbers, booleans, strings, and nested arrays / tuples.
105- */
10688struct ConcoreValue {
10789 ConcoreValueType type;
10890 double number;
@@ -122,7 +104,7 @@ struct ConcoreValue {
122104 ConcoreValue cv;
123105 cv.type = ConcoreValueType::BOOL ;
124106 cv.boolean = v;
125- cv.number = v ? 1.0 : 0.0 ; // Python: True == 1, False == 0
107+ cv.number = v ? 1.0 : 0.0 ;
126108 return cv;
127109 }
128110 static ConcoreValue make_string (const std::string& v) {
@@ -139,8 +121,6 @@ struct ConcoreValue {
139121 }
140122};
141123
142- // --------------- internal helpers (anonymous-namespace-like) --------
143-
144124inline void skip_ws (const std::string& s, size_t & pos) {
145125 while (pos < s.size () && std::isspace (static_cast <unsigned char >(s[pos])))
146126 ++pos;
@@ -149,7 +129,7 @@ inline void skip_ws(const std::string& s, size_t& pos) {
149129inline ConcoreValue parse_literal_value (const std::string& s, size_t & pos);
150130
151131inline ConcoreValue parse_literal_string (const std::string& s, size_t & pos) {
152- char quote = s[pos]; // ' or "
132+ char quote = s[pos];
153133 ++pos;
154134 std::string result;
155135 while (pos < s.size () && s[pos] != quote) {
@@ -170,7 +150,7 @@ inline ConcoreValue parse_literal_string(const std::string& s, size_t& pos) {
170150 }
171151 if (pos >= s.size ())
172152 throw std::runtime_error (" Invalid concore payload: unterminated string" );
173- ++pos; // skip closing quote
153+ ++pos;
174154 return ConcoreValue::make_string (result);
175155}
176156
@@ -190,45 +170,35 @@ inline ConcoreValue parse_literal_array(const std::string& s, size_t& pos) {
190170 throw std::runtime_error (" Invalid concore payload: unterminated array/tuple" );
191171}
192172
193- /* *
194- * Recursive descent parser entry for a single Python literal value.
195- * Advances `pos` past the consumed token.
196- */
197173inline ConcoreValue parse_literal_value (const std::string& s, size_t & pos) {
198174 skip_ws (s, pos);
199175 if (pos >= s.size ())
200176 throw std::runtime_error (" Invalid concore payload: unexpected end of input" );
201177
202178 char c = s[pos];
203179
204- // Array / Tuple
205180 if (c == ' [' || c == ' (' )
206181 return parse_literal_array (s, pos);
207182
208- // String
209183 if (c == ' \' ' || c == ' "' )
210184 return parse_literal_string (s, pos);
211185
212- // Boolean True
213186 if (s.compare (pos, 4 , " True" ) == 0 &&
214187 (pos + 4 >= s.size () || !std::isalnum (static_cast <unsigned char >(s[pos + 4 ])))) {
215188 pos += 4 ;
216189 return ConcoreValue::make_bool (true );
217190 }
218- // Boolean False
219191 if (s.compare (pos, 5 , " False" ) == 0 &&
220192 (pos + 5 >= s.size () || !std::isalnum (static_cast <unsigned char >(s[pos + 5 ])))) {
221193 pos += 5 ;
222194 return ConcoreValue::make_bool (false );
223195 }
224- // None → treat as string "None" (no numeric equivalent)
225196 if (s.compare (pos, 4 , " None" ) == 0 &&
226197 (pos + 4 >= s.size () || !std::isalnum (static_cast <unsigned char >(s[pos + 4 ])))) {
227198 pos += 4 ;
228199 return ConcoreValue::make_string (" None" );
229200 }
230201
231- // Number (int, float, negative, scientific notation)
232202 {
233203 size_t start = pos;
234204 if (pos < s.size () && (s[pos] == ' +' || s[pos] == ' -' )) ++pos;
@@ -257,18 +227,14 @@ inline ConcoreValue parse_literal_value(const std::string& s, size_t& pos) {
257227 " Invalid concore payload: bad number '" + numstr + " '" );
258228 }
259229 }
260- pos = start; // backtrack
230+ pos = start;
261231 }
262232
263233 throw std::runtime_error (
264234 std::string (" Invalid concore payload: unsupported literal at position " ) +
265235 std::to_string (pos));
266236}
267237
268- /* *
269- * Parses a complete Python literal string and returns a ConcoreValue.
270- * Trailing content after the value (other than whitespace) is an error.
271- */
272238inline ConcoreValue parse_literal (const std::string& s) {
273239 size_t pos = 0 ;
274240 ConcoreValue v = parse_literal_value (s, pos);
@@ -279,12 +245,6 @@ inline ConcoreValue parse_literal(const std::string& s) {
279245 return v;
280246}
281247
282- /* *
283- * Recursively extracts all numeric values from a ConcoreValue.
284- * Booleans convert to 1.0 / 0.0 (matching Python's int(True) / int(False)).
285- * Strings are skipped.
286- * Nested arrays are flattened.
287- */
288248inline void flatten_numeric_impl (const ConcoreValue& v, std::vector<double >& out) {
289249 switch (v.type ) {
290250 case ConcoreValueType::NUMBER :
@@ -294,7 +254,6 @@ inline void flatten_numeric_impl(const ConcoreValue& v, std::vector<double>& out
294254 out.push_back (v.boolean ? 1.0 : 0.0 );
295255 break ;
296256 case ConcoreValueType::STRING :
297- // Skip non-numeric tokens
298257 break ;
299258 case ConcoreValueType::ARRAY :
300259 for (const auto & elem : v.array )
@@ -309,16 +268,13 @@ inline std::vector<double> flatten_numeric(const ConcoreValue& v) {
309268 return out;
310269}
311270
312- // --------------- parselist_double (full definition) -----------------
313-
314271inline std::vector<double > parselist_double (const std::string& str) {
315272 std::string trimmed = stripstr (str);
316273 if (trimmed.empty ()) return {};
317274 try {
318275 ConcoreValue v = parse_literal (trimmed);
319276 return flatten_numeric (v);
320277 } catch (...) {
321- // Fall back to the simple comma-split parser for edge cases
322278 std::vector<double > result;
323279 if (trimmed.size () < 2 ) return result;
324280 if (trimmed.front () == ' [' || trimmed.front () == ' (' ) {
0 commit comments