Skip to content

Commit

Permalink
[Voi-92] Basic type casting via match statements (#32)
Browse files Browse the repository at this point in the history
* WIP

* Handle default

* Basic match type checking (For now)

* Assembler match support?

* Almost

* IT WORKS

* Add Custom RTT logic to fix extension testing

* Better implementation of last commit

* Add tests

* Update docs

* Switch to : for match cases

* Update docs

* Update tests
  • Loading branch information
drew-y authored Sep 4, 2024
1 parent 06f8550 commit 2e17135
Show file tree
Hide file tree
Showing 27 changed files with 788 additions and 233 deletions.
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,17 +240,20 @@ for item in iterable

### Match Statements

Match statements are used for type narrowing

```rust
let x = 3
match x
1 => print "One"
2 => print "Two"
3 => print "Three"
_ =>
// Match statements are exhaustive, they must cover every possible
// case. When not every case is covered, a default handler must be
// provided.
write "A number"
obj Animal
obj Cat extends Animal
obj Dog extends Animal

let dog = Dog {}

match(dog)
Dog: print "Woof"
Cat: print "Meow"
else:
print "Blurb"
```

## Closures
Expand Down
46 changes: 46 additions & 0 deletions reference/control-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Control Flow

## Match

Used to narrow types. Can operate on object to narrow
child objects or on unions to narrow union members

Signature(s):
```
fn match<T extends Object, U>(val: T, body: MatchBlock) -> U
fn match<T extends Object, U>(val: T, bind_identifier: Identifier, body: MatchBlock) -> U
```

Example:
```void
obj Optional
obj None extends Optional
obj Some extends Optional {
value: i32
}
fn divide(a: i32, b: i32) -> Optional
if b == 0
None {}
else:
Some { value: a / b }
fn main(a: i32, b: i32) -> String
let x = a.divide(b)
match(x)
Some: "The value is ${x}"
None: "Error: divide by zero"
else: "Bleh"
```

The second signature of match is useful when the value being matched against
is not already bound to an identifier (i.e. dot pipelines):
```void
fn main(a: i32, b: i32) -> String
a.divide(b)
.match(x) // Here, match binds the result of the previous expression to x
Some: "The value is ${x}"
None: "Error: divide by zero"
```
26 changes: 26 additions & 0 deletions reference/types/objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,32 @@ obj Cat extends Animal {
}
```

## Object Type Narrowing

```void
obj Optional
obj None extends Optional
obj Some extends Optional {
value: i32
}
fn divide(a: i32, b: i32) -> Optional
if b == 0
None { }
else:
Some { value: a / b }
fn main(a: i32, b: i32)
a.divide(b)
.match(x)
Some:
log "The value is ${x}"
None:
log "Error: divide by zero"
```

# Traits

Traits are first class types that define the behavior of a nominal object.
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/compiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ describe("E2E Compiler Pipeline", () => {
const test2 = getWasmFn("test2", instance);
const test3 = getWasmFn("test3", instance);
const test4 = getWasmFn("test4", instance);
const test5 = getWasmFn("test5", instance);
const test6 = getWasmFn("test6", instance);
assert(test1, "Test1 exists");
assert(test2, "Test2 exists");
assert(test3, "Test3 exists");
assert(test4, "Test4 exists");
assert(test5, "Test3 exists");
assert(test6, "Test4 exists");
t.expect(test1(), "test 1 returns correct value").toEqual(13);
t.expect(test2(), "test 2 returns correct value").toEqual(1);
t.expect(test3(), "test 3 returns correct value").toEqual(2);
t.expect(test4(), "test 4 returns correct value").toEqual(52);
t.expect(test5(), "test 5 returns correct value").toEqual(21);
t.expect(test6(), "test 6 returns correct value").toEqual(-1);
});
});
22 changes: 21 additions & 1 deletion src/__tests__/fixtures/e2e-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ obj Pointy extends Vec {
z: i32
}
obj Bitly extends Vec {
x: i32,
y: i32,
z: i32
}
fn get_x(vec: Vec)
vec.x
Expand All @@ -44,6 +50,12 @@ fn get_member(vec: Point)
fn get_member(vec: Pointy)
vec.x
fn get_num_from_vec_sub_obj(vec: Vec)
match(vec)
Pointy: get_member(vec)
Point: get_member(vec)
else: -1
// Should return 13
pub fn test1()
let vec = Point { x: 1, y: 2, z: 13 }
Expand All @@ -59,10 +71,18 @@ pub fn test3()
let vec = Vec { x: 1, y: 2 }
vec.get_member()
// Should return 52
pub fn test4()
let vec = Point { x: 52, y: 2, z: 21 }
vec.get_x()
// Test match type guard (Point case), should return 21
pub fn test5()
let vec = Point { x: 52, y: 2, z: 21 }
get_num_from_vec_sub_obj(vec)
// Test match type guard (else case), should return -1
pub fn test6()
let vec = Bitly { x: 52, y: 2, z: 21 }
get_num_from_vec_sub_obj(vec)
`;
Loading

0 comments on commit 2e17135

Please sign in to comment.