Merge pull request #2107 from ewanmellor/swift-fix-deprecated-string-methods
Remove use of deprecated String functions
This commit is contained in:
commit
84e9a46932
|
@ -6,7 +6,7 @@ set -euo pipefail
|
||||||
# here since environment variables doesn't pass
|
# here since environment variables doesn't pass
|
||||||
# across scripts
|
# across scripts
|
||||||
if [ $TRAVIS_OS_NAME == "linux" ]; then
|
if [ $TRAVIS_OS_NAME == "linux" ]; then
|
||||||
export SWIFT_VERSION=swift-4.0
|
export SWIFT_VERSION=swift-4.0.2
|
||||||
export SWIFT_HOME=$(pwd)/swift/$SWIFT_VERSION-RELEASE-ubuntu14.04/usr/bin/
|
export SWIFT_HOME=$(pwd)/swift/$SWIFT_VERSION-RELEASE-ubuntu14.04/usr/bin/
|
||||||
export PATH=$SWIFT_HOME:$PATH
|
export PATH=$SWIFT_HOME:$PATH
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,8 @@ public class ANTLRInputStream: CharStream {
|
||||||
/// Copy data in string to a local char array
|
/// Copy data in string to a local char array
|
||||||
///
|
///
|
||||||
public init(_ input: String) {
|
public init(_ input: String) {
|
||||||
self.data = Array(input.characters) // input.toCharArray();
|
self.data = Array(input)
|
||||||
self.n = input.length
|
self.n = data.count
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
|
@ -441,14 +441,13 @@ public class BufferedTokenStream: TokenStream {
|
||||||
/// Collect all hidden tokens (any off-default channel) to the left of
|
/// Collect all hidden tokens (any off-default channel) to the left of
|
||||||
/// the current token up until we see a token on DEFAULT_TOKEN_CHANNEL.
|
/// the current token up until we see a token on DEFAULT_TOKEN_CHANNEL.
|
||||||
///
|
///
|
||||||
public func getHiddenTokensToLeft(_ tokenIndex: Int) throws -> Array<Token>? {
|
public func getHiddenTokensToLeft(_ tokenIndex: Int) throws -> [Token]? {
|
||||||
return try getHiddenTokensToLeft(tokenIndex, -1)
|
return try getHiddenTokensToLeft(tokenIndex, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func filterForChannel(_ from: Int, _ to: Int, _ channel: Int) -> Array<Token>? {
|
internal func filterForChannel(_ from: Int, _ to: Int, _ channel: Int) -> [Token]? {
|
||||||
var hidden: Array<Token> = Array<Token>()
|
var hidden = [Token]()
|
||||||
for i in from...to {
|
for t in tokens[from...to] {
|
||||||
let t: Token = tokens[i]
|
|
||||||
if channel == -1 {
|
if channel == -1 {
|
||||||
if t.getChannel() != Lexer.DEFAULT_TOKEN_CHANNEL {
|
if t.getChannel() != Lexer.DEFAULT_TOKEN_CHANNEL {
|
||||||
hidden.append(t)
|
hidden.append(t)
|
||||||
|
@ -478,8 +477,8 @@ public class BufferedTokenStream: TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getText(_ interval: Interval) throws -> String {
|
public func getText(_ interval: Interval) throws -> String {
|
||||||
let start: Int = interval.a
|
let start = interval.a
|
||||||
var stop: Int = interval.b
|
var stop = interval.b
|
||||||
if start < 0 || stop < 0 {
|
if start < 0 || stop < 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -488,15 +487,14 @@ public class BufferedTokenStream: TokenStream {
|
||||||
stop = tokens.count - 1
|
stop = tokens.count - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let buf: StringBuilder = StringBuilder()
|
var buf = ""
|
||||||
for i in start...stop {
|
for t in tokens[start...stop] {
|
||||||
let t: Token = tokens[i]
|
|
||||||
if t.getType() == BufferedTokenStream.EOF {
|
if t.getType() == BufferedTokenStream.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
buf.append(t.getText()!)
|
buf += t.getText()!
|
||||||
}
|
}
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -518,9 +516,9 @@ public class BufferedTokenStream: TokenStream {
|
||||||
///
|
///
|
||||||
public func fill() throws {
|
public func fill() throws {
|
||||||
try lazyInit()
|
try lazyInit()
|
||||||
let blockSize: Int = 1000
|
let blockSize = 1000
|
||||||
while true {
|
while true {
|
||||||
let fetched: Int = try fetch(blockSize)
|
let fetched = try fetch(blockSize)
|
||||||
if fetched < blockSize {
|
if fetched < blockSize {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -413,29 +413,27 @@ open class Lexer: Recognizer<LexerATNSimulator>, TokenSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
open func getErrorDisplay(_ s: String) -> String {
|
open func getErrorDisplay(_ s: String) -> String {
|
||||||
let buf = StringBuilder()
|
var buf = ""
|
||||||
for c in s.characters {
|
for c in s {
|
||||||
buf.append(getErrorDisplay(c))
|
buf += getErrorDisplay(c)
|
||||||
}
|
}
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
open func getErrorDisplay(_ c: Character) -> String {
|
open func getErrorDisplay(_ c: Character) -> String {
|
||||||
var s = String(c)
|
|
||||||
if c.integerValue == CommonToken.EOF {
|
if c.integerValue == CommonToken.EOF {
|
||||||
s = "<EOF>"
|
return "<EOF>"
|
||||||
}
|
}
|
||||||
switch s {
|
switch c {
|
||||||
case "\n":
|
case "\n":
|
||||||
s = "\\n"
|
return "\\n"
|
||||||
case "\t":
|
case "\t":
|
||||||
s = "\\t"
|
return "\\t"
|
||||||
case "\r":
|
case "\r":
|
||||||
s = "\\r"
|
return "\\r"
|
||||||
default:
|
default:
|
||||||
break
|
return String(c)
|
||||||
}
|
}
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open func getCharErrorDisplay(_ c: Character) -> String {
|
open func getCharErrorDisplay(_ c: Character) -> String {
|
||||||
|
|
|
@ -85,9 +85,8 @@ public class ListTokenSource: TokenSource {
|
||||||
let lastToken = tokens[tokens.count - 1]
|
let lastToken = tokens[tokens.count - 1]
|
||||||
|
|
||||||
if let tokenText = lastToken.getText() {
|
if let tokenText = lastToken.getText() {
|
||||||
let lastNewLine = tokenText.lastIndexOf("\n")
|
if let lastNewLine = tokenText.lastIndex(of: "\n") {
|
||||||
if lastNewLine >= 0 {
|
return tokenText.distance(from: lastNewLine, to: tokenText.endIndex) - 1
|
||||||
return tokenText.length - lastNewLine - 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (lastToken.getCharPositionInLine() +
|
return (lastToken.getCharPositionInLine() +
|
||||||
|
@ -142,9 +141,8 @@ public class ListTokenSource: TokenSource {
|
||||||
var line = lastToken.getLine()
|
var line = lastToken.getLine()
|
||||||
|
|
||||||
if let tokenText = lastToken.getText() {
|
if let tokenText = lastToken.getText() {
|
||||||
let length = tokenText.length
|
for c in tokenText {
|
||||||
for j in 0..<length {
|
if c == "\n" {
|
||||||
if String(tokenText[j]) == "\n" {
|
|
||||||
line += 1
|
line += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -429,7 +429,7 @@ open class Parser: Recognizer<ParserATNSimulator> {
|
||||||
if result == nil {
|
if result == nil {
|
||||||
let deserializationOptions = ATNDeserializationOptions()
|
let deserializationOptions = ATNDeserializationOptions()
|
||||||
try! deserializationOptions.setGenerateRuleBypassTransitions(true)
|
try! deserializationOptions.setGenerateRuleBypassTransitions(true)
|
||||||
result = try! ATNDeserializer(deserializationOptions).deserialize(Array(serializedAtn.characters))
|
result = try! ATNDeserializer(deserializationOptions).deserialize(Array(serializedAtn))
|
||||||
self.bypassAltsAtnCache[serializedAtn] = result!
|
self.bypassAltsAtnCache[serializedAtn] = result!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,12 +132,12 @@ open class RuleContext: RuleNode {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
let builder = StringBuilder()
|
var builder = ""
|
||||||
for i in 0..<length {
|
for i in 0..<length {
|
||||||
builder.append((getChild(i) as! ParseTree).getText())
|
builder += (getChild(i) as! ParseTree).getText()
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.toString()
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
open func getRuleIndex() -> Int {
|
open func getRuleIndex() -> Int {
|
||||||
|
@ -202,31 +202,31 @@ open class RuleContext: RuleNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
open func toString(_ ruleNames: [String]?, _ stop: RuleContext?) -> String {
|
open func toString(_ ruleNames: [String]?, _ stop: RuleContext?) -> String {
|
||||||
let buf = StringBuilder()
|
var buf = ""
|
||||||
var p: RuleContext? = self
|
var p: RuleContext? = self
|
||||||
buf.append("[")
|
buf += "["
|
||||||
while let pWrap = p, pWrap !== stop {
|
while let pWrap = p, pWrap !== stop {
|
||||||
if let ruleNames = ruleNames {
|
if let ruleNames = ruleNames {
|
||||||
let ruleIndex = pWrap.getRuleIndex()
|
let ruleIndex = pWrap.getRuleIndex()
|
||||||
let ruleIndexInRange = (ruleIndex >= 0 && ruleIndex < ruleNames.count)
|
let ruleIndexInRange = (ruleIndex >= 0 && ruleIndex < ruleNames.count)
|
||||||
let ruleName = (ruleIndexInRange ? ruleNames[ruleIndex] : String(ruleIndex))
|
let ruleName = (ruleIndexInRange ? ruleNames[ruleIndex] : String(ruleIndex))
|
||||||
buf.append(ruleName)
|
buf += ruleName
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if !pWrap.isEmpty() {
|
if !pWrap.isEmpty() {
|
||||||
buf.append(pWrap.invokingState)
|
buf += String(pWrap.invokingState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pWrap.parent != nil && (ruleNames != nil || !pWrap.parent!.isEmpty()) {
|
if pWrap.parent != nil && (ruleNames != nil || !pWrap.parent!.isEmpty()) {
|
||||||
buf.append(" ")
|
buf += " "
|
||||||
}
|
}
|
||||||
|
|
||||||
p = pWrap.parent
|
p = pWrap.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.append("]")
|
buf += "]"
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
open func castdown<T>(_ subType: T.Type) -> T {
|
open func castdown<T>(_ subType: T.Type) -> T {
|
||||||
|
|
|
@ -163,18 +163,14 @@ public class RuntimeMetaData {
|
||||||
/// only the major and minor components of the version string.
|
/// only the major and minor components of the version string.
|
||||||
///
|
///
|
||||||
public static func getMajorMinorVersion(_ version: String) -> String {
|
public static func getMajorMinorVersion(_ version: String) -> String {
|
||||||
let firstDot: Int = version.indexOf(".")
|
var result = version
|
||||||
let secondDot: Int = firstDot >= 0 ? version.indexOf(".", startIndex: firstDot + 1) : -1
|
|
||||||
let firstDash: Int = version.indexOf("-")
|
let dotBits = version.split(separator: ".", maxSplits: 2, omittingEmptySubsequences: false)
|
||||||
var referenceLength: Int = version.length
|
if dotBits.count >= 2 {
|
||||||
if secondDot >= 0 {
|
result = dotBits[0..<2].joined(separator: ".")
|
||||||
referenceLength = min(referenceLength, secondDot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if firstDash >= 0 {
|
let dashBits = result.split(separator: "-", maxSplits: 1, omittingEmptySubsequences: false)
|
||||||
referenceLength = min(referenceLength, firstDash)
|
return String(dashBits[0])
|
||||||
}
|
|
||||||
|
|
||||||
return version[0 ..< referenceLength] // version.substring(0, referenceLength);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,19 +84,19 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class TokenStreamRewriter {
|
public class TokenStreamRewriter {
|
||||||
public let DEFAULT_PROGRAM_NAME: String = "default"
|
public let DEFAULT_PROGRAM_NAME = "default"
|
||||||
public static let PROGRAM_INIT_SIZE: Int = 100
|
public static let PROGRAM_INIT_SIZE = 100
|
||||||
public static let MIN_TOKEN_INDEX: Int = 0
|
public static let MIN_TOKEN_INDEX = 0
|
||||||
|
|
||||||
// Define the rewrite operation hierarchy
|
// Define the rewrite operation hierarchy
|
||||||
public class RewriteOperation: CustomStringConvertible {
|
public class RewriteOperation: CustomStringConvertible {
|
||||||
/// What index into rewrites List are we?
|
/// What index into rewrites List are we?
|
||||||
internal var instructionIndex: Int = 0
|
internal var instructionIndex = 0
|
||||||
/// Token buffer index.
|
/// Token buffer index.
|
||||||
internal var index: Int
|
internal var index: Int
|
||||||
internal var text: String?
|
internal var text: String?
|
||||||
internal var lastIndex: Int = 0
|
internal var lastIndex = 0
|
||||||
internal weak var tokens: TokenStream!
|
internal weak var tokens: TokenStream!
|
||||||
|
|
||||||
init(_ index: Int, _ tokens: TokenStream) {
|
init(_ index: Int, _ tokens: TokenStream) {
|
||||||
self.index = index
|
self.index = index
|
||||||
|
@ -112,7 +112,7 @@ public class TokenStreamRewriter {
|
||||||
/// Execute the rewrite operation by possibly adding to the buffer.
|
/// Execute the rewrite operation by possibly adding to the buffer.
|
||||||
/// Return the index of the next token to operate on.
|
/// Return the index of the next token to operate on.
|
||||||
///
|
///
|
||||||
public func execute(_ buf: StringBuilder) throws -> Int {
|
public func execute(_ buf: inout String) throws -> Int {
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,16 +123,13 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InsertBeforeOp: RewriteOperation {
|
public class InsertBeforeOp: RewriteOperation {
|
||||||
public override init(_ index: Int, _ text: String?, _ tokens: TokenStream) {
|
override public func execute(_ buf: inout String) throws -> Int {
|
||||||
super.init(index, text, tokens)
|
if let text = text {
|
||||||
}
|
buf.append(text)
|
||||||
|
|
||||||
override public func execute(_ buf: StringBuilder) throws -> Int {
|
|
||||||
if text != nil {
|
|
||||||
buf.append(text!)
|
|
||||||
}
|
}
|
||||||
if try tokens.get(index).getType() != CommonToken.EOF {
|
let token = try tokens.get(index)
|
||||||
buf.append(try tokens.get(index).getText()!)
|
if token.getType() != CommonToken.EOF {
|
||||||
|
buf.append(token.getText()!)
|
||||||
}
|
}
|
||||||
return index + 1
|
return index + 1
|
||||||
}
|
}
|
||||||
|
@ -146,7 +143,7 @@ public class TokenStreamRewriter {
|
||||||
|
|
||||||
/// I'm going to try replacing range from x..y with (y-x)+1 ReplaceOp
|
/// I'm going to try replacing range from x..y with (y-x)+1 ReplaceOp
|
||||||
/// instructions.
|
/// instructions.
|
||||||
///
|
///
|
||||||
|
|
||||||
public class ReplaceOp: RewriteOperation {
|
public class ReplaceOp: RewriteOperation {
|
||||||
|
|
||||||
|
@ -156,27 +153,32 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
override
|
override
|
||||||
public func execute(_ buf: StringBuilder) -> Int {
|
public func execute(_ buf: inout String) -> Int {
|
||||||
if text != nil {
|
if let text = text {
|
||||||
buf.append(text!)
|
buf += text
|
||||||
}
|
}
|
||||||
return lastIndex + 1
|
return lastIndex + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
override
|
override
|
||||||
public var description: String {
|
public var description: String {
|
||||||
if text == nil {
|
let token = try! tokens.get(index)
|
||||||
return "<DeleteOp@\(try! tokens.get(index))..\(try! tokens.get(lastIndex))>"
|
let lastToken = try! tokens.get(lastIndex)
|
||||||
|
if let text = text {
|
||||||
|
return "<ReplaceOp@\(token)..\(lastToken):\"\(text)\">"
|
||||||
}
|
}
|
||||||
return "<ReplaceOp@\(try! tokens.get(index))..\(try! tokens.get(lastIndex)):\"\(text!)\">"
|
return "<DeleteOp@\(token)..\(lastToken)>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RewriteOperationArray{
|
public class RewriteOperationArray{
|
||||||
private final var rewrites: Array<RewriteOperation?> = Array<RewriteOperation?>()
|
private final var rewrites = [RewriteOperation?]()
|
||||||
public init(){
|
|
||||||
|
public init() {
|
||||||
rewrites.reserveCapacity(TokenStreamRewriter.PROGRAM_INIT_SIZE)
|
rewrites.reserveCapacity(TokenStreamRewriter.PROGRAM_INIT_SIZE)
|
||||||
}
|
}
|
||||||
final func append(_ op: RewriteOperation){
|
|
||||||
|
final func append(_ op: RewriteOperation) {
|
||||||
op.instructionIndex = rewrites.count
|
op.instructionIndex = rewrites.count
|
||||||
rewrites.append(op)
|
rewrites.append(op)
|
||||||
}
|
}
|
||||||
|
@ -184,12 +186,15 @@ public class TokenStreamRewriter {
|
||||||
final func rollback(_ instructionIndex: Int) {
|
final func rollback(_ instructionIndex: Int) {
|
||||||
rewrites = Array(rewrites[TokenStreamRewriter.MIN_TOKEN_INDEX ..< instructionIndex])
|
rewrites = Array(rewrites[TokenStreamRewriter.MIN_TOKEN_INDEX ..< instructionIndex])
|
||||||
}
|
}
|
||||||
final var count: Int{
|
|
||||||
|
final var count: Int {
|
||||||
return rewrites.count
|
return rewrites.count
|
||||||
}
|
}
|
||||||
final var isEmpty: Bool{
|
|
||||||
|
final var isEmpty: Bool {
|
||||||
return rewrites.isEmpty
|
return rewrites.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We need to combine operations and report invalid operations (like
|
/// We need to combine operations and report invalid operations (like
|
||||||
/// overlapping replaces that are not completed nested). Inserts to
|
/// overlapping replaces that are not completed nested). Inserts to
|
||||||
/// same index need to be combined etc... Here are the cases:
|
/// same index need to be combined etc... Here are the cases:
|
||||||
|
@ -239,37 +244,34 @@ public class TokenStreamRewriter {
|
||||||
///
|
///
|
||||||
/// Return a map from token index to operation.
|
/// Return a map from token index to operation.
|
||||||
///
|
///
|
||||||
final func reduceToSingleOperationPerIndex() throws -> Dictionary<Int, RewriteOperation> {
|
final func reduceToSingleOperationPerIndex() throws -> [Int: RewriteOperation] {
|
||||||
|
|
||||||
let rewritesCount = rewrites.count
|
let rewritesCount = rewrites.count
|
||||||
// WALK REPLACES
|
// WALK REPLACES
|
||||||
for i in 0..<rewritesCount {
|
for i in 0..<rewritesCount {
|
||||||
guard let rop = rewrites[i] else {
|
guard let rop = rewrites[i] as? ReplaceOp else {
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !(rop is ReplaceOp) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wipe prior inserts within range
|
// Wipe prior inserts within range
|
||||||
let inserts = getKindOfOps(&rewrites, InsertBeforeOp.self, i)
|
let inserts = getKindOfOps(&rewrites, InsertBeforeOp.self, i)
|
||||||
for j in inserts {
|
for j in inserts {
|
||||||
if let iop = rewrites[j] {
|
if let iop = rewrites[j] {
|
||||||
if iop.index == rop.index {
|
if iop.index == rop.index {
|
||||||
// E.g., insert before 2, delete 2..2; update replace
|
// E.g., insert before 2, delete 2..2; update replace
|
||||||
// text to include insert before, kill insert
|
// text to include insert before, kill insert
|
||||||
rewrites[iop.instructionIndex] = nil
|
rewrites[iop.instructionIndex] = nil
|
||||||
rop.text = catOpText(iop.text, rop.text)
|
rop.text = catOpText(iop.text, rop.text)
|
||||||
|
}
|
||||||
} else if iop.index > rop.index && iop.index <= rop.lastIndex {
|
else if iop.index > rop.index && iop.index <= rop.lastIndex {
|
||||||
// delete insert as it's a no-op.
|
// delete insert as it's a no-op.
|
||||||
rewrites[iop.instructionIndex] = nil
|
rewrites[iop.instructionIndex] = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Drop any prior replaces contained within
|
// Drop any prior replaces contained within
|
||||||
let prevRopIndexList = getKindOfOps(&rewrites, ReplaceOp.self, i)
|
let prevRopIndexList = getKindOfOps(&rewrites, ReplaceOp.self, i)
|
||||||
for j in prevRopIndexList {
|
for j in prevRopIndexList {
|
||||||
if let prevRop = rewrites[j] {
|
if let prevRop = rewrites[j] {
|
||||||
if prevRop.index >= rop.index && prevRop.lastIndex <= rop.lastIndex {
|
if prevRop.index >= rop.index && prevRop.lastIndex <= rop.lastIndex {
|
||||||
// delete replace as it's a no-op.
|
// delete replace as it's a no-op.
|
||||||
|
@ -285,7 +287,6 @@ public class TokenStreamRewriter {
|
||||||
rewrites[prevRop.instructionIndex] = nil // kill first delete
|
rewrites[prevRop.instructionIndex] = nil // kill first delete
|
||||||
rop.index = min(prevRop.index, rop.index)
|
rop.index = min(prevRop.index, rop.index)
|
||||||
rop.lastIndex = max(prevRop.lastIndex, rop.lastIndex)
|
rop.lastIndex = max(prevRop.lastIndex, rop.lastIndex)
|
||||||
print("new rop \(rop)")
|
|
||||||
} else if !disjoint {
|
} else if !disjoint {
|
||||||
throw ANTLRError.illegalArgument(msg: "replace op boundaries of \(rop.description) " +
|
throw ANTLRError.illegalArgument(msg: "replace op boundaries of \(rop.description) " +
|
||||||
"overlap with previous \(prevRop.description)")
|
"overlap with previous \(prevRop.description)")
|
||||||
|
@ -341,9 +342,9 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var m: Dictionary<Int, RewriteOperation> = Dictionary<Int, RewriteOperation>()
|
var m = [Int: RewriteOperation]()
|
||||||
for i in 0..<rewritesCount {
|
for i in 0..<rewritesCount {
|
||||||
if let op: RewriteOperation = rewrites[i] {
|
if let op = rewrites[i] {
|
||||||
if m[op.index] != nil {
|
if m[op.index] != nil {
|
||||||
throw ANTLRError.illegalArgument(msg: "should only be one op per index")
|
throw ANTLRError.illegalArgument(msg: "should only be one op per index")
|
||||||
}
|
}
|
||||||
|
@ -355,17 +356,16 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
final func catOpText(_ a: String?, _ b: String?) -> String {
|
final func catOpText(_ a: String?, _ b: String?) -> String {
|
||||||
let x: String = a ?? ""
|
let x = a ?? ""
|
||||||
let y: String = b ?? ""
|
let y = b ?? ""
|
||||||
|
|
||||||
return x + y
|
return x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all operations before an index of a particular kind
|
/// Get all operations before an index of a particular kind
|
||||||
|
|
||||||
final func getKindOfOps<T: RewriteOperation>(_ rewrites: inout [RewriteOperation?], _ kind: T.Type, _ before: Int ) -> [Int] {
|
final func getKindOfOps<T: RewriteOperation>(_ rewrites: inout [RewriteOperation?], _ kind: T.Type, _ before: Int ) -> [Int] {
|
||||||
|
|
||||||
let length = min(before,rewrites.count)
|
let length = min(before, rewrites.count)
|
||||||
var op = [Int]()
|
var op = [Int]()
|
||||||
op.reserveCapacity(length)
|
op.reserveCapacity(length)
|
||||||
for i in 0..<length {
|
for i in 0..<length {
|
||||||
|
@ -375,7 +375,6 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Our source stream
|
/// Our source stream
|
||||||
|
@ -385,14 +384,13 @@ public class TokenStreamRewriter {
|
||||||
/// I'm calling these things "programs."
|
/// I'm calling these things "programs."
|
||||||
/// Maps String (name) → rewrite (List)
|
/// Maps String (name) → rewrite (List)
|
||||||
///
|
///
|
||||||
internal var programs: Dictionary<String, RewriteOperationArray> //Array<RewriteOperation>
|
internal var programs = [String: RewriteOperationArray]()
|
||||||
|
|
||||||
/// Map String (program name) → Integer index
|
/// Map String (program name) → Integer index
|
||||||
internal final var lastRewriteTokenIndexes: Dictionary<String, Int>
|
internal final var lastRewriteTokenIndexes: [String: Int]
|
||||||
|
|
||||||
public init(_ tokens: TokenStream) {
|
public init(_ tokens: TokenStream) {
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
programs = Dictionary<String, RewriteOperationArray>()
|
|
||||||
programs[DEFAULT_PROGRAM_NAME] = RewriteOperationArray()
|
programs[DEFAULT_PROGRAM_NAME] = RewriteOperationArray()
|
||||||
lastRewriteTokenIndexes = Dictionary<String, Int>()
|
lastRewriteTokenIndexes = Dictionary<String, Int>()
|
||||||
}
|
}
|
||||||
|
@ -439,7 +437,7 @@ public class TokenStreamRewriter {
|
||||||
public func insertAfter(_ programName: String, _ index: Int, _ text: String) {
|
public func insertAfter(_ programName: String, _ index: Int, _ text: String) {
|
||||||
// to insert after, just insert before next index (even if past end)
|
// to insert after, just insert before next index (even if past end)
|
||||||
let op = InsertAfterOp(index, text, tokens)
|
let op = InsertAfterOp(index, text, tokens)
|
||||||
let rewrites = getProgram(programName)
|
let rewrites = getProgram(programName)
|
||||||
rewrites.append(op)
|
rewrites.append(op)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,8 +454,8 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func insertBefore(_ programName: String, _ index: Int, _ text: String) {
|
public func insertBefore(_ programName: String, _ index: Int, _ text: String) {
|
||||||
let op: RewriteOperation = InsertBeforeOp(index, text, tokens)
|
let op = InsertBeforeOp(index, text, tokens)
|
||||||
let rewrites: RewriteOperationArray = getProgram(programName)
|
let rewrites = getProgram(programName)
|
||||||
rewrites.append(op)
|
rewrites.append(op)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,10 +479,9 @@ public class TokenStreamRewriter {
|
||||||
if from > to || from < 0 || to < 0 || to >= tokens.size() {
|
if from > to || from < 0 || to < 0 || to >= tokens.size() {
|
||||||
throw ANTLRError.illegalArgument(msg: "replace: range invalid: \(from)..\(to)(size=\(tokens.size()))")
|
throw ANTLRError.illegalArgument(msg: "replace: range invalid: \(from)..\(to)(size=\(tokens.size()))")
|
||||||
}
|
}
|
||||||
let op: RewriteOperation = ReplaceOp(from, to, text, tokens)
|
let op = ReplaceOp(from, to, text, tokens)
|
||||||
let rewritesArray: RewriteOperationArray = getProgram(programName)
|
let rewritesArray = getProgram(programName)
|
||||||
rewritesArray.append(op)
|
rewritesArray.append(op)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func replace(_ programName: String, _ from: Token, _ to: Token, _ text: String?) throws {
|
public func replace(_ programName: String, _ from: Token, _ to: Token, _ text: String?) throws {
|
||||||
|
@ -523,30 +520,24 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func getLastRewriteTokenIndex(_ programName: String) -> Int {
|
internal func getLastRewriteTokenIndex(_ programName: String) -> Int {
|
||||||
let I: Int? = lastRewriteTokenIndexes[programName]
|
return lastRewriteTokenIndexes[programName] ?? -1
|
||||||
if I == nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return I!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func setLastRewriteTokenIndex(_ programName: String, _ i: Int) {
|
internal func setLastRewriteTokenIndex(_ programName: String, _ i: Int) {
|
||||||
lastRewriteTokenIndexes[programName] = i
|
lastRewriteTokenIndexes[programName] = i
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func getProgram(_ name: String) -> RewriteOperationArray
|
internal func getProgram(_ name: String) -> RewriteOperationArray {
|
||||||
{
|
if let program = programs[name] {
|
||||||
var program: RewriteOperationArray? = programs[name]
|
return program
|
||||||
if program == nil {
|
}
|
||||||
program = initializeProgram(name)
|
else {
|
||||||
|
return initializeProgram(name)
|
||||||
}
|
}
|
||||||
return program!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initializeProgram(_ name: String) -> RewriteOperationArray
|
private func initializeProgram(_ name: String) -> RewriteOperationArray {
|
||||||
{
|
let program = RewriteOperationArray()
|
||||||
let program: RewriteOperationArray = RewriteOperationArray()
|
|
||||||
|
|
||||||
programs[name] = program
|
programs[name] = program
|
||||||
return program
|
return program
|
||||||
}
|
}
|
||||||
|
@ -579,8 +570,8 @@ public class TokenStreamRewriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getText(_ programName: String, _ interval: Interval) throws -> String {
|
public func getText(_ programName: String, _ interval: Interval) throws -> String {
|
||||||
var start: Int = interval.a
|
var start = interval.a
|
||||||
var stop: Int = interval.b
|
var stop = interval.b
|
||||||
|
|
||||||
// ensure start/end are in range
|
// ensure start/end are in range
|
||||||
if stop > tokens.size() - 1 {
|
if stop > tokens.size() - 1 {
|
||||||
|
@ -589,32 +580,31 @@ public class TokenStreamRewriter {
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
guard let rewrites = programs[programName] , !rewrites.isEmpty else {
|
guard let rewrites = programs[programName], !rewrites.isEmpty else {
|
||||||
return try tokens.getText(interval) // no instructions to execute
|
return try tokens.getText(interval) // no instructions to execute
|
||||||
}
|
}
|
||||||
|
|
||||||
let buf: StringBuilder = StringBuilder()
|
var buf = ""
|
||||||
|
|
||||||
// First, optimize instruction stream
|
// First, optimize instruction stream
|
||||||
var indexToOp: Dictionary<Int, RewriteOperation> = try rewrites.reduceToSingleOperationPerIndex()
|
var indexToOp = try rewrites.reduceToSingleOperationPerIndex()
|
||||||
|
|
||||||
// Walk buffer, executing instructions and emitting tokens
|
// Walk buffer, executing instructions and emitting tokens
|
||||||
var i: Int = start
|
var i = start
|
||||||
while i <= stop && i < tokens.size() {
|
while i <= stop && i < tokens.size() {
|
||||||
let op: RewriteOperation? = indexToOp[i]
|
let op = indexToOp[i]
|
||||||
indexToOp.removeValue(forKey: i)
|
indexToOp.removeValue(forKey: i) // remove so any left have index size-1
|
||||||
//indexToOp.remove(i); // remove so any left have index size-1
|
let t = try tokens.get(i)
|
||||||
let t: Token = try tokens.get(i)
|
if let op = op {
|
||||||
if op == nil {
|
i = try op.execute(&buf) // execute operation and skip
|
||||||
|
}
|
||||||
|
else {
|
||||||
// no operation at that index, just dump token
|
// no operation at that index, just dump token
|
||||||
if t.getType() != CommonToken.EOF {
|
if t.getType() != CommonToken.EOF {
|
||||||
buf.append(t.getText()!)
|
buf.append(t.getText()!)
|
||||||
}
|
}
|
||||||
i += 1 // move to next token
|
i += 1 // move to next token
|
||||||
} else {
|
|
||||||
i = try op!.execute(buf) // execute operation and skip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// include stuff after end if it's last index in buffer
|
// include stuff after end if it's last index in buffer
|
||||||
|
@ -623,14 +613,13 @@ public class TokenStreamRewriter {
|
||||||
if stop == tokens.size() - 1 {
|
if stop == tokens.size() - 1 {
|
||||||
// Scan any remaining operations after last token
|
// Scan any remaining operations after last token
|
||||||
// should be included (they will be inserts).
|
// should be included (they will be inserts).
|
||||||
for op: RewriteOperation in indexToOp.values {
|
for op in indexToOp.values {
|
||||||
if op.index >= tokens.size() - 1 {
|
if op.index >= tokens.size() - 1 {
|
||||||
buf.append(op.text!)
|
buf += op.text!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,27 +284,23 @@ public class UnbufferedTokenStream: TokenStream {
|
||||||
|
|
||||||
|
|
||||||
public func getText(_ interval: Interval) throws -> String {
|
public func getText(_ interval: Interval) throws -> String {
|
||||||
let bufferStartIndex: Int = getBufferStartIndex()
|
let bufferStartIndex = getBufferStartIndex()
|
||||||
let bufferStopIndex: Int = bufferStartIndex + tokens.count - 1
|
let bufferStopIndex = bufferStartIndex + tokens.count - 1
|
||||||
|
|
||||||
let start: Int = interval.a
|
let start = interval.a
|
||||||
let stop: Int = interval.b
|
let stop = interval.b
|
||||||
if start < bufferStartIndex || stop > bufferStopIndex {
|
if start < bufferStartIndex || stop > bufferStopIndex {
|
||||||
|
|
||||||
throw ANTLRError.unsupportedOperation(msg: "interval \(interval) not in token buffer window: \(bufferStartIndex)..bufferStopIndex)")
|
throw ANTLRError.unsupportedOperation(msg: "interval \(interval) not in token buffer window: \(bufferStartIndex)..bufferStopIndex)")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let a: Int = start - bufferStartIndex
|
let a = start - bufferStartIndex
|
||||||
let b: Int = stop - bufferStartIndex
|
let b = stop - bufferStartIndex
|
||||||
|
|
||||||
let buf: StringBuilder = StringBuilder()
|
var buf = ""
|
||||||
for i in a...b {
|
for t in tokens[a...b] {
|
||||||
let t: Token = tokens[i]
|
buf += t.getText()!
|
||||||
buf.append(t.getText()!)
|
|
||||||
}
|
}
|
||||||
|
return buf
|
||||||
return buf.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal final func getBufferStartIndex() -> Int {
|
internal final func getBufferStartIndex() -> Int {
|
||||||
|
|
|
@ -85,27 +85,25 @@ public class Vocabulary: Hashable {
|
||||||
/// the display names of tokens.
|
/// the display names of tokens.
|
||||||
///
|
///
|
||||||
public static func fromTokenNames(_ tokenNames: [String?]?) -> Vocabulary {
|
public static func fromTokenNames(_ tokenNames: [String?]?) -> Vocabulary {
|
||||||
guard let tokenNames = tokenNames , tokenNames.count > 0 else {
|
guard let tokenNames = tokenNames, tokenNames.count > 0 else {
|
||||||
return EMPTY_VOCABULARY
|
return EMPTY_VOCABULARY
|
||||||
}
|
}
|
||||||
|
|
||||||
var literalNames: [String?] = tokenNames// Arrays.copyOf(tokenNames, tokenNames.length);
|
var literalNames = tokenNames
|
||||||
var symbolicNames: [String?] = tokenNames
|
var symbolicNames = tokenNames
|
||||||
let length = tokenNames.count
|
let length = tokenNames.count
|
||||||
for i in 0..<length {
|
for i in 0..<length {
|
||||||
guard let tokenName = tokenNames[i] else {
|
guard let tokenName = tokenNames[i] else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !tokenName.isEmpty {
|
if let firstChar = tokenName.first {
|
||||||
let firstChar: Character = tokenName[0]
|
|
||||||
if firstChar == "\'" {
|
if firstChar == "\'" {
|
||||||
symbolicNames[i] = nil
|
symbolicNames[i] = nil
|
||||||
continue
|
continue
|
||||||
} else {
|
}
|
||||||
if String(firstChar).uppercased() != String(firstChar) {
|
else if String(firstChar).uppercased() != String(firstChar) {
|
||||||
literalNames[i] = nil
|
literalNames[i] = nil
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,20 +139,17 @@ public class Vocabulary: Hashable {
|
||||||
|
|
||||||
public func getDisplayName(_ tokenType: Int) -> String {
|
public func getDisplayName(_ tokenType: Int) -> String {
|
||||||
if tokenType >= 0 && tokenType < displayNames.count {
|
if tokenType >= 0 && tokenType < displayNames.count {
|
||||||
let displayName: String? = displayNames[tokenType]
|
if let displayName = displayNames[tokenType] {
|
||||||
if displayName != nil {
|
return displayName
|
||||||
return displayName!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let literalName: String? = getLiteralName(tokenType)
|
if let literalName = getLiteralName(tokenType) {
|
||||||
if literalName != nil {
|
return literalName
|
||||||
return literalName!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbolicName: String? = getSymbolicName(tokenType)
|
if let symbolicName = getSymbolicName(tokenType) {
|
||||||
if symbolicName != nil {
|
return symbolicName
|
||||||
return symbolicName!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return String(tokenType)
|
return String(tokenType)
|
||||||
|
|
|
@ -159,37 +159,27 @@ public class ATNConfig: Hashable, CustomStringConvertible {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
//return "MyClass \(string)"
|
//return "MyClass \(string)"
|
||||||
return toString(nil, true)
|
return toString(nil, true)
|
||||||
}
|
}
|
||||||
public func toString<T>(_ recog: Recognizer<T>?, _ showAlt: Bool) -> String {
|
public func toString<T>(_ recog: Recognizer<T>?, _ showAlt: Bool) -> String {
|
||||||
let buf: StringBuilder = StringBuilder()
|
var buf = "(\(state)"
|
||||||
buf.append("(")
|
|
||||||
buf.append(state)
|
|
||||||
if showAlt {
|
if showAlt {
|
||||||
buf.append(",")
|
buf += ",\(alt)"
|
||||||
buf.append(alt)
|
|
||||||
}
|
}
|
||||||
|
if let context = context {
|
||||||
if context != nil {
|
buf += ",[\(context)]"
|
||||||
buf.append(",[")
|
|
||||||
buf.append(context!)
|
|
||||||
buf.append("]")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if semanticContext != SemanticContext.NONE {
|
if semanticContext != SemanticContext.NONE {
|
||||||
buf.append(",")
|
buf += ",\(semanticContext)"
|
||||||
buf.append(semanticContext)
|
|
||||||
}
|
}
|
||||||
if getOuterContextDepth() > 0 {
|
let outerDepth = getOuterContextDepth()
|
||||||
buf.append(",up=").append(getOuterContextDepth())
|
if outerDepth > 0 {
|
||||||
|
buf += ",up=\(outerDepth)"
|
||||||
}
|
}
|
||||||
buf.append(")")
|
buf += ")"
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,27 +275,21 @@ public class ATNConfigSet: Hashable, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
let buf = StringBuilder()
|
var buf = ""
|
||||||
buf.append(elements().map({ $0.description }))
|
buf += String(describing: elements())
|
||||||
if hasSemanticContext {
|
if hasSemanticContext {
|
||||||
buf.append(",hasSemanticContext=")
|
buf += ",hasSemanticContext=true"
|
||||||
buf.append(hasSemanticContext)
|
|
||||||
}
|
}
|
||||||
if uniqueAlt != ATN.INVALID_ALT_NUMBER {
|
if uniqueAlt != ATN.INVALID_ALT_NUMBER {
|
||||||
buf.append(",uniqueAlt=")
|
buf += ",uniqueAlt=\(uniqueAlt)"
|
||||||
buf.append(uniqueAlt)
|
|
||||||
}
|
}
|
||||||
if let conflictingAlts = conflictingAlts {
|
if let conflictingAlts = conflictingAlts {
|
||||||
buf.append(",conflictingAlts=")
|
buf += ",conflictingAlts=\(conflictingAlts)"
|
||||||
buf.append(conflictingAlts.description)
|
|
||||||
}
|
}
|
||||||
if dipsIntoOuterContext {
|
if dipsIntoOuterContext {
|
||||||
buf.append(",dipsIntoOuterContext")
|
buf += ",dipsIntoOuterContext"
|
||||||
}
|
}
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
|
@ -136,9 +136,6 @@ public class ATNState: Hashable, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
//return "MyClass \(string)"
|
//return "MyClass \(string)"
|
||||||
return String(stateNumber)
|
return String(stateNumber)
|
||||||
|
|
|
@ -58,28 +58,25 @@ public class ArrayPredictionContext: PredictionContext {
|
||||||
if isEmpty() {
|
if isEmpty() {
|
||||||
return "[]"
|
return "[]"
|
||||||
}
|
}
|
||||||
let buf: StringBuilder = StringBuilder()
|
var buf = "["
|
||||||
buf.append("[")
|
for (i, returnState) in returnStates.enumerated() {
|
||||||
let length = returnStates.count
|
|
||||||
|
|
||||||
for i in 0..<length {
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
buf.append(", ")
|
buf += ", "
|
||||||
}
|
}
|
||||||
if returnStates[i] == PredictionContext.EMPTY_RETURN_STATE {
|
if returnState == PredictionContext.EMPTY_RETURN_STATE {
|
||||||
buf.append("$")
|
buf += "$"
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
buf.append(returnStates[i])
|
buf += "\(returnState)"
|
||||||
if parents[i] != nil {
|
if let parent = parents[i] {
|
||||||
buf.append(" ")
|
buf += " \(parent)"
|
||||||
buf.append(parents[i].debugDescription)
|
}
|
||||||
} else {
|
else {
|
||||||
buf.append("null")
|
buf += "null"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.append("]")
|
buf += "]"
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
internal final func combineCommonParents() {
|
internal final func combineCommonParents() {
|
||||||
|
|
|
@ -225,21 +225,22 @@ public class DecisionInfo: CustomStringConvertible {
|
||||||
|
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
let desc: StringBuilder = StringBuilder()
|
var desc = ""
|
||||||
desc.append("{")
|
|
||||||
desc.append("decision=\(decision)")
|
|
||||||
desc.append(", contextSensitivities=\(contextSensitivities.count)")
|
|
||||||
desc.append(", errors=\(errors.count)")
|
|
||||||
desc.append(", ambiguities=\(ambiguities.count)")
|
|
||||||
desc.append(", SLL_lookahead=\(SLL_TotalLook)")
|
|
||||||
desc.append(", SLL_ATNTransitions=\(SLL_ATNTransitions)")
|
|
||||||
desc.append(", SLL_DFATransitions=\(SLL_DFATransitions)")
|
|
||||||
desc.append(", LL_Fallback=\(LL_Fallback)")
|
|
||||||
desc.append(", LL_lookahead=\(LL_TotalLook)")
|
|
||||||
desc.append(", LL_ATNTransitions=\(LL_ATNTransitions)")
|
|
||||||
desc.append("}")
|
|
||||||
|
|
||||||
return desc.toString()
|
desc += "{"
|
||||||
|
desc += "decision=\(decision)"
|
||||||
|
desc += ", contextSensitivities=\(contextSensitivities.count)"
|
||||||
|
desc += ", errors=\(errors.count)"
|
||||||
|
desc += ", ambiguities=\(ambiguities.count)"
|
||||||
|
desc += ", SLL_lookahead=\(SLL_TotalLook)"
|
||||||
|
desc += ", SLL_ATNTransitions=\(SLL_ATNTransitions)"
|
||||||
|
desc += ", SLL_DFATransitions=\(SLL_DFATransitions)"
|
||||||
|
desc += ", LL_Fallback=\(LL_Fallback)"
|
||||||
|
desc += ", LL_lookahead=\(LL_TotalLook)"
|
||||||
|
desc += ", LL_ATNTransitions=\(LL_ATNTransitions)"
|
||||||
|
desc += "}"
|
||||||
|
|
||||||
|
return desc
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1907,7 +1907,7 @@ open class ParserATNSimulator: ATNSimulator {
|
||||||
}
|
}
|
||||||
else if let st = t as? SetTransition {
|
else if let st = t as? SetTransition {
|
||||||
let not = st is NotSetTransition
|
let not = st is NotSetTransition
|
||||||
trans = (not ? "~" : "") + "Set " + st.set.toString()
|
trans = (not ? "~" : "") + "Set " + st.set.description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errPrint("\(c.toString(parser, true)):\(trans)")
|
errPrint("\(c.toString(parser, true)):\(trans)")
|
||||||
|
|
|
@ -42,9 +42,4 @@ public final class PrecedencePredicateTransition: AbstractPredicateTransition, C
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return "\(precedence) >= _p"
|
return "\(precedence) >= _p"
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
/// multiple ATN configurations into a single DFA state.
|
/// multiple ATN configurations into a single DFA state.
|
||||||
///
|
///
|
||||||
|
|
||||||
public final class PredicateTransition: AbstractPredicateTransition {
|
public final class PredicateTransition: AbstractPredicateTransition, CustomStringConvertible {
|
||||||
public let ruleIndex: Int
|
public let ruleIndex: Int
|
||||||
public let predIndex: Int
|
public let predIndex: Int
|
||||||
public let isCtxDependent: Bool
|
public let isCtxDependent: Bool
|
||||||
|
@ -47,9 +47,7 @@ public final class PredicateTransition: AbstractPredicateTransition {
|
||||||
return SemanticContext.Predicate(ruleIndex, predIndex, isCtxDependent)
|
return SemanticContext.Predicate(ruleIndex, predIndex, isCtxDependent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
public func toString() -> String {
|
|
||||||
return "pred_\(ruleIndex):\(predIndex)"
|
return "pred_\(ruleIndex):\(predIndex)"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -518,47 +518,40 @@ public class PredictionContext: Hashable, CustomStringConvertible {
|
||||||
if context == nil {
|
if context == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
let buf = StringBuilder()
|
var buf = ""
|
||||||
buf.append("digraph G {\n")
|
buf += "digraph G {\n"
|
||||||
buf.append("rankdir=LR;\n")
|
buf += "rankdir=LR;\n"
|
||||||
|
|
||||||
var nodes = getAllContextNodes(context!)
|
var nodes = getAllContextNodes(context!)
|
||||||
|
|
||||||
nodes.sort { $0.id > $1.id }
|
nodes.sort { $0.id > $1.id }
|
||||||
|
|
||||||
|
|
||||||
for current in nodes {
|
for current in nodes {
|
||||||
if current is SingletonPredictionContext {
|
if current is SingletonPredictionContext {
|
||||||
let s = String(current.id)
|
buf += " s\(current.id)"
|
||||||
buf.append(" s").append(s)
|
|
||||||
var returnState = String(current.getReturnState(0))
|
var returnState = String(current.getReturnState(0))
|
||||||
if current is EmptyPredictionContext {
|
if current is EmptyPredictionContext {
|
||||||
returnState = "$"
|
returnState = "$"
|
||||||
}
|
}
|
||||||
buf.append(" [label=\"")
|
buf += " [label=\"\(returnState)\"];\n"
|
||||||
buf.append(returnState)
|
|
||||||
buf.append("\"];\n")
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let arr = current as! ArrayPredictionContext
|
let arr = current as! ArrayPredictionContext
|
||||||
buf.append(" s").append(arr.id)
|
buf += " s\(arr.id) [shape=box, label=\"["
|
||||||
buf.append(" [shape=box, label=\"")
|
|
||||||
buf.append("[")
|
|
||||||
var first = true
|
var first = true
|
||||||
let returnStates = arr.returnStates
|
let returnStates = arr.returnStates
|
||||||
for inv in returnStates {
|
for inv in returnStates {
|
||||||
if !first {
|
if !first {
|
||||||
buf.append(", ")
|
buf += ", "
|
||||||
}
|
}
|
||||||
if inv == EMPTY_RETURN_STATE {
|
if inv == EMPTY_RETURN_STATE {
|
||||||
buf.append("$")
|
buf += "$"
|
||||||
} else {
|
} else {
|
||||||
buf.append(inv)
|
buf += String(inv)
|
||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
buf.append("]")
|
buf += "]\"];\n"
|
||||||
buf.append("\"];\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for current in nodes {
|
for current in nodes {
|
||||||
|
@ -570,21 +563,17 @@ public class PredictionContext: Hashable, CustomStringConvertible {
|
||||||
guard let currentParent = current.getParent(i) else {
|
guard let currentParent = current.getParent(i) else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let s = String(current.id)
|
buf += " s\(current.id) -> s\(currentParent.id)"
|
||||||
buf.append(" s").append(s)
|
|
||||||
buf.append("->")
|
|
||||||
buf.append("s")
|
|
||||||
buf.append(currentParent.id)
|
|
||||||
if current.size() > 1 {
|
if current.size() > 1 {
|
||||||
buf.append(" [label=\"parent[\(i)]\"];\n")
|
buf += " [label=\"parent[\(i)]\"];\n"
|
||||||
} else {
|
} else {
|
||||||
buf.append(";\n")
|
buf += ";\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.append("}\n")
|
buf.append("}\n")
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// From Sam
|
// From Sam
|
||||||
|
@ -700,8 +689,7 @@ public class PredictionContext: Hashable, CustomStringConvertible {
|
||||||
var last = true
|
var last = true
|
||||||
var p = self
|
var p = self
|
||||||
var stateNumber = currentState
|
var stateNumber = currentState
|
||||||
let localBuffer = StringBuilder()
|
var localBuffer = "["
|
||||||
localBuffer.append("[")
|
|
||||||
while !p.isEmpty() && p !== stop {
|
while !p.isEmpty() && p !== stop {
|
||||||
var index = 0
|
var index = 0
|
||||||
if p.size() > 0 {
|
if p.size() > 0 {
|
||||||
|
@ -724,9 +712,9 @@ public class PredictionContext: Hashable, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let recognizer = recognizer {
|
if let recognizer = recognizer {
|
||||||
if localBuffer.length > 1 {
|
if localBuffer.count > 1 {
|
||||||
// first char is '[', if more than that this isn't the first rule
|
// first char is '[', if more than that this isn't the first rule
|
||||||
localBuffer.append(" ")
|
localBuffer += " "
|
||||||
}
|
}
|
||||||
|
|
||||||
let atn = recognizer.getATN()
|
let atn = recognizer.getATN()
|
||||||
|
@ -736,19 +724,19 @@ public class PredictionContext: Hashable, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
else if p.getReturnState(index) != PredictionContext.EMPTY_RETURN_STATE {
|
else if p.getReturnState(index) != PredictionContext.EMPTY_RETURN_STATE {
|
||||||
if !p.isEmpty() {
|
if !p.isEmpty() {
|
||||||
if localBuffer.length > 1 {
|
if localBuffer.count > 1 {
|
||||||
// first char is '[', if more than that this isn't the first rule
|
// first char is '[', if more than that this isn't the first rule
|
||||||
localBuffer.append(" ")
|
localBuffer += " "
|
||||||
}
|
}
|
||||||
|
|
||||||
localBuffer.append(p.getReturnState(index))
|
localBuffer += String(p.getReturnState(index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stateNumber = p.getReturnState(index)
|
stateNumber = p.getReturnState(index)
|
||||||
p = p.getParent(index)!
|
p = p.getParent(index)!
|
||||||
}
|
}
|
||||||
localBuffer.append("]")
|
localBuffer += "]"
|
||||||
result.append(localBuffer.toString())
|
result.append(localBuffer)
|
||||||
|
|
||||||
if last {
|
if last {
|
||||||
break
|
break
|
||||||
|
|
|
@ -31,11 +31,6 @@ public final class RangeTransition: Transition, CustomStringConvertible {
|
||||||
return symbol >= from && symbol <= to
|
return symbol >= from && symbol <= to
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return "'" + String(from) + "'..'" + String(to) + "'"
|
return "'" + String(from) + "'..'" + String(to) + "'"
|
||||||
|
|
||||||
|
|
|
@ -277,10 +277,6 @@ public class SemanticContext: Hashable, CustomStringConvertible {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
override
|
override
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return opnds.map({ $0.description }).joined(separator: "&&")
|
return opnds.map({ $0.description }).joined(separator: "&&")
|
||||||
|
@ -386,11 +382,6 @@ public class SemanticContext: Hashable, CustomStringConvertible {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
override
|
override
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return opnds.map({ $0.description }).joined(separator: "||")
|
return opnds.map({ $0.description }).joined(separator: "||")
|
||||||
|
|
|
@ -49,8 +49,8 @@ public class SingletonPredictionContext: PredictionContext {
|
||||||
|
|
||||||
override
|
override
|
||||||
public var description: String {
|
public var description: String {
|
||||||
let up: String = parent != nil ? parent!.description : ""
|
let up = parent?.description ?? ""
|
||||||
if up.length == 0 {
|
if up.isEmpty {
|
||||||
if returnState == PredictionContext.EMPTY_RETURN_STATE {
|
if returnState == PredictionContext.EMPTY_RETURN_STATE {
|
||||||
return "$"
|
return "$"
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,18 +165,13 @@ public class DFA: CustomStringConvertible {
|
||||||
return toString(Vocabulary.EMPTY_VOCABULARY)
|
return toString(Vocabulary.EMPTY_VOCABULARY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
public func toString(_ vocabulary: Vocabulary) -> String {
|
public func toString(_ vocabulary: Vocabulary) -> String {
|
||||||
if s0 == nil {
|
if s0 == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
let serializer = DFASerializer(self, vocabulary)
|
let serializer = DFASerializer(self, vocabulary)
|
||||||
return serializer.toString()
|
return serializer.description
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toLexerString() -> String {
|
public func toLexerString() -> String {
|
||||||
|
@ -184,7 +179,7 @@ public class DFA: CustomStringConvertible {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
let serializer = LexerDFASerializer(self)
|
let serializer = LexerDFASerializer(self)
|
||||||
return serializer.toString()
|
return serializer.description
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,37 +22,25 @@ public class DFASerializer: CustomStringConvertible {
|
||||||
if dfa.s0 == nil {
|
if dfa.s0 == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
let buf = StringBuilder()
|
var buf = ""
|
||||||
let states = dfa.getStates()
|
let states = dfa.getStates()
|
||||||
for s in states {
|
for s in states {
|
||||||
guard let edges = s.edges else {
|
guard let edges = s.edges else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let n = edges.count
|
for (i, t) in edges.enumerated() {
|
||||||
for i in 0..<n {
|
guard let t = t, t.stateNumber != Int.max else {
|
||||||
if let t = s.edges![i], t.stateNumber != Int.max {
|
continue
|
||||||
buf.append(getStateString(s))
|
|
||||||
let label = getEdgeLabel(i)
|
|
||||||
buf.append("-")
|
|
||||||
buf.append(label)
|
|
||||||
buf.append("->")
|
|
||||||
buf.append(getStateString(t))
|
|
||||||
buf.append("\n")
|
|
||||||
}
|
}
|
||||||
|
let edgeLabel = getEdgeLabel(i)
|
||||||
|
buf += getStateString(s)
|
||||||
|
buf += "-\(edgeLabel)->"
|
||||||
|
buf += getStateString(t)
|
||||||
|
buf += "\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = buf.toString()
|
return buf
|
||||||
if output.length == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
//return Utils.sortLinesInString(output);
|
|
||||||
return output
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func getEdgeLabel(_ i: Int) -> String {
|
internal func getEdgeLabel(_ i: Int) -> String {
|
||||||
|
|
|
@ -136,17 +136,17 @@ public class DFAState: Hashable, CustomStringConvertible {
|
||||||
/// _#stateNumber_ is irrelevant.
|
/// _#stateNumber_ is irrelevant.
|
||||||
///
|
///
|
||||||
public var description: String {
|
public var description: String {
|
||||||
let buf = StringBuilder()
|
var buf = "\(stateNumber):\(configs)"
|
||||||
buf.append(stateNumber).append(":").append(configs)
|
|
||||||
if isAcceptState {
|
if isAcceptState {
|
||||||
buf.append("=>")
|
buf += "=>"
|
||||||
if let predicates = predicates {
|
if let predicates = predicates {
|
||||||
buf.append(predicates.map({ $0.description }))
|
buf += String(describing: predicates)
|
||||||
} else {
|
}
|
||||||
buf.append(prediction)
|
else {
|
||||||
|
buf += String(prediction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1104,15 +1104,15 @@ public class BitSet: Hashable, CustomStringConvertible {
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
///
|
///
|
||||||
/// BitSet drPepper = new BitSet();
|
/// `BitSet drPepper = new BitSet();`
|
||||||
/// Now `drPepper.toString()` returns "`{`}".
|
/// Now `drPepper.description` returns `"{}"`.
|
||||||
///
|
///
|
||||||
/// drPepper.set(2);
|
/// `drPepper.set(2);`
|
||||||
/// Now `drPepper.toString()` returns "`{2`}".
|
/// Now `drPepper.description` returns `"{2}"`.
|
||||||
///
|
///
|
||||||
/// drPepper.set(4);
|
/// `drPepper.set(4);`
|
||||||
/// drPepper.set(10);
|
/// `drPepper.set(10);`
|
||||||
/// Now `drPepper.toString()` returns "`{2, 4, 10`}".
|
/// Now `drPepper.description` returns `"{2, 4, 10}"`.
|
||||||
///
|
///
|
||||||
/// - returns: a string representation of this bit set
|
/// - returns: a string representation of this bit set
|
||||||
///
|
///
|
||||||
|
@ -1121,29 +1121,24 @@ public class BitSet: Hashable, CustomStringConvertible {
|
||||||
|
|
||||||
//let numBits: Int = (wordsInUse > 128) ?
|
//let numBits: Int = (wordsInUse > 128) ?
|
||||||
// cardinality() : wordsInUse * BitSet.BITS_PER_WORD
|
// cardinality() : wordsInUse * BitSet.BITS_PER_WORD
|
||||||
let b = StringBuilder()
|
var b = "{"
|
||||||
b.append("{")
|
|
||||||
var i = firstSetBit()
|
var i = firstSetBit()
|
||||||
if i != -1 {
|
if i != -1 {
|
||||||
b.append(i)
|
b += String(i)
|
||||||
i = try! nextSetBit(i + 1)
|
i = try! nextSetBit(i + 1)
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
let endOfRun = try! nextClearBit(i)
|
let endOfRun = try! nextClearBit(i)
|
||||||
repeat {
|
repeat {
|
||||||
b.append(", ").append(i)
|
b += ", \(i)"
|
||||||
i += 1
|
i += 1
|
||||||
} while i < endOfRun
|
} while i < endOfRun
|
||||||
i = try! nextSetBit(i + 1)
|
i = try! nextSetBit(i + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.append("}")
|
b += "}"
|
||||||
return b.toString()
|
return b
|
||||||
|
|
||||||
}
|
}
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func ==(lhs: BitSet, rhs: BitSet) -> Bool {
|
public func ==(lhs: BitSet, rhs: BitSet) -> Bool {
|
||||||
|
|
|
@ -146,6 +146,4 @@ public protocol IntSet {
|
||||||
/// in ascending numerical order.
|
/// in ascending numerical order.
|
||||||
///
|
///
|
||||||
func toList() -> [Int]
|
func toList() -> [Int]
|
||||||
|
|
||||||
func toString() -> String
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,12 +159,6 @@ public class Interval: Hashable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return "\(a)..\(b)"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return "\(a)..\(b)"
|
return "\(a)..\(b)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,6 @@ public class IntervalSet: IntSet, Hashable, CustomStringConvertible {
|
||||||
if readonly {
|
if readonly {
|
||||||
throw ANTLRError.illegalState(msg: "can't alter readonly IntervalSet")
|
throw ANTLRError.illegalState(msg: "can't alter readonly IntervalSet")
|
||||||
}
|
}
|
||||||
//System.out.println("add "+addition+" to "+intervals.toString());
|
|
||||||
if addition.b < addition.a {
|
if addition.b < addition.a {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -509,109 +508,112 @@ public class IntervalSet: IntSet, Hashable, CustomStringConvertible {
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return toString(false)
|
return toString(false)
|
||||||
}
|
}
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
public func toString(_ elemAreChar: Bool) -> String {
|
public func toString(_ elemAreChar: Bool) -> String {
|
||||||
let buf = StringBuilder()
|
if intervals.isEmpty {
|
||||||
if self.intervals.isEmpty {
|
|
||||||
return "{}"
|
return "{}"
|
||||||
}
|
}
|
||||||
if self.size() > 1 {
|
|
||||||
buf.append("{")
|
let selfSize = size()
|
||||||
|
|
||||||
|
var buf = ""
|
||||||
|
|
||||||
|
if selfSize > 1 {
|
||||||
|
buf += "{"
|
||||||
}
|
}
|
||||||
var first = true
|
var first = true
|
||||||
for interval in intervals {
|
for interval in intervals {
|
||||||
if !first {
|
if !first {
|
||||||
buf.append(", ")
|
buf += ", "
|
||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
|
|
||||||
let a = interval.a
|
let a = interval.a
|
||||||
let b = interval.b
|
let b = interval.b
|
||||||
if a == b {
|
if a == b {
|
||||||
if a == CommonToken.EOF {
|
if a == CommonToken.EOF {
|
||||||
buf.append("<EOF>")
|
buf += "<EOF>"
|
||||||
} else {
|
|
||||||
if elemAreChar {
|
|
||||||
buf.append("'").append(String(a)).append("'")
|
|
||||||
} else {
|
|
||||||
buf.append(a)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
else if elemAreChar {
|
||||||
if elemAreChar {
|
buf += "'\(a)'"
|
||||||
buf.append("'").append(String(a)).append("'..'").append(String(b)).append("'")
|
}
|
||||||
} else {
|
else {
|
||||||
buf.append(a).append("..").append(b)
|
buf += "\(a)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if elemAreChar {
|
||||||
|
buf += "'\(a)'..'\(b)'"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf += "\(a)..\(b)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.size() > 1 {
|
|
||||||
buf.append("}")
|
if selfSize > 1 {
|
||||||
|
buf += "}"
|
||||||
}
|
}
|
||||||
return buf.toString()
|
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toString(_ vocabulary: Vocabulary) -> String {
|
public func toString(_ vocabulary: Vocabulary) -> String {
|
||||||
let buf = StringBuilder()
|
if intervals.isEmpty {
|
||||||
|
|
||||||
if self.intervals.isEmpty {
|
|
||||||
return "{}"
|
return "{}"
|
||||||
}
|
}
|
||||||
if self.size() > 1 {
|
|
||||||
buf.append("{")
|
let selfSize = size()
|
||||||
|
|
||||||
|
var buf = ""
|
||||||
|
|
||||||
|
if selfSize > 1 {
|
||||||
|
buf += "{"
|
||||||
}
|
}
|
||||||
|
|
||||||
var first = true
|
var first = true
|
||||||
for interval in intervals {
|
for interval in intervals {
|
||||||
if !first {
|
if !first {
|
||||||
buf.append(", ")
|
buf += ", "
|
||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
|
|
||||||
let a = interval.a
|
let a = interval.a
|
||||||
let b = interval.b
|
let b = interval.b
|
||||||
if a == b {
|
if a == b {
|
||||||
buf.append(elementName(vocabulary, a))
|
buf += elementName(vocabulary, a)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
for i in a...b {
|
for i in a...b {
|
||||||
if i > a {
|
if i > a {
|
||||||
buf.append(", ")
|
buf += ", "
|
||||||
}
|
}
|
||||||
buf.append(elementName(vocabulary, i))
|
buf += elementName(vocabulary, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if selfSize > 1 {
|
||||||
|
buf += "}"
|
||||||
}
|
}
|
||||||
if self.size() > 1 {
|
|
||||||
buf.append("}")
|
return buf
|
||||||
}
|
|
||||||
return buf.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func elementName(_ vocabulary: Vocabulary, _ a: Int) -> String {
|
internal func elementName(_ vocabulary: Vocabulary, _ a: Int) -> String {
|
||||||
if a == CommonToken.EOF {
|
if a == CommonToken.EOF {
|
||||||
return "<EOF>"
|
return "<EOF>"
|
||||||
} else {
|
}
|
||||||
if a == CommonToken.EPSILON {
|
else if a == CommonToken.EPSILON {
|
||||||
return "<EPSILON>"
|
return "<EPSILON>"
|
||||||
} else {
|
}
|
||||||
return vocabulary.getDisplayName(a)
|
else {
|
||||||
}
|
return vocabulary.getDisplayName(a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func size() -> Int {
|
public func size() -> Int {
|
||||||
var n = 0
|
var n = 0
|
||||||
let numIntervals = intervals.count
|
for interval in intervals {
|
||||||
if numIntervals == 1 {
|
|
||||||
let firstInterval = intervals[0]
|
|
||||||
return firstInterval.b - firstInterval.a + 1
|
|
||||||
}
|
|
||||||
for i in 0..<numIntervals {
|
|
||||||
let interval = intervals[i]
|
|
||||||
n += (interval.b - interval.a + 1)
|
n += (interval.b - interval.a + 1)
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
|
|
|
@ -10,27 +10,25 @@ import Foundation
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
public static func escapeWhitespace(_ s: String, _ escapeSpaces: Bool) -> String {
|
public static func escapeWhitespace(_ s: String, _ escapeSpaces: Bool) -> String {
|
||||||
let buf: StringBuilder = StringBuilder()
|
var buf = ""
|
||||||
for c: Character in s.characters {
|
for c in s {
|
||||||
if c == " " && escapeSpaces {
|
if c == " " && escapeSpaces {
|
||||||
buf.append("\u{00B7}")
|
buf += "\u{00B7}"
|
||||||
} else {
|
}
|
||||||
if c == "\t" {
|
else if c == "\t" {
|
||||||
buf.append("\\t")
|
buf += "\\t"
|
||||||
} else {
|
}
|
||||||
if c == "\n" {
|
else if c == "\n" {
|
||||||
buf.append("\\n")
|
buf += "\\n"
|
||||||
} else {
|
}
|
||||||
if c == "\r" {
|
else if c == "\r" {
|
||||||
buf.append("\\r")
|
buf += "\\r"
|
||||||
} else {
|
}
|
||||||
buf.append(String(c))
|
else {
|
||||||
}
|
buf.append(c)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,62 +6,38 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
//http://stackoverflow.com/questions/28182441/swift-how-to-get-substring-from-start-to-last-index-of-character
|
|
||||||
//https://github.com/williamFalcon/Bolt_Swift/blob/master/Bolt/BoltLibrary/String/String.swift
|
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
func lastIndex(of target: String) -> String.Index? {
|
||||||
var length: Int {
|
if target.isEmpty {
|
||||||
return self.characters.count
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func indexOf(_ target: String) -> Int {
|
|
||||||
let range = self.range(of: target)
|
|
||||||
if let range = range {
|
|
||||||
return self.characters.distance(from: self.startIndex, to: range.lowerBound)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return -1
|
|
||||||
}
|
}
|
||||||
}
|
var result: String.Index? = nil
|
||||||
|
var substring = self[...]
|
||||||
func indexOf(_ target: String, startIndex: Int) -> Int {
|
while true {
|
||||||
|
guard let targetRange = substring.range(of: target) else {
|
||||||
let startRange = self.characters.index(self.startIndex, offsetBy: startIndex)
|
return result
|
||||||
let range = self.range(of: target, options: .literal, range: startRange..<self.endIndex)
|
|
||||||
|
|
||||||
if let range = range {
|
|
||||||
|
|
||||||
return self.characters.distance(from: self.startIndex, to: range.lowerBound)
|
|
||||||
} else {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lastIndexOf(_ target: String) -> Int {
|
|
||||||
var index = -1
|
|
||||||
var stepIndex = self.indexOf(target)
|
|
||||||
while stepIndex > -1 {
|
|
||||||
index = stepIndex
|
|
||||||
if stepIndex + target.length < self.length {
|
|
||||||
stepIndex = indexOf(target, startIndex: stepIndex + target.length)
|
|
||||||
} else {
|
|
||||||
stepIndex = -1
|
|
||||||
}
|
}
|
||||||
|
result = targetRange.lowerBound
|
||||||
|
let nextChar = substring.index(after: targetRange.lowerBound)
|
||||||
|
substring = self[nextChar...]
|
||||||
}
|
}
|
||||||
return index
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(integerIndex: Int) -> Character {
|
|
||||||
let index = characters.index(startIndex, offsetBy: integerIndex)
|
|
||||||
return self[index]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subscript(integerRange: Range<Int>) -> String {
|
subscript(integerRange: Range<Int>) -> String {
|
||||||
let start = characters.index(startIndex, offsetBy: integerRange.lowerBound)
|
let start = index(startIndex, offsetBy: integerRange.lowerBound)
|
||||||
let end = characters.index(startIndex, offsetBy: integerRange.upperBound)
|
let end = index(startIndex, offsetBy: integerRange.upperBound)
|
||||||
let range = start ..< end
|
let range = start ..< end
|
||||||
return String(self[range])
|
return String(self[range])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Implement Substring.hasPrefix, which is not currently in the Linux stdlib.
|
||||||
|
// https://bugs.swift.org/browse/SR-5627
|
||||||
|
#if os(Linux)
|
||||||
|
extension Substring {
|
||||||
|
func hasPrefix(_ prefix: String) -> Bool {
|
||||||
|
return String(self).hasPrefix(prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
|
|
||||||
* Use of this file is governed by the BSD 3-clause license that
|
|
||||||
* can be found in the LICENSE.txt file in the project root.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// StringBuilder.swift
|
|
||||||
// antlr.swift
|
|
||||||
//
|
|
||||||
// Created by janyou on 15/9/4.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public class StringBuilder {
|
|
||||||
private var stringValue: String
|
|
||||||
|
|
||||||
public init(string: String = "") {
|
|
||||||
self.stringValue = string
|
|
||||||
}
|
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return stringValue
|
|
||||||
}
|
|
||||||
|
|
||||||
public var length: Int {
|
|
||||||
return stringValue.length
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
public func append(_ string: String) -> StringBuilder {
|
|
||||||
stringValue += string
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
public func append<T:CustomStringConvertible>(_ value: T) -> StringBuilder {
|
|
||||||
stringValue += value.description
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
public func appendLine(_ string: String) -> StringBuilder {
|
|
||||||
stringValue += string + "\n"
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
public func appendLine<T:CustomStringConvertible>(_ value: T) -> StringBuilder {
|
|
||||||
stringValue += value.description + "\n"
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
@discardableResult
|
|
||||||
public func clear() -> StringBuilder {
|
|
||||||
stringValue = ""
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +=(lhs: StringBuilder, rhs: String) {
|
|
||||||
lhs.append(rhs)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +=<T:CustomStringConvertible>(lhs: StringBuilder, rhs: T) {
|
|
||||||
lhs.append(rhs.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +(lhs: StringBuilder, rhs: StringBuilder) -> StringBuilder {
|
|
||||||
return StringBuilder(string: lhs.toString() + rhs.toString())
|
|
||||||
}
|
|
||||||
|
|
|
@ -64,24 +64,20 @@ public class Trees {
|
||||||
/// parse trees and extract data appropriately.
|
/// parse trees and extract data appropriately.
|
||||||
///
|
///
|
||||||
public static func toStringTree(_ t: Tree, _ ruleNames: Array<String>?) -> String {
|
public static func toStringTree(_ t: Tree, _ ruleNames: Array<String>?) -> String {
|
||||||
var s: String = Utils.escapeWhitespace(getNodeText(t, ruleNames), false)
|
let s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false)
|
||||||
if t.getChildCount() == 0 {
|
if t.getChildCount() == 0 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
let buf: StringBuilder = StringBuilder()
|
var buf = "(\(s) "
|
||||||
buf.append("(")
|
|
||||||
s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false)
|
|
||||||
buf.append(s)
|
|
||||||
buf.append(" ")
|
|
||||||
let length = t.getChildCount()
|
let length = t.getChildCount()
|
||||||
for i in 0..<length {
|
for i in 0..<length {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
buf.append(" ")
|
buf += " "
|
||||||
}
|
}
|
||||||
buf.append(toStringTree(t.getChild(i)!, ruleNames))
|
buf += toStringTree(t.getChild(i)!, ruleNames)
|
||||||
}
|
}
|
||||||
buf.append(")")
|
buf += ")"
|
||||||
return buf.toString()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getNodeText(_ t: Tree, _ recog: Parser?) -> String {
|
public static func getNodeText(_ t: Tree, _ recog: Parser?) -> String {
|
||||||
|
|
|
@ -160,9 +160,6 @@ public class ParseTreeMatch: CustomStringConvertible {
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toString() -> String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
let info = succeeded() ? "succeeded" : "failed"
|
let info = succeeded() ? "succeeded" : "failed"
|
||||||
return "Match \(info); found \(getLabels().size()) labels"
|
return "Match \(info); found \(getLabels().size()) labels"
|
||||||
|
|
|
@ -311,7 +311,7 @@ public class ParseTreePatternMatcher {
|
||||||
for chunk in chunks {
|
for chunk in chunks {
|
||||||
if let tagChunk = chunk as? TagChunk {
|
if let tagChunk = chunk as? TagChunk {
|
||||||
// add special rule token or conjure up new token from name
|
// add special rule token or conjure up new token from name
|
||||||
let firstStr = String(tagChunk.getTag()[0])
|
let firstStr = String(tagChunk.getTag().first!)
|
||||||
if firstStr.lowercased() != firstStr {
|
if firstStr.lowercased() != firstStr {
|
||||||
let ttype = parser.getTokenType(tagChunk.getTag())
|
let ttype = parser.getTokenType(tagChunk.getTag())
|
||||||
if ttype == CommonToken.INVALID_TYPE {
|
if ttype == CommonToken.INVALID_TYPE {
|
||||||
|
@ -351,29 +351,34 @@ public class ParseTreePatternMatcher {
|
||||||
/// Split `<ID> = <e:expr> ;` into 4 chunks for tokenizing by _#tokenize_.
|
/// Split `<ID> = <e:expr> ;` into 4 chunks for tokenizing by _#tokenize_.
|
||||||
///
|
///
|
||||||
public func split(_ pattern: String) throws -> [Chunk] {
|
public func split(_ pattern: String) throws -> [Chunk] {
|
||||||
var p = 0
|
var p = pattern.startIndex
|
||||||
let n = pattern.length
|
let n = pattern.endIndex
|
||||||
var chunks = [Chunk]()
|
var chunks = [Chunk]()
|
||||||
// find all start and stop indexes first, then collect
|
// find all start and stop indexes first, then collect
|
||||||
var starts = [Int]()
|
var starts = [Range<String.Index>]()
|
||||||
var stops = [Int]()
|
var stops = [Range<String.Index>]()
|
||||||
|
let escapedStart = escape + start
|
||||||
|
let escapedStop = escape + stop
|
||||||
while p < n {
|
while p < n {
|
||||||
if p == pattern.indexOf(escape + start, startIndex: p) {
|
let slice = pattern[p...]
|
||||||
p += escape.length + start.length
|
if slice.hasPrefix(escapedStart) {
|
||||||
|
p = pattern.index(p, offsetBy: escapedStart.count)
|
||||||
}
|
}
|
||||||
else if p == pattern.indexOf(escape + stop, startIndex: p) {
|
else if slice.hasPrefix(escapedStop) {
|
||||||
p += escape.length + stop.length
|
p = pattern.index(p, offsetBy: escapedStop.count)
|
||||||
}
|
}
|
||||||
else if p == pattern.indexOf(start, startIndex: p) {
|
else if slice.hasPrefix(start) {
|
||||||
starts.append(p)
|
let upperBound = pattern.index(p, offsetBy: start.count)
|
||||||
p += start.length
|
starts.append(p ..< upperBound)
|
||||||
|
p = upperBound
|
||||||
}
|
}
|
||||||
else if p == pattern.indexOf(stop, startIndex: p) {
|
else if slice.hasPrefix(stop) {
|
||||||
stops.append(p)
|
let upperBound = pattern.index(p, offsetBy: stop.count)
|
||||||
p += stop.length
|
stops.append(p ..< upperBound)
|
||||||
|
p = upperBound
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p += 1
|
p = pattern.index(after: p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,46 +392,50 @@ public class ParseTreePatternMatcher {
|
||||||
|
|
||||||
let ntags = starts.count
|
let ntags = starts.count
|
||||||
for i in 0..<ntags {
|
for i in 0..<ntags {
|
||||||
if starts[i] >= stops[i] {
|
if starts[i].lowerBound >= stops[i].lowerBound {
|
||||||
throw ANTLRError.illegalArgument(msg: "tag delimiters out of order in pattern: " + pattern)
|
throw ANTLRError.illegalArgument(msg: "tag delimiters out of order in pattern: " + pattern)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect into chunks now
|
// collect into chunks now
|
||||||
if ntags == 0 {
|
if ntags == 0 {
|
||||||
let text = pattern[0 ..< n]
|
let text = String(pattern[..<n])
|
||||||
chunks.append(TextChunk(text))
|
chunks.append(TextChunk(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ntags > 0 && starts[0] > 0 {
|
if ntags > 0 && starts[0].lowerBound > pattern.startIndex {
|
||||||
// copy text up to first tag into chunks
|
// copy text up to first tag into chunks
|
||||||
let text = pattern[0 ..< starts[0]]
|
let text = pattern[pattern.startIndex ..< starts[0].lowerBound]
|
||||||
chunks.append(TextChunk(text))
|
chunks.append(TextChunk(String(text)))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 ..< ntags {
|
for i in 0 ..< ntags {
|
||||||
// copy inside of <tag>
|
// copy inside of <tag>
|
||||||
let tag = pattern[starts[i] + start.length ..< stops[i]]
|
let tag = pattern[starts[i].upperBound ..< stops[i].lowerBound]
|
||||||
var ruleOrToken = tag
|
let ruleOrToken: String
|
||||||
var label: String?
|
let label: String?
|
||||||
let colon = tag.indexOf(":")
|
let bits = tag.split(separator: ":", maxSplits: 1)
|
||||||
if colon >= 0 {
|
if bits.count == 2 {
|
||||||
label = tag[0 ..< colon]
|
label = String(bits[0])
|
||||||
ruleOrToken = tag[colon + 1 ..< tag.length]
|
ruleOrToken = String(bits[1])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
label = nil
|
||||||
|
ruleOrToken = String(tag)
|
||||||
}
|
}
|
||||||
chunks.append(try TagChunk(label, ruleOrToken))
|
chunks.append(try TagChunk(label, ruleOrToken))
|
||||||
if i + 1 < ntags {
|
if i + 1 < ntags {
|
||||||
// copy from end of <tag> to start of next
|
// copy from end of <tag> to start of next
|
||||||
let text = pattern[stops[i] + stop.length ..< starts[i + 1]]
|
let text = pattern[stops[i].upperBound ..< starts[i + 1].lowerBound]
|
||||||
chunks.append(TextChunk(text))
|
chunks.append(TextChunk(String(text)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ntags > 0 {
|
if ntags > 0 {
|
||||||
let afterLastTag = stops[ntags - 1] + stop.length
|
let afterLastTag = stops[ntags - 1].upperBound
|
||||||
if afterLastTag < n {
|
if afterLastTag < n {
|
||||||
// copy text from end of last tag to end
|
// copy text from end of last tag to end
|
||||||
let text = pattern[afterLastTag ..< n]
|
let text = pattern[afterLastTag ..< n]
|
||||||
chunks.append(TextChunk(text))
|
chunks.append(TextChunk(String(text)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +444,7 @@ public class ParseTreePatternMatcher {
|
||||||
let c = chunks[i]
|
let c = chunks[i]
|
||||||
if let tc = c as? TextChunk {
|
if let tc = c as? TextChunk {
|
||||||
let unescaped = tc.getText().replacingOccurrences(of: escape, with: "")
|
let unescaped = tc.getText().replacingOccurrences(of: escape, with: "")
|
||||||
if unescaped.length < tc.getText().length {
|
if unescaped.count < tc.getText().count {
|
||||||
chunks[i] = TextChunk(unescaped)
|
chunks[i] = TextChunk(unescaped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/// Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
|
||||||
|
/// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
/// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
import Antlr4
|
||||||
|
|
||||||
|
class RuntimeMetaDataTests: XCTestCase {
|
||||||
|
|
||||||
|
func testGetMajorMinorVersion() {
|
||||||
|
doGetMajorMinorVersionTest("", "")
|
||||||
|
doGetMajorMinorVersionTest("4", "4")
|
||||||
|
doGetMajorMinorVersionTest("4.", "4.")
|
||||||
|
doGetMajorMinorVersionTest("4.7", "4.7")
|
||||||
|
doGetMajorMinorVersionTest("4.7.1", "4.7")
|
||||||
|
doGetMajorMinorVersionTest("4-SNAPSHOT", "4")
|
||||||
|
doGetMajorMinorVersionTest("4.-SNAPSHOT", "4.")
|
||||||
|
doGetMajorMinorVersionTest("4.7-SNAPSHOT", "4.7")
|
||||||
|
doGetMajorMinorVersionTest("4.7.1-SNAPSHOT", "4.7")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func doGetMajorMinorVersionTest(_ input: String, _ expected: String) {
|
||||||
|
XCTAssertEqual(RuntimeMetaData.getMajorMinorVersion(input), expected)
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/// Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
|
||||||
|
/// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
/// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
@testable import Antlr4
|
||||||
|
|
||||||
|
class StringExtensionTests: XCTestCase {
|
||||||
|
|
||||||
|
func testLastIndex() {
|
||||||
|
doLastIndexTest("", "", nil)
|
||||||
|
doLastIndexTest("a", "", nil)
|
||||||
|
doLastIndexTest("a", "a", 0)
|
||||||
|
doLastIndexTest("aba", "a", 2)
|
||||||
|
doLastIndexTest("aba", "b", 1)
|
||||||
|
doLastIndexTest("abc", "d", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func doLastIndexTest(_ str: String, _ target: String, _ expectedOffset: Int?) {
|
||||||
|
let expectedIdx: String.Index?
|
||||||
|
if let expectedOffset = expectedOffset {
|
||||||
|
expectedIdx = str.index(str.startIndex, offsetBy: expectedOffset)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expectedIdx = nil
|
||||||
|
}
|
||||||
|
XCTAssertEqual(str.lastIndex(of: target), expectedIdx)
|
||||||
|
}
|
Loading…
Reference in New Issue