Skip to content

Introduce a library to handle user-defined operators #619

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 50 commits into from
Sep 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
f74e98e
Operator-precedence parsing for sequence expressions
DougGregor Aug 4, 2022
679ad36
Use InfixOperatorExpr
DougGregor Aug 6, 2022
f4a47d3
Restrict access to a number of operator-precedence-related APIs
DougGregor Aug 6, 2022
b838b33
Add standard and logical operator definitions
DougGregor Aug 6, 2022
7240dd5
Use a syntax visitor for adding operators and prededence groups from …
DougGregor Aug 6, 2022
4950068
Don't use deprecated factory method
DougGregor Aug 17, 2022
a4336b1
Start tracking syntax nodes for operator and precedence group declara…
DougGregor Aug 17, 2022
284d90a
Report errors through an optionally-throwing error handler.
DougGregor Aug 17, 2022
1c3d0ec
Add tests for the various kinds of operator-precedence errors
DougGregor Aug 18, 2022
2dcafed
Diagnose incomparable operators without parentheses
DougGregor Aug 18, 2022
e30281a
Move operator-precedence parsing into its own module
DougGregor Aug 19, 2022
8e5c944
Split the operator-precedence module into many more files
DougGregor Aug 19, 2022
112b314
Add DocC documentation for the SwiftOperatorPrecedence library
DougGregor Aug 19, 2022
faf0c01
Implement a recursive "foldAll" operation to fold all sequences.
DougGregor Aug 20, 2022
3c27453
Make an OperatorPrecedenceError into a DiagnosticMessage.
DougGregor Aug 20, 2022
297c45e
Add infrastructure to make it easy to test the result of sequence fol…
DougGregor Aug 21, 2022
472020e
Adapt to upstream changes in diagnostics handling
DougGregor Aug 28, 2022
188bd7c
Fold the ternary operator a ? b : c.
DougGregor Aug 28, 2022
7ff59a4
Add sequence folding for the assignment operator.
DougGregor Aug 28, 2022
1beb841
Fold explicit casts
DougGregor Aug 28, 2022
96295ad
Add folding for the arrow operator
DougGregor Aug 28, 2022
67635c4
Account for the removal of XCTAssertSameStructure
DougGregor Sep 1, 2022
867e812
Make the declaration of the default operator precedence properties si…
DougGregor Sep 1, 2022
498bde9
Hide the `OperatorPrecedence.record` APIs. We don't need them.
DougGregor Sep 1, 2022
fb2e36a
Factor out the precedence relation search into a separate function
DougGregor Sep 1, 2022
dabf779
Teach precedence.precedence(...) to look in both directions
DougGregor Sep 2, 2022
230287f
Remove unused function
DougGregor Sep 2, 2022
76c71db
Improve naming and documentation for internal operations.
DougGregor Sep 2, 2022
aaef6e7
Use a switch over SyntaxEnum to clean up the semantic check
DougGregor Sep 2, 2022
9b71480
Minor cleanup
DougGregor Sep 2, 2022
39ef90c
Eliminate the use of the existential SyntaxProtocol
DougGregor Sep 2, 2022
4076969
Document foldAll behavior that calls error handler twice.
DougGregor Sep 6, 2022
28d2918
Rename strange-sounding "group syntax" to "operator syntax".
DougGregor Sep 6, 2022
b3c3bd1
Rename OperatorPrecedence to OperatorTable and improve its documentat…
DougGregor Sep 6, 2022
5e7b0a6
Record prefix and postfix operators in the operator table.
DougGregor Sep 6, 2022
45b0b8a
Rename several source files to reflect the new "OperatorTable" name
DougGregor Sep 6, 2022
b94546c
Refer to OperatorTable in the documentation
DougGregor Sep 6, 2022
f620763
Use reflection to derive diagnostic IDs
DougGregor Sep 6, 2022
9e4c0ce
Rename SwiftOperatorPrecedence -> SwiftOperators
DougGregor Sep 6, 2022
4dc0f3d
Rename OperatorPrecedenceError -> OperatorError.
DougGregor Sep 6, 2022
2dd5885
Add the ability to synthesize syntax for an operator
DougGregor Sep 6, 2022
7d43bb1
Synthesize syntax nodes for operator and precedence groups
DougGregor Sep 7, 2022
b67fdaa
Make Operator, PrecedenceGroup, and OperatorTable CustomStringConvert…
DougGregor Sep 7, 2022
c22124e
Make all Syntax non-optional in OperatorError cases.
DougGregor Sep 7, 2022
95461b5
Fix file name and add license header
DougGregor Sep 7, 2022
280bf24
Add `--fold-sequences` operation to swift-parser-test
DougGregor Sep 7, 2022
1c17c41
Return `Syntax`, rather than `SyntaxProtocol`, from `foldAll`
DougGregor Sep 7, 2022
5efa052
Hoist "try" and "await" expressions during folding.
DougGregor Sep 12, 2022
7c4b3fe
Work around debug-info assertion in optimized builds
DougGregor Sep 16, 2022
7bcba76
Adjust for refactoring of precedence group declarations
DougGregor Sep 16, 2022
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
18 changes: 16 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ let package = Package(
.macCatalyst(.v13),
],
products: [
.library(name: "SwiftOperators", type: .static,
targets: ["SwiftOperators"]),
.library(name: "SwiftParser", type: .static, targets: ["SwiftParser"]),
.library(name: "SwiftSyntax", type: .static, targets: ["SwiftSyntax"]),
.library(name: "SwiftSyntaxParser", type: .static, targets: ["SwiftSyntaxParser"]),
Expand Down Expand Up @@ -118,13 +120,19 @@ let package = Package(
"DeclarationAttribute.swift.gyb",
]
),
.target(
name: "SwiftOperators",
dependencies: ["SwiftSyntax", "SwiftParser", "SwiftDiagnostics"]
),
.executableTarget(
name: "lit-test-helper",
dependencies: ["SwiftSyntax", "SwiftSyntaxParser"]
),
.executableTarget(
name: "swift-parser-test",
dependencies: ["SwiftDiagnostics", "SwiftSyntax", "SwiftParser", .product(name: "ArgumentParser", package: "swift-argument-parser")]
dependencies: ["SwiftDiagnostics", "SwiftSyntax", "SwiftParser",
"SwiftOperators",
.product(name: "ArgumentParser", package: "swift-argument-parser")]
),
.executableTarget(
name: "generate-swift-syntax-builder",
Expand Down Expand Up @@ -171,7 +179,13 @@ let package = Package(
),
.testTarget(
name: "SwiftParserTest",
dependencies: ["SwiftDiagnostics", "SwiftParser", "_SwiftSyntaxTestSupport"]
dependencies: ["SwiftDiagnostics", "SwiftOperators", "SwiftParser",
"_SwiftSyntaxTestSupport"]
),
.testTarget(
name: "SwiftOperatorsTest",
dependencies: ["SwiftOperators", "_SwiftSyntaxTestSupport",
"SwiftParser"]
),
]
)
Expand Down
57 changes: 57 additions & 0 deletions Sources/SwiftOperators/Operator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===------------------ Operator.swift ------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax

