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