Merge remote-tracking branch 'origin/master' into sheet
[pspp] / src / language / lexer / variable-parser.c
index 84cf972563545becd34344a8b1b64a787c89ce0e..d160b6f9791780a40b51000837a4c0fe3fadc2b4 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <stdbool.h>
 #include <stdlib.h>
 
+#include "data/dataset.h"
 #include "data/dictionary.h"
-#include "data/procedure.h"
 #include "data/variable.h"
 #include "language/lexer/lexer.h"
 #include "libpspp/assertion.h"
 #include "libpspp/cast.h"
 #include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
 #include "libpspp/hmapx.h"
 #include "libpspp/message.h"
 #include "libpspp/misc.h"
@@ -37,6 +38,8 @@
 #include "libpspp/str.h"
 #include "libpspp/stringi-set.h"
 
+#include "math/interaction.h"
+
 #include "gl/c-ctype.h"
 #include "gl/xalloc.h"
 
@@ -198,9 +201,9 @@ add_variable (struct variable ***v, size_t *nv, size_t *mv,
                "All variables in this variable list must have the "
                "same width.  %s will be omitted from the list."),
          var_get_name ((*v)[0]), add_name, add_name);
-  else if ((pv_opts & PV_NO_DUPLICATE) && included[idx])
+  else if ((pv_opts & PV_NO_DUPLICATE) && included && included[idx])
     msg (SE, _("Variable %s appears twice in variable list."), add_name);
-  else if ((pv_opts & PV_DUPLICATE) || !included[idx])
+  else if ((pv_opts & PV_DUPLICATE) || !included || !included[idx])
     {
       if (*nv >= *mv)
         {
@@ -415,8 +418,8 @@ add_var_name (char *name,
 /* Parses a list of variable names according to the DATA LIST version
    of the TO convention.  */
 bool
-parse_DATA_LIST_vars (struct lexer *lexer, char ***namesp,
-                      size_t *n_varsp, int pv_opts)
+parse_DATA_LIST_vars (struct lexer *lexer, const struct dictionary *dict,
+                      char ***namesp, size_t *n_varsp, int pv_opts)
 {
   char **names;
   size_t n_vars;
@@ -453,7 +456,8 @@ parse_DATA_LIST_vars (struct lexer *lexer, char ***namesp,
 
   do
     {
-      if (lex_token (lexer) != T_ID)
+      if (lex_token (lexer) != T_ID
+          || !dict_id_is_valid (dict, lex_tokcstr (lexer), true))
        {
          lex_error (lexer, "expecting variable name");
          goto exit;
@@ -474,7 +478,8 @@ parse_DATA_LIST_vars (struct lexer *lexer, char ***namesp,
           unsigned long int number;
 
          lex_get (lexer);
-         if (lex_token (lexer) != T_ID)
+         if (lex_token (lexer) != T_ID
+              || !dict_id_is_valid (dict, lex_tokcstr (lexer), true))
            {
              lex_error (lexer, "expecting variable name");
              goto exit;
@@ -574,7 +579,8 @@ register_vars_pool (struct pool *pool, char **names, size_t nnames)
    parse_DATA_LIST_vars(), except that all allocations are taken
    from the given POOL. */
 bool
-parse_DATA_LIST_vars_pool (struct lexer *lexer, struct pool *pool,
+parse_DATA_LIST_vars_pool (struct lexer *lexer, const struct dictionary *dict,
+                           struct pool *pool,
                            char ***names, size_t *nnames, int pv_opts)
 {
   int retval;
@@ -585,7 +591,7 @@ parse_DATA_LIST_vars_pool (struct lexer *lexer, struct pool *pool,
      re-free it later. */
   assert (!(pv_opts & PV_APPEND));
 
-  retval = parse_DATA_LIST_vars (lexer, names, nnames, pv_opts);
+  retval = parse_DATA_LIST_vars (lexer, dict, names, nnames, pv_opts);
   if (retval)
     register_vars_pool (pool, *names, *nnames);
   return retval;
@@ -624,10 +630,13 @@ parse_mixed_vars (struct lexer *lexer, const struct dictionary *dict,
          free (v);
          *nnames += nv;
        }
-      else if (!parse_DATA_LIST_vars (lexer, names, nnames, PV_APPEND))
+      else if (!parse_DATA_LIST_vars (lexer, dict, names, nnames, PV_APPEND))
        goto fail;
     }
-  return 1;
+  if (*nnames == 0)
+    goto fail;
+
+  return true;
 
 fail:
   for (i = 0; i < *nnames; i++)
@@ -635,7 +644,7 @@ fail:
   free (*names);
   *names = NULL;
   *nnames = 0;
-  return 0;
+  return false;
 }
 
 /* Parses a list of variables where some of the variables may be
@@ -814,9 +823,9 @@ array_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
   struct hmapx_node *node;
   struct variable **varp;
 
-  HMAPX_FOR_EACH_WITH_HASH (varp, node, hash_case_string (name, 0),
+  HMAPX_FOR_EACH_WITH_HASH (varp, node, utf8_hash_case_string (name, 0),
                             &avs->vars_by_name)
-    if (!strcasecmp (name, var_get_name (*varp)))
+    if (!utf8_strcasecmp (name, var_get_name (*varp)))
       {
         *idx = varp - avs->var;
         return true;
@@ -864,9 +873,76 @@ var_set_create_from_array (struct variable *const *var, size_t var_cnt)
           return NULL;
         }
       hmapx_insert (&avs->vars_by_name, CONST_CAST (void *, &avs->var[i]),
-                    hash_case_string (name, 0));
+                    utf8_hash_case_string (name, 0));
     }
 
   return vs;
 }
 
+
+/* Match a variable.
+   If the match succeeds, the variable will be placed in VAR.
+   Returns true if successful */
+bool
+lex_match_variable (struct lexer *lexer, const struct dictionary *dict, const struct variable **var)
+{
+  if (lex_token (lexer) !=  T_ID)
+    return false;
+
+  *var = parse_variable_const  (lexer, dict);
+
+  if ( *var == NULL)
+    return false;
+  return true;
+}
+
+/* An interaction is a variable followed by {*, BY} followed by an interaction */
+static bool
+parse_internal_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact, struct interaction **it)
+{
+  const struct variable *v = NULL;
+  assert (iact);
+
+  switch  (lex_next_token (lexer, 1))
+    {
+    case T_ENDCMD:
+    case T_SLASH:
+    case T_COMMA:
+    case T_ID:
+    case T_BY:
+    case T_ASTERISK:
+      break;
+    default:
+      return false;
+      break;
+    }
+
+  if (! lex_match_variable (lexer, dict, &v))
+    {
+      if (it)
+       interaction_destroy (*it);
+      *iact = NULL;
+      return false;
+    }
+
+  assert (v);
+
+  if ( *iact == NULL)
+    *iact = interaction_create (v);
+  else
+    interaction_add_variable (*iact, v);
+
+  if ( lex_match (lexer, T_ASTERISK) || lex_match (lexer, T_BY))
+    {
+      return parse_internal_interaction (lexer, dict, iact, iact);
+    }
+
+  return true;
+}
+
+bool
+parse_design_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact)
+{
+  return parse_internal_interaction (lexer, dict, iact, NULL);
+}
+