Skip to content

Instantly share code, notes, and snippets.

@dsyme
Created February 15, 2022 16:41
Show Gist options
  • Save dsyme/04e86e1965628745d63bf804bab82e1e to your computer and use it in GitHub Desktop.
Save dsyme/04e86e1965628745d63bf804bab82e1e to your computer and use it in GitHub Desktop.
namespace global
open System
open System.Collections.Generic
/// Naive ArgumentParser - only string arguments right now
///
/// Note expects "dotnet fsi vae.fsx <args>" as invocation
type ArgumentParser() =
let specs = ResizeArray()
let results = Dictionary()
#if INTERACTIVE
let scriptName = fsi.CommandLineArgs[0]
let args = fsi.CommandLineArgs[1..]
#else
let scriptName = System.Environment.GetCommandLineArgs()[0]
let args = System.Environment.GetCommandLineArgs()[1..]
#endif
let usage() =
printfn $"dotnet fsi {scriptName} <args>"
for (spec, hasValue, choices:string list, _, _, help) in specs do
printfn $""" {spec} {if not choices.IsEmpty then "{" + String.concat "|" choices + "}" elif hasValue then "<value> " else ""}{help}"""
let exitf fmt = Printf.kprintf (fun s -> Console.Write(s); usage(); exit 1) fmt
member _.getArgs() = args
member _.add_argument(name, ?choices: string list, ?required: bool, ?dflt: string, ?help: string) =
if required.IsSome && dflt.IsSome then failwith $"can't specify both required and dflt, invalid spec for argument argument {name}"
let required = defaultArg required false
let choices = defaultArg choices []
let help = defaultArg help ""
specs.Add((name, true, choices, required, dflt, help))
member _.add_argument0(name, ?help: string) =
let help = defaultArg help ""
specs.Add((name, false, [], false, Some "false", help))
member _.result(nm) = results[nm]
member _.resultInt(arg) =
let v = results[arg]
try int v
with _ -> exitf $"{scriptName}: invalid value {v} for {arg}, expected an integer"
member _.resultIntOption(arg) =
let v = results[arg]
if v = "" then None else
try Some (int v)
with _ -> exitf $"{scriptName}: invalid value {v} for {arg}, expected an integer"
member _.resultFloat(arg) =
let v = results[arg]
try float v
with _ -> exitf $"{scriptName}: invalid value {v} for {arg}, expected a floating point number"
member _.resultFloatOption(arg) =
let v = results[arg]
if v = "" then None else
try Some (float v)
with _ -> exitf $"{scriptName}: invalid value {v} for {arg}, expected a floating point number"
member _.resultBool(arg) =
let v = results[arg]
try bool.Parse v
with _ -> exitf $"{scriptName}: invalid value {v} for {arg}, expected true or false"
member _.parse_args() =
let mutable args = args |> Array.toList
if args.Length < 1 then
usage()
exit 1
while not args.IsEmpty do
let mutable processed = false
let arg = args.Head
args <- args.Tail
for (spec, hasValue, choices, _, _, _) in specs do
if spec = arg then
processed <- true
if hasValue then
if args.IsEmpty then
exitf $"{scriptName}: expected value for argument {spec}"
let v = args.Head
if choices <> [] && not (List.contains v choices) then
exitf $"{scriptName}: unrecognised value {v} for for argument {spec}, expected one of {choices}"
results[spec] <- v
args <- args.Tail
else
results[spec] <- "true"
if not processed then
exitf $"{scriptName}: unrecognised argument {arg}"
for (spec, _, _, required, dflt, _) in specs do
if required && not (results.ContainsKey spec) then
exitf $"{scriptName}: argument {spec} required but not specified"
if dflt.IsSome && not (results.ContainsKey spec) then
results.Add(spec, dflt.Value)
if dflt.IsNone && not (results.ContainsKey spec) then
results.Add(spec, "")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment