Conditionals — If / Else / Switch / Case¶
The new parser adds proper structured conditionals, replacing the
low-level JumpIf / JumpIfNot / Mark / JumpTo cluster for the
common cases.
If / Else¶
The basic shape:
If (<condition>) {
... rules to run when condition is true ...
} Else {
... rules to run when condition is false ...
}
The condition is an infix expression — comparisons return booleans,
which the If consumes. Both branches are blocks of rules; either
branch may be omitted (Else is optional). Variables modified
inside a branch persist into the rest of the chain.
Worked example — fallback when a primary lookup is null¶
Source intnum.csv has four integer columns. The primary lookup
sometimes returns NULL; in that case, fall back to a different
source field through a different Usagi map.
logic: :int1 => UsagiMap("A.csv", NULL)[] => :var1
logic: If (:var1 = NULL) {
logic: :int4 => UsagiMap("D.csv", NULL)[] => :var1
logic: }
Read top-down:
- Run
UsagiMap("A.csv")against:int1, defaulting toNULLon a miss. Store the multi-result in:var1. - If
:var1isNULL, replace it with the result of runningUsagiMap("D.csv")against:int4.
Note = is the comparison operator (not ==).
When to use If vs ||¶
The single-rule || is shorter when both branches are one rule:
If wins when:
- A branch has multiple rules to execute as a unit.
- The branches operate on different inputs (the
||form requires the same input for both alternatives). - A condition is expressed by comparison rather than by "did the rule succeed".
For multi-rule branches that share an input, the block fallback
form { ... }(:var) || { ... }(:var) is also worth
considering — it keeps the "first success wins" semantics of ||
while letting each branch be a block.
Switch / Case¶
A clean cascade for many small lookups against a single value:
logic: Switch (:intnum2) {
logic: Case 1: SetValue(38003563)
logic: Case 2: SetValue(38003564)
logic: Case 3: SetValue(38003565)
logic: Default: SetValue(0)
logic: }
Equivalent to a chain of If/ElseIfs, but reads more naturally
when the cases are all literal values against the same expression.
Worked example — concept-id by enum¶
Source intnum1 carries a small set of enum-style integers. The
target CDM field gender_concept_id is a real OMOP concept id, so
each enum value maps to a different concept:
logic: Switch (:intnum1) {
logic: Case 999: SetValue(9999)
logic: Case 100: SetValue(8507)
logic: Case 200: SetValue(8532)
logic: Default: SetValue(0)
logic: }
For string keys this is essentially what Map([...], default) does
in one line. Switch is the better choice when the right-hand side
of each branch is a block of rules rather than just a value.
How conditionals desugar¶
Under the hood, If / Else and Switch / Case compile down to
the JumpIf,
JumpIfNot,
JumpTo, and
Mark rules. You can write
those directly if you need control flow that the structured forms do
not express, but the structured forms are strongly preferred —
they're easier to read and harder to get wrong.
See also¶
JumpIfandMark— the underlying primitives.- Conditional Chaining —
||for single-rule fallback. - Fallback chains (
||) — block-level fallback.
Source tests¶
tests/test062/ — If for null-fallback after a multi-result
UsagiMap. tests/test067/ — Switch / Case for enum-to-concept-id
mapping.