more tests
[pspp] / src / language / expressions / parse.c
index 2b78b0b14b41491cc172fc301ac4476fdf0765ba..1e3844a6cf1a1d0541407a634ee1d44f2714cb05 100644 (file)
@@ -60,12 +60,22 @@ atom_type expr_node_returns (const struct expr_node *);
 static const char *atom_type_name (atom_type);
 static struct expression *finish_expression (struct expr_node *,
                                              struct expression *);
-static bool type_check (const struct expr_node *, enum val_type expected_type);
+static bool type_check (const struct expression *, const struct expr_node *,
+                        enum val_type expected_type);
 static struct expr_node *allocate_unary_variable (struct expression *,
                                                 const struct variable *);
 \f
 /* Public functions. */
 
+static struct expr_node *
+parse_expr (struct lexer *lexer, struct expression *e)
+{
+  struct expr_node *n = parse_or (lexer, e);
+  if (n && n->type == OP_VEC_ELEM_NUM_RAW)
+    n->type = OP_VEC_ELEM_NUM;
+  return n;
+}
+
 /* Parses an expression of the given TYPE.  If DS is nonnull then variables and
    vectors within it may be referenced within the expression; otherwise, the
    expression must not reference any variables or vectors.  Returns the new
@@ -76,8 +86,8 @@ expr_parse (struct lexer *lexer, struct dataset *ds, enum val_type type)
   assert (val_type_is_valid (type));
 
   struct expression *e = expr_create (ds);
-  struct expr_node *n = parse_or (lexer, e);
-  if (!n || !type_check (n, type))
+  struct expr_node *n = parse_expr (lexer, e);
+  if (!n || !type_check (e, n, type))
     {
       expr_free (e);
       return NULL;
@@ -91,7 +101,7 @@ struct expression *
 expr_parse_bool (struct lexer *lexer, struct dataset *ds)
 {
   struct expression *e = expr_create (ds);
-  struct expr_node *n = parse_or (lexer, e);
+  struct expr_node *n = parse_expr (lexer, e);
   if (!n)
     {
       expr_free (e);
@@ -100,10 +110,12 @@ expr_parse_bool (struct lexer *lexer, struct dataset *ds)
 
   atom_type actual_type = expr_node_returns (n);
   if (actual_type == OP_number)
-    n = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, n);
+    n = expr_allocate_binary (e, OP_EXPR_TO_BOOLEAN, n,
+                              expr_allocate_expr_node (e, n));
   else if (actual_type != OP_boolean)
     {
-      msg (SE, _("Type mismatch: expression has %s type, "
+      msg_at (SE, expr_location (e, n),
+              _("Type mismatch: expression has %s type, "
                  "but a boolean value is required here."),
            atom_type_name (actual_type));
       expr_free (e);
@@ -121,7 +133,7 @@ expr_parse_new_variable (struct lexer *lexer, struct dataset *ds,
                          const char *new_var_name)
 {
   struct expression *e = expr_create (ds);
-  struct expr_node *n = parse_or (lexer, e);
+  struct expr_node *n = parse_expr (lexer, e);
   if (!n)
     {
       expr_free (e);
@@ -158,7 +170,7 @@ expr_parse_any (struct lexer *lexer, struct dataset *ds, bool optimize)
   struct expression *e;
 
   e = expr_create (ds);
-  n = parse_or (lexer, e);
+  n = parse_expr (lexer, e);
   if (n == NULL)
     {
       expr_free (e);
@@ -195,6 +207,7 @@ atom_type_stack (atom_type type)
     {
     case OP_number:
     case OP_boolean:
+    case OP_num_vec_elem:
       return &on_number_stack;
 
     case OP_string:
@@ -208,6 +221,7 @@ atom_type_stack (atom_type type)
     case OP_integer:
     case OP_pos_int:
     case OP_vector:
+    case OP_expr_node:
       return &not_on_stack;
 
     default:
@@ -284,7 +298,8 @@ finish_expression (struct expr_node *n, struct expression *e)
    converted to type EXPECTED_TYPE, inserting a conversion at *N
    if necessary.  Returns true if successful, false on failure. */
 static bool
