tips:revmedia:r65

R/BASIC Coding Standards

Published ByDateVersionKnowledge LevelKeywords
Revelation Technologies10 OCT 19902.XINTERMEDIATEEQUATE, COMMON, LABELLED, VARIABLE, CONVENTIONS

These are the coding standards used by the programmers at Revelation Technologies Inc. They are provided here as guidelines for your own standards, or you can adopt these as your own.

There are four goals for the standards. By following the standards, your programs will be:

  • Easier to read.
  • Easier to maintain.
  • More efficient.
  • Easier to debug.

By adhering to these naming conventions, you will avoid accidentally reusing variables.

Variable Names

Variable names should be meaningful.

EQUATE

Suffix all EQUATE variables with a "$".

  EQU TRUE$  TO 1
  EQU FALSE$ TO 0

COMMON

Suffix all COMMON variables with a "%".

  COMMON NAME%, INVAL%

LABELLED COMMON

Suffix all LABELLED COMMON variables with an "@".

  COMMON /POINTERS/ F_NAME@, HANDLE@

Variable Delimiter

In order to maintain compatibility with other languages and SQL, use "_" (underscore) to delimit the words in a variable.

  EMPLOYEE_NO

OPEN

When opening a file to a file variable, suffix the file variable with "_FILE" and use the name of the real file.

OPEN "CUST" TO CUST_FILE ELSE
  MSG(NOT_ATTACHED$, "", "", "CUST")
  DONE = TRUE$
END

If the name of the file is not known ahead of time, the file should be opened to a variable name that describes the file's role. The file variable should be prefixed with "FILE_"

OPEN IN_FILE TO SOURCE_FILE ELSE
  MSG(NOT_ATTACHED$, "", "", IN_FILE)
  DONE = TRUE$
END

Advanced Revelation is a highly customizable product. In order to make it possible for users to customize the system messages, the following guidelines apply to calling MSG.

Note: Revalation Technologies programmers store their messages in the SYS.MESSAGES file. Other programmers may use either SYS.MESSAGES or MESSAGES. Messages stored in SYS.MESSAGES are subject to being overwritten by future upgrades, messages in MESSAGES are not.

Equate message numbers

Message numbers should be assigned equate values in the MESSAGES section of the program header. Be sure to indicate, in a comment, what the message is for.

DO:

EQU NOT_ATTACHED$ TO 201 ;* File not attached.
(...)
MSG(NOT_ATTACHED$, "", "", FILE_NAME)

NOT:

MSG("201", "", "", FILE_NAME)

UB or U (Message Up) messages

Messages of type UB should initialize the IMAGE variable before calling MSG. This will allow the user to turn off the message without getting a "Variable Not Assigned a Value" error when the DB messsage call is made.

DO:

IMAGE = ""
MSG(PROCESSING$, "", IMAGE, "")

NOT:

MSG(PROCESSING$, "", IMAGE, "")

DB or D (Message Down) messages

Programs should test for the IMAGE variable before executing a DB message. If IMAGE is null, then the UB message was never executed, and the program can avoid an unnecessary subroutine call.

DO:

IF IMAGE THEN
  MSG("", "DB", IMAGE, "")
END

NOT:

MSG("", "DB", IMAGE, "")

Reply messages

Unless the program requires a lowercase response, all reply messages should be of type RC.

All reply messages should initialize the reply variable to provide a default response for the user.

Starting with Advanced Revelation version 2.1, the variable should be initialized with the internal representation of the data. MSG will be responsible for converting it to the appropriate text.

DO:

REPLY = 1 ;* Boolean conversion.
MSG(DELETE$, "", REPLY, "")

NOT:

MSG(DELETE$, "", REPLY, "")

Structured Coding

Use a structured code approach. Modularize routines and have a simple, concise mainline whenever possible. Avoid GOTOs, because they make the flow of the program difficult to follow.

Under most circumstances, there should be only one exit point from a program, loop, or subroutine. If there are multiple exit points, be sure to document them well.

Standard Top

All programs should use a standard top. Figure 1 provides an example. All parts of the top should be completely filled out. To aid readability, descriptions should be in upper and lower case.

Documentation

Document your code well! At complex parts of your logic, reference a section of your header documentation and describe it there.

Comments should be in upper and lower case.

Comment lines should be indented as if they were normal program lines. In cases where the comment applies to one line of code and the comment is short, put the comment at the end of the line.

