lexer: Reimplement for better testability and internationalization.
[pspp-builds.git] / src / language / lexer / value-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2005, 2006, 2009, 2010, 2011 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
19 #include "value-parser.h"
20
21 #include <stdbool.h>
22
23 #include "data/data-in.h"
24 #include "data/format.h"
25 #include "data/value.h"
26 #include "language/lexer/lexer.h"
27 #include "libpspp/cast.h"
28 #include "libpspp/i18n.h"
29 #include "libpspp/message.h"
30 #include "libpspp/str.h"
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34 #define N_(msgid) msgid
35
36 static bool parse_number (struct lexer *, double *, const enum fmt_type *);
37
38 /* Parses and stores a numeric value, or a range of the form "x
39    THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
40    y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
41    value and returns success.
42
43    Numeric values are always accepted.  If FORMAT is nonnull,
44    then string values are also accepted, and converted to numeric
45    values using *FORMAT. */
46 bool
47 parse_num_range (struct lexer *lexer,
48                  double *x, double *y, const enum fmt_type *format)
49 {
50   if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
51     *x = LOWEST;
52   else if (!parse_number (lexer, x, format))
53     return false;
54
55   if (lex_match_id (lexer, "THRU"))
56     {
57       if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
58         *y = HIGHEST;
59       else if (!parse_number (lexer, y, format))
60         return false;
61
62       if (*y < *x)
63         {
64           double t;
65           msg (SW, _("Low end of range (%g) is below high end (%g).  "
66                      "The range will be treated as reversed."),
67                *x, *y);
68           t = *x;
69           *x = *y;
70           *y = t;
71         }
72       else if (*x == *y)
73         msg (SW, _("Ends of range are equal (%g)."), *x);
74
75       return true;
76     }
77   else
78     {
79       if (*x == LOWEST)
80         {
81           msg (SE, _("LO or LOWEST must be part of a range."));
82           return false;
83         }
84       *y = *x;
85     }
86
87   return true;
88 }
89
90 /* Parses a number and stores it in *X.  Returns success.
91
92    Numeric values are always accepted.  If FORMAT is nonnull,
93    then string values are also accepted, and converted to numeric
94    values using *FORMAT. */
95 static bool
96 parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
97 {
98   if (lex_is_number (lexer))
99     {
100       *x = lex_number (lexer);
101       lex_get (lexer);
102       return true;
103     }
104   else if (lex_is_string (lexer) && format != NULL)
105     {
106       union value v;
107
108       assert (fmt_get_category (*format) != FMT_CAT_STRING);
109
110       if (!data_in_msg (lex_tokss (lexer), "UTF-8", *format, &v, 0, NULL))
111         return false;
112
113       lex_get (lexer);
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 (format != NULL)
125         lex_error (lexer, _("expecting number or data string"));
126       else
127         lex_force_num (lexer);
128       return false;
129     }
130 }
131
132 /* Parses the current token from LEXER into value V, which must
133    already have been initialized with the specified WIDTH.
134    Returns true if successful, false otherwise. */
135 bool
136 parse_value (struct lexer *lexer, union value *v, int width)
137 {
138   if (width == 0)
139     {
140       if (!lex_force_num (lexer))
141         return false;
142       v->f = lex_tokval (lexer);
143     }
144   else if (lex_force_string (lexer))
145     {
146       const char *s = lex_tokcstr (lexer);
147       value_copy_str_rpad (v, width, CHAR_CAST_BUG (const uint8_t *, s), ' ');
148     }
149   else
150     return false;
151
152   lex_get (lexer);
153
154   return true;
155 }