-type_check (const struct expr_node *n, enum val_type expected_type)
+type_check (const struct expression *e, const struct expr_node *n,
+            enum val_type expected_type)
 {
   atom_type actual_type = expr_node_returns (n);
 
@@ -293,8 +308,9 @@ type_check (const struct expr_node *n, enum val_type expected_type)
     case VAL_NUMERIC:
       if (actual_type != OP_number && actual_type != OP_boolean)
        {
-         msg (SE, _("Type mismatch: expression has %s type, "
-                     "but a numeric value is required here."),
+         msg_at (SE, expr_location (e, n),
+                  _("Type mismatch: expression has type '%s', "
+                     "but a numeric value is required."),
                atom_type_name (actual_type));
          return false;
        }
@@ -303,8 +319,9 @@ type_check (const struct expr_node *n, enum val_type expected_type)
     case VAL_STRING:
       if (actual_type != OP_string)
         {
-          msg (SE, _("Type mismatch: expression has %s type, "
-                     "but a string value is required here."),
+          msg_at (SE, expr_location (e, n),
+                  _("Type mismatch: expression has type '%s', "
+                     "but a string value is required."),
                atom_type_name (actual_type));
           return false;
         }
@@ -361,8 +378,8 @@ expr_location__ (struct expression *e,
 
 /* Returns the source code location corresponding to expression NODE, computing
    it lazily if needed. */
-static const struct msg_location *
-expr_location (struct expression *e, const struct expr_node *node_)
+const struct msg_location *
+expr_location (const struct expression *e_, const struct expr_node *node_)
 {
   struct expr_node *node = CONST_CAST (struct expr_node *, node_);
   if (!node)
@@ -370,6 +387,7 @@ expr_location (struct expression *e, const struct expr_node *node_)
 
   if (!node->location)
     {
+      struct expression *e = CONST_CAST (struct expression *, e_);
       const struct msg_location *min = NULL;
       const struct msg_location *max = NULL;
       expr_location__ (e, node, &min, &max);
@@ -432,6 +450,12 @@ type_coercion__ (struct expression *e, struct expr_node *node, size_t arg_idx,
             *argp = expr_allocate_unary (e, OP_BOOLEAN_TO_NUM, arg);
           return true;
         }
+      else if (actual_type == OP_num_vec_elem)
+        {
+          if (do_coercion)
+            arg->type = OP_VEC_ELEM_NUM;
+          return true;
+        }
       break;
 
     case OP_string:
@@ -443,12 +467,24 @@ type_coercion__ (struct expression *e, struct expr_node *node, size_t arg_idx,
         {
           /* Convert numeric to boolean. */
           if (do_coercion)
-            *argp = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, arg);
+            *argp = expr_allocate_binary (e, OP_OPERAND_TO_BOOLEAN, arg,
+                                          expr_allocate_expr_node (e, node));
+          return true;
+        }
+      break;
+
+    case OP_integer:
+      if (actual_type == OP_number)
+        {
+          /* Convert number to integer. */
+          if (do_coercion)
+            *argp = expr_allocate_unary (e, OP_NUM_TO_INTEGER, arg);
           return true;
         }
       break;
 
     case OP_format:
+      /* We never coerce to OP_format, only to OP_ni_format or OP_no_format. */
       NOT_REACHED ();
 
     case OP_ni_format:
@@ -711,8 +747,7 @@ parse_rel (struct lexer *lexer, struct expression *e)
       "not produce the mathematically expected result.  "
       "Use the AND logical operator to fix the problem "
       "(e.g. `a < b AND b < c').  "
-      "If chaining is really intended, parentheses will disable "
-      "this warning (e.g. `(a < b) < c'.)");
+      "To disable this warning, insert parentheses.");
 
   static const struct operator ops[] =
     {
@@ -772,9 +807,8 @@ parse_exp (struct lexer *lexer, struct expression *e)
   static const struct operator op = { .token = T_EXP, .num_op = OP_POW };
 
   const char *chain_warning =
-    _("The exponentiation operator (`**') is left-associative, "
-      "even though right-associative semantics are more useful.  "
-      "That is, `a**b**c' equals `(a**b)**c', not as `a**(b**c)'.  "
+    _("The exponentiation operator (`**') is left-associative: "
+      "`a**b**c' equals `(a**b)**c', not `a**(b**c)'.  "
       "To disable this warning, insert parentheses.");
 
   if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
@@ -787,6 +821,7 @@ parse_exp (struct lexer *lexer, struct expression *e)
   int start_ofs = lex_ofs (lexer);
   struct expr_node *lhs = expr_allocate_number (e, -lex_tokval (lexer));
   lex_get (lexer);
+  expr_add_location (lexer, e, start_ofs, lhs);
 
   struct expr_node *node = parse_binary_operators__ (
     lexer, e, &op, 1, parse_primary, chain_warning, lhs);
@@ -798,6 +833,46 @@ parse_exp (struct lexer *lexer, struct expression *e)
   return node;
 }
 
+static double
+ymd_to_offset (int y, int m, int d)
+{
+  char *error;
+  double retval = calendar_gregorian_to_offset (
+    y, m, d, settings_get_fmt_settings (), &error);
+  if (error)
+    {
+      msg (SE, "%s", error);
+      free (error);
+    }
+  return retval;
+}
+
+static struct expr_node *
+expr_date (struct expression *e, int year_digits)
+{
+  static const char *months[12] =
+    {
+      "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+      "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
+    };
+
+  time_t last_proc_time = time_of_last_procedure (e->ds);
+  struct tm *time = localtime (&last_proc_time);
+
+  char *tmp = (year_digits == 2
+               ? xasprintf ("%02d-%s-%02d", time->tm_mday, months[time->tm_mon],
+                            time->tm_year % 100)
+               : xasprintf ("%02d-%s-%04d", time->tm_mday, months[time->tm_mon],
+                            time->tm_year + 1900));
+
+  struct substring s;
+  ss_alloc_substring_pool (&s, ss_cstr (tmp), e->expr_pool);
+
+  free (tmp);
+
+  return expr_allocate_string (e, s);
+}
+
 /* Parses system variables. */
 static struct expr_node *
 parse_sysvar (struct lexer *lexer, struct expression *e)
@@ -805,25 +880,9 @@ parse_sysvar (struct lexer *lexer, struct expression *e)
   if (lex_match_id (lexer, "$CASENUM"))
     return expr_allocate_nullary (e, OP_CASENUM);
   else if (lex_match_id (lexer, "$DATE"))
-    {
-      static const char *months[12] =
-        {
-          "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
-          "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
-        };
-
-      time_t last_proc_time = time_of_last_procedure (e->ds);
-      struct tm *time;
-      char temp_buf[10];
-      struct substring s;
-
-      time = localtime (&last_proc_time);
-      sprintf (temp_buf, "%02d %s %02d", abs (time->tm_mday) % 100,
-               months[abs (time->tm_mon) % 12], abs (time->tm_year) % 100);
-
-      ss_alloc_substring (&s, ss_cstr (temp_buf));
-      return expr_allocate_string (e, s);
-    }
+    return expr_date (e, 2);
+  else if (lex_match_id (lexer, "$DATE11"))
+    return expr_date (e, 4);
   else if (lex_match_id (lexer, "$TRUE"))
     return expr_allocate_boolean (e, 1.0);
   else if (lex_match_id (lexer, "$FALSE"))
@@ -834,16 +893,15 @@ parse_sysvar (struct lexer *lexer, struct expression *e)
     {
       time_t time = time_of_last_procedure (e->ds);
       struct tm *tm = localtime (&time);
-      return expr_allocate_number (e, expr_ymd_to_ofs (tm->tm_year + 1900,
-                                                       tm->tm_mon + 1,
-                                                       tm->tm_mday));
+      return expr_allocate_number (e, ymd_to_offset (tm->tm_year + 1900,
+                                                     tm->tm_mon + 1,
+                                                     tm->tm_mday));
     }
   else if (lex_match_id (lexer, "$TIME"))
     {
       time_t time = time_of_last_procedure (e->ds);
       struct tm *tm = localtime (&time);
-      return expr_allocate_number (e,
-                                   expr_ymd_to_date (tm->tm_year + 1900,
+      return expr_allocate_number (e, ymd_to_offset (tm->tm_year + 1900,
                                                      tm->tm_mon + 1,
                                                      tm->tm_mday)
                                    + tm->tm_hour * 60 * 60.
@@ -936,23 +994,9 @@ parse_primary__ (struct lexer *lexer, struct expression *e)
 
     case T_LPAREN:
       {
-        /* Count number of left parentheses so that we can match them against
-           an equal number of right parentheses.  This defeats trivial attempts
-           to exhaust the stack with a lot of left parentheses.  (More
-           sophisticated attacks will still succeed.) */
-        size_t n = 0;
-        while (lex_match (lexer, T_LPAREN))
-          n++;
-
+        lex_get (lexer);
         struct expr_node *node = parse_or (lexer, e);
-       if (!node)
-          return NULL;
-
-        for (size_t i = 0; i < n; i++)
-          if (!lex_force_match (lexer, T_RPAREN))
-            return NULL;
-
-        return node;
+       return !node || !lex_force_match (lexer, T_RPAREN) ? NULL : node;
       }
 
     default:
@@ -999,12 +1043,12 @@ parse_vector_element (struct lexer *lexer, struct expression *e)
     return NULL;
 
   operation_type type = (vector_get_type (vector) == VAL_NUMERIC
-                         ? OP_VEC_ELEM_NUM : OP_VEC_ELEM_STR);
+                         ? OP_VEC_ELEM_NUM_RAW : OP_VEC_ELEM_STR);
   struct expr_node *node = expr_allocate_binary (
     e, type, element, expr_allocate_vector (e, vector));
   expr_add_location (lexer, e, vector_start_ofs, node);
 
-  if (!type_coercion (e, node, 1))
+  if (!type_coercion (e, node, 0))
     {
       msg_at (SE, expr_location (e, node),
               _("A vector index must be numeric."));
@@ -1144,7 +1188,8 @@ match_function (struct expr_node *node,
 }
 
 static bool
-validate_function_args (const struct operation *f, int n_args, int min_valid)
+validate_function_args (const struct expression *e, const struct expr_node *n,
+                        const struct operation *f, int n_args, int min_valid)
 {
   /* Count the function arguments that go into the trailing array (if any).  We
      know that there must be at least the minimum number because
@@ -1160,7 +1205,8 @@ validate_function_args (const struct operation *f, int n_args, int min_valid)
          here. */
       assert (f->array_granularity == 2);
       assert (n_args % 2 == 0);
-      msg (SE, _("%s must have an odd number of arguments."), f->prototype);
+      msg_at (SE, expr_location (e, n),
+              _("%s must have an odd number of arguments."), f->prototype);
       return false;
     }
 
@@ -1169,9 +1215,10 @@ validate_function_args (const struct operation *f, int n_args, int min_valid)
       if (f->array_min_elems == 0)
         {
           assert ((f->flags & OPF_MIN_VALID) == 0);
-          msg (SE, _("%s function cannot accept suffix .%d to specify the "
-                     "minimum number of valid arguments."),
-               f->prototype, min_valid);
+          msg_at (SE, expr_location (e, n),
+                  _("%s function cannot accept suffix .%d to specify the "
+                    "minimum number of valid arguments."),
+                  f->prototype, min_valid);
           return false;
         }
       else
@@ -1179,9 +1226,10 @@ validate_function_args (const struct operation *f, int n_args, int min_valid)
           assert (f->flags & OPF_MIN_VALID);
           if (min_valid > array_n_args)
             {
-              msg (SE, _("For %s with %d arguments, at most %d (not %d) may be "
-                         "required to be valid."),
-                   f->prototype, n_args, array_n_args, min_valid);
+              msg_at (SE, expr_location (e, n),
+                      _("For %s with %d arguments, at most %d (not %d) may be "
+                        "required to be valid."),
+                      f->prototype, n_args, array_n_args, min_valid);
               return false;
             }
         }
@@ -1220,16 +1268,15 @@ put_invocation (struct string *s,
 
 static void
 no_match (struct expression *e, const char *func_name, struct expr_node *node,
-          const struct operation *first, const struct operation *last)
+          const struct operation *ops, size_t n)
 {
   struct string s;
-  const struct operation *f;
 
   ds_init_empty (&s);
 
-  if (last - first == 1)
+  if (n == 1)
     {
-      ds_put_format (&s, _("Type mismatch invoking %s as "), first->prototype);
+      ds_put_format (&s, _("Type mismatch invoking %s as "), ops->prototype);
       put_invocation (&s, func_name, node);
     }
   else
@@ -1238,13 +1285,33 @@ no_match (struct expression *e, const char *func_name, struct expr_node *node,
       put_invocation (&s, func_name, node);
       ds_put_cstr (&s, _(" does not match any known function.  Candidates are:"));
 
-      for (f = first; f < last; f++)
-        ds_put_format (&s, "\n%s", f->prototype);
+      for (size_t i = 0; i < n; i++)
+        ds_put_format (&s, "\n%s", ops[i].prototype);
     }
   ds_put_byte (&s, '.');
 
   msg_at (SE, expr_location (e, node), "%s", ds_cstr (&s));
 
+  if (n == 1 && ops->n_args == node->n_args)
+    {
+      for (size_t i = 0; i < ops->n_args; i++)
+        if ((ops->args[i] == OP_ni_format
+             || ops->args[i] == OP_no_format)
+            && expr_node_returns (node->args[i]) == OP_format)
+          {
+            const struct fmt_spec *f = &node->args[i]->format;
+            char *error = fmt_check__ (f, (ops->args[i] == OP_ni_format
+                                           ? FMT_FOR_INPUT : FMT_FOR_OUTPUT));
+            if (!error)
+              error = fmt_check_type_compat__ (f, VAL_NUMERIC);
+            if (error)
+              {
+                msg_at (SN, expr_location (e, node->args[i]), "%s", error);
+                free (error);
+              }
+          }
+    }
+
   ds_destroy (&s);
 }
 
@@ -1318,7 +1385,7 @@ parse_function (struct lexer *lexer, struct expression *e)
   const struct operation *f = match_function (n, first, last);
   if (!f)
     {
-      no_match (e, ds_cstr (&func_name), n, first, last);
+      no_match (e, ds_cstr (&func_name), n, first, last - first);
       goto fail;
     }
   n->type = f - operations;
@@ -1331,21 +1398,23 @@ parse_function (struct lexer *lexer, struct expression *e)
            arguments were coercible. */
         NOT_REACHED ();
       }
-  if (!validate_function_args (f, n_args, min_valid))
+  if (!validate_function_args (e, n, f, n_args, min_valid))
     goto fail;
 
   if ((f->flags & OPF_EXTENSION) && settings_get_syntax () == COMPATIBLE)
-    msg (SW, _("%s is a PSPP extension."), f->prototype);
+    msg_at (SW, expr_location (e, n),
+            _("%s is a PSPP extension."), f->prototype);
   if (f->flags & OPF_UNIMPLEMENTED)
     {
-      msg (SE, _("%s is not available in this version of PSPP."),
-           f->prototype);
+      msg_at (SE, expr_location (e, n),
+              _("%s is not available in this version of PSPP."), f->prototype);
       goto fail;
     }
   if ((f->flags & OPF_PERM_ONLY) &&
       proc_in_temporary_transformations (e->ds))
     {
-      msg (SE, _("%s may not appear after %s."), f->prototype, "TEMPORARY");
+      msg_at (SE, expr_location (e, n),
+              _("%s may not appear after %s."), f->prototype, "TEMPORARY");
       goto fail;
     }
 
@@ -1518,6 +1587,15 @@ expr_allocate_format (struct expression *e, const struct fmt_spec *format)
   return n;
 }
 
+struct expr_node *
+expr_allocate_expr_node (struct expression *e,
+                         const struct expr_node *expr_node)
+{
+  struct expr_node *n = pool_alloc (e->expr_pool, sizeof *n);
+  *n = (struct expr_node) { .type = OP_expr_node, .expr_node = expr_node };
+  return n;
+}
+
 /* Allocates a unary composite node that represents the value of
    variable V in expression E. */
 static struct expr_node *