Use C-style comment delimiters for multi-line comments. It makes the comments easier to read and modify.

Comment branches in the logic immediately after the branch.

(...)
ALPHA = "100" ;* Initialize Alpha
IF ALPHA > ZETA THEN
  /* This is the normal condition. Process accordingly. */
  (...)
END ELSE
  /* Error, post message. */
  MSG(ERR_ZETA$, "", "", "")
END
(...)

Subroutine and Function Declaration

Declare all SUBROUTINES and FUNCTIONS at the appropriate section in the header. Do not use the format "CALL program" unless the program is being called indirectly, such as "CALL @PROG.VAR".

DO:

NET_PRESENT_VALUE(A)

NOT:

CALL NET_PRESENT_VALUE(A)

Internal Subroutines

In order to make program listings more readable, the following guidelines apply to internal subroutines.

Use descriptive, alphabetic labels. Do not use numeric labels.

All labels must follow all mainline code.

Subroutines should be arranged alphabetically by label. This is especially important for large programs with many subroutines.

The subroutine section should be set off from the mainline code with a header:

/*SUBROUTINE SECTION*/

Each subroutine should have its own header and a brief description of its purpose.

/*SORT - This subroutine sortsthe array built by the user.*/SORT:

Branching to internal subroutines

R/BASIC requires a colon on subroutine labels. When using internal labels, do not follow the statement initiating the branch with a colon. This allows for quick searches to find the label, not the branch.

DO:

GOSUB COMPUTE_IT

NOT:

GOSUB COMPUTE_IT:

$INSERTS

Never nest $INSERTS. Normally, use $INSERT to insert blocks of code common to 2 or more programs. It should only be used to insert specific program code when source code limits are being hit.

Indentation

Each logical level in a program should be indented 3 spaces.

WHILE and UNTIL keywords should be aligned with the LOOP and REPEAT keywords. This makes it easier to see the exit conditions for the loop. See Figure 2.

CASE statements should be aligned under the word CASE in the BEGIN CASE statement. See Figure 3.

Use of spaces

Proper use of spaces will make it easier to read programs and to navigate through them. Commas in comma-delimited argument lists should be followed by spaces. Operators and operands should be separated by spaces.

DO:

OFFSET = START_POS + FRAME_SIZE
KEY_LIST = DELETE(KEY_LIST, 1, 0, 0)

NOT:

OFFSET=START_POS+FRAMESIZE
KEY_LIST=DELETE(KEY_LIST,1,0,0)

Revisions

All source code must be checked out before modifications can be made.

All revisions that are made to a program should be reflected in the appropriate parts of the header. This includes updating the THEORY OF OPERATION and PROCEDURES, if necessary.

Your initials and the date should be placed above and below the altered lines as well as in the REVISION section of the heading. Describe the change in comments.

MSG(BAD_DATA$, '', '', '')
/*--> 6/1/88 - JAH - should do a RETURN instead of STOP. report #3344 */
* STOP
RETURN
*--> 6/1/88 - JAH

IF...THEN...ELSE statement

IF…THEN…ELSE statements should be broken into multiple lines, rather than run together on a single line.

DO:

IF COUNTER > MAX_LINES THEN
  GOSUB ERROR
END ELSE
  GOSUB EDIT
END

NOT:

IF COUNTER > MAX_LINES THEN GOSUB ERROR ELSE GOSUB EDIT

CLEAR statement

Do not use the CLEAR statement to initialize variables. Be explicit when initializing each variable.

STOP

When terminating a program in an error condition, do not just issue a STOP. Display a message by calling "MSG" and put the message in the SYS.MESSAGES file. (See "Messages" above.)

DO:

READ INVOICE_REC FROM INVOICE_FILE ELSE
  MSG(NO_REC$, "", "", "INVOICE")
  DONE = TRUE$
END

NOT:

READ INVOICE_REC FROM INVOICE_FILE ELSE
  PRINT "Invoice record missing"
  STOP
END

Do not do a STOP, ABORT, or ABORT ALL from within a subroutine. This will cancel one or more main programs. Instead, set an error flag or post an error message and RETURN to the calling program. Let the calling program deal with the problem.

READNEXT

To locate the end of a select list in a READNEXT loop, do not test the value of the key variable. Use a "DONE" variable instead. You can never be too sure what kind of key will be coming through a select list.

