Compaq BASIC for OpenVMS
Alpha and VAX Systems
User Manual


Previous Contents Index

12.6.5 STRING$ Function

The STRING$ function creates a character string containing multiple occurrences of a single character. In the following example, 23 is the length of the returned string, and 30 is the ASCII value of the character that makes up the string. This value is treated modulo 256.


STRING$(23,30) 

The following example creates a 10-character string containing uppercase As, which have ASCII value 65:


out$ = STRING$(10%, 65%) 
PRINT out$ 
END 

Output


AAAAAAAAAA 

Keep these considerations in mind when you use STRING$:

12.6.6 SPACE$ Function

The SPACE$ function creates a character string containing spaces. In this example, 5 is the number of spaces in the string:


SPACE$(5) 

The following example creates a 9-character string which contains 3 spaces:


A$ = "ABC" 
B$ = "XYZ" 
PRINT A$ + SPACE$(3%) + B$ 
END 

Output


ABC   XYZ 

12.6.7 TRM$ Function

The TRM$ function removes trailing blanks and tabs from a string. The input string remains unchanged. In the following example, all trailing blanks that appear in the string expression "ABCDE " are removed once the TRM$ function is invoked:


A$ = "ABCDE   " 
B$ = "XYZ" 
first$ = A$ + B$ 
second$ = TRM$(A$) + B$ 
PRINT first$ 
PRINT second$ 
END 

Output


ABCDE   XYZ 
ABCDEXYZ 

The TRM$ function is especially useful for extracting the nonblank characters from a fixed-length string (for example, a COMMON or MAP, or a parameter passed from a program written in another language).

12.6.8 EDIT$ Function

The EDIT$ function performs one or more string editing functions, depending on the value of an argument you supply. The input string remains unchanged. In the following example, stu_rec is a string expression and 32 determines the editing function performed:


EDIT$(stu_rec,32) 

Table 12-2 shows the action BASIC takes for a given value of the integer expression.

Table 12-2 EDIT$ Options
Value of
Expression
Effect
1 Discards each character's parity bit (bit 7). Note that you should not use this value for characters in the DEC Multinational character set.
2 Discards all spaces and tabs.
4 Discards all carriage returns, line feeds, form feeds, deletes, escapes, and nulls.
8 Discards leading spaces and tabs.
16 Converts multiple spaces and tabs to a single space.
32 Converts lowercase letters to uppercase.
64 Converts left brackets ([) to left parentheses [(], and right brackets (]) to right parentheses [)].
128 Discards trailing spaces and tabs. (Same as TRM$ function.)
256 Suppresses all editing for characters within quotation marks. If the string has only one quotation mark, BASIC suppresses all editing for the characters following the quotation mark.

All values are additive; for example, by specifying 168, you can perform the following:

The following example requests an input string, discards all spaces and tabs, converts lowercase letters to uppercase, and converts brackets to parentheses:


LINPUT "PLEASE TYPE A STRING";input_string$ 
new_string$ = EDIT$(input_string$, 2% + 32% + 64%) 
PRINT new_string$ 
END 

Output


PLEASE TYPE A STRING? 88  abc[TAB][5,5] 
88ABC(5,5) 

12.7 Manipulating String Data with Multiple Maps

Mapping a string storage area in more than one way lets you extract a substring from a string or concatenate strings. In the following example, the three MAP statements reference the same 108 bytes of data:


MAP (emprec) first_name$ = 10,                 & 
             last_name$ = 20,                  & 
             street_number$ = 6,               & 
             street$ = 15,                     & 
             city$ = 20,                       & 
             state$ = 2,                       & 
             zip$ = 5,                         & 
             wage_class$ = 2,                  & 
             date_of_review$ = 8,              & 
             salary_ytd$ = 10,                 & 
             tax_ytd$ = 10 
MAP (emprec) full_name$ = 30,                  & 
             address$ = 48,                    & 
             salary_info$ = 30 
MAP (emprec) employee_record$ = 108 

You can move data into a map in different ways. For instance, you can use terminal input, arrays, and files. In the following example, the READ and DATA statements are used to move data into a map:


READ EMPLOYEE_RECORD$ 
DATA "WILLIAM   DAVIDSON            2241  MADISON BLVD   " & 
"SCRANTON            PA14225A912/10/78$13,325.77$925.31" 
 

