code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(263 lines)

Command Line Parsing

Tomo supports automatic command line argument parsing for programs. Here's a simple example:

# greet.tm
func main(name:Text, be_excited|E:Bool=no)
    if be_excited
        say("Hello $name!!!")
    else
        say("Hi $name.")

This program will automatically support command line argument parsing for the arguments to main():

$ tomo -e greet.tm
Compiled executable: greet

$ ./greet
greet: Required argument 'name' was not provided!
Usage: greet [--help] <name> [--be-excited|-E|--no-be-exited]

$ ./greet --help
Usage: greet [--help] <name> [--be-excited|-E|--no-be-excited]

$ ./greet "Zaphod"
Hi Zaphod. 

$ ./greet "Zaphod" --be-excited
Hello Zaphod!!!

$ ./greet "Zaphod" -E
Hello Zaphod!!!

$ ./greet --no-be-excited --name="Zaphod"
Hi Zaphod.

$ ./greet --not-a-real-argument "Bob"
greet: Unrecognized argument: --not-a-real-argument
Usage: greet [--help] <name> [--be-excited|-E|--no-be-excited]

Underscores in argument names are converted to dashes when parsing command line arguments.

Running Programs Directly

If you want to run a program directly (instead of compiling to an executable with tomo -e), you can run the program with tomo program.tm -- [program arguments...]. The -- is required to separate the arguments passed to the Tomo compiler from those being passed to your program. For example, tomo greet.tm -- --help will pass the argument --help to your program, whereas tomo greet.tm --help will pass --help to tomo.

Positional vs Default Arguments

Any arguments with a default value must be specified with a --flag=value or --flag value. Arguments without a default value can be specified either by explicit --flag or positionally. If an argument does not have a default value it is required and the program will report a usage error if it is missing.

Supported Argument Types

Tomo automatically supports several argument types out of the box, but if there is a type that isn't supported, you can always fall back to accepting a Text argument and parsing it yourself.

Text

Text arguments are the simplest: the input arguments are taken verbatim.

Bool

For a boolean argument, foo, the argument can be passed in several ways:

  • --foo or --no-foo provide the argument as yes/no respectively
  • --foo=yes/--foo=on/--foo=true/--foo=1 all parse as yes (case insensitive)
  • --foo=no/--foo=off/--foo=false/--foo=0 all parse as no (case insensitive)
  • Any other values will report a usage error

Integers and Numbers

Integer and number values can be passed and parsed automatically. Any failures to parse will cause a usage error. Integers support decimal (123), hexadecimal (0xFF), and octal values (0o644). Nums support regular (123 or 1.23) or scientific notation (1e99).

For fixed-size integers (Int64, Int32, Int16, Int8), arguments that exceed the representable range for those values are considered usage errors.

Structs

For structs, values can be passed using positional arguments for each struct field.

# foo.tm
struct Pair(x,y:Int)

func main(pair:Pair)
    >> pair


$ tomo foo.tm -- --pair 1 2
Pair(x=1, y=2)

Tomo does not currently support omitting fields with default values or passing individual struct fields by named flag.

Enums

For enums, values can be passed using the enum's tag name and each of its fields positionally (the same as for structs). Parsing is case-sensitive:

# foo.tm
enum Foo(Nothing, AnInteger(i:Int), TwoThings(i:Int, text:Text))
func main(foo:Foo)
    >> foo

$ tomo foo.tm -- Nothing
Nothing

$ tomo foo.tm -- AnInteger 123
AnInteger(123)

$ tomo foo.tm -- TwoThings 123 hello
TwoThings(i=123, text="hello")

Like structs, enums do not currently support passing fields as flags or omitting fields with default values.

Lists of Text

Currently, Tomo supports accepting arguments that take a list of text. List-of-text arguments can be passed like this:

# many-texts.tm
func main(args:[Text])
    >> args
$ tomo many-texts.tm
>> [] : [Text]

$ tomo many-texts.tm one two three
>> ["one", "two", "three"] : [Text]

$ tomo many-texts.tm --args=one,two,three
>> ["one", "two", "three"] : [Text]

$ tomo many-texts.tm -- one --not-a-flag 'a space'
>> ["one", "--not-a-flag", "a space"] : [Text]

Aliases and Flag Arguments

Each argument may optionally have an alias of the form name|alias. This allows you to specify a long-form argument and a single-letter flag like verbose|v = no. Single letter flags (whether as an alias or as a main flag name) have slightly different command line parsing rules:

  • Single letter flags use only a single dash: -v vs --verbose
  • Single letter flags can coalesce with other single letter flags: -abc is the same as -a -b -c

When single letter flags coalesce together, the first flags in the cluster must be boolean values, while the last one is allowed to be any type. This lets you specify several flags at once while still providing arguments:

func main(output|o:Path? = none, verbose|v:Bool = no)
    ...
