Switch Statement

The switch statement can be used instead of an if-construct that repeatedly tests the same expression, for example:
if    ch='a' then   puts(1,"ch is a\n")
elsif ch='b' then   puts(1,"ch is b\n")
elsif ch='c' then   puts(1,"ch is c\n")
  ...
else                puts(1,"ch is something else\n")
end if
Can, if you prefer, and perhaps with some caution as detailed below, be replaced with:
switch ch
  case 'a': puts(1,"ch is a\n")
  case 'b': puts(1,"ch is b\n")
  case 'c': puts(1,"ch is c\n")
    ...
  default:  puts(1,"ch is something else\n")
end switch
Note that in phix the choice of which construct to use should be entirely based on personal preference, ease of writing, and ease of future maintenance (not performance).

For instance one thing that may significantly simplify maintenance is that it is trivial to change the second branch of the if-construct to
elsif ch='b' and ignoreb=0 then
whereas making the equivalent change to the switch-construct, whereby a 'b' ends up in the default case when ignoreb=1, is not quite so easy:
The clause case 'b' and ignoreb=0: is in fact equivalent to case TRUE and ignoreb=0:, which is the same as case ignoreb=0:, ie/aka if ch=(ignoreb=0) then. When your write your case expressions, you have to remember there is an implicit switchvar=( ) around them, so it is ch=('b' and ignoreb=0) rather than (ch='b') and (ignoreb=0). The closest thing would instead be
    case 'b': if ignoreb=0 then
                puts(1,"ch is b\n")
                break
              end if
              fallthrough
    default:
But, obviously, you can only move one case so that it is immediately above the default.

Conversely you might want to make it deliberately difficult to thwart the creation of a jump table (assuming careful testing has proved that to be significant).

In both the above (first two) snippets, it is the back-end that decides whether a jump table is suitable: experiments revealed that branch misprediction makes <8 branches faster as a daisy-chain of jumps, and >8 branches faster as a jump table, and the implementation is such that both constructs can generate either. A jump table can only be constructed when all tests apply (as they always do in a switch statement) to the same variable, all values are fixed integers, and no complex/compound tests are involved.

In particular, not that it happens by default any longer in phix, a switch true/false using a jump table was woefully slower than a single test/jump. Also, I saw no reason to waste time converting perfectly valid if-constructs in legacy code to switch statements, especially when doing so could actually make things slower, and introduce bugs/typos, so instead I decided to make the compiler do all the work for me automatically.

A jump table usually causes an AGI stall and almost inevitably incurs a branch misprediction, which flushes the pipeline and costs at least 8 clocks, on some recent/advanced processors even more. Modern processors are also quite astonishingly good at branch prediction, including spotting a whole range of loop patterns, but that occurs (in hardware) at the instruction level, not at jump table entry level. As above, you need about 8 branches to "break even", but the back-end is perfectly happy to make that decision for you, including whether or not a jump table would be "too sparse" (<5% populated), as well as perform any necessary preliminary bounds checking. For practical reasons, a switch construct with no case statements triggers a compilation error ('case expected' on the 'end switch'), in much the same way that you cannot have an if-construct with no conditions.

In short, unlike most other languages, performance should not or at least very rarely (and only after testing/timing both) be a factor in determining which construct to use.

The full syntax of a switch statement is as follows:
    switch <expr> [("with"|"without") "fallthrough"|"fallthru"|"jump_table"] [do]
      {"case" <expr>{","<expr>}|"default"|"else") [":"|"then"] [<block>] ["break"|"fallthrough"|"fallthru"]}
    end switch
Additional "break" statements can also be buried deep inside <block>, with execution resuming at the end switch.
Also note that "exit" and "return" statements terminate the containing loop/routine, without any attention to the location of the end switch.

The else keyword can be used instead of default, and behaves identically.
Only one default clause is permitted per switch statement, but it can be placed anywhere.
If placed anywhere other than at the end, an error triggers if a jump table cannot be constructed.

Normally each branch rejoins execution at the end switch statement, but you can change that for individual cases or the entire switch construct using either spelling of fallthrough (fallthru is allowed for compatibility with OpenEuphoria). The presence of any fallthrough or the more explicit jump_table option forces the use of a jump table, should you incorrectly care about that. The diligent programmer is expected to avoid switch/fallthrough/jump_table when inappropriate, for instance "with jump_table case 1,1_000_000_000:" would force the compiler to try and create a 4 or 8GB jump table, perhaps with only 2 entries that are actually used, and unable to rely on the <5% rule because of the explicit overriding "with jump_table" directive.

"break" and "fallthough" are effectively polar opposites: in a "switch with fallthrough" you may need the occasional "break"; whereas in a "switch [without fallthrough]" you may need the occasional "fallthrough", and in both cases the other will be implicitly assumed when not explicitly present. One other difference is that a "break" is effectively "goto the end switch", and can occur within nested conditional statements (and may be used to that effect in all forms of the switch statement), whereas "fallthrough" is effectively "omit the usual goto" and is therefore only valid immediately prior to case/default/else.

Duplicate entries in the jump table trigger an error. Note that the same error may also occur on a plain if-construct, should they be detected before anything that prohibits the use of a jump table.

So-called "smart switch" processing is also supported, in other words "back-to-back" cases do not have the usual implied "break":
    case 3:
    case 4: <block>
behaves identically to
    case 3,4: <block>
Be wary of the hidden gotcha in that commenting out the last line of code between two cases can effectively insert a "fallthrough".

It is also possible to write fully "variable/polymorphic" switch statements, eg:
    procedure dosomething(object action, stop, start, term)
      switch action
        case stop: running=false
        case start: running=true
        case term: terminate=true
      end switch
    end procedure

    dosomething(i,1,0,-1)
    ...
    dosomething(j,1,2,3)
    ...
    dosomething("hey",55.37,"NO",{{"this"}})
Obviously when the branch cases are not fixed, or non-integer, it is not possible to construct a jump table, and attempts to fallthrough will trigger an error (you would need to resort to multiple if-constructs with any required additional "or action=").

Of course the reverse may be true: an if-construct which looks completely variable to the front-end may in fact prove to consist entirely of known fixed integer values after the detailed analysis phase, so we can emit a jump table for it. That might be particularly useful for general purpose library code of which your program only exercises a small portion.

Very rarely, the compiler might construct a jump table for an if-construct that proves noticeably less efficient than a daisy-chain of cmp/jmp, specifically when the first few branches account for a very high percentage of cases. Should that happen, one easy solution might be to invert one test, eg "if 'a'=ch then" (with an appropriate comment). You would need to use "p -d" and examine the resulting list.asm to verify the presence/absense of a jump table.

Unlike OpenEuphoria, Phix does not support "with label" on if/for/while/switch constructs, or the goto or break "label" statements.