Created
February 15, 2022 16:41
-
-
Save dsyme/04e86e1965628745d63bf804bab82e1e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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