Skip to content

Adding Concurrency and Error Throws #77

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ identifier_name:
excluded:
- id
- no
type_name:
excluded:
- If
- Do
excluded:
- DerivedData
- .build
Expand Down
42 changes: 42 additions & 0 deletions Examples/Completed/concurrency/code.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}

class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0


func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}


guard item.count > 0 else {
throw VendingMachineError.outOfStock
}


guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}


coinsDeposited -= item.price


var newItem = item
newItem.count -= 1
inventory[name] = newItem


print("Dispensing \(name)")
}
}
48 changes: 48 additions & 0 deletions Examples/Completed/concurrency/dsl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Enum("VendingMachineError") {
Case("invalidSelection")
Case("insufficientFunds").associatedValue("coinsNeeded", type: "Int")
Case("outOfStock")
}

Class("VendingMachine") {
Variable(.var, name: "inventory", equals: Literal.dictionary(Dictionary(uniqueKeysWithValues: [
("Candy Bar", Item(price: 12, count: 7)),
("Chips", Item(price: 10, count: 4)),
("Pretzels", Item(price: 7, count: 11))
])))
Variable(.var, name: "coinsDeposited", equals: 0)

Function("vend"){
Parameter("name", labeled: "itemNamed", type: "String")
} _: {
Guard("let item = inventory[itemNamed]") else: {
Throw(
EnumValue("VendingMachineError", case: "invalidSelection")
)
}
Guard("item.count > 0") else: {
Throw(
EnumValue("VendingMachineError", case: "outOfStock")
)
}
Guard("item.price <= coinsDeposited") else: {
Throw(
EnumValue("VendingMachineError", case: "insufficientFunds"){
ParameterExp("coinsNeeded", value: Infix("-"){
VariableExp("item").property("price")
VariableExp("coinsDeposited")
})
}
)
}
Infix("-=", "coinsDeposited", VariableExp("item").property("price"))
Variable("newItem", equals: VariableExp("item"))
Infix("-=", "newItem.count", 1)
Assignment("inventory[itemNamed]", .ref("newItem"))
Call("print", "Dispensing \\(itemNamed)")
}
}




1 change: 1 addition & 0 deletions Examples/Completed/concurrency/syntax.json

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions Examples/Completed/errors_async/code.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
print("Unexpected error: \(error).")
}

func summarize(_ ratings: [Int]) throws(StatisticsError) {
guard !ratings.isEmpty else { throw .noRatings }
}

async let data = fetchUserData(id: 1)
async let posts = fetchUserPosts(id: 1)
let (fetchedData, fetchedPosts) = try await (data, posts)
74 changes: 74 additions & 0 deletions Examples/Completed/errors_async/dsl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
Variable(.var, name: "vendingMachine", equals: Init("VendingMachine"))
Assignment("vendingMachine.coinsDeposited", Literal.integer(8))
Do {
Call("buyFavoriteSnack") {
ParameterExp(name: "person", value: Literal.string("Alice"))
ParameterExp(name: "vendingMachine", value: Literal.ref("vendingMachine"))
}.throwing()
Call("print") {
ParameterExp(unlabeled: Literal.string("Success! Yum."))
}
} catch: {
Catch(EnumCase("invalidSelection")) {
Call("print") {
ParameterExp(unlabeled: Literal.string("Invalid Selection."))
}
}
Catch(EnumCase("outOfStock")) {
Call("print") {
ParameterExp(unlabeled: Literal.string("Out of Stock."))
}
}
Catch(
EnumCase("insufficientFunds").associatedValue(
"coinsNeeded", type: "Int")
) {
Call("print") {
ParameterExp(
unlabeled: Literal.string(
"Insufficient funds. Please insert an additional \\(coinsNeeded) coins."))
}
}
Catch {
Call("print") {
ParameterExp(unlabeled: Literal.string("Unexpected error: \\(error)."))
}
}
}

Function("summarize") {
Parameter(name: "ratings", type: "[Int]")
} _: {
Guard{
VariableExp("ratings").property("isEmpty").not()
} else: {
Throw(EnumCase("noRatings"))
}
}.throws("StatisticsError")

Do {
Variable(.let, name: "data") {
Call("fetchUserData") {
ParameterExp(name: "id", value: Literal.integer(1))
}
}.async()
Variable(.let, name: "posts") {
Call("fetchUserPosts") {
ParameterExp(name: "id", value: Literal.integer(1))
}
}.async()
TupleAssignment(["fetchedData", "fetchedPosts"], equals: Tuple {
VariableExp("data")
VariableExp("posts")
}).async().throwing()
} catch: {
Catch(EnumCase("fetchError")) {
// Example catch for async/await
}
}






1 change: 1 addition & 0 deletions Examples/Completed/errors_async/syntax.json

Large diffs are not rendered by default.

87 changes: 0 additions & 87 deletions Examples/Remaining/concurrency/code.swift

This file was deleted.

58 changes: 58 additions & 0 deletions Examples/Remaining/result_builders/code.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
print("Unexpected error: \(error).")
}

func summarize(_ ratings: [Int]) throws(StatisticsError) {
guard !ratings.isEmpty else { throw .noRatings }
}

// MARK: - Task Groups
func fetchMultipleUsers(ids: [Int]) async throws -> [String] {
try await withThrowingTaskGroup(of: String.self) { group in
for id in ids {
group.addTask {
try await fetchUserData(id: id)
}
}

var results: [String] = []
for try await result in group {
results.append(result)
}
return results
}
}

// Demonstrate concurrent tasks
print("\nFetching user data and posts concurrently...")
async let data = fetchUserData(id: 1)
async let posts = fetchUserPosts(id: 1)
let (fetchedData, fetchedPosts) = try await (data, posts)
print("Data: \(fetchedData)")
print("Posts: \(fetchedPosts)")


Task {
_ = try await fetchUserData(id: 1)
}

Task { @MainActor [unowned self] in
_ = try await fetchUserData(id: 1)
}


func nonThrowingFunction() throws(Never) {

}
3 changes: 2 additions & 1 deletion Line.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// Created by Leo Dion on 6/16/25.
//

/// Represents a single comment line that can be attached to a syntax node when using `.comment { ... }` in the DSL.
/// Represents a single comment line that can be attached to a syntax node when using
/// `.comment { ... }` in the DSL.
public struct Line {
public enum Kind {
/// Regular line comment that starts with `//`.
Expand Down
3 changes: 2 additions & 1 deletion Sources/SyntaxKit/Assignment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public struct Assignment: CodeBlock {
elements: ExprListSyntax([
left,
ExprSyntax(
AssignmentExprSyntax(equal: .equalToken(leadingTrivia: .space, trailingTrivia: .space))),
AssignmentExprSyntax(equal: .equalToken(leadingTrivia: .space, trailingTrivia: .space))
),
right,
])
)
Expand Down
Loading
Loading