Skip to content

Commit e5cbfcc

Browse files
committed
[benchmark][Gardening] Declarative ArgumentParser
The `ArgumentParser` now has a configuration phase which specifies the supported arguments and their handling. The configured parser is then executed using the `parse` method which returns the parsed result.
1 parent 9f90286 commit e5cbfcc

File tree

2 files changed

+41
-30
lines changed

2 files changed

+41
-30
lines changed

benchmark/utils/ArgParse.swift

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,31 +119,47 @@ func checked<T>(
119119

120120
class ArgumentParser<U> {
121121
var result: U
122-
let validOptions: [String]
123-
private let benchArgs: Arguments
122+
var validOptions: [String] = []
123+
private var benchArgs: Arguments!
124+
private var parsers: [() throws -> ()] = []
124125

125-
init(into result: U, validOptions: [String]) throws {
126+
init(into result: U) throws {
126127
self.result = result
127-
self.validOptions = validOptions
128-
guard let benchArgs = parseArgs(validOptions) else {
129-
throw ArgumentError.general("Failed to parse arguments")
130-
}
131-
self.benchArgs = benchArgs
132128
}
133129

134-
func parseArg<T>(
130+
func parse() throws -> U {
131+
guard let benchArgs = parseArgs(validOptions) else {
132+
throw ArgumentError.general("Failed to parse arguments")
133+
}
134+
self.benchArgs = benchArgs
135+
try parsers.forEach { try $0() } // parse all arguments
136+
return result
137+
}
138+
139+
func addArgument<T>(
135140
_ name: String?,
136141
_ property: WritableKeyPath<U, T>,
137142
defaultValue: T? = nil,
138-
parser parse: (String) throws -> T? = { _ in nil }
143+
parser: @escaping (String) throws -> T? = { _ in nil }
144+
) {
145+
if let name = name { validOptions.append(name) }
146+
parsers.append(
147+
{ try self.parseArgument(name, property, defaultValue, parser) })
148+
}
149+
150+
func parseArgument<T>(
151+
_ name: String?,
152+
_ property: WritableKeyPath<U, T>,
153+
_ defaultValue: T?,
154+
_ parse: (String) throws -> T?
139155
) throws {
140156
if let name = name, let value = benchArgs.optionalArgsMap[name] {
141157
guard !value.isEmpty || defaultValue != nil
142158
else { throw ArgumentError.missingValue(name) }
143159

144160
result[keyPath: property] = (value.isEmpty)
145161
? defaultValue!
146-
: try checked(parse, value, argument:name)
162+
: try checked(parse, value, argument: name)
147163
} else if name == nil {
148164
result[keyPath: property] = benchArgs.positionalArgs as! T
149165
}

benchmark/utils/DriverUtils.swift

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,6 @@ struct TestConfig {
7272
var tests: [String]?
7373
}
7474

75-
let p = try ArgumentParser(into: PartialTestConfig(), validOptions: [
76-
"--iter-scale", "--num-samples", "--num-iters",
77-
"--verbose", "--delim", "--list", "--sleep",
78-
"--tags", "--skip-tags", "--help"
79-
])
80-
8175
func tags(tags: String) throws -> Set<BenchmarkCategory> {
8276
// We support specifying multiple tags by splitting on comma, i.e.:
8377
// --tags=Array,Dictionary
@@ -87,21 +81,22 @@ struct TestConfig {
8781
try checked({ BenchmarkCategory(rawValue: $0) }, $0) })
8882
}
8983

90-
// Parse command line arguments
91-
try p.parseArg("--iter-scale", \.iterationScale) { Int($0) }
92-
try p.parseArg("--num-iters", \.fixedNumIters) { UInt($0) }
93-
try p.parseArg("--num-samples", \.numSamples) { Int($0) }
94-
try p.parseArg("--verbose", \.verbose, defaultValue: true)
95-
try p.parseArg("--delim", \.delim) { $0 }
96-
try p.parseArg("--tags", \PartialTestConfig.tags, parser: tags)
97-
try p.parseArg("--skip-tags", \PartialTestConfig.skipTags,
84+
// Configure the command line argument parser
85+
let p = try ArgumentParser(into: PartialTestConfig())
86+
p.addArgument("--iter-scale", \.iterationScale) { Int($0) }
87+
p.addArgument("--num-iters", \.fixedNumIters) { UInt($0) }
88+
p.addArgument("--num-samples", \.numSamples) { Int($0) }
89+
p.addArgument("--verbose", \.verbose, defaultValue: true)
90+
p.addArgument("--delim", \.delim) { $0 }
91+
p.addArgument("--tags", \PartialTestConfig.tags, parser: tags)
92+
p.addArgument("--skip-tags", \PartialTestConfig.skipTags,
9893
defaultValue: [], parser: tags)
99-
try p.parseArg("--sleep", \.afterRunSleep) { Int($0) }
100-
try p.parseArg("--list", \.action, defaultValue: .listTests)
101-
try p.parseArg("--help", \.action, defaultValue: .help(p.validOptions))
102-
try p.parseArg(nil, \.tests) // positional arguments
94+
p.addArgument("--sleep", \.afterRunSleep) { Int($0) }
95+
p.addArgument("--list", \.action, defaultValue: .listTests)
96+
p.addArgument("--help", \.action, defaultValue: .help(p.validOptions))
97+
p.addArgument(nil, \.tests) // positional arguments
10398

104-
let c = p.result
99+
let c = try p.parse()
105100

106101
// Configure from the command line arguments, filling in the defaults.
107102
delim = c.delim ?? ","

0 commit comments

Comments
 (0)