Fix ParseTreePatternMatcher.split in the Swift runtime.

Fix the parsing inside ParseTreePatternMatcher.split.  It was trivially
broken in a number of ways, with bugs that aren't in the Java version
that it was ported from, so it's obviously never been run before.

This adds unit tests for ParseTreePatternMatcher.split, and makes Chunk
implement Equatable, so that it we can compare Chunk instances in the
tests.

Tidy up the description implementations at the same time.
This commit is contained in:
Ewan Mellor 2017-11-01 22:18:55 -07:00
parent b4c34da1f0
commit fd8fd175a6
No known key found for this signature in database
GPG Key ID: 7CE1C6BC9EC8645D
5 changed files with 68 additions and 14 deletions

View File

@ -17,5 +17,12 @@
/// regular tokens of the text surrounding the tags. /// regular tokens of the text surrounding the tags.
/// ///
public class Chunk { public class Chunk: Equatable {
public static func ==(lhs: Chunk, rhs: Chunk) -> Bool {
return lhs.isEqual(rhs)
}
public func isEqual(_ other: Chunk) -> Bool {
return false
}
} }

View File

@ -387,7 +387,7 @@ 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] >= stops[i] {
throw ANTLRError.illegalArgument(msg: "tag delimiters out of order in pattern: " + pattern) throw ANTLRError.illegalArgument(msg: "tag delimiters out of order in pattern: " + pattern)
} }
} }
@ -408,7 +408,7 @@ public class ParseTreePatternMatcher {
// copy inside of <tag> // copy inside of <tag>
let tag = pattern[starts[i] + start.length ..< stops[i]] let tag = pattern[starts[i] + start.length ..< stops[i]]
var ruleOrToken = tag var ruleOrToken = tag
var label = "" var label: String?
let colon = tag.indexOf(":") let colon = tag.indexOf(":")
if colon >= 0 { if colon >= 0 {
label = tag[0 ..< colon] label = tag[0 ..< colon]
@ -417,7 +417,7 @@ public class ParseTreePatternMatcher {
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] + stop.length ..< starts[i + 1]]
chunks.append(TextChunk(text)) chunks.append(TextChunk(text))
} }
} }

View File

@ -87,10 +87,19 @@ public class TagChunk: Chunk, CustomStringConvertible {
/// returned as just the tag name. /// returned as just the tag name.
/// ///
public var description: String { public var description: String {
if label != nil { if let label = label {
return label! + ":" + tag return "\(label):\(tag)"
} }
else {
return tag return tag
} }
}
override public func isEqual(_ other: Chunk) -> Bool {
guard let other = other as? TagChunk else {
return false
}
return tag == other.tag && label == other.label
}
} }

View File

@ -36,15 +36,19 @@ public class TextChunk: Chunk, CustomStringConvertible {
return text return text
} }
///
///
/// ///
/// The implementation for _org.antlr.v4.runtime.tree.pattern.TextChunk_ returns the result of /// The implementation for _org.antlr.v4.runtime.tree.pattern.TextChunk_ returns the result of
/// _#getText()_ in single quotes. /// _#getText()_ in single quotes.
/// ///
public var description: String { public var description: String {
return "'" + text + "'" return "'\(text)'"
}
override public func isEqual(_ other: Chunk) -> Bool {
guard let other = other as? TextChunk else {
return false
}
return text == other.text
} }
} }

View File

@ -0,0 +1,34 @@
/// 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 ParseTreePatternMatcherTests: XCTestCase {
func testSplit() throws {
try doSplitTest("", [TextChunk("")])
try doSplitTest("Foo", [TextChunk("Foo")])
try doSplitTest("<ID> = <e:expr> ;",
[TagChunk("ID"), TextChunk(" = "), TagChunk("e", "expr"), TextChunk(" ;")])
try doSplitTest("\\<ID\\> = <e:expr> ;",
[TextChunk("<ID> = "), TagChunk("e", "expr"), TextChunk(" ;")])
}
}
private func doSplitTest(_ input: String, _ expected: [Chunk]) throws {
let matcher = try makeMatcher()
XCTAssertEqual(try matcher.split(input), expected)
}
private func makeMatcher() throws -> ParseTreePatternMatcher {
// The lexer and parser here aren't actually used. They're just here
// so that ParseTreePatternMatcher can be constructed, but in this file
// we're currently only testing methods that don't depend on them.
let lexer = Lexer()
let ts = BufferedTokenStream(lexer)
let parser = try Parser(ts)
return ParseTreePatternMatcher(lexer, parser)
}