Skip to content

Instantly share code, notes, and snippets.

@harlanhaskins
Last active June 15, 2018 23:43
Show Gist options
  • Save harlanhaskins/af7b9a4408fcb631140a2534e069c92e to your computer and use it in GitHub Desktop.
Save harlanhaskins/af7b9a4408fcb631140a2534e069c92e to your computer and use it in GitHub Desktop.
import Foundation
import SwiftSyntax
/*:
# Syntax Rewriting
SwiftSyntax provides a class called `SyntaxRewriter`, which will walk a Syntax tree and
perform transformations over the nodes.
By default, these transformations don't do anything. It's the subclass's job to
choose which nodes it wants to transform, perform the transformations, and return
the result.
Provided below is a `SyntaxRewriter` that increments all the integers in a source file.
Notice that it overrides `visit(_ token: TokenSyntax)`. This means it will perform your
custom transformation on all the tokens in the file.
*/
/// Increments all integer literals present in a source file. This does not affect
/// integers present in the middle of string literals or in comments.
public class IncrementIntegers: SyntaxRewriter {
/// If the provided token is an integer literal, increments the underlying value
/// and transforms the token. Otherwise, does not modify the token.
/// - parameter token: The token to increment.
/// - returns: A new token with the integer value incremented, or the same token
/// if the provided token was not an intger literal.
public override func visit(_ token: TokenSyntax) -> Syntax {
/*:
First, make sure we're dealing with an integer literal. To do this,
inspect the _kind_ of the token. If it's anything other than
`integerLiteral`, then return the token unchanged.
*/
guard case .integerLiteral(let intToken) = token.tokenKind else {
return token
}
/*:
Next, make sure we can parse the integer literal using Swift's built-in
integer parsing. This should not ever fail, but if it does, return the
token unchanged.
*/
guard let integerValue = Int(intToken) else {
return token
}
/*:
Increment the integer.
*/
let newValue = integerValue + 1
/*:
Construct a new `TokenKind` with the `String`ified version of our new integer.
*/
let newKind = TokenKind.integerLiteral(newValue.description)
/*:
Finally, call `TokenSyntax`'s `withKind(_:)` method. This creates a duplicate
token with its underlying `kind` replaced, but preserving the leading and
trailing whitespace and comments.
*/
return token.withKind(newKind)
}
}
/*:
To use this class, parse a Swift file and then pass it to the class's `visit(_:)` method.
*/
let testFile = URL(fileURLWithPath: #file)
let sourceFile = try SourceFileSyntax.parse(testFile)
let rewriter = IncrementIntegers()
let rewritten = rewriter.visit(sourceFile)
print(rewritten)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment