Skip to content

Commit 42a1131

Browse files
authored
Merge pull request #619 from DougGregor/operator-precedence
Introduce a library to handle user-defined operators
2 parents 0b9c005 + 7bcba76 commit 42a1131

16 files changed

+2438
-8
lines changed

Package.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ let package = Package(
4545
.macCatalyst(.v13),
4646
],
4747
products: [
48+
.library(name: "SwiftOperators", type: .static,
49+
targets: ["SwiftOperators"]),
4850
.library(name: "SwiftParser", type: .static, targets: ["SwiftParser"]),
4951
.library(name: "SwiftSyntax", type: .static, targets: ["SwiftSyntax"]),
5052
.library(name: "SwiftSyntaxParser", type: .static, targets: ["SwiftSyntaxParser"]),
@@ -118,13 +120,19 @@ let package = Package(
118120
"DeclarationAttribute.swift.gyb",
119121
]
120122
),
123+
.target(
124+
name: "SwiftOperators",
125+
dependencies: ["SwiftSyntax", "SwiftParser", "SwiftDiagnostics"]
126+
),
121127
.executableTarget(
122128
name: "lit-test-helper",
123129
dependencies: ["SwiftSyntax", "SwiftSyntaxParser"]
124130
),
125131
.executableTarget(
126132
name: "swift-parser-test",
127-
dependencies: ["SwiftDiagnostics", "SwiftSyntax", "SwiftParser", .product(name: "ArgumentParser", package: "swift-argument-parser")]
133+
dependencies: ["SwiftDiagnostics", "SwiftSyntax", "SwiftParser",
134+
"SwiftOperators",
135+
.product(name: "ArgumentParser", package: "swift-argument-parser")]
128136
),
129137
.executableTarget(
130138
name: "generate-swift-syntax-builder",
@@ -171,7 +179,13 @@ let package = Package(
171179
),
172180
.testTarget(
173181
name: "SwiftParserTest",
174-
dependencies: ["SwiftDiagnostics", "SwiftParser", "_SwiftSyntaxTestSupport"]
182+
dependencies: ["SwiftDiagnostics", "SwiftOperators", "SwiftParser",
183+
"_SwiftSyntaxTestSupport"]
184+
),
185+
.testTarget(
186+
name: "SwiftOperatorsTest",
187+
dependencies: ["SwiftOperators", "_SwiftSyntaxTestSupport",
188+
"SwiftParser"]
175189
),
176190
]
177191
)

