langur

switch/given expressions

0.11 adds a switch expression. A given is the same as a switch, except that switch defaults to testing that any condition is true (case or) and given defaults to testing that all conditions are true (case and). These expressions are very flexible. You would not likely use every feature available within a single expression.

In the following descriptions, the parts of the tests listed after the switch or given 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 given .x, .y, .z ...).

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

As of 0.7.0, complex test expressions are evaluated once, instead of being done on every case condition test.

conditions

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

You do not have to list a condition for every test expression. Please read the rest of this page, as it explains the options.

A default may be specified with the default keyword.

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 }

given .x { case >= 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 }

given .x, .y, .z { case true: ... # all are true case true, _: ... # .x == true }

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).

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 given .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 <= : ...).

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, and given defaults to logical and. An alternate logical operator may be specified immediately after the case keyword (such as case or 1, 2, 3: ...).

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.

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

An alternate logical operator may be specified immediately after a semicolon in a case test containing alternate conditions (such as case 1; xor .x > .y: ...).

fallthrough

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

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/given

A condition or test expression may be a regex literal, which will match the other value against the regex pattern (such as case re/abc+/:). If both are regex literals, they will be treated the same as other test conditions.

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

shortened form switch/given expressions

A shortened form switch or given 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.

given .x { case 100: 1; case 200: 2; case 300: 3; default: 4} # long form given expression given(.x; 100: 1; 200: 2; 300: 3; 4) # shortened form given expression

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 or given expression always returns something, even if it's nothing (null).

given on catch

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

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

breakdown

The following gives a semi-technical general idea for the syntax of a given expression.

switch/given [[comparison op] expr1 | expr1 [comparison op] [, ... [comparison op] exprn | [comparison op] exprn]] { case [[logical op] [[comparison op] expr1 | expr1 [comparison op] [, ... [comparison op] exprn | [comparison op] exprn]]] [; [logical op] alt1 [, ... altn]] : ... default: ... }

scope within switch/given expressions

Declarations are not allowed within case statements.

Blocks within switch and given 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 given .x { case > 100: val .y = 7 ... } # .y == 789 still

Declarations and assignments are moved from test expressions and placed ahead of the switch or given expression in a scoped block. This ensures the declaration and assignment happens once, rather than on each test.

given val .x = len(.y) { ... } # ... is the same as ... { val .x = len(.y) given .x { ... } }