Skip to content

Commit 46e08b5

Browse files
authored
Merge pull request #8 from trill-lang/globalism
Allow user-defined global constants in capture patterns
2 parents 1c96658 + 8a531f0 commit 46e08b5

File tree

6 files changed

+125
-10
lines changed

6 files changed

+125
-10
lines changed

Sources/FileCheck/FileCheck.swift

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public struct FileCheckOptions: OptionSet {
2727
public static let matchFullLines = FileCheckOptions(rawValue: 1 << 2)
2828
/// Disable colored diagnostics.
2929
public static let disableColors = FileCheckOptions(rawValue: 1 << 3)
30+
/// Enables scoping for variables defined in patterns. Variables with names
31+
/// that do not start with `$` will be reset at the beginning of each
32+
/// `CHECK-LABEL` block.
33+
public static let scopedVariables = FileCheckOptions(rawValue: 1 << 4)
3034
}
3135

3236
/// `FileCheckFD` represents the standard output streams `FileCheck` is capable
@@ -101,6 +105,8 @@ extension FileCheckSource: ExpressibleByStringLiteral {
101105
/// - parameter FD: The file descriptor to override and read from.
102106
/// - parameter prefixes: Specifies one or more prefixes to match. By default
103107
/// these patterns are prefixed with "CHECK".
108+
/// - parameter globals: Specifies a dictionary of global variables whose
109+
/// names may be used in capture patterns.
104110
/// - parameter checkNot: Specifies zero or more prefixes to implicitly reject
105111
/// in the output stream. This can be used to implement LLVM-verifier-like
106112
/// checks.
@@ -111,7 +117,15 @@ extension FileCheckSource: ExpressibleByStringLiteral {
111117
/// file descriptor.
112118
///
113119
/// - returns: Whether or not FileCheck succeeded in verifying the file.
114-
public func fileCheckOutput(of FD : FileCheckFD = .stdout, withPrefixes prefixes : [String] = ["CHECK"], checkNot : [String] = [], against source : FileCheckSource = #file, options: FileCheckOptions = [], block : () -> ()) -> Bool {
120+
public func fileCheckOutput(
121+
of FD : FileCheckFD = .stdout,
122+
withPrefixes prefixes : [String] = ["CHECK"],
123+
withGlobals globals: [String:String] = [:],
124+
checkNot : [String] = [],
125+
against source : FileCheckSource = #file,
126+
options: FileCheckOptions = [],
127+
block : () -> ()
128+
) -> Bool {
115129
guard let validPrefixes = validateCheckPrefixes(prefixes) else {
116130
print("Supplied check-prefix is invalid! Prefixes must be unique and",
117131
"start with a letter and contain only alphanumeric characters,",
@@ -154,7 +168,7 @@ public func fileCheckOutput(of FD : FileCheckFD = .stdout, withPrefixes prefixes
154168
return false
155169
}
156170

157-
return check(input: input, against: checkStrings, options: options)
171+
return check(input: input, against: checkStrings, withGlobals: globals, options: options)
158172
}
159173

160174
private func overrideFDAndCollectOutput(file : FileCheckFD, of block : () -> ()) -> String {
@@ -511,12 +525,17 @@ private func readCheckStrings(in buf : UnsafeBufferPointer<CChar>, withPrefixes
511525
/// strings read from the check file.
512526
///
513527
/// Returns `false` if the input fails to satisfy the checks.
514-
private func check(input b : String, against checkStrings : [CheckString], options: FileCheckOptions) -> Bool {
528+
private func check(
529+
input b : String,
530+
against checkStrings : [CheckString],
531+
withGlobals globals: [String:String],
532+
options: FileCheckOptions
533+
) -> Bool {
515534
var buffer = Substring(b)
516535
var failedChecks = false
517536

518537
// This holds all the current filecheck variables.
519-
var variableTable = [String:String]()
538+
var variableTable = globals
520539

521540
var i = 0
522541
var j = 0
@@ -543,6 +562,20 @@ private func check(input b : String, against checkStrings : [CheckString], optio
543562
j += 1
544563
}
545564

565+
// Remove local variables from the variable table. Global variables
566+
// (start with `$`) are preserved.
567+
if options.contains(.scopedVariables) {
568+
var localVariables = [String]()
569+
localVariables.reserveCapacity(16)
570+
for (k, v) in variableTable where !k.hasPrefix("$") {
571+
localVariables.append(k)
572+
}
573+
574+
for k in localVariables {
575+
variableTable.removeValue(forKey: k)
576+
}
577+
}
578+
546579
while i != j {
547580
defer { i += 1 }
548581

Sources/FileCheck/Pattern.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,20 @@ final class Pattern {
186186
var isExpression = false
187187
let diagLoc = CheckLocation.inBuffer(pattern.baseAddress!, buf)
188188
for (i, c) in name.enumerated() {
189-
if i == 0 && c == "@" {
190-
if nameEnd != nil {
191-
diagnose(.error, at: diagLoc, with: "invalid name in named regex definition", options: options)
192-
return nil
189+
if i == 0 {
190+
// Global vars start with '$'
191+
if c == "$" {
192+
continue
193+
}
194+
195+
if c == "@" {
196+
if nameEnd != nil {
197+
diagnose(.error, at: diagLoc, with: "invalid name in named regex definition", options: options)
198+
return nil
199+
}
200+
isExpression = true
201+
continue
193202
}
194-
isExpression = true
195-
continue
196203
}
197204
if c != "_" && isalnum(Int32(c.utf8CodePoint)) == 0 && (!isExpression || (c != "+" && c != "-")) {
198205
diagnose(.error, at: diagLoc, with: "invalid name in named regex", options: options)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import FileCheck
2+
import XCTest
3+
import Foundation
4+
5+
class DefinesSpec : XCTestCase {
6+
func testGlobalDefines() {
7+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["PASSDEF"], withGlobals: ["VALUE":"10"]) {
8+
// PASSDEF: Value = [[VALUE]]
9+
print("Value = 10")
10+
})
11+
12+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["FAILDEF-ERRMSG"]) {
13+
// FAILDEF-ERRMSG: error: {{.*}}: could not find a match for regex 'Value = 20' in input
14+
// FAILDEF-ERRMSG: note: with variable 'VALUE' equal to '20'
15+
// FAILDEF-ERRMSG: note: possible intended match here
16+
XCTAssertFalse(fileCheckOutput(of: .stdout, withPrefixes: ["FAILDEF"], withGlobals: ["VALUE":"20"], options: [.disableColors]) {
17+
// FAILDEF: Value = [[VALUE]]
18+
print("Value = 10")
19+
})
20+
})
21+
}
22+
23+
#if !os(macOS)
24+
static var allTests = testCase([
25+
("testGlobalDefines", testGlobalDefines),
26+
])
27+
#endif
28+
}
29+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import FileCheck
2+
import XCTest
3+
import Foundation
4+
5+
class RegexScopeSpec : XCTestCase {
6+
func testRegexScope() {
7+
let block = { () -> () in
8+
// CHECK: [[LOCAL:loc.*]]
9+
// CHECK: [[$GLOBAL:glo.*]]
10+
print("""
11+
local
12+
global
13+
""")
14+
// CHECK: [[LOCAL]]2
15+
// CHECK: [[$GLOBAL]]2
16+
print("""
17+
local2
18+
global2
19+
""")
20+
// CHECK-LABEL: barrier
21+
print("""
22+
barrier:
23+
""")
24+
// LOCAL: [[LOCAL]]3
25+
// GLOBAL: [[$GLOBAL]]3
26+
print("""
27+
local3
28+
global3
29+
""")
30+
}
31+
XCTAssert(fileCheckOutput(of: .stdout, block: block))
32+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["CHECK", "GLOBAL"], block: block))
33+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["CHECK", "LOCAL"], block: block))
34+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["CHECK", "GLOBAL"], options: [.scopedVariables], block: block))
35+
XCTAssertFalse(fileCheckOutput(of: .stdout, withPrefixes: ["CHECK", "LOCAL"], options: [.scopedVariables], block: block))
36+
}
37+
#if !os(macOS)
38+
static var allTests = testCase([
39+
("testRegexScope", testRegexScope),
40+
])
41+
#endif
42+
}
43+

Tests/FileCheckTests/VariableRefSpec.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class VariableRefSpec : XCTestCase {
1414
print("op4 g1, g2, g1")
1515
})
1616
}
17+
1718
#if !os(macOS)
1819
static var allTests = testCase([
1920
("testSameLineVarRef", testSameLineVarRef),

Tests/LinuxMain.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import XCTest
55
#if !os(macOS)
66
XCTMain([
77
DAGSpec.allTests,
8+
DefinesSpec.allTests,
89
EmptySpec.allTests,
910
FileCheckSpec.allTests,
1011
LabelSpec.allTests,
1112
LineCountSpec.allTests,
1213
MultiPrefixSpec.allTests,
14+
RegexScopeSpec.allTests,
1315
VariableRefSpec.allTests,
1416
])
1517
#endif

0 commit comments

Comments
 (0)