Table of Contents

5.4.2.10 Select Case Statement

Note

This section describes the implementation of MS-VBAL §5.4.2.10 Select Case Statement.

Syntax

AST node Instruction kind(s) Notes
SelectCaseStatementNode Select The Select Case opener. Its End is right past the block.
CaseExpressionStatementNode ConditionalBranch A Case header. Its Matching names the offset of its Select opener. Its Else is the offset to go to on no match: the next header in the chain, the first instruction of the Case Else body, or right past the whole construct.
CaseElseClauseStatementNode — Case Else needs no condition.
— (synthesized) Jump The trailing jump at the end of each branch's body. Node is null; Target is right past the whole construct.

Each Case line's comma-separated conditions form their own node hierarchy under the abstract CaseRangeClauseNode. The three condition forms of MS-VBAL §5.4.2.10 are modelled independently, as three node types:

Condition form Example AST node
Single value Case 5 CaseValueRangeClauseNode
Comparison Case Is > 5 CaseComparisonRangeClauseNode
Inclusive range Case 1 To 10 CaseToRangeClauseNode

See RD-VBAL §3.4.1 Block Statements and RD-VBAL §3.5.2 Instruction.

Runtime Semantics

Select

  1. Evaluate the header expression (the selector) once.
  2. Store the selector on the activation, keyed by the Select instruction's own offset.
  3. Fall through into the body, to the first Case header.

MS-VBAL §5.4.2.10 says "the select-expression is immediately evaluated": the selector is evaluated once, ahead of every case-clause. RD-VBA passes the evaluated selector to every clause comparison, in accordance with this.

Case Header

The executor dispatches ConditionalBranch separately for a Case header: a Case header has Instruction.Matching set instead of a Boolean condition.

  1. Read the enclosing Select's stored selector, through the header's Matching offset.
  2. If the selector is a Variant, unwrap it.
  3. If the selector is Null, go directly to the header's Else offset without evaluating any range clause. Every Case header does the same, so control reaches Case Else.
  4. Otherwise, match the header's range clauses against the selector, as a comparison or logical expression.
  5. On a match, fall through into the branch's body. Otherwise, go to the header's Else offset: the next header in the chain, the first instruction of the Case Else body, or right past the whole construct.
  6. After the branch's body runs, the synthesized trailing Jump goes right past the whole construct.

MS-VBAL §5.4.2.10: "If select-expression is the data value Null, only the case-else-clause is executed". A Null can reach Select Case only through a Variant selector: a local declared Long can never hold Null.

Range Clause Matching

A Case header's range clauses are matched as a comparison or logical expression, the way MS-VBAL §5.4.2.10 phrases its own runtime semantics:

Range clause Example Matched as
Value Case 5 selector = 5
Comparison Case Is > 5 selector > 5. The clause's own, already-normalized comparison-operator token selects the relational operator strategy directly.
To Case 1 To 10 (selector >= 1) And (selector <= 10)

Each comparison is evaluated through the same operator strategies every other expression uses (BinaryRelationalOperatorRuntimeSemantics, BinaryAndLogicalOperatorRuntimeSemantics; RD-VBAL §5.6.9.5 Relational Operators, RD-VBAL §5.6.9.8 Logical Operators).

The already-evaluated selector is passed to each clause comparison as an operand value. It is not re-evaluated per clause.

Implementation

  • ProcedureExecutor dispatches Select, and the ConditionalBranch of a Case header.
  • The selector is stored through CallStackFrame.SetBlockState and read back through ICallStackFrame.TryGetBlockState, keyed by the opener's offset. A single block-state value is enough for the selector. There is no separate closer instruction to pop the stored state on exit (RD-VBAL §3.5.4 Execution).
  • A Case header's Matching is the same field a ForNext instruction uses for its back-reference to its opener (RD-VBAL §5.4.2.3 For Statement).
  • Select Case needs no synthesized closer: leaving the last branch, or Case Else, already reaches the offset that the construct's own End/Else chaining designates (RD-VBAL §3.5.3 Lowering Block Statements).
  • ProcedureExecutor.ExecuteCaseHeader's Null-selector short-circuit unwraps a Variant selector before testing it for Null.
  • RDCore.Runtime.Execution.CaseMatchEvaluator matches the range clauses against the selector. It calls the specific operator strategy directly rather than going through OperatorRuntimeSemanticsProvider's token dispatch, because the range clause already determines the operator strategy.
  • CaseMatchEvaluator passes the clause's own operand expression as the location-bearing node of each operator call, never a fabricated node standing in for the already-evaluated selector.
  • This relies on the node parameter of OperatorRuntimeSemantics<TContext,TFlags> being typed ExpressionNode (RD-VBAL §5.6.9.2 Simple Data Operators).

⏮️ RD-VBAL §5.4.2.9 Single-line If Statement | ⏭️ RD-VBAL §5.4.2.11 Stop Statement