mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
Updated to version 0.4
This commit is contained in:
+32
-16
@@ -2,7 +2,7 @@
|
||||
// SwiftFormat
|
||||
// main.swift
|
||||
//
|
||||
// Version 0.3
|
||||
// Version 0.4
|
||||
//
|
||||
// Created by Nick Lockwood on 12/08/2016.
|
||||
// Copyright 2016 Charcoal Design
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
let version = "0.3"
|
||||
let version = "0.4"
|
||||
|
||||
func processInput(inputURL: NSURL, andWriteToOutput outputURL: NSURL, withOptions options: FormattingOptions) -> Int {
|
||||
let manager = NSFileManager.defaultManager()
|
||||
@@ -144,22 +144,23 @@ func processArguments(args: [String]) {
|
||||
guard let args = preprocessArguments(args, [
|
||||
"output",
|
||||
"indent",
|
||||
"semicolons",
|
||||
"help",
|
||||
"version",
|
||||
]) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Version
|
||||
if args["version"] != nil {
|
||||
print("swiftformat, version \(version)")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Get input / output paths
|
||||
let inputPath = args["1"]
|
||||
let outputPath = args["output"] ?? inputPath
|
||||
|
||||
var inputPath = args["1"]
|
||||
var outputPath = args["output"] ?? inputPath
|
||||
|
||||
// Show help if requested specifically or if no arguments are passed
|
||||
if args["help"] != nil || inputPath == nil {
|
||||
print("swiftformat, version \(version)")
|
||||
@@ -167,22 +168,27 @@ func processArguments(args: [String]) {
|
||||
print("")
|
||||
print("usage: swiftformat <file> [-o path] [-i spaces]")
|
||||
print("")
|
||||
print(" <file> input file or directory path")
|
||||
print(" -o, --output output path (defaults to input path)")
|
||||
print(" -i, --indent number of spaces to indent, or \"tab\" to use tabs")
|
||||
print(" -h, --help this help page")
|
||||
print(" -v, --version version information")
|
||||
print(" <file> input file or directory path")
|
||||
print(" -o, --output output path (defaults to input path)")
|
||||
print(" -i, --indent number of spaces to indent, or \"tab\" to use tabs")
|
||||
print(" -s, --semicolons allow semicolons. values are \"never\" or \"inline\" (default)")
|
||||
print(" -h, --help this help page")
|
||||
print(" -v, --version version information")
|
||||
print("")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
print("running swiftformat...")
|
||||
|
||||
|
||||
// Expand input / output paths
|
||||
inputPath = NSString(string: inputPath!).stringByExpandingTildeInPath
|
||||
outputPath = NSString(string: outputPath!).stringByExpandingTildeInPath
|
||||
|
||||
// Convert paths to file URLs relative to current directory
|
||||
let directoryURL = NSURL(fileURLWithPath: NSFileManager.defaultManager().currentDirectoryPath)
|
||||
let inputURL = NSURL(fileURLWithPath: inputPath!, relativeToURL: directoryURL)
|
||||
let outputURL = NSURL(fileURLWithPath: outputPath!, relativeToURL: directoryURL)
|
||||
|
||||
|
||||
// Get options
|
||||
var options = FormattingOptions()
|
||||
if let indent = args["indent"] {
|
||||
@@ -195,7 +201,17 @@ func processArguments(args: [String]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if let semicolons = args["semicolons"] {
|
||||
if semicolons == "inline" {
|
||||
options.allowInlineSemicolons = true
|
||||
} else if semicolons == "never" {
|
||||
options.allowInlineSemicolons = false
|
||||
} else {
|
||||
print("error: unsupported semicolons value: \(semicolons).")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Format the code
|
||||
let filesWritten = processInput(inputURL, andWriteToOutput: outputURL, withOptions: options)
|
||||
print("swiftformat completed. \(filesWritten) file(s) updated.")
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
SwiftFormat
|
||||
|
||||
Version 0.3, August 23rd, 2016
|
||||
Version 0.4, August 24th, 2016
|
||||
|
||||
Copyright (c) 2016 Nick Lockwood
|
||||
|
||||
|
||||
@@ -176,6 +176,16 @@ Here are all the rules that SwiftFormat currently applies:
|
||||
|
||||
// MARK - UIScrollViewDelegate --> // MARK: - UIScrollViewDelegate
|
||||
|
||||
*semicolons* - removes semicolons at the end of lines and (optionally) replaces inline semicolons with a linebreak:
|
||||
|
||||
let foo = 5; --> let foo = 5
|
||||
|
||||
let foo = 5; let bar = 6 --> let foo = 5
|
||||
let bar = 6
|
||||
|
||||
return; --> return;
|
||||
goto(fail) goto(fail)
|
||||
|
||||
|
||||
FAQ
|
||||
-----
|
||||
@@ -296,16 +306,25 @@ Or begin each line with a `*` (or any other non-whitespace character)
|
||||
What's next?
|
||||
--------------
|
||||
|
||||
I expect people will discover (and hopefully report) a lot of bugs in this first release, so the next step will be to fix all of those.
|
||||
|
||||
There are a bunch of additional rules I'd like to add, such as removing trailing semicolons, or correctly formatting headerdoc comments.
|
||||
There are a bunch of additional rules I'd like to add, such as correctly formatting headerdoc comments.
|
||||
|
||||
At some point I should probably add an intermediate parsing stage that identifies high-level constructs such as classes and functions and assembles them into a syntax tree. I did't bother doing this originally because I thought it would be easier to implement formatting at the token level, but in fact this just meant that the logic for distinguishing between syntax constructs had to be split between the tokenizer and the formatting rules, making both of them more complex than they ought to be.
|
||||
|
||||
With a syntax tree in place, it should become possible to add much more sophisticated rules, such as converting uppercase enums to lowercase for Swift 3, etc.
|
||||
|
||||
|
||||
Release notes
|
||||
----------------
|
||||
|
||||
Version 0.4
|
||||
|
||||
- Added new `semicolons` rule, which removes semicolons wherever it's safe to do so
|
||||
- Added `--semicolons` command-line argument for enabling inline semicolon stripping
|
||||
- The `todos` rule now corrects `MARK :` to `MARK:` instead of `MARK: :`
|
||||
- Paths containing ~ are now handled correctly by the command line tool
|
||||
- Fixed some bugs in generics and custom operator parsing, and added more tests
|
||||
- Removed trailing whitespace on blank lines caused by the `indent` rule
|
||||
|
||||
Version 0.3
|
||||
|
||||
- Fixed several cases where generics were misidentified as operators
|
||||
|
||||
+196
-33
@@ -2,7 +2,7 @@
|
||||
// SwiftFormat
|
||||
// Formatter.swift
|
||||
//
|
||||
// Version 0.3
|
||||
// Version 0.4
|
||||
//
|
||||
// Created by Nick Lockwood on 12/08/2016.
|
||||
// Copyright 2016 Charcoal Design
|
||||
@@ -36,7 +36,15 @@ import Foundation
|
||||
/// Configuration options for formatting. These aren't actually used by the
|
||||
/// Formatter class itself, but it makes them available to the format rules.
|
||||
public struct FormattingOptions {
|
||||
var indent: String = " "
|
||||
public var indent: String
|
||||
public var allowInlineSemicolons: Bool
|
||||
|
||||
public init(indent: String = " ",
|
||||
allowInlineSemicolons: Bool = true) {
|
||||
|
||||
self.indent = indent
|
||||
self.allowInlineSemicolons = allowInlineSemicolons
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a utility class used for manipulating a tokenized source file.
|
||||
@@ -48,20 +56,20 @@ public struct FormattingOptions {
|
||||
public class Formatter {
|
||||
private(set) var tokens: [Token]
|
||||
let options: FormattingOptions
|
||||
|
||||
|
||||
private var indexStack: [Int] = []
|
||||
|
||||
|
||||
init(_ tokens: [Token], options: FormattingOptions) {
|
||||
self.tokens = tokens
|
||||
self.options = options
|
||||
}
|
||||
|
||||
|
||||
/// Returns the token at the specified index, or nil if index is invalid
|
||||
public func tokenAtIndex(index: Int) -> Token? {
|
||||
guard index >= 0 && index < tokens.count else { return nil }
|
||||
return tokens[index]
|
||||
}
|
||||
|
||||
|
||||
/// Replaces the token at the specified index with one or more new tokens
|
||||
public func replaceTokenAtIndex(index: Int, with tokens: Token...) {
|
||||
if tokens.count == 0 {
|
||||
@@ -73,7 +81,7 @@ public class Formatter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Replaces the tokens in the specified range with new tokens
|
||||
public func replaceTokensInRange(range: Range<Int>, with tokens: Token...) {
|
||||
let max = min(range.count, tokens.count)
|
||||
@@ -90,7 +98,7 @@ public class Formatter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Removes the token at the specified indez
|
||||
public func removeTokenAtIndex(index: Int) {
|
||||
tokens.removeAtIndex(index)
|
||||
@@ -100,17 +108,17 @@ public class Formatter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Removes the tokens in the specified range
|
||||
public func removeTokensInRange(range: Range<Int>) {
|
||||
replaceTokensInRange(range)
|
||||
}
|
||||
|
||||
|
||||
/// Removes the last token
|
||||
public func removeLastToken() {
|
||||
tokens.removeLast()
|
||||
}
|
||||
|
||||
|
||||
/// Inserts a tokens at the specified index
|
||||
public func insertToken(token: Token, atIndex index: Int) {
|
||||
tokens.insert(token, atIndex: index)
|
||||
@@ -120,7 +128,7 @@ public class Formatter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Loops through each token in the array. It is safe to mutate the token
|
||||
/// array inside the body block, but note that the index and token arguments
|
||||
/// may not reflect the current token any more after a mutation
|
||||
@@ -134,19 +142,19 @@ public class Formatter {
|
||||
}
|
||||
indexStack.popLast()
|
||||
}
|
||||
|
||||
|
||||
/// As above, but only loops through tokens with the specified type
|
||||
public func forEachToken(ofType type: TokenType, _ body: (Int, Token) -> Void) {
|
||||
forEachToken(matching: { $0.type == type }, body)
|
||||
}
|
||||
|
||||
|
||||
/// As above, but only loops through tokens with the specified type and string
|
||||
public func forEachToken(string: String, ofType type: TokenType, _ body: (Int, Token) -> Void) {
|
||||
forEachToken(matching: {
|
||||
return $0.type == type && $0.string == string
|
||||
}, body)
|
||||
}
|
||||
|
||||
|
||||
/// As above, but only loops through tokens with the specified string.
|
||||
/// Tokens of type `StringBody` and `CommentBody` are ignored, as these
|
||||
/// can't be usefully identified by their string value
|
||||
@@ -155,7 +163,7 @@ public class Formatter {
|
||||
return $0.string == string && $0.type != .StringBody && $0.type != .CommentBody
|
||||
}, body)
|
||||
}
|
||||
|
||||
|
||||
private func forEachToken(matching condition: (Token) -> Bool, _ body: (Int, Token) -> Void) {
|
||||
forEachToken { index, token in
|
||||
if condition(token) {
|
||||
@@ -200,7 +208,7 @@ public func spaceAroundParens(formatter: Formatter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
formatter.forEachToken("(") { i, token in
|
||||
guard let previousToken = formatter.tokenAtIndex(i - 1) else {
|
||||
return
|
||||
@@ -268,7 +276,7 @@ public func spaceAroundBrackets(formatter: Formatter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
formatter.forEachToken("[") { i, token in
|
||||
guard let previousToken = formatter.tokenAtIndex(i - 1) else {
|
||||
return
|
||||
@@ -396,7 +404,7 @@ public func spaceAroundOperators(formatter: Formatter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func isRvalue(token: Token) -> Bool {
|
||||
switch token.type {
|
||||
case .Identifier, .Number, .StartOfScope:
|
||||
@@ -405,7 +413,7 @@ public func spaceAroundOperators(formatter: Formatter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func spaceAfter(identifier: String) -> Bool {
|
||||
switch identifier {
|
||||
case "case",
|
||||
@@ -423,7 +431,7 @@ public func spaceAroundOperators(formatter: Formatter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var scopeStack: [Token] = []
|
||||
formatter.forEachToken { i, token in
|
||||
switch token.type {
|
||||
@@ -595,7 +603,7 @@ public func indent(formatter: Formatter) {
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
|
||||
func nextNonWhitespaceToken(fromIndex index: Int) -> Token? {
|
||||
var index = index
|
||||
while let token = formatter.tokenAtIndex(index) {
|
||||
@@ -606,7 +614,7 @@ public func indent(formatter: Formatter) {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func setIndent(indent: String, atIndex index: Int) {
|
||||
if formatter.tokenAtIndex(index)?.type == .Whitespace {
|
||||
if indent != "" {
|
||||
@@ -618,7 +626,7 @@ public func indent(formatter: Formatter) {
|
||||
formatter.insertToken(Token(.Whitespace, indent), atIndex: index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var scopeIndexStack: [Int] = []
|
||||
var scopeStartLineIndexes: [Int] = []
|
||||
var lastNonWhitespaceOrLinebreakIndex = -1
|
||||
@@ -626,14 +634,14 @@ public func indent(formatter: Formatter) {
|
||||
var indentStack = [""]
|
||||
var lineIndex = 0
|
||||
var linewrapped = false
|
||||
|
||||
|
||||
func currentScope() -> Token? {
|
||||
if let scopeIndex = scopeIndexStack.last {
|
||||
return formatter.tokens[scopeIndex]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func tokenIsEndOfStatement(i: Int) -> Bool {
|
||||
if let token = formatter.tokenAtIndex(i) {
|
||||
switch token.type {
|
||||
@@ -703,7 +711,7 @@ public func indent(formatter: Formatter) {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func tokenIsStartOfStatement(i: Int) -> Bool {
|
||||
if let token = formatter.tokenAtIndex(i) {
|
||||
switch token.type {
|
||||
@@ -744,7 +752,7 @@ public func indent(formatter: Formatter) {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
formatter.forEachToken { i, token in
|
||||
if token.type == .StartOfScope {
|
||||
// Handle start of scope
|
||||
@@ -831,8 +839,12 @@ public func indent(formatter: Formatter) {
|
||||
indentStack.append(indentStack.last ?? "")
|
||||
}
|
||||
lineIndex += 1
|
||||
let indent = (indentStack.last ?? "") + (linewrapped ? formatter.options.indent : "")
|
||||
setIndent(indent, atIndex: i + 1)
|
||||
setIndent("", atIndex: i + 1)
|
||||
// Only indent if line isn't blank
|
||||
if let nextToken = formatter.tokenAtIndex(i + 1) where nextToken.type != .Linebreak {
|
||||
let indent = (indentStack.last ?? "") + (linewrapped ? formatter.options.indent : "")
|
||||
setIndent(indent, atIndex: i + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Track token for line wraps
|
||||
@@ -953,7 +965,161 @@ public func todos(formatter: Formatter) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove semicolons, except where doing so would change the meaning of the code
|
||||
public func semicolons(formatter: Formatter) {
|
||||
func firstNonWhitespaceOrComment(fromIndex index: Int) -> Token? {
|
||||
var i = index
|
||||
var scopeStack: [Token] = []
|
||||
while let token = formatter.tokenAtIndex(i) {
|
||||
if let scope = scopeStack.last {
|
||||
if token.closesScopeForToken(scope) {
|
||||
scopeStack.popLast()
|
||||
if token.type == .Linebreak {
|
||||
return token
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch token.type {
|
||||
case .Whitespace:
|
||||
break
|
||||
case .StartOfScope:
|
||||
if token.string == "/*" || token.string == "//" {
|
||||
scopeStack.append(token)
|
||||
} else {
|
||||
return token
|
||||
}
|
||||
default:
|
||||
return token
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func firstNonWhitespaceOrCommentOrLinebreak(fromIndex index: Int) -> Token? {
|
||||
var i = index
|
||||
var scopeStack: [Token] = []
|
||||
while let token = formatter.tokenAtIndex(i) {
|
||||
if let scope = scopeStack.last {
|
||||
if token.closesScopeForToken(scope) {
|
||||
scopeStack.popLast()
|
||||
}
|
||||
} else {
|
||||
switch token.type {
|
||||
case .Whitespace, .Linebreak:
|
||||
break
|
||||
case .StartOfScope:
|
||||
if token.string == "/*" || token.string == "//" {
|
||||
scopeStack.append(token)
|
||||
} else {
|
||||
return token
|
||||
}
|
||||
default:
|
||||
return token
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lastNonWhitespaceOrCommentOrLinebreak(fromIndex index: Int) -> Token? {
|
||||
var i = index
|
||||
var scopeStack: [Token] = []
|
||||
while let token = formatter.tokenAtIndex(i) {
|
||||
if let scope = scopeStack.last {
|
||||
if token.type == .StartOfScope && scope.closesScopeForToken(token) {
|
||||
scopeStack.popLast()
|
||||
} else {
|
||||
return token
|
||||
}
|
||||
} else {
|
||||
switch token.type {
|
||||
case .Whitespace, .Linebreak:
|
||||
break
|
||||
case .EndOfScope:
|
||||
if token.string == "*/" {
|
||||
scopeStack.append(token)
|
||||
} else {
|
||||
return token
|
||||
}
|
||||
default:
|
||||
return token
|
||||
}
|
||||
}
|
||||
i -= 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func currentScopeAtIndex(index: Int) -> Token? {
|
||||
var i = index
|
||||
var scopeStack: [Token] = []
|
||||
while let token = formatter.tokenAtIndex(i) {
|
||||
if token.type == .StartOfScope {
|
||||
if let scope = scopeStack.last where scope.closesScopeForToken(token) {
|
||||
scopeStack.popLast()
|
||||
} else {
|
||||
return token
|
||||
}
|
||||
} else if token.type == .EndOfScope {
|
||||
scopeStack.append(token)
|
||||
}
|
||||
i -= 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func indentAtIndex(index: Int) -> Token? {
|
||||
var i = index
|
||||
while let token = formatter.tokenAtIndex(i) {
|
||||
if token.type == .Linebreak {
|
||||
break
|
||||
}
|
||||
i -= 1
|
||||
}
|
||||
if let token = formatter.tokenAtIndex(i + 1) {
|
||||
if token.type == .Whitespace {
|
||||
return token
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
formatter.forEachToken(";") { i, token in
|
||||
if let nextToken = firstNonWhitespaceOrCommentOrLinebreak(fromIndex: i + 1) {
|
||||
let lastToken = lastNonWhitespaceOrCommentOrLinebreak(fromIndex: i - 1)
|
||||
if lastToken == nil || nextToken.string == "}" {
|
||||
// Safe to remove
|
||||
formatter.removeTokenAtIndex(i)
|
||||
} else if lastToken?.string == "return" || currentScopeAtIndex(i)?.string == "(" {
|
||||
// Not safe to remove or replace
|
||||
} else if firstNonWhitespaceOrComment(fromIndex: i + 1)?.type == .Linebreak {
|
||||
// Safe to remove
|
||||
formatter.removeTokenAtIndex(i)
|
||||
} else if !formatter.options.allowInlineSemicolons {
|
||||
// Replace with a linebreak
|
||||
if formatter.tokenAtIndex(i + 1)?.type == .Whitespace {
|
||||
formatter.removeTokenAtIndex(i + 1)
|
||||
}
|
||||
if let indent = indentAtIndex(i) {
|
||||
formatter.insertToken(indent, atIndex: i + 1)
|
||||
}
|
||||
formatter.replaceTokenAtIndex(i, with: Token(.Linebreak, "\n"))
|
||||
}
|
||||
} else {
|
||||
// Safe to remove
|
||||
formatter.removeTokenAtIndex(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public let defaultRules: [FormatRule] = [
|
||||
semicolons,
|
||||
knrBraces,
|
||||
elseOnSameLine,
|
||||
indent,
|
||||
spaceAroundParens,
|
||||
spaceInsideParens,
|
||||
spaceAroundBrackets,
|
||||
@@ -967,9 +1133,6 @@ public let defaultRules: [FormatRule] = [
|
||||
noTrailingWhitespace,
|
||||
noConsecutiveBlankLines,
|
||||
linebreakAtEndOfFile,
|
||||
indent,
|
||||
knrBraces,
|
||||
elseOnSameLine,
|
||||
trailingCommas,
|
||||
todos,
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SwiftFormat
|
||||
// SwiftFormat.h
|
||||
//
|
||||
// Version 0.3
|
||||
// Version 0.4
|
||||
//
|
||||
// Created by Nick Lockwood on 12/08/2016.
|
||||
// Copyright 2016 Charcoal Design
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SwiftFormat
|
||||
// SwiftFormat.swift
|
||||
//
|
||||
// Version 0.3
|
||||
// Version 0.4
|
||||
//
|
||||
// Created by Nick Lockwood on 12/08/2016.
|
||||
// Copyright 2016 Charcoal Design
|
||||
@@ -37,15 +37,15 @@ import Foundation
|
||||
public func format(source: String,
|
||||
rules: [FormatRule] = defaultRules,
|
||||
options: FormattingOptions = FormattingOptions()) -> String {
|
||||
|
||||
|
||||
// Parse
|
||||
var tokens = tokenize(source)
|
||||
|
||||
|
||||
// Format
|
||||
let formatter = Formatter(tokens, options: options)
|
||||
rules.forEach { $0(formatter) }
|
||||
tokens = formatter.tokens
|
||||
|
||||
|
||||
// Output
|
||||
return tokens.reduce("", combine: { $0 + $1.string })
|
||||
}
|
||||
|
||||
+39
-39
@@ -2,7 +2,7 @@
|
||||
// SwiftFormat
|
||||
// Tokenizer.swift
|
||||
//
|
||||
// Version 0.3
|
||||
// Version 0.4
|
||||
//
|
||||
// Created by Nick Lockwood on 11/08/2016.
|
||||
// Copyright 2016 Charcoal Design
|
||||
@@ -50,12 +50,12 @@ public enum TokenType {
|
||||
public struct Token: Equatable {
|
||||
public let type: TokenType
|
||||
public let string: String
|
||||
|
||||
|
||||
public init(_ type: TokenType, _ string: String) {
|
||||
self.type = type
|
||||
self.string = string
|
||||
}
|
||||
|
||||
|
||||
public var isWhitespaceOrComment: Bool {
|
||||
switch type {
|
||||
case .Whitespace, .CommentBody:
|
||||
@@ -68,11 +68,11 @@ public struct Token: Equatable {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var isWhitespaceOrCommentOrLinebreak: Bool {
|
||||
return type == .Linebreak || isWhitespaceOrComment
|
||||
}
|
||||
|
||||
|
||||
public func closesScopeForToken(token: Token) -> Bool {
|
||||
guard type != .StringBody && type != .CommentBody else {
|
||||
return false
|
||||
@@ -105,17 +105,17 @@ public func ==(lhs: Token, rhs: Token) -> Bool {
|
||||
}
|
||||
|
||||
private extension Character {
|
||||
|
||||
|
||||
var unicodeValue: UInt32 {
|
||||
return String(self).unicodeScalars.first?.value ?? 0
|
||||
}
|
||||
|
||||
|
||||
var isAlpha: Bool { return isalpha(Int32(unicodeValue)) > 0 }
|
||||
var isDigit: Bool { return isdigit(Int32(unicodeValue)) > 0 }
|
||||
}
|
||||
|
||||
private extension String.CharacterView {
|
||||
|
||||
|
||||
mutating func scanCharacter(matching: (Character) -> Bool) -> String? {
|
||||
if let c = first where matching(c) {
|
||||
self = suffixFrom(startIndex.advancedBy(1))
|
||||
@@ -123,7 +123,7 @@ private extension String.CharacterView {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func scanString(matching string: String) -> String? {
|
||||
if startsWith(string.characters) {
|
||||
self = suffixFrom(startIndex.advancedBy(string.characters.count))
|
||||
@@ -131,7 +131,7 @@ private extension String.CharacterView {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func scanCharacters(matching: (Character) -> Bool) -> String? {
|
||||
var index = endIndex
|
||||
for (i, c) in enumerate() {
|
||||
@@ -147,49 +147,49 @@ private extension String.CharacterView {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func scanInteger() -> String? {
|
||||
return scanCharacters({ $0.isDigit })
|
||||
}
|
||||
}
|
||||
|
||||
private extension String.CharacterView {
|
||||
|
||||
|
||||
mutating func parseToken(type: TokenType, _ character: Character) -> Token? {
|
||||
if let _ = scanCharacter({ $0 == character }) {
|
||||
return Token(type, String(character))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parseToken(type: TokenType, _ string: String) -> Token? {
|
||||
if let string = scanString(matching: string) {
|
||||
return Token(type, string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parseToken(type: TokenType, oneOf characters: String.CharacterView) -> Token? {
|
||||
if let string = scanCharacter({ characters.contains($0) }) {
|
||||
return Token(type, String(string))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parseToken(type: TokenType, _ characters: String.CharacterView) -> Token? {
|
||||
if let string = scanCharacters({ characters.contains($0) }) {
|
||||
return Token(type, string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parseToken(type: TokenType, upTo characters: String.CharacterView) -> Token? {
|
||||
if let string = scanCharacters({ !characters.contains($0) }) {
|
||||
return Token(type, string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parseToken(type: TokenType, upTo character: Character) -> Token? {
|
||||
if let string = scanCharacters({ $0 != character }) {
|
||||
return Token(type, string)
|
||||
@@ -199,11 +199,11 @@ private extension String.CharacterView {
|
||||
}
|
||||
|
||||
private extension String.CharacterView {
|
||||
|
||||
|
||||
mutating func parseWhitespace() -> Token? {
|
||||
return parseToken(.Whitespace, " \t".characters) // TODO: vertical tab
|
||||
}
|
||||
|
||||
|
||||
mutating func parseOperator() -> Token? {
|
||||
func isHead(c: Character) -> Bool {
|
||||
if "./=-+!*%<>&|^~?".characters.contains(c) {
|
||||
@@ -230,7 +230,7 @@ private extension String.CharacterView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func isTail(c: Character) -> Bool {
|
||||
if isHead(c) {
|
||||
return true
|
||||
@@ -247,7 +247,7 @@ private extension String.CharacterView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if var tail = scanCharacter(isHead) {
|
||||
var head = ""
|
||||
while let c = scanCharacter(isTail) {
|
||||
@@ -274,19 +274,19 @@ private extension String.CharacterView {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parsePunctuation() -> Token? {
|
||||
return parseToken(.Operator, ":;,".characters)
|
||||
}
|
||||
|
||||
|
||||
mutating func parseStartOfScope() -> Token? {
|
||||
return parseToken(.StartOfScope, oneOf: "([{\"".characters)
|
||||
}
|
||||
|
||||
|
||||
mutating func parseEndOfScope() -> Token? {
|
||||
return parseToken(.EndOfScope, oneOf: "}])".characters)
|
||||
}
|
||||
|
||||
|
||||
mutating func parseIdentifier() -> Token? {
|
||||
func isHead(c: Character) -> Bool {
|
||||
if c.isAlpha || c == "_" || c == "$" {
|
||||
@@ -344,7 +344,7 @@ private extension String.CharacterView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func isTail(c: Character) -> Bool {
|
||||
if isHead(c) || c.isDigit {
|
||||
return true
|
||||
@@ -359,7 +359,7 @@ private extension String.CharacterView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func scanIdentifier() -> String? {
|
||||
if let head = scanCharacter({ isHead($0) || $0 == "@" || $0 == "#" }) {
|
||||
if let tail = scanCharacters({ isTail($0) }) {
|
||||
@@ -369,7 +369,7 @@ private extension String.CharacterView {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let start = self
|
||||
if scanCharacter({ $0 == "`" }) != nil {
|
||||
if let identifier = scanIdentifier() {
|
||||
@@ -389,7 +389,7 @@ private extension String.CharacterView {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parseNumber() -> Token? {
|
||||
var number = ""
|
||||
if let integer = scanInteger() {
|
||||
@@ -415,7 +415,7 @@ private extension String.CharacterView {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
mutating func parseLineBreak() -> Token? {
|
||||
if scanCharacter({ $0 == "\r" }) != nil {
|
||||
if scanCharacter({ $0 == "\n" }) != nil {
|
||||
@@ -425,7 +425,7 @@ private extension String.CharacterView {
|
||||
}
|
||||
return parseToken(.Linebreak, "\n")
|
||||
}
|
||||
|
||||
|
||||
mutating func parseToken() -> Token? {
|
||||
// Have to split into groups for Swift to be able to process this
|
||||
if let token = parseWhitespace() ??
|
||||
@@ -453,7 +453,7 @@ func tokenize(source: String) -> [Token] {
|
||||
var characters = source.characters
|
||||
var lastNonWhitespaceIndex: Int?
|
||||
var closedGenericScopeIndexes: [Int] = []
|
||||
|
||||
|
||||
func processStringBody() {
|
||||
var string = ""
|
||||
var escaped = false
|
||||
@@ -487,7 +487,7 @@ func tokenize(source: String) -> [Token] {
|
||||
string += c
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func processCommentBody() {
|
||||
var comment = ""
|
||||
while let c = characters.scanCharacter({ _ in true }) {
|
||||
@@ -528,7 +528,7 @@ func tokenize(source: String) -> [Token] {
|
||||
comment += c
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func processToken() {
|
||||
let token = tokens.last!
|
||||
if token.type != .Whitespace {
|
||||
@@ -548,7 +548,7 @@ func tokenize(source: String) -> [Token] {
|
||||
case .StartOfScope:
|
||||
wasOperator = (token.string == "\"")
|
||||
case .Operator:
|
||||
wasOperator = !["->", ">", ",", ":", ";", "?", "!", "."].contains(token.string)
|
||||
wasOperator = !["=", "->", ">", ",", ":", ";", "?", "!", "."].contains(token.string)
|
||||
default:
|
||||
wasOperator = false
|
||||
}
|
||||
@@ -596,7 +596,7 @@ func tokenize(source: String) -> [Token] {
|
||||
processToken()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
} else if scopeIndexStack.last != nil && tokens[scopeIndexStack.last!].string == "\"" {
|
||||
processStringBody()
|
||||
}
|
||||
@@ -654,18 +654,18 @@ func tokenize(source: String) -> [Token] {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while let token = characters.parseToken() {
|
||||
tokens.append(token)
|
||||
processToken()
|
||||
}
|
||||
|
||||
|
||||
if let scopeIndex = scopeIndexStack.last where tokens[scopeIndex].string == "<" {
|
||||
// If we encountered an end-of-file while a generic scope was
|
||||
// still open, the opening < must have been an operator
|
||||
tokens[scopeIndex] = Token(.Operator, "<")
|
||||
scopeIndexStack.popLast()
|
||||
}
|
||||
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
// SwiftFormat
|
||||
// TokenizerTests.swift
|
||||
//
|
||||
// Version 0.3
|
||||
// Version 0.4
|
||||
//
|
||||
// Created by Nick Lockwood on 12/08/2016.
|
||||
// Copyright 2016 Charcoal Design
|
||||
@@ -39,9 +39,9 @@ func XCTAssertEqualArrays<T: Equatable>(first: [T], _ second: [T]) {
|
||||
}
|
||||
|
||||
class TokenizerTests: XCTestCase {
|
||||
|
||||
|
||||
// MARK: Strings
|
||||
|
||||
|
||||
func testEmptyString() {
|
||||
let input = "\"\""
|
||||
let output = [
|
||||
@@ -50,7 +50,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testSimpleString() {
|
||||
let input = "\"foo\""
|
||||
let output = [
|
||||
@@ -60,7 +60,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testStringWithEscape() {
|
||||
let input = "\"hello\\tworld\""
|
||||
let output = [
|
||||
@@ -70,7 +70,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testStringWithEscapedQuotes() {
|
||||
let input = "\"\\\"nice\\\" to meet you\""
|
||||
let output = [
|
||||
@@ -80,7 +80,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testStringWithEscapedLogic() {
|
||||
let input = "\"hello \\(name)\""
|
||||
let output = [
|
||||
@@ -93,7 +93,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testStringWithEscapedBackslash() {
|
||||
let input = "\"\\\\\""
|
||||
let output = [
|
||||
@@ -103,9 +103,9 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Single-line comments
|
||||
|
||||
|
||||
func testSingleLineComment() {
|
||||
let input = "//foo"
|
||||
let output = [
|
||||
@@ -114,7 +114,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testSingleLineCommentWithSpace() {
|
||||
let input = "// foo"
|
||||
let output = [
|
||||
@@ -124,7 +124,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testSingleLineCommentWithLinebreak() {
|
||||
let input = "//foo\nbar"
|
||||
let output = [
|
||||
@@ -135,9 +135,9 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Multiline comments
|
||||
|
||||
|
||||
func testSingleLineMultilineComment() {
|
||||
let input = "/*foo*/"
|
||||
let output = [
|
||||
@@ -147,7 +147,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testSingleLineMultilineCommentWithSpace() {
|
||||
let input = "/* foo*/"
|
||||
let output = [
|
||||
@@ -158,7 +158,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testMultilineComment() {
|
||||
let input = "/*foo\nbar*/"
|
||||
let output = [
|
||||
@@ -170,7 +170,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testMultilineCommentWithWhitespace() {
|
||||
let input = "/*foo\n bar*/"
|
||||
let output = [
|
||||
@@ -183,7 +183,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testNestedComments() {
|
||||
let input = "/*foo/*bar*/baz*/"
|
||||
let output = [
|
||||
@@ -197,27 +197,27 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Numbers
|
||||
|
||||
|
||||
func testZero() {
|
||||
let input = "0"
|
||||
let output = [Token(.Number, "0")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testSmallInteger() {
|
||||
let input = "5"
|
||||
let output = [Token(.Number, "5")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testLargeInteger() {
|
||||
let input = "12345678901234567890"
|
||||
let output = [Token(.Number, "12345678901234567890")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testNegativeInteger() {
|
||||
let input = "-7"
|
||||
let output = [
|
||||
@@ -226,19 +226,19 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testSmallFloat() {
|
||||
let input = "0.2"
|
||||
let output = [Token(.Number, "0.2")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testLargeFloat() {
|
||||
let input = "1234.567890"
|
||||
let output = [Token(.Number, "1234.567890")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testNegativeFloat() {
|
||||
let input = "-0.34"
|
||||
let output = [
|
||||
@@ -247,114 +247,114 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testExponential() {
|
||||
let input = "1234e5"
|
||||
let output = [Token(.Number, "1234e5")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testPositiveExponential() {
|
||||
let input = "0.123e+4"
|
||||
let output = [Token(.Number, "0.123e+4")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testNegativeExponential() {
|
||||
let input = "0.123e-4"
|
||||
let output = [Token(.Number, "0.123e-4")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testCapitalExponential() {
|
||||
let input = "0.123E-4"
|
||||
let output = [Token(.Number, "0.123E-4")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Identifiers
|
||||
|
||||
|
||||
func testFoo() {
|
||||
let input = "foo"
|
||||
let output = [Token(.Identifier, "foo")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testDollar0() {
|
||||
let input = "$0"
|
||||
let output = [Token(.Identifier, "$0")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testDollar() {
|
||||
// Note: support for this is deprecated in Swift 3
|
||||
let input = "$"
|
||||
let output = [Token(.Identifier, "$")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testFooDollar() {
|
||||
let input = "foo$"
|
||||
let output = [Token(.Identifier, "foo$")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func test_() {
|
||||
let input = "_"
|
||||
let output = [Token(.Identifier, "_")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func test_foo() {
|
||||
let input = "_foo"
|
||||
let output = [Token(.Identifier, "_foo")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testFoo_bar() {
|
||||
let input = "foo_bar"
|
||||
let output = [Token(.Identifier, "foo_bar")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testAtFoo() {
|
||||
let input = "@foo"
|
||||
let output = [Token(.Identifier, "@foo")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testHashFoo() {
|
||||
let input = "#foo"
|
||||
let output = [Token(.Identifier, "#foo")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testUnicode() {
|
||||
let input = "µsec"
|
||||
let output = [Token(.Identifier, "µsec")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testEmoji() {
|
||||
let input = "💩"
|
||||
let output = [Token(.Identifier, "💩")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testBacktickEscapedClass() {
|
||||
let input = "`class`"
|
||||
let output = [Token(.Identifier, "`class`")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Operators
|
||||
|
||||
|
||||
func testBasicOperator() {
|
||||
let input = "+="
|
||||
let output = [Token(.Operator, "+=")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testDivide() {
|
||||
let input = "a / b"
|
||||
let output = [
|
||||
@@ -366,13 +366,13 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testCustomOperator() {
|
||||
let input = "~="
|
||||
let output = [Token(.Operator, "~=")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testSequentialOperators() {
|
||||
let input = "a *= -b"
|
||||
let output = [
|
||||
@@ -385,19 +385,19 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testDotPrefixedOperator() {
|
||||
let input = "..."
|
||||
let output = [Token(.Operator, "...")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testUnicodeOperator() {
|
||||
let input = "≥"
|
||||
let output = [Token(.Operator, "≥")]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testOperatorFollowedByComment() {
|
||||
let input = "a +/* b */"
|
||||
let output = [
|
||||
@@ -411,7 +411,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testOperatorPrecededByComment() {
|
||||
let input = "/* a */-b"
|
||||
let output = [
|
||||
@@ -424,9 +424,9 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
// MARK: chevrons (might be operators or generics)
|
||||
|
||||
|
||||
func testLessThanGreaterThan() {
|
||||
let input = "a<b == a>c"
|
||||
let output = [
|
||||
@@ -442,7 +442,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testBitshift() {
|
||||
let input = "a>>b"
|
||||
let output = [
|
||||
@@ -452,7 +452,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testTripleShift() {
|
||||
let input = "a>>>b"
|
||||
let output = [
|
||||
@@ -462,7 +462,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testTripleShiftEquals() {
|
||||
let input = "a>>=b"
|
||||
let output = [
|
||||
@@ -472,7 +472,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testBitshiftThatLooksLikeAGeneric() {
|
||||
let input = "a<b, b<c, d>>e"
|
||||
let output = [
|
||||
@@ -492,7 +492,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testBasicGeneric() {
|
||||
let input = "Foo<Bar, Baz>"
|
||||
let output = [
|
||||
@@ -506,7 +506,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testNestedGenerics() {
|
||||
let input = "Foo<Bar<Baz>>"
|
||||
let output = [
|
||||
@@ -520,7 +520,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testFunctionThatLooksLikeGenericType() {
|
||||
let input = "y<CGRectGetMaxY(r)"
|
||||
let output = [
|
||||
@@ -533,7 +533,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericClassDeclaration() {
|
||||
let input = "class Foo<T,U> {}"
|
||||
let output = [
|
||||
@@ -551,7 +551,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericSubclassDeclaration() {
|
||||
let input = "class Foo<T,U>: Bar"
|
||||
let output = [
|
||||
@@ -569,7 +569,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericFunctionDeclaration() {
|
||||
let input = "func foo<T>(bar:T)"
|
||||
let output = [
|
||||
@@ -587,7 +587,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericClassInit() {
|
||||
let input = "foo = Foo<Int,String>()"
|
||||
let output = [
|
||||
@@ -606,7 +606,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericFollowedByDot() {
|
||||
let input = "Foo<Bar>.baz()"
|
||||
let output = [
|
||||
@@ -621,7 +621,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testConstantThatLooksLikeGenericType() {
|
||||
let input = "(y<Pi)"
|
||||
let output = [
|
||||
@@ -633,7 +633,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testTupleOfBoolsThatLooksLikeGeneric() {
|
||||
let input = "(Foo<T,U>V)"
|
||||
let output = [
|
||||
@@ -649,7 +649,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericClassInitThatLooksLikeTuple() {
|
||||
let input = "(Foo<String,Int>(Bar))"
|
||||
let output = [
|
||||
@@ -667,7 +667,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testCustomChevronOperatorThatLooksLikeGeneric() {
|
||||
let input = "Foo<Bar,Baz>>>5"
|
||||
let output = [
|
||||
@@ -681,7 +681,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericAsFunctionType() {
|
||||
let input = "Foo<Bar,Baz>->Void"
|
||||
let output = [
|
||||
@@ -696,7 +696,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericContainingArrayType() {
|
||||
let input = "Foo<[Bar],Baz>"
|
||||
let output = [
|
||||
@@ -711,7 +711,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericContainingTupleType() {
|
||||
let input = "Foo<(Bar,Baz)>"
|
||||
let output = [
|
||||
@@ -726,7 +726,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericContainingArrayAndTupleType() {
|
||||
let input = "Foo<[Bar],(Baz)>"
|
||||
let output = [
|
||||
@@ -743,7 +743,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGenericFollowedByIn() {
|
||||
let input = "Foo<Bar,Baz> in"
|
||||
let output = [
|
||||
@@ -758,7 +758,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testOptionalGenericType() {
|
||||
let input = "Foo<T?,U>"
|
||||
let output = [
|
||||
@@ -772,7 +772,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testTrailingOptionalGenericType() {
|
||||
let input = "Foo<T?>"
|
||||
let output = [
|
||||
@@ -784,7 +784,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testNestedOptionalGenericType() {
|
||||
let input = "Foo<Bar<T?>>"
|
||||
let output = [
|
||||
@@ -799,7 +799,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testCustomOperatorStartingWithOpenChevron() {
|
||||
let input = "foo<--bar"
|
||||
let output = [
|
||||
@@ -809,7 +809,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testCustomOperatorEndingWithCloseChevron() {
|
||||
let input = "foo-->bar"
|
||||
let output = [
|
||||
@@ -819,7 +819,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testGreaterThanLessThanOperator() {
|
||||
let input = "foo><bar"
|
||||
let output = [
|
||||
@@ -829,7 +829,7 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
|
||||
func testLessThanGreaterThanOperator() {
|
||||
let input = "foo<>bar"
|
||||
let output = [
|
||||
@@ -839,4 +839,24 @@ class TokenizerTests: XCTestCase {
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
|
||||
func testGenericFollowedByAssign() {
|
||||
let input = "let foo: Bar<Baz> = 5"
|
||||
let output = [
|
||||
Token(.Identifier, "let"),
|
||||
Token(.Whitespace, " "),
|
||||
Token(.Identifier, "foo"),
|
||||
Token(.Operator, ":"),
|
||||
Token(.Whitespace, " "),
|
||||
Token(.Identifier, "Bar"),
|
||||
Token(.StartOfScope, "<"),
|
||||
Token(.Identifier, "Baz"),
|
||||
Token(.EndOfScope, ">"),
|
||||
Token(.Whitespace, " "),
|
||||
Token(.Operator, "="),
|
||||
Token(.Whitespace, " "),
|
||||
Token(.Number, "5"),
|
||||
]
|
||||
XCTAssertEqualArrays(tokenize(input), output)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user