Skip to content

Commit ab48c8e

Browse files
committed
Divide the implementation
1 parent a274224 commit ab48c8e

File tree

5 files changed

+864
-830
lines changed

5 files changed

+864
-830
lines changed

Sources/FileCheck/BoxedTable.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// BoxedTable.swift
3+
// FileCheck
4+
//
5+
// Created by Robert Widmann on 3/9/17.
6+
//
7+
//
8+
9+
final class BoxedTable {
10+
var table : [String:String] = [:]
11+
12+
init() {}
13+
14+
subscript(_ i : String) -> String? {
15+
set {
16+
self.table[i] = newValue!
17+
}
18+
get {
19+
return self.table[i]
20+
}
21+
}
22+
}
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
//
2+
// CheckString.swift
3+
// FileCheck
4+
//
5+
// Created by Robert Widmann on 3/9/17.
6+
//
7+
//
8+
9+
import Foundation
10+
11+
enum CheckType {
12+
case none
13+
case plain
14+
case next
15+
case same
16+
case not
17+
case dag
18+
case label
19+
case badNot
20+
21+
/// MatchEOF - When set, this pattern only matches the end of file. This is
22+
/// used for trailing CHECK-NOTs.
23+
case EOF
24+
25+
// Get the size of the prefix extension.
26+
var size : Int {
27+
switch (self) {
28+
case .none:
29+
return 0
30+
case .badNot:
31+
return 0
32+
case .plain:
33+
return ":".utf8.count
34+
case .next:
35+
return "-NEXT:".utf8.count
36+
case .same:
37+
return "-SAME:".utf8.count
38+
case .not:
39+
return "-NOT:".utf8.count
40+
case .dag:
41+
return "-DAG:".utf8.count
42+
case .label:
43+
return "-LABEL:".utf8.count
44+
case .EOF:
45+
fatalError("Should not be using EOF size")
46+
}
47+
}
48+
}
49+
50+
/// CheckString - This is a check that we found in the input file.
51+
struct CheckString {
52+
/// The pattern to match.
53+
let pattern : Pattern
54+
55+
/// Which prefix name this check matched.
56+
let prefix : String
57+
58+
/// The location in the match file that the check string was specified.
59+
let loc : CheckLoc
60+
61+
/// These are all of the strings that are disallowed from occurring between
62+
/// this match string and the previous one (or start of file).
63+
var dagNotStrings : Array<Pattern>
64+
65+
/// Match check string and its "not strings" and/or "dag strings".
66+
func check(_ buffer : String, _ isLabelScanMode : Bool, _ variableTable : BoxedTable, _ options: FileCheckOptions) -> NSRange? {
67+
// This condition is true when we are scanning forward to find CHECK-LABEL
68+
// bounds we have not processed variable definitions within the bounded block
69+
// yet so cannot handle any final CHECK-DAG yet this is handled when going
70+
// over the block again (including the last CHECK-LABEL) in normal mode.
71+
let lastPos : Int
72+
let notStrings : [Pattern]
73+
if !isLabelScanMode {
74+
// Match "dag strings" (with mixed "not strings" if any).
75+
guard let (lp, ns) = self.checkDAG(buffer, variableTable, options) else {
76+
return nil
77+
}
78+
lastPos = lp
79+
notStrings = ns
80+
} else {
81+
lastPos = 0
82+
notStrings = []
83+
}
84+
85+
// Match itself from the last position after matching CHECK-DAG.
86+
let matchBuffer = buffer.substring(from: buffer.index(buffer.startIndex, offsetBy: lastPos))
87+
guard let range = self.pattern.match(matchBuffer, variableTable) else {
88+
if self.pattern.fixedString.isEmpty {
89+
diagnose(.error,
90+
at: self.loc,
91+
with: self.prefix + ": could not find a match for regex '\(self.pattern.regExPattern)' in input",
92+
options: options
93+
)
94+
} else if self.pattern.regExPattern.isEmpty {
95+
diagnose(.error,
96+
at: self.loc,
97+
with: self.prefix + ": could not find '\(self.pattern.fixedString)' in input",
98+
options: options
99+
)
100+
} else {
101+
diagnose(.error,
102+
at: self.loc,
103+
with: self.prefix + ": could not find '\(self.pattern.fixedString)' (with regex '\(self.pattern.regExPattern)') in input",
104+
options: options
105+
)
106+
}
107+
return nil
108+
}
109+
let (matchPos, matchLen) = (range.location, range.length)
110+
111+
// Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
112+
// or CHECK-NOT
113+
if !isLabelScanMode {
114+
let skippedRegion = buffer.substring(
115+
with: Range<String.Index>(
116+
uncheckedBounds: (
117+
buffer.index(buffer.startIndex, offsetBy: lastPos),
118+
buffer.index(buffer.startIndex, offsetBy: matchPos)
119+
)
120+
)
121+
)
122+
let rest = buffer.substring(from: buffer.index(buffer.startIndex, offsetBy: matchPos))
123+
124+
// If this check is a "CHECK-NEXT", verify that the previous match was on
125+
// the previous line (i.e. that there is one newline between them).
126+
if self.checkNext(skippedRegion, rest, options) {
127+
return nil
128+
}
129+
130+
// If this check is a "CHECK-SAME", verify that the previous match was on
131+
// the same line (i.e. that there is no newline between them).
132+
if self.checkSame(skippedRegion, rest, options) {
133+
return nil
134+
}
135+
136+
// If this match had "not strings", verify that they don't exist in the
137+
// skipped region.
138+
if self.checkNot(skippedRegion, notStrings, variableTable, options) {
139+
return nil
140+
}
141+
}
142+
143+
return NSRange(location: lastPos + matchPos, length: matchLen)
144+
}
145+
146+
/// Verify there is no newline in the given buffer.
147+
private func checkSame(_ buffer : String, _ rest : String, _ options: FileCheckOptions) -> Bool {
148+
if self.pattern.type != .same {
149+
return false
150+
}
151+
152+
// Count the number of newlines between the previous match and this one.
153+
// assert(Buffer.data() !=
154+
// SM.getMemoryBuffer(SM.FindBufferContainingLoc(
155+
// SMLoc::getFromPointer(Buffer.data())))
156+
// ->getBufferStart() &&
157+
// "CHECK-SAME can't be the first check in a file")
158+
159+
let (numNewLines, _ /*firstNewLine*/) = countNumNewlinesBetween(buffer)
160+
if numNewLines != 0 {
161+
diagnose(.error,
162+
at: self.loc,
163+
with: self.prefix + "-SAME: is not on the same line as the previous match",
164+
options: options
165+
)
166+
rest.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
167+
let loc = CheckLoc.inBuffer(buf.baseAddress!, buf)
168+
diagnose(.note, at: loc, with: "'next' match was here", options: options)
169+
}
170+
buffer.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
171+
let loc = CheckLoc.inBuffer(buf.baseAddress!, buf)
172+
diagnose(.note, at: loc, with: "previous match ended here", options: options)
173+
}
174+
return true
175+
}
176+
177+
return false
178+
}
179+
180+
/// Verify there is a single line in the given buffer.
181+
private func checkNext(_ buffer : String, _ rest : String, _ options: FileCheckOptions) -> Bool {
182+
if self.pattern.type != .next {
183+
return false
184+
}
185+
186+
// Count the number of newlines between the previous match and this one.
187+
// assert(Buffer.data() !=
188+
// SM.getMemoryBuffer(SM.FindBufferContainingLoc(
189+
// SMLoc::getFromPointer(Buffer.data())))
190+
// ->getBufferStart(), "CHECK-NEXT can't be the first check in a file")
191+
192+
let (numNewLines, firstNewLine) = countNumNewlinesBetween(buffer)
193+
if numNewLines == 0 {
194+
diagnose(.error, at: self.loc, with: prefix + "-NEXT: is on the same line as previous match", options: options)
195+
rest.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
196+
let loc = CheckLoc.inBuffer(buf.baseAddress!, buf)
197+
diagnose(.note, at: loc, with: "'next' match was here", options: options)
198+
}
199+
buffer.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
200+
let loc = CheckLoc.inBuffer(buf.baseAddress!, buf)
201+
diagnose(.note, at: loc, with: "previous match ended here", options: options)
202+
}
203+
return true
204+
}
205+
206+
if numNewLines != 1 {
207+
diagnose(.error, at: self.loc, with: prefix + "-NEXT: is not on the line after the previous match", options: options)
208+
rest.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
209+
let loc = CheckLoc.inBuffer(buf.baseAddress!, buf)
210+
diagnose(.note, at: loc, with: "'next' match was here", options: options)
211+
}
212+
buffer.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
213+
let loc = CheckLoc.inBuffer(buf.baseAddress!, buf)
214+
diagnose(.note, at: loc, with: "previous match ended here", options: options)
215+
if let fnl = firstNewLine {
216+
let noteLoc = CheckLoc.inBuffer(buf.baseAddress!.advanced(by: buffer.distance(from: buffer.startIndex, to: fnl)), buf)
217+
diagnose(.note, at: noteLoc, with: "non-matching line after previous match is here", options: options)
218+
}
219+
}
220+
return true
221+
}
222+
223+
return false
224+
}
225+
226+
/// Verify there's no "not strings" in the given buffer.
227+
private func checkNot(_ buffer : String, _ notStrings : [Pattern], _ variableTable : BoxedTable, _ options: FileCheckOptions) -> Bool {
228+
for pat in notStrings {
229+
assert(pat.type == .not, "Expect CHECK-NOT!")
230+
231+
guard let range = pat.match(buffer, variableTable) else {
232+
continue
233+
}
234+
buffer.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
235+
let loc = CheckLoc.inBuffer(buf.baseAddress!.advanced(by: range.location), buf)
236+
diagnose(.error, at: loc, with: self.prefix + "-NOT: string occurred!", options: options)
237+
}
238+
diagnose(.note, at: pat.patternLoc, with: self.prefix + "-NOT: pattern specified here", options: options)
239+
return true
240+
}
241+
242+
return false
243+
}
244+
245+
/// Match "dag strings" and their mixed "not strings".
246+
func checkDAG(_ buffer : String, _ variableTable : BoxedTable, _ options: FileCheckOptions) -> (Int, [Pattern])? {
247+
var notStrings = [Pattern]()
248+
if dagNotStrings.isEmpty {
249+
return (0, notStrings)
250+
}
251+
252+
var lastPos = 0
253+
var startPos = lastPos
254+
255+
for pattern in self.dagNotStrings {
256+
assert((pattern.type == .dag || pattern.type == .not), "Invalid CHECK-DAG or CHECK-NOT!")
257+
258+
if pattern.type == .not {
259+
notStrings.append(pattern)
260+
continue
261+
}
262+
263+
assert((pattern.type == .dag), "Expect CHECK-DAG!")
264+
265+
// CHECK-DAG always matches from the start.
266+
let matchBuffer = buffer.substring(from: buffer.index(buffer.startIndex, offsetBy: startPos))
267+
// With a group of CHECK-DAGs, a single mismatching means the match on
268+
// that group of CHECK-DAGs fails immediately.
269+
guard let range = pattern.match(matchBuffer, variableTable) else {
270+
// PrintCheckFailed(SM, Pat.getLoc(), Pat, MatchBuffer, VariableTable)
271+
return nil
272+
}
273+
274+
// Re-calc it as the offset relative to the start of the original string.
275+
let matchPos = range.location + startPos
276+
if !notStrings.isEmpty {
277+
if matchPos < lastPos {
278+
// Reordered?
279+
buffer.cString(using: .utf8)?.withUnsafeBufferPointer { buf in
280+
let loc1 = CheckLoc.inBuffer(buf.baseAddress!.advanced(by: matchPos), buf)
281+
diagnose(.error, at: loc1, with: prefix + "-DAG: found a match of CHECK-DAG reordering across a CHECK-NOT", options: options)
282+
let loc2 = CheckLoc.inBuffer(buf.baseAddress!.advanced(by: lastPos), buf)
283+
diagnose(.note, at: loc2, with: prefix + "-DAG: the farthest match of CHECK-DAG is found here", options: options)
284+
}
285+
diagnose(.note, at: notStrings[0].patternLoc, with: prefix + "-NOT: the crossed pattern specified here", options: options)
286+
diagnose(.note, at: pattern.patternLoc, with: prefix + "-DAG: the reordered pattern specified here", options: options)
287+
return nil
288+
}
289+
// All subsequent CHECK-DAGs should be matched from the farthest
290+
// position of all precedent CHECK-DAGs (including this one.)
291+
startPos = lastPos
292+
// If there's CHECK-NOTs between two CHECK-DAGs or from CHECK to
293+
// CHECK-DAG, verify that there's no 'not' strings occurred in that
294+
// region.
295+
let skippedRegion = buffer.substring(
296+
with: Range<String.Index>(
297+
uncheckedBounds: (
298+
buffer.index(buffer.startIndex, offsetBy: lastPos),
299+
buffer.index(buffer.startIndex, offsetBy: matchPos)
300+
)
301+
)
302+
)
303+
if self.checkNot(skippedRegion, notStrings, variableTable, options) {
304+
return nil
305+
}
306+
// Clear "not strings".
307+
notStrings.removeAll()
308+
}
309+
310+
// Update the last position with CHECK-DAG matches.
311+
lastPos = max(matchPos + range.length, lastPos)
312+
}
313+
314+
return (lastPos, notStrings)
315+
}
316+
}

0 commit comments

Comments
 (0)