Make value_set_missing(), etc. tolerate values of width -1.
[pspp-builds.git] / src / language / lexer / range-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2005, 2006 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 #include "range-parser.h"
19 #include <stdbool.h>
20 #include <data/data-in.h>
21 #include <libpspp/message.h>
22 #include "lexer.h"
23 #include <libpspp/str.h>
24 #include <data/value.h>
25 #include <data/format.h>
26
27 #include "gettext.h"
28 #define _(msgid) gettext (msgid)
29 #define N_(msgid) msgid
30
31 static bool parse_number (struct lexer *, double *, const enum fmt_type *);
32
33 /* Parses and stores a numeric value, or a range of the form "x
34    THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
35    y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
36    value and returns success.
37
38    Numeric values are always accepted.  If FORMAT is nonnull,
39    then string values are also accepted, and converted to numeric
40    values using *FORMAT. */
41 bool
42 parse_num_range (struct lexer *lexer,
43                  double *x, double *y, const enum fmt_type *format)
44 {
45   if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
46     *x = LOWEST;
47   else if (!parse_number (lexer, x, format))
48     return false;
49
50   if (lex_match_id (lexer, "THRU"))
51     {
52       if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
53         *y = HIGHEST;
54       else if (!parse_number (lexer, y, format))
55         return false;
56
57       if (*y < *x)
58         {
59           double t;
60           msg (SW, _("Low end of range (%g) is below high end (%g).  "
61                      "The range will be treated as reversed."),
62                *x, *y);
63           t = *x;
64           *x = *y;
65           *y = t;
66         }
67       else if (*x == *y)
68         msg (SW, _("Ends of range are equal (%g)."), *x);
69
70       return true;
71     }
72   else
73     {
74       if (*x == LOWEST)
75         {
76           msg (SE, _("LO or LOWEST must be part of a range."));
77           return false;
78         }
79       *y = *x;
80     }
81
82   return true;
83 }
84
85 /* Parses a number and stores it in *X.  Returns success.
86
87    Numeric values are always accepted.  If FORMAT is nonnull,
88    then string values are also accepted, and converted to numeric
89    values using *FORMAT. */
90 static bool
91 parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
92 {
93   if (lex_is_number (lexer))
94     {
95       *x = lex_number (lexer);
96       lex_get (lexer);
97       return true;
98     }
99   else if (lex_token (lexer) == T_STRING && format != NULL)
100     {
101       union value v;
102       data_in (ds_ss (lex_tokstr (lexer)), LEGACY_NATIVE,
103                *format, 0, 0, 0, &v, 0);
104       lex_get (lexer);
105       *x = v.f;
106       if (*x == SYSMIS)
107         {
108           msg (SE, _("System-missing value is not valid here."));
109           return false;
110         }
111       return true;
112     }
113   else
114     {
115       if (format != NULL)
116         lex_error (lexer, _("expecting number or data string"));
117       else
118         lex_force_num (lexer);
119       return false;
120     }
121 }