Add switch expression.

This commit is contained in:
Stephen Chung
2020-11-13 18:32:18 +08:00
parent 7d1b971b39
commit 55b4907f19
19 changed files with 547 additions and 252 deletions

View File

@@ -77,13 +77,14 @@ The Rhai Scripting Language
7. [Logic Operators](language/logic.md)
8. [Other Operators](language/other-op.md)
9. [If Statement](language/if.md)
10. [While Loop](language/while.md)
11. [Loop Statement](language/loop.md)
12. [For Loop](language/for.md)
13. [Return Values](language/return.md)
14. [Throw Exception on Error](language/throw.md)
15. [Catch Exceptions](language/try-catch.md)
16. [Functions](language/functions.md)
10. [Switch Expression](language/switch.md)
11. [While Loop](language/while.md)
12. [Loop Statement](language/loop.md)
13. [For Loop](language/for.md)
14. [Return Values](language/return.md)
15. [Throw Exception on Error](language/throw.md)
16. [Catch Exceptions](language/try-catch.md)
17. [Functions](language/functions.md)
1. [Call Method as Function](language/method.md)
2. [Overloading](language/overload.md)
3. [Namespaces](language/fn-namespaces.md)
@@ -91,11 +92,11 @@ The Rhai Scripting Language
5. [Currying](language/fn-curry.md)
6. [Anonymous Functions](language/fn-anon.md)
7. [Closures](language/fn-closure.md)
17. [Print and Debug](language/print-debug.md)
18. [Modules](language/modules/index.md)
18. [Print and Debug](language/print-debug.md)
19. [Modules](language/modules/index.md)
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
2. [Import Modules](language/modules/import.md)
19. [Eval Statement](language/eval.md)
20. [Eval Statement](language/eval.md)
6. [Safety and Protection](safety/index.md)
1. [Checked Arithmetic](safety/checked.md)
2. [Sand-Boxing](safety/sandbox.md)

View File

@@ -13,6 +13,7 @@ Keywords List
| `is_shared` | is a value shared? | [`no_closure`] | yes | no |
| `if` | if statement | | no | |
| `else` | else block of if statement | | no | |
| `switch` | matching | | no | |
| `while` | while loop | | no | |
| `loop` | infinite loop | | no | |
| `for` | for loop | | no | |
@@ -52,7 +53,6 @@ Reserved Keywords
| `then` | control flow |
| `goto` | control flow |
| `exit` | control flow |
| `switch` | matching |
| `match` | matching |
| `case` | matching |
| `public` | function/field access |

View File

@@ -39,6 +39,7 @@ Symbols and Patterns
| Symbol | Name | Description |
| ---------------------------------- | :------------------: | ------------------------------------- |
| `_` | underscore | default `switch` case |
| `;` | semicolon | statement separator |
| `,` | comma | list separator |
| `:` | colon | [object map] property value separator |
@@ -52,6 +53,7 @@ Symbols and Patterns
| <code>\|</code> .. <code>\|</code> | pipes | closure |
| `[` .. `]` | brackets | [array] literal |
| `!` | bang | function call in calling scope |
| `=>` | double arrow | `switch` expression case separator |
| `//` | comment | line comment |
| `/*` .. `*/` | comment | block comment |
| `(*` .. `*)` | comment | _reserved_ |
@@ -64,7 +66,6 @@ Symbols and Patterns
| `#` | hash | _reserved_ |
| `@` | at | _reserved_ |
| `$` | dollar | _reserved_ |
| `=>` | double arrow | _reserved_ |
| `->` | arrow | _reserved_ |
| `<-` | left arrow | _reserved_ |
| `===` | strict equals to | _reserved_ |

View File

