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:
--fooor--no-fooprovide the argument asyes/norespectively--foo=yes/--foo=on/--foo=true/--foo=1all parse asyes(case insensitive)--foo=no/--foo=off/--foo=false/--foo=0all parse asno(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:
-vvs--verbose - Single letter flags can coalesce with other single letter flags:
-abcis 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, soUSAGE: "--foo"would translate to the error messageUsage: myprogram --foo. If this is not present, it will be generated automatically.HELP: The help message displayed when the--helpflag 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 Parsing3 Tomo supports automatic command line argument parsing for programs.4 Here's a simple example:7 # greet.tm8 func main(name:Text, be_excited|E:Bool=no)9 if be_excited10 say("Hello $name!!!")11 else12 say("Hi $name.")13 ```15 This program will automatically support command line argument parsing19 $ tomo -e greet.tm20 Compiled executable: greet22 $ ./greet23 greet: Required argument 'name' was not provided!24 Usage: greet [--help] <name> [--be-excited|-E|--no-be-exited]26 $ ./greet --help27 Usage: greet [--help] <name> [--be-excited|-E|--no-be-excited]29 $ ./greet "Zaphod"30 Hi Zaphod.32 $ ./greet "Zaphod" --be-excited33 Hello Zaphod!!!35 $ ./greet "Zaphod" -E36 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-argument43 Usage: greet [--help] <name> [--be-excited|-E|--no-be-excited]44 ```46 Underscores in argument names are converted to dashes when parsing command line47 arguments.49 ## Running Programs Directly51 If you want to run a program directly (instead of compiling to an executable58 ## Positional vs Default Arguments63 it is required and the program will report a usage error if it is missing.65 ## Supported Argument Types67 Tomo automatically supports several argument types out of the box, but if there69 argument and parsing it yourself.71 ### Text73 Text arguments are the simplest: the input arguments are taken verbatim.75 ### Bool82 - Any other values will report a usage error84 ### Integers and Numbers86 Integer and number values can be passed and parsed automatically. Any failures92 exceed the representable range for those values are considered usage errors.94 ### Structs96 For structs, values can be passed using positional arguments for each struct97 field.99 ```100 # foo.tm101 struct Pair(x,y:Int)103 func main(pair:Pair)104 >> pair107 $ tomo foo.tm -- --pair 1 2108 Pair(x=1, y=2)109 ```111 Tomo does not currently support omitting fields with default values or passing112 individual struct fields by named flag.114 ### Enums116 For enums, values can be passed using the enum's tag name and each of its117 fields positionally (the same as for structs). Parsing is case-sensitive:119 ```120 # foo.tm121 enum Foo(Nothing, AnInteger(i:Int), TwoThings(i:Int, text:Text))122 func main(foo:Foo)123 >> foo125 $ tomo foo.tm -- Nothing126 Nothing128 $ tomo foo.tm -- AnInteger 123129 AnInteger(123)131 $ tomo foo.tm -- TwoThings 123 hello132 TwoThings(i=123, text="hello")133 ```135 Like structs, enums do not currently support passing fields as flags or136 omitting fields with default values.138 ### Lists of Text140 Currently, Tomo supports accepting arguments that take a list of text.141 List-of-text arguments can be passed like this:144 # many-texts.tm145 func main(args:[Text])146 >> args147 ```150 $ tomo many-texts.tm151 >> [] : [Text]153 $ tomo many-texts.tm one two three154 >> ["one", "two", "three"] : [Text]156 $ tomo many-texts.tm --args=one,two,three157 >> ["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 Arguments168 slightly different command line parsing rules:174 When single letter flags coalesce together, the first flags in the cluster must175 be boolean values, while the last one is allowed to be any type. This lets you176 specify several flags at once while still providing arguments:179 func main(output|o:Path? = none, verbose|v:Bool = no)180 ...181 ```183 $ tomo -e program.tm && ./program -vo outfile.txt`184 ```186 ## Help and Manpages189 you have one defined) with automatically generated usage information. If you190 add comments in front of your main function arguments, they will appear in the193 installed if you install your program.196 func main(197 # Whether or not to frob your gropnoggles198 frob: Bool = no199 )200 pass201 ```204 $ tomo -e myprogram.tm205 $ ./myprogram --help206 # Usage: ./myprogram [--help] [--frob|--no-frob]207 #208 # --frob|--no-frob Whether or not to frob your gropnoggles (default:no)209 #210 ```213 $ man .build/myprogram.1214 ```216 ```217 MYPROGRAM(1)219 NAME220 myprogram - a Tomo program222 OPTIONS223 --frob | --no-frob224 Whether or not to frob your gropnoggles225 ```227 ## Metadata229 You can specify metadata for a program, which is used for CLI messages like231 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 parameter240 --help Show this message241 "242 MANPAGE_DESCRIPTION: (./description.roff)243 ```245 Supported metadata:248 will be the name of the Tomo source file without the ".tm" extension.253 present, it will be generated automatically.256 is an argument parsing error. This should be a description of the program with257 a multi-line documentation of commonly used flags.