Full negative tests for macros. dev7
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 4 Jul 2021 04:41:36 +0000 (21:41 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 4 Jul 2021 04:41:36 +0000 (21:41 -0700)
src/language/lexer/macro.c
tests/language/control/define.at

index 8678a0bcdb5f808bcf2f1f872356624706a5b221..e4302bf2fa91e121a09638a3dbf1ea3f13bdd2f1 100644 (file)
@@ -1080,10 +1080,10 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
 {
   struct string_array args;
 
-  if (parse_macro_function (ctx, &args, ss_cstr ("!length"), 1, 1,
+  if (parse_macro_function (ctx, &args, ss_cstr ("!LENGTH"), 1, 1,
                             input_consumed))
     ds_put_format (output, "%zu", strlen (args.strings[0]));
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!blanks"), 1, 1,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!BLANKS"), 1, 1,
                                  input_consumed))
     {
       int n;
@@ -1098,14 +1098,14 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
 
       ds_put_byte_multiple (output, ' ', n);
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!concat"), 1, INT_MAX,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!CONCAT"), 1, INT_MAX,
                                  input_consumed))
     {
       for (size_t i = 0; i < args.n; i++)
         if (!unquote_string (args.strings[i], ctx->segmenter_mode, output))
           ds_put_cstr (output, args.strings[i]);
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!head"), 1, 1,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!HEAD"), 1, 1,
                                  input_consumed))
     {
       struct string tmp;
@@ -1120,14 +1120,14 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
       macro_tokens_uninit (&mts);
       ds_destroy (&tmp);
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!index"), 2, 2,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!INDEX"), 2, 2,
                                  input_consumed))
     {
       const char *haystack = args.strings[0];
       const char *needle = strstr (haystack, args.strings[1]);
       ds_put_format (output, "%zu", needle ? needle - haystack + 1 : 0);
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!quote"), 1, 1,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!QUOTE"), 1, 1,
                                  input_consumed))
     {
       if (unquote_string (args.strings[0], ctx->segmenter_mode, NULL))
@@ -1145,7 +1145,7 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
           ds_put_byte (output, '\'');
         }
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!substr"), 2, 3,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!SUBSTR"), 2, 3,
                                  input_consumed))
     {
       int start;
@@ -1165,7 +1165,7 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
           macro_error (ctx->stack, NULL,
                        _("Third argument of !SUBSTR must be "
                          "non-negative integer (not \"%s\")."),
-                       args.strings[1]);
+                       args.strings[2]);
           string_array_destroy (&args);
           return false;
         }
@@ -1173,7 +1173,7 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
       struct substring s = ss_cstr (args.strings[0]);
       ds_put_substring (output, ss_substr (s, start - 1, count));
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!tail"), 1, 1,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!TAIL"), 1, 1,
                                  input_consumed))
     {
       struct string tmp;
@@ -1191,13 +1191,13 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
       macro_tokens_uninit (&mts);
       ds_destroy (&tmp);
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!unquote"), 1, 1,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!UNQUOTE"), 1, 1,
                                  input_consumed))
     {
       if (!unquote_string (args.strings[0], ctx->segmenter_mode, output))
         ds_put_cstr (output, args.strings[0]);
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!upcase"), 1, 1,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!UPCASE"), 1, 1,
                                  input_consumed))
     {
       struct string tmp;
@@ -1208,7 +1208,7 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
       free (upper);
       ds_destroy (&tmp);
     }