@@ -49,7 +49,7 @@ For example:
let animal = "rabbit";
let food = "carrot";
animal eats food // custom operator - 'eats'
animal eats food // custom operator 'eats'
eats(animal, food) // <- the above really de-sugars to this
```
@@ -61,7 +61,7 @@ nevertheless it makes the DSL syntax much simpler and expressive.
Custom Syntax
-------------
For advanced DSL scenarios, it is possible to define entire expression [_syntax_][custom syntax] -
For advanced DSL scenarios, it is possible to define entire expression [_syntax_][custom syntax] &ndash
essentially custom statement types.
For example, the following is a SQL-like syntax for some obscure DSL operation:

View File

@@ -1,5 +1,5 @@
`Scope` - Initializing and Maintaining State
===========================================
=================================================
{{#include ../links.md}}

View File

@@ -12,27 +12,19 @@ Use `type_of()` to Get Value Type
Because [`type_of()`] a `Dynamic` value returns the type of the actual value,
it is usually used to perform type-specific actions based on the actual value's type.
```rust
```c
let mystery = get_some_dynamic_value();
if type_of(mystery) == "i64" {
print("Hey, I got an integer here!");
} else if type_of(mystery) == "f64" {
print("Hey, I got a float here!");
} else if type_of(mystery) == "string" {
print("Hey, I got a string here!");
} else if type_of(mystery) == "bool" {
print("Hey, I got a boolean here!");
} else if type_of(mystery) == "array" {
print("Hey, I got an array here!");
} else if type_of(mystery) == "map" {
print("Hey, I got an object map here!");
} else if type_of(mystery) == "Fn" {
print("Hey, I got a function pointer here!");
} else if type_of(mystery) == "TestStruct" {
print("Hey, I got the TestStruct custom type here!");
} else {
print("I don't know what this is: " + type_of(mystery));
switch mystery {
"i64" => print("Hey, I got an integer here!"),
"f64" => print("Hey, I got a float here!"),
"string" => print("Hey, I got a string here!"),
"bool" => print("Hey, I got a boolean here!"),
"array" => print("Hey, I got an array here!"),
"map" => print("Hey, I got an object map here!"),
"Fn" => print("Hey, I got a function pointer here!"),
"TestStruct" => print("Hey, I got the TestStruct custom type here!"),
_ => print("I don't know what this is: " + type_of(mystery))
}
```

View File

@@ -0,0 +1,97 @@
`switch` Expression
===================
{{#include ../links.md}}
The `switch` _expression_ allows matching on literal values, and it mostly follows Rust's
`match` syntax:
```c
switch calc_secret_value(x) {
1 => print("It's one!"),
2 => {
print("It's two!");
print("Again!");
}
3 => print("Go!"),
// _ is the default when no cases match
_ => print("Oops! Something's wrong: " + x)
}
```
Expression, Not Statement
------------------------
`switch` is not a statement, but an expression. This means that a `switch` expression can
appear anywhere a regular expression can, e.g. as function call arguments.
```c
let x = switch foo { 1 => true, _ => false };
func(switch foo {
"hello" => 42,
"world" => 123,
_ => 0
});
// The above is somewhat equivalent to:
let x = if foo == 1 { true } else { false };
if foo == "hello" {
func(42);
} else if foo == "world" {
func(123);
} else {
func(0);
}
```
Array and Object Map Literals Also Work
--------------------------------------
The `switch` expression can match against any _literal_, including [array] and [object map] literals.
```c
// Match on arrays
switch [foo, bar, baz] {
["hello", 42, true] => { ... }
["hello", 123, false] => { ... }
["world", 1, true] => { ... }
_ => { ... }
}
// Match on object maps
switch map {
#{ a: 1, b: 2, c: true } => { ... }
#{ a: 42, d: "hello" } => { ... }
_ => { ... }
}
```
Difference From If-Else Chain
-----------------------------
Although a `switch` expression looks _almost_ the same as an `if`-`else` chain,
there are subtle differences between the two.
A `switch` expression matches through _hashing_ via a look-up table.
Therefore, matching is very fast. Walking down an `if`-`else` chain
will be _much_ slower.
On the other hand, operators can be [overloaded][operator overloading] in Rhai,
meaning that it is possible to override the `==` operator for integers such
that `if x == y` returns a different result from the built-in default.
`switch` expressions do _not_ use the `==` operator for comparison;
instead, they _hash_ the data values and jump directly to the correct
statements via a pre-compiled look-up table. This makes matching extremely
efficient, but it also means that [overloading][operator overloading]
the `==` operator will have no effect.
Therefore, in environments where it is desirable to [overload][operator overloading]
the `==` operator - though it is difficult to think of valid scenarios where you'd want
`1 == 1` to return something other than `true` - avoid using the `switch` expression.