treewide: Use struct fmt_spec by value instead of pointer in most cases.
[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   int start_ofs = lex_ofs (lexer);
52
53   if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
54     *x = LOWEST;
55   else if (!parse_number (lexer, x, format))
56     return false;
57
58   if (lex_match_id (lexer, "THRU"))
59     {
60       if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
61         *y = HIGHEST;
62       else if (!parse_number (lexer, y, format))
63         return false;
64
65       if (*y < *x)
66         {
67           double t;
68           lex_ofs_msg (lexer, SW, start_ofs, lex_ofs (lexer) - 1,
69                        ("The high end of the range (%.*g) is below the low end "
70                         "(%.*g).  The range will be treated as if reversed."),
71                        DBL_DIG + 1, *y, DBL_DIG + 1, *x);
72           t = *x;
73           *x = *y;
74           *y = t;
75         }
76       else if (*x == *y)
77         lex_ofs_msg (lexer, SW, start_ofs, lex_ofs (lexer) - 1,
78                      _("Ends of range are equal (%.*g)."), DBL_DIG + 1, *x);
79
80       return true;
81     }
82   else
83     {
84       if (*x == LOWEST)
85         {
86           lex_next_msg (lexer, SW, -1, -1,
87                         _("%s or %s must be part of a range."),
88                          "LO", "LOWEST");
89           return false;
90         }
91       *y = *x;
92     }
93
94   return true;
95 }
96
97 /* Parses a number and stores it in *X.  Returns success.
98
99    Numeric values are always accepted.  If FORMAT is nonnull,
100    then string values are also accepted, and converted to numeric
101    values using *FORMAT. */
102 static bool
103 parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
104 {
105   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,
112                         settings_get_fmt_settings (), &v, 0, NULL))
113         return false;
114
115       lex_get (lexer);
116       *x = v.f;
117       if (*x == SYSMIS)
118         {
119           lex_next_error (lexer, -1, -1,
120                           _("System-missing value is not valid here."));
121           return false;
122         }
123       return true;
124     }
125
126     if (lex_force_num (lexer))
127     {
128       *x = lex_number (lexer);
129       lex_get (lexer);
130       return true;
131     }
132
133     return false;
134 }
135
136 /* Parses the current token from LEXER into value V, which must already have
137    been initialized with the specified VAR's WIDTH.  Returns true if
138    successful, false otherwise. */
139 bool
140 parse_value (struct lexer *lexer, union value *v, const struct variable *var)
141 {
142   int width = var_get_width (var);
143   if (width == 0)
144     {
145       struct fmt_spec format = var_get_print_format (var);
146       return parse_number (lexer, &v->f, &format.type);
147     }
148   else if (lex_force_string (lexer))
149     {
150       struct substring out;
151       if (recode_pedantically (var_get_encoding (var), "UTF-8",
152                                lex_tokss (lexer), NULL, &out))
153         {
154           lex_error (lexer, _("This string is not representable in the "
155                               "dataset encoding."));
156           return false;
157         }
158       if (out.length > width)
159         {
160           lex_error (lexer, _("This %zu-byte string is too long for "
161                               "variable %s with width %d."),
162                      out.length, var_get_name (var), width);
163           ss_dealloc (&out);
164           return false;
165         }
166
167       value_copy_buf_rpad (v, width, CHAR_CAST (const uint8_t *, out.string),
168                            out.length, ' ');
169       ss_dealloc (&out);
170     }
171   else
172     return false;
173
174   lex_get (lexer);
175
176   return true;
177 }