!BLANKS and !CONCAT pass basic tests.
authorBen Pfaff <blp@cs.stanford.edu>
Fri, 4 Jun 2021 06:23:33 +0000 (23:23 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 4 Jun 2021 06:23:33 +0000 (23:23 -0700)
doc/flow-control.texi
doc/utilities.texi
src/language/lexer/macro.c
tests/language/control/define.at

index 77b4a3c42e350de55dbff86d2bc8876f06d6b83c..7a95758a78b92cb4936dedc6d9b43e70395df59d 100644 (file)
@@ -338,6 +338,8 @@ are equivalent; for a quoted string of spaces, use
 In the examples below, @samp{_} stands in for a space to make the
 results visible.
 
+@c Keep these examples in sync with the test for !BLANKS in
+@c tests/language/control/define.at:
 @example
 !BLANKS(0)                  @expansion{} @r{empty}
 !BLANKS(1)                  @expansion{} _
@@ -351,6 +353,8 @@ Expands to the concatenation of all of the arguments.  Before
 concatenation, each quoted string argument is unquoted, as if
 @code{!UNQUOTE} were applied.
 
+@c Keep these examples in sync with the test for !CONCAT in
+@c tests/language/control/define.at:
 @example
 !CONCAT(x, y)                @expansion{} xy
 !CONCAT('x', 'y')            @expansion{} xy
@@ -512,9 +516,26 @@ to uppercase.
 @node Macro Settings
 @subsection Macro Settings
 
-MPRINT
-MEXPAND
-MNEST
+Some macro behavior is controlled through the SET command
+(@pxref{SET}).  This section describes these settings.
+
+Any SET command that changes these settings within a macro body only
+takes effect following the macro.  This is because PSPP expands a
+macro's entire body at once, so that the SET command inside the body
+only executes afterwards.
+
+The MEXPAND setting (@pxref{SET MEXPAND}) controls whether macros will
+be expanded at all.  By default, macro expansion is on.  To avoid
+expansion of macros called within a macro body, use @code{!OFFEXPAND}
+and @code{!ONEXPAND} (@pxref{Controlling Macro Expansion}).
+
+When MPRINT (@pxref{SET MPRINT}) is turned on, PSPP logs an expansion
+of each macro in the input.  This feature can be useful for debugging
+macro definitions.
+
+MNEST (@pxref{SET MNEST}) limits the depth of expansion of macro
+calls, that is, the nesting level of macro expansion.
+
 MITERATE
 
 PRESERVE...RESTORE
index 62ca1a8f245ab03632ce086f818cfe198b80d49e..bd5b10c9e2bebe0699ccfc408e875fb94d3e55d5 100644 (file)
@@ -942,18 +942,21 @@ The following subcommands affect the interpretation of macros.
 Controls whether macros are expanded.  The default is ON.
 
 @item MPRINT
+@anchor{SET MPRINT}
 Controls whether the expansion of macros is included in output.  This
 is separate from whether command syntax in general is included in
 output.  The default is OFF.
 
 @item MITERATE
+@anchor{SET MITERATE}
 Limits the number of iterations executed in @code{!DO} loops within
 macros.  This does not affect other language constructs such as
 @cmd{LOOP}.  This must be set to a positive integer.  The default is
 1000.
 
 @item MNEST
-Limits the number of levels of nested macro expansion.  This must be
+@anchor{SET MNEST}
+Limits the number of levels of nested macro expansions.  This must be
 set to a positive integer.  The default is 50.
 @end table
 
index 584ee7f2fd3ed29e3d4880761c7176814e55c62c..628b83af01bd3d5e80a67c88baf8c5963463c5ee 100644 (file)
@@ -875,33 +875,19 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
   else if (parse_macro_function (ctx, &args, ss_cstr ("!concat"), 1, INT_MAX,
                                  input_consumed))
     {
-      struct string s;
-      bool all_strings = true;
+      struct string s = DS_EMPTY_INITIALIZER;
       for (size_t i = 0; i < args.n; i++)
         {
           if (args.mts[i].token.type == T_STRING)
             ds_put_substring (&s, args.mts[i].token.string);
           else
-            {
-              all_strings = false;
-              ds_put_substring (&s, args.mts[i].representation);
-            }
+            ds_put_substring (&s, args.mts[i].representation);
         }
 
-      if (all_strings)
-        {
-          *output = (struct macro_token) {
-            .token = { .type = T_STRING, .string = s.ss },
-          };
-          output->representation = ss_cstr (token_to_string (&output->token));
-        }
-      else
-        {
-          *output = (struct macro_token) {
-            .token = { .type = T_MACRO_ID /*XXX*/, .string = s.ss },
-          };
-          ss_alloc_substring (&output->representation, s.ss);
-        }
+      *output = (struct macro_token) {
+        .token = { .type = T_MACRO_ID /*XXX*/, .string = s.ss },
+      };
+      ss_alloc_substring (&output->representation, s.ss);
     }
   else if (parse_macro_function (ctx, &args, ss_cstr ("!quote"), 1, 1,
                                  input_consumed))
@@ -927,6 +913,17 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
       else
         macro_token_copy (output, &args.mts[0]);
     }
+  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")))
+    {
+      *input_consumed = 1;
+      *output = (struct macro_token) {
+        .token = { .type = T_MACRO_ID /* XXX*/ },
+      };
+      ss_alloc_substring (&output->token.string, ss_cstr (""));
+      return true;
+    }
   else
     return false;
 
index 8f1c8dea8db61b993a663ab5852db9273a0a2fd1..7a005fc8059c43c4db40de80da7ca6697e84fd49 100644 (file)
@@ -18,10 +18,12 @@ AT_BANNER([DEFINE])
 
 m4_define([PSPP_CHECK_MACRO_EXPANSION],
   [AT_SETUP([macro expansion - $1])
+   AT_KEYWORDS([m4_bpatsubst([$1], [!], [])])
    AT_DATA([define.sps], [$2
 DEBUG EXPAND.
 $3
 ])
+   AT_CAPTURE_FILE([define.sps])
    AT_DATA([expout], [$4
 ])
    AT_CHECK([pspp --testing-mode define.sps | sed '/^$/d'], [$6], [expout])
@@ -351,4 +353,40 @@ PSPP_CHECK_MACRO_EXPANSION([default keyword arguments],
   [!k arg1=x.
 !k],
   [k(x)
-k(a b c)])
\ No newline at end of file
+k(a b c)])
+
+dnl Keep this test in sync with the examples for !BLANKS in the manual.
+PSPP_CHECK_MACRO_EXPANSION([!BLANKS],
+  [DEFINE !b()
+!BLANKS(0).
+!QUOTE(!BLANKS(0)).
+!BLANKS(1).
+!QUOTE(!BLANKS(1)).
+!BLANKS(2).
+!QUOTE(!BLANKS(2)).
+!BLANKS(5).
+!QUOTE(!BLANKS(5)).
+!ENDDEFINE],
+  [!b.],
+  [.
+''.
+ .
+' '.
+  .
+'  '.
+     .
+'     '.])
+
+dnl Keep this test in sync with the examples for !CONCAT in the manual.
+PSPP_CHECK_MACRO_EXPANSION([!CONCAT],
+  [DEFINE !c()
+!CONCAT(x, y).
+!CONCAT('x', 'y').
+!CONCAT(12, 34).
+!CONCAT(!NULL, 123).
+!ENDDEFINE],
+  [!c.],
+  [xy.
+xy.
+1234.
+123.])