logical operator bug fixes
[pspp] / src / language / lexer / macro.c
index fdf76bacea12b2a07d5d3e6ead9811e7cdb84922..718f268e658505dd3c74f7e76cbafd3c28ad011e 100644 (file)
@@ -32,6 +32,8 @@
 #include "libpspp/str.h"
 #include "libpspp/string-array.h"
 
+#include "gl/c-ctype.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
@@ -1099,19 +1101,52 @@ macro_evaluate_literal (const struct expr_context *ctx,
     .expand = ctx->expand,
   };
   struct string function_output = DS_EMPTY_INITIALIZER;
-  size_t function_consumed;
-  if (expand_macro_function (&fctx, &function_output, &function_consumed))
+  size_t function_consumed = parse_function_arg (&fctx, 0, &function_output);
+  *tokens = p + function_consumed;
+  return ds_steal_cstr (&function_output);
+}
+
+/* Returns true if MT is valid as a macro operator.  Only operators written as
+   symbols (e.g. <>) are usable in macro expressions, not operator written as
+   letters (e.g. EQ). */
+static bool
+is_macro_operator (const struct macro_token *mt)
+{
+  return (mt->representation.length > 0
+          && !c_isalpha (mt->representation.string[0]));
+}
+
+static enum token_type
+parse_relational_op (const struct macro_token *mt)
+{
+  switch (mt->token.type)
     {
-      *tokens = p + function_consumed;
-      return ds_steal_cstr (&function_output);
-    }
+    case T_EQUALS:
+      return T_EQ;
 
-  *tokens = p + 1;
-  return ss_xstrdup (p->representation);
+    case T_NE:
+    case T_LT:
+    case T_GT:
+    case T_LE:
+    case T_GE:
+      return is_macro_operator (mt) ? mt->token.type : T_STOP;
+
+    case T_MACRO_ID:
+      return (ss_equals_case (mt->token.string, ss_cstr ("!EQ")) ? T_EQ
+              : ss_equals_case (mt->token.string, ss_cstr ("!NE")) ? T_NE
+              : ss_equals_case (mt->token.string, ss_cstr ("!LT")) ? T_LT
+              : ss_equals_case (mt->token.string, ss_cstr ("!GT")) ? T_GT
+              : ss_equals_case (mt->token.string, ss_cstr ("!LE")) ? T_LE
+              : ss_equals_case (mt->token.string, ss_cstr ("!GE")) ? T_GE
+              : T_STOP);
+
+    default:
+      return T_STOP;
+    }
 }
 
 static char *
-macro_evaluate_logical (const struct expr_context *ctx,
+macro_evaluate_relational (const struct expr_context *ctx,
                         const struct macro_token **tokens,
                         const struct macro_token *end)
 {
@@ -1120,9 +1155,8 @@ macro_evaluate_logical (const struct expr_context *ctx,
   if (!lhs)
     return NULL;
 
-  enum token_type op = p < end ? p->token.type : T_STOP;
-  if (op != T_EQUALS && op != T_EQ && op != T_NE && op != T_LT
-      && op != T_GT && op != T_LE && op != T_GE)
+  enum token_type op = p >= end ? T_STOP : parse_relational_op (p);
+  if (op == T_STOP)
     {
       *tokens = p;
       return lhs;
@@ -1144,7 +1178,7 @@ macro_evaluate_logical (const struct expr_context *ctx,
 
   free (lhs);
   free (rhs);
-  
+
   bool b = (op == T_EQUALS || op == T_EQ ? !cmp
             : op == T_NE ? cmp
             : op == T_LT ? cmp < 0
@@ -1164,13 +1198,15 @@ macro_evaluate_not (const struct expr_context *ctx,
   const struct macro_token *p = *tokens;
 
   unsigned int negations = 0;
-  while (p < end && p->token.type == T_NOT)
+  while (p < end
+         && (ss_equals_case (p->representation, ss_cstr ("!NOT"))
+             || ss_equals (p->representation, ss_cstr ("~"))))
     {
       p++;
       negations++;
     }
 
-  char *operand = macro_evaluate_logical (ctx, &p, end);
+  char *operand = macro_evaluate_relational (ctx, &p, end);
   if (!operand || !negations)
     {
       *tokens = p;
@@ -1193,7 +1229,9 @@ macro_evaluate_and (const struct expr_context *ctx,
   if (!lhs)
     return NULL;
 
-  while (p < end && p->token.type == T_AND)
+  while (p < end
+         && (ss_equals_case (p->representation, ss_cstr ("!AND"))
+             || ss_equals (p->representation, ss_cstr ("&"))))
     {
       p++;
       char *rhs = macro_evaluate_not (ctx, &p, end);
@@ -1222,7 +1260,9 @@ macro_evaluate_or (const struct expr_context *ctx,
   if (!lhs)
     return NULL;
 
-  while (p < end && p->token.type == T_OR)
+  while (p < end
+         && (ss_equals_case (p->representation, ss_cstr ("!OR"))
+             || ss_equals (p->representation, ss_cstr ("|"))))
     {
       p++;
       char *rhs = macro_evaluate_and (ctx, &p, end);