$ tomo -e program.tm && ./program -vo outfile.txt`

Help and Manpages

When your program is generated, it will also come with a --help flag (unless you have one defined) with automatically generated usage information. If you add comments in front of your main function arguments, they will appear in the --help output. Additionally, when your program is compiled, Tomo will also build a Manpage for your program in .build/yourprogram.1, which will get installed if you install your program.

func main(
    # Whether or not to frob your gropnoggles
    frob: Bool = no
)
    pass
$ tomo -e myprogram.tm
$ ./myprogram --help
# Usage: ./myprogram [--help] [--frob|--no-frob]
#
#  --frob|--no-frob Whether or not to frob your gropnoggles (default:no)
#
$ man .build/myprogram.1
MYPROGRAM(1)

NAME
       myprogram - a Tomo program

OPTIONS
       --frob | --no-frob
              Whether or not to frob your gropnoggles

Metadata

You can specify metadata for a program, which is used for CLI messages like --help, as well as manpage documentation. Metadata can be specified as either a text literal (no interpolation) or as a file path literal.

USAGE: "--foo <n>"
HELP: "
    This is some custom help text.
    You can use these flags:

    --foo <n>  The foo parameter
    --help     Show this message
"
MANPAGE_DESCRIPTION: (./description.roff)

Supported metadata:

  • EXECUTABLE: the name of the executable to compile. If not provided, the name will be the name of the Tomo source file without the ".tm" extension.

  • USAGE: the short form usage shown in CLI parsing errors and help pages. This should be a single line without the name of the program, so USAGE: "--foo" would translate to the error message Usage: myprogram --foo. If this is not present, it will be generated automatically.

  • HELP: The help message displayed when the --help flag is used or when there is an argument parsing error. This should be a description of the program with a multi-line documentation of commonly used flags.

  • MANPAGE: the full manpage (overrules the options below).

  • MANPAGE_SYNOPSYS: the synopsis section of the manpage (inserted literally).

  • MANPAGE_DESCRIPTION: the description section of the manpage (inserted literally).

1 # Command Line Parsing
3 Tomo supports automatic command line argument parsing for programs.
4 Here's a simple example:
6 ```tomo
7 # greet.tm
8 func main(name:Text, be_excited|E:Bool=no)
9 if be_excited
10 say("Hello $name!!!")
11 else
12 say("Hi $name.")
13 ```
15 This program will automatically support command line argument parsing
16 for the arguments to `main()`:
18 ```bash
19 $ tomo -e greet.tm
20 Compiled executable: greet
22 $ ./greet
23 greet: Required argument 'name' was not provided!
24 Usage: greet [--help] <name> [--be-excited|-E|--no-be-exited]
26 $ ./greet --help
27 Usage: greet [--help] <name> [--be-excited|-E|--no-be-excited]
29 $ ./greet "Zaphod"
30 Hi Zaphod.
32 $ ./greet "Zaphod" --be-excited
33 Hello Zaphod!!!
35 $ ./greet "Zaphod" -E
36 Hello Zaphod!!!
38 $ ./greet --no-be-excited --name="Zaphod"
39 Hi Zaphod.
41 $ ./greet --not-a-real-argument "Bob"
42 greet: Unrecognized argument: --not-a-real-argument
43 Usage: greet [--help] <name> [--be-excited|-E|--no-be-excited]
44 ```
46 Underscores in argument names are converted to dashes when parsing command line
47 arguments.
49 ## Running Programs Directly
51 If you want to run a program directly (instead of compiling to an executable
52 with `tomo -e`), you can run the program with `tomo program.tm -- [program
53 arguments...]`. The `--` is required to separate the arguments passed to the
54 Tomo compiler from those being passed to your program. For example, `tomo
55 greet.tm -- --help` will pass the argument `--help` to your program, whereas
56 `tomo greet.tm --help` will pass `--help` to `tomo`.
58 ## Positional vs Default Arguments
60 Any arguments with a default value must be specified with a `--flag=value` or
61 `--flag value`. Arguments without a default value can be specified either by
62 explicit `--flag` or positionally. If an argument does not have a default value
63 it is required and the program will report a usage error if it is missing.
65 ## Supported Argument Types
67 Tomo automatically supports several argument types out of the box, but if there
68 is a type that isn't supported, you can always fall back to accepting a `Text`
69 argument and parsing it yourself.
71 ### Text
73 Text arguments are the simplest: the input arguments are taken verbatim.
75 ### Bool
77 For a boolean argument, `foo`, the argument can be passed in several ways:
79 - `--foo` or `--no-foo` provide the argument as `yes`/`no` respectively
80 - `--foo=yes`/`--foo=on`/`--foo=true`/`--foo=1` all parse as `yes` (case insensitive)
81 - `--foo=no`/`--foo=off`/`--foo=false`/`--foo=0` all parse as `no` (case insensitive)
82 - Any other values will report a usage error
84 ### Integers and Numbers
86 Integer and number values can be passed and parsed automatically. Any failures
87 to parse will cause a usage error. Integers support decimal (`123`),
88 hexadecimal (`0xFF`), and octal values (`0o644`). Nums support regular (`123`
89 or `1.23`) or scientific notation (`1e99`).
91 For fixed-size integers (`Int64`, `Int32`, `Int16`, `Int8`), arguments that
92 exceed the representable range for those values are considered usage errors.
94 ### Structs
96 For structs, values can be passed using positional arguments for each struct
97 field.
99 ```
100 # foo.tm
101 struct Pair(x,y:Int)
103 func main(pair:Pair)
104 >> pair
107 $ tomo foo.tm -- --pair 1 2
108 Pair(x=1, y=2)
109 ```
111 Tomo does not currently support omitting fields with default values or passing
112 individual struct fields by named flag.
114 ### Enums
116 For enums, values can be passed using the enum's tag name and each of its
117 fields positionally (the same as for structs). Parsing is case-sensitive:
119 ```
120 # foo.tm
121 enum Foo(Nothing, AnInteger(i:Int), TwoThings(i:Int, text:Text))
122 func main(foo:Foo)
123 >> foo
125 $ tomo foo.tm -- Nothing
126 Nothing
128 $ tomo foo.tm -- AnInteger 123
129 AnInteger(123)
131 $ tomo foo.tm -- TwoThings 123 hello
132 TwoThings(i=123, text="hello")
133 ```
135 Like structs, enums do not currently support passing fields as flags or
136 omitting fields with default values.
138 ### Lists of Text
140 Currently, Tomo supports accepting arguments that take a list of text.
141 List-of-text arguments can be passed like this:
143 ```tomo
144 # many-texts.tm
145 func main(args:[Text])
146 >> args
147 ```
149 ```bash
150 $ tomo many-texts.tm
151 >> [] : [Text]
153 $ tomo many-texts.tm one two three
154 >> ["one", "two", "three"] : [Text]
156 $ tomo many-texts.tm --args=one,two,three
157 >> ["one", "two", "three"] : [Text]
159 $ tomo many-texts.tm -- one --not-a-flag 'a space'
160 >> ["one", "--not-a-flag", "a space"] : [Text]
161 ```
163 ## Aliases and Flag Arguments
165 Each argument may optionally have an alias of the form `name|alias`. This allows
166 you to specify a long-form argument and a single-letter flag like `verbose|v =
167 no`. Single letter flags (whether as an alias or as a main flag name) have
168 slightly different command line parsing rules:
170 - Single letter flags use only a single dash: `-v` vs `--verbose`
171 - Single letter flags can coalesce with other single letter flags: `-abc` is the
172 same as `-a -b -c`
174 When single letter flags coalesce together, the first flags in the cluster must
175 be boolean values, while the last one is allowed to be any type. This lets you
176 specify several flags at once while still providing arguments:
178 ```tomo
179 func main(output|o:Path? = none, verbose|v:Bool = no)
180 ...
181 ```
182 ```bash
183 $ tomo -e program.tm && ./program -vo outfile.txt`
184 ```
186 ## Help and Manpages
188 When your program is generated, it will also come with a `--help` flag (unless
189 you have one defined) with automatically generated usage information. If you
190 add comments in front of your main function arguments, they will appear in the
191 `--help` output. Additionally, when your program is compiled, Tomo will also
192 build a Manpage for your program in `.build/yourprogram.1`, which will get
193 installed if you install your program.
195 ```tomo
196 func main(
197 # Whether or not to frob your gropnoggles
198 frob: Bool = no
200 pass
201 ```
203 ```bash
204 $ tomo -e myprogram.tm
205 $ ./myprogram --help
206 # Usage: ./myprogram [--help] [--frob|--no-frob]
208 # --frob|--no-frob Whether or not to frob your gropnoggles (default:no)
210 ```
212 ```bash
213 $ man .build/myprogram.1
214 ```
216 ```
217 MYPROGRAM(1)
219 NAME
220 myprogram - a Tomo program
222 OPTIONS
223 --frob | --no-frob
224 Whether or not to frob your gropnoggles
225 ```
227 ## Metadata
229 You can specify metadata for a program, which is used for CLI messages like
230 `--help`, as well as manpage documentation. Metadata can be specified as either
231 a text literal (no interpolation) or as a file path literal.
233 ```
234 USAGE: "--foo <n>"
235 HELP: "
236 This is some custom help text.
237 You can use these flags:
239 --foo <n> The foo parameter
240 --help Show this message
242 MANPAGE_DESCRIPTION: (./description.roff)
243 ```
245 Supported metadata:
247 - `EXECUTABLE`: the name of the executable to compile. If not provided, the name
248 will be the name of the Tomo source file without the ".tm" extension.
250 - `USAGE`: the short form usage shown in CLI parsing errors and help pages. This
251 should be a single line without the name of the program, so `USAGE: "--foo"`
252 would translate to the error message `Usage: myprogram --foo`. If this is not
253 present, it will be generated automatically.
255 - `HELP`: The help message displayed when the `--help` flag is used or when there
256 is an argument parsing error. This should be a description of the program with
257 a multi-line documentation of commonly used flags.
259 - `MANPAGE`: the full manpage (overrules the options below).
261 - `MANPAGE_SYNOPSYS`: the synopsis section of the manpage (inserted literally).
263 - `MANPAGE_DESCRIPTION`: the description section of the manpage (inserted literally).