/// Names an operator.
///
/// TODO: For now, we'll use strings, but we likely want to move this to a
/// general notion of an Identifier.
public typealias OperatorName = String

/// Describes the kind of an operator.
public enum OperatorKind: String {
/// Infix operator such as the + in a + b.
case infix

/// Prefix operator such as the - in -x.
case prefix

/// Postfix operator such as the ! in x!.
case postfix
}

/// Describes an operator.
public struct Operator {
public let kind: OperatorKind
public let name: OperatorName
public let precedenceGroup: PrecedenceGroupName?
public let syntax: OperatorDeclSyntax?

public init(
kind: OperatorKind, name: OperatorName,
precedenceGroup: PrecedenceGroupName?,
syntax: OperatorDeclSyntax? = nil
) {
self.kind = kind
self.name = name
self.precedenceGroup = precedenceGroup
self.syntax = syntax
}
}

extension Operator: CustomStringConvertible {
/// The description of an operator is the source code that produces it.
public var description: String {
(syntax ?? synthesizedSyntax()).description
}
}
75 changes: 75 additions & 0 deletions Sources/SwiftOperators/OperatorError+Diagnostics.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===------------------ OperatorError.swift ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftDiagnostics
import SwiftParser
import SwiftSyntax

extension OperatorError : DiagnosticMessage {
public var severity: DiagnosticSeverity {
.error
}

public var message: String {
switch self {
case .groupAlreadyExists(let existing, _):
return "redefinition of precedence group '\(existing.name)'"

case .missingGroup(let groupName, _):
return "unknown precedence group '\(groupName)'"

case .operatorAlreadyExists(let existing, _):
return "redefinition of \(existing.kind) operator '\(existing.name)'"

case .missingOperator(let operatorName, _):
return "unknown infix operator '\(operatorName)'"

case .incomparableOperators(_, let leftGroup, _, let rightGroup):
if leftGroup == rightGroup {
return "adjacent operators are in non-associative precedence group '\(leftGroup)'"
}

return "adjacent operators are in unordered precedence groups '\(leftGroup)' and '\(rightGroup)'"
}
}

public var diagnosticID: MessageID {
MessageID(domain: "SwiftOperators", id: "\(self)")
}
}

