Skip to content

Commit c4af5ed

Browse files
authored
Adding Concurrency and Error Throws (#77)
1 parent f158317 commit c4af5ed

77 files changed

Lines changed: 4787 additions & 870 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.swiftlint.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ identifier_name:
111111
excluded:
112112
- id
113113
- no
114+
type_name:
115+
excluded:
116+
- If
117+
- Do
114118
excluded:
115119
- DerivedData
116120
- .build
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
enum VendingMachineError: Error {
2+
case invalidSelection
3+
case insufficientFunds(coinsNeeded: Int)
4+
case outOfStock
5+
}
6+
7+
class VendingMachine {
8+
var inventory = [
9+
"Candy Bar": Item(price: 12, count: 7),
10+
"Chips": Item(price: 10, count: 4),
11+
"Pretzels": Item(price: 7, count: 11)
12+
]
13+
var coinsDeposited = 0
14+
15+
16+
func vend(itemNamed name: String) throws {
17+
guard let item = inventory[name] else {
18+
throw VendingMachineError.invalidSelection
19+
}
20+
21+
22+
guard item.count > 0 else {
23+
throw VendingMachineError.outOfStock
24+
}
25+
26+
27+
guard item.price <= coinsDeposited else {
28+
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
29+
}
30+
31+
32+
coinsDeposited -= item.price
33+
34+
35+
var newItem = item
36+
newItem.count -= 1
37+
inventory[name] = newItem
38+
39+
40+
print("Dispensing \(name)")
41+
}
42+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
Enum("VendingMachineError") {
2+
Case("invalidSelection")
3+
Case("insufficientFunds").associatedValue("coinsNeeded", type: "Int")
4+
Case("outOfStock")
5+
}
6+
7+
Class("VendingMachine") {
8+
Variable(.var, name: "inventory", equals: Literal.dictionary(Dictionary(uniqueKeysWithValues: [
9+
("Candy Bar", Item(price: 12, count: 7)),
10+
("Chips", Item(price: 10, count: 4)),
11+
("Pretzels", Item(price: 7, count: 11))
12+
])))
13+
Variable(.var, name: "coinsDeposited", equals: 0)
14+
15+
Function("vend"){
16+
Parameter("name", labeled: "itemNamed", type: "String")
17+
} _: {
18+
Guard("let item = inventory[itemNamed]") else: {
19+
Throw(
20+
EnumValue("VendingMachineError", case: "invalidSelection")
21+
)
22+
}
23+
Guard("item.count > 0") else: {
24+
Throw(
25+
EnumValue("VendingMachineError", case: "outOfStock")
26+
)
27+
}
28+
Guard("item.price <= coinsDeposited") else: {
29+
Throw(
30+
EnumValue("VendingMachineError", case: "insufficientFunds"){
31+
ParameterExp("coinsNeeded", value: Infix("-"){
32+
VariableExp("item").property("price")
33+
VariableExp("coinsDeposited")
34+
})
35+
}
36+
)
37+
}
38+
Infix("-=", "coinsDeposited", VariableExp("item").property("price"))
39+
Variable("newItem", equals: VariableExp("item"))
40+
Infix("-=", "newItem.count", 1)
41+
Assignment("inventory[itemNamed]", .ref("newItem"))
42+
Call("print", "Dispensing \\(itemNamed)")
43+
}
44+
}
45+
46+
47+
48+

Examples/Completed/concurrency/syntax.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
var vendingMachine = VendingMachine()
3+
vendingMachine.coinsDeposited = 8
4+
do {
5+
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
6+
print("Success! Yum.")
7+
} catch VendingMachineError.invalidSelection {
8+
print("Invalid Selection.")
9+
} catch VendingMachineError.outOfStock {
10+
print("Out of Stock.")
11+
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
12+
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
13+
} catch {
14+
print("Unexpected error: \(error).")
15+
}
16+
17+
func summarize(_ ratings: [Int]) throws(StatisticsError) {
18+
guard !ratings.isEmpty else { throw .noRatings }
19+
}
20+
21+
async let data = fetchUserData(id: 1)
22+
async let posts = fetchUserPosts(id: 1)
23+
let (fetchedData, fetchedPosts) = try await (data, posts)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
Variable(.var, name: "vendingMachine", equals: Init("VendingMachine"))
2+
Assignment("vendingMachine.coinsDeposited", Literal.integer(8))
3+
Do {
4+
Call("buyFavoriteSnack") {
5+
ParameterExp(name: "person", value: Literal.string("Alice"))
6+
ParameterExp(name: "vendingMachine", value: Literal.ref("vendingMachine"))
7+
}.throwing()
8+
Call("print") {
9+
ParameterExp(unlabeled: Literal.string("Success! Yum."))
10+
}
11+
} catch: {
12+
Catch(EnumCase("invalidSelection")) {
13+
Call("print") {
14+
ParameterExp(unlabeled: Literal.string("Invalid Selection."))
15+
}
16+
}
17+
Catch(EnumCase("outOfStock")) {
18+
Call("print") {
19+
ParameterExp(unlabeled: Literal.string("Out of Stock."))
20+
}
21+
}
22+
Catch(
23+
EnumCase("insufficientFunds").associatedValue(
24+
"coinsNeeded", type: "Int")
25+
) {
26+
Call("print") {
27+
ParameterExp(
28+
unlabeled: Literal.string(
29+
"Insufficient funds. Please insert an additional \\(coinsNeeded) coins."))
30+
}
31+
}
32+
Catch {
33+
Call("print") {
34+
ParameterExp(unlabeled: Literal.string("Unexpected error: \\(error)."))
35+
}
36+
}
37+
}
38+
39+
Function("summarize") {
40+
Parameter(name: "ratings", type: "[Int]")
41+
} _: {
42+
Guard{
43+
VariableExp("ratings").property("isEmpty").not()
44+
} else: {
45+
Throw(EnumCase("noRatings"))
46+
}
47+
}.throws("StatisticsError")
48+
49+
Do {
50+
Variable(.let, name: "data") {
51+
Call("fetchUserData") {
52+
ParameterExp(name: "id", value: Literal.integer(1))
53+
}
54+
}.async()
55+
Variable(.let, name: "posts") {
56+
Call("fetchUserPosts") {
57+
ParameterExp(name: "id", value: Literal.integer(1))
58+
}
59+
}.async()
60+
TupleAssignment(["fetchedData", "fetchedPosts"], equals: Tuple {
61+
VariableExp("data")
62+
VariableExp("posts")
63+
}).async().throwing()
64+
} catch: {
65+
Catch(EnumCase("fetchError")) {
66+
// Example catch for async/await
67+
}
68+
}
69+
70+
71+
72+
73+
74+

Examples/Completed/errors_async/syntax.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

Examples/Remaining/concurrency/code.swift

Lines changed: 0 additions & 87 deletions
This file was deleted.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
var vendingMachine = VendingMachine()
3+
vendingMachine.coinsDeposited = 8
4+
do {
5+
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
6+
print("Success! Yum.")
7+
} catch VendingMachineError.invalidSelection {
8+
print("Invalid Selection.")
9+
} catch VendingMachineError.outOfStock {
10+
print("Out of Stock.")
11+
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
12+
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
13+
} catch {
14+
print("Unexpected error: \(error).")
15+
}
16+
17+
func summarize(_ ratings: [Int]) throws(StatisticsError) {
18+
guard !ratings.isEmpty else { throw .noRatings }
19+
}
20+
21+
// MARK: - Task Groups
22+
func fetchMultipleUsers(ids: [Int]) async throws -> [String] {
23+
try await withThrowingTaskGroup(of: String.self) { group in
24+
for id in ids {
25+
group.addTask {
26+
try await fetchUserData(id: id)
27+
}
28+
}
29+
30+
var results: [String] = []
31+
for try await result in group {
32+
results.append(result)
33+
}
34+
return results
35+
}
36+
}
37+
38+
// Demonstrate concurrent tasks
39+
print("\nFetching user data and posts concurrently...")
40+
async let data = fetchUserData(id: 1)
41+
async let posts = fetchUserPosts(id: 1)
42+
let (fetchedData, fetchedPosts) = try await (data, posts)
43+
print("Data: \(fetchedData)")
44+
print("Posts: \(fetchedPosts)")
45+
46+
47+
Task {
48+
_ = try await fetchUserData(id: 1)
49+
}
50+
51+
Task { @MainActor [unowned self] in
52+
_ = try await fetchUserData(id: 1)
53+
}
54+
55+
56+
func nonThrowingFunction() throws(Never) {
57+
58+
}

Line.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
// Created by Leo Dion on 6/16/25.
66
//
77

8-
/// Represents a single comment line that can be attached to a syntax node when using `.comment { ... }` in the DSL.
8+
/// Represents a single comment line that can be attached to a syntax node when using
9+
/// `.comment { ... }` in the DSL.
910
public struct Line {
1011
public enum Kind {
1112
/// Regular line comment that starts with `//`.

0 commit comments

Comments
 (0)