-  else if (parse_macro_function (ctx, &args, ss_cstr ("!eval"), 1, 1,
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!EVAL"), 1, 1,
                                  input_consumed))
     {
       struct macro_tokens mts = { .n = 0 };
@@ -1227,7 +1227,7 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
     }
   else if (ctx->n_input > 0
            && ctx->input[0].token.type == T_MACRO_ID
-           && ss_equals_case (ctx->input[0].token.string, ss_cstr ("!null")))
+           && ss_equals_case (ctx->input[0].token.string, ss_cstr ("!NULL")))
     {
       *input_consumed = 1;
       return true;
@@ -1279,6 +1279,12 @@ macro_evaluate_literal (const struct expr_context *ctx,
       *tokens = p;
       return value;
     }
+  else if (p->token.type == T_RPAREN)
+    {
+      macro_error (ctx->stack, p, _("Expecting literal or function invocation "
+                                    "in macro expression."));
+      return NULL;
+    }
 
   struct parse_macro_function_ctx fctx = {
     .input = p,
@@ -1522,7 +1528,6 @@ macro_evaluate_number (const struct macro_token **tokens, size_t n_tokens,
   macro_tokens_from_string__ (&mts, ss_cstr (s), segmenter_mode, stack);
   if (mts.n != 1 || !token_is_number (&mts.mts[0].token))
     {
-      macro_tokens_print (&mts, stdout);
       macro_error (stack, mts.n > 0 ? &mts.mts[0] : NULL,
                    _("Macro expression must evaluate to "
                      "a number (not \"%s\")."), s);
index 989f4093ec2a483b3234a8e32e891ef88577b19d..de89f1737f33ae9501c450ef0b864c46f8fb6bc9 100644 (file)
@@ -1231,4 +1231,272 @@ AT_CHECK([pspp -O format=csv define.sps --testing-mode], [0], [dnl
 
 (x = 1) (y = 2)
 ])
-AT_CLEANUP
\ No newline at end of file
+AT_CLEANUP
+
+AT_SETUP([bad token in macro body])
+AT_DATA([define.sps], [dnl
+DEFINE !x()
+x'123'
+!ENDDEFINE.
+])
+AT_CHECK([pspp define.sps], [1], [dnl
+define.sps:3: error: DEFINE: String of hex digits has 3 characters, which is
+not a multiple of 2.
+])
+AT_CLEANUP
+
+AT_SETUP([generic macro function syntax errors])
+AT_DATA([define.sps], [dnl
+DEFINE !a() !SUBSTR !ENDDEFINE.
+DEFINE !b() !SUBSTR x !ENDDEFINE.
+DEFINE !c() !SUBSTR(1,2,3,4) !ENDDEFINE.
+DEFINE !d() !SUBSTR(1x) !ENDDEFINE.
+DEFINE !e() !SUBSTR(1 !ENDDEFINE.
+dnl )
+DEBUG EXPAND.
+!a.
+!b.
+!c.
+!d.
+!e.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+define.sps:1: In the expansion of `!a',
+define.sps:7: error: DEBUG EXPAND: `(' expected following !SUBSTR.
+
+!SUBSTR
+
+define.sps:2: At `x' in the expansion of `!b',
+define.sps:8: error: DEBUG EXPAND: `(' expected following !SUBSTR.
+
+!SUBSTR x
+
+define.sps:3: At `)' in the expansion of `!c',
+define.sps:9: error: DEBUG EXPAND: Wrong number of arguments to macro
+function !SUBSTR.
+
+!SUBSTR(1, 2, 3, 4)
+
+define.sps:4: At `x' in the expansion of `!d',
+define.sps:10: error: DEBUG EXPAND: `,' or `)' expected in call to macro
+function !SUBSTR.
+
+!SUBSTR(1 x)
+
+define.sps:5: In the expansion of `!e',
+define.sps:11: error: DEBUG EXPAND: Missing `)' in call to macro function !
+SUBSTR.
+
+!SUBSTR(1
+])
+AT_CLEANUP
+
+AT_SETUP([specific macro function syntax errors])
+AT_DATA([define.sps], [dnl
+DEFINE !a() !BLANKS(x). !ENDDEFINE.
+DEFINE !b() !SUBSTR(x, y). !ENDDEFINE.
+DEFINE !c() !SUBSTR(x, 1, z). !ENDDEFINE.
+DEBUG EXPAND.
+!a.
+!b.
+!c.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+define.sps:1: In the expansion of `!a',
+define.sps:5: error: DEBUG EXPAND: Argument to !BLANKS must be non-negative
+integer (not "x").
+
+!BLANKS(x).
+
+define.sps:2: In the expansion of `!b',
+define.sps:6: error: DEBUG EXPAND: Second argument of !SUBSTR must be positive
+integer (not "y").
+
+!SUBSTR(x, y).
+
+define.sps:3: In the expansion of `!c',
+define.sps:7: error: DEBUG EXPAND: Third argument of !SUBSTR must be non-
+negative integer (not "z").
+
+!SUBSTR(x, 1, z).
+])
+AT_CLEANUP
+
+AT_SETUP([macro expression errors])
+AT_DATA([define.sps], [dnl
+DEFINE !a() !LET !x = (1. !ENDDEFINE dnl )
+
+DEFINE !b() !DO !x = x. !ENDDEFINE.
+DEFINE !c() !LET !x = (). !ENDDEFINE.
+DEBUG EXPAND.
+!a.
+!b.
+!c.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+define.sps:1-2: At `.' in the expansion of `!a',
+define.sps:5: error: DEBUG EXPAND: Expecting ')' in macro expression.
+
+!LET !x = (1.
+
+At `x' in the expansion of `!DO',
+define.sps:2: inside the expansion of `!b',
+define.sps:6: error: DEBUG EXPAND: Macro expression must evaluate to a number
+(not "x").
+
+!DO !x = x.
+
+define.sps:3: At `)' in the expansion of `!c',
+define.sps:7: error: DEBUG EXPAND: Expecting literal or function invocation in
+macro expression.
+
+!LET !x = ( ).
+])
+AT_CLEANUP
+
+AT_SETUP([macro !IF errors])
+AT_KEYWORDS([IF])
+AT_DATA([define.sps], [dnl
+DEFINE !a() !IF 1 !ENDDEFINE.
+DEFINE !b() !IF 1 !THEN !ENDDEFINE.
+DEFINE !c() !IF 1 !THEN !ELSE !ENDDEFINE.
+DEBUG EXPAND.
+!a.
+!b.
+!c.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+define.sps:1: In the expansion of `!a',
+define.sps:5: error: DEBUG EXPAND: !THEN expected in macro !IF construct.
+
+!IF 1
+
+define.sps:2: In the expansion of `!b',
+define.sps:6: error: DEBUG EXPAND: !ELSE or !IFEND expected in macro !IF
+construct.
+
+!IF 1 !THEN
+
+define.sps:3: In the expansion of `!c',
+define.sps:7: error: DEBUG EXPAND: !IFEND expected in macro !IF construct.
+
+!IF 1 !THEN !ELSE
+])
+AT_CLEANUP
+
+AT_SETUP([macro !LET errors])
+AT_KEYWORDS([LET])
+AT_DATA([define.sps], [dnl
+DEFINE !a() !LET !ENDDEFINE.
+DEFINE !b() !LET 0 !ENDDEFINE.
+DEFINE !c() !LET !x !ENDDEFINE.
+DEFINE !d() !LET !x y !ENDDEFINE.
+DEBUG EXPAND.
+!a.
+!b.
+!c.
+!d.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+define.sps:1: In the expansion of `!a',
+define.sps:6: error: DEBUG EXPAND: Expected macro variable name following !LET.
+
+!LET
+
+define.sps:2: At `0' in the expansion of `!b',
+define.sps:7: error: DEBUG EXPAND: Expected macro variable name following !LET.
+
+!LET 0
+
+define.sps:3: In the expansion of `!c',
+define.sps:8: error: DEBUG EXPAND: Expected `=' following !LET.
+
+!LET !x
+
+define.sps:4: At `y' in the expansion of `!d',
+define.sps:9: error: DEBUG EXPAND: Expected `=' following !LET.
+
+!LET !x y
+])
+AT_CLEANUP
+
+AT_SETUP([macro !DO errors])
+AT_KEYWORDS([DO])
+AT_DATA([define.sps], [dnl
+DEFINE !a() !DO !ENDDEFINE.
+DEFINE !b() !DO 0 !ENDDEFINE.
+DEFINE !c() !DO !x !ENDDEFINE.
+DEFINE !d() !DO !x !in (x) !ENDDEFINE.
+DEFINE !e() !DO !x = x. !ENDDEFINE.
+DEFINE !f() !DO !x = 5 x !ENDDEFINE.
+DEFINE !g() !DO !x = 5 !TO 6 !BY 0 !ENDDEFINE.
+DEFINE !h() !DO !x !ENDDEFINE.
+DEFINE !i() !DO !x 0 !ENDDEFINE.
+DEFINE !j() !BREAK !ENDDEFINE.
+DEBUG EXPAND.
+!a.
+!b.
+!c.
+!d.
+!e.
+!f.
+!g.
+!h.
+!i.
+!j.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+define.sps:1: In the expansion of `!a',
+define.sps:12: error: DEBUG EXPAND: Expected macro variable name following !DO.
+
+!DO
+
+define.sps:2: At `0' in the expansion of `!b',
+define.sps:13: error: DEBUG EXPAND: Expected macro variable name following !DO.
+
+!DO 0
+
+define.sps:3: In the expansion of `!c',
+define.sps:14: error: DEBUG EXPAND: Expected `=' or !IN in !DO loop.
+
+!DO !x
+
+define.sps:4: In the expansion of `!d',
+define.sps:15: error: DEBUG EXPAND: Missing !DOEND.
+
+!DO !x !in(x)
+
+At `x' in the expansion of `!DO',
+define.sps:5: inside the expansion of `!e',
+define.sps:16: error: DEBUG EXPAND: Macro expression must evaluate to a number
+(not "x").
+
+!DO !x = x.
+
+define.sps:6: At `x' in the expansion of `!f',
+define.sps:17: error: DEBUG EXPAND: Expected !TO in numerical !DO loop.
+
+!DO !x = 5 x
+
+define.sps:7: In the expansion of `!g',
+define.sps:18: error: DEBUG EXPAND: !BY value cannot be zero.
+
+!DO !x = 5 !TO 6 !BY 0
+
+define.sps:8: In the expansion of `!h',
+define.sps:19: error: DEBUG EXPAND: Expected `=' or !IN in !DO loop.
+
+!DO !x
+
+define.sps:9: At `0' in the expansion of `!i',
+define.sps:20: error: DEBUG EXPAND: Expected `=' or !IN in !DO loop.
+
+!DO !x 0
+
+define.sps:10: At `!BREAK' in the expansion of `!j',
+define.sps:21: error: DEBUG EXPAND: !BREAK outside !DO.
+
+!BREAK
+])
+AT_CLEANUP
+