langur

switch expressions

This may sometimes look like a typical switch, but is far more flexible. You would likely not use every feature within a single expression.

In the following descriptions, the parts of the tests listed after the switch keyword are called "test expressions" and the parts listed after the case keyword are called "conditions."

test expressions

You can use 1 or more expressions to test against, as a comma-delimited list preceding the opening curly brace (such as switch .x, .y, .z ...).

As the name implies, test expressions are not limited to variables and constants.

conditions

Conditions are comma-delimited lists after the case keyword, preceding an opening colon (such as case 123, .abc, 890: ...).

You do not have to list a condition for every test expression. The rest of this page explains the options.

A default may be specified with the default keyword.

no-op

A test condition may use an underscore to indicate no-op (no comparison, such as case _, 2: ... to test only the second expression or case 2, _: ... to test only the first expression).

one test expression with multiple conditions

Using one test expression with multiple conditions will test all conditions against the expression.

switch .x { case 1, 2, 3: ... # .x equals 1, 2, or 3 }

switch .x { case[and] >= 50, <= 100: ... # .x between 50 and 100 }

one condition with multiple test expressions

A single test condition, without no-ops, is a special condition meaning to test all the expressions against the condition.

switch .x, .y, .z { case true: ... # any are true case true, _: ... # .x == true }

switch .x, .y, .z { case[and] true: ... # all are true case true, _: ... # .x == true }

alternate comparison operators

By default, tests and conditions are compared as operands of the == operator. Alternate default comparison operators may be specified at the beginning or end of each test expression (such as switch .x >, != .y, .z ...).

An alternate comparison operator may also be specified as part of a condition, preceding or following the value (such as case != 100, 5 <= : ...).

Alternate comparison operators specified in conditions override operators specified in test expressions.

alternate logical operators

A switch defaults to logical or between test conditions in a case statement. There are 2 ways to specify an alternate operator. Using square brackets, an alternate infix operator may be specified directly on the switch keyword (such as switch[and]), or one may be specified on the case keyword (such as case[and] 1, 2, 3: ...).

An operator specified on the case keyword overrides an operator specified on the switch keyword.

alternate conditions

Alternate test conditions may be specified as a comma-delimited list after a semicolon within a case test. This allows you to insert other test conditions without having to use an if/else expression.

switch .x { case 1, 2 ; .z > .y: ... # .x == 1 or .x == 2 or .z > .y }

A switch may also have no test expressions. In this case, alternate test conditions are used without a semicolon.

switch { case z > .y: ... # .z > .y }

An alternate logical operator may be specified immediately after a semicolon in a case test containing alternate conditions.

switch .x { case 1 ; xor .z > .y: ... # .x == 1 xor .z > .y }

fallthrough

Unlike other languages, a fallthrough statement is allowed anywhere within a switch expression body (but not in the default section, of course).

Some languages have implicit fallthrough. With the following exception, there is no implicit fallthrough in langur.

case alternate

A case with an empty body has an implicit fallthrough (case alternate).

switch .x { case 123: # empty body; falls through if this case tests true case 456: .a = .b }

regex in switch

A condition or test expression may use a regex literal as a right-hand operand of the forward operator, which will match the other value against the regex pattern (such as case -> re/abc+/:).

Non-string values are converted to strings using auto-stringification before being compared against a regex.

shortened form switch expressions

A shortened form switch expression uses parentheses, colons, and semicolons, and no extra keywords.

The shortened form expects a single expression per action section rather than an implicit block.

You may still use multiple test expressions and conditions, as comma-delimited lists.

switch .x { case 100: 1; case 200: 2; case 300: 3; default: 4} # long form switch expression switch(.x; 100: 1; 200: 2; 300: 3; 4) # shortened form switch expression switch(.x, .y; 100: 1; 200, 400: 2; 300: 3; 4) # another one

This is a semantic convenience and the result is the same as the long form.

The shortened form is more limited, as you cannot use alternate test conditions, nor alternate logical operators, nor explicit fallthrough.

expression value

A switch expression always returns something, even if it's nothing (null).

switch on catch

Using a switch on a catch can be convenient....

catch : switch _err'cat, _err'msg { case "math", _: writeln("math error!") ... default: throw # rethrow }

scope within switch expressions

Declarations are not allowed within case statements.

Blocks within switch expressions are scoped, as illustrated below.

switch ... { case 1: val .x = 123; .x + .y case 2: val .x = 789; .x + .y } # each .x different, and not seen after switch expression

val .y = 789 switch .x { case > 100: val .y = 7 ... } # .y == 789 still

Complex test expressions are evaluated once, instead of being done on every case condition test. This ensures that this is calculated once, rather than on each test.

switch .i * 7 { ... } # ... internally becomes something like ... { val ._test1 = .i * 7 switch ._test1 { ... } }