Skip to content

Commit 6f29dae

Browse files
committed
Fix float literal parsing, making it consistent across all platforms. Fixes #941.
1 parent b01ab04 commit 6f29dae

File tree

6 files changed

+95
-18
lines changed

6 files changed

+95
-18
lines changed

changes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Changes:
2020
avoid issues with some compilers.
2121
- Fix AST string allocation to avoid warnings about writing into unallocated
2222
memory.
23+
- Fix float literal parsing, making it consistent across all platforms (:bugref:`941`).
2324

2425
Bug fixes:
2526
^^^^^^^^^^

include/minizinc/parser.hh

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ class ParserLocation;
5353
#include <utility>
5454
#include <vector>
5555

56+
#if defined(_WIN32)
57+
#include <locale.h> // _create_locale, _free_locale
58+
#else
59+
#include <locale.h> // newlocale, freelocale
60+
#endif
61+
5662
namespace MiniZinc {
5763

5864
struct ParseWorkItem {
@@ -95,7 +101,23 @@ public:
95101
isSTDLib(isSTDLib0),
96102
parseDocComments(parseDocComments0),
97103
hadError(false),
98-
err(err0) {}
104+
err(err0) {
105+
#if defined(_WIN32)
106+
cLocale = _create_locale(LC_ALL, "C");
107+
#else
108+
cLocale = newlocale(LC_ALL_MASK, "C", nullptr);
109+
#endif
110+
}
111+
112+
~ParserState() {
113+
if (cLocale != nullptr) {
114+
#if defined(_WIN32)
115+
_free_locale(cLocale);
116+
#else
117+
freelocale(cLocale);
118+
#endif
119+
}
120+
}
99121

100122
const char* filename;
101123

@@ -122,6 +144,12 @@ public:
122144

123145
std::string stringBuffer;
124146

147+
#if defined(_WIN32)
148+
_locale_t cLocale;
149+
#else
150+
locale_t cLocale;
151+
#endif
152+
125153
std::string getCurrentLine(int firstCol, int lastCol) const {
126154
std::stringstream ss;
127155
const char* eol_c = strchr(buf + lineStartPos, '\n');

lib/cached/lexer.yy.cpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,10 +1402,29 @@ namespace MiniZinc {
14021402
return true;
14031403
}
14041404

1405-
bool strtofloatval(const char* s, double& v) {
1406-
std::istringstream iss(s);
1407-
iss >> v;
1408-
return !iss.fail();
1405+
bool strtofloatval(void* parm, const char* s, double& val) {
1406+
MiniZinc::ParserState* pp =
1407+
static_cast<MiniZinc::ParserState*>(parm);
1408+
#if defined(_WIN32)
1409+
_locale_t c_loc = pp->cLocale;
1410+
if (!c_loc) return false;
1411+
errno = 0;
1412+
char* end = nullptr;
1413+
val = _strtod_l(s, &end, c_loc);
1414+
#else
1415+
locale_t c_loc = pp->cLocale;
1416+
if (!c_loc) return false;
1417+
errno = 0;
1418+
char* end = nullptr;
1419+
val = strtod_l(s, &end, c_loc);
1420+
#endif
1421+
if (end == s) return false;
1422+
if (*end != '\0') return false;
1423+
if (!std::isfinite(val)) return false;
1424+
if (errno != 0 && errno != ERANGE) {
1425+
return false;
1426+
}
1427+
return true;
14091428
}
14101429

14111430
void clearBuffer(void* parm) {
@@ -2041,7 +2060,7 @@ YY_RULE_SETUP
20412060
case 26:
20422061
YY_RULE_SETUP
20432062
{
2044-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
2063+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
20452064
return MZN_FLOAT_LITERAL;
20462065
else
20472066
return MZN_INVALID_FLOAT_LITERAL;
@@ -2083,7 +2102,7 @@ YY_RULE_SETUP
20832102
case 31:
20842103
YY_RULE_SETUP
20852104
{
2086-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
2105+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
20872106
return MZN_FLOAT_LITERAL;
20882107
else
20892108
return MZN_INVALID_FLOAT_LITERAL;
@@ -2092,7 +2111,7 @@ YY_RULE_SETUP
20922111
case 32:
20932112
YY_RULE_SETUP
20942113
{
2095-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
2114+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
20962115
return MZN_FLOAT_LITERAL;
20972116
else
20982117
return MZN_INVALID_FLOAT_LITERAL;
@@ -2101,7 +2120,7 @@ YY_RULE_SETUP
21012120
case 33:
21022121
YY_RULE_SETUP
21032122
{
2104-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
2123+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
21052124
return MZN_FLOAT_LITERAL;
21062125
else
21072126
return MZN_INVALID_FLOAT_LITERAL;

lib/cached/md5_cached.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
set(lexer_lxx_md5_cached "32d69703b364c176cd579c08cd355b7a")
1+
set(lexer_lxx_md5_cached "7e7c8bb77b26ec4392039dd6c72a514b")
22
set(parser_yxx_md5_cached "05473ab05bdc731a974f36a75cfa93cf")
33
set(regex_lexer_lxx_md5_cached "5b4c138ececa989083ac2ed39f8959c2")
44
set(regex_parser_yxx_md5_cached "e44695248dcfad64a56982f126f8e4f5")

lib/lexer.lxx

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,29 @@ namespace MiniZinc {
100100
return true;
101101
}
102102

103-
bool strtofloatval(const char* s, double& v) {
104-
std::istringstream iss(s);
105-
iss >> v;
106-
return !iss.fail();
103+
bool strtofloatval(void* parm, const char* s, double& val) {
104+
MiniZinc::ParserState* pp =
105+
static_cast<MiniZinc::ParserState*>(parm);
106+
#if defined(_WIN32)
107+
_locale_t c_loc = pp->cLocale;
108+
if (!c_loc) return false;
109+
errno = 0;
110+
char* end = nullptr;
111+
val = _strtod_l(s, &end, c_loc);
112+
#else
113+
locale_t c_loc = pp->cLocale;
114+
if (!c_loc) return false;
115+
errno = 0;
116+
char* end = nullptr;
117+
val = strtod_l(s, &end, c_loc);
118+
#endif
119+
if (end == s) return false;
120+
if (*end != '\0') return false;
121+
if (!std::isfinite(val)) return false;
122+
if (errno != 0 && errno != ERANGE) {
123+
return false;
124+
}
125+
return true;
107126
}
108127

109128
void clearBuffer(void* parm) {
@@ -185,7 +204,7 @@ namespace MiniZinc {
185204
"false" { yylval->iValue = 0; return MZN_BOOL_LITERAL; }
186205

187206
0[xX]([0-9a-fA-F]*\.[0-9a-fA-F]+|[0-9a-fA-F]+\.)([pP][+-]?[0-9]+)|(0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+) {
188-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
207+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
189208
return MZN_FLOAT_LITERAL;
190209
else
191210
return MZN_INVALID_FLOAT_LITERAL;
@@ -214,19 +233,19 @@ namespace MiniZinc {
214233
}
215234

216235
[0-9]+\.[0-9]+ {
217-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
236+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
218237
return MZN_FLOAT_LITERAL;
219238
else
220239
return MZN_INVALID_FLOAT_LITERAL;
221240
}
222241
[0-9]+\.[0-9]+[Ee][+-]?[0-9]+ {
223-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
242+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
224243
return MZN_FLOAT_LITERAL;
225244
else
226245
return MZN_INVALID_FLOAT_LITERAL;
227246
}
228247
[0-9]+[Ee][+-]?[0-9]+ {
229-
if (::MiniZinc::strtofloatval(yytext, yylval->dValue))
248+
if (::MiniZinc::strtofloatval(yyget_extra(yyscanner), yytext, yylval->dValue))
230249
return MZN_FLOAT_LITERAL;
231250
else
232251
return MZN_INVALID_FLOAT_LITERAL;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/***
2+
!Test
3+
solvers: [gecode]
4+
expected: !Result
5+
solution: !Solution
6+
***/
7+
8+
float: x ::output = 4.94065645841247e-324;
9+
float: y ::output = 0x1.8p+1;
10+
float: z ::output = 0x1p-1022;

0 commit comments

Comments
 (0)