Skip to content

Commit 18bc948

Browse files
authored
Merge pull request #19 from cgay/subcommands
Fix subcommand option parsing
2 parents 014c500 + a72ab27 commit 18bc948

6 files changed

+45
-17
lines changed

command-line-parser.dylan

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ copyright: See LICENSE file in this distribution.
66
//======================================================================
77
// The All-Singing, All-Dancing Argument Parser
88
//======================================================================
9+
//
910
// Ole J. Tetlie wrote an option parser, and it was pretty good. But it
1011
// didn't support all the option types required by d2c, and besides, we
1112
// felt a need to overdo something.
@@ -29,20 +30,12 @@ copyright: See LICENSE file in this distribution.
2930
// All the tokens on that command line are arguments. "-x" and "--y"
3031
// are options, and "bar" is a parameter. "baz" is a positional argument.
3132

32-
// todo -- There is no indication of default values in the generated synopsis,
33-
// and the syntax for specifying "syntax" and docstring is bizarre at
34-
// best. --cgay 2006.11.27
35-
3633
// TODO(cgay): <choice-option>: --foo=a|b|c (#f as choice means option
3734
// value is optional?)
3835

3936
// TODO(cgay): Add a required: (or required?: ?) init keyword that
4037
// makes non-positional args required else an error is generated.
4138

42-
// TODO(cgay): This error sucks: "<unknown-option>" is not present as
43-
// a key for {<string-table>: size 12}. How about "<unknown-option>
44-
// is not a recognized command-line option." See next item.
45-
4639
// TODO(cgay): With an option that has negative options (e.g.,
4740
// --verbose and --quiet in the same option) just show the positive
4841
// option in the synopsis but add a comment to the doc about the
@@ -747,14 +740,14 @@ define function process-tokens
747740
option.option-present? := #t;
748741
end;
749742
<short-option-token>, <long-option-token> =>
750-
let option = find-option(parser, value)
743+
let option = find-option(subcmd | parser, value)
751744
| usage-error("Unrecognized option: %s%s",
752745
if (value.size = 1) "-" else "--" end,
753746
value);
754747
if (instance?(option, <help-option>))
755748
// Handle --help early in case the remainder of the command line is
756749
// invalid or there are missing required arguments.
757-
print-synopsis(parser, subcmd);
750+
print-help(parser, subcmd);
758751
abort-command(0);
759752
end;
760753
parse-option(option, parser);

help.dylan

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ Module: command-line-parser
22
Synopsis: Implements the --help flag and help subcommand
33

44

5+
// TODO(cgay): Automatically display option default values. It's too easy to
6+
// forget to add %default% to the help string.
7+
58
// TODO(cgay): Wrap the descriptions nicely
69

710
define function program-name () => (name :: <string>)
@@ -68,12 +71,12 @@ define method execute-subcommand
6871
if (name)
6972
let subcmd = find-subcommand(parser, name);
7073
if (subcmd)
71-
print-synopsis(parser, subcmd);
74+
print-help(parser, subcmd);
7275
else
7376
usage-error("Subcommand %= not found.", name);
7477
end;
7578
else
76-
print-synopsis(parser, #f); // 'app help' same as 'app --help'
79+
print-help(parser, #f); // 'app help' same as 'app --help'
7780
end;
7881
end method;
7982

@@ -146,10 +149,10 @@ define method format-option-usage
146149
option.canonical-option-name
147150
end;
148151

149-
define open generic print-synopsis
152+
define open generic print-help
150153
(parser :: <command-line-parser>, subcmd :: false-or(<subcommand>), #key stream);
151154

152-
define method print-synopsis
155+
define method print-help
153156
(parser :: <command-line-parser>, subcmd == #f,
154157
#key stream :: <stream> = *standard-output*)
155158
format(stream, "%s\n", parser.command-help);
@@ -177,7 +180,7 @@ define method print-synopsis
177180
end;
178181
end method;
179182

180-
define method print-synopsis
183+
define method print-help
181184
(parser :: <command-line-parser>, subcmd :: <subcommand>,
182185
#key stream :: <stream> = *standard-output*)
183186
format(stream, "%s\n", subcmd.command-help);

library.dylan

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ define module command-line-parser
6969
add-option,
7070
parse-command-line,
7171
get-option-value,
72-
print-synopsis,
72+
print-help,
7373

7474
parse-option-value;
7575

tests/command-line-parser-test-suite.dylan

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ end test test-command-line-parser;
8989
define test test-synopsis-format ()
9090
let parser = make-parser();
9191
let synopsis = with-output-to-string (stream)
92-
print-synopsis(parser, #f, stream: stream)
92+
print-help(parser, #f, stream: stream)
9393
end;
9494
let expected = #:str:"x
9595

tests/command-line-parser-test-suite.lid

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ target-type: dll
33
files: command-line-parser-test-suite-library
44
options-test
55
command-line-parser-test-suite
6+
subcommands-test

tests/subcommands-test.dylan

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Module: command-line-parser-test-suite
2+
3+
4+
define class <subcommand-s1> (<subcommand>) end;
5+
6+
define test test-subcommand-parsing ()
7+
let global-a = make(<flag-option>, names: #("a"), help: "a help");
8+
let global-b = make(<parameter-option>, names: #("b"), help: "b help");
9+
let local-c = make(<flag-option>, names: #("c"), help: "c help");
10+
let positional-d = make(<positional-option>, names: #("d"), help: "d help");
11+
let positional-e = make(<positional-option>,
12+
repeated?: #t, names: #("e"), help: "e help");
13+
let s1 = make(<subcommand-s1>, name: "s1", help: "s1 help");
14+
add-option(s1, local-c);
15+
add-option(s1, positional-d);
16+
add-option(s1, positional-e);
17+
let p = make(<command-line-parser>,
18+
help: "main help",
19+
subcommands: list(s1));
20+
add-option(p, global-a);
21+
add-option(p, global-b);
22+
23+
// Done with setup
24+
25+
assert-no-errors(parse-command-line(p, #["-a", "s1", "-c", "d", "e", "e"]));
26+
assert-true(get-option-value(p, "a"));
27+
assert-false(get-option-value(p, "b"));
28+
assert-true(get-option-value(s1, "c"));
29+
assert-equal("d", get-option-value(s1, "d"));
30+
assert-equal(#["e", "e"], get-option-value(s1, "e"));
31+
end test;

0 commit comments

Comments
 (0)