Because all the MAP statements in the previous example reference the same storage area (emprec), you can access parts of this area through the mapped variables as shown in the following examples:


PRINT full_name$ 
PRINT wage_class$ 
PRINT salary_ytd$ 

Output 1


WILLIAM   DAVIDSON 
A9 
$13,325.77 

Example 2


PRINT last_name$ 
PRINT tax_ytd$ 

Output 2


DAVIDSON 
$925.31 

You can assign a new value to any of the mapped variables. The following example prompts the user for changed information by displaying a menu of topics. The user can then choose which topics need to be changed and then separately assign new values to each variable.


Loop_1: 
WHILE 1% = 1% 
      INPUT "Changes? (please type YES or NO)"; CH$ 
      EXIT Loop_1 IF CH$ = "NO" 
      PRINT "1. FIRST NAME" 
      PRINT "2. LAST NAME" 
      PRINT "3. STREET NUMBER" 
      PRINT "4. STREET" 
      PRINT "5. CITY" 
      PRINT "6. STATE" 
      PRINT "7. ZIP" 
      PRINT "8. WAGE CLASS" 
      PRINT "9. DATE OF REVIEW" 
      PRINT "10. SALARY YTD" 
      PRINT "11. TAX YTD" 
      INPUT "CHANGE NUMBER"; NUMBER% 
      SELECT NUMBER% 
      CASE 1% 
           INPUT "FIRST NAME"; first_name$ 
      CASE 2% 
           INPUT "LAST NAME"; last_name$ 
      CASE 3% 
           INPUT "STREET NUMBER"; street_number$ 
      CASE 4% 
           INPUT "STREET"; street$ 
      CASE 5% 
           INPUT "CITY"; city$ 
      CASE 6% 
          INPUT "STATE"; state$ 
      CASE 7% 
           INPUT "ZIP CODE"; zip$ 
      CASE 8% 
           INPUT "WAGE CLASS"; wage_class$ 
      CASE 9% 
           INPUT "DATE OF REVIEW"; date_of_review$ 
      CASE 10% 
           INPUT "SALARY YTD"; salary_ytd$ 
      CASE 11% 
           INPUT "TAX YTD"; tax_ytd$ 
      CASE ELSE 
           PRINT "Invalid choice" 
      END SELECT 
NEXT 
END 

Output


Changes? (please type YES or NO)? YES 
1.  FIRST NAME 
2.  LAST NAME 
3.  STREET NUMBER 
4.  STREET 
5.  CITY 
6.  STATE 
7.  ZIP 
8.  WAGE CLASS 
9.  DATE OF REVIEW 
10.  SALARY YTD 
11.  TAX YTD 
 
CHANGE NUMBER? 10 
SALARY YTD? 14,277.08 
Changes? (please type YES or NO)? YES 
CHANGE NUMBER? 11 
TAX YTD? 998.32 
Changes? (please type YES or NO)? NO 

See Chapter 8 and the Compaq BASIC for OpenVMS Alpha and VAX Systems Reference Manual for more information about the MAP statement.


Chapter 13
Program Segmentation

Program segmentation is the process of dividing a program into small, manageable routines and modules. In a segmented or modular program, each routine or module usually performs only one logical function. You can, therefore, design and implement a modular program faster than a nonsegmented program. Program modularity also simplifies debugging and testing, as well as program maintenance and transportability.

This chapter describes how to:

Subprograms processed by the BASIC compiler conform to the OpenVMS Procedure Calling Standard. This standard prescribes how arguments are passed, how values are returned, and how procedures receive and return control. Because BASIC conforms to the OpenVMS Procedure Calling Standard, a BASIC subprogram or main program can call or be called by any procedure written in a language that also conforms to this standard. For information about calling non BASIC procedures, see Chapter 20.

13.1 BASIC Subprograms

BASIC has SUB, FUNCTION, and PICTURE subprograms. Each of these subprograms receives parameters and can modify parameters passed by reference or by descriptor. The differences between SUB, FUNCTION, and PICTURE subprograms are as follows:

All subprograms invoked by a BASIC program must have unique names. A BASIC program cannot have different subprograms with the same identifiers.

Subprograms can return a value to the calling program with parameters. You can use subprograms to separate routines that you commonly use. For example, you can use subprograms to perform file I/O operations, to sort data, or for table lookups.

