Rewrite expression code.
[pspp-builds.git] / src / inpt-pgm.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "error.h"
22 #include <float.h>
23 #include <stdlib.h>
24 #include "alloc.h"
25 #include "case.h"
26 #include "command.h"
27 #include "data-list.h"
28 #include "dfm-read.h"
29 #include "dictionary.h"
30 #include "error.h"
31 #include "expressions/public.h"
32 #include "file-handle.h"
33 #include "lexer.h"
34 #include "misc.h"
35 #include "str.h"
36 #include "var.h"
37 #include "vfm.h"
38
39 #include "debug-print.h"
40
41 /* Indicates how a `union value' should be initialized. */
42 enum value_init_type
43   {
44     INP_NUMERIC = 01,           /* Numeric. */
45     INP_STRING = 0,             /* String. */
46     
47     INP_INIT_ONCE = 02,         /* Initialize only once. */
48     INP_REINIT = 0,             /* Reinitialize for each iteration. */
49   };
50
51 struct input_program_pgm 
52   {
53     enum value_init_type *init; /* How to initialize each `union value'. */
54     size_t init_cnt;            /* Number of elements in inp_init. */
55     size_t case_size;           /* Size of case in bytes. */
56   };
57
58 static trns_proc_func end_case_trns_proc, reread_trns_proc, end_file_trns_proc;
59 static trns_free_func reread_trns_free;
60
61 int
62 cmd_input_program (void)
63 {
64   discard_variables ();
65
66   /* FIXME: we shouldn't do this here, but I'm afraid that other
67      code will check the class of vfm_source. */
68   vfm_source = create_case_source (&input_program_source_class,
69                                    default_dict, NULL);
70
71   return lex_end_of_command ();
72 }
73
74 int
75 cmd_end_input_program (void)
76 {
77   struct input_program_pgm *inp;
78   size_t i;
79
80   if (!case_source_is_class (vfm_source, &input_program_source_class))
81     {
82       msg (SE, _("No matching INPUT PROGRAM command."));
83       return CMD_FAILURE;
84     }
85   
86   if (dict_get_next_value_idx (default_dict) == 0)
87     msg (SW, _("No data-input or transformation commands specified "
88          "between INPUT PROGRAM and END INPUT PROGRAM."));
89
90   /* Mark the boundary between INPUT PROGRAM transformations and
91      ordinary transformations. */
92   f_trns = n_trns;
93
94   /* Figure out how to initialize each input case. */
95   inp = xmalloc (sizeof *inp);
96   inp->init_cnt = dict_get_next_value_idx (default_dict);
97   inp->init = xmalloc (inp->init_cnt * sizeof *inp->init);
98   for (i = 0; i < inp->init_cnt; i++)
99     inp->init[i] = -1;
100   for (i = 0; i < dict_get_var_cnt (default_dict); i++)
101     {
102       struct variable *var = dict_get_var (default_dict, i);
103       enum value_init_type value_init;
104       size_t j;
105       
106       value_init = var->type == NUMERIC ? INP_NUMERIC : INP_STRING;
107       value_init |= var->reinit ? INP_REINIT : INP_INIT_ONCE;
108
109       for (j = 0; j < var->nv; j++)
110         inp->init[j + var->fv] = value_init;
111     }
112   for (i = 0; i < inp->init_cnt; i++)
113     assert (inp->init[i] != -1);
114   inp->case_size = dict_get_case_size (default_dict);
115
116   /* Put inp into vfm_source for later use. */
117   vfm_source->aux = inp;
118
119   /* FIXME: we should use create_case_source() here. */
120   vfm_source->value_cnt = dict_get_next_value_idx (default_dict);
121
122   return lex_end_of_command ();
123 }
124
125 /* Initializes case C.  Called before the first case is read. */
126 static void
127 init_case (const struct input_program_pgm *inp, struct ccase *c)
128 {
129   size_t i;
130
131   for (i = 0; i < inp->init_cnt; i++)
132     switch (inp->init[i]) 
133       {
134       case INP_NUMERIC | INP_INIT_ONCE:
135         case_data_rw (c, i)->f = 0.0;
136         break;
137       case INP_NUMERIC | INP_REINIT:
138         case_data_rw (c, i)->f = SYSMIS;
139         break;
140       case INP_STRING | INP_INIT_ONCE:
141       case INP_STRING | INP_REINIT:
142         memset (case_data_rw (c, i)->s, ' ', sizeof case_data_rw (c, i)->s);
143         break;
144       default:
145         assert (0);
146       }
147 }
148
149 /* Clears case C.  Called between reading successive records. */
150 static void
151 clear_case (const struct input_program_pgm *inp, struct ccase *c)
152 {
153   size_t i;
154
155   for (i = 0; i < inp->init_cnt; i++)
156     switch (inp->init[i]) 
157       {
158       case INP_NUMERIC | INP_INIT_ONCE:
159         break;
160       case INP_NUMERIC | INP_REINIT:
161         case_data_rw (c, i)->f = SYSMIS;
162         break;
163       case INP_STRING | INP_INIT_ONCE:
164         break;
165       case INP_STRING | INP_REINIT:
166         memset (case_data_rw (c, i)->s, ' ', sizeof case_data_rw (c, i)->s);
167         break;
168       default:
169         assert (0);
170       }
171 }
172
173 /* Executes each transformation in turn on a `blank' case.  When a
174    transformation fails, returning -2, then that's the end of the
175    file.  -1 means go on to the next transformation.  Otherwise the
176    return value is the index of the transformation to go to next. */
177 static void
178 input_program_source_read (struct case_source *source,
179                            struct ccase *c,
180                            write_case_func *write_case,
181                            write_case_data wc_data)
182 {
183   struct input_program_pgm *inp = source->aux;
184   int i;
185
186   /* Nonzero if there were any END CASE commands in the set of
187      transformations.  If so, we don't automatically write out
188      cases. */
189   int end_case = 0;
190
191   /* FIXME?  This is the number of cases sent out of the input
192      program, not the number of cases written to the procedure.
193      The difference should only show up in $CASENUM in COMPUTE.
194      We should check behavior against SPSS. */
195   int cases_written = 0;
196
197   assert (inp != NULL);
198
199   /* Figure end_case. */
200   for (i = 0; i < f_trns; i++)
201     if (t_trns[i]->proc == end_case_trns_proc)
202       end_case = 1;
203
204   /* FIXME: This is an ugly kluge. */
205   for (i = 0; i < f_trns; i++)
206     if (t_trns[i]->proc == repeating_data_trns_proc)
207       repeating_data_set_write_case (t_trns[i], write_case, wc_data);
208
209   init_case (inp, c);
210   for (;;)
211     {
212       /* Perform transformations on `blank' case. */
213       for (i = 0; i < f_trns; )
214         {
215           int code;     /* Return value of last-called transformation. */
216
217           if (t_trns[i]->proc == end_case_trns_proc) 
218             {
219               cases_written++;
220               if (!write_case (wc_data))
221                 goto done;
222               clear_case (inp, c);
223               i++;
224               continue;
225             }
226
227           code = t_trns[i]->proc (t_trns[i], c, cases_written + 1);
228           switch (code)
229             {
230             case -1:
231               i++;
232               break;
233             case -2:
234               goto done;
235             case -3:
236               goto next_case;
237             default:
238               i = code;
239               break;
240             }
241         }
242
243       /* Write the case if appropriate. */
244       if (!end_case) 
245         {
246           cases_written++;
247           if (!write_case (wc_data))
248             break;
249         }
250
251       /* Blank out the case for the next iteration. */
252     next_case:
253       clear_case (inp, c);
254     }
255  done: ;
256 }
257
258 /* Destroys an INPUT PROGRAM source. */
259 static void
260 input_program_source_destroy (struct case_source *source)
261 {
262   struct input_program_pgm *inp = source->aux;
263
264   cancel_transformations ();
265
266   if (inp != NULL) 
267     {
268       free (inp->init);
269       free (inp);
270     }
271 }
272
273 const struct case_source_class input_program_source_class =
274   {
275     "INPUT PROGRAM",
276     NULL,
277     input_program_source_read,
278     input_program_source_destroy,
279   };
280 \f
281 int
282 cmd_end_case (void)
283 {
284   struct trns_header *t;
285
286   if (!case_source_is_class (vfm_source, &input_program_source_class))
287     {
288       msg (SE, _("This command may only be executed between INPUT PROGRAM "
289                  "and END INPUT PROGRAM."));
290       return CMD_FAILURE;
291     }
292
293   t = xmalloc (sizeof *t);
294   t->proc = end_case_trns_proc;
295   t->free = NULL;
296   add_transformation ((struct trns_header *) t);
297
298   return lex_end_of_command ();
299 }
300
301 /* Should never be called, because this is handled in
302    input_program_source_read(). */
303 int
304 end_case_trns_proc (struct trns_header *t UNUSED, struct ccase * c UNUSED,
305                     int case_num UNUSED)
306 {
307   assert (0);
308   abort ();
309 }
310
311 /* REREAD transformation. */
312 struct reread_trns
313   {
314     struct trns_header h;
315
316     struct dfm_reader *reader;  /* File to move file pointer back on. */
317     struct expression *column;  /* Column to reset file pointer to. */
318   };
319
320 /* Parses REREAD command. */
321 int
322 cmd_reread (void)
323 {
324   struct file_handle *fh;       /* File to be re-read. */
325   struct expression *e;         /* Expression for column to set. */
326   struct reread_trns *t;        /* Created transformation. */
327
328   fh = default_handle;
329   e = NULL;
330   while (token != '.')
331     {
332       if (lex_match_id ("COLUMN"))
333         {
334           lex_match ('=');
335           
336           if (e)
337             {
338               msg (SE, _("COLUMN subcommand multiply specified."));
339               expr_free (e);
340               return CMD_FAILURE;
341             }
342           
343           e = expr_parse (default_dict, EXPR_NUMBER);
344           if (!e)
345             return CMD_FAILURE;
346         }
347       else if (lex_match_id ("FILE"))
348         {
349           lex_match ('=');
350           fh = fh_parse ();
351           if (fh == NULL)
352             {
353               expr_free (e);
354               return CMD_FAILURE;
355             }
356           lex_get ();
357         }
358       else
359         {
360           lex_error (NULL);
361           expr_free (e);
362         }
363     }
364
365   t = xmalloc (sizeof *t);
366   t->h.proc = reread_trns_proc;
367   t->h.free = reread_trns_free;
368   t->reader = dfm_open_reader (fh);
369   t->column = e;
370   add_transformation ((struct trns_header *) t);
371
372   return CMD_SUCCESS;
373 }
374
375 /* Executes a REREAD transformation. */
376 static int
377 reread_trns_proc (struct trns_header * pt, struct ccase * c,
378                   int case_num)
379 {
380   struct reread_trns *t = (struct reread_trns *) pt;
381
382   if (t->column == NULL)
383     dfm_reread_record (t->reader, 1);
384   else
385     {
386       double column = expr_evaluate_num (t->column, c, case_num);
387       if (!finite (column) || column < 1)
388         {
389           msg (SE, _("REREAD: Column numbers must be positive finite "
390                "numbers.  Column set to 1."));
391           dfm_reread_record (t->reader, 1);
392         }
393       else
394         dfm_reread_record (t->reader, column);
395     }
396   return -1;
397 }
398
399 /* Frees a REREAD transformation. */
400 static void
401 reread_trns_free (struct trns_header *t_)
402 {
403   struct reread_trns *t = (struct reread_trns *) t_;
404   expr_free (t->column);
405   dfm_close_reader (t->reader);
406 }
407
408 /* Parses END FILE command. */
409 int
410 cmd_end_file (void)
411 {
412   struct trns_header *t;
413
414   if (!case_source_is_class (vfm_source, &input_program_source_class))
415     {
416       msg (SE, _("This command may only be executed between INPUT PROGRAM "
417                  "and END INPUT PROGRAM."));
418       return CMD_FAILURE;
419     }
420
421   t = xmalloc (sizeof *t);
422   t->proc = end_file_trns_proc;
423   t->free = NULL;
424   add_transformation ((struct trns_header *) t);
425
426   return lex_end_of_command ();
427 }
428
429 /* Executes an END FILE transformation. */
430 static int
431 end_file_trns_proc (struct trns_header * t UNUSED, struct ccase * c UNUSED,
432                     int case_num UNUSED)
433 {
434   return -2;
435 }