From: Kevin Myers on
P.S. - I also wrote an xSubstr macro to eliminate the need for using my
xSubstr fuction via %sysfunc, but xIndexc (similar to indexc, except that it
ignores quoted and parenthesized values and also handles negative start
values and counts) is not so easily replaced...

And of course all of this came up in some programs that I have been using
under V8 for years, but now I don't have V8 any more. I didn't expect to
run into these problems, and of course I need the results TODAY! :-(

----- Original Message -----
From: "Kevin Myers" <kmyers1(a)CLEARWIRE.NET>
To: <SAS-L(a)LISTSERV.UGA.EDU>
Sent: Tuesday, March 09, 2010 11:39
Subject: Using PROC FCMP Functions with %SYSFUNC


Under SAS 9.2 I have several user defined functions that have been compiled
using PROC FCMP. These functions work fine when I use them in data step
code. However, when I attempt to use any of these functions in a macro via
%SYSFUNC, I get an error message that the function cannot be found, e.g.:

116 data _null_;
117
118 n=nchars("asdf ");
119 put n=;
120
121 c=xsubstr("asdf ",-4,2);
122 put c=;
123
124 n=xindexc("asdf ","sd",1,1,"","","","");
125 put n=;
126
127 run;
n=5
c=sd
n=2
NOTE: DATA statement used (Total process time):
real time 0.15 seconds
cpu time 0.15 seconds
?
ERROR: The NCHARS function referenced in the %SYSFUNC or %QSYSFUNC macro
function is not found.
128
129 %put %sysfunc(nchars(asdf));
130 %put %sysfunc(xsubstr(asdf,2,2));
ERROR: The XSUBSTR function referenced in the %SYSFUNC or %QSYSFUNC macro
function is not found.
131 %put %sysfunc(xindexc(asdf,sd,1,1,%str(),%str(),%str(),%str()));
ERROR: The XINDEXC function referenced in the %SYSFUNC or %QSYSFUNC macro
function is not found.

My nchars function is left over from V8 days, and is easily replaced with
the lengthc function in V9, but the other 2 functions (for which I
previously used SAS/Toolkit functions in V8) are not so easily replaced, as
there are still no equivalent built-in functions in V9.2, and I no longer
have access to SAS/Toolkit in 9.2.

Shouldn't these functions work with %SYSFUNC? Is this a known bug? Any
fixes/workarounds/alternate suggestions? The proc fcmp code for these
functions is provided below.

Thanks,
s/KAM


proc fcmp outlib=SASUtils.KAMUtils.V8Functions;

function nChars(inStr $);
return (lengthc(inStr));
endsub;

function xSubstr(inStr $, aStart, length) $;
if aStart<0 then start=lengthc(inStr) + aStart + 1; else start=aStart;
if missing(length) then return (substrn(inStr,start));
else return (substrn(inStr,start,length));
endsub;

function xIndexC(inStr $, search $, aStart, aValNo, aQuotes1 $, aQuotes2 $,
aParens1 $, aParens2 $);

length start valNo 8 quotes1 quotes2 parens1 parens2 $256;
length cInstr cQuotes1 cQuotes2 cParens1 cParens2 valNo2 8;
length inChar $1 strTemp $256;
length cTemp inc cPos quoteNo parenNo nParens 8;
array parens[1] /NOSYMBOLS;

start=aStart; valNo=aValNo;
quotes1=aQuotes1; quotes2=aQuotes2; parens1=aParens1; parens2=aParens2;

cInstr=lengthc(inStr);
call dynamic_array(parens,cInstr);

/* get start position and occurance arg values */

if missing(start) then start=1;
else if start<0 then start=cInstr+ceil(start)+1;
else start=floor(start);

if missing(valNo) then valNo=1;
else if valNo<0 then valNo=ceil(valNo);
else valNo=floor(valNo);

if start<=cInstr and valNo>=1 or start>=1 and valNo<=-1 then do;

/* adjust start position and determine increment */

if valNo>0 then do;
inc=1;
if start<1 then start=1;
end;
else do;
inc=-1;
if start>cInstr then start=cInstr;
end;

/* get optional quote and parenthesis arg values */

cQuotes1=lengthn(quotes1);
if lengthn(quotes2)=0 then quotes2=quotes1;
cQuotes2=lengthn(quotes2);

cParens1=lengthn(parens1);
cParens2=lengthn(parens2);

if inc<0 then do;

/* if searching backwards swap quote and paren arg values */

strTemp=quotes1; quotes1=quotes2; quotes2=strTemp;
cTemp=cQuotes1; cQuotes1=cQuotes2; cQuotes2=cTemp;

strTemp=parens1; parens1=parens2; parens2=strTemp;
cTemp=cParens1; cParens1=cParens2; cParens2=cTemp;

end;

valNo2=abs(valNo);
quoteNo=0; /* not yet in quoted string */
nParens=0; /* not yet in parentheses */

cPos=start; dPos=0;

*put inStr= search= start= valNo= quotes1= quotes2= parens1= parens2=;
*put cInstr= inc= cQuotes1= cQuotes2= cParens1= cParens2=;
*put valNo2= quoteNo= nParens=;

do while (cPos>=1 and cPos<=cInstr and not dPos);

inChar=substrn(inStr,cPos,1);
*put / cPos= inChar= quoteNo= nParens=;

if quoteNo then do;

/* process quoted string */

/* if closing quote found signal non-quoted mode */

if quoteNo<=cQuotes2 and inChar=substrn(quotes2,quoteNo) then do;
quoteNo=0; *put quoteNo=; end;

end; /* if quoteNo */

else /* if not quoteNo */ do;

/* not in quoted string */

/* if quote found then save closing quote */
/* which also signals quoted string mode */

if indexc(quotes1,inChar) then do;
quoteNo=indexc(quotes1,inChar);
*put quoteNo=;
end;

/* if open parenthesis found then save closing paren */
/* in parens array and increment paren levels counter */
/* which also signals parenthesized string mode */

else if indexc(parens1,inChar) then do;
nParens+1;
parenNo=indexc(parens1,inChar);
parens[nParens]=parenNo;
*put nParens= parenNo=;
end;

else if nParens then do;

/* process parenthesized string */

/* if closing parenthesis found then decrement */
/* paren levels counter and set new closing paren */

if parenNo<=cParens2 and inChar=substrn(parens2,parenNo,1) then do;
nParens+(-1);
if nParens then parenNo=parens[nParens];
*put nParens= parenNo=;
end;

end; /* else if nParens */

/* if search character found then set return value */

else if indexc(search,inChar) then do;
valNo2+(-1);
if valNo2<1 then dPos=cPos;
*put valNo2= dPos=;
end;

end; /* if not quoteNo */

/* increment input character pointer */
cPos+inc;

end; /* while (cPos>=1 and cPos<=cInstr and not dPos) */

if not dPos then do; dPos=cPos; *put dPos=; end;

end; /* if start not past EOS */

else if start<1 then do; dPos=0; *put dPos=; end;
else if start>lengthc(inStr) then do; dPos=lengthc(inStr)+1; *put dPos=;
end;
else do; dPos=start; *put dPos=; end; /* valNo=0 */

return (dPos);

endsub;

run;
From: Toby Dunn on
Not a bug according to the online docs:

You can use the functions and subroutines that you create in PROC FCMP
with the DATA step, the WHERE statement, the Output Delivery System (ODS),
and with the following procedures:

PROC CALIS

PROC COMPILE

PROC COMPUTAB

PROC GA

PROC GENMOD

PROC MCMC

PROC MODEL

PROC NLIN

PROC NLMIXED

PROC NLP

PROC PHREG

PROC REPORT COMPUTE blocks

Risk Dimensions procedures

PROC SIMILARITY

PROC SQL (functions with array arguments are not supported)



Never mentions using it in Macro code as you are attemtping to do.
My guess is it hasnt been integrated into the macro facility and wont be
until the next release. Even in version 9.1.3 and 9.2 I believe not all
the data step functions are usable in %SysFunc


On Tue, 9 Mar 2010 11:39:13 -0600, Kevin Myers <kmyers1(a)CLEARWIRE.NET>
wrote:

>Under SAS 9.2 I have several user defined functions that have been
compiled using PROC FCMP. These functions work fine when I use them in
data step code. However, when I attempt to use any of these functions in
a macro via %SYSFUNC, I get an error message that the function cannot be
found, e.g.:
>
>116 data _null_;
>117
>118 n=nchars("asdf ");
>119 put n=;
>120
>121 c=xsubstr("asdf ",-4,2);
>122 put c=;
>123
>124 n=xindexc("asdf ","sd",1,1,"","","","");
>125 put n=;
>126
>127 run;
>n=5
>c=sd
>n=2
>NOTE: DATA statement used (Total process time):
>real time 0.15 seconds
>cpu time 0.15 seconds
>?
>ERROR: The NCHARS function referenced in the %SYSFUNC or %QSYSFUNC macro
function is not found.
>128
>129 %put %sysfunc(nchars(asdf));
>130 %put %sysfunc(xsubstr(asdf,2,2));
>ERROR: The XSUBSTR function referenced in the %SYSFUNC or %QSYSFUNC macro
function is not found.
>131 %put %sysfunc(xindexc(asdf,sd,1,1,%str(),%str(),%str(),%str()));
>ERROR: The XINDEXC function referenced in the %SYSFUNC or %QSYSFUNC macro
function is not found.
>
>My nchars function is left over from V8 days, and is easily replaced with
the lengthc function in V9, but the other 2 functions (for which I
previously used SAS/Toolkit functions in V8) are not so easily replaced,
as there are still no equivalent built-in functions in V9.2, and I no
longer have access to SAS/Toolkit in 9.2.
>
>Shouldn't these functions work with %SYSFUNC? Is this a known bug? Any
fixes/workarounds/alternate suggestions? The proc fcmp code for these
functions is provided below.
>
>Thanks,
>s/KAM
>
>
>proc fcmp outlib=SASUtils.KAMUtils.V8Functions;
>
> function nChars(inStr $);
> return (lengthc(inStr));
> endsub;
>
> function xSubstr(inStr $, aStart, length) $;
> if aStart<0 then start=lengthc(inStr) + aStart + 1; else start=aStart;
> if missing(length) then return (substrn(inStr,start));
> else return (substrn(inStr,start,length));
> endsub;
>
> function xIndexC(inStr $, search $, aStart, aValNo, aQuotes1 $, aQuotes2
$, aParens1 $, aParens2 $);
>
> length start valNo 8 quotes1 quotes2 parens1 parens2 $256;
> length cInstr cQuotes1 cQuotes2 cParens1 cParens2 valNo2 8;
> length inChar $1 strTemp $256;
> length cTemp inc cPos quoteNo parenNo nParens 8;
> array parens[1] /NOSYMBOLS;
>
> start=aStart; valNo=aValNo;
> quotes1=aQuotes1; quotes2=aQuotes2; parens1=aParens1; parens2=aParens2;
>
> cInstr=lengthc(inStr);
> call dynamic_array(parens,cInstr);
>
> /* get start position and occurance arg values */
>
> if missing(start) then start=1;
> else if start<0 then start=cInstr+ceil(start)+1;
> else start=floor(start);
>
> if missing(valNo) then valNo=1;
> else if valNo<0 then valNo=ceil(valNo);
> else valNo=floor(valNo);
>
> if start<=cInstr and valNo>=1 or start>=1 and valNo<=-1 then do;
>
> /* adjust start position and determine increment */
>
> if valNo>0 then do;
> inc=1;
> if start<1 then start=1;
> end;
> else do;
> inc=-1;
> if start>cInstr then start=cInstr;
> end;
>
> /* get optional quote and parenthesis arg values */
>
> cQuotes1=lengthn(quotes1);
> if lengthn(quotes2)=0 then quotes2=quotes1;
> cQuotes2=lengthn(quotes2);
>
> cParens1=lengthn(parens1);
> cParens2=lengthn(parens2);
>
> if inc<0 then do;
>
> /* if searching backwards swap quote and paren arg values */
>
> strTemp=quotes1; quotes1=quotes2; quotes2=strTemp;
> cTemp=cQuotes1; cQuotes1=cQuotes2; cQuotes2=cTemp;
>
> strTemp=parens1; parens1=parens2; parens2=strTemp;
> cTemp=cParens1; cParens1=cParens2; cParens2=cTemp;
>
> end;
>
> valNo2=abs(valNo);
> quoteNo=0; /* not yet in quoted string */
> nParens=0; /* not yet in parentheses */
>
> cPos=start; dPos=0;
>
> *put inStr= search= start= valNo= quotes1= quotes2= parens1= parens2=;
> *put cInstr= inc= cQuotes1= cQuotes2= cParens1= cParens2=;
> *put valNo2= quoteNo= nParens=;
>
> do while (cPos>=1 and cPos<=cInstr and not dPos);
>
> inChar=substrn(inStr,cPos,1);
> *put / cPos= inChar= quoteNo= nParens=;
>
> if quoteNo then do;
>
> /* process quoted string */
>
> /* if closing quote found signal non-quoted mode */
>
> if quoteNo<=cQuotes2 and inChar=substrn(quotes2,quoteNo) then do;
> quoteNo=0; *put quoteNo=; end;
>
> end; /* if quoteNo */
>
> else /* if not quoteNo */ do;
>
> /* not in quoted string */
>
> /* if quote found then save closing quote */
> /* which also signals quoted string mode */
>
> if indexc(quotes1,inChar) then do;
> quoteNo=indexc(quotes1,inChar);
> *put quoteNo=;
> end;
>
> /* if open parenthesis found then save closing paren */
> /* in parens array and increment paren levels counter */
> /* which also signals parenthesized string mode */
>
> else if indexc(parens1,inChar) then do;
> nParens+1;
> parenNo=indexc(parens1,inChar);
> parens[nParens]=parenNo;
> *put nParens= parenNo=;
> end;
>
> else if nParens then do;
>
> /* process parenthesized string */
>
> /* if closing parenthesis found then decrement */
> /* paren levels counter and set new closing paren */
>
> if parenNo<=cParens2 and inChar=substrn(parens2,parenNo,1) then do;
> nParens+(-1);
> if nParens then parenNo=parens[nParens];
> *put nParens= parenNo=;
> end;
>
> end; /* else if nParens */
>
> /* if search character found then set return value */
>
> else if indexc(search,inChar) then do;
> valNo2+(-1);
> if valNo2<1 then dPos=cPos;
> *put valNo2= dPos=;
> end;
>
> end; /* if not quoteNo */
>
> /* increment input character pointer */
> cPos+inc;
>
> end; /* while (cPos>=1 and cPos<=cInstr and not dPos) */
>
> if not dPos then do; dPos=cPos; *put dPos=; end;
>
> end; /* if start not past EOS */
>
> else if start<1 then do; dPos=0; *put dPos=; end;
> else if start>lengthc(inStr) then do; dPos=lengthc(inStr)+1; *put
dPos=; end;
> else do; dPos=start; *put dPos=; end; /* valNo=0 */
>
> return (dPos);
>
> endsub;
>
>run;
From: "Data _null_;" on
Does FINDC provide any of the features you describe for your XINDEXC?

Do the new features of the SCAN functions and call routine provide any
further help?

What about regex?



On 3/9/10, Kevin Myers <KevinMyers(a)austin.rr.com> wrote:
> P.S. - I also wrote an xSubstr macro to eliminate the need for using my
> xSubstr fuction via %sysfunc, but xIndexc (similar to indexc, except that it
> ignores quoted and parenthesized values and also handles negative start
> values and counts) is not so easily replaced...
From: Kevin Myers on
Null -

Those suggestions are helpful, thanks.

FINDC allows the negative start value supported by my xIndexC function, but
does not support the occurance number, and doesn't support the "q" modfier
(as supported by the SCAN function) that would be needed to avoid searching
within quoted values.

Modifying my existing code to use regular expressions would take quite a bit
of time, but may be possible.

xIndexC gets used in my code primarily as a utility within other functions
that are performing various string searches and manipulations. I may be
able to make additional changes to the calling functions that would allow
for replacing xIndexc with code based on FINDC and SCAN. That will also
take some time, but presently seems like my best option.

I'll look into these further...

Regards,
s/KAM


----- Original Message -----
From: "Data _null_;" <iebupdte(a)gmail.com>
To: "Kevin Myers" <KevinMyers(a)austin.rr.com>
Cc: <SAS-L(a)listserv.uga.edu>
Sent: Tuesday, March 09, 2010 12:03
Subject: Re: Using PROC FCMP Functions with %SYSFUNC


> Does FINDC provide any of the features you describe for your XINDEXC?
>
> Do the new features of the SCAN functions and call routine provide any
> further help?
>
> What about regex?
>
>
>
> On 3/9/10, Kevin Myers <KevinMyers(a)austin.rr.com> wrote:
>> P.S. - I also wrote an xSubstr macro to eliminate the need for using my
>> xSubstr fuction via %sysfunc, but xIndexc (similar to indexc, except that
>> it
>> ignores quoted and parenthesized values and also handles negative start
>> values and counts) is not so easily replaced...
>
From: "Data _null_;" on
On 3/9/10, Kevin Myers <KevinMyers(a)austin.rr.com> wrote:
> xIndexC gets used in my code primarily as a utility within other functions
> that are performing various string searches and manipulations. I may be
> able to make additional changes to the calling functions that would allow
> for replacing xIndexc with code based on FINDC and SCAN. That will also
> take some time, but presently seems like my best option.

Another thought with regards to %SYSFUNC. It might be possible for
you to use data step(s) where you are now using MACRO language and
%SYSFUNC. Which would require some extra work but may allow you to use
your FCMP functions.

Do you want some help with regards to "various string searches and
manipulations"? You might get solutions that are "better" and do not
require as much special processing as you might think.