DATA Step, Macro, Functions and more

prxchange() within %sysfunc()

Reply
Respected Advisor
Posts: 4,173

prxchange() within %sysfunc()

Hi all

Using the following prxchange() function in a data step and everything works fine:

data _null_;
NewString=prxchange('s/ old / NEWER /oi',-1,' new and old ');
put NewString=;
run;

in SAS log:
NewString=new and NEWER



Using exactly the same syntax in a %sysfunc():

%put New String: %sysfunc(prxchange('s/ old / NEWER /oi',-1,' new and old '));

... and I get the following error:

ERROR: The routine PRXCHANGE was called using a regular expression that contains no replacement text.
ERROR: An error occurred while executing function PRXCHANGE referenced by the %SYSFUNC or %QSYSFUNC
macro function.
New String:


What am I missing here?

Thanks
Patrick

P.S: I'm using SAS9.2 under Windows 2003 Message was edited by: Patrick
Respected Advisor
Posts: 3,799

Re: prxchange() within %sysfunc()

I learned this on SAS-L. Replace / with ! the / causes trouble with implied eval "I think". Also don't use quotes, but maybe sometimes.

[pre]
63 %put New String: %sysfunc(prxchange(s! old ! NEWER !oi,-1,%str( new and old )));
New String: new and NEWER
[/pre]
Respected Advisor
Posts: 4,173

Re: prxchange() within %sysfunc()

Posted in reply to data_null__
Thank you data _null_

I'm asking myself now if I should start in general to use '!' instead of '/' for the sake of unified code. ...but then my syntax would differ from the docu.... Hmmm!?

Any thoughts about this?

Thanks
Patrick
Super User
Posts: 10,020

Re: prxchange() within %sysfunc()

Hi . Patrick.

Agree with data _null_;
Because %sysfunc(prxchange()) treats all of parameters as text ( for the sake of Macro ) , so there is no need to have quote .
Occasional Contributor
Posts: 10

Re: prxchange() within %sysfunc()

This is less a case of PRX not being able to handle the '/' and more the case of using qoutes when there shouldnt be any, trying to use PRX functions like you would in the data step.

In short when one is using the PRX functions in teh macro facility always use PRXParse first to create the pattern.

%Let Pattern = %SysFunc( PrxParse( s/ old / NEWER /oi ) ) ;
%Put %SysFunc( PrxChange( &Pattern , -1 , %str( new and old ) ) ) ;

Its not the '/' per se that screw things up but rather when you try to let the functions compile their pattern as many of us do. My guess, Im going to send this on to Jason at SAS who is responsible for these functions, is that there is something hinky going on when the Macro facility passes everything over to the Perl dll. In short this never should have been a problem, the initial use of quotes and failure to use the %str function yes, but not the use of /'s.
SAS Employee
Posts: 39

Re: prxchange() within %sysfunc()

Posted in reply to TobyDunn_hotmail_com
I believe that everything is work as it should. However, the need to quote certain characters in Macro with Macro quoting functions is different than how quoting works in DATA step. This post will discuss three items:

(1) The valid delimiter charaterse that delineate parts of a regular expression.
(2) How to get / working as a delimiter with %SYSFUNC.
(3) How to use PRXDEBUG to see the regular expression PRXCHANGE is passing to Perl.






The delimiters for a Perl regular expression can be any non-alphanumeric character. So, using ! or ' or | are valid delimiters. The most common delimiter is /, however feel free to use the delimiter that works the best for your situation. In the case of Macro, using ! may be easier is most situations because it doesn't require Macro quoting.





Now, how do we use / as a delimiter from %SYSFUNC? In the original %PUT code:





[pre]
%put New String: %sysfunc(prxchange('s/ old / NEWER /oi',-1,' new and old '));
[/pre]




The regular expression given to PRXCHANGE is 's/ old / NEWER /oi', including the single quotes. PRXCHANGE treats the single quotes as the delimiter and since there isn't anything after the second delimiter, it complains that there isn't any replacement text.





The way strings are passed to functions from Macro is different than in DATA step. Toby hints at how to quote the regular expression with %STR(). To get this code to work with / as the delimiter, wrap the regular expression with %STR(), as in this example:





[pre]
%put New String: %sysfunc(prxchange(%str(s/ old / NEWER /oi),-1,' new and old '));
[/pre]




To debug this problem, PRXDEBUG could have been used to see the regular expression that Perl received from the PRXCHANGE. Here is an example showing how to use PRXDEBUG from DATA step and Macro.





In the DATA step case, notice that the pattern being searched for is ` old ' while in the Macro case it is `s/ old / NEWER /oi'. In the Macro case, the search string is literally "s/ old / NEWER /oi", indicating an unintended search pattern is being given to Perl. While the PRXDEBUG output isn't the most elegant, it may help point out a problem .





[pre]
20 data _null_;
21 call prxdebug(1);
22 NewString=prxchange('s/ old / NEWER /oi',-1,' new and old ');
23 put NewString=;
24 run;

