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