Every meaningful business application makes decisions. Should this loan be approved? Is this transaction fraudulent? Does this employee qualify for a raise? At the heart of every such question lies conditional logic -- the ability of a program to...
In This Chapter
- Introduction: Where Business Rules Come to Life
- 7.1 The IF Statement
- 7.2 Relation Conditions
- 7.3 Sign Conditions
- 7.4 Class Conditions
- 7.5 Condition Names (88-Level Items)
- 7.6 The EVALUATE Statement
- 7.7 CONTINUE and NEXT SENTENCE
- 7.8 De Morgan's Laws in COBOL
- 7.9 Short-Circuit Evaluation: A COBOL Difference
- 7.10 Performance: EVALUATE vs. Nested IF
- 7.11 Common Patterns
- 7.12 Common Mistakes and How to Avoid Them
- 7.13 Fixed-Format vs. Free-Format Examples
- 7.14 Putting It All Together: A Complete Example
- Summary
Chapter 7: Conditional Logic -- IF, EVALUATE, and Condition Names
Introduction: Where Business Rules Come to Life
Every meaningful business application makes decisions. Should this loan be approved? Is this transaction fraudulent? Does this employee qualify for a raise? At the heart of every such question lies conditional logic -- the ability of a program to examine data and choose a course of action based on what it finds.
COBOL was designed from its earliest days to express business rules clearly, and its conditional logic facilities reflect that mission. Where other languages offer terse symbols and compact syntax, COBOL provides readable, English-like constructs that allow a business analyst to look at the code and understand the rule being implemented. This is not an accident; it is the fundamental design philosophy that has kept COBOL relevant for over six decades.
In this chapter, you will master three primary mechanisms for conditional logic in COBOL:
- The IF statement -- the workhorse of decision-making, supporting simple, nested, and compound conditions
- The EVALUATE statement -- COBOL's powerful multi-way branching construct, far more capable than the SWITCH/CASE of other languages
- Condition names (88-levels) -- a uniquely COBOL feature that transforms cryptic codes and magic numbers into self-documenting, readable conditions
By the end of this chapter, you will be able to implement complex business rule engines, validation systems, and decision trees using idiomatic COBOL that is both correct and maintainable.
7.1 The IF Statement
The IF statement is the most fundamental decision-making construct in COBOL. It evaluates a condition and executes one set of statements if the condition is true, and optionally a different set if the condition is false.
7.1.1 Basic Syntax
The general form of the IF statement is:
IF condition
statement-1
statement-2
ELSE
statement-3
statement-4
END-IF
The ELSE clause is optional. When omitted, the IF statement simply executes the statements when the condition is true and skips them otherwise:
IF WS-ACCOUNT-BALANCE < ZERO
DISPLAY "Account is overdrawn!"
END-IF
When the ELSE clause is present, the program takes one path or the other -- never both, never neither:
IF WS-EMPLOYEE-AGE >= 65
DISPLAY "Eligible for retirement benefits."
PERFORM CALCULATE-PENSION
ELSE
DISPLAY "Not yet eligible for retirement."
PERFORM CALCULATE-CURRENT-BENEFITS
END-IF
Multiple statements can appear in both the IF and ELSE branches. There is no need for braces, brackets, or BEGIN/END blocks -- COBOL uses the scope terminator END-IF to mark the end of the entire construct.
7.1.2 The Importance of END-IF (Scope Terminators)
Prior to the COBOL-85 standard, the IF statement was terminated by a period. This led to one of the most infamous sources of bugs in COBOL programming history. Consider this pre-85 style code:
* PRE-COBOL-85 STYLE (DO NOT USE)
IF WS-AMOUNT > 1000
PERFORM PROCESS-LARGE-ORDER.
PERFORM LOG-TRANSACTION.
The period after PROCESS-LARGE-ORDER terminates the IF statement. This means LOG-TRANSACTION is performed unconditionally -- a subtle and dangerous bug. The programmer likely intended both PERFORM statements to be conditional.
The END-IF scope terminator, introduced in COBOL-85, eliminates this entire class of bugs:
* MODERN COBOL STYLE (ALWAYS USE THIS)
IF WS-AMOUNT > 1000
PERFORM PROCESS-LARGE-ORDER
PERFORM LOG-TRANSACTION
END-IF
Best practice: Always use END-IF. Never rely on periods to terminate IF statements. The only period in the PROCEDURE DIVISION should be the one at the end of each paragraph or section name and at the end of the last statement in each paragraph.
7.1.3 Nested IF Statements
IF statements can be nested inside other IF statements to handle multi-level decision logic:
IF WS-CUSTOMER-TYPE = "P"
IF WS-ORDER-AMOUNT > 5000
MOVE 15.00 TO WS-DISCOUNT-PCT
ELSE
IF WS-ORDER-AMOUNT > 1000
MOVE 10.00 TO WS-DISCOUNT-PCT
ELSE
MOVE 5.00 TO WS-DISCOUNT-PCT
END-IF
END-IF
ELSE
IF WS-ORDER-AMOUNT > 5000
MOVE 8.00 TO WS-DISCOUNT-PCT
ELSE
MOVE 0.00 TO WS-DISCOUNT-PCT
END-IF
END-IF
While nesting is sometimes necessary, deeply nested IF statements become difficult to read and maintain. A good rule of thumb is to limit nesting to three levels. When you find yourself going deeper, consider refactoring to use the EVALUATE statement (covered in Section 7.3) or breaking the logic into separate paragraphs.
Readability guidelines for nested IFs:
- Indent each level consistently (4 spaces is standard)
- Ensure each IF has a matching END-IF at the same indentation level
- Add comments at the END-IF to indicate which IF it closes when nesting is deep
- Consider whether EVALUATE would express the logic more clearly
7.1.4 Compound Conditions: AND, OR, NOT
COBOL supports compound conditions using the logical operators AND, OR, and NOT. These allow you to combine multiple simple conditions into a single complex condition.
AND (Conjunction)
AND requires all conditions to be true for the compound condition to be true:
IF WS-EMPLOYEE-AGE > 30
AND WS-YEARS-SERVICE >= 10
AND WS-PERFORMANCE-RATING = "A"
DISPLAY "Employee qualifies for promotion."
END-IF
All three conditions must be true. If any one is false, the entire compound condition is false.
OR (Disjunction)
OR requires at least one condition to be true:
IF WS-DEPARTMENT = "FINANCE"
OR WS-DEPARTMENT = "ACCOUNTING"
OR WS-DEPARTMENT = "AUDIT"
DISPLAY "Employee is in a financial department."
END-IF
If any one of the three comparisons is true, the compound condition is true.
NOT (Negation)
NOT reverses the truth value of a condition:
IF NOT WS-ACCOUNT-STATUS = "CLOSED"
PERFORM PROCESS-TRANSACTION
END-IF
NOT can also be applied to compound conditions using parentheses:
IF NOT (WS-TEMPERATURE < 32 OR WS-WIND-SPEED > 50)
DISPLAY "Weather conditions are acceptable."
END-IF
Operator Precedence
When AND and OR appear in the same condition without parentheses, AND has higher precedence than OR. This means:
A OR B AND C
is evaluated as:
A OR (B AND C)
This is the same precedence rule used in most programming languages and in Boolean algebra. However, always use parentheses when mixing AND and OR to make your intent explicit:
* UNCLEAR - relies on precedence
IF WS-TYPE = "A" OR WS-TYPE = "B" AND WS-AMT > 1000
PERFORM PROCESS-SPECIAL
END-IF
* CLEAR - parentheses make intent obvious
IF (WS-TYPE = "A" OR WS-TYPE = "B")
AND WS-AMT > 1000
PERFORM PROCESS-SPECIAL
END-IF
7.1.5 Implied Subjects and Implied Operators
COBOL supports two shorthand notations that can make compound conditions more concise, but can also introduce confusion if not understood properly.
Implied Subject
When comparing the same variable against multiple values with OR, you can omit the repeated subject:
* Full form
IF WS-STATE = "NY"
OR WS-STATE = "NJ"
OR WS-STATE = "CT"
* Implied subject form (equivalent)
IF WS-STATE = "NY" OR "NJ" OR "CT"
Both forms are identical in meaning. The implied subject is the last explicitly named subject -- in this case, WS-STATE.
Implied Operator
Similarly, the relational operator can be implied:
* Full form
IF WS-TEMP > WS-HUMIDITY
AND WS-TEMP > WS-WIND-SPEED
* Implied operator form (equivalent)
IF WS-TEMP > WS-HUMIDITY AND WS-WIND-SPEED
The implied operator is the last explicitly stated operator (here, >), and the implied subject is the last explicitly stated subject (here, WS-TEMP).
A Common Trap
Consider this condition:
IF WS-CODE = "A" OR "B"
A programmer might read this as "if WS-CODE equals A, or if B is true." But that is not what it means. Because of the implied subject rule, it means:
IF WS-CODE = "A" OR WS-CODE = "B"
Best practice: While implied subjects and operators are valid COBOL, many shops discourage their use because they introduce ambiguity. Write conditions explicitly for maximum clarity, especially in code that will be maintained by teams.
7.2 Relation Conditions
Relation conditions compare two operands using relational operators. COBOL provides both symbolic and word-form operators, and they are completely interchangeable.
7.2.1 Symbol Form
| Symbol | Meaning |
|---|---|
= |
Equal to |
> |
Greater than |
< |
Less than |
>= |
Greater than or equal to |
<= |
Less than or equal to |
NOT = |
Not equal to |
7.2.2 Word Form
| Word Form | Equivalent Symbol |
|---|---|
IS EQUAL TO |
= |
IS GREATER THAN |
> |
IS LESS THAN |
< |
IS GREATER THAN OR EQUAL TO |
>= |
IS LESS THAN OR EQUAL TO |
<= |
IS NOT EQUAL TO |
NOT = |
IS NOT GREATER THAN |
<= |
IS NOT LESS THAN |
>= |
The word IS and the word THAN are optional noise words. All of the following are equivalent:
IF WS-AMOUNT > 1000
IF WS-AMOUNT IS GREATER THAN 1000
IF WS-AMOUNT GREATER THAN 1000
IF WS-AMOUNT GREATER 1000
Use whichever form reads most naturally in context. Many experienced COBOL programmers prefer the symbolic form for numeric comparisons and the word form when it improves readability.
7.2.3 Comparing Different Data Types
When COBOL compares two operands, the rules depend on the types involved:
- Numeric to numeric: Algebraic comparison.
PIC S9(3) VALUE -5is less thanPIC 9(3) VALUE 3. - Alphanumeric to alphanumeric: Character-by-character comparison using the collating sequence (typically ASCII or EBCDIC). Shorter fields are padded with spaces on the right.
- Numeric to alphanumeric: The numeric operand is converted to its display form, then compared as alphanumeric. This can produce unexpected results -- avoid mixing types in comparisons.
7.3 Sign Conditions
Sign conditions test whether a numeric value is positive, negative, or zero:
IF WS-BALANCE IS POSITIVE
DISPLAY "Account has funds."
END-IF
IF WS-PROFIT-LOSS IS NEGATIVE
DISPLAY "Operating at a loss."
END-IF
IF WS-NET-CHANGE IS ZERO
DISPLAY "No change this period."
END-IF
The relationships are straightforward:
| Sign Condition | Equivalent Relation |
|---|---|
IS POSITIVE |
> 0 |
IS NEGATIVE |
< 0 |
IS ZERO |
= 0 |
IS NOT POSITIVE |
<= 0 |
IS NOT NEGATIVE |
>= 0 |
IS NOT ZERO |
NOT = 0 |
Sign conditions can only be used with numeric operands. The word IS is optional.
7.4 Class Conditions
Class conditions test the nature of the data stored in a field, rather than its value. They are essential for input validation.
7.4.1 Built-in Class Conditions
| Class Test | True When |
|---|---|
NUMERIC |
All characters are digits (0-9). For signed numeric fields, also allows the sign. |
ALPHABETIC |
All characters are letters (A-Z, a-z) or spaces. |
ALPHABETIC-LOWER |
All characters are lowercase letters (a-z) or spaces. |
ALPHABETIC-UPPER |
All characters are uppercase letters (A-Z) or spaces. |
IF WS-INPUT-FIELD IS NUMERIC
COMPUTE WS-RESULT = FUNCTION NUMVAL(WS-INPUT-FIELD)
ELSE
DISPLAY "Error: non-numeric input"
END-IF
Important notes about class conditions:
- The NUMERIC test on a
PIC Xfield checks that every character is a digit (0-9). Spaces, decimal points, signs, and commas are NOT numeric in a PIC X field. - Spaces pass the ALPHABETIC, ALPHABETIC-LOWER, and ALPHABETIC-UPPER tests.
- Class conditions apply to the entire field. You cannot test a substring directly (use reference modification to extract a portion first).
7.4.2 User-Defined Class Conditions
You can define custom class tests in the SPECIAL-NAMES paragraph of the ENVIRONMENT DIVISION:
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
CLASS VALID-PHONE IS "0" THRU "9" "-" "(" ")" " ".
CLASS HEX-CHAR IS "0" THRU "9" "A" THRU "F"
"a" THRU "f".
These custom classes can then be used just like the built-in ones:
IF WS-PHONE-NUMBER IS VALID-PHONE
DISPLAY "Phone format is acceptable."
ELSE
DISPLAY "Invalid characters in phone number."
END-IF
User-defined classes are powerful tools for input validation. They allow you to define exactly which characters are acceptable for a given field, and the resulting condition reads like a business requirement.
7.5 Condition Names (88-Level Items)
Condition names are one of COBOL's most distinctive and powerful features. They attach meaningful names to specific values or ranges of values that a data item can hold, transforming cryptic codes into self-documenting conditions.
7.5.1 Defining Condition Names
Condition names are defined at level 88, immediately following the data item they describe:
01 WS-ACCOUNT-STATUS PIC X(1).
88 ACCOUNT-ACTIVE VALUE "A".
88 ACCOUNT-SUSPENDED VALUE "S".
88 ACCOUNT-CLOSED VALUE "C".
88 ACCOUNT-PENDING VALUE "P".
The 88-level item does not occupy any storage. It is simply a named alias for a particular value of its parent data item.
7.5.2 Using Condition Names in IF Statements
Once defined, a condition name can be used anywhere a condition is expected:
* With condition name (clear and self-documenting)
IF ACCOUNT-ACTIVE
PERFORM PROCESS-TRANSACTION
END-IF
* Without condition name (cryptic)
IF WS-ACCOUNT-STATUS = "A"
PERFORM PROCESS-TRANSACTION
END-IF
Both forms are functionally identical, but the condition name version reads like English and does not require the reader to know that "A" means "active."
7.5.3 Multiple Values
A single condition name can represent multiple values:
01 WS-TRANSACTION-CODE PIC X(2).
88 TRANS-CREDIT VALUE "DP" "TR".
88 TRANS-DEBIT VALUE "WD" "PY".
88 VALID-TRANSACTION VALUE "DP" "WD"
"TR" "PY".
When you test VALID-TRANSACTION, it is true if WS-TRANSACTION-CODE contains any of the four listed values. This is equivalent to writing:
IF WS-TRANSACTION-CODE = "DP" OR "WD" OR "TR" OR "PY"
7.5.4 Ranges with THRU
Condition names can specify ranges of values using the THRU keyword:
01 WS-CREDIT-SCORE PIC 9(3).
88 CREDIT-EXCELLENT VALUE 750 THRU 850.
88 CREDIT-GOOD VALUE 700 THRU 749.
88 CREDIT-FAIR VALUE 650 THRU 699.
88 CREDIT-POOR VALUE 550 THRU 649.
88 CREDIT-VERY-POOR VALUE 300 THRU 549.
The range is inclusive on both ends. CREDIT-EXCELLENT is true for any credit score from 750 through 850.
Ranges and discrete values can be combined:
01 WS-ERROR-CODE PIC 9(4).
88 RECOVERABLE-ERROR VALUE 1000 THRU 3999.
88 FATAL-ERROR VALUE 4000 THRU 5999.
88 SPECIAL-ERRORS VALUE 100 200 300
9000 THRU 9999.
7.5.5 The SET Statement
The SET statement provides a clean way to assign values to condition names:
SET ACCOUNT-ACTIVE TO TRUE
This is equivalent to:
MOVE "A" TO WS-ACCOUNT-STATUS
The SET statement moves the first VALUE associated with the condition name into the parent variable. This means the programmer does not need to know or remember the underlying code -- they simply express their intent.
SET TO FALSE is also supported (when a FALSE value is defined):
01 WS-OVERDUE-FLAG PIC X(1).
88 IS-OVERDUE VALUE "Y"
FALSE IS "N".
88 IS-NOT-OVERDUE VALUE "N"
FALSE IS "Y".
SET IS-OVERDUE TO TRUE *> WS-OVERDUE-FLAG = "Y"
SET IS-OVERDUE TO FALSE *> WS-OVERDUE-FLAG = "N"
Note: SET TO FALSE requires the FALSE IS clause in the 88-level definition. Not all compilers support this extension to the COBOL standard. An alternative pattern is to define complementary condition names, as shown above.
7.5.6 Condition Names as Self-Documenting Code
The real power of condition names is not in saving keystrokes but in creating code that documents itself. Compare these two fragments:
* WITHOUT condition names (what does "S" mean?)
IF WS-EMP-STATUS = "S" AND WS-PAY-GRADE >= 12
IF WS-DEPT-CODE = "FIN" OR "ACC"
COMPUTE WS-BONUS = WS-SALARY * 0.15
END-IF
END-IF
* WITH condition names (instantly understandable)
IF EMPLOYEE-SALARIED AND PAY-GRADE-SENIOR
IF DEPT-FINANCIAL
COMPUTE WS-BONUS = WS-SALARY * 0.15
END-IF
END-IF
When maintaining a system years after it was written, condition names are invaluable. They capture the business meaning of data values at the point of definition, and that meaning flows automatically into every condition that uses them.
7.6 The EVALUATE Statement
The EVALUATE statement is COBOL's multi-way branching construct. Introduced in COBOL-85, it replaced the deeply nested IF chains that plagued earlier COBOL programs. EVALUATE is far more powerful than the SWITCH/CASE statements found in C, Java, or Python -- it supports range matching, multiple subjects, and arbitrary condition testing.
7.6.1 EVALUATE Identifier (Like SWITCH/CASE)
The simplest form of EVALUATE tests a single variable against specific values:
EVALUATE WS-DAY-OF-WEEK
WHEN 1
MOVE "MONDAY" TO WS-DAY-NAME
WHEN 2
MOVE "TUESDAY" TO WS-DAY-NAME
WHEN 3
MOVE "WEDNESDAY" TO WS-DAY-NAME
WHEN 4
MOVE "THURSDAY" TO WS-DAY-NAME
WHEN 5
MOVE "FRIDAY" TO WS-DAY-NAME
WHEN 6
MOVE "SATURDAY" TO WS-DAY-NAME
WHEN 7
MOVE "SUNDAY" TO WS-DAY-NAME
WHEN OTHER
MOVE "INVALID" TO WS-DAY-NAME
END-EVALUATE
Key points:
- WHEN OTHER is the default case, executed when no other WHEN matches. It is optional but strongly recommended.
- There is no fall-through between WHEN clauses. Unlike C's switch statement, only the matching WHEN's statements are executed, and control passes to the statement after END-EVALUATE. No BREAK statement is needed.
- The first matching WHEN is executed. If multiple WHEN clauses could match, only the first one found is used.
7.6.2 Multiple Values in a Single WHEN
You can stack multiple WHEN clauses to share the same block of statements:
EVALUATE WS-MONTH
WHEN 12
WHEN 1
WHEN 2
MOVE "WINTER" TO WS-SEASON
WHEN 3
WHEN 4
WHEN 5
MOVE "SPRING" TO WS-SEASON
WHEN 6
WHEN 7
WHEN 8
MOVE "SUMMER" TO WS-SEASON
WHEN 9
WHEN 10
WHEN 11
MOVE "AUTUMN" TO WS-SEASON
END-EVALUATE
7.6.3 EVALUATE TRUE (Replacing Nested IF)
EVALUATE TRUE is the most commonly used form of EVALUATE in business programming. Instead of testing a variable against values, it tests arbitrary conditions:
EVALUATE TRUE
WHEN WS-SCORE >= 90
MOVE "A" TO WS-GRADE
WHEN WS-SCORE >= 80
MOVE "B" TO WS-GRADE
WHEN WS-SCORE >= 70
MOVE "C" TO WS-GRADE
WHEN WS-SCORE >= 60
MOVE "D" TO WS-GRADE
WHEN OTHER
MOVE "F" TO WS-GRADE
END-EVALUATE
The keyword TRUE means "match the first WHEN clause whose condition evaluates to true." This is equivalent to an IF/ELSE IF chain, but much cleaner:
* EQUIVALENT but harder to read:
IF WS-SCORE >= 90
MOVE "A" TO WS-GRADE
ELSE IF WS-SCORE >= 80
MOVE "B" TO WS-GRADE
ELSE IF WS-SCORE >= 70
MOVE "C" TO WS-GRADE
...
END-IF END-IF END-IF END-IF
EVALUATE TRUE is especially powerful with condition names:
EVALUATE TRUE
WHEN ACCOUNT-ACTIVE
PERFORM PROCESS-ACTIVE-ACCOUNT
WHEN ACCOUNT-SUSPENDED
PERFORM HANDLE-SUSPENDED
WHEN ACCOUNT-CLOSED
PERFORM REJECT-TRANSACTION
WHEN OTHER
PERFORM HANDLE-UNKNOWN-STATUS
END-EVALUATE
7.6.4 EVALUATE with THRU (Range Matching)
The THRU keyword in EVALUATE allows matching ranges of values:
EVALUATE WS-ANNUAL-INCOME
WHEN 0 THRU 11000
MOVE 10.00 TO WS-TAX-RATE
WHEN 11001 THRU 44725
MOVE 12.00 TO WS-TAX-RATE
WHEN 44726 THRU 95375
MOVE 22.00 TO WS-TAX-RATE
WHEN OTHER
MOVE 24.00 TO WS-TAX-RATE
END-EVALUATE
THRU ranges are inclusive on both ends, just as with 88-level condition names.
7.6.5 EVALUATE with ALSO (Multi-Dimensional Conditions)
The ALSO keyword allows EVALUATE to test multiple subjects simultaneously, creating a decision matrix:
EVALUATE WS-MEMBER-LEVEL ALSO TRUE
WHEN "G" ALSO WS-AMOUNT > 5000
COMPUTE WS-POINTS = WS-AMOUNT * 5
WHEN "G" ALSO WS-AMOUNT > 1000
COMPUTE WS-POINTS = WS-AMOUNT * 3
WHEN "G" ALSO OTHER
COMPUTE WS-POINTS = WS-AMOUNT * 2
WHEN "S" ALSO WS-AMOUNT > 1000
COMPUTE WS-POINTS = WS-AMOUNT * 2
WHEN "S" ALSO OTHER
COMPUTE WS-POINTS = WS-AMOUNT * 1
WHEN OTHER
MOVE ZEROS TO WS-POINTS
END-EVALUATE
Each WHEN clause must have the same number of ALSO parts as the EVALUATE subject line. The clause matches only when all parts match simultaneously.
7.6.6 EVALUATE TRUE ALSO TRUE
The most powerful form of EVALUATE uses TRUE ALSO TRUE, allowing arbitrary conditions on all axes:
EVALUATE TRUE ALSO TRUE
WHEN FULL-TIME
ALSO WS-YEARS >= 10
MOVE 25 TO WS-VACATION-DAYS
WHEN FULL-TIME
ALSO WS-YEARS >= 5
MOVE 20 TO WS-VACATION-DAYS
WHEN PART-TIME
ALSO WS-YEARS >= 5
MOVE 10 TO WS-VACATION-DAYS
WHEN CONTRACTOR
ALSO ANY
MOVE 0 TO WS-VACATION-DAYS
WHEN OTHER
MOVE 5 TO WS-VACATION-DAYS
END-EVALUATE
The ANY keyword matches any value for that position. This is useful when one dimension is irrelevant for a particular case.
7.6.7 EVALUATE FALSE
Less commonly used, EVALUATE FALSE matches the first WHEN clause whose condition is false:
EVALUATE FALSE
WHEN ACCOUNT-ACTIVE
DISPLAY "Account is NOT active"
WHEN WS-BALANCE > ZERO
DISPLAY "Balance is NOT positive"
END-EVALUATE
This is occasionally useful but can be confusing. Most programmers prefer EVALUATE TRUE with negated conditions for clarity.
7.7 CONTINUE and NEXT SENTENCE
7.7.1 CONTINUE (No-Operation Placeholder)
The CONTINUE statement is a no-operation placeholder. It does nothing, but it satisfies the syntax requirement that an IF or WHEN clause must contain at least one statement:
EVALUATE WS-STATUS
WHEN "A"
CONTINUE *> No action needed for active
WHEN "S"
PERFORM HANDLE-SUSPENDED
WHEN "C"
PERFORM HANDLE-CLOSED
END-EVALUATE
CONTINUE is also useful when you want to document that the absence of action is intentional, not an oversight:
IF WS-DEBUG-MODE = "Y"
PERFORM WRITE-DEBUG-LOG
ELSE
CONTINUE *> Production mode: no logging
END-IF
7.7.2 NEXT SENTENCE (Deprecated)
NEXT SENTENCE transfers control to the next sentence (the statement following the next period). It is a relic from pre-COBOL-85 and should not be used in modern code:
* DEPRECATED -- DO NOT USE
IF WS-AMOUNT = ZERO
NEXT SENTENCE
ELSE
PERFORM PROCESS-AMOUNT.
The problem with NEXT SENTENCE is that it interacts with the period-terminated sentence structure in ways that are confusing and error-prone. Use CONTINUE instead -- it is always safe and its behavior does not depend on where periods appear.
7.8 De Morgan's Laws in COBOL
De Morgan's laws describe how negation distributes over AND and OR:
NOT (A AND B)is equivalent to(NOT A) OR (NOT B)NOT (A OR B)is equivalent to(NOT A) AND (NOT B)
These laws are useful when you need to negate a compound condition or when debugging logic errors. In COBOL:
* Original condition: heat advisory
IF WS-TEMPERATURE > 100 AND WS-HUMIDITY > 80
DISPLAY "HEAT ADVISORY"
END-IF
* Negated form using De Morgan's:
IF WS-TEMPERATURE <= 100 OR WS-HUMIDITY <= 80
DISPLAY "No heat advisory"
END-IF
Both tests partition all possible inputs into the same two groups. De Morgan's laws guarantee this equivalence.
When you encounter complex negated conditions in existing code, applying De Morgan's laws can make the logic clearer:
* Hard to understand:
IF NOT (WS-A > 10 AND WS-B < 5 AND WS-C = "Y")
* De Morgan's equivalent (often clearer):
IF WS-A <= 10 OR WS-B >= 5 OR WS-C NOT = "Y"
7.9 Short-Circuit Evaluation: A COBOL Difference
In languages like C, Java, and Python, compound conditions use short-circuit evaluation: if the first operand of an AND is false, the second operand is not evaluated (because the result must be false regardless). Similarly, if the first operand of an OR is true, the second is not evaluated.
COBOL does not guarantee short-circuit evaluation. The COBOL standard does not specify the order in which conditions are evaluated, and most compilers evaluate all conditions regardless of intermediate results.
This has practical implications:
* DANGEROUS in COBOL (but safe in C/Java):
IF WS-DIVISOR NOT = ZERO
AND (WS-DIVIDEND / WS-DIVISOR) > 10
PERFORM PROCESS-RESULT
END-IF
In a language with short-circuit evaluation, the division would not occur when WS-DIVISOR is zero. In COBOL, the division may be attempted, causing a runtime error. Write defensive code:
* SAFE approach:
IF WS-DIVISOR NOT = ZERO
COMPUTE WS-QUOTIENT = WS-DIVIDEND / WS-DIVISOR
IF WS-QUOTIENT > 10
PERFORM PROCESS-RESULT
END-IF
END-IF
7.10 Performance: EVALUATE vs. Nested IF
A common question is whether EVALUATE is faster or slower than equivalent nested IF statements. The answer: they are essentially equivalent in performance. Modern COBOL compilers optimize both constructs to produce similar machine code.
Choose between them based on readability and maintainability:
| Scenario | Preferred Construct |
|---|---|
| Two-way decision | IF/ELSE |
| Testing a single variable against many values | EVALUATE identifier |
| Multi-level decision with many branches | EVALUATE TRUE |
| Testing multiple variables simultaneously | EVALUATE with ALSO |
| Complex boolean expression | IF with AND/OR |
| Range-based decisions | EVALUATE with THRU |
The real performance concern with conditional logic is algorithmic: if you have hundreds of sequential IF tests that could be replaced by a table lookup, the table lookup will be dramatically faster regardless of whether you use IF or EVALUATE.
7.11 Common Patterns
7.11.1 Validation Logic
Input validation is one of the most common uses of conditional logic. A typical pattern validates multiple fields and collects errors:
MOVE ZEROS TO WS-ERROR-COUNT
IF WS-CUSTOMER-NAME = SPACES
ADD 1 TO WS-ERROR-COUNT
DISPLAY "Error: Customer name is required"
END-IF
IF WS-AMOUNT-INPUT IS NOT NUMERIC
ADD 1 TO WS-ERROR-COUNT
DISPLAY "Error: Amount must be numeric"
END-IF
IF WS-PHONE IS NOT VALID-PHONE
ADD 1 TO WS-ERROR-COUNT
DISPLAY "Error: Invalid phone format"
END-IF
IF WS-ERROR-COUNT = ZERO
PERFORM PROCESS-VALID-INPUT
ELSE
PERFORM DISPLAY-ERROR-SUMMARY
END-IF
7.11.2 Business Rule Engines
Complex business rules can be expressed as a series of condition checks with early exit:
SET CONTINUE-PROCESSING TO TRUE
IF CONTINUE-PROCESSING AND NOT ACCOUNT-ACTIVE
MOVE "Account not active" TO WS-DENIAL-REASON
SET STOP-PROCESSING TO TRUE
END-IF
IF CONTINUE-PROCESSING AND OVER-CREDIT-LIMIT
MOVE "Over credit limit" TO WS-DENIAL-REASON
SET STOP-PROCESSING TO TRUE
END-IF
IF CONTINUE-PROCESSING AND FRAUD-DETECTED
MOVE "Fraud flag raised" TO WS-DENIAL-REASON
SET STOP-PROCESSING TO TRUE
END-IF
IF CONTINUE-PROCESSING
PERFORM APPROVE-TRANSACTION
ELSE
PERFORM DENY-TRANSACTION
END-IF
This pattern is clean, sequential, and easy to extend with new rules.
7.11.3 State Machines
EVALUATE is natural for implementing state machines:
EVALUATE TRUE
WHEN ORDER-STATE-NEW
IF EVENT-PAYMENT-RECEIVED
SET ORDER-STATE-PAID TO TRUE
PERFORM SEND-CONFIRMATION
END-IF
WHEN ORDER-STATE-PAID
IF EVENT-SHIPPED
SET ORDER-STATE-SHIPPED TO TRUE
PERFORM SEND-TRACKING
END-IF
WHEN ORDER-STATE-SHIPPED
IF EVENT-DELIVERED
SET ORDER-STATE-COMPLETE TO TRUE
PERFORM CLOSE-ORDER
END-IF
END-EVALUATE
7.12 Common Mistakes and How to Avoid Them
7.12.1 The Premature Period
The most notorious COBOL bug is a period that terminates an IF statement prematurely:
* BUG: period terminates IF, PERFORM always executes
IF WS-STATUS = "A"
DISPLAY "Active".
PERFORM PROCESS-ACTIVE.
Fix: Use END-IF consistently. Never place a period inside an IF statement's body.
7.12.2 Missing END-IF
Without END-IF, the compiler may pair ELSE with the wrong IF:
* Ambiguous: which IF does ELSE belong to?
IF WS-A > 10
IF WS-B > 20
DISPLAY "Both conditions met"
ELSE
DISPLAY "Which condition failed?"
Without END-IF, the ELSE belongs to the innermost IF (the B test). This may not be the programmer's intent.
Fix: Always use END-IF for every IF statement.
7.12.3 Compound Condition Logic Errors
Misunderstanding operator precedence:
* BUG: AND binds tighter than OR
* This means: A=1 OR (B=2 AND C=3), not (A=1 OR B=2) AND C=3
IF WS-A = 1 OR WS-B = 2 AND WS-C = 3
Fix: Always use parentheses when combining AND and OR.
7.12.4 Comparing Incompatible Types
* Potential issue: comparing numeric to alphanumeric
01 WS-AMOUNT PIC 9(5) VALUE 100.
01 WS-LIMIT-STR PIC X(5) VALUE "00100".
IF WS-AMOUNT = WS-LIMIT-STR *> Results may surprise
Fix: Compare like types. If you must compare across types, convert explicitly first.
7.12.5 Forgetting That COBOL Does Not Short-Circuit
* BUG: may divide by zero
IF WS-COUNT > 0
AND (WS-TOTAL / WS-COUNT) > THRESHOLD
Fix: Split into nested IFs or compute the result first.
7.13 Fixed-Format vs. Free-Format Examples
All examples in this chapter use fixed-format (traditional COBOL) column layout. For reference, here is the same logic in both formats:
Fixed Format
IDENTIFICATION DIVISION.
PROGRAM-ID. FIXED-FORMAT-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-STATUS PIC X(1).
88 IS-ACTIVE VALUE "A".
PROCEDURE DIVISION.
MAIN-LOGIC.
MOVE "A" TO WS-STATUS
IF IS-ACTIVE
DISPLAY "Account is active"
ELSE
DISPLAY "Account is not active"
END-IF
STOP RUN.
Free Format
identification division.
program-id. free-format-demo.
data division.
working-storage section.
01 ws-status pic x(1).
88 is-active value "A".
procedure division.
main-logic.
move "A" to ws-status
if is-active
display "Account is active"
else
display "Account is not active"
end-if
stop run.
Free-format COBOL (available in COBOL 2002 and later, and supported by GnuCOBOL with the -free flag) removes the column restrictions. The logic and syntax are identical; only the layout rules differ.
7.14 Putting It All Together: A Complete Example
The following example combines IF, EVALUATE, condition names, class conditions, and sign conditions into a single credit card authorization program. See example-06-complex-logic.cob in the code directory for the complete source.
The program implements these business rules:
- Card status check -- Is the card active, blocked, stolen, or expired?
- Expiration date validation -- Has the card expired based on the current date?
- Currency support -- Is the transaction currency supported?
- Credit limit check -- Does the cardholder have sufficient available credit?
- Transaction velocity -- Has the card been used an unusual number of times today?
- Merchant category restrictions -- Is the merchant category allowed for this card?
- Fraud risk assessment -- Does the fraud score indicate risk?
- Geographic risk -- Is this a cross-border transaction to a high-risk country?
Each check uses condition names to express the business rule clearly. The overall flow uses a "continue processing" flag that allows early termination when a definitive denial is reached, while accumulating warning flags for borderline cases.
This pattern -- sequential rule evaluation with early exit and flag accumulation -- is one of the most common architectures in production COBOL business systems.
Summary
Conditional logic is the engine that drives every business application. In this chapter, you have learned:
- The IF statement provides two-way branching with optional ELSE and mandatory END-IF scope terminators
- Compound conditions with AND, OR, and NOT combine simple conditions, with AND taking precedence over OR
- Implied subjects and operators provide shorthand but can introduce ambiguity -- prefer explicit conditions
- Relation conditions compare values using both symbolic (=, >, <) and word forms (EQUAL TO, GREATER THAN)
- Sign conditions (POSITIVE, NEGATIVE, ZERO) test the sign of numeric values
- Class conditions (NUMERIC, ALPHABETIC, and user-defined) validate the nature of data
- Condition names (88-levels) attach meaningful names to values, creating self-documenting code
- SET TRUE provides a clean way to assign values through condition names
- EVALUATE replaces nested IF chains with clean multi-way branching
- EVALUATE TRUE tests arbitrary conditions in sequence
- EVALUATE with THRU matches ranges; EVALUATE with ALSO creates multi-dimensional decision matrices
- CONTINUE is the preferred no-operation placeholder; NEXT SENTENCE is deprecated
- COBOL does not guarantee short-circuit evaluation -- write defensive code
- De Morgan's laws help simplify and debug negated compound conditions
The combination of EVALUATE and 88-level condition names is the signature idiom of well-written COBOL. Master these constructs and you will write code that is not only correct but readable, maintainable, and aligned with the business rules it implements.
In the next chapter, we will explore COBOL's iteration constructs -- the PERFORM statement and its variations -- which, together with the conditional logic you have learned here, give you complete control over program flow.