1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2006, 2010, 2011 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "language/data-io/placement-parser.h"
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"
30 #include "gl/xalloc.h"
34 #define _(msgid) gettext (msgid)
36 /* Extensions to the format specifiers used only for
40 PRS_TYPE_T = SCHAR_MAX - 3, /* Tab to absolute column. */
41 PRS_TYPE_X, /* Skip columns. */
42 PRS_TYPE_NEW_REC /* Next record. */
45 static bool fixed_parse_columns (struct lexer *, struct pool *, size_t var_cnt, bool for_input,
46 struct fmt_spec **, size_t *);
47 static bool fixed_parse_fortran (struct lexer *l, struct pool *, bool for_input,
48 struct fmt_spec **, size_t *);
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
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.
63 Uses POOL for allocation. When the caller is finished
64 interpreting *FORMATS, POOL may be destroyed. */
66 parse_var_placements (struct lexer *lexer, struct pool *pool, size_t var_cnt, bool for_input,
67 struct fmt_spec **formats, size_t *format_cnt)
70 if (lex_is_number (lexer))
71 return fixed_parse_columns (lexer, pool, var_cnt, for_input, formats, format_cnt);
72 else if (lex_match (lexer, T_LPAREN))
74 size_t assignment_cnt;
77 if (!fixed_parse_fortran (lexer, pool, for_input, formats, format_cnt))
81 for (i = 0; i < *format_cnt; i++)
82 assignment_cnt += (*formats)[i].type < FMT_NUMBER_OF_FORMATS;
84 if (assignment_cnt != var_cnt)
86 msg (SE, _("Number of variables specified (%zu) "
87 "differs from number of variable formats (%zu)."),
88 var_cnt, assignment_cnt);
96 msg (SE, _("SPSS-like or Fortran-like format "
97 "specification expected after variable names."));
102 /* Implements parse_var_placements for column-based formats. */
104 fixed_parse_columns (struct lexer *lexer, struct pool *pool, size_t var_cnt, bool for_input,
105 struct fmt_spec **formats, size_t *format_cnt)
107 struct fmt_spec format;
111 if ( !parse_column_range (lexer, 1, &fc, &lc, NULL) )
114 /* Divide columns evenly. */
115 format.w = (lc - fc + 1) / var_cnt;
116 if ((lc - fc + 1) % var_cnt)
118 msg (SE, _("The %d columns %d-%d "
119 "can't be evenly divided into %zu fields."),
120 lc - fc + 1, fc, lc, var_cnt);
124 /* Format specifier. */
125 if (lex_match (lexer, T_LPAREN))
127 /* Get format type. */
128 if (lex_token (lexer) == T_ID)
130 if (!parse_format_specifier_name (lexer, &format.type))
132 lex_match (lexer, T_COMMA);
137 /* Get decimal places. */
138 if (lex_is_integer (lexer))
140 format.d = lex_integer (lexer);
146 if (!lex_force_match (lexer, T_RPAREN))
154 if (!fmt_check (&format, for_input))
157 *formats = pool_nalloc (pool, var_cnt + 1, sizeof **formats);
158 *format_cnt = var_cnt + 1;
159 (*formats)[0].type = PRS_TYPE_T;
160 (*formats)[0].w = fc;
161 for (i = 1; i <= var_cnt; i++)
162 (*formats)[i] = format;
166 /* Implements parse_var_placements for Fortran-like formats. */
168 fixed_parse_fortran (struct lexer *lexer, struct pool *pool, bool for_input,
169 struct fmt_spec **formats, size_t *format_cnt)
171 size_t formats_allocated = 0;
172 size_t formats_used = 0;
175 while (!lex_match (lexer, T_RPAREN))
178 struct fmt_spec *new_formats;
179 size_t new_format_cnt;
181 size_t formats_needed;
184 if (lex_is_integer (lexer))
186 count = lex_integer (lexer);
192 /* Parse format specifier. */
193 if (lex_match (lexer, T_LPAREN))
195 /* Call ourselves recursively to handle parentheses. */
196 if (!fixed_parse_fortran (lexer, pool, for_input,
197 &new_formats, &new_format_cnt))
204 if (lex_match (lexer, T_SLASH))
205 f.type = PRS_TYPE_NEW_REC;
208 char type[FMT_TYPE_LEN_MAX + 1];
210 if (!parse_abstract_format_specifier (lexer, type, &f.w, &f.d))
213 if (!strcasecmp (type, "T"))
215 else if (!strcasecmp (type, "X"))
223 if (!fmt_from_name (type, &f.type))
225 msg (SE, _("Unknown format type `%s'."), type);
228 if (!fmt_check (&f, for_input))
234 /* Add COUNT copies of the NEW_FORMAT_CNT formats in
235 NEW_FORMATS to FORMATS. */
236 if (new_format_cnt != 0
237 && size_overflow_p (xtimes (xsum (formats_used,
238 xtimes (count, new_format_cnt)),
241 formats_needed = count * new_format_cnt;
242 if (formats_used + formats_needed > formats_allocated)
244 formats_allocated = formats_used + formats_needed;
245 *formats = pool_2nrealloc (pool, *formats, &formats_allocated,
248 for (; count > 0; count--)
250 memcpy (&(*formats)[formats_used], new_formats,
251 sizeof **formats * new_format_cnt);
252 formats_used += new_format_cnt;
255 lex_match (lexer, T_COMMA);
258 *format_cnt = formats_used;
262 /* Checks whether FORMAT represents one of the special "formats"
263 for T, X, or /. If so, updates *RECORD or *COLUMN (or both)
264 as appropriate, and returns true. Otherwise, returns false
265 without any side effects. */
267 execute_placement_format (const struct fmt_spec *format,
268 int *record, int *column)
270 switch (format->type)
273 *column += format->w;
280 case PRS_TYPE_NEW_REC:
286 assert (format->type < FMT_NUMBER_OF_FORMATS);
291 /* Parses a BASE-based column using LEXER. Returns true and
292 stores a 1-based column number into *COLUMN if successful,
293 otherwise emits an error message and returns false. */
295 parse_column (int value, int base, int *column)
297 assert (base == 0 || base == 1);
298 *column = value - base + 1;
302 msg (SE, _("Column positions for fields must be positive."));
304 msg (SE, _("Column positions for fields must not be negative."));
310 /* Parse a column or a range of columns, specified as a single
311 integer or two integers delimited by a dash. Stores the range
312 in *FIRST_COLUMN and *LAST_COLUMN. (If only a single integer
313 is given, it is stored in both.) If RANGE_SPECIFIED is
314 non-null, then *RANGE_SPECIFIED is set to true if the syntax
315 contained a dash, false otherwise. Returns true if
316 successful, false if the syntax was invalid or the values
317 specified did not make sense.
319 If BASE is 0, zero-based column numbers are parsed; if BASE is
320 1, 1-based column numbers are parsed. Regardless of BASE, the
321 values stored in *FIRST_COLUMN and *LAST_COLUMN are
324 parse_column_range (struct lexer *lexer, int base,
325 int *first_column, int *last_column,
326 bool *range_specified)
329 if (!lex_force_int (lexer)
330 || !parse_column (lex_integer (lexer), base, first_column))
335 if (lex_is_integer (lexer) && lex_integer (lexer) < 0)
337 if (!parse_column (-lex_integer (lexer), base, last_column))
341 if (*last_column < *first_column)
343 msg (SE, _("The ending column for a field must be "
344 "greater than the starting column."));
349 *range_specified = true;
353 *last_column = *first_column;
355 *range_specified = false;
361 /* Parses a (possibly empty) sequence of slashes, each of which
362 may be followed by an integer. A slash on its own increases
363 *RECORD by 1 and sets *COLUMN to 1. A slash followed by an
364 integer sets *RECORD to the integer, as long as that increases
365 *RECORD, and sets *COLUMN to 1.
367 Returns true if successful, false on syntax error. */
369 parse_record_placement (struct lexer *lexer, int *record, int *column)
371 while (lex_match (lexer, T_SLASH))
373 if (lex_is_integer (lexer))
375 if (lex_integer (lexer) <= *record)
377 msg (SE, _("The record number specified, %ld, is at or "
378 "before the previous record, %d. Data "
379 "fields must be listed in order of "
380 "increasing record number."),
381 lex_integer (lexer), *record);
384 *record = lex_integer (lexer);
391 assert (*record >= 1);