DO:

DONE = FALSE$
LOOP
  READNEXT KEY ELSE
    DONE = TRUE$
  END
UNTIL DONE
  (...)
REPEAT

NOT:

LOOP
  READNEXT KEY ELSE
    KEY = "DONE"
  END
UNTIL KEY = "DONE"
  (...)
REPEAT

R/BASIC SELECT

When doing an R/BASIC SELECT statement, make sure that it is surrounded with a PUSH.SAVE and a POP.SELECT. This avoids inadvertently using outside select lists.

(...)
PUSH.SELECT(CURSOR, SAVE1, SAVE2, SAVE3)
SELECT SOURCE_FILE
(...)
POP.SELECT(CURSOR, SAVE1, SAVE2, SAVE3)
RETURN

@VARIABLES

Use system @variables whenever possible, making sure to guard against clobbering another process when using @DICT, @ID, and @RECORD. For example, use @FM, @VM, @TM, @SVM, @UPPER.CASE, @LOWER.CASE, etc.

Semicolons

Do not use semicolons to put multiple statements on a line (except comments). Keep lines very simple.

DO:

VAL = 0  ; * Initialize value
INCR = 1 ; * Initialize increment

NOT:

* Initialize variables
VAL = 0 ; INCR = 1

Multipart Keys

When constructing multipart record keys, use an "*" (asterisk) as the delimiter.

CASE statements

If you use a CASE statement, it should be easy to read. If the code inside a particular case branch is long, move it to an internal subroutine and call it via a GOSUB.

DO:

BEGIN CASE
  CASE CODE = "ALPHA"
    GOSUB ALPHA_CONDITION
  CASE CODE = "BETA"
    GOSUB BETA_CONDITION
END CASE

NOT:

BEGIN CASE
  CASE CODE = "ALPHA"
    (a page of code)
  CASE CODE = "BETA"
    (half a page of code)
END CASE

If the CASE statement is testing for a large number of conditions (as in a commuter module) use a LOCATE BY … ON…GOSUB format.

DO:

SUBLIST = "PRE_READ,POST_DEL,OPTIONS"
LOCATE BRANCH IN SUBLIST USING ","SETTING POS THEN
  ON POS GOSUB PRE_READ, POST_DEL, OPT
END ELSE
  MSG(ILLEGAL_BRANCH$, "", "", BRANCH)
END

NOT:

BEGIN CASE
  CASE BRANCH = "PRE_READ"
    GOSUB PRE_READ
  CASE BRANCH = "POST_DEL"
    GOSUB POST_DEL
  CASE BRANCH = "OPTIONS"
    GOSUB OPT
  CASE OTHERWISE$
    MSG(ILLEGAL_BRANCH$, "", "",ï BRANCH)
END CASE

Use CASE statements in place of IF…THEN statements whenever program logic calls for vertical cascading conditions. The resulting code will be easier to read.

DO:

BEGIN CASE
  CASE STATUS = "A"
    A += 1
  CASE STATUS = "B"
    C -= 3
  CASE OTHERWISE$
    D = 3
END CASE

NOT:

IF STATUS = "A" THEN
  A += 1
END ELSE
  IF STATUS = "B" THEN
    C -= 3
  END ELSE
    D = 3
  END
END

Delay loops

Delay loops should use the system subroutine DELAY. This allows for differences between CPU speeds and operating systems.

DO:

DECLARE SUBROUTINE DELAY
DELAY(5)

NOT:

FOR DELAY = 1 TO 1000
  LINEMARK
NEXT DELAY

Line length

If possible, keep lines shorter than 80 characters.

Assignment

Use the UNASSIGNED function to check variables that might not have been assigned. This is especially important in subroutines.

DO:

IF UNASSIGNED(VAR) THEN
  VAR = NULL$ ;* Or default value.
END

While programs that are easy to read and easy to maintain are a worthy goal, programs that execute quickly are also desirable. Following these guidelines will make programs more efficient.

EQUATE

Always equate constants instead of assigning them to variables. It will produce less object code. The reverse is usually true if you equate to a variable or expression.

Do not equate a variable to a simple function, because the function must be resolved at each reference.

DO:

CLS = @(-1) : "PGM001"

NOT:

EQU CLS$ TO @(-1) : "PGM001"

Special Operators