You can also use subprograms to separate large programs into smaller, more manageable routines, or you can separate modules that are modified often. If all references to system-specific features are isolated, it is easier to transport the program to a different system. OpenVMS System Services and OpenVMS Run-Time Library routines are specific to OpenVMS systems; therefore, you should consider isolating references to them in subprograms. Chapter 20 describes how to access Run-Time Library routines and system services from BASIC.

You should also consider isolating complex processing algorithms that are used commonly. If complex processing routines are isolated, they can be shared by many programs while the complexity remains hidden from the main program logic. However, they can share data only if the following is true:

All DATA statements are local to a subprogram. Each time you call a subprogram, BASIC positions the data pointer at the beginning of the subprogram's data.

The data pointer in the main program is not affected by READ or RESTORE statements in the subprogram (in contrast with the RESTORE # statement, which resets record pointers to the first record in the file no matter where it is executed). Chapter 6 contains more information about the READ and RESTORE statements. For more information about the RESTORE # statement, see Chapter 14.

13.1.1 SUB Subprograms

A SUB subprogram is a program module that can be separately compiled and that cannot return a value. A SUB subprogram is delimited by the SUB and END SUB statements. You may use the EXTERNAL statement to explicitly declare the SUB subprogram.

The END SUB statement does the following:

The EXIT SUB statement transfers control to the statement lexically following the statement that invoked the subprogram. It is equivalent to an unconditional branch to an END SUB statement.

The following SUB subprogram sorts two integers. If this SUB statement is invoked with actual parameter values that are already in sorted order, the EXIT SUB statement is executed and control returns to the calling program.


SUB sort_out (INTEGER X, INTEGER Y) 
DECLARE INTEGER temp 
  IF X > Y 
   THEN 
      temp = X 
      X = Y 
      Y = temp 
   ELSE 
      EXIT SUB 
   END IF 
END SUB 

13.1.2 FUNCTION Subprograms

A FUNCTION subprogram is a program module that returns a value and can be separately compiled. It must be delimited by the FUNCTION and END FUNCTION statements. You use the EXTERNAL statement to name and explicitly declare the data type of an external function.

The END FUNCTION statement does the following:

The EXIT FUNCTION statement immediately returns program control to the statement that invoked the function and optionally returns the function's return value. It is equivalent to an unconditional transfer to the END FUNCTION statement.

You can specify an expression with both the END FUNCTION and EXIT FUNCTION statements, which is another way of returning a function value. This expression must match the function data type, and it supersedes any function assignment. For more information, see the Compaq BASIC for OpenVMS Alpha and VAX Systems Reference Manual.

The following function returns the volume of a sphere of radius R. If this function is invoked with an actual parameter value less than or equal to zero, the function returns zero.


FUNCTION REAL Sphere_volume (REAL R) 
     IF R <= 0 
       THEN 
           Sphere_volume = 0.0 
       ELSE 
           Sphere_volume = 4/3 * PI * R ** 3 
      END IF 
END FUNCTION 

The following example declares the FUNCTION subprogram and invokes it:


PROGRAM call_sphere 
  EXTERNAL REAL FUNCTION SPHERE_VOLUME(REAL) 
  PRINT  SPHERE_VOLUME(5.925) 
END PROGRAM 

Note that this module is compiled separately from the FUNCTION subprogram. You can link these modules together to run the program from DCL level. To run the program in the VAX BASIC Environment, follow these steps:

  1. Compile the function subprogram.
  2. Load the resulting object module with the LOAD command.
  3. Read in the main program with the OLD command.
  4. Enter the RUN command.

See Chapter 2 for more information about the LOAD command and linking and running multiple-unit programs.

13.2 Declaring Subprograms and Parameters

You declare a subprogram by naming it in an EXTERNAL statement in the calling program. You may also declare the data type of each parameter. If the subprogram is a function, the EXTERNAL statement also lets you specify the data type of the returned value.

The following statements are subprogram declarations using the EXTERNAL statement:


EXTERNAL SUB my_sub (LONG, STRING) 
EXTERNAL GFLOAT FUNCTION my_func (GFLOAT, LONG, GFLOAT) 
EXTERNAL REAL FUNCTION determinant (LONG DIM(,)) 

Note that the parameter lists contain only data type and dimension information; they cannot contain any format or actual parameters. When the external procedure is invoked, BASIC ensures that the actual parameter data type matches the data type specified in the EXTERNAL declaration. However, BASIC does not check to make sure that the parameters declared in the EXTERNAL statement match those in the external routine. You must ensure that these parameters match.

You can pass data of any BASIC data type to a BASIC subprogram, including RFAs and RECORDs. BASIC allows you to pass up to 255 parameters, separated by commas. The data can be any one of the following:

For passing constants, variables, functions, and array elements, name them in the argument list. For example:


CALL SUB01(var1, var2) 
 
CALL SUB02(Po_num%, Vouch, 66.67, Cust_list(5), FNA(B%)) 

However, when passing an entire array, you must use a special format. You specify the array name followed by commas enclosed in parentheses. The number of commas must be the number of array dimensions minus one. For example, array_name() is a one-dimensional array, array_name(,) is a two-dimensional array, array_name(,,) is a three-dimensional array, and so on.

The following example creates a three-dimensional array, loads the array with values, and passes the array to a subprogram as a parameter. The subprogram can access and change values in array elements, and these changes remain in effect when control returns to the main program.


PROGRAM fill_array 
OPTION TYPE = EXPLICIT 
DECLARE LONG I,J,K, three_d(10,10,10) 
EXTERNAL SUB example_sub (LONG DIM(,,)) 
FOR I = 0 TO 10 
    FOR J = 0 TO 10 
        FOR K = 0 TO 10 
            three_d(I,J,K) = I + J + K 
        NEXT K 
    NEXT J 
NEXT I 
 
CALL  example_sub( three_d(,,)) 
END PROGRAM 
 
SUB example_sub( LONG X( , , )) 
   .
   .
   .
END SUB 

If you do not specify data types for parameters, the default data type is determined by:

The last specified parameter data type overrides all the other default data types, the defaults specified in the OPTION statement override any compilation qualifiers and system defaults, and so on. See Chapter 2 for more information about the OPTION statement and establishing data-type defaults.

When you know the length of a string or the dimensions of an array at compile time, you can achieve optimum performance by passing them BY REF. When you call programs written in other languages, the practice of declaring subprograms and specifying the data types of parameters becomes more important because other languages might not use the BASIC default parameter-passing mechanisms. For more information about calling subprograms written in other languages, see Chapter 20.

13.3 Compiling Subprograms

A BASIC source file can contain multiple program units. When you compile such a file, BASIC produces a single object file containing the code from all the program units. You can then link this object file to create an executable image.

If the main program and subprograms are in separate source files, you can compile them separately from the DCL level. The following command causes BASIC to create MAIN.OBJ, SUB1.OBJ, and SUB2.OBJ by separating the file names with commas:


$ BASIC main,sub1,sub2

To link these programs, you must specify all object files as input to the OpenVMS Linker.

Alternatively, you can compile multiple modules into a single object file at the DCL command level by separating the file names with a plus sign (+) as follows:


$ BASIC main+sub1+sub2

The plus signs used to separate the file names instruct BASIC to create a single object file called MAIN.OBJ from the three source modules. To link this program, you specify only one input file to the linker. Note that in VAX BASIC, you cannot concatenate source files that do not contain line numbers.

In the VAX BASIC Environment, you can compile multiple program units with a resulting single object file by using the APPEND command followed by the COMPILE command. For more information about the APPEND and COMPILE commands, see Chapter 2.

When creating a multiple-unit program, follow these rules:

Note that in a multiple-unit program that contains line numbers, any comments or statements following an END, END SUB, or END FUNCTION statement become part of the preceding subprogram unless they begin on a numbered line. In a multiple-unit program that does not contain line numbers, however, any comments following an END, END SUB, or END FUNCTION statement become part of the following subprogram if one exists.

In the following example, the function Strip changes all brackets to parentheses in the string A$ or alpha, and strips all trailing spaces and tabs:


PROGRAM scan 
  EXTERNAL STRING FUNCTION Strip (STRING) 
  A$ = "USER$DISK:[BASIC.TRYOUTS]"
  B$ = Strip( A$ ) 
  PRINT B$ 
END PROGRAM 
 
FUNCTION STRING Strip( STRING alpha ) 
IF (POS( alpha, "[", 1%)) > 0% 
   THEN Strip = EDIT$(alpha, 128% +64%) 
   ELSE Strip = EDIT$(alpha, 128%) 
END IF 
END FUNCTION 


Previous Next Contents Index