Sources/SwiftOperators/Operator.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===------------------ Operator.swift ------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftSyntax
14+
15+
/// Names an operator.
16+
///
17+
/// TODO: For now, we'll use strings, but we likely want to move this to a
18+
/// general notion of an Identifier.
19+
public typealias OperatorName = String
20+
21+
/// Describes the kind of an operator.
22+
public enum OperatorKind: String {
23+
/// Infix operator such as the + in a + b.
24+
case infix
25+
26+
/// Prefix operator such as the - in -x.
27+
case prefix
28+
29+
/// Postfix operator such as the ! in x!.
30+
case postfix
31+
}
32+
33+
/// Describes an operator.
34+
public struct Operator {
35+
public let kind: OperatorKind
36+
public let name: OperatorName
37+
public let precedenceGroup: PrecedenceGroupName?
38+
public let syntax: OperatorDeclSyntax?
39+
40+
public init(
41+
kind: OperatorKind, name: OperatorName,
42+
precedenceGroup: PrecedenceGroupName?,
43+
syntax: OperatorDeclSyntax? = nil
44+
) {
45+
self.kind = kind
46+
self.name = name
47+
self.precedenceGroup = precedenceGroup
48+
self.syntax = syntax
49+
}
50+
}
51+
52+
extension Operator: CustomStringConvertible {
53+
/// The description of an operator is the source code that produces it.
54+
public var description: String {
55+
(syntax ?? synthesizedSyntax()).description
56+
}
57+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===------------------ OperatorError.swift ---------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftDiagnostics
14+
import SwiftParser
15+
import SwiftSyntax
16+
17+
extension OperatorError : DiagnosticMessage {
18+
public var severity: DiagnosticSeverity {
19+
.error
20+
}
21+
22+
public var message: String {
23+
switch self {
24+
case .groupAlreadyExists(let existing, _):
25+
return "redefinition of precedence group '\(existing.name)'"
26+
27+
case .missingGroup(let groupName, _):
28+
return "unknown precedence group '\(groupName)'"
29+
30+
case .operatorAlreadyExists(let existing, _):
31+
return "redefinition of \(existing.kind) operator '\(existing.name)'"
32+
33+
case .missingOperator(let operatorName, _):
34+
return "unknown infix operator '\(operatorName)'"
35+
36+
case .incomparableOperators(_, let leftGroup, _, let rightGroup):
37+
if leftGroup == rightGroup {
38+
return "adjacent operators are in non-associative precedence group '\(leftGroup)'"
39+
}
40+
41+
return "adjacent operators are in unordered precedence groups '\(leftGroup)' and '\(rightGroup)'"
42+
}
43+
}
44+
45+
public var diagnosticID: MessageID {
46+
MessageID(domain: "SwiftOperators", id: "\(self)")
47+
}
48+
}
49+
50+
extension OperatorError {
51+
/// Produce the syntax node at which a diagnostic should be displayed.
52+
var diagnosticDisplayNode: Syntax {
53+
switch self {
54+
case .incomparableOperators(let leftOperator, _, _, _):
55+
return Syntax(leftOperator)
56+
57+
case .missingOperator(_, let node):
58+
return node
59+
60+
case .operatorAlreadyExists(_, let newOperator):
61+
return Syntax(newOperator.syntax ?? newOperator.synthesizedSyntax())
62+
63+
case .missingGroup(_, let node):
64+
return node
65+
66+
case .groupAlreadyExists(_, let newGroup):
67+
return Syntax(newGroup.syntax ?? newGroup.synthesizedSyntax())
68+
}
69+
}
70+
71+
/// Produce a diagnostic for a given operator-precedence error.
72+
public var asDiagnostic: Diagnostic {
73+
.init(node: diagnosticDisplayNode, message: self)
74+
}
75+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===------------------ OperatorError.swift ---------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
import SwiftSyntax
13+
14+
/// Describes errors that can occur when working with user-defined operators.
15+
public enum OperatorError: Error {
16+
/// Error produced when a given precedence group already exists in the
17+
/// precedence graph.
18+
case groupAlreadyExists(existing: PrecedenceGroup, new: PrecedenceGroup)
19+
20+
/// The named precedence group is missing from the precedence graph.
21+
case missingGroup(PrecedenceGroupName, referencedFrom: Syntax)
22+
23+
/// Error produced when a given operator already exists.
24+
case operatorAlreadyExists(existing: Operator, new: Operator)
25+
26+
/// The named operator is missing from the precedence graph.
27+
case missingOperator(OperatorName, referencedFrom: Syntax)
28+
29+
/// No associativity relationship between operators.
30+
case incomparableOperators(
31+
leftOperator: ExprSyntax, leftPrecedenceGroup: PrecedenceGroupName,
32+
rightOperator: ExprSyntax, rightPrecedenceGroup: PrecedenceGroupName
33+
)
34+
}
35+
36+
/// A function that receives an operator precedence error and may do with it
37+
/// whatever it likes.
38+
///
39+
/// Operator precedence error handlers are passed into each function in the
40+
/// operator-precedence parser that can produce a failure. The handler
41+
/// may choose to throw (in which case the error will propagate outward) or
42+
/// may separately record/drop the error and return without throwing (in
43+
/// which case the operator-precedence parser will recover).
44+
public typealias OperatorErrorHandler =
45+
(OperatorError) throws -> Void

0 commit comments

Comments
 (0)