Completely rewrite src/data/format.[ch], to achieve better
[pspp-builds.git] / src / language / lexer / range-parser.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2005, 2006 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "range-parser.h"
22 #include <stdbool.h>
23 #include <data/data-in.h>
24 #include <libpspp/message.h>
25 #include "lexer.h"
26 #include <libpspp/magic.h>
27 #include <libpspp/str.h>
28 #include <data/value.h>
29
30 #include "gettext.h"
31 #define _(msgid) gettext (msgid)
32 #define N_(msgid) msgid
33
34 static bool parse_number (double *, const struct fmt_spec *);
35
36 /* Parses and stores a numeric value, or a range of the form "x
37    THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
38    y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
39    value and returns success.
40
41    Numeric values are always accepted.  If F is nonnull, then
42    string values are also accepted, and converted to numeric
43    values using the specified format. */
44 bool
45 parse_num_range (double *x, double *y, const struct fmt_spec *f) 
46 {
47   if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
48     *x = LOWEST;
49   else if (!parse_number (x, f))
50     return false;
51
52   if (lex_match_id ("THRU")) 
53     {
54       if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
55         *y = HIGHEST;
56       else if (!parse_number (y, f))
57         return false;
58
59       if (*y < *x) 
60         {
61           double t;
62           msg (SW, _("Low end of range (%g) is below high end (%g).  "
63                      "The range will be treated as reversed."),
64                *x, *y);
65           t = *x;
66           *x = *y;
67           *y = t;
68         }
69       else if (*x == *y) 
70         msg (SW, _("Ends of range are equal (%g)."), *x);
71
72       return true;
73     }
74   else
75     {
76       if (*x == LOWEST) 
77         {
78           msg (SE, _("LO or LOWEST must be part of a range."));
79           return false;
80         }
81       *y = *x;
82     }
83   
84   return true;
85 }
86
87 /* Parses a number and stores it in *X.  Returns success.
88
89    Numeric values are always accepted.  If F is nonnull, then
90    string values are also accepted, and converted to numeric
91    values using the specified format. */
92 static bool
93 parse_number (double *x, const struct fmt_spec *f)
94 {
95   if (lex_is_number ()) 
96     {
97       *x = lex_number ();
98       lex_get ();
99       return true;
100     }
101   else if (token == T_STRING && f != NULL) 
102     {
103       struct data_in di;
104       union value v;
105       di.s = ds_data (&tokstr);
106       di.e = ds_end (&tokstr);
107       di.v = &v;
108       di.flags = 0;
109       di.f1 = 1;
110       di.f2 = ds_length (&tokstr);
111       di.format = *f;
112       data_in (&di);
113       lex_get ();
114       *x = v.f;
115       if (*x == SYSMIS)
116         {
117           msg (SE, _("System-missing value is not valid here."));
118           return false;
119         }
120       return true;
121     }
122   else 
123     {
124       if (f != NULL)
125         lex_error (_("expecting number or data string"));
126       else
127         lex_force_num ();
128       return false; 
129     }
130 }