refactoring
[pspp] / src / language / data-io / placement-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2006, 2010, 2011, 2012 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 "language/data-io/placement-parser.h"
20
21 #include <assert.h>
22
23 #include "data/format.h"
24 #include "language/lexer/format-parser.h"
25 #include "language/lexer/lexer.h"
26 #include "libpspp/message.h"
27 #include "libpspp/pool.h"
28 #include "libpspp/str.h"
29
30 #include "gl/c-strcase.h"
31 #include "gl/xalloc.h"
32 #include "gl/xsize.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36
37 /* Extensions to the format specifiers used only for
38    placement. */
39 enum
40   {
41     PRS_TYPE_T = SCHAR_MAX - 3, /* Tab to absolute column. */
42     PRS_TYPE_X,                 /* Skip columns. */
43     PRS_TYPE_NEW_REC            /* Next record. */
44   };
45
46 static bool fixed_parse_columns (struct lexer *, struct pool *, size_t n_vars,
47                                  enum fmt_use, struct fmt_spec **, size_t *);
48 static bool fixed_parse_fortran (struct lexer *l, struct pool *, enum fmt_use,
49                                  struct fmt_spec **, size_t *);
50
51 /* Parses Fortran-like or column-based specifications for placing
52    variable data in fixed positions in columns and rows, that is,
53    formats like those parsed by DATA LIST or PRINT.  Returns true
54    only if successful.
55
56    The formats parsed are either input or output formats, according
57    to USE.
58
59    If USE is FMT_FOR_INPUT, then T, X, and / "formats" are parsed,
60    in addition to regular formats.  If USE is FMT_FOR_OUTPUT, then
61    T and X "formats" are parsed but not /.
62
63    If successful, formats for N_VARS variables are stored in
64    *FORMATS, and the number of formats required is stored in
65    *FORMAT_CNT.  *FORMAT_CNT may be greater than N_VARS because
66    of T, X, and / "formats", but success guarantees that exactly
67    N_VARS variables will be placed by the output formats.  The
68    caller should call execute_placement_format to process those
69    "formats" in interpreting the output.
70
71    Uses POOL for allocation.  When the caller is finished
72    interpreting *FORMATS, POOL may be destroyed. */
73 bool
74 parse_var_placements (struct lexer *lexer, struct pool *pool, size_t n_vars,
75                       enum fmt_use use,
76                       struct fmt_spec **formats, size_t *n_formats)
77 {
78   assert (n_vars > 0);
79   if (lex_is_number (lexer))
80     return fixed_parse_columns (lexer, pool, n_vars, use,
81                                 formats, n_formats);
82   else if (lex_match (lexer, T_LPAREN))
83     {
84       size_t n_assignments;
85       size_t i;
86
87       if (!fixed_parse_fortran (lexer, pool, use, formats, n_formats))
88         return false;
89
90       n_assignments = 0;
91       for (i = 0; i < *n_formats; i++)
92         n_assignments += (*formats)[i].type < FMT_NUMBER_OF_FORMATS;
93
94       if (n_assignments != n_vars)
95         {
96           msg (SE, _("Number of variables specified (%zu) "
97                      "differs from number of variable formats (%zu)."),
98                n_vars, n_assignments);
99           return false;
100         }
101
102       return true;
103     }
104   else
105     {
106       msg (SE, _("SPSS-like or Fortran-like format "
107                  "specification expected after variable names."));
108       return false;
109     }
110 }
111
112 /* Implements parse_var_placements for column-based formats. */
113 static bool
114 fixed_parse_columns (struct lexer *lexer, struct pool *pool, size_t n_vars,
115                      enum fmt_use use,
116                      struct fmt_spec **formats, size_t *n_formats)
117 {
118   struct fmt_spec format;
119   int fc, lc;
120   size_t i;
121
122   if (!parse_column_range (lexer, 1, &fc, &lc, NULL))
123     return false;
124
125   /* Divide columns evenly. */
126   format.w = (lc - fc + 1) / n_vars;
127   if ((lc - fc + 1) % n_vars)
128     {
129       msg (SE, _("The %d columns %d-%d "
130                  "can't be evenly divided into %zu fields."),
131            lc - fc + 1, fc, lc, n_vars);
132       return false;
133     }
134
135   /* Format specifier. */
136   if (lex_match (lexer, T_LPAREN))
137     {
138       /* Get format type. */
139       if (lex_token (lexer) == T_ID)
140         {
141           if (!parse_format_specifier_name (lexer, &format.type))
142             return false;
143           lex_match (lexer, T_COMMA);
144         }
145       else
146         format.type = FMT_F;
147
148       /* Get decimal places. */
149       if (lex_is_integer (lexer))
150         {
151           format.d = lex_integer (lexer);
152           lex_get (lexer);
153         }
154       else
155         format.d = 0;
156
157       if (!lex_force_match (lexer, T_RPAREN))
158         return false;
159     }
160   else
161     {
162       format.type = FMT_F;
163       format.d = 0;
164     }
165   if (!fmt_check (&format, use))
166     return false;
167
168   *formats = pool_nalloc (pool, n_vars + 1, sizeof **formats);
169   *n_formats = n_vars + 1;
170   (*formats)[0].type = (enum fmt_type) PRS_TYPE_T;
171   (*formats)[0].w = fc;
172   for (i = 1; i <= n_vars; i++)
173     (*formats)[i] = format;
174   return true;
175 }
176
177 /* Implements parse_var_placements for Fortran-like formats. */
178 static bool
179 fixed_parse_fortran (struct lexer *lexer, struct pool *pool, enum fmt_use use,
180                      struct fmt_spec **formats, size_t *n_formats)
181 {
182   size_t formats_allocated = 0;
183   size_t formats_used = 0;
184
185   *formats = NULL;
186   while (!lex_match (lexer, T_RPAREN))
187     {
188       struct fmt_spec f;
189       struct fmt_spec *new_formats;
190       size_t n_new_formats;
191       size_t count;
192       size_t formats_needed;
193
194       /* Parse count. */
195       if (lex_is_integer (lexer))
196         {
197           count = lex_integer (lexer);
198           lex_get (lexer);
199         }
200       else
201         count = 1;
202
203       /* Parse format specifier. */
204       if (lex_match (lexer, T_LPAREN))
205         {
206           /* Call ourselves recursively to handle parentheses. */
207           if (!fixed_parse_fortran (lexer, pool, use,
208                                     &new_formats, &n_new_formats))
209             return false;
210         }
211       else
212         {
213           new_formats = &f;
214           n_new_formats = 1;
215           if (use == FMT_FOR_INPUT && lex_match (lexer, T_SLASH))
216             f.type = (enum fmt_type) PRS_TYPE_NEW_REC;
217           else
218             {
219               char type[FMT_TYPE_LEN_MAX + 1];
220
221               if (!parse_abstract_format_specifier (lexer, type, &f.w, &f.d))
222                 return false;
223
224               if (!c_strcasecmp (type, "T"))
225                 f.type = (enum fmt_type) PRS_TYPE_T;
226               else if (!c_strcasecmp (type, "X"))
227                 {
228                   f.type = (enum fmt_type) PRS_TYPE_X;
229                   f.w = count;
230                   count = 1;
231                 }
232               else
233                 {
234                   if (!fmt_from_name (type, &f.type))
235                     {
236                       msg (SE, _("Unknown format type `%s'."), type);
237                       return false;
238                     }
239                   if (!fmt_check (&f, use))
240                     return false;
241                 }
242             }
243         }
244
245       /* Add COUNT copies of the NEW_FORMAT_CNT formats in
246          NEW_FORMATS to FORMATS. */
247       if (n_new_formats != 0
248           && size_overflow_p (xtimes (xsum (formats_used,
249                                             xtimes (count, n_new_formats)),
250                                       sizeof *formats)))
251         xalloc_die ();
252       formats_needed = count * n_new_formats;
253       if (formats_used + formats_needed > formats_allocated)
254         {
255           formats_allocated = formats_used + formats_needed;
256           *formats = pool_2nrealloc (pool, *formats, &formats_allocated,
257                                      sizeof **formats);
258         }
259       for (; count > 0; count--)
260         {
261           memcpy (&(*formats)[formats_used], new_formats,
262                   sizeof **formats * n_new_formats);
263           formats_used += n_new_formats;
264         }
265
266       lex_match (lexer, T_COMMA);
267     }
268
269   *n_formats = formats_used;
270   return true;
271 }
272
273 /* Checks whether FORMAT represents one of the special "formats"
274    for T, X, or /.  If so, updates *RECORD or *COLUMN (or both)
275    as appropriate, and returns true.  Otherwise, returns false
276    without any side effects. */
277 bool
278 execute_placement_format (const struct fmt_spec *format,
279                           int *record, int *column)
280 {
281   switch ((int) format->type)
282     {
283     case PRS_TYPE_X:
284       *column += format->w;
285       return true;
286
287     case PRS_TYPE_T:
288       *column = format->w;
289       return true;
290
291     case PRS_TYPE_NEW_REC:
292       (*record)++;
293       *column = 1;
294       return true;
295
296     default:
297       assert (format->type < FMT_NUMBER_OF_FORMATS);
298       return false;
299     }
300 }
301
302 static bool
303 parse_column__ (int value, int base, int *column)
304 {
305   assert (base == 0 || base == 1);
306   *column = value - base + 1;
307   if (*column < 1)
308     {
309       if (base == 1)
310         msg (SE, _("Column positions for fields must be positive."));
311       else
312         msg (SE, _("Column positions for fields must not be negative."));
313       return false;
314     }
315   return true;
316 }
317
318 /* Parses a BASE-based column using LEXER.  Returns true and
319    stores a 1-based column number into *COLUMN if successful,
320    otherwise emits an error message and returns false.
321
322    If BASE is 0, zero-based column numbers are parsed; if BASE is
323    1, 1-based column numbers are parsed.  Regardless of BASE, the
324    values stored in *FIRST_COLUMN and *LAST_COLUMN are
325    1-based. */
326 bool
327 parse_column (struct lexer *lexer, int base, int *column)
328 {
329   assert (base == 0 || base == 1);
330
331   if (!lex_force_int (lexer)
332       || !parse_column__ (lex_integer (lexer), base, column))
333     return false;
334
335   lex_get (lexer);
336   return true;
337 }
338
339 /* Parse a column or a range of columns, specified as a single
340    integer or two integers delimited by a dash.  Stores the range
341    in *FIRST_COLUMN and *LAST_COLUMN.  (If only a single integer
342    is given, it is stored in both.)  If RANGE_SPECIFIED is
343    non-null, then *RANGE_SPECIFIED is set to true if the syntax
344    contained a dash, false otherwise.  Returns true if
345    successful, false if the syntax was invalid or the values
346    specified did not make sense.
347
348    If BASE is 0, zero-based column numbers are parsed; if BASE is
349    1, 1-based column numbers are parsed.  Regardless of BASE, the
350    values stored in *FIRST_COLUMN and *LAST_COLUMN are
351    1-based. */
352 bool
353 parse_column_range (struct lexer *lexer, int base,
354                     int *first_column, int *last_column,
355                     bool *range_specified)
356 {
357   /* First column. */
358   if (!lex_force_int (lexer)
359       || !parse_column__ (lex_integer (lexer), base, first_column))
360     return false;
361   lex_get (lexer);
362
363   /* Last column. */
364   if (lex_is_integer (lexer) && lex_integer (lexer) < 0)
365     {
366       if (!parse_column__ (-lex_integer (lexer), base, last_column))
367         return false;
368       lex_get (lexer);
369
370       if (*last_column < *first_column)
371         {
372           msg (SE, _("The ending column for a field must be "
373                      "greater than the starting column."));
374           return false;
375         }
376
377       if (range_specified)
378         *range_specified = true;
379     }
380   else
381     {
382       *last_column = *first_column;
383       if (range_specified)
384         *range_specified = false;
385     }
386
387   return true;
388 }
389
390 /* Parses a (possibly empty) sequence of slashes, each of which
391    may be followed by an integer.  A slash on its own increases
392    *RECORD by 1 and sets *COLUMN to 1.  A slash followed by an
393    integer sets *RECORD to the integer, as long as that increases
394    *RECORD, and sets *COLUMN to 1.
395
396    Returns true if successful, false on syntax error. */
397 bool
398 parse_record_placement (struct lexer *lexer, int *record, int *column)
399 {
400   while (lex_match (lexer, T_SLASH))
401     {
402       if (lex_is_number (lexer))
403         {
404           if (!lex_force_int_range (lexer, NULL, *record + 1, INT_MAX))
405             return false;
406           *record = lex_integer (lexer);
407           lex_get (lexer);
408         }
409       else
410         (*record)++;
411       *column = 1;
412     }
413   assert (*record >= 1);
414
415   return true;
416 }