1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009, 2011, 2013, 2015 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 "data/transformations.h"
24 #include "libpspp/str.h"
25 #include "language/control/control-stack.h" /* XXX layering violation */
27 #include "gl/xalloc.h"
29 /* A single transformation. */
32 /* Offset to add to EXECUTE's return value, if it returns a
33 transformation index. Normally 0 but set to the starting
34 index of a spliced chain after splicing. */
36 trns_proc_func *execute; /* Executes the transformation. */
37 trns_free_func *free; /* Garbage collector proc. */
38 void *aux; /* Auxiliary data. */
41 /* A chain of transformations. */
44 struct transformation *trns; /* Array of transformations. */
45 size_t trns_cnt; /* Number of transformations. */
46 size_t trns_cap; /* Allocated capacity. */
47 bool finalized; /* Finalize functions called? */
50 /* Allocates and returns a new transformation chain. */
52 trns_chain_create (void)
54 struct trns_chain *chain = xmalloc (sizeof *chain);
58 chain->finalized = false;
62 /* Finalizes all the un-finalized transformations in CHAIN.
63 Any given transformation is only finalized once. */
65 trns_chain_finalize (struct trns_chain *chain)
67 while (!chain->finalized)
69 ctl_stack_clear (); /* XXX layering violation */
70 chain->finalized = true;
74 /* Destroys CHAIN, finalizing it in the process if it has not
75 already been finalized. */
77 trns_chain_destroy (struct trns_chain *chain)
85 /* Needed to ensure that the control stack gets cleared. */
86 trns_chain_finalize (chain);
88 for (i = 0; i < chain->trns_cnt; i++)
90 struct transformation *trns = &chain->trns[i];
91 if (trns->free != NULL)
92 ok = trns->free (trns->aux) && ok;
101 /* Returns true if CHAIN contains any transformations,
104 trns_chain_is_empty (const struct trns_chain *chain)
106 return chain->trns_cnt == 0;
109 /* Adds a transformation to CHAIN with execute function EXECUTE, free function
110 FREE, and auxiliary data AUX. */
112 trns_chain_append (struct trns_chain *chain, trns_proc_func *execute,
113 trns_free_func *free, void *aux)
115 struct transformation *trns;
117 chain->finalized = false;
119 if (chain->trns_cnt == chain->trns_cap)
120 chain->trns = x2nrealloc (chain->trns, &chain->trns_cap,
121 sizeof *chain->trns);
123 trns = &chain->trns[chain->trns_cnt++];
125 trns->execute = execute;
130 /* Appends the transformations in SRC to those in DST,
132 Both DST and SRC must already be finalized. */
134 trns_chain_splice (struct trns_chain *dst, struct trns_chain *src)
138 assert (dst->finalized);
139 assert (src->finalized);
141 if (dst->trns_cnt + src->trns_cnt > dst->trns_cap)
143 dst->trns_cap = dst->trns_cnt + src->trns_cnt;
144 dst->trns = xnrealloc (dst->trns, dst->trns_cap, sizeof *dst->trns);
147 for (i = 0; i < src->trns_cnt; i++)
149 struct transformation *d = &dst->trns[i + dst->trns_cnt];
150 const struct transformation *s = &src->trns[i];
152 d->idx_ofs += src->trns_cnt;
154 dst->trns_cnt += src->trns_cnt;
157 trns_chain_destroy (src);
160 /* Returns the index that a transformation execution function may
161 return to "jump" to the next transformation to be added. */
163 trns_chain_next (struct trns_chain *chain)
165 return chain->trns_cnt;
168 /* Executes the given CHAIN of transformations on *C,
169 passing CASE_NR as the case number.
170 *C may be replaced by a new case.
171 Returns the result code that caused the transformations to
172 terminate, or TRNS_CONTINUE if the transformations finished
173 due to "falling off the end" of the set of transformations. */
175 trns_chain_execute (const struct trns_chain *chain, enum trns_result start,
176 struct ccase **c, casenumber case_nr)
180 assert (chain->finalized);
181 for (i = start < 0 ? 0 : start; i < chain->trns_cnt; )
183 struct transformation *trns = &chain->trns[i];
184 int retval = trns->execute (trns->aux, c, case_nr);
185 if (retval == TRNS_CONTINUE)
187 else if (retval >= 0)
188 i = retval + trns->idx_ofs;
190 return retval == TRNS_END_CASE ? i + 1 : retval;
193 return TRNS_CONTINUE;