4

%str should pass a string as one parameter to a sas macro, even if it contains commas, but that apparently does not work if the argument of %str is in it self the result of a macro? I get ERROR: More positional parameters found than defined.

Example

This is what the error means

    15         %macro outer_macro(left, right);
    16          %put NOTE: outer_macro: left is &left;
    17          %put NOTE: outer_macro: right is &right;
    18         %mend;
    19         %outer_macro(left, right);
    NOTE: outer_macro: left is left
    NOTE: outer_macro: right is right
    20         %outer_macro(left, midle, right);
    ERROR: More positional parameters found than defined.

This is how %str resolves it in normal situations.

    21         %outer_macro(left, %str(midle, right));
    NOTE: outer_macro: left is left
    NOTE: outer_macro: right is midle, right

It is possible to construct an argument of a macro with an inner macro

    23         %macro blank_macro(left, right);
    24          %put NOTE: blank_macro: left is &left;
    25          %put NOTE: blank_macro: right is &right;
    26          &left. &right.
    27         %mend;
    28         %outer_macro(links, %blank_macro(midden, rechts));
    NOTE: blank_macro: left is midden
    NOTE: blank_macro: right is rechts
    NOTE: outer_macro: left is links
    NOTE: outer_macro: right is midden rechts

But if the inner macro inserts a comma, you get the original error

    30         %macro comma_macro(left, right);
    31          %put NOTE: comma_macro: left is &left;
    32          %put NOTE: comma_macro: right is &right;
    33          &left., &right.
    34         %mend;
    35         %outer_macro(left, %comma_macro(midle, right));
    NOTE: comma_macro: left is midle
    NOTE: comma_macro: right is right
    ERROR: More positional parameters found than defined.

I would expect %str to resolve this, but it does not. Why?

    36         %outer_macro(left, %str(%comma_macro(midle, right)));
    NOTE: comma_macro: left is midle, right
    NOTE: comma_macro: right is
    ERROR: More positional parameters found than defined.

Context

For your information: I need this because I wrote a macro to list a few fields I should query from a database and I need to pass my sql to the database via another macro.

%run_SQL(select key, %list_fields(some_arguments), from something);
Dirk Horsten
  • 3,753
  • 4
  • 20
  • 37

2 Answers2

5

The problem you have is that %str masks characters during macro compilation, not during macro execution. So, the macro call compiles appropriately, executes %comma_macro, but then once that's executed, the values are no longer quoted (as %str has already done its thing). It won't mask the values a second time.

That's evidenced further by the fact that your %comma_macro call is also wrong. It masks the ,, which then leads to %comma_macro thinking midle,right is the first argument (identically to if you'd used the %str inside the macro parentheses).

Note that SAS specifically mentions this practice as dangerous in the %str documentation:

Do not use %STR to enclose other macro functions or macro invocations that have a list of parameter values. Because %STR masks parentheses without a match, the macro processor does not recognize the arguments of a function or the parameter values of a macro invocation.

%quote is identical to %str, except it masks characters during resolution:

%QUOTE and %NRQUOTE mask the same items as %STR and %NRSTR, respectively. However, %STR and %NRSTR mask constant text instead of a resolved value. And, %STR and %NRSTR work when a macro compiles, while %QUOTE and %NRQUOTE work when a macro executes.

In your example, replacing %str with %quote would work perfectly well. I would argue %bquote is probably the preferred choice, however, as it provides additional protection for unmatched quotation marks. I've always assumed the b in %bquote stood for better, and never seen a reason to use %quote over it.

The %BQUOTE and %NRBQUOTE functions mask a character string or resolved value of a text expression during execution of a macro or macro language statement.

That sounds like what you're doing to me. You don't need to use %NRBQUOTE, unless & % characters might be in the result.

The table of macro quoting timelines:

Quoting Type  | Occurs At       | Mask details
%str          | Compilation     | Unmatched Quotations/Parens with %
%nrstr        | Compilation     | Same as %str plus & %
%quote        | Execution       | Same as %str
%nrquote      | Execution       | Same as %quote plus & %
%bquote       | Execution       | Unmatched quotes/parens automatically
%nrbquote     | Execution       | Same as %bquote plus & %
%superq       | Execution       | Only variable and without &, same list as %nbrquote
 (continuned)                   | Does not attempt to resolve anything inside of variable
Joe
  • 62,789
  • 6
  • 49
  • 67
  • I think officially the B in %BQUOTE stands for "blind", as in "quote whatever value comes from resolving this macro expression, even though I cannot see that value in this code." As opposed to %STR/%NRSTR, which quote symbols you can see. Agree, %BQUOTE replaced %QUOTE. I have NEVER found %NRBQUOTE useful, since it doesn't actually prevent the macro processor from trying to resolve % or &. I think %SUPERQ() does what %NRBQUOTE should have done. I believe Master Whitlock has written that %STR %NRSTR %BQUOTE %SUPERQ are a comprehensive suite of quoting functions that should suffice. – Quentin Mar 04 '16 at 17:23
  • `%QUOTE/%BQUOTE` both have that functionality though - they're both execution time, not compile time. It's very possible it means blind, but in my head I've always thought Better :) And - I agree with Ian, though I've run into at least one edge case where NBRQUOTE was what I wanted; I don't recall what that was. – Joe Mar 04 '16 at 17:33
  • Fair point. Perhaps blind to whether there is an unmatched quote mark. %Bquote can handle these %Quote can't (unless they are marked). But agree, %Bquote is definitely better, and came after %quote. So perhaps they just had to give it a new name. Would definitely be interested in an example where %NRBQUOTE is useful and does not generate failed resolution warnings. – Quentin Mar 04 '16 at 18:18
3

Quote the output from %COMMA_MACRO

33          %macro comma_macro(left, right);
34            %put NOTE: comma_macro: left is &left;
35            %put NOTE: comma_macro: right is &right;
36            %nrbquote(&left., &right.)
37            %mend;
38         %macro outer_macro(left, right);
39            %put NOTE: outer_macro: left is &left;
40            %put NOTE: outer_macro: right is &right;
41            %mend;
42         %outer_macro(left, %comma_macro(midle, right));
NOTE: comma_macro: left is midle
NOTE: comma_macro: right is right
NOTE: outer_macro: left is left
NOTE: outer_macro: right is midle, right
data _null_
  • 8,534
  • 12
  • 14
  • 1
    or %outer_macro(left, %nrbquote(%comma_macro(midle, right))); when %COMMA_MACRO does not quote the result. Maybe Quentin will come along and explain it all. – data _null_ Mar 04 '16 at 13:23