Compiling REx ` old '
size 4 first at 1
1: EXACTF < old >(4)
4: END(0)
stclass `EXACTF < old >' minlen 5

Matching REx ` old ' against ` new and old '
Setting an EVAL scope, savestack=0
8 <new and> < old > | 1: EXACTF < old >
13 <new and old > <> | 4: END
Match successful!
Match failed

NewString=new and NEWER

25
26 %let one=1;
27 %syscall prxdebug(one);
28 %put New String: %sysfunc(prxchange('s/ old / NEWER /oi',-1,' new and old '));
Compiling REx `s/ old / NEWER /oi'
size 7 first at 1
rarest char W at 11
1: EXACT <s/ old / NEWER /oi>(7)
7: END(0)
anchored `s/ old / NEWER /oi' at 0 (checking anchored isall) minlen 18

ERROR: The routine PRXCHANGE was called using a regular expression that
contains no replacement text.
ERROR: An error occurred while executing function PRXCHANGE referenced by the
%SYSFUNC or %QSYSFUNC macro function.
[/pre]




I hope this helps.



Jason
Respected Advisor
Posts: 4,173

Re: prxchange() within %sysfunc()

Posted in reply to JasonS_SAS
Hi guys
I’m now kind of pleased that I’ve got the syntax wrong and asked the question.
Thanks a lot for all your quality answers. They’ve taught me a lot.
Cheers
Patrick
Occasional Contributor
Posts: 14

Re: prxchange() within %sysfunc()

Posted in reply to JasonS_SAS
Hello everyone,
JasionS, I work with SAS 9.1.3 and I do infact belive that not everything is working correctly with the prx functions when used within %sysfunc.

Both the prxmatch and prxchange functions are overloaded, they can have their first argument to be a number (regex_id) or a string with a regex.
Everytime one uses the string overload and use / as a delimiter I think SAS assumes that we are trying to make a division and our argument should be a number, we get a error message :

%put %sysfunc(prxchange(%str(s/\d/a/),-1,123));
(ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required.)

If we parse the regex in advance and then use the regex_id in the prxmatch/prxchange function, everything works like it should:
%let regex=%sysfunc(prxparse(%str(s/\d/a/)));
%put %sysfunc(prxchange(&regex,-1,123));
aaa

Edit: using numerical operators as a delimiter have the same problem, if I use ! or ? as a delimiter all works fine.
Tried also to quote only the slashes(s%str(/)\d%str(/)a%str(/)) and I get the same result Message was edited by: Pgloria
SAS Employee
Posts: 39

Re: prxchange() within %sysfunc()

My prior post isn't entirely correct. For instance, you do not need the %STR around the regular expression. And, there is a difference in behavior in macro between SAS 9.2 and earlier versions of SAS.



Given that I'm the world's worst macro programmer :-), I had a side chat with the developer for macro and Toby to educate me on what's happening with this seemingly simple example. Here is a better analysis of this situation:



(1) The original code shouldn't put single quotes around the regular expression because macro passes the single quotes to PRXCHANGE, which causes a PRXCHANGE error.



[pre]
%put New String: %sysfunc(prxchange('s/ old / NEWER /oi',-1,' new and old '));
[/pre]


Should be:



[pre]
%put New String: %sysfunc(prxchange(s/ old / NEWER /oi,-1,' new and old '));
[/pre]


(2) On SAS 9.1.3, this code causes an error. On SAS 9.2, this code works fine.



In SAS 9.1.3, the macro processor sees PRXCHANGE could have a numeric or character first argument and it sees the / in the first argument, so it attempts to evaluate the first argument as a numeric expression. This results in an error, an error is output to the log, and a zero length string is passed to PRXCHANGE. PRXCHANGE then outputs an error because an invalid pattern was passed to it.



In SAS 9.2, the macro processor was modified such that if the numeric evaluation fails, it assumes the value must be a character value and the character value is passed to PRXCHANGE. At this point, folks with SAS 9.2 are happy.



What should folks with SAS 9.1.3 do?



I see two solutions:



(2a) Change the regular expression delimiter from / to a non-arithmetic character, like !.


[pre]
%put New String: %sysfunc(prxchange(s! old ! NEWER !oi,-1,' new and old '));
[/pre]


(2b) Have a three statement program that uses PRXPARSE and PRXFREE.



[pre]
%let re = %sysfunc(prxparse(s/ old / NEWER /oi));
%put New String: %sysfunc(prxchange(&re,-1,' new and old '));
%syscall prxfree(re);
[/pre]


This solution works because PRXPARSE only accepts a character argument, so no numeric evaluation is attempted.



(3) The original code has single quotes around the string to be modified. If the result shouldn't have single quotes, the %STR macro function can be used to preserve leading and trailing blanks.



[pre]
%put New String: %sysfunc(prxchange(s! old ! NEWER !oi,-1,%str( new and old )));
[/pre]


Does this make sense?



Jason
Ask a Question
Discussion stats
  • 8 replies
  • 2236 views
  • 0 likes
  • 6 in conversation