c4a590e56e6b6abde9582f09ecd7a46e3caa08da
[pspp-builds.git] / src / language / lexer / range-parser.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2005, 2006 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    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, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17    02110-1301, USA. */
18
19 #include <config.h>
20 #include "range-parser.h"
21 #include <stdbool.h>
22 #include <data/data-in.h>
23 #include <libpspp/message.h>
24 #include "lexer.h"
25 #include <libpspp/magic.h>
26 #include <libpspp/str.h>
27 #include <data/value.h>
28
29 #include "gettext.h"
30 #define _(msgid) gettext (msgid)
31 #define N_(msgid) msgid
32
33 static bool parse_number (struct lexer *, double *, const enum fmt_type *);
34
35 /* Parses and stores a numeric value, or a range of the form "x
36    THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
37    y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
38    value and returns success.
39
40    Numeric values are always accepted.  If FORMAT is nonnull,
41    then string values are also accepted, and converted to numeric
42    values using *FORMAT. */
43 bool
44 parse_num_range (struct lexer *lexer,
45                  double *x, double *y, const enum fmt_type *format)
46 {
47   if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
48     *x = LOWEST;
49   else if (!parse_number (lexer, x, format))
50     return false;
51
52   if (lex_match_id (lexer, "THRU"))
53     {
54       if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
55         *y = HIGHEST;
56       else if (!parse_number (lexer, y, format))
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 FORMAT is nonnull,
90    then string values are also accepted, and converted to numeric
91    values using *FORMAT. */
92 static bool
93 parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
94 {
95   if (lex_is_number (lexer))
96     {
97       *x = lex_number (lexer);
98       lex_get (lexer);
99       return true;
100     }
101   else if (lex_token (lexer) == T_STRING && format != NULL)
102     {
103       union value v;
104       data_in (ds_ss (lex_tokstr (lexer)), *format, 0, 0, &v, 0);
105       lex_get (lexer);
106       *x = v.f;
107       if (*x == SYSMIS)
108         {
109           msg (SE, _("System-missing value is not valid here."));
110           return false;
111         }
112       return true;
113     }
114   else
115     {
116       if (format != NULL)
117         lex_error (lexer, _("expecting number or data string"));
118       else
119         lex_force_num (lexer);
120       return false;
121     }
122 }