81e3fee8661a9c2f7b2dc69934e7a325c15177ef
[pspp-builds.git] / src / language / data-io / placement-parser.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    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, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17    02110-1301, USA. */
18
19 #include <config.h>
20
21 #include <language/data-io/placement-parser.h>
22
23 #include <assert.h>
24
25 #include <language/lexer/format-parser.h>
26 #include <language/lexer/lexer.h>
27 #include <libpspp/message.h>
28 #include <libpspp/pool.h>
29 #include <libpspp/str.h>
30
31 #include "xalloc.h"
32 #include "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 var_cnt, bool for_input,
47                                  struct fmt_spec **, size_t *);
48 static bool fixed_parse_fortran (struct lexer *l, struct pool *, bool for_input,
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    If successful, formats for VAR_CNT variables are stored in
57    *FORMATS, and the number of formats required is stored in
58    *FORMAT_CNT.  *FORMAT_CNT may be greater than VAR_CNT because
59    of T, X, and / "formats", but success guarantees that exactly
60    VAR_CNT variables will be placed by the output formats.  The
61    caller should call execute_placement_format to process those
62    "formats" in interpreting the output.
63
64    Uses POOL for allocation.  When the caller is finished
65    interpreting *FORMATS, POOL may be destroyed. */
66 bool
67 parse_var_placements (struct lexer *lexer, struct pool *pool, size_t var_cnt, bool for_input,
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, for_input, formats, format_cnt);
73   else if (lex_match (lexer, '(')) 
74     {
75       size_t assignment_cnt;
76       size_t i;
77
78       if (!fixed_parse_fortran (lexer, pool, for_input, formats, format_cnt))
79         return false; 
80
81       assignment_cnt = 0;
82       for (i = 0; i < *format_cnt; i++)
83         assignment_cnt += (*formats)[i].type < FMT_NUMBER_OF_FORMATS;
84
85       if (assignment_cnt != var_cnt)
86         {
87           msg (SE, _("Number of variables specified (%d) "
88                      "differs from number of variable formats (%d)."),
89                (int) var_cnt, (int) assignment_cnt);
90           return false;
91         }
92
93       return true;
94     }
95   else
96     {
97       msg (SE, _("SPSS-like or Fortran-like format "
98                  "specification expected after variable names."));
99       return false;
100     }
101 }
102
103 /* Implements parse_var_placements for column-based formats. */
104 static bool
105 fixed_parse_columns (struct lexer *lexer, struct pool *pool, size_t var_cnt, bool for_input,
106                      struct fmt_spec **formats, size_t *format_cnt)
107 {
108   struct fmt_spec format;
109   int fc, lc;
110   size_t i;
111
112   if ( !parse_column_range (lexer, &fc, &lc, NULL) )
113     return false;
114
115   /* Divide columns evenly. */    
116   format.w = (lc - fc + 1) / var_cnt;
117   if ((lc - fc + 1) % var_cnt)
118     {
119       msg (SE, _("The %d columns %d-%d "
120                  "can't be evenly divided into %u fields."),
121            lc - fc + 1, fc, lc, (unsigned int) var_cnt);
122       return false;
123     }
124
125   /* Format specifier. */
126   if (lex_match (lexer, '('))
127     {
128       /* Get format type. */
129       if (lex_token (lexer) == T_ID)
130         {
131           if (!parse_format_specifier_name (lexer, &format.type))
132             return false;
133           lex_match (lexer, ',');
134         }
135       else
136         format.type = FMT_F;
137
138       /* Get decimal places. */
139       if (lex_is_integer (lexer))
140         {
141           format.d = lex_integer (lexer);
142           lex_get (lexer);
143         }
144       else
145         format.d = 0;
146
147       if (!lex_force_match (lexer, ')'))
148         return false;
149     }
150   else
151     {
152       format.type = FMT_F;
153       format.d = 0;
154     }
155   if (!fmt_check (&format, for_input))
156     return false;
157
158   *formats = pool_nalloc (pool, var_cnt + 1, sizeof **formats);
159   *format_cnt = var_cnt + 1;
160   (*formats)[0].type = PRS_TYPE_T;
161   (*formats)[0].w = fc;
162   for (i = 1; i <= var_cnt; i++)
163     (*formats)[i] = format;
164   return true;
165 }
166
167 /* Implements parse_var_placements for Fortran-like formats. */
168 static bool
169 fixed_parse_fortran (struct lexer *lexer, struct pool *pool, bool for_input,
170                      struct fmt_spec **formats, size_t *format_cnt)
171 {
172   size_t formats_allocated = 0;
173   size_t formats_used = 0;
174
175   *formats = NULL;
176   while (!lex_match (lexer, ')'))
177     {
178       struct fmt_spec f;
179       struct fmt_spec *new_formats;
180       size_t new_format_cnt;
181       size_t count;
182       size_t formats_needed;
183       
184       /* Parse count. */
185       if (lex_is_integer (lexer))
186         {
187           count = lex_integer (lexer);
188           lex_get (lexer);
189         }
190       else
191         count = 1;
192
193       /* Parse format specifier. */
194       if (lex_match (lexer, '('))
195         {
196           /* Call ourselves recursively to handle parentheses. */
197           if (!fixed_parse_fortran (lexer, pool, for_input,
198                                     &new_formats, &new_format_cnt))
199             return false;
200         }
201       else
202         {
203           new_formats = &f;
204           new_format_cnt = 1;
205           if (lex_match (lexer, '/'))
206             f.type = PRS_TYPE_NEW_REC;
207           else
208             {
209               char type[FMT_TYPE_LEN_MAX + 1];
210               
211               if (!parse_abstract_format_specifier (lexer, type, &f.w, &f.d))
212                 return false;
213
214               if (!strcasecmp (type, "T")) 
215                 f.type = PRS_TYPE_T;
216               else if (!strcasecmp (type, "X")) 
217                 {
218                   f.type = PRS_TYPE_X;
219                   f.w = count;
220                   count = 1;
221                 }
222               else 
223                 {
224                   if (!fmt_from_name (type, &f.type)) 
225                     {
226                       msg (SE, _("Unknown format type \"%s\"."), type);
227                       return false;
228                     }
229                   if (!fmt_check (&f, for_input))
230                     return false;
231                 }
232             } 
233         }
234
235       /* Add COUNT copies of the NEW_FORMAT_CNT formats in
236          NEW_FORMATS to FORMATS. */
237       if (new_format_cnt != 0
238           && size_overflow_p (xtimes (xsum (formats_used,
239                                             xtimes (count, new_format_cnt)),
240                                       sizeof *formats)))
241         xalloc_die ();
242       formats_needed = count * new_format_cnt;
243       if (formats_used + formats_needed > formats_allocated) 
244         {
245           formats_allocated = formats_used + formats_needed;
246           *formats = pool_2nrealloc (pool, *formats, &formats_allocated,
247                                      sizeof **formats);
248         }
249       for (; count > 0; count--) 
250         {
251           memcpy (&(*formats)[formats_used], new_formats,
252                   sizeof **formats * new_format_cnt);
253           formats_used += new_format_cnt;
254         }
255
256       lex_match (lexer, ',');
257     }
258
259   *format_cnt = formats_used;
260   return true;
261 }
262
263 /* Checks whether FORMAT represents one of the special "formats"
264    for T, X, or /.  If so, updates *RECORD or *COLUMN (or both)
265    as appropriate, and returns true.  Otherwise, returns false
266    without any side effects. */
267 bool
268 execute_placement_format (const struct fmt_spec *format,
269                           int *record, int *column) 
270 {
271   switch (format->type) 
272     {
273     case PRS_TYPE_X:
274       *column += format->w;
275       return true;
276       
277     case PRS_TYPE_T:
278       *column = format->w;
279       return true;
280       
281     case PRS_TYPE_NEW_REC:
282       (*record)++;
283       *column = 1;
284       return true;
285
286     default:
287       assert (format->type < FMT_NUMBER_OF_FORMATS);
288       return false;
289     }
290 }
291
292 /* Parse a column or a range of columns, specified as a single
293    integer or two integer delimited by a dash.  Stores the range
294    in *FIRST_COLUMN and *LAST_COLUMN.  (If only a single integer
295    is given, it is stored in both.)  If RANGE_SPECIFIED is
296    non-null, then *RANGE_SPECIFIED is set to true if the syntax
297    contained a dash, false otherwise.  Returns true if
298    successful, false if the syntax was invalid or the values
299    specified did not make sense. */
300 bool
301 parse_column_range (struct lexer *lexer, int *first_column, int *last_column,
302                     bool *range_specified) 
303 {
304   /* First column. */
305   if (!lex_force_int (lexer))
306     return false;
307   *first_column = lex_integer (lexer);
308   if (*first_column < 1)
309     {
310       msg (SE, _("Column positions for fields must be positive."));
311       return false;
312     }
313   lex_get (lexer);
314
315   /* Last column. */
316   lex_negative_to_dash (lexer);
317   if (lex_match (lexer, '-'))
318     {
319       if (!lex_force_int (lexer))
320         return false;
321       *last_column = lex_integer (lexer);
322       if (*last_column < 1)
323         {
324           msg (SE, _("Column positions for fields must be positive."));
325           return false;
326         }
327       else if (*last_column < *first_column)
328         {
329           msg (SE, _("The ending column for a field must be "
330                      "greater than the starting column."));
331           return false;
332         }
333
334       if (range_specified)
335         *range_specified = true;
336       lex_get (lexer);
337     }
338   else 
339     {
340       *last_column = *first_column;
341       if (range_specified)
342         *range_specified = false;
343     }
344
345   return true;
346 }
347
348 /* Parses a (possibly empty) sequence of slashes, each of which
349    may be followed by an integer.  A slash on its own increases
350    *RECORD by 1 and sets *COLUMN to 1.  A slash followed by an
351    integer sets *RECORD to the integer, as long as that increases
352    *RECORD, and sets *COLUMN to 1.
353
354    Returns true if successful, false on syntax error. */
355 bool
356 parse_record_placement (struct lexer *lexer, int *record, int *column) 
357 {
358   while (lex_match (lexer, '/'))
359     {
360       if (lex_is_integer (lexer))
361         {
362           if (lex_integer (lexer) <= *record)
363             {
364               msg (SE, _("The record number specified, %ld, is at or "
365                          "before the previous record, %d.  Data "
366                          "fields must be listed in order of "
367                          "increasing record number."),
368                    lex_integer (lexer), *record);
369               return false;
370             }
371           *record = lex_integer (lexer);
372           lex_get (lexer);
373         }
374       else
375         (*record)++;
376       *column = 1;
377     }
378   assert (*record >= 1);
379   
380   return true;
381 }