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."
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 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.
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 }
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 }
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).
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.
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: ...).
Prior to 0.12.5, you would specify the alternate logical operator without brackets, such as case or.
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: ...).
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.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 }
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
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.
A switch or given expression always returns something, even if it's nothing (null).
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 }}
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 [[[infix op]] [[comparison op] expr1 | expr1 [comparison op] [, ... [comparison op] exprn | [comparison op] exprn]]] [; [logical op] alt1 [, ... altn]] : ... default: ... }
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 { ... } }