Skip to content

Commit 62c300b

Browse files
[Option] Support MultiArg options
Teach SwiftOption to handle MultiArg options, including generating Options from Options.td, etc. Even the only MultiArg option in swift is a NoDriver option, still teach the OptionTable to parse such options to future proof the implementation.
1 parent 3e48888 commit 62c300b

File tree

8 files changed

+86
-16
lines changed

8 files changed

+86
-16
lines changed

Sources/SwiftDriver/Jobs/CommandLineArguments.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ extension Array where Element == Job.ArgTemplate {
5656
/// Append an option's spelling to the command line arguments.
5757
mutating func appendFlag(_ option: Option) {
5858
switch option.kind {
59-
case .flag, .joinedOrSeparate, .remaining, .separate:
59+
case .flag, .joinedOrSeparate, .remaining, .separate, .multiArg:
6060
break
6161
case .commaJoined, .input, .joined:
6262
fatalError("Option cannot be appended as a flag: \(option)")
@@ -95,7 +95,7 @@ extension Array where Element == Job.ArgTemplate {
9595
assert(!option.attributes.contains(.argumentIsPath))
9696
appendFlag(option.spelling + argument.asMultiple.joined(separator: ","))
9797

98-
case .remaining:
98+
case .remaining, .multiArg:
9999
appendFlag(option.spelling)
100100
for arg in argument.asMultiple {
101101
try appendSingleArgument(option: option, argument: arg)

Sources/SwiftOptions/Option.swift

+9-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public struct Option {
5656
/// An option with multiple arguments, which are collected by splitting
5757
/// the text directly following the spelling at each comma.
5858
case commaJoined
59+
/// An option with multiple arguments, which the number of arguments is
60+
/// specified by numArgs.
61+
case multiArg
5962
}
6063

6164
/// The spelling of the option, including any leading dashes.
@@ -81,18 +84,23 @@ public struct Option {
8184
/// The group in which this option occurs.
8285
public let group: Group?
8386

87+
/// The number of arguments for MultiArg options.
88+
public let numArgs: UInt
89+
8490
public init(_ spelling: String, _ kind: Kind,
8591
alias: Option? = nil,
8692
attributes: OptionAttributes = [], metaVar: String? = nil,
8793
helpText: String? = nil,
88-
group: Group? = nil) {
94+
group: Group? = nil,
95+
numArgs: UInt = 0) {
8996
self.spelling = spelling
9097
self.kind = kind
9198
self.aliasFunction = alias.map { aliasOption in { aliasOption }}
9299
self.attributes = attributes
93100
self.metaVar = metaVar
94101
self.helpText = helpText
95102
self.group = group
103+
self.numArgs = numArgs
96104
}
97105
}
98106

Sources/SwiftOptions/OptionParsing.swift

+15
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,21 @@ extension OptionTable {
171171

172172
parsedOptions.addOption(option, argument: .single(arguments[index]))
173173
index += 1
174+
175+
case .multiArg:
176+
if argument != option.spelling {
177+
throw OptionParseError.unknownOption(
178+
index: index - 1, argument: argument)
179+
}
180+
let endIdx = index + Int(option.numArgs)
181+
if endIdx > arguments.endIndex {
182+
throw OptionParseError.missingArgument(
183+
index: index - 1, argument: argument)
184+
}
185+
parsedOptions.addOption(option, argument: .multiple(Array()))
186+
arguments[index..<endIdx].map { String($0) }.forEach { parsedOptions.addInput($0) }
187+
index = endIdx
188+
174189
}
175190
}
176191
parsedOptions.buildIndex()

Sources/SwiftOptions/OptionTable.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ extension OptionTable {
7575
case .joined, .commaJoined:
7676
displayName += option.metaVar ?? "<value>"
7777

78-
case .separate, .remaining, .joinedOrSeparate:
78+
case .separate, .remaining, .joinedOrSeparate, .multiArg:
7979
displayName += " " + (option.metaVar ?? "<value>")
8080
}
8181

Sources/SwiftOptions/Options.swift

+27-9
Large diffs are not rendered by default.

Sources/SwiftOptions/ParsedOptions.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ extension ParsedOption: CustomStringConvertible {
7474
case .joinedOrSeparate, .separate:
7575
return option.spelling + " " + argument.asSingle.spm_shellEscaped()
7676

77-
case .remaining:
77+
case .remaining, .multiArg:
7878
let args = argument.asMultiple
7979
if args.isEmpty {
8080
return option.spelling
@@ -193,7 +193,7 @@ extension ParsedOptions {
193193
result.append(parsed.option.spelling)
194194
result.append(parsed.argument.asSingle)
195195

196-
case .remaining:
196+
case .remaining, .multiArg:
197197
result.append(parsed.option.spelling)
198198
result.append(contentsOf: parsed.argument.asMultiple)
199199
}

Sources/makeOptions/makeOptions.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum class OptionKind {
3434
RemainingArgs,
3535
CommaJoined,
3636
JoinedOrSeparate,
37+
MultiArg,
3738
};
3839

3940
#define LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(ID_PREFIX, PREFIX, NAME, ID, KIND, \
@@ -113,6 +114,7 @@ struct RawOption {
113114
unsigned flags;
114115
const char *helpText;
115116
const char *metaVar;
117+
unsigned numArgs;
116118

117119
bool isGroup() const {
118120
return kind == OptionKind::Group;
@@ -135,7 +137,7 @@ static const RawOption rawOptions[] = {
135137
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
136138
HELPTEXT, METAVAR, VALUES) \
137139
{ OptionID::Opt_##ID, PREFIX, NAME, swiftify(#ID), OptionKind::KIND, \
138-
OptionID::Opt_##GROUP, OptionID::Opt_##ALIAS, FLAGS, HELPTEXT, METAVAR },
140+
OptionID::Opt_##GROUP, OptionID::Opt_##ALIAS, FLAGS, HELPTEXT, METAVAR, PARAM },
139141
#include "swift/Option/Options.inc"
140142
#undef OPTION
141143
};
@@ -291,6 +293,10 @@ int makeOptions_main() {
291293
out << ".separate";
292294
break;
293295

296+
case OptionKind::MultiArg:
297+
out << ".multiArg";
298+
break;
299+
294300
case OptionKind::Group:
295301
case OptionKind::Unknown:
296302
assert(false && "Should have been filtered out");
@@ -350,6 +356,9 @@ int makeOptions_main() {
350356
if (option.group != OptionID::Opt_INVALID) {
351357
out << ", group: ." << groups[groupIndexByID[option.group]].id;
352358
}
359+
if (option.kind == OptionKind::MultiArg) {
360+
out << ", numArgs: " << option.numArgs;
361+
}
353362
out << ")\n";
354363
});
355364
});

Tests/SwiftOptionsTests/OptionParsingTests.swift

+20
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ final class SwiftDriverTests: XCTestCase {
3636
XCTAssertEqual(results.description, "-- input1 input2")
3737
}
3838

39+
func testParsingMultiArg() throws {
40+
var options = OptionTable()
41+
let two = Option("-two", .multiArg, attributes: [], numArgs: 2)
42+
let three = Option("-three", .multiArg, attributes: [], numArgs: 3)
43+
options.addNewOption(two)
44+
options.addNewOption(three)
45+
let results = try options.parse(["-two", "1", "2", "-three", "1", "2", "3", "-two", "2", "3"], for: .batch)
46+
XCTAssertEqual(results.description, "-two 1 2 -three 1 2 3 -two 2 3")
47+
// Check not enough arguments are passed.
48+
XCTAssertThrowsError(try options.parse(["-two", "1"], for: .batch)) { error in
49+
XCTAssertEqual(error as? OptionParseError, .missingArgument(index: 0, argument: "-two"))
50+
}
51+
}
52+
3953
func testParseErrors() {
4054
let options = OptionTable()
4155

@@ -85,3 +99,9 @@ final class SwiftDriverTests: XCTestCase {
8599
}
86100
}
87101
}
102+
103+
extension OptionTable {
104+
mutating func addNewOption(_ opt: Option) {
105+
options.append(opt)
106+
}
107+
}

0 commit comments

Comments
 (0)