Bug #18706.
@display
Two possible syntaxes:
VECTOR vec_name=var_list.
- VECTOR vec_name_list(count).
+ VECTOR vec_name_list(count [format]).
@end display
@cmd{VECTOR} allows a group of variables to be accessed as if they
were consecutive members of an array with a vector(index) notation.
-To make a vector out of a set of existing variables, specify a name for
-the vector followed by an equals sign (@samp{=}) and the variables that
-belong in the vector.
+To make a vector out of a set of existing variables, specify a name
+for the vector followed by an equals sign (@samp{=}) and the variables
+to put in the vector. All the variables in the vector must be the same
+type. String variables in a vector must all have the same width.
To make a vector and create variables at the same time, specify one or
more vector names followed by a count in parentheses. This will cause
variables named @code{@var{vec}1} through @code{@var{vec}@var{count}}
-to be created as numeric variables with print and write format F8.2.
-Variable names including numeric suffixes may not exceed 64 characters
-in length, and none of the variables may exist prior to @cmd{VECTOR}.
-
-All the variables in a vector must be the same type. String variables
-in a vector must all have the same width.
+to be created as numeric variables. By default, the new variables
+have print and write format F8.2, but an alternate format may be
+specified inside the parentheses before or after the count and
+separated from it by white space or a comma. Variable names including
+numeric suffixes may not exceed 64 characters in length, and none of
+the variables may exist prior to @cmd{VECTOR}.
Vectors created with @cmd{VECTOR} disappear after any procedure or
procedure-like command is executed. The variables contained in the
Variables within a vector may be referenced in expressions using
@code{vector(index)} syntax.
-
-
-
@node WRITE FORMATS, , VECTOR, Variable Attributes
@section WRITE FORMATS
@vindex WRITE FORMATS
+Sat Feb 3 21:52:17 2007 Ben Pfaff <blp@gnu.org>
+
+ * dictionary.c (dict_create_vector_assert): New function.
+
Wed Feb 7 21:25:15 2007 Ben Pfaff <blp@gnu.org>
* file-name.c (fn_normalize): Correct name of function
return false;
}
+/* Creates in D a vector named NAME that contains the CNT
+ variables in VAR. A vector named NAME must not already exist
+ in D. */
+void
+dict_create_vector_assert (struct dictionary *d,
+ const char *name,
+ struct variable **var, size_t cnt)
+{
+ assert (dict_lookup_vector (d, name) == NULL);
+ dict_create_vector (d, name, var, cnt);
+}
+
/* Returns the vector in D with index IDX, which must be less
than dict_get_vector_cnt (D). */
const struct vector *
bool dict_create_vector (struct dictionary *,
const char *name,
struct variable **, size_t cnt);
+void dict_create_vector_assert (struct dictionary *,
+ const char *name,
+ struct variable **, size_t cnt);
const struct vector *dict_get_vector (const struct dictionary *,
size_t idx);
size_t dict_get_vector_cnt (const struct dictionary *);
+Sat Feb 3 21:52:35 2007 Ben Pfaff <blp@gnu.org>
+
+ * vector.c (cmd_vector): Add support for specifying an output
+ format in the short form of the command, fixing bug #18706.
+ Rewrite to get rid of weird data structure and simplify.
+
+ * sys-file-info.c (display_vectors): For DISPLAY VECTORS, display,
+ in addition to the names of vectors, the names, positions, and
+ print formats of the variables contained in the vectors.
+
Wed Dec 13 20:59:54 2006 Ben Pfaff <blp@gnu.org>
* automake.mk: Add delete-variables.c
int i;
struct tab_table *t;
size_t nvec;
+ size_t nrow;
+ size_t row;
nvec = dict_get_vector_cnt (dict);
if (nvec == 0)
}
vl = xnmalloc (nvec, sizeof *vl);
- for (i = 0; i < nvec; i++)
- vl[i] = dict_get_vector (dict, i);
+ nrow = 0;
+ for (i = 0; i < nvec; i++)
+ {
+ vl[i] = dict_get_vector (dict, i);
+ nrow += vector_get_var_cnt (vl[i]);
+ }
if (sorted)
qsort (vl, nvec, sizeof *vl, compare_vector_ptrs_by_name);
- t = tab_create (1, nvec + 1, 0);
+ t = tab_create (4, nrow + 1, 0);
tab_headers (t, 0, 0, 1, 0);
tab_columns (t, TAB_COL_DOWN, 1);
tab_dim (t, tab_natural_dimensions);
- tab_hline (t, TAL_1, 0, 0, 1);
+ tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, nrow);
+ tab_box (t, -1, -1, -1, TAL_1, 0, 0, 3, nrow);
+ tab_hline (t, TAL_2, 0, 3, 1);
tab_text (t, 0, 0, TAT_TITLE | TAB_LEFT, _("Vector"));
+ tab_text (t, 1, 0, TAT_TITLE | TAB_LEFT, _("Position"));
+ tab_text (t, 2, 0, TAT_TITLE | TAB_LEFT, _("Variable"));
+ tab_text (t, 3, 0, TAT_TITLE | TAB_LEFT, _("Print Format"));
tab_flags (t, SOMF_NO_TITLE);
- for (i = 0; i < nvec; i++)
- tab_text (t, 0, i + 1, TAB_LEFT, vector_get_name (vl[i]));
- tab_submit (t);
-
- free (vl);
-}
-
-
-
-
-
-
-
+ row = 1;
+ for (i = 0; i < nvec; i++)
+ {
+ const struct vector *vec = vl[i];
+ size_t j;
+
+ tab_joint_text (t, 0, row, 0, row + vector_get_var_cnt (vec) - 1,
+ TAB_LEFT, vector_get_name (vl[i]));
+ for (j = 0; j < vector_get_var_cnt (vec); j++)
+ {
+ struct variable *var = vector_get_var (vec, j);
+ char fmt_string[FMT_STRING_LEN_MAX + 1];
+ fmt_to_string (var_get_print_format (var), fmt_string);
+
+ tab_text (t, 1, row, TAB_RIGHT | TAT_PRINTF, "%d", (int) j + 1);
+ tab_text (t, 2, row, TAB_LEFT, var_get_name (var));
+ tab_text (t, 3, row, TAB_LEFT, fmt_string);
+ row++;
+ }
+ tab_hline (t, TAL_1, 0, 3, row);
+ }
+ tab_submit (t);
+ free (vl);
+}
#include <stdlib.h>
+#include <data/format.h>
#include <data/procedure.h>
#include <data/dictionary.h>
#include <data/variable.h>
#include <language/command.h>
+#include <language/lexer/format-parser.h>
#include <language/lexer/lexer.h>
#include <language/lexer/variable-parser.h>
#include <libpspp/alloc.h>
#include <libpspp/assertion.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
+#include <libpspp/pool.h>
#include <libpspp/str.h>
+#include "intprops.h"
+
#include "gettext.h"
#define _(msgid) gettext (msgid)
int
cmd_vector (struct lexer *lexer, struct dataset *ds)
{
- /* Just to be different, points to a set of null terminated strings
- containing the names of the vectors to be created. The list
- itself is terminated by a empty string. So a list of three
- elements, A B C, would look like this: "A\0B\0C\0\0". */
- char *vecnames;
-
- /* vecnames iterators. */
- char *cp, *cp2;
-
- /* Maximum allocated position for vecnames, plus one position. */
- char *endp = NULL;
-
struct dictionary *dict = dataset_dict (ds);
+ struct pool *pool = pool_create ();
- cp = vecnames = xmalloc (256);
- endp = &vecnames[256];
do
{
+ char **vectors;
+ size_t vector_cnt, vector_cap;
+
/* Get the name(s) of the new vector(s). */
if (!lex_force_id (lexer))
return CMD_CASCADING_FAILURE;
+
+ vectors = NULL;
+ vector_cnt = vector_cap = 0;
while (lex_token (lexer) == T_ID)
{
- if (cp + 16 > endp)
+ size_t i;
+
+ if (dict_lookup_vector (dict, lex_tokid (lexer)))
{
- char *old_vecnames = vecnames;
- vecnames = xrealloc (vecnames, endp - vecnames + 256);
- cp = (cp - old_vecnames) + vecnames;
- endp = (endp - old_vecnames) + vecnames + 256;
+ msg (SE, _("A vector named %s already exists."),
+ lex_tokid (lexer));
+ goto fail;
}
- for (cp2 = cp; cp2 < cp; cp2 += strlen (cp))
- if (!strcasecmp (cp2, lex_tokid (lexer)))
+ for (i = 0; i < vector_cnt; i++)
+ if (!strcasecmp (vectors[i], lex_tokid (lexer)))
{
- msg (SE, _("Vector name %s is given twice."), lex_tokid (lexer));
+ msg (SE, _("Vector name %s is given twice."),
+ lex_tokid (lexer));
goto fail;
}
- if (dict_lookup_vector (dict, lex_tokid (lexer)))
- {
- msg (SE, _("There is already a vector with name %s."), lex_tokid (lexer));
- goto fail;
- }
+ if (vector_cnt == vector_cap)
+ vectors = pool_2nrealloc (pool,
+ vectors, &vector_cap, sizeof *vectors);
+ vectors[vector_cnt++] = xstrdup (lex_tokid (lexer));
- cp = stpcpy (cp, lex_tokid (lexer)) + 1;
lex_get (lexer);
lex_match (lexer, ',');
}
- *cp++ = 0;
/* Now that we have the names it's time to check for the short
or long forms. */
struct variable **v;
size_t nv;
- if (strchr (vecnames, '\0')[1])
+ if (vector_cnt > 1)
{
- /* There's more than one vector name. */
- msg (SE, _("A slash must be used to separate each vector "
- "specification when using the long form. Commands "
- "such as VECTOR A,B=Q1 TO Q20 are not supported."));
+ msg (SE, _("A slash must separate each vector "
+ "specification in VECTOR's long form."));
goto fail;
}
- if (!parse_variables (lexer, dict, &v, &nv,
- PV_SAME_WIDTH | PV_DUPLICATE))
+ if (!parse_variables_pool (lexer, pool, dict, &v, &nv,
+ PV_SAME_WIDTH | PV_DUPLICATE))
goto fail;
- dict_create_vector (dict, vecnames, v, nv);
- free (v);
+ dict_create_vector (dict, vectors[0], v, nv);
}
else if (lex_match (lexer, '('))
{
- int i;
-
- /* Maximum number of digits in a number to add to the base
- vecname. */
- int ndig;
-
- /* Name of an individual variable to be created. */
- char name[SHORT_NAME_LEN + 1];
-
- /* Vector variables. */
- struct variable **v;
- int nv;
-
- if (!lex_force_int (lexer))
- return CMD_CASCADING_FAILURE;
- nv = lex_integer (lexer);
- lex_get (lexer);
- if (nv <= 0)
- {
- msg (SE, _("Vectors must have at least one element."));
- goto fail;
- }
- if (!lex_force_match (lexer, ')'))
- goto fail;
-
- /* First check that all the generated variable names
- are LONG_NAME_LEN characters or shorter. */
- ndig = intlog10 (nv);
- for (cp = vecnames; *cp;)
+ /* Short form. */
+ struct fmt_spec format;
+ bool seen_format = false;
+
+ struct variable **vars;
+ int var_cnt;
+
+ size_t i;
+
+ var_cnt = 0;
+ format = fmt_for_output (FMT_F, 8, 2);
+ seen_format = false;
+ while (!lex_match (lexer, ')'))
+ {
+ if (lex_is_integer (lexer) && var_cnt == 0)
+ {
+ var_cnt = lex_integer (lexer);
+ lex_get (lexer);
+ if (var_cnt <= 0)
+ {
+ msg (SE, _("Vectors must have at least one element."));
+ goto fail;
+ }
+ }
+ else if (lex_token (lexer) == T_ID && !seen_format)
+ {
+ seen_format = true;
+ if (!parse_format_specifier (lexer, &format)
+ || !fmt_check_output (&format)
+ || !fmt_check_type_compat (&format, VAR_NUMERIC))
+ goto fail;
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto fail;
+ }
+ lex_match (lexer, ',');
+ }
+ if (var_cnt == 0)
+ {
+ lex_error (lexer, _("expecting vector length"));
+ goto fail;
+ }
+
+ /* Check that none of the variables exist and that
+ their names are no more than LONG_NAME_LEN bytes
+ long. */
+ for (i = 0; i < vector_cnt; i++)
{
- int len = strlen (cp);
- if (len + ndig > LONG_NAME_LEN)
+ int j;
+ for (j = 0; j < var_cnt; j++)
{
- msg (SE, _("%s%d is too long for a variable name."), cp, nv);
- goto fail;
- }
- cp += len + 1;
- }
-
- /* Next check that none of the variables exist. */
- for (cp = vecnames; *cp;)
- {
- for (i = 0; i < nv; i++)
- {
- sprintf (name, "%s%d", cp, i + 1);
- if (dict_lookup_var (dict, name))
+ char name[LONG_NAME_LEN + INT_STRLEN_BOUND (int) + 1];
+ sprintf (name, "%s%d", vectors[i], j + 1);
+ if (strlen (name) > LONG_NAME_LEN)
+ {
+ msg (SE, _("%s is too long for a variable name."), name);
+ goto fail;
+ }
+ if (dict_lookup_var (dict, name))
{
- msg (SE, _("There is already a variable named %s."),
- name);
+ msg (SE, _("%s is an existing variable name."), name);
goto fail;
}
}
- cp += strlen (cp) + 1;
}
/* Finally create the variables and vectors. */
- v = xmalloc (nv * sizeof *v);
- for (cp = vecnames; *cp;)
+ vars = pool_nmalloc (pool, var_cnt, sizeof *vars);
+ for (i = 0; i < vector_cnt; i++)
{
- for (i = 0; i < nv; i++)
+ int j;
+ for (j = 0; j < var_cnt; j++)
{
- sprintf (name, "%s%d", cp, i + 1);
- v[i] = dict_create_var_assert (dict, name, 0);
+ char name[LONG_NAME_LEN + 1];
+ sprintf (name, "%s%d", vectors[i], j + 1);
+ vars[j] = dict_create_var_assert (dict, name, 0);
+ var_set_both_formats (vars[j], &format);
}
- if (!dict_create_vector (dict, cp, v, nv))
- NOT_REACHED ();
- cp += strlen (cp) + 1;
+ dict_create_vector_assert (dict, vectors[i], vars, var_cnt);
}
- free (v);
}
else
{
- msg (SE, _("The syntax for this command does not match "
- "the expected syntax for either the long form "
- "or the short form of VECTOR."));
+ lex_error (lexer, NULL);
goto fail;
}
-
- free (vecnames);
- vecnames = NULL;
}
while (lex_match (lexer, '/'));
- if (lex_token (lexer) != '.')
- {
- lex_error (lexer, _("expecting end of command"));
- goto fail;
- }
- return CMD_SUCCESS;
+ pool_destroy (pool);
+ return lex_end_of_command (lexer);
fail:
- free (vecnames);
+ pool_destroy (pool);
return CMD_FAILURE;
}
+Sat Feb 3 21:56:46 2007 Ben Pfaff <blp@gnu.org>
+
+ * table.c (tab_hline): Allow t->nr as y argument, so that we can
+ draw a line below the bottom row of the table.
+
Wed Feb 7 21:38:12 2007 Ben Pfaff <blp@gnu.org>
* afm.c: Add #include <limits.h>. Thanks to John McCabe-Dansted
y += t->row_ofs;
assert (y >= 0);
- assert (y < t->nr);
+ assert (y <= t->nr);
assert (x2 >= x1 );
assert (x1 >= 0 );
assert (x2 < t->nc);
+Sat Feb 3 21:57:34 2007 Ben Pfaff <blp@gnu.org>
+
+ * automake.mk: Add tests/command/vector.sh.
+
+ * tests/command/vector.sh: New test.
+
Wed Jan 24 21:13:53 2007 Ben Pfaff <blp@gnu.org>
* automake.mk: Add tests/libpspp/abt-test.
tests/command/trimmed-mean.sh \
tests/command/tabs.sh \
tests/command/use.sh \
+ tests/command/vector.sh \
tests/command/very-long-strings.sh \
tests/command/weight.sh \
tests/formats/bcd-in.sh \
--- /dev/null
+#!/bin/sh
+
+# This program tests the VECTOR command
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_builddir are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+ cd /
+ rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+ echo $activity
+ echo FAILED
+ cleanup;
+ exit 1;
+}
+
+
+no_result()
+{
+ echo $activity
+ echo NO RESULT;
+ cleanup;
+ exit 2;
+}
+
+pass()
+{
+ cleanup;
+ exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="create prog"
+cat > $TEMPDIR/vector.stat <<EOF
+data list notable/x 1.
+vector v(4).
+display vector.
+
+data list notable/x 1.
+vector #vec(4, comma10.2).
+display vector.
+
+input program.
+vector x(5).
+data list/x5 x2 x3 x1 x4 1-5.
+end input program.
+display vector.
+
+data list notable/u w x y z 1-5.
+vector a=u to y.
+vector b=x to z.
+vector c=all.
+display vector.
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii -e $TEMPDIR/stdout $TEMPDIR/vector.stat
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="compare stdout"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/stdout
+diff -b $TEMPDIR/stdout - <<EOF
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+activity="compare results"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b $TEMPDIR/pspp.list - <<EOF
++------+--------+--------+------------+
+|Vector|Position|Variable|Print Format|
+#======#========#========#============#
+|v | 1|v1 |F8.2 |
+| | 2|v2 |F8.2 |
+| | 3|v3 |F8.2 |
+| | 4|v4 |F8.2 |
++------+--------+--------+------------+
++------+--------+--------+------------+
+|Vector|Position|Variable|Print Format|
+#======#========#========#============#
+|#vec | 1|#vec1 |COMMA10.2 |
+| | 2|#vec2 |COMMA10.2 |
+| | 3|#vec3 |COMMA10.2 |
+| | 4|#vec4 |COMMA10.2 |
++------+--------+--------+------------+
+1.1 DATA LIST. Reading 1 record from INLINE.
++--------+------+-------+------+
+|Variable|Record|Columns|Format|
+#========#======#=======#======#
+|x5 | 1| 1- 1|F1.0 |
+|x2 | 1| 2- 2|F1.0 |
+|x3 | 1| 3- 3|F1.0 |
+|x1 | 1| 4- 4|F1.0 |
+|x4 | 1| 5- 5|F1.0 |
++--------+------+-------+------+
++------+--------+--------+------------+
+|Vector|Position|Variable|Print Format|
+#======#========#========#============#
+|x | 1|x1 |F8.2 |
+| | 2|x2 |F8.2 |
+| | 3|x3 |F8.2 |
+| | 4|x4 |F8.2 |
+| | 5|x5 |F8.2 |
++------+--------+--------+------------+
++------+--------+--------+------------+
+|Vector|Position|Variable|Print Format|
+#======#========#========#============#
+|a | 1|u |F1.0 |
+| | 2|w |F1.0 |
+| | 3|x |F1.0 |
+| | 4|y |F1.0 |
++------+--------+--------+------------+
+|b | 1|x |F1.0 |
+| | 2|y |F1.0 |
+| | 3|z |F1.0 |
++------+--------+--------+------------+
+|c | 1|u |F1.0 |
+| | 2|w |F1.0 |
+| | 3|x |F1.0 |
+| | 4|y |F1.0 |
+| | 5|z |F1.0 |
++------+--------+--------+------------+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+pass;