extension OperatorError {
/// Produce the syntax node at which a diagnostic should be displayed.
var diagnosticDisplayNode: Syntax {
switch self {
case .incomparableOperators(let leftOperator, _, _, _):
return Syntax(leftOperator)

case .missingOperator(_, let node):
return node

case .operatorAlreadyExists(_, let newOperator):
return Syntax(newOperator.syntax ?? newOperator.synthesizedSyntax())

case .missingGroup(_, let node):
return node

case .groupAlreadyExists(_, let newGroup):
return Syntax(newGroup.syntax ?? newGroup.synthesizedSyntax())
}
}

/// Produce a diagnostic for a given operator-precedence error.
public var asDiagnostic: Diagnostic {
.init(node: diagnosticDisplayNode, message: self)
}
}
45 changes: 45 additions & 0 deletions Sources/SwiftOperators/OperatorError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//===------------------ OperatorError.swift ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftSyntax

/// Describes errors that can occur when working with user-defined operators.
public enum OperatorError: Error {
/// Error produced when a given precedence group already exists in the
/// precedence graph.
case groupAlreadyExists(existing: PrecedenceGroup, new: PrecedenceGroup)

/// The named precedence group is missing from the precedence graph.
case missingGroup(PrecedenceGroupName, referencedFrom: Syntax)

/// Error produced when a given operator already exists.
case operatorAlreadyExists(existing: Operator, new: Operator)

/// The named operator is missing from the precedence graph.
case missingOperator(OperatorName, referencedFrom: Syntax)

/// No associativity relationship between operators.
case incomparableOperators(
leftOperator: ExprSyntax, leftPrecedenceGroup: PrecedenceGroupName,
rightOperator: ExprSyntax, rightPrecedenceGroup: PrecedenceGroupName
)
}

/// A function that receives an operator precedence error and may do with it
/// whatever it likes.
///
/// Operator precedence error handlers are passed into each function in the
/// operator-precedence parser that can produce a failure. The handler
/// may choose to throw (in which case the error will propagate outward) or
/// may separately record/drop the error and return without throwing (in
/// which case the operator-precedence parser will recover).
public typealias OperatorErrorHandler =
(OperatorError) throws -> Void
Loading