d6d352b8bfff5c56057267b9af71bbe89ebb353f
[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 (struct lexer *, double *, const enum fmt_type *);
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 FORMAT is nonnull,
42    then string values are also accepted, and converted to numeric
43    values using *FORMAT. */
44 bool
45 parse_num_range (struct lexer *lexer,
46                  double *x, double *y, const enum fmt_type *format) 
47 {
48   if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
49     *x = LOWEST;
50   else if (!parse_number (lexer, x, format))
51     return false;
52
53   if (lex_match_id (lexer, "THRU")) 
54     {
55       if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
56         *y = HIGHEST;
57       else if (!parse_number (lexer, y, format))
58         return false;
59
60       if (*y < *x) 
61         {
62           double t;
63           msg (SW, _("Low end of range (%g) is below high end (%g).  "
64                      "The range will be treated as reversed."),
65                *x, *y);
66           t = *x;
67           *x = *y;
68           *y = t;
69         }
70       else if (*x == *y) 
71         msg (SW, _("Ends of range are equal (%g)."), *x);
72
73       return true;
74     }
75   else
76     {
77       if (*x == LOWEST) 
78         {
79           msg (SE, _("LO or LOWEST must be part of a range."));
80           return false;
81         }
82       *y = *x;
83     }
84   
85   return true;
86 }
87
88 /* Parses a number and stores it in *X.  Returns success.
89
90    Numeric values are always accepted.  If FORMAT is nonnull,
91    then string values are also accepted, and converted to numeric
92    values using *FORMAT. */
93 static bool
94 parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
95 {
96   if (lex_is_number (lexer)) 
97     {
98       *x = lex_number (lexer);
99       lex_get (lexer);
100       return true;
101     }
102   else if (lex_token (lexer) == T_STRING && format != NULL) 
103     {
104       union value v;
105       data_in (ds_ss (lex_tokstr (lexer)), *format, 0, 0, &v, 0);
106       lex_get (lexer);
107       *x = v.f;
108       if (*x == SYSMIS)
109         {
110           msg (SE, _("System-missing value is not valid here."));
111           return false;
112         }
113       return true;
114     }
115   else 
116     {
117       if (format != NULL)
118         lex_error (lexer, _("expecting number or data string"));
119       else
120         lex_force_num (lexer);
121       return false; 
122     }
123 }