While working on completing my BCScript (AL) compiler/Interpreter, I realized that the case statement in AL is not like the case statement in Pascal. Follow along the discovery in this video:

In this video, Erik dives into an unexpected discovery about how the case statement works in AL for Business Central. If you’re coming from a Pascal or most other language backgrounds, you might assume that case labels must be constants — but AL has a surprise in store. Erik walks through the behavior, demonstrates it with live code and the debugger, and explores what this means for compiler optimization.
The Basics: Case Statements in AL
If you’re coming from another language, you’d think of the case statement as a switch statement. In AL, it works like this:
case age of
0:
Message('Welcome to the world');
1, 2, 3, 4, 5, 6:
Message('Not in school');
7 .. 18:
Message('In school');
19 .. 99:
Message('Working');
end;
With age set to 16, this evaluates to “In school” — no surprises there.
Overlapping Ranges and Order of Evaluation
In most languages, if you add a duplicate case label that’s already covered by another case (or a range), you’d get a compiler error. In AL, that’s not entirely the case. If you add a specific case for 16 alongside the range 7 .. 18, AL compiles without complaint.
The order matters though: whichever case appears first wins. If the explicit 16: case is placed before the 7 .. 18 range, it matches first. If the range comes first, the specific 16 case is never reached. If you add two identical constant labels (e.g., two entries for 16), AL will flag that. But when a range overlaps with a specific value, it’s silent.
The Real Discovery: Expressions as Case Labels
This is where things get interesting. In classic Pascal — going all the way back to the early 1980s — the values before the colon in a case statement must be constants. But the AL documentation says that a value set can be an expression or a range.
That means you can write something like this:
case age of
8 + 8:
Message('Eight plus eight!');
16:
Message('Sweet sixteen');
end;
And it works. 8 + 8 evaluates to 16, and the first match wins. But it goes further than just arithmetic — you can use function calls as case labels.
Function Calls in Case Labels
Here’s the source code Erik demonstrates. A simple test function that returns whatever integer you pass in:
procedure test(i: Integer): Integer
begin
//message('Testing %1', i);
exit(i);
end;
And the case statement using function calls as value sets:
case age of
test(0):
Message('Welcome to the world');
test(1):
Message('Learn to walk');
test(16):
Message('Sweet sixteen');
else
message('Anything else!');
end;
With age set to 16, this correctly outputs “Sweet sixteen.” But the implications are profound: every case label is evaluated at runtime. When you uncomment the message inside the test function, you can see that test(0), test(1), and test(16) are all called sequentially until a match is found.
It’s Really Just an If-Else Chain
This means the case statement in AL is semantically equivalent to a chain of if...else if statements:
if test(0) = 0 then
message('Welcome to the world')
else if test(1) = 1 then
message('Learn to walk')
else if test(16) = 16 then
message('Sweet sixteen!')
else
message('Anything else!');
Erik notes an irony here: the AL code analyzer will actually suggest converting if...else if chains into case statements for readability. Traditionally, you’d assume this also enables optimization — a compiler could use a hash table or jump table for constant case labels. But since AL allows arbitrary expressions (including function calls), those optimizations are impossible in the general case.
A Debugger Mystery
During the demo, Erik stumbled onto an odd behavior: when running with the debugger attached and a breakpoint set, the OnOpenPage trigger appeared to execute multiple times (two or three times instead of once). Without the debugger, the code ran exactly once. Erik wasn’t able to explain this during the video and invited viewers to share their theories — it may be related to how the Business Central web client initializes pages or how the debugger attaches to the session.
Impact on Compiler Design
Erik discovered all of this while implementing case statement support in his own AL compiler (for his BC Script tool). He was initially designing an optimized implementation using hash tables for value lookups and jump tables for fast dispatch — the kind of optimizations that are standard for constant case labels in languages like C or Pascal.
Once he realized that AL case labels can be arbitrary expressions evaluated at runtime, all of those optimization strategies went out the window. The case statement essentially must be compiled as a sequential evaluation, checking each label expression in order.
Full Source Code
Here’s the complete page extension Erik used for the demonstration:
pageextension 50117 CustomerListExt extends "Customer List"
{
trigger OnOpenPage();
var
age: Integer;
begin
age := 16;
if test(0) = 0 then
message('Welcome to the world')
else if test(1) = 1 then
message('Learn to walk')
else if test(16) = 16 then
message('Sweet sixteen!')
else
message('Anything else!');
case age of
test(0):
Message('Welcome to the world');
test(1):
Message('Learn to walk');
test(16):
Message('Sweet sixteen');
else
message('Anything else!');
end;
end;
procedure test(i: Integer): Integer
begin
//message('Testing %1', i);
exit(i);
end;
}
Conclusion
The AL case statement is more powerful — and more surprising — than you might expect if you’re coming from a Pascal background. Unlike traditional Pascal where case labels must be compile-time constants, AL allows full expressions, including function calls, as case labels. These are evaluated sequentially at runtime until a match is found, making the case statement functionally equivalent to an if...else if chain. While this adds flexibility, it also means the compiler can’t apply the jump table or hash-based optimizations that make case statements fast in other languages. It’s a subtle but important difference to be aware of when writing AL code for Business Central.