c56e089a9639ecc784715023ef65d37305be696d
[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       lex_error (lexer, _("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                       lex_next_error (lexer, -1, -1,
237                                       _("Unknown format type `%s'."), type);
238                       return false;
239                     }
240                   if (!fmt_check (&f, use))
241                     return false;
242                 }
243             }
244         }
245
246       /* Add COUNT copies of the NEW_FORMAT_CNT formats in
247          NEW_FORMATS to FORMATS. */
248       if (n_new_formats != 0
249           && size_overflow_p (xtimes (xsum (formats_used,
250                                             xtimes (count, n_new_formats)),
251                                       sizeof *formats)))
252         xalloc_die ();
253       formats_needed = count * n_new_formats;
254       if (formats_used + formats_needed > formats_allocated)
255         {
256           formats_allocated = formats_used + formats_needed;
257           *formats = pool_2nrealloc (pool, *formats, &formats_allocated,
258                                      sizeof **formats);
259         }
260       for (; count > 0; count--)
261         {
262           memcpy (&(*formats)[formats_used], new_formats,
263                   sizeof **formats * n_new_formats);
264           formats_used += n_new_formats;
265         }
266
267       lex_match (lexer, T_COMMA);
268     }
269
270   *n_formats = formats_used;
271   return true;
272 }
273
274 /* Checks whether FORMAT represents one of the special "formats"
275    for T, X, or /.  If so, updates *RECORD or *COLUMN (or both)
276    as appropriate, and returns true.  Otherwise, returns false
277    without any side effects. */
278 bool
279 execute_placement_format (const struct fmt_spec *format,
280                           int *record, int *column)
281 {
282   switch ((int) format->type)
283     {
284     case PRS_TYPE_X:
285       *column += format->w;
286       return true;
287
288     case PRS_TYPE_T:
289       *column = format->w;
290       return true;
291
292     case PRS_TYPE_NEW_REC:
293       (*record)++;
294       *column = 1;
295       return true;
296
297     default:
298       assert (format->type < FMT_NUMBER_OF_FORMATS);
299       return false;
300     }
301 }
302
303 static bool
304 parse_column__ (struct lexer *lexer, bool negative, int base, int *column)
305 {
306   assert (base == 0 || base == 1);
307
308   if (!lex_force_int (lexer))
309     return false;
310   long int value = lex_integer (lexer);
311   if (negative)
312     value = -value;
313   lex_get (lexer);
314
315   *column = value - base + 1;
316   if (*column < 1)
317     {
318       if (base == 1)
319         lex_next_error (lexer, -1, -1,
320                         _("Column positions for fields must be positive."));
321       else
322         lex_next_error (lexer, -1, -1,
323                         _("Column positions for fields must not be negative."));
324       return false;
325     }
326   return true;
327 }
328
329 /* Parses a BASE-based column using LEXER.  Returns true and
330    stores a 1-based column number into *COLUMN if successful,
331    otherwise emits an error message and returns false.
332
333    If BASE is 0, zero-based column numbers are parsed; if BASE is
334    1, 1-based column numbers are parsed.  Regardless of BASE, the
335    values stored in *FIRST_COLUMN and *LAST_COLUMN are
336    1-based. */
337 bool
338 parse_column (struct lexer *lexer, int base, int *column)
339 {
340   return parse_column__ (lexer, false, base, column);
341 }
342
343 /* Parse a column or a range of columns, specified as a single
344    integer or two integers delimited by a dash.  Stores the range
345    in *FIRST_COLUMN and *LAST_COLUMN.  (If only a single integer
346    is given, it is stored in both.)  If RANGE_SPECIFIED is
347    non-null, then *RANGE_SPECIFIED is set to true if the syntax
348    contained a dash, false otherwise.  Returns true if
349    successful, false if the syntax was invalid or the values
350    specified did not make sense.
351
352    If BASE is 0, zero-based column numbers are parsed; if BASE is
353    1, 1-based column numbers are parsed.  Regardless of BASE, the
354    values stored in *FIRST_COLUMN and *LAST_COLUMN are
355    1-based. */
356 bool
357 parse_column_range (struct lexer *lexer, int base,
358                     int *first_column, int *last_column,
359                     bool *range_specified)
360 {
361   int start_ofs = lex_ofs (lexer);
362
363   /* First column. */
364   if (!parse_column__ (lexer, false, base, first_column))
365     return false;
366
367   /* Last column. */
368   if (lex_is_integer (lexer) && lex_integer (lexer) < 0)
369     {
370       if (!parse_column__ (lexer, true, base, last_column))
371         return false;
372
373       if (*last_column < *first_column)
374         {
375           lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
376                          _("The ending column for a field must be "
377                            "greater than the starting column."));
378           return false;
379         }
380
381       if (range_specified)
382         *range_specified = true;
383     }
384   else
385     {
386       *last_column = *first_column;
387       if (range_specified)
388         *range_specified = false;
389     }
390
391   return true;
392 }
393
394 /* Parses a (possibly empty) sequence of slashes, each of which
395    may be followed by an integer.  A slash on its own increases
396    *RECORD by 1 and sets *COLUMN to 1.  A slash followed by an
397    integer sets *RECORD to the integer, as long as that increases
398    *RECORD, and sets *COLUMN to 1.
399
400    Returns true if successful, false on syntax error. */
401 bool
402 parse_record_placement (struct lexer *lexer, int *record, int *column)
403 {
404   while (lex_match (lexer, T_SLASH))
405     {
406       if (lex_is_number (lexer))
407         {
408           if (!lex_force_int_range (lexer, NULL, *record + 1, INT_MAX))
409             return false;
410           *record = lex_integer (lexer);
411           lex_get (lexer);
412         }
413       else
414         (*record)++;
415       *column = 1;
416     }
417   assert (*record >= 1);
418
419   return true;
420 }