Change many %g format specifiers to %.*g with precision DBL_DIG + 1.
[pspp] / src / language / lexer / value-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2005, 2006, 2009, 2010, 2011, 2014 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "value-parser.h"
20
21 #include <float.h>
22 #include <stdbool.h>
23
24 #include "data/data-in.h"
25 #include "data/format.h"
26 #include "data/value.h"
27 #include "language/lexer/lexer.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/i18n.h"
30 #include "libpspp/message.h"
31 #include "libpspp/str.h"
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35 #define N_(msgid) msgid
36
37 static bool parse_number (struct lexer *, double *, const enum fmt_type *);
38
39 /* Parses and stores a numeric value, or a range of the form "x
40    THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
41    y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
42    value and returns success.
43
44    Numeric values are always accepted.  If FORMAT is nonnull,
45    then string values are also accepted, and converted to numeric
46    values using *FORMAT. */
47 bool
48 parse_num_range (struct lexer *lexer,
49                  double *x, double *y, const enum fmt_type *format)
50 {
51   if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
52     *x = LOWEST;
53   else if (!parse_number (lexer, x, format))
54     return false;
55
56   if (lex_match_id (lexer, "THRU"))
57     {
58       if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
59         *y = HIGHEST;
60       else if (!parse_number (lexer, y, format))
61         return false;
62
63       if (*y < *x)
64         {
65           double t;
66           msg (SW, _("The high end of the range (%.*g) is below the low end "
67                      "(%.*g).  The range will be treated as if reversed."),
68                DBL_DIG + 1, *y, DBL_DIG + 1, *x);
69           t = *x;
70           *x = *y;
71           *y = t;
72         }
73       else if (*x == *y)
74         msg (SW, _("Ends of range are equal (%.*g)."), DBL_DIG + 1, *x);
75
76       return true;
77     }
78   else
79     {
80       if (*x == LOWEST)
81         {
82           msg (SE, _("LO or LOWEST must be part of a range."));
83           return false;
84         }
85       *y = *x;
86     }
87
88   return true;
89 }
90
91 /* Parses a number and stores it in *X.  Returns success.
92
93    Numeric values are always accepted.  If FORMAT is nonnull,
94    then string values are also accepted, and converted to numeric
95    values using *FORMAT. */
96 static bool
97 parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
98 {
99   if (lex_is_number (lexer))
100     {
101       *x = lex_number (lexer);
102       lex_get (lexer);
103       return true;
104     }
105   else if (lex_is_string (lexer) && format != NULL)
106     {
107       union value v;
108
109       assert (fmt_get_category (*format) != FMT_CAT_STRING);
110
111       if (!data_in_msg (lex_tokss (lexer), "UTF-8", *format, &v, 0, NULL))
112         return false;
113
114       lex_get (lexer);
115       *x = v.f;
116       if (*x == SYSMIS)
117         {
118           msg (SE, _("System-missing value is not valid here."));
119           return false;
120         }
121       return true;
122     }
123   else
124     {
125       if (format != NULL)
126         lex_error (lexer, _("expecting number or data string"));
127       else
128         lex_force_num (lexer);
129       return false;
130     }
131 }
132
133 /* Parses the current token from LEXER into value V, which must already have
134    been initialized with the specified VAR's WIDTH.  Returns true if
135    successful, false otherwise. */
136 bool
137 parse_value (struct lexer *lexer, union value *v, const struct variable *var)
138 {
139   int width = var_get_width (var);
140   if (width == 0)
141     return parse_number (lexer, &v->f, &var_get_print_format (var)->type);
142   else if (lex_force_string (lexer))
143     {
144       const char *s = lex_tokcstr (lexer);
145       value_copy_str_rpad (v, width, CHAR_CAST_BUG (const uint8_t *, s), ' ');
146     }
147   else
148     return false;
149
150   lex_get (lexer);
151
152   return true;
153 }