Question about Functions and Dynamic variables

I’m wanting to write a Function that will receive a string and determine if there are “invalid” characters in the string. I’d like the Function to handle flexible string sizes (dynamic) but the calling program shouldn’t have to define it’s string as dynamic. For example.

program


0001 DEFINE DATA LOCAL
0002 1 #STR (A25) INIT <'ABC$123'>
0003 END-DEFINE
0004 *
0005 DEFINE PROTOTYPE HAS_INVALID_CHARS RETURNS (L)
0006 DEFINE DATA PARAMETER UNKNOWN
0007 END-DEFINE
0008 END-PROTOTYPE
0009 *
0010 IF HAS_INVALID_CHARS(<STR>)
0011   write 'This string has invalid characters.'
0012 END-IF
0013 END

Note to web master: In line 10 above, the message editor removed a # before STR.

function


0001 DEFINE FUCNTION HAS_INVALID_CHARS RETURNS (L)
0002 DEFINE DATA PARAMETER
0003 1 #PARM-STRING (A) DYNAMIC
0004 LOCAL
...
0009 END-DEFINE

...determine if string contains any specific special characters. I need more power than using MASK(S)

0013 IF #INVALID
0014   MOVE #INVALID TO HAS_INVALID_CHARS
0015 END-IF
0016 END-FUNCTION
0017 END

The problem: I’m getting a runtime error (NAT0969) indicating that the parameters don’t match.

Yes, I could define a dynamic string (#str-d) in the program, move #str to #str-d, then pass #str-d to the function but that reduces the power using the function.

Any ideas?

By the way, Functions are awesome and if you’re not using them yet, you need to take a good look at them.

Thanks,
Stevie Lee J

I have not tried it and am leaving in 2 minutes; but have you tried to make the DYNAMIC parameter BY VALUE or BY VALUE RESULT?

steve

I have now had time to try this. It works just fine. For example:

define data local
1 #a (a10) INIT <‘QWE’>
1 #b (a10) INIT <‘ASD’>
end-define
*
define prototype dyntest
returns (a10)
define data parameter
1 #a (a10)
1 #b (a10)
end-define
end-prototype
*
WRITE 5T ‘DATA FOR THIS PROGRAM’ /
5T ‘---------------------’ /
5T ‘=’ #A /
5T ‘=’ #B ///
*
write 5t ‘output is :’ dyntest ()
END

And here is the function dyntest

DEFINE FUNCTION DYNTEST
*
RETURNS (A10)
*

  • NOTE THE USE OF BY VALUE RESULT IN THE PDA
  • DEFINE DATA PARAMETER
    1 #A (A) DYNAMIC BY VALUE RESULT
    1 #B (A) DYNAMIC BY VALUE RESULT
    END-DEFINE
  • NOW WE GET TO THE “BODY” OF THE FUNCTION
    IF #A = #B
    MOVE ‘EQUAL’ TO DYNTEST
    ELSE
    MOVE ‘NOT EQUAL’ TO DYNTEST
    END-IF

END-FUNCTION
END

The above works just fine. Note that by making #a and #b not only DYNAMIC, but BY VALUE RESULT, I could accept any compatible format for them (different A, or (A) DYNAMIC), and also could change them in my function and have the change show up back in the program.

I could also have define RETURNS as (A) DYNAMIC.

steve

Thanks Steve,

by changing line 3 of the Function as follows

0003 1 #PARM-STRING (A) DYNAMIC BY VALUE 

I’m getting the exact result I desired.

Thanks,

Stevie Lee J

I was interested to see your post, as I have written a function recently which provides extended MASK functionality for just this kind of purpose. In case you might find it helpful I’ve attached it to a post in the Natural Code Samples forum section.
Here are the extended mask characters it provides:


*     @  =  Any letter or digit, but not space
*     ^  =  Space
*     +  =  Tab
*     |  =  Any of the delimiters Tab, Pipe, Comma or Semi-Colon.
*     -  =  Any of the date format delimiters Dash, Slash or Period.
*     {} =  Hex string eg {0D0A09} = carriage return, line feed, tab
*     ~  =  Any number (or none) of the next mask char.
*           Can be followed by m[:n] where m and n 
*           are the min and max number of the char to find:
*              ~0x is the same as ~x
*              ~1x is at least one of the mask character x
*              ~2:5x scans for 2 to 5 instances of the character x
*           Note that this test finds the longest match it can.
*           Therefore, without a max, the subsequent mask test char
*           (after x) must not be a subset of x, as this will fail,
*           since all the string x's will have been passed by the ~x.
*     ¬  =  Any one char which is NOT the next mask char.
*           Can be used with ~[m:n], eg ~1:2¬x
*           (but not directly before the ~)
*     [] =  Optional characters, to be accepted if possible.
*           Note that preceding mask chars are tested independently,
*           whereas subsequent are tested together with the optional,
*           so YY[YY] means YY, optionally followed by another YY,
*           whereas [YY]YY means either YY or YYYY.
*     $  = User-defined set of characters
*
* Mask Examples:
*
*     ¬A       Any character except a letter.
*     ~@       Any letters or digits.
*     ~¬C      Any chars which are not letters or digits or space.
*     ~^~$     Any spaces, then any chars which are in Set.
*     ¬$       Not a Set char, 
*                 e.g. if Set = '+-*/=' then any char but +-*/=
*     ~1¬@     One or more chars which are not letters or digits.
*     *A¬/     String contains a letter which is not at the end.
*     *P{0D}P  String contains a carriage return 
*              surrounded by printable characters.
*
* Call Examples:
*
*     1. IF MASK$( #String,'*|*¬|/' )
*
*           is TRUE if #String contains a delimiter,
*                      and does not end with a delimiter.
*
*     2. IF MASK$( #String,'~1:5$/','"AEIOUaeiou"' )
*
*           is TRUE if #String contains 1 to 5 vowels only.
*
*     3. IF MASK$( #String,'*DD-MM-[YY]YY',,50,20 )
*
*           is TRUE if SUBSTRING(#String,50,20) 
*                      contains a date as dd,mm,yy or yyyy
*                      separated by dashes, slashes or periods.
*
*     4. RESET #S #L
*        IF MASK$( #String,'*U*","N',,#S,#L )
*
*           is TRUE if #String contains an upper case letter
*                   followed later by a comma & digit.
*                   #S returns the position of U in the string
*                   and #L returns the length
*                   from U to first ,N inclusive.

Sorry to come in so late on this thread …
.
I tried Steve’s PROGRAM and FUNCTION with Natural v6.3.8 on Windows but it doesn’t work. It gets a NAT0285 error on the line:
WRITE 5T ‘output is :’ DYNTEST () /* error line
Perhaps this was just a typo or a strange early behaviour which has been fixed ??
.

  1. In the Program, the function call should be:
    WRITE 5T ‘output is :’ DYNTEST (
    ) /* explicit Function call
  2. and the “DEFINE PROTOTYPE” statement is optional - because we call the function explicitly with the correct function name (not dynamically with the function name in a variable)

Correction:
DYNTEST () /* ie. DYNTEST ()

Ahh, I see the problem … stupid Editor replaces good text with garbage !
DYNTEST ( ) /* Hash-A, Hash-B