Use the special operators (+=, -=, := ) whenever possible. This is especially true for :=, as it does not require a separate copy of the item to be loaded on to the stack. This is quite important when dealing with large items.

DO:

STRING := "More stuff"

NOT:

STRING = STRING : "More stuff"

NOT function

If possible, avoid using the NOT function in an IF…THEN construction. Use IF…ELSE instead.

DO:

IF GRAND_TOTAL > 100 ELSE

NOT:

IF NOT(GRAND_TOTAL > 100) THEN

LOCATE...BY

Use LOCATE…BY only when necessary, because it is much slower than LOCATE.

As a rule, it is faster to sort an array with V119 than with LOCATE…BY. LOCATE…BY will be slightly faster when the array you are building must contain unique values. If in doubt, use V119.

EXTRACT and REPLACE

Use the dynamic form of the extract instead of the function.

DO:

CUST_REC<3> = 4
STATUS = CUST_REC<3>

NOT:

STATUS = EXTRACT(CUST_REC,3,0,0)
CUST_REC = REPLACE(CUST_REC,3,0,0,4)

TRANSFER

If you are assigning the value of one variable to another variable and then nulling the first variable, use the TRANSFER statement.

This is a very fast operation, in which only descriptors are changed, not string space.

DO:

TRANSFER A TO B ;* ("A" becomes null)

NOT:

B = A
A = ""

CASE vs. IF...THEN

When compiled, CASE statements become nested IF…THEN statements. Therefore, test for the most likely conditions first.

Existence

Check for the existence of a variable by checking for its length.

DO:

IF LEN(KEY) THEN

NOT:

IF KEY THEN

NOT:

IF KEY <> NULL$ THEN

Figure 1

/***************************
PROGRAM NAME

This program is proprietary and is not to be used by or disclosed to others,
nor is it to be copied without written permission from REVELATION
TECHNOLOGIES, INC.

÷ VERSION : (Version of the program)

÷ PURPOSE : (Short description of why this program exists)

÷ AUTHOR : (Name of the original programmer)

÷ CREATED : MM-DD-YY

÷ PROCEDURES : (How to call the program, setup requirements etc.)

÷ WARNINGS : (What might happen if you're not careful. Include any error
              conditions that you know about but that are not being checked.

÷ THEORY OF OPERATION : (Description of how the program is constructed.
Include descriptions of the algorithm(s) used. If necessary, refer to
another record containing more information.

÷ REVISION HISTORY (Most CURRENT first) :

DATE IMPLEMENTOR FUNCTION
-------- ----------- --------
MM-DD-YY initials Modification

***************************/

*÷ COMMON Variables (Terminate with '%') :

*÷ LABELED COMMON Variables (Terminate with '@') :

*÷ EQUATE Variables (Terminate with '$') :

EQU RTI$       TO "Copyright (C) 1990, REVELATION TECHNOLOGIES, INC."
EQU TRUE$      TO 1
EQU FALSE$     TO 0
EQU YES$       TO 1
EQU NO$        TO 0
EQU OTHERWISE$ TO 1
EQU NULL$      TO ""
EQU SPACE$     TO " "

*÷ MESSAGES called (Terminate with '$') :

*÷ DECLARED - FUNCTIONS called :

*÷ DECLARED - SUBROUTINES called :
/**************************
÷ INDIRECT - FUNCTIONS/SUBROUTINES called if known (Make COMMENTS) :
**************************/
*÷ PROGRAM TOP

Figure 2

DONE      = FALSE$
NO_REC    = FALSE$
RECS_DONE = 0
LOOP
  READNEXT KEY ELSE
    DONE = TRUE$
  END
UNTIL DONE
  READ @RECORD FROM CUSTOMER_FILE, KEY THEN
    GOSUB PROCESS_RECORD
    RECS_DONE += 1
  END ELSE
    NO_REC = TRUE$
    GOSUB REC_ERROR
  END
REPEAT

Figure 3.

BEGIN CASE
  CASE CHARACTER MATCHES '0A'
    GOSUB ALPHA_INPUT
  CASE CHARACTER MATCHES '0N'
    GOSUB NUMERIC_INPUT
  CASE OTHERWISE$
    GOSUB INVALID_INPUT
END CASE
  • tips/revmedia/r65.txt
  • Last modified: 2024/06/19 20:20
  • by 127.0.0.1