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