given expressions

Langur's "switch" is the given expression. Given 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 given keyword are called "test expressions" and the parts listed after the case keyword are called "conditions."

Some examples are given at the end of this page.

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.

conditions

Conditions are listed as 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.

A default may be specified with the default keyword.

one condition

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

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, conditions are compared as rightmost operands of the == operator (the test expressions at the beginning of the given expression being the leftmost operands by default). 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 immediately before or after the test condition (such as case != 100, 5 <=: ...).

Comparison operators specified in case statements override operators specified in test expressions.

alternate logical operators

By default, logical and is used between test conditions in a case statement. An alternate logical operator may be specified immediately after a case keyword (such as case or 1, 2, 3: ...).

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

regex in given

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

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

alternate expressions / non-variable given

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

A given may also have no expressions to test against (non-variable given). In this case, alternate test expressions are used without a semicolon.

fallthrough

A fallthrough statement is allowed anywhere within a given statement body.

A case statement with an empty body has an implicit fallthrough (case alternate). Otherwise, there is no implicit fallthrough.

shortened form given expressions

A shortened form 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, as a comma-delimited list.

You cannot use alternate test conditions, nor alternate logical operators, nor explicit fallthrough, in the shortened form.

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.

expression value

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

given example 1

given .x, .y, .z { case true: ... # all are true case true, false, null: ... # .x == true and .y == false and .z == null case _, 123, < 7: ... # .y == 123 and .z < 7 case 10 >=, _: ... # 10 >= .x case _, re/abc+/: ... # .y matches regex pattern abc+ default: ... }

given example 2

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

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

given example 3

val .y = "abcd" given .x { case 100: ... # .x == 100 case 200; len(.y) > 3: ... # .x == 200 and len(.y) > 3 }

given example 4

val .z = "abcd" given <= .x, .y > { case 100: ... # 100 <= .x and .y > 100 case or 200, 300; len(.z) > 3: ... # (200 <= .x or .y > 300) and len(.z) > 3 case 200, 300 >=; or len(.z) > 3: ... # 200 <= .x and 300 >= .y or len(.z) > 3 case ; len(.z) > 3: ... # len(.z) > 3 }

breakdown

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

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

Declarations are not allowed within case statements.

Blocks within given expressions are scoped, as illustrated below.

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

val .y = 789 given .x { case > 100: val .y = 7 ... return .x ^ .y } # .y == 789 again

Declarations and assignments are moved from test expressions and placed ahead of the 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 { ... } }