-
Notifications
You must be signed in to change notification settings - Fork 8
/
parmv.sas
336 lines (280 loc) · 13.2 KB
/
parmv.sas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
%macro parmv
/*----------------------------------------------------------------------
Parameter validation with standard error message generation.
----------------------------------------------------------------------*/
(_parm /* Macro parameter name (REQ) */
,_val= /* Space delimited list of valid values. (OPT) */
/* There are three special values of _VAL. To test for integers */
/* use either _val=POSITIVE or _val=NONNEGATIVE. To test for */
/* boolean values use _val=0 1. PARMV will accept the aliases */
/* (OFF N NO F FALSE) or (ON Y YES T TRUE) and convert them to */
/* 0 or 1, respectively. To test just for 1 and 0 then use */
/* _val=1 0 instead. */
,_req=0 /* Is a value required? 0=No, 1=Yes. */
,_words=0 /* Multiple values allowed? 0=No ,1=Yes */
,_case=U /* Change case of value? U=Upper, L=Lower, N=No conversion */
,_force=0 /* Force macro variable to exist? 0=No, 1=Yes. */
,_defvar= /* Name of macro variable to check for a default value */
/* when parameter value not assigned by calling macro. */
,_def= /* Default parameter value when not assigned by calling */
/* macro or by macro variable named in _defvar. */
,_msg= /* When specified, set parmerr to 1 and writes _msg as the */
/* last error message. */
);
/*----------------------------------------------------------------------
Use the PARMV macro to validate parameter values and generate error
messages in a standardized format. It can insure that values are in a
consistent value by setting default values and casing.
The calling macro requires local variable PARMERR to return error code.
Invoke macro PARMV once for each macro parameter. After the last
invocation branch to the macro's end whenever PARMERR equals 1 (e.g.,
%if (&parmerr) %then %goto quit;).
Set the global macro variable S_PARMV to 0 to disable the error testing
without disabling the case adjustment and default value setting.
When _VAL=0 1 then parameter will default to 0 when empty.
PARMV tool cannot be used for macros variables with names that match
the parameters (_PARM _VAL _REQ _WORDS _CASE _FORCE _DEFVAR _DEF _MSG)
or local macro variables (_WORD _N _VL _PL _ERROR _PARMTYP)
used by PARMV itself.
Use the _MSG parameter to set PARMERR to 1 and issue a message based on
errors detected by criteria within the calling program.
Note that for efficiency reasons, PARMV does not validate its own
parameters. Only code valid values for the _PARM, _REQ, _WORDS, _CASE,
and _FORCE parameters.
------------------------------------------------------------------------
Usage example:
%macro test(internal,ivar,lzero,uzero,high,day,print);
%local parmerr;
%parmv(interval,_req=1,_words=1)
%parmv(ivar,_def=period)
%parmv(visit,_req=1)
%if (%length(&visit) > 7) %then
%parmv(visit,_msg=SAS name containing 7 or less characters)
;
%parmv(lzero,_req=1)
%parmv(uzero,_req=1)
%parmv(high,_req=1,_val=0 1)
%parmv(day,_req=1)
%parmv(print,_def=1,_val=0 1)
%if (&parmerr) %then %goto quit;
....
%quit:
%mend test;
-----------------------------------------------------------------------
History:
Based on original macro developed by Tom Hoffman of HOFFMAN CONSULTING.
2005-03-20 abernt Added _DEFVAR parameter. Modified final unquote step
to skip unquoting if strings includes quote or dquote
(in addition to % and &).
2016-08-16 abernt Rewritten to use SAS 9.4 macro enhancements
----------------------------------------------------------------------*/
%local _word _n _vl _pl _error _parmtyp;
%*----------------------------------------------------------------------
Make sure return macro variables exists.
-----------------------------------------------------------------------;
%if ^%symexist(parmerr) %then %global parmerr;
%if ^%symexist(s_msg) %then %global s_msg;
%if ^%symexist(s_parmv) %then %let s_parmv=1;
%*----------------------------------------------------------------------
If PARMERR has never been set then initialize it and S_MSG.
-----------------------------------------------------------------------;
%if (&parmerr = ) %then %do;
%let parmerr = 0;
%let s_msg = ;
%end;
%*----------------------------------------------------------------------
Quote the parameter value and calculate length for a numeric switch.
When parameter does not exist then create it as local or gobal based on
the setting of _FORCE.
-----------------------------------------------------------------------;
%if ^%length(&_parm) %then %let _pl=-1;
%else %if %symexist(&_parm) %then %do;
%let &_parm=%superq(&_parm);
%let _pl = %length(&&&_parm);
%end;
%else %do;
%if (&_force) %then %do;
%global &_parm;
%let _pl = 0;
%end;
%else %do;
%local &_parm;
%let _pl = -1;
%end;
%end;
%*----------------------------------------------------------------------
Get length of _val to use as a numeric switch.
-----------------------------------------------------------------------;
%let _vl = %length(&_val);
%if (&_pl=0) & %length(&_defvar) %then %if %symexist(&_defvar) %then %do;
%*----------------------------------------------------------------------
Take default value from macro variable defined by _DEFVAR parameter.
-----------------------------------------------------------------------;
%let &_parm = %superq(&_defvar);
%let _pl = %length(&&&_parm);
%end;
%if (&_pl=0) & %length(&_def) %then %do;
%*----------------------------------------------------------------------
Take default value from _DEF parameter.
-----------------------------------------------------------------------;
%let _pl = %length(&_def);
%let &_parm = &_def;
%end;
%*----------------------------------------------------------------------
Adjust CASE of the parameter value and valid values if requested.
-----------------------------------------------------------------------;
%if (&_pl>0) and (%qupcase(&_case) = U) %then %do;
%let &_parm = %qupcase(&&&_parm);
%let _val = %qupcase(&_val);
%end;
%else %if (&_pl>0) and (%qupcase(&_case) = L) %then %do;
%if (&_pl) %then %let &_parm = %qsysfunc(lowcase(&&&_parm));
%if (&_vl) %then %let _val = %qsysfunc(lowcase(&_val));
%end;
%*----------------------------------------------------------------------
Handle aliases for BOOLEAN flags. Default to 0 when not required.
-----------------------------------------------------------------------;
%if (&_pl>-1) and (&_val = 0 1) %then %do;
%if ^(&_req) & ^(&_pl) %then %let &_parm=0;
%else %if %sysfunc(indexw(OFF NO N FALSE F,%upcase(&&&_parm)))
%then %let &_parm = 0;
%else %if %sysfunc(indexw(ON YES Y TRUE T,%upcase(&&&_parm)))
%then %let &_parm = 1;
%let _pl=%length(&&&_parm);
%end;
%*----------------------------------------------------------------------
Bail out when no parameter validation is requested
-----------------------------------------------------------------------;
%if (&s_parmv = 0) %then %goto quit;
%*----------------------------------------------------------------------
Initialize to no errors seen.
-----------------------------------------------------------------------;
%let _error = 0;
%*----------------------------------------------------------------------
Error 5: _MSG specified
-----------------------------------------------------------------------;
%if %length(&_msg) %then %let _error = 5;
%*----------------------------------------------------------------------
Error 6: Parameter does not exist
-----------------------------------------------------------------------;
%else %if (&_req) and (&_pl=-1) %then %let _error=6;
%*----------------------------------------------------------------------
Macro variable specified by _PARM is not null.
-----------------------------------------------------------------------;
%else %if (&_pl>0) %then %do;
%*----------------------------------------------------------------------
Error processing - parameter value not null
Error 1: Invalid value - not a positive/nonnegative integer
Error 2: Invalid value - not in valid list
Error 3: Multiple values not allowed
Error 4: Value required
-----------------------------------------------------------------------;
%*----------------------------------------------------------------------
Loop through possible list of words in the _PARM macro variable.
------------------------------------------------------------------------;
%if ((&_vl) | ^(&_words)) %then %do;
%let _n = 1;
%let _word = %qscan(&&&_parm,1,%str( ));
%*----------------------------------------------------------------------
Check against valid list for each word in macro parameter
-----------------------------------------------------------------------;
%do %while (%length(&_word));
%*----------------------------------------------------------------------
Positive integer check.
-----------------------------------------------------------------------;
%if (&_val = POSITIVE) %then %do;
%if %sysfunc(verify(&_word,0123456789)) %then %let _error = 1;
%else %if ^(&_word) %then %let _error = 1;
%end;
%*----------------------------------------------------------------------
Non-negative integer check.
-----------------------------------------------------------------------;
%else %if (&_val = NONNEGATIVE) %then %do;
%if %sysfunc(verify(&_word,0123456789)) %then %let _error = 1;
%end;
%*----------------------------------------------------------------------
Check against valid list. Note blank padding.
-----------------------------------------------------------------------;
%else %if (&_vl) %then %do;
%if ^%index(%str( &_val ),%str( &_word )) %then %let _error = 2;
%end;
%*---------------------------------------------------------------------
Get next word from parameter value
-----------------------------------------------------------------------;
%let _n = %eval(&_n + 1);
%let _word = %qscan(&&&_parm,&_n,%str( ));
%end; %* for each word in parameter value;
%*----------------------------------------------------------------------
Check for multiple _words. Set error flag if not allowed.
-----------------------------------------------------------------------;
%if (&_n ^= 2) & ^(&_words) %then %let _error = 3;
%end; %* valid not null ;
%end; %* parameter value not null ;
%*----------------------------------------------------------------------
Error processing - Parameter value null
Error 4: Value required.
-----------------------------------------------------------------------;
%else %if (&_req) %then %let _error = 4;
%*----------------------------------------------------------------------
Write error messages
-----------------------------------------------------------------------;
%if (&_error) %then %do;
%*----------------------------------------------------------------------
Set PARMERR to indicate error was found.
-----------------------------------------------------------------------;
%let parmerr = 1;
%*----------------------------------------------------------------------
Force parameter name to uppercase for clearer messages.
-----------------------------------------------------------------------;
%let _parm = %upcase(&_parm);
%*----------------------------------------------------------------------
Call the parameter a macro variable when called from open code or when
_FORCE was set.
-----------------------------------------------------------------------;
%if (&_force) | (1=%sysmexecdepth) %then %let _parmtyp=macro variable;
%else %if (&_pl>-1) %then %let _parmtyp = parameter;
%*----------------------------------------------------------------------
Write initial error message line.
-----------------------------------------------------------------------;
%put %str( );
%put ERROR:%str( )%sysmexecname(%sysmexecdepth-1) user error.;
%if (&_error = 1) %then %do;
%put ERROR: &&&_parm is not a valid value for the &_parm &_parmtyp..;
%put ERROR: Only &_val integers are allowed.;
%let _vl = 0;
%end;
%else %if (&_error = 2) %then
%put ERROR: &&&_parm is not a valid value for the &_parm &_parmtyp..
;
%else %if (&_error = 3) %then %do;
%put ERROR: &&&_parm is not a valid value for the &_parm &_parmtyp..;
%put ERROR: The &_parm &_parmtyp may not have multiple values.;
%end;
%else %if (&_error = 4) %then
%put ERROR: A value for the &_parm &_parmtyp is required.
;
%else %if (&_error = 5) %then %do;
%if (&_parm ^= ) and (&_pl) %then
%put ERROR: &&&_parm is not a valid value for the &_parm &_parmtyp..;
%put ERROR: &_msg..;
%end;
%else %if (&_error = 6) %then
%put ERROR: The &_parm &_parmtyp does not exist.
;
%if (&_vl) %then %do;
%if (&_val = 0 1) %then
%let _val=%quote(0 (or OFF NO N FALSE F) 1 (or ON YES Y TRUE T)) ;
%put ERROR: Allowable values are: &_val..;
%end;
%if %length(&_msg) %then %let s_msg = &_msg;
%else %let s_msg = Problem with%str( )%sysmexecname(%sysmexecdepth-1) &_parmtyp
validation - see LOG for details.;
%end; %* errors ;
%quit:
%*----------------------------------------------------------------------
Unquote the the parameter value, unless it contains an ampersand, per
cent sign, quote, or dquote.
-----------------------------------------------------------------------;
%if ^%sysfunc(indexc(&&&_parm,'&%"')) %then
%let &_parm = %unquote(&&&_parm);
%mend parmv;