From fa022b75eafb00482369079f71e528aabcd0b3d2 Mon Sep 17 00:00:00 2001
From: Smaug123 <3138005+Smaug123@users.noreply.github.com>
Date: Sun, 13 Apr 2025 11:52:17 +0100
Subject: [PATCH 01/26] WIP: complete arg parser
---
Directory.Build.props | 1 +
Playground/Domain.fs | 48 +++++
Playground/Library.fs | 358 +++++++++++++++++++++++++++++++++++
Playground/Playground.fsproj | 19 ++
Playground/Program.fs | 11 ++
WoofWare.Myriad.sln | 6 +
6 files changed, 443 insertions(+)
create mode 100644 Playground/Domain.fs
create mode 100644 Playground/Library.fs
create mode 100644 Playground/Playground.fsproj
create mode 100644 Playground/Program.fs
diff --git a/Directory.Build.props b/Directory.Build.props
index f0daa0b1..506a07b1 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -8,6 +8,7 @@
true
embedded
FS3388,FS3559
+ $(NoWarn),NU1900
diff --git a/Playground/Domain.fs b/Playground/Domain.fs
new file mode 100644
index 00000000..425d27b6
--- /dev/null
+++ b/Playground/Domain.fs
@@ -0,0 +1,48 @@
+namespace Playground
+
+open System
+open WoofWare.Myriad.Plugins
+
+[]
+type SubMode1 =
+ {
+ Info1 : int
+ Info2 : string
+ Rest : string list
+ }
+
+[]
+type SubMode2 =
+ {
+ Info1 : int
+ Info2 : string
+ Rest : int list
+ }
+
+[]
+type Mode1 =
+ {
+ Things : SubMode1
+ Whatnot : int
+ }
+
+[]
+type Mode2 =
+ {
+ Things : SubMode2
+ Whatnot : DateTime
+ }
+
+[]
+type Modes =
+ | Mode1 of Mode1
+ | Mode2 of Mode2
+
+[]
+type Args =
+ {
+ WhatToDo : Modes
+ []
+ OtherArgs : string list
+ }
+
diff --git a/Playground/Library.fs b/Playground/Library.fs
new file mode 100644
index 00000000..7dd79002
--- /dev/null
+++ b/Playground/Library.fs
@@ -0,0 +1,358 @@
+// The following code was mostly generated by Gemini 2.5 Pro (Experimental).
+// I have not reviewed it at all yet; I have simply made it compile and tightened up the types.
+
+namespace GeneratedParsers // Assuming a namespace
+
+open System
+open System.IO
+open Playground
+open WoofWare.Myriad.Plugins // Assuming attributes are here
+
+// Assume original type definitions are accessible here
+// [] type SubMode1 = { Info1 : int; Info2 : string; Rest : string list }
+// [] type SubMode2 = { Info1 : int; Info2 : string; Rest : int list }
+// [] type Mode1 = { Things : SubMode1; Whatnot : int }
+// [] type Mode2 = { Things : SubMode2; Whatnot : DateTime }
+// [] type Modes = | Mode1 of Mode1 | Mode2 of Mode2
+// [] type Args = { WhatToDo : Modes; [] OtherArgs : string list }
+
+
+/// Methods to parse arguments for the type Args
+[]
+module ArgsModule =
+
+ //--------------------------------------------------------------------------
+ // Internal state definitions for the multi-candidate DU parser
+ //--------------------------------------------------------------------------
+
+ /// State representing the parse progress for Mode1
+ type private State_Mode1 = {
+ mutable Things_Info1 : int option
+ mutable Things_Info2 : string option
+ Things_Rest : ResizeArray // Corresponds to --rest for Mode1
+ mutable Whatnot : int option
+ } with
+ static member Create() = {
+ Things_Info1 = None
+ Things_Info2 = None
+ Things_Rest = ResizeArray()
+ Whatnot = None
+ }
+
+ /// State representing the parse progress for Mode2
+ type private State_Mode2 = {
+ mutable Things_Info1 : int option
+ mutable Things_Info2 : string option
+ Things_Rest : ResizeArray // Corresponds to --rest for Mode2
+ mutable Whatnot : DateTime option
+ } with
+ static member Create() = {
+ Things_Info1 = None
+ Things_Info2 = None
+ Things_Rest = ResizeArray()
+ Whatnot = None
+ }
+
+ type private CandidateParseStateContents =
+ | Mode1 of State_Mode1
+ | Mode2 of State_Mode2
+
+ /// State for a single candidate parse path for the Modes DU
+ type private CandidateParseState_Modes = {
+ mutable IsViable : bool
+ Errors : ResizeArray // Errors specific to this candidate's path
+ ConsumedArgIndices : System.Collections.Generic.HashSet // Indices consumed *by this candidate*
+ CaseState : CandidateParseStateContents
+ CaseName : string
+ } with
+ static member CreateMode1() = {
+ IsViable = true
+ Errors = ResizeArray()
+ ConsumedArgIndices = System.Collections.Generic.HashSet()
+ CaseState = State_Mode1.Create() |> CandidateParseStateContents.Mode1
+ CaseName = "Mode1"
+ }
+ static member CreateMode2() = {
+ IsViable = true
+ Errors = ResizeArray()
+ ConsumedArgIndices = System.Collections.Generic.HashSet()
+ CaseState = State_Mode2.Create() |> CandidateParseStateContents.Mode2
+ CaseName = "Mode2"
+ }
+
+ //--------------------------------------------------------------------------
+ // Main Parser Logic
+ //--------------------------------------------------------------------------
+
+ type private ParseState_Args =
+ /// Ready to consume a key or positional arg
+ | AwaitingArg
+ /// Waiting to receive a value for the key we've already consumed (at given index)
+ | AwaitingValue of keyIndex: int * key: string
+
+ let parse' (getEnvironmentVariable: string -> string) (args: string list) : Args =
+ let ArgParser_errors = ResizeArray() // Global errors
+
+ let helpText () =
+ // Note: Help text generation for DUs needs careful thought.
+ // This version lists all possible args, but doesn't specify Mode context well.
+ [ (sprintf "%s int32%s%s" (sprintf "--%s" "info1") "" " (for Mode1/Mode2 Things)")
+ (sprintf "%s string%s%s" (sprintf "--%s" "info2") "" " (for Mode1/Mode2 Things)")
+ (sprintf "%s string%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode1 Things)")
+ (sprintf "%s int32%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode2 Things)")
+ (sprintf "%s int32%s%s" (sprintf "--%s" "whatnot") "" " (for Mode1)")
+ (sprintf "%s DateTime%s%s" (sprintf "--%s" "whatnot") "" " (for Mode2)")
+ (sprintf "%s string%s%s" (sprintf "--%s" "other-args") " (positional args) (can be repeated)" "")
+ ]
+ |> String.concat "\n"
+
+ // State for top-level fields
+ let arg_OtherArgs: string ResizeArray = ResizeArray()
+ let mutable candidates_WhatToDo: CandidateParseState_Modes list =
+ [ CandidateParseState_Modes.CreateMode1()
+ CandidateParseState_Modes.CreateMode2() ]
+ // Keep track of args consumed by *any* viable candidate for the DU
+ let consumedArgIndices_WhatToDo = System.Collections.Generic.HashSet()
+
+ //----------------------------------------------------------------------
+ // Helper functions for applying args to DU candidates
+ //----------------------------------------------------------------------
+
+ /// Tries to apply a key-value pair to a single candidate. Updates candidate state.
+ let applyKeyValueToCandidate (argIndex: int, keyIndex: int, key: string, value: string) (candidate: CandidateParseState_Modes) : unit =
+ if not candidate.IsViable then () else
+
+ match candidate.CaseState with
+ | Mode1 state ->
+ if String.Equals(key, "--info1", StringComparison.OrdinalIgnoreCase) then
+ match state.Things_Info1 with
+ | Some _ -> candidate.Errors.Add(sprintf "Argument '--info1' supplied multiple times for Mode1 candidate"); candidate.IsViable <- false
+ | None ->
+ try state.Things_Info1 <- Some(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' for --info1 (Mode1): %s" value ex.Message); candidate.IsViable <- false
+ elif String.Equals(key, "--info2", StringComparison.OrdinalIgnoreCase) then
+ match state.Things_Info2 with
+ | Some _ -> candidate.Errors.Add(sprintf "Argument '--info2' supplied multiple times for Mode1 candidate"); candidate.IsViable <- false
+ | None -> state.Things_Info2 <- Some value; candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ elif String.Equals(key, "--rest", StringComparison.OrdinalIgnoreCase) then
+ // String list for Mode1
+ state.Things_Rest.Add value; candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ elif String.Equals(key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
+ match state.Whatnot with
+ | Some _ -> candidate.Errors.Add(sprintf "Argument '--whatnot' supplied multiple times for Mode1 candidate"); candidate.IsViable <- false
+ | None ->
+ try state.Whatnot <- Some(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' as int32 for --whatnot (Mode1): %s" value ex.Message); candidate.IsViable <- false
+ else
+ // Key not relevant to Mode1, ignore it for this candidate
+ ()
+
+ | Mode2 state ->
+ if String.Equals(key, "--info1", StringComparison.OrdinalIgnoreCase) then
+ match state.Things_Info1 with
+ | Some _ -> candidate.Errors.Add(sprintf "Argument '--info1' supplied multiple times for Mode2 candidate"); candidate.IsViable <- false
+ | None ->
+ try state.Things_Info1 <- Some(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' for --info1 (Mode2): %s" value ex.Message); candidate.IsViable <- false
+ elif String.Equals(key, "--info2", StringComparison.OrdinalIgnoreCase) then
+ match state.Things_Info2 with
+ | Some _ -> candidate.Errors.Add(sprintf "Argument '--info2' supplied multiple times for Mode2 candidate"); candidate.IsViable <- false
+ | None -> state.Things_Info2 <- Some value; candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ elif String.Equals(key, "--rest", StringComparison.OrdinalIgnoreCase) then
+ // Int list for Mode2
+ try state.Things_Rest.Add(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' as int32 for --rest (Mode2): %s" value ex.Message); candidate.IsViable <- false
+ elif String.Equals(key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
+ match state.Whatnot with
+ | Some _ -> candidate.Errors.Add(sprintf "Argument '--whatnot' supplied multiple times for Mode2 candidate"); candidate.IsViable <- false
+ | None ->
+ try state.Whatnot <- Some(DateTime.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' as DateTime for --whatnot (Mode2): %s" value ex.Message); candidate.IsViable <- false
+ else
+ // Key not relevant to Mode2, ignore it for this candidate
+ ()
+
+ /// Processes a key-value pair across all candidates. Returns true if handled by *any* viable candidate.
+ let processKeyValue (keyIndex: int, key: string, valueIndex: int, value: string) : bool =
+ let mutable handled = false
+ for candidate in candidates_WhatToDo do
+ let initialConsumedCount = candidate.ConsumedArgIndices.Count
+ applyKeyValueToCandidate (valueIndex, keyIndex, key, value) candidate
+ if candidate.IsViable && candidate.ConsumedArgIndices.Count > initialConsumedCount then
+ // Mark as handled if *any* viable candidate consumed it
+ handled <- true
+ // Add consumed indices to the global set for leftover detection later
+ consumedArgIndices_WhatToDo.Add keyIndex |> ignore
+ consumedArgIndices_WhatToDo.Add valueIndex |> ignore
+ handled
+
+ /// Processes a flag across all candidates. Returns true if handled by *any* viable candidate.
+ /// Note: No boolean flags defined in this example, so this is trivial.
+ let setFlagValue (keyIndex: int, key: string) : bool =
+ let mutable handled = false
+ // Example: If --info1 were a flag for Mode1
+ // for candidate in candidates_WhatToDo do
+ // if candidate.IsViable && candidate.CaseName = "Mode1" then
+ // let state = candidate.CaseState :?> State_Mode1
+ // if String.Equals(key, "--info1", StringComparison.OrdinalIgnoreCase) then
+ // match state.Things_Info1 with // Assuming it was bool option
+ // | Some _ -> candidate.Errors.Add(...) ; candidate.IsViable <- false
+ // | None -> state.Things_Info1 <- Some true; candidate.ConsumedArgIndices.Add keyIndex |> ignore; handled <- true
+ // if handled then consumedArgIndices_WhatToDo.Add keyIndex |> ignore
+ handled // No flags in this specific schema
+
+
+ //----------------------------------------------------------------------
+ // Main parsing loop
+ //----------------------------------------------------------------------
+ let rec go (state: ParseState_Args) (args: (int * string) list) =
+ match args with
+ | [] -> // End of arguments
+ match state with
+ | ParseState_Args.AwaitingArg -> () // Expected state
+ | ParseState_Args.AwaitingValue (keyIndex, key) ->
+ // Trailing key without value
+ if not (setFlagValue (keyIndex, key)) then
+ // Not a flag either, report error
+ ArgParser_errors.Add (sprintf "Trailing argument '%s' (at index %d) requires a value." key keyIndex)
+
+ | (argIndex, arg) :: remainingArgs ->
+ match state with
+ | ParseState_Args.AwaitingArg ->
+ if arg = "--" then
+ // Consume rest as positional
+ remainingArgs |> List.iter (fun (i, positionalArg) ->
+ // Check if arg was potentially consumed by DU before adding
+ if not (consumedArgIndices_WhatToDo.Contains i) then
+ arg_OtherArgs.Add positionalArg
+ )
+ go ParseState_Args.AwaitingArg [] // Go to end state
+
+ elif arg.StartsWith("--", StringComparison.Ordinal) then
+ if arg = "--help" then
+ helpText () |> failwithf "Help text requested.\n%s"
+ else
+ let equalsPos = arg.IndexOf('=')
+ if equalsPos > 0 then
+ // --key=value format
+ let key = arg.[0 .. equalsPos - 1]
+ let value = arg.[equalsPos + 1 ..]
+ if not (processKeyValue (argIndex, key, argIndex, value)) then
+ // Key-value not handled by DU candidates, check if it belongs elsewhere (none in this example)
+ // If still not handled, consider it potentially positional only if not consumed by DU
+ if not (consumedArgIndices_WhatToDo.Contains argIndex) then
+ arg_OtherArgs.Add arg // Treat unhandled --key=value as positional
+ go ParseState_Args.AwaitingArg remainingArgs
+ else
+ // --key format (potential flag or key needing subsequent value)
+ if setFlagValue (argIndex, arg) then
+ consumedArgIndices_WhatToDo.Add argIndex |> ignore
+ go ParseState_Args.AwaitingArg remainingArgs // Flag consumed
+ else
+ go (ParseState_Args.AwaitingValue (argIndex, arg)) remainingArgs // Expect value next
+
+ else // Positional argument
+ // Add positional arg *only if* it hasn't been consumed by the DU logic
+ if not (consumedArgIndices_WhatToDo.Contains argIndex) then
+ arg_OtherArgs.Add arg
+ go ParseState_Args.AwaitingArg remainingArgs
+
+ | ParseState_Args.AwaitingValue (keyIndex, key) ->
+ // We have a key, current arg is its potential value
+ if processKeyValue (keyIndex, key, argIndex, arg) then
+ go ParseState_Args.AwaitingArg remainingArgs // Key-value pair consumed
+ else
+ // Value wasn't parseable/applicable for the key via DU candidates.
+ // Could the key have been a flag?
+ if setFlagValue (keyIndex, key) then
+ consumedArgIndices_WhatToDo.Add keyIndex |> ignore
+ // Flag consumed, reprocess the current arg in AwaitingArg state
+ go ParseState_Args.AwaitingArg ((argIndex, arg) :: remainingArgs)
+ else
+ // Not a flag, not a valid value. Error reported by processKeyValue/apply...
+ // Treat *both* key and arg as positional if not consumed by DU.
+ if not (consumedArgIndices_WhatToDo.Contains keyIndex) then
+ arg_OtherArgs.Add key
+ if not (consumedArgIndices_WhatToDo.Contains argIndex) then
+ arg_OtherArgs.Add arg
+ go ParseState_Args.AwaitingArg remainingArgs
+
+
+ args |> List.mapi (fun i s -> (i, s)) |> go ParseState_Args.AwaitingArg
+
+ //----------------------------------------------------------------------
+ // Final Validation and Assembly
+ //----------------------------------------------------------------------
+
+ // 1. Validate and Assemble the DU 'WhatToDo'
+ let viableWinners =
+ candidates_WhatToDo
+ |> List.filter (fun c -> c.IsViable)
+ // Further filter: ensure all required args *for the specific case* are present
+ // And ensure no args were left unconsumed *relative to this candidate*
+ |> List.filter (fun c ->
+ let mutable caseComplete = true
+ let caseErrors = ResizeArray()
+
+ // Check required fields based on case
+ match c.CaseState with
+ | Mode1 state ->
+ if state.Things_Info1.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info1' is required for Mode1.")
+ if state.Things_Info2.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info2' is required for Mode1.")
+ // Rest is list, always 'complete'
+ if state.Whatnot.IsNone then caseComplete <- false; caseErrors.Add("Argument '--whatnot' is required for Mode1.")
+ | Mode2 state ->
+ if state.Things_Info1.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info1' is required for Mode2.")
+ if state.Things_Info2.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info2' is required for Mode2.")
+ // Rest is list, always 'complete'
+ if state.Whatnot.IsNone then caseComplete <- false; caseErrors.Add("Argument '--whatnot' is required for Mode2.")
+
+ // Check for relative leftovers: Ensure all args were either consumed by this candidate or the top-level positional args
+ let isLeftover (i: int, _:string) =
+ not (c.ConsumedArgIndices.Contains i) && // Not consumed by this candidate
+ not (arg_OtherArgs.Contains (args.[i])) // Not consumed by top-level positional (approx check) - better check indices!
+ // A more accurate leftover check requires comparing consumed sets properly
+ let hasRelativeLeftovers = false // Simplified: Assume validation handles required fields, and global positional catches others.
+
+ if not caseComplete then c.Errors.AddRange caseErrors
+ caseComplete && not hasRelativeLeftovers
+ )
+
+ let whatToDoResult =
+ match viableWinners with
+ | [] ->
+ ArgParser_errors.Add("No valid parse found for 'WhatToDo'.")
+ // Add specific errors from candidates if available
+ candidates_WhatToDo
+ |> List.iter (fun c -> if c.Errors.Count <> 0 then ArgParser_errors.Add(sprintf " Candidate %s errors: %s" c.CaseName (String.concat "; " c.Errors)))
+ Unchecked.defaultof<_> // Error path
+ | [winner] ->
+ // Assemble the winning case
+ match winner.CaseState with
+ | Mode1 state ->
+ // We know required fields are Some(_) due to filter above
+ let subMode1: SubMode1 = { Info1 = state.Things_Info1.Value; Info2 = state.Things_Info2.Value; Rest = state.Things_Rest |> Seq.toList }
+ let mode1: Mode1 = { Things = subMode1; Whatnot = state.Whatnot.Value }
+ Modes.Mode1 mode1
+ | Mode2 state ->
+ let subMode2 = { Info1 = state.Things_Info1.Value; Info2 = state.Things_Info2.Value; Rest = state.Things_Rest |> Seq.toList }
+ let mode2 = { Things = subMode2; Whatnot = state.Whatnot.Value }
+ Modes.Mode2 mode2
+
+ | winners -> // Ambiguous parse
+ ArgParser_errors.Add("Ambiguous parse for 'WhatToDo'. Multiple modes matched:")
+ winners |> List.iter (fun c -> ArgParser_errors.Add(sprintf " - %s" c.CaseName))
+ Unchecked.defaultof<_> // Error path
+
+ // 2. Finalize OtherArgs
+ let otherArgsResult = arg_OtherArgs |> Seq.toList
+
+ // 3. Assemble Final Result or Fail
+ if ArgParser_errors.Count > 0 then
+ ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s\n\nHelp Text:\n%s" (helpText())
+ else
+ { WhatToDo = whatToDoResult; OtherArgs = otherArgsResult }
+
+ /// Parses the command line arguments into an Args record.
+ let parse (args: string list) : Args =
+ parse' System.Environment.GetEnvironmentVariable args
diff --git a/Playground/Playground.fsproj b/Playground/Playground.fsproj
new file mode 100644
index 00000000..cddb718b
--- /dev/null
+++ b/Playground/Playground.fsproj
@@ -0,0 +1,19 @@
+
+
+
+ net9.0
+ true
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Playground/Program.fs b/Playground/Program.fs
new file mode 100644
index 00000000..a96d3b55
--- /dev/null
+++ b/Playground/Program.fs
@@ -0,0 +1,11 @@
+namespace Playground
+
+open GeneratedParsers
+
+module Program =
+ []
+ let main argv =
+ ["--whatnot=2024-01-12";"--info1=4";"--info2=hi"]
+ |> ArgsModule.parse
+ |> printfn "%O"
+ 0
diff --git a/WoofWare.Myriad.sln b/WoofWare.Myriad.sln
index 61d3d2ba..14f8b813 100644
--- a/WoofWare.Myriad.sln
+++ b/WoofWare.Myriad.sln
@@ -10,6 +10,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Att
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Attributes.Test", "WoofWare.Myriad.Plugins.Attributes\Test\WoofWare.Myriad.Plugins.Attributes.Test.fsproj", "{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Playground", "Playground\Playground.fsproj", "{6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -36,5 +38,9 @@ Global
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
From 4befdb93e58b4459da762ca921be0c3d64ea8b65 Mon Sep 17 00:00:00 2001
From: Smaug123 <3138005+Smaug123@users.noreply.github.com>
Date: Sun, 13 Apr 2025 18:43:23 +0100
Subject: [PATCH 02/26] WIP: define the helper types
---
ConsumePlugin/ConsumePlugin.fsproj | 4 +
ConsumePlugin/GeneratedArgs.fs | 4457 ++---------------
Playground/Domain.fs | 1 -
Playground/Library.fs | 733 ++-
Playground/Program.fs | 7 +-
WoofWare.Myriad.Plugins/ShibaGenerator.fs | 629 +++
.../WoofWare.Myriad.Plugins.fsproj | 3 +-
7 files changed, 1519 insertions(+), 4315 deletions(-)
create mode 100644 WoofWare.Myriad.Plugins/ShibaGenerator.fs
diff --git a/ConsumePlugin/ConsumePlugin.fsproj b/ConsumePlugin/ConsumePlugin.fsproj
index 7d053e93..41927305 100644
--- a/ConsumePlugin/ConsumePlugin.fsproj
+++ b/ConsumePlugin/ConsumePlugin.fsproj
@@ -12,6 +12,7 @@
+
Args.fs
+
diff --git a/ConsumePlugin/GeneratedArgs.fs b/ConsumePlugin/GeneratedArgs.fs
index 7a0752fd..f852cda1 100644
--- a/ConsumePlugin/GeneratedArgs.fs
+++ b/ConsumePlugin/GeneratedArgs.fs
@@ -8,2501 +8,455 @@
-namespace ConsumePlugin
-
-open System
-open System.IO
-open WoofWare.Myriad.Plugins
+namespace ArgParserHelpers
+
+/// Helper types for arg parsing
+module private ArgParseHelpers_ConsumePlugin =
+ open System
+ open System.IO
+ open WoofWare.Myriad.Plugins
+ open ConsumePlugin
+
+ /// A partially-parsed BasicNoPositionals.
+ type private BasicNoPositionals_InProgress =
+ {
+ Foo : System.Int32 option
+ Bar : System.String option
+ Baz : System.Boolean option
+ Rest : string list
+ }
+
+ member this.Assemble (positionals : string list) : BasicNoPositionals = "TODO: now construct the object"
+
+ /// A partially-parsed Basic.
+ type private Basic_InProgress =
+ {
+ Foo : System.Int32 option
+ Bar : System.String option
+ Baz : System.Boolean option
+ }
+
+ member this.Assemble (positionals : string list) : Basic = "TODO: now construct the object"
+
+ /// A partially-parsed BasicWithIntPositionals.
+ type private BasicWithIntPositionals_InProgress =
+ {
+ Foo : System.Int32 option
+ Bar : System.String option
+ Baz : System.Boolean option
+ }
+
+ member this.Assemble (positionals : string list) : BasicWithIntPositionals = "TODO: now construct the object"
+
+ /// A partially-parsed LoadsOfTypes.
+ type private LoadsOfTypes_InProgress =
+ {
+ Foo : System.Int32 option
+ Bar : System.String option
+ Baz : System.Boolean option
+ SomeFile : FileInfo option
+ SomeDirectory : DirectoryInfo option
+ SomeList : string list
+ OptionalThingWithNoDefault : int option
+ OptionalThing : bool option
+ AnotherOptionalThing : int option
+ YetAnotherOptionalThing : string option
+ }
+
+ member this.Assemble (positionals : string list) : LoadsOfTypes = "TODO: now construct the object"
+
+ /// A partially-parsed LoadsOfTypesNoPositionals.
+ type private LoadsOfTypesNoPositionals_InProgress =
+ {
+ Foo : System.Int32 option
+ Bar : System.String option
+ Baz : System.Boolean option
+ SomeFile : FileInfo option
+ SomeDirectory : DirectoryInfo option
+ SomeList : string list
+ OptionalThingWithNoDefault : int option
+ OptionalThing : bool option
+ AnotherOptionalThing : int option
+ YetAnotherOptionalThing : string option
+ }
+
+ member this.Assemble (positionals : string list) : LoadsOfTypesNoPositionals = "TODO: now construct the object"
+
+ /// A partially-parsed DatesAndTimes.
+ type private DatesAndTimes_InProgress =
+ {
+ Plain : TimeSpan option
+ Invariant : TimeSpan option
+ Exact : TimeSpan option
+ InvariantExact : TimeSpan option
+ }
+
+ member this.Assemble (positionals : string list) : DatesAndTimes = "TODO: now construct the object"
+
+ /// A partially-parsed ChildRecord.
+ type private ChildRecord_InProgress =
+ {
+ Thing1 : System.Int32 option
+ Thing2 : System.String option
+ }
+
+ member this.Assemble (positionals : string list) : ChildRecord = "TODO: now construct the object"
+
+ /// A partially-parsed ParentRecord.
+ type private ParentRecord_InProgress =
+ {
+ Child : ChildRecord_InProgress
+ AndAnother : System.Boolean option
+ }
+
+ member this.Assemble (positionals : string list) : ParentRecord = "TODO: now construct the object"
+
+ /// A partially-parsed ChildRecordWithPositional.
+ type private ChildRecordWithPositional_InProgress =
+ {
+ Thing1 : System.Int32 option
+ }
+
+ member this.Assemble (positionals : string list) : ChildRecordWithPositional = "TODO: now construct the object"
+
+ /// A partially-parsed ParentRecordChildPos.
+ type private ParentRecordChildPos_InProgress =
+ {
+ Child : ChildRecordWithPositional_InProgress
+ AndAnother : System.Boolean option
+ }
+
+ member this.Assemble (positionals : string list) : ParentRecordChildPos = "TODO: now construct the object"
+
+ /// A partially-parsed ParentRecordSelfPos.
+ type private ParentRecordSelfPos_InProgress =
+ {
+ Child : ChildRecord_InProgress
+ }
+
+ member this.Assemble (positionals : string list) : ParentRecordSelfPos = "TODO: now construct the object"
+
+ /// A partially-parsed ChoicePositionals.
+ type private ChoicePositionals_InProgress =
+ {
+ _Dummy : unit
+ }
+
+ member this.Assemble (positionals : string list) : ChoicePositionals = "TODO: now construct the object"
+
+ /// A partially-parsed ContainsBoolEnvVar.
+ type private ContainsBoolEnvVar_InProgress =
+ {
+ BoolVar : bool option
+ }
+
+ member this.Assemble (positionals : string list) : ContainsBoolEnvVar = "TODO: now construct the object"
+
+ /// A partially-parsed WithFlagDu.
+ type private WithFlagDu_InProgress =
+ {
+ DryRun : DryRunMode option
+ }
+
+ member this.Assemble (positionals : string list) : WithFlagDu = "TODO: now construct the object"
+
+ /// A partially-parsed ContainsFlagEnvVar.
+ type private ContainsFlagEnvVar_InProgress =
+ {
+ DryRun : DryRunMode option
+ }
+
+ member this.Assemble (positionals : string list) : ContainsFlagEnvVar = "TODO: now construct the object"
+
+ /// A partially-parsed ContainsFlagDefaultValue.
+ type private ContainsFlagDefaultValue_InProgress =
+ {
+ DryRun : DryRunMode option
+ }
+
+ member this.Assemble (positionals : string list) : ContainsFlagDefaultValue = "TODO: now construct the object"
+
+ /// A partially-parsed ManyLongForms.
+ type private ManyLongForms_InProgress =
+ {
+ DoTheThing : System.String option
+ SomeFlag : System.Boolean option
+ }
+
+ member this.Assemble (positionals : string list) : ManyLongForms = "TODO: now construct the object"
+
+ /// A partially-parsed FlagsIntoPositionalArgs.
+ type private FlagsIntoPositionalArgs_InProgress =
+ {
+ A : System.String option
+ }
+
+ member this.Assemble (positionals : string list) : FlagsIntoPositionalArgs = "TODO: now construct the object"
+
+ /// A partially-parsed FlagsIntoPositionalArgsChoice.
+ type private FlagsIntoPositionalArgsChoice_InProgress =
+ {
+ A : System.String option
+ }
+
+ member this.Assemble (positionals : string list) : FlagsIntoPositionalArgsChoice =
+ "TODO: now construct the object"
+
+ /// A partially-parsed FlagsIntoPositionalArgsInt.
+ type private FlagsIntoPositionalArgsInt_InProgress =
+ {
+ A : System.String option
+ }
-/// Methods to parse arguments for the type BasicNoPositionals
-[]
-module BasicNoPositionals =
- type private ParseState_BasicNoPositionals =
- /// Ready to consume a key or positional arg
- | AwaitingKey
- /// Waiting to receive a value for the key we've already consumed
- | AwaitingValue of key : string
+ member this.Assemble (positionals : string list) : FlagsIntoPositionalArgsInt = "TODO: now construct the object"
+
+ /// A partially-parsed FlagsIntoPositionalArgsIntChoice.
+ type private FlagsIntoPositionalArgsIntChoice_InProgress =
+ {
+ A : System.String option
+ }
- let parse' (getEnvironmentVariable : string -> string) (args : string list) : BasicNoPositionals =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s int32%s%s" (sprintf "--%s" "foo") "" "")
- (sprintf "%s string%s%s" (sprintf "--%s" "bar") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s" "baz") "" "")
- (sprintf "%s int32%s%s" (sprintf "--%s" "rest") " (can be repeated)" "")
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : int option = None
- let mutable arg_1 : string option = None
- let mutable arg_2 : bool option = None
- let arg_3 : int ResizeArray = ResizeArray ()
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "rest", System.StringComparison.OrdinalIgnoreCase) then
- value |> (fun x -> System.Int32.Parse x) |> arg_3.Add
- () |> Ok
- else if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "baz")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "bar", System.StringComparison.OrdinalIgnoreCase) then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "bar")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "foo", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "foo")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_2 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_BasicNoPositionals) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_BasicNoPositionals.AwaitingKey -> ()
- | ParseState_BasicNoPositionals.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_BasicNoPositionals.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_BasicNoPositionals.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_BasicNoPositionals.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf "Unable to process argument %s as key %s and value %s" arg key value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_BasicNoPositionals.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_BasicNoPositionals.AwaitingKey args
- | ParseState_BasicNoPositionals.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_BasicNoPositionals.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_BasicNoPositionals.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf "Unable to process supplied arg %s. Help text follows.\n%s" key (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_BasicNoPositionals.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "foo")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "bar")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_3 = arg_3 |> Seq.toList
-
- if 0 = ArgParser_errors.Count then
- {
- Bar = arg_1
- Baz = arg_2
- Foo = arg_0
- Rest = arg_3
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ member this.Assemble (positionals : string list) : FlagsIntoPositionalArgsIntChoice =
+ "TODO: now construct the object"
+
+ /// A partially-parsed FlagsIntoPositionalArgs'.
+ type private FlagsIntoPositionalArgs'_InProgress =
+ {
+ A : System.String option
+ }
- let parse (args : string list) : BasicNoPositionals =
- parse' System.Environment.GetEnvironmentVariable args
+ member this.Assemble (positionals : string list) : FlagsIntoPositionalArgs' = "TODO: now construct the object"
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type Basic
-[]
-module Basic =
- type private ParseState_Basic =
+/// Methods to parse arguments for the type FlagsIntoPositionalArgs'
+[]
+module FlagsIntoPositionalArgs'ArgParse =
+ type private ParseState_FlagsIntoPositionalArgs' =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- let parse' (getEnvironmentVariable : string -> string) (args : string list) : Basic =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s int32%s%s" (sprintf "--%s" "foo") "" (sprintf " : %s" ("This is a foo!")))
- (sprintf "%s string%s%s" (sprintf "--%s" "bar") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s" "baz") "" "")
- (sprintf
- "%s string%s%s"
- (sprintf "--%s" "rest")
- " (positional args) (can be repeated)"
- (sprintf " : %s" ("Here's where the rest of the args go")))
- ]
- |> String.concat "\n"
-
- let arg_3 : string ResizeArray = ResizeArray ()
- let mutable arg_0 : int option = None
- let mutable arg_1 : string option = None
- let mutable arg_2 : bool option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "baz")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "bar", System.StringComparison.OrdinalIgnoreCase) then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "bar")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "foo", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "foo")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "rest", System.StringComparison.OrdinalIgnoreCase) then
- value |> (fun x -> x) |> arg_3.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_2 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_Basic) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_Basic.AwaitingKey -> ()
- | ParseState_Basic.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_3.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_Basic.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_Basic.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_Basic.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf "Unable to process argument %s as key %s and value %s" arg key value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_Basic.AwaitingKey args
- else
- arg |> (fun x -> x) |> arg_3.Add
- go ParseState_Basic.AwaitingKey args
- | ParseState_Basic.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_Basic.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_Basic.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf "Unable to process supplied arg %s. Help text follows.\n%s" key (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_Basic.AwaitingKey args
- let arg_3 = arg_3 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "foo")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "bar")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- Bar = arg_1
- Baz = arg_2
- Foo = arg_0
- Rest = arg_3
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ /// Extension methods for argument parsing
+ type FlagsIntoPositionalArgs' with
- let parse (args : string list) : Basic =
- parse' System.Environment.GetEnvironmentVariable args
+ static member parse'
+ (getEnvironmentVariable : string -> string)
+ (args : string list)
+ : FlagsIntoPositionalArgs'
+ =
+ failwith "todo"
+
+ static member parse (args : string list) : FlagsIntoPositionalArgs' =
+ FlagsIntoPositionalArgs'.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type BasicWithIntPositionals
-[]
-module BasicWithIntPositionals =
- type private ParseState_BasicWithIntPositionals =
+/// Methods to parse arguments for the type FlagsIntoPositionalArgsIntChoice
+[]
+module FlagsIntoPositionalArgsIntChoiceArgParse =
+ type private ParseState_FlagsIntoPositionalArgsIntChoice =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- let parse' (getEnvironmentVariable : string -> string) (args : string list) : BasicWithIntPositionals =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s int32%s%s" (sprintf "--%s" "foo") "" "")
- (sprintf "%s string%s%s" (sprintf "--%s" "bar") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s" "baz") "" "")
- (sprintf "%s int32%s%s" (sprintf "--%s" "rest") " (positional args) (can be repeated)" "")
- ]
- |> String.concat "\n"
-
- let arg_3 : int ResizeArray = ResizeArray ()
- let mutable arg_0 : int option = None
- let mutable arg_1 : string option = None
- let mutable arg_2 : bool option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "baz")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "bar", System.StringComparison.OrdinalIgnoreCase) then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "bar")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "foo", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "foo")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "rest", System.StringComparison.OrdinalIgnoreCase) then
- value |> (fun x -> System.Int32.Parse x) |> arg_3.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_2 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_BasicWithIntPositionals) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_BasicWithIntPositionals.AwaitingKey -> ()
- | ParseState_BasicWithIntPositionals.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_3.AddRange (rest |> Seq.map (fun x -> System.Int32.Parse x))
- | arg :: args ->
- match state with
- | ParseState_BasicWithIntPositionals.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_BasicWithIntPositionals.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_BasicWithIntPositionals.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf "Unable to process argument %s as key %s and value %s" arg key value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_BasicWithIntPositionals.AwaitingKey args
- else
- arg |> (fun x -> System.Int32.Parse x) |> arg_3.Add
- go ParseState_BasicWithIntPositionals.AwaitingKey args
- | ParseState_BasicWithIntPositionals.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_BasicWithIntPositionals.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_BasicWithIntPositionals.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf "Unable to process supplied arg %s. Help text follows.\n%s" key (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_BasicWithIntPositionals.AwaitingKey args
- let arg_3 = arg_3 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "foo")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "bar")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- Bar = arg_1
- Baz = arg_2
- Foo = arg_0
- Rest = arg_3
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ /// Extension methods for argument parsing
+ type FlagsIntoPositionalArgsIntChoice with
- let parse (args : string list) : BasicWithIntPositionals =
- parse' System.Environment.GetEnvironmentVariable args
+ static member parse'
+ (getEnvironmentVariable : string -> string)
+ (args : string list)
+ : FlagsIntoPositionalArgsIntChoice
+ =
+ failwith "todo"
+
+ static member parse (args : string list) : FlagsIntoPositionalArgsIntChoice =
+ FlagsIntoPositionalArgsIntChoice.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type LoadsOfTypes
-[]
-module LoadsOfTypes =
- type private ParseState_LoadsOfTypes =
+/// Methods to parse arguments for the type FlagsIntoPositionalArgsInt
+[]
+module FlagsIntoPositionalArgsIntArgParse =
+ type private ParseState_FlagsIntoPositionalArgsInt =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- let parse' (getEnvironmentVariable : string -> string) (args : string list) : LoadsOfTypes =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s int32%s%s" (sprintf "--%s" "foo") "" "")
- (sprintf "%s string%s%s" (sprintf "--%s" "bar") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s" "baz") "" "")
- (sprintf "%s FileInfo%s%s" (sprintf "--%s" "some-file") "" "")
- (sprintf "%s DirectoryInfo%s%s" (sprintf "--%s" "some-directory") "" "")
- (sprintf "%s DirectoryInfo%s%s" (sprintf "--%s" "some-list") " (can be repeated)" "")
- (sprintf "%s int32%s%s" (sprintf "--%s" "optional-thing-with-no-default") " (optional)" "")
-
- (sprintf
- "%s bool%s%s"
- (sprintf "--%s" "optional-thing")
- (LoadsOfTypes.DefaultOptionalThing ()
- |> (fun x -> x.ToString ())
- |> sprintf " (default value: %s)")
- "")
-
- (sprintf
- "%s int32%s%s"
- (sprintf "--%s" "another-optional-thing")
- (LoadsOfTypes.DefaultAnotherOptionalThing ()
- |> (fun x -> x.ToString ())
- |> sprintf " (default value: %s)")
- "")
-
- (sprintf
- "%s string%s%s"
- (sprintf "--%s" "yet-another-optional-thing")
- ("CONSUMEPLUGIN_THINGS" |> sprintf " (default value populated from env var %s)")
- "")
- (sprintf "%s int32%s%s" (sprintf "--%s" "positionals") " (positional args) (can be repeated)" "")
- ]
- |> String.concat "\n"
-
- let arg_7 : int ResizeArray = ResizeArray ()
- let mutable arg_0 : int option = None
- let mutable arg_1 : string option = None
- let mutable arg_2 : bool option = None
- let mutable arg_3 : FileInfo option = None
- let mutable arg_4 : DirectoryInfo option = None
- let arg_5 : DirectoryInfo ResizeArray = ResizeArray ()
- let mutable arg_6 : int option = None
- let mutable arg_8 : bool option = None
- let mutable arg_9 : int option = None
- let mutable arg_10 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if
- System.String.Equals (
- key,
- sprintf "--%s" "yet-another-optional-thing",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_10 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "yet-another-optional-thing")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_10 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "another-optional-thing",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_9 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "another-optional-thing")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_9 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "optional-thing", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_8 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "optional-thing")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_8 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "optional-thing-with-no-default",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_6 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "optional-thing-with-no-default")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_6 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "some-list", System.StringComparison.OrdinalIgnoreCase)
- then
- value |> (fun x -> System.IO.DirectoryInfo x) |> arg_5.Add
- () |> Ok
- else if
- System.String.Equals (key, sprintf "--%s" "some-directory", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_4 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "some-directory")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_4 <- value |> (fun x -> System.IO.DirectoryInfo x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "some-file", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_3 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "some-file")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_3 <- value |> (fun x -> System.IO.FileInfo x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "baz")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "bar", System.StringComparison.OrdinalIgnoreCase) then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "bar")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "foo", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "foo")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "positionals", System.StringComparison.OrdinalIgnoreCase)
- then
- value |> (fun x -> System.Int32.Parse x) |> arg_7.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if
- System.String.Equals (key, sprintf "--%s" "optional-thing", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_8 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "optional-thing")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_8 <- true |> Some
- true
- else if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_2 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_LoadsOfTypes) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_LoadsOfTypes.AwaitingKey -> ()
- | ParseState_LoadsOfTypes.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_7.AddRange (rest |> Seq.map (fun x -> System.Int32.Parse x))
- | arg :: args ->
- match state with
- | ParseState_LoadsOfTypes.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_LoadsOfTypes.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_LoadsOfTypes.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf "Unable to process argument %s as key %s and value %s" arg key value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_LoadsOfTypes.AwaitingKey args
- else
- arg |> (fun x -> System.Int32.Parse x) |> arg_7.Add
- go ParseState_LoadsOfTypes.AwaitingKey args
- | ParseState_LoadsOfTypes.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_LoadsOfTypes.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_LoadsOfTypes.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf "Unable to process supplied arg %s. Help text follows.\n%s" key (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_LoadsOfTypes.AwaitingKey args
- let arg_7 = arg_7 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "foo")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "bar")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_3 =
- match arg_3 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "some-file")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_4 =
- match arg_4 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "some-directory")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_5 = arg_5 |> Seq.toList
- let arg_6 = arg_6
-
- let arg_8 =
- match arg_8 with
- | None -> LoadsOfTypes.DefaultOptionalThing () |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- let arg_9 =
- match arg_9 with
- | None -> LoadsOfTypes.DefaultAnotherOptionalThing () |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- let arg_10 =
- match arg_10 with
- | None ->
- match "CONSUMEPLUGIN_THINGS" |> getEnvironmentVariable with
- | null ->
- sprintf
- "No value was supplied for %s, nor was environment variable %s set"
- (sprintf "--%s" "yet-another-optional-thing")
- "CONSUMEPLUGIN_THINGS"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | x -> x |> (fun x -> x)
- |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- if 0 = ArgParser_errors.Count then
- {
- AnotherOptionalThing = arg_9
- Bar = arg_1
- Baz = arg_2
- Foo = arg_0
- OptionalThing = arg_8
- OptionalThingWithNoDefault = arg_6
- Positionals = arg_7
- SomeDirectory = arg_4
- SomeFile = arg_3
- SomeList = arg_5
- YetAnotherOptionalThing = arg_10
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ /// Extension methods for argument parsing
+ type FlagsIntoPositionalArgsInt with
+
+ static member parse'
+ (getEnvironmentVariable : string -> string)
+ (args : string list)
+ : FlagsIntoPositionalArgsInt
+ =
+ failwith "todo"
- let parse (args : string list) : LoadsOfTypes =
- parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : FlagsIntoPositionalArgsInt =
+ FlagsIntoPositionalArgsInt.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type LoadsOfTypesNoPositionals
-[]
-module LoadsOfTypesNoPositionals =
- type private ParseState_LoadsOfTypesNoPositionals =
+/// Methods to parse arguments for the type FlagsIntoPositionalArgsChoice
+[]
+module FlagsIntoPositionalArgsChoiceArgParse =
+ type private ParseState_FlagsIntoPositionalArgsChoice =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- let parse' (getEnvironmentVariable : string -> string) (args : string list) : LoadsOfTypesNoPositionals =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s int32%s%s" (sprintf "--%s" "foo") "" "")
- (sprintf "%s string%s%s" (sprintf "--%s" "bar") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s" "baz") "" "")
- (sprintf "%s FileInfo%s%s" (sprintf "--%s" "some-file") "" "")
- (sprintf "%s DirectoryInfo%s%s" (sprintf "--%s" "some-directory") "" "")
- (sprintf "%s DirectoryInfo%s%s" (sprintf "--%s" "some-list") " (can be repeated)" "")
- (sprintf "%s int32%s%s" (sprintf "--%s" "optional-thing-with-no-default") " (optional)" "")
-
- (sprintf
- "%s bool%s%s"
- (sprintf "--%s" "optional-thing")
- (LoadsOfTypesNoPositionals.DefaultOptionalThing ()
- |> (fun x -> x.ToString ())
- |> sprintf " (default value: %s)")
- "")
-
- (sprintf
- "%s int32%s%s"
- (sprintf "--%s" "another-optional-thing")
- (LoadsOfTypesNoPositionals.DefaultAnotherOptionalThing ()
- |> (fun x -> x.ToString ())
- |> sprintf " (default value: %s)")
- "")
- (sprintf
- "%s string%s%s"
- (sprintf "--%s" "yet-another-optional-thing")
- ("CONSUMEPLUGIN_THINGS" |> sprintf " (default value populated from env var %s)")
- "")
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : int option = None
- let mutable arg_1 : string option = None
- let mutable arg_2 : bool option = None
- let mutable arg_3 : FileInfo option = None
- let mutable arg_4 : DirectoryInfo option = None
- let arg_5 : DirectoryInfo ResizeArray = ResizeArray ()
- let mutable arg_6 : int option = None
- let mutable arg_7 : bool option = None
- let mutable arg_8 : int option = None
- let mutable arg_9 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if
- System.String.Equals (
- key,
- sprintf "--%s" "yet-another-optional-thing",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_9 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "yet-another-optional-thing")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_9 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "another-optional-thing",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_8 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "another-optional-thing")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_8 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "optional-thing", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_7 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "optional-thing")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_7 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "optional-thing-with-no-default",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_6 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "optional-thing-with-no-default")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_6 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "some-list", System.StringComparison.OrdinalIgnoreCase)
- then
- value |> (fun x -> System.IO.DirectoryInfo x) |> arg_5.Add
- () |> Ok
- else if
- System.String.Equals (key, sprintf "--%s" "some-directory", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_4 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "some-directory")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_4 <- value |> (fun x -> System.IO.DirectoryInfo x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "some-file", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_3 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "some-file")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_3 <- value |> (fun x -> System.IO.FileInfo x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "baz")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "bar", System.StringComparison.OrdinalIgnoreCase) then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "bar")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if System.String.Equals (key, sprintf "--%s" "foo", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "foo")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if
- System.String.Equals (key, sprintf "--%s" "optional-thing", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_7 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "optional-thing")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_7 <- true |> Some
- true
- else if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
- match arg_2 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_2 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_LoadsOfTypesNoPositionals) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_LoadsOfTypesNoPositionals.AwaitingKey -> ()
- | ParseState_LoadsOfTypesNoPositionals.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_LoadsOfTypesNoPositionals.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_LoadsOfTypesNoPositionals.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_LoadsOfTypesNoPositionals.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf "Unable to process argument %s as key %s and value %s" arg key value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_LoadsOfTypesNoPositionals.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_LoadsOfTypesNoPositionals.AwaitingKey args
- | ParseState_LoadsOfTypesNoPositionals.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_LoadsOfTypesNoPositionals.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_LoadsOfTypesNoPositionals.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf "Unable to process supplied arg %s. Help text follows.\n%s" key (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_LoadsOfTypesNoPositionals.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "foo")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "bar")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "baz")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_3 =
- match arg_3 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "some-file")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_4 =
- match arg_4 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "some-directory")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_5 = arg_5 |> Seq.toList
- let arg_6 = arg_6
-
- let arg_7 =
- match arg_7 with
- | None -> LoadsOfTypesNoPositionals.DefaultOptionalThing () |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- let arg_8 =
- match arg_8 with
- | None -> LoadsOfTypesNoPositionals.DefaultAnotherOptionalThing () |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- let arg_9 =
- match arg_9 with
- | None ->
- match "CONSUMEPLUGIN_THINGS" |> getEnvironmentVariable with
- | null ->
- sprintf
- "No value was supplied for %s, nor was environment variable %s set"
- (sprintf "--%s" "yet-another-optional-thing")
- "CONSUMEPLUGIN_THINGS"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | x -> x |> (fun x -> x)
- |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- if 0 = ArgParser_errors.Count then
- {
- AnotherOptionalThing = arg_8
- Bar = arg_1
- Baz = arg_2
- Foo = arg_0
- OptionalThing = arg_7
- OptionalThingWithNoDefault = arg_6
- SomeDirectory = arg_4
- SomeFile = arg_3
- SomeList = arg_5
- YetAnotherOptionalThing = arg_9
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ /// Extension methods for argument parsing
+ type FlagsIntoPositionalArgsChoice with
- let parse (args : string list) : LoadsOfTypesNoPositionals =
- parse' System.Environment.GetEnvironmentVariable args
+ static member parse'
+ (getEnvironmentVariable : string -> string)
+ (args : string list)
+ : FlagsIntoPositionalArgsChoice
+ =
+ failwith "todo"
+
+ static member parse (args : string list) : FlagsIntoPositionalArgsChoice =
+ FlagsIntoPositionalArgsChoice.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type DatesAndTimes
+/// Methods to parse arguments for the type FlagsIntoPositionalArgs
[]
-module DatesAndTimesArgParse =
- type private ParseState_DatesAndTimes =
+module FlagsIntoPositionalArgsArgParse =
+ type private ParseState_FlagsIntoPositionalArgs =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type DatesAndTimes with
+ type FlagsIntoPositionalArgs with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : DatesAndTimes =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s TimeSpan%s%s" (sprintf "--%s" "plain") "" "")
- (sprintf "%s TimeSpan%s%s" (sprintf "--%s" "invariant") "" "")
-
- (sprintf
- "%s TimeSpan%s%s"
- (sprintf "--%s" "exact")
- ""
- (sprintf " : %s" (sprintf "%s [Parse format (.NET): %s]" "An exact time please" @"hh\:mm\:ss")))
- (sprintf
- "%s TimeSpan%s%s"
- (sprintf "--%s" "invariant-exact")
- ""
- (sprintf " : %s" (sprintf "[Parse format (.NET): %s]" @"hh\:mm\:ss")))
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : TimeSpan option = None
- let mutable arg_1 : TimeSpan option = None
- let mutable arg_2 : TimeSpan option = None
- let mutable arg_3 : TimeSpan option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if
- System.String.Equals (
- key,
- sprintf "--%s" "invariant-exact",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_3 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "invariant-exact")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_3 <-
- value
- |> (fun x ->
- System.TimeSpan.ParseExact (
- x,
- @"hh\:mm\:ss",
- System.Globalization.CultureInfo.InvariantCulture
- )
- )
- |> Some
-
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "exact", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "exact")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <-
- value
- |> (fun x ->
- System.TimeSpan.ParseExact (
- x,
- @"hh\:mm\:ss",
- System.Globalization.CultureInfo.CurrentCulture
- )
- )
- |> Some
-
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "invariant", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "invariant")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <-
- value
- |> (fun x ->
- System.TimeSpan.Parse (x, System.Globalization.CultureInfo.InvariantCulture)
- )
- |> Some
-
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "plain", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "plain")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.TimeSpan.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_DatesAndTimes) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_DatesAndTimes.AwaitingKey -> ()
- | ParseState_DatesAndTimes.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_DatesAndTimes.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_DatesAndTimes.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_DatesAndTimes.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_DatesAndTimes.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_DatesAndTimes.AwaitingKey args
- | ParseState_DatesAndTimes.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_DatesAndTimes.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_DatesAndTimes.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_DatesAndTimes.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "plain")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "invariant")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "exact")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_3 =
- match arg_3 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "invariant-exact")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- Exact = arg_2
- Invariant = arg_1
- InvariantExact = arg_3
- Plain = arg_0
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse'
+ (getEnvironmentVariable : string -> string)
+ (args : string list)
+ : FlagsIntoPositionalArgs
+ =
+ failwith "todo"
- static member parse (args : string list) : DatesAndTimes =
- DatesAndTimes.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : FlagsIntoPositionalArgs =
+ FlagsIntoPositionalArgs.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type ParentRecord
+/// Methods to parse arguments for the type ManyLongForms
[]
-module ParentRecordArgParse =
- type private ParseState_ParentRecord =
+module ManyLongFormsArgParse =
+ type private ParseState_ManyLongForms =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type ParentRecord with
+ type ManyLongForms with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ParentRecord =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s int32%s%s" (sprintf "--%s" "thing1") "" "")
- (sprintf "%s string%s%s" (sprintf "--%s" "thing2") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s" "and-another") "" "")
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : int option = None
- let mutable arg_1 : string option = None
- let mutable arg_2 : bool option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if
- System.String.Equals (key, sprintf "--%s" "and-another", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "and-another")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "thing2", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "thing2")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "thing1", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "thing1")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if
- System.String.Equals (key, sprintf "--%s" "and-another", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_2 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "and-another")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_2 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_ParentRecord) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ParentRecord.AwaitingKey -> ()
- | ParseState_ParentRecord.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_ParentRecord.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ParentRecord.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ParentRecord.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ParentRecord.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_ParentRecord.AwaitingKey args
- | ParseState_ParentRecord.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ParentRecord.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ParentRecord.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ParentRecord.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "thing1")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "thing2")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "and-another")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- AndAnother = arg_2
- Child =
- {
- Thing1 = arg_0
- Thing2 = arg_1
- }
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ManyLongForms =
+ failwith "todo"
- static member parse (args : string list) : ParentRecord =
- ParentRecord.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : ManyLongForms =
+ ManyLongForms.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type ParentRecordChildPos
+/// Methods to parse arguments for the type ContainsFlagDefaultValue
[]
-module ParentRecordChildPosArgParse =
- type private ParseState_ParentRecordChildPos =
+module ContainsFlagDefaultValueArgParse =
+ type private ParseState_ContainsFlagDefaultValue =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type ParentRecordChildPos with
+ type ContainsFlagDefaultValue with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ParentRecordChildPos =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s bool%s%s" (sprintf "--%s" "and-another") "" "")
- (sprintf "%s int32%s%s" (sprintf "--%s" "thing1") "" "")
- (sprintf "%s URI%s%s" (sprintf "--%s" "thing2") " (positional args) (can be repeated)" "")
- ]
- |> String.concat "\n"
-
- let arg_1 : Uri ResizeArray = ResizeArray ()
- let mutable arg_2 : bool option = None
- let mutable arg_0 : int option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "thing1", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "thing1")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "and-another", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_2 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "and-another")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "thing2", System.StringComparison.OrdinalIgnoreCase)
- then
- value |> (fun x -> System.Uri x) |> arg_1.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if
- System.String.Equals (key, sprintf "--%s" "and-another", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_2 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "and-another")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_2 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_ParentRecordChildPos) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ParentRecordChildPos.AwaitingKey -> ()
- | ParseState_ParentRecordChildPos.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_1.AddRange (rest |> Seq.map (fun x -> System.Uri x))
- | arg :: args ->
- match state with
- | ParseState_ParentRecordChildPos.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ParentRecordChildPos.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ParentRecordChildPos.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ParentRecordChildPos.AwaitingKey args
- else
- arg |> (fun x -> System.Uri x) |> arg_1.Add
- go ParseState_ParentRecordChildPos.AwaitingKey args
- | ParseState_ParentRecordChildPos.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ParentRecordChildPos.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ParentRecordChildPos.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ParentRecordChildPos.AwaitingKey args
- let arg_1 = arg_1 |> Seq.toList
-
- let arg_2 =
- match arg_2 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "and-another")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "thing1")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- AndAnother = arg_2
- Child =
- {
- Thing1 = arg_0
- Thing2 = arg_1
- }
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse'
+ (getEnvironmentVariable : string -> string)
+ (args : string list)
+ : ContainsFlagDefaultValue
+ =
+ failwith "todo"
- static member parse (args : string list) : ParentRecordChildPos =
- ParentRecordChildPos.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : ContainsFlagDefaultValue =
+ ContainsFlagDefaultValue.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type ParentRecordSelfPos
+/// Methods to parse arguments for the type ContainsFlagEnvVar
[]
-module ParentRecordSelfPosArgParse =
- type private ParseState_ParentRecordSelfPos =
+module ContainsFlagEnvVarArgParse =
+ type private ParseState_ContainsFlagEnvVar =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type ParentRecordSelfPos with
+ type ContainsFlagEnvVar with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ParentRecordSelfPos =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s int32%s%s" (sprintf "--%s" "thing1") "" "")
- (sprintf "%s string%s%s" (sprintf "--%s" "thing2") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s" "and-another") " (positional args) (can be repeated)" "")
- ]
- |> String.concat "\n"
-
- let arg_2 : bool ResizeArray = ResizeArray ()
- let mutable arg_0 : int option = None
- let mutable arg_1 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "thing2", System.StringComparison.OrdinalIgnoreCase) then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "thing2")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "thing1", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "thing1")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Int32.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "and-another", System.StringComparison.OrdinalIgnoreCase)
- then
- value |> (fun x -> System.Boolean.Parse x) |> arg_2.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_ParentRecordSelfPos) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ParentRecordSelfPos.AwaitingKey -> ()
- | ParseState_ParentRecordSelfPos.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_2.AddRange (rest |> Seq.map (fun x -> System.Boolean.Parse x))
- | arg :: args ->
- match state with
- | ParseState_ParentRecordSelfPos.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ParentRecordSelfPos.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ParentRecordSelfPos.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ParentRecordSelfPos.AwaitingKey args
- else
- arg |> (fun x -> System.Boolean.Parse x) |> arg_2.Add
- go ParseState_ParentRecordSelfPos.AwaitingKey args
- | ParseState_ParentRecordSelfPos.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ParentRecordSelfPos.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ParentRecordSelfPos.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ParentRecordSelfPos.AwaitingKey args
- let arg_2 = arg_2 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "thing1")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "thing2")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- AndAnother = arg_2
- Child =
- {
- Thing1 = arg_0
- Thing2 = arg_1
- }
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ContainsFlagEnvVar =
+ failwith "todo"
- static member parse (args : string list) : ParentRecordSelfPos =
- ParentRecordSelfPos.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : ContainsFlagEnvVar =
+ ContainsFlagEnvVar.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type ChoicePositionals
+/// Methods to parse arguments for the type WithFlagDu
[]
-module ChoicePositionalsArgParse =
- type private ParseState_ChoicePositionals =
+module WithFlagDuArgParse =
+ type private ParseState_WithFlagDu =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type ChoicePositionals with
+ type WithFlagDu with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ChoicePositionals =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s string%s%s" (sprintf "--%s" "args") " (positional args) (can be repeated)" "")
- ]
- |> String.concat "\n"
-
- let arg_0 : Choice ResizeArray = ResizeArray ()
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "args", System.StringComparison.OrdinalIgnoreCase) then
- value |> (fun x -> x) |> Choice1Of2 |> arg_0.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_ChoicePositionals) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ChoicePositionals.AwaitingKey -> ()
- | ParseState_ChoicePositionals.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_0.AddRange (rest |> Seq.map (fun x -> x) |> Seq.map Choice2Of2)
- | arg :: args ->
- match state with
- | ParseState_ChoicePositionals.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ChoicePositionals.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ChoicePositionals.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ChoicePositionals.AwaitingKey args
- else
- arg |> (fun x -> x) |> Choice1Of2 |> arg_0.Add
- go ParseState_ChoicePositionals.AwaitingKey args
- | ParseState_ChoicePositionals.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ChoicePositionals.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ChoicePositionals.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ChoicePositionals.AwaitingKey args
- let arg_0 = arg_0 |> Seq.toList
-
- if 0 = ArgParser_errors.Count then
- {
- Args = arg_0
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : WithFlagDu =
+ failwith "todo"
- static member parse (args : string list) : ChoicePositionals =
- ChoicePositionals.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : WithFlagDu =
+ WithFlagDu.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
@@ -2522,161 +476,7 @@ module ContainsBoolEnvVarArgParse =
type ContainsBoolEnvVar with
static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ContainsBoolEnvVar =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf
- "%s bool%s%s"
- (sprintf "--%s" "bool-var")
- ("CONSUMEPLUGIN_THINGS" |> sprintf " (default value populated from env var %s)")
- "")
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : bool option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "bool-var", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "bool-var")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if System.String.Equals (key, sprintf "--%s" "bool-var", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "bool-var")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_0 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_ContainsBoolEnvVar) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ContainsBoolEnvVar.AwaitingKey -> ()
- | ParseState_ContainsBoolEnvVar.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_ContainsBoolEnvVar.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ContainsBoolEnvVar.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ContainsBoolEnvVar.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ContainsBoolEnvVar.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_ContainsBoolEnvVar.AwaitingKey args
- | ParseState_ContainsBoolEnvVar.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ContainsBoolEnvVar.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ContainsBoolEnvVar.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ContainsBoolEnvVar.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- match "CONSUMEPLUGIN_THINGS" |> getEnvironmentVariable with
- | null ->
- sprintf
- "No value was supplied for %s, nor was environment variable %s set"
- (sprintf "--%s" "bool-var")
- "CONSUMEPLUGIN_THINGS"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | x ->
- if System.String.Equals (x, "1", System.StringComparison.OrdinalIgnoreCase) then
- true
- else if System.String.Equals (x, "0", System.StringComparison.OrdinalIgnoreCase) then
- false
- else
- x |> (fun x -> System.Boolean.Parse x)
- |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- if 0 = ArgParser_errors.Count then
- {
- BoolVar = arg_0
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ failwith "todo"
static member parse (args : string list) : ContainsBoolEnvVar =
ContainsBoolEnvVar.parse' System.Environment.GetEnvironmentVariable args
@@ -2686,1642 +486,209 @@ open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type WithFlagDu
+/// Methods to parse arguments for the type ChoicePositionals
[]
-module WithFlagDuArgParse =
- type private ParseState_WithFlagDu =
+module ChoicePositionalsArgParse =
+ type private ParseState_ChoicePositionals =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type WithFlagDu with
+ type ChoicePositionals with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : WithFlagDu =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [ (sprintf "%s bool%s%s" (sprintf "--%s" "dry-run") "" "") ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : DryRunMode option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "dry-run")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <-
- value
- |> (fun x ->
- if System.Boolean.Parse x = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- )
- |> Some
-
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "dry-run")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_0 <-
- if true = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- |> Some
-
- true
- else
- false
-
- let rec go (state : ParseState_WithFlagDu) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_WithFlagDu.AwaitingKey -> ()
- | ParseState_WithFlagDu.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_WithFlagDu.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_WithFlagDu.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_WithFlagDu.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_WithFlagDu.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_WithFlagDu.AwaitingKey args
- | ParseState_WithFlagDu.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_WithFlagDu.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_WithFlagDu.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_WithFlagDu.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "dry-run")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- DryRun = arg_0
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ChoicePositionals =
+ failwith "todo"
- static member parse (args : string list) : WithFlagDu =
- WithFlagDu.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : ChoicePositionals =
+ ChoicePositionals.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type ContainsFlagEnvVar
+/// Methods to parse arguments for the type ParentRecordSelfPos
[]
-module ContainsFlagEnvVarArgParse =
- type private ParseState_ContainsFlagEnvVar =
+module ParentRecordSelfPosArgParse =
+ type private ParseState_ParentRecordSelfPos =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type ContainsFlagEnvVar with
+ type ParentRecordSelfPos with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ContainsFlagEnvVar =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf
- "%s bool%s%s"
- (sprintf "--%s" "dry-run")
- ("CONSUMEPLUGIN_THINGS" |> sprintf " (default value populated from env var %s)")
- "")
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : DryRunMode option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "dry-run")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <-
- value
- |> (fun x ->
- if System.Boolean.Parse x = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- )
- |> Some
-
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "dry-run")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_0 <-
- if true = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- |> Some
-
- true
- else
- false
-
- let rec go (state : ParseState_ContainsFlagEnvVar) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ContainsFlagEnvVar.AwaitingKey -> ()
- | ParseState_ContainsFlagEnvVar.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_ContainsFlagEnvVar.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ContainsFlagEnvVar.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ContainsFlagEnvVar.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ContainsFlagEnvVar.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_ContainsFlagEnvVar.AwaitingKey args
- | ParseState_ContainsFlagEnvVar.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ContainsFlagEnvVar.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ContainsFlagEnvVar.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ContainsFlagEnvVar.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- match "CONSUMEPLUGIN_THINGS" |> getEnvironmentVariable with
- | null ->
- sprintf
- "No value was supplied for %s, nor was environment variable %s set"
- (sprintf "--%s" "dry-run")
- "CONSUMEPLUGIN_THINGS"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | x ->
- if System.String.Equals (x, "1", System.StringComparison.OrdinalIgnoreCase) then
- if true = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- else if System.String.Equals (x, "0", System.StringComparison.OrdinalIgnoreCase) then
- if false = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- else
- x
- |> (fun x ->
- if System.Boolean.Parse x = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- )
- |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- if 0 = ArgParser_errors.Count then
- {
- DryRun = arg_0
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ParentRecordSelfPos =
+ failwith "todo"
- static member parse (args : string list) : ContainsFlagEnvVar =
- ContainsFlagEnvVar.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : ParentRecordSelfPos =
+ ParentRecordSelfPos.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type ContainsFlagDefaultValue
+/// Methods to parse arguments for the type ParentRecordChildPos
[]
-module ContainsFlagDefaultValueArgParse =
- type private ParseState_ContainsFlagDefaultValue =
+module ParentRecordChildPosArgParse =
+ type private ParseState_ParentRecordChildPos =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type ContainsFlagDefaultValue with
+ type ParentRecordChildPos with
- static member parse'
- (getEnvironmentVariable : string -> string)
- (args : string list)
- : ContainsFlagDefaultValue
- =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf
- "%s bool%s%s"
- (sprintf "--%s" "dry-run")
- (match ContainsFlagDefaultValue.DefaultDryRun () with
- | DryRunMode.Wet -> if Consts.FALSE = true then "true" else "false"
- | DryRunMode.Dry -> if true = true then "true" else "false"
- |> (fun x -> x.ToString ())
- |> sprintf " (default value: %s)")
- "")
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : DryRunMode option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "dry-run")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <-
- value
- |> (fun x ->
- if System.Boolean.Parse x = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- )
- |> Some
-
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "dry-run")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_0 <-
- if true = Consts.FALSE then
- DryRunMode.Wet
- else
- DryRunMode.Dry
- |> Some
-
- true
- else
- false
-
- let rec go (state : ParseState_ContainsFlagDefaultValue) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ContainsFlagDefaultValue.AwaitingKey -> ()
- | ParseState_ContainsFlagDefaultValue.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_ContainsFlagDefaultValue.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ContainsFlagDefaultValue.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ContainsFlagDefaultValue.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ContainsFlagDefaultValue.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_ContainsFlagDefaultValue.AwaitingKey args
- | ParseState_ContainsFlagDefaultValue.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ContainsFlagDefaultValue.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ContainsFlagDefaultValue.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ContainsFlagDefaultValue.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None -> ContainsFlagDefaultValue.DefaultDryRun () |> Choice2Of2
- | Some x -> Choice1Of2 x
-
- if 0 = ArgParser_errors.Count then
- {
- DryRun = arg_0
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ParentRecordChildPos =
+ failwith "todo"
- static member parse (args : string list) : ContainsFlagDefaultValue =
- ContainsFlagDefaultValue.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : ParentRecordChildPos =
+ ParentRecordChildPos.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type ManyLongForms
+/// Methods to parse arguments for the type ParentRecord
[]
-module ManyLongFormsArgParse =
- type private ParseState_ManyLongForms =
+module ParentRecordArgParse =
+ type private ParseState_ParentRecord =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type ManyLongForms with
+ type ParentRecord with
- static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ManyLongForms =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s string%s%s" (sprintf "--%s / --%s" "do-something-else" "anotherarg") "" "")
- (sprintf "%s bool%s%s" (sprintf "--%s / --%s" "turn-it-on" "dont-turn-it-off") "" "")
- ]
- |> String.concat "\n"
-
- let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
- let mutable arg_0 : string option = None
- let mutable arg_1 : bool option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if
- System.String.Equals (
- key,
- sprintf "--%s" "dont-turn-it-off",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s / --%s" "turn-it-on" "dont-turn-it-off")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "turn-it-on", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_1 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s / --%s" "turn-it-on" "dont-turn-it-off")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_1 <- value |> (fun x -> System.Boolean.Parse x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (key, sprintf "--%s" "anotherarg", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s / --%s" "do-something-else" "anotherarg")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "do-something-else",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s / --%s" "do-something-else" "anotherarg")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool =
- if
- System.String.Equals (
- key,
- sprintf "--%s" "dont-turn-it-off",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- match arg_1 with
- | Some x ->
- sprintf
- "Flag '%s' was supplied multiple times"
- (sprintf "--%s / --%s" "turn-it-on" "dont-turn-it-off")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_1 <- true |> Some
- true
- else if
- System.String.Equals (key, sprintf "--%s" "turn-it-on", System.StringComparison.OrdinalIgnoreCase)
- then
- match arg_1 with
- | Some x ->
- sprintf
- "Flag '%s' was supplied multiple times"
- (sprintf "--%s / --%s" "turn-it-on" "dont-turn-it-off")
- |> ArgParser_errors.Add
-
- true
- | None ->
- arg_1 <- true |> Some
- true
- else
- false
-
- let rec go (state : ParseState_ManyLongForms) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_ManyLongForms.AwaitingKey -> ()
- | ParseState_ManyLongForms.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_ManyLongForms.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_ManyLongForms.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_ManyLongForms.AwaitingKey args
- | Error x ->
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_ManyLongForms.AwaitingKey args
- else
- arg |> (fun x -> x) |> parser_LeftoverArgs.Add
- go ParseState_ManyLongForms.AwaitingKey args
- | ParseState_ManyLongForms.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_ManyLongForms.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_ManyLongForms.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_ManyLongForms.AwaitingKey args
-
- let parser_LeftoverArgs =
- if 0 = parser_LeftoverArgs.Count then
- ()
- else
- parser_LeftoverArgs
- |> String.concat " "
- |> sprintf "There were leftover args: %s"
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf
- "Required argument '%s' received no value"
- (sprintf "--%s / --%s" "do-something-else" "anotherarg")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- let arg_1 =
- match arg_1 with
- | None ->
- sprintf
- "Required argument '%s' received no value"
- (sprintf "--%s / --%s" "turn-it-on" "dont-turn-it-off")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- DoTheThing = arg_0
- SomeFlag = arg_1
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : ParentRecord =
+ failwith "todo"
- static member parse (args : string list) : ManyLongForms =
- ManyLongForms.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : ParentRecord =
+ ParentRecord.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type FlagsIntoPositionalArgs
+/// Methods to parse arguments for the type DatesAndTimes
[]
-module FlagsIntoPositionalArgsArgParse =
- type private ParseState_FlagsIntoPositionalArgs =
+module DatesAndTimesArgParse =
+ type private ParseState_DatesAndTimes =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
/// Extension methods for argument parsing
- type FlagsIntoPositionalArgs with
+ type DatesAndTimes with
- static member parse'
- (getEnvironmentVariable : string -> string)
- (args : string list)
- : FlagsIntoPositionalArgs
- =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s string%s%s" (sprintf "--%s" "a") "" "")
- (sprintf
- "%s string%s%s"
- (sprintf "--%s" "grab-everything")
- " (positional args) (can be repeated)"
- "")
- ]
- |> String.concat "\n"
-
- let arg_1 : string ResizeArray = ResizeArray ()
- let mutable arg_0 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "a", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "a")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "grab-everything",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- value |> (fun x -> x) |> arg_1.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_FlagsIntoPositionalArgs) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_FlagsIntoPositionalArgs.AwaitingKey -> ()
- | ParseState_FlagsIntoPositionalArgs.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_1.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_FlagsIntoPositionalArgs.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_FlagsIntoPositionalArgs.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_FlagsIntoPositionalArgs.AwaitingKey args
- | Error x ->
- if true then
- arg |> (fun x -> x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgs.AwaitingKey args
- else
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_FlagsIntoPositionalArgs.AwaitingKey args
- else
- arg |> (fun x -> x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgs.AwaitingKey args
- | ParseState_FlagsIntoPositionalArgs.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_FlagsIntoPositionalArgs.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_FlagsIntoPositionalArgs.AwaitingKey (arg :: args)
- else if true then
- key |> (fun x -> x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgs.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_FlagsIntoPositionalArgs.AwaitingKey args
- let arg_1 = arg_1 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "a")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- A = arg_0
- GrabEverything = arg_1
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ static member parse' (getEnvironmentVariable : string -> string) (args : string list) : DatesAndTimes =
+ failwith "todo"
- static member parse (args : string list) : FlagsIntoPositionalArgs =
- FlagsIntoPositionalArgs.parse' System.Environment.GetEnvironmentVariable args
+ static member parse (args : string list) : DatesAndTimes =
+ DatesAndTimes.parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type FlagsIntoPositionalArgsChoice
-[]
-module FlagsIntoPositionalArgsChoiceArgParse =
- type private ParseState_FlagsIntoPositionalArgsChoice =
+/// Methods to parse arguments for the type LoadsOfTypesNoPositionals
+[]
+module LoadsOfTypesNoPositionals =
+ type private ParseState_LoadsOfTypesNoPositionals =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- /// Extension methods for argument parsing
- type FlagsIntoPositionalArgsChoice with
-
- static member parse'
- (getEnvironmentVariable : string -> string)
- (args : string list)
- : FlagsIntoPositionalArgsChoice
- =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s string%s%s" (sprintf "--%s" "a") "" "")
- (sprintf
- "%s string%s%s"
- (sprintf "--%s" "grab-everything")
- " (positional args) (can be repeated)"
- "")
- ]
- |> String.concat "\n"
-
- let arg_1 : Choice ResizeArray = ResizeArray ()
- let mutable arg_0 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "a", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "a")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "grab-everything",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- value |> (fun x -> x) |> Choice1Of2 |> arg_1.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_FlagsIntoPositionalArgsChoice) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey -> ()
- | ParseState_FlagsIntoPositionalArgsChoice.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_1.AddRange (rest |> Seq.map (fun x -> x) |> Seq.map Choice2Of2)
- | arg :: args ->
- match state with
- | ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_FlagsIntoPositionalArgsChoice.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey args
- | Error x ->
- if true then
- arg |> (fun x -> x) |> Choice1Of2 |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey args
- else
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey args
- else
- arg |> (fun x -> x) |> Choice1Of2 |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey args
- | ParseState_FlagsIntoPositionalArgsChoice.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey (arg :: args)
- else if true then
- key |> (fun x -> x) |> Choice1Of2 |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_FlagsIntoPositionalArgsChoice.AwaitingKey args
- let arg_1 = arg_1 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "a")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- A = arg_0
- GrabEverything = arg_1
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ let parse' (getEnvironmentVariable : string -> string) (args : string list) : LoadsOfTypesNoPositionals =
+ failwith "todo"
- static member parse (args : string list) : FlagsIntoPositionalArgsChoice =
- FlagsIntoPositionalArgsChoice.parse' System.Environment.GetEnvironmentVariable args
+ let parse (args : string list) : LoadsOfTypesNoPositionals =
+ parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type FlagsIntoPositionalArgsInt
-[]
-module FlagsIntoPositionalArgsIntArgParse =
- type private ParseState_FlagsIntoPositionalArgsInt =
+/// Methods to parse arguments for the type LoadsOfTypes
+[]
+module LoadsOfTypes =
+ type private ParseState_LoadsOfTypes =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- /// Extension methods for argument parsing
- type FlagsIntoPositionalArgsInt with
-
- static member parse'
- (getEnvironmentVariable : string -> string)
- (args : string list)
- : FlagsIntoPositionalArgsInt
- =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s string%s%s" (sprintf "--%s" "a") "" "")
- (sprintf
- "%s int32%s%s"
- (sprintf "--%s" "grab-everything")
- " (positional args) (can be repeated)"
- "")
- ]
- |> String.concat "\n"
-
- let arg_1 : int ResizeArray = ResizeArray ()
- let mutable arg_0 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "a", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "a")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "grab-everything",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- value |> (fun x -> System.Int32.Parse x) |> arg_1.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_FlagsIntoPositionalArgsInt) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_FlagsIntoPositionalArgsInt.AwaitingKey -> ()
- | ParseState_FlagsIntoPositionalArgsInt.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_1.AddRange (rest |> Seq.map (fun x -> System.Int32.Parse x))
- | arg :: args ->
- match state with
- | ParseState_FlagsIntoPositionalArgsInt.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_FlagsIntoPositionalArgsInt.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey args
- | Error x ->
- if true then
- arg |> (fun x -> System.Int32.Parse x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey args
- else
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey args
- else
- arg |> (fun x -> System.Int32.Parse x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey args
- | ParseState_FlagsIntoPositionalArgsInt.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey (arg :: args)
- else if true then
- key |> (fun x -> System.Int32.Parse x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_FlagsIntoPositionalArgsInt.AwaitingKey args
- let arg_1 = arg_1 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "a")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- A = arg_0
- GrabEverything = arg_1
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ let parse' (getEnvironmentVariable : string -> string) (args : string list) : LoadsOfTypes = failwith "todo"
- static member parse (args : string list) : FlagsIntoPositionalArgsInt =
- FlagsIntoPositionalArgsInt.parse' System.Environment.GetEnvironmentVariable args
+ let parse (args : string list) : LoadsOfTypes =
+ parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type FlagsIntoPositionalArgsIntChoice
-[]
-module FlagsIntoPositionalArgsIntChoiceArgParse =
- type private ParseState_FlagsIntoPositionalArgsIntChoice =
+/// Methods to parse arguments for the type BasicWithIntPositionals
+[]
+module BasicWithIntPositionals =
+ type private ParseState_BasicWithIntPositionals =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- /// Extension methods for argument parsing
- type FlagsIntoPositionalArgsIntChoice with
-
- static member parse'
- (getEnvironmentVariable : string -> string)
- (args : string list)
- : FlagsIntoPositionalArgsIntChoice
- =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s string%s%s" (sprintf "--%s" "a") "" "")
- (sprintf
- "%s int32%s%s"
- (sprintf "--%s" "grab-everything")
- " (positional args) (can be repeated)"
- "")
- ]
- |> String.concat "\n"
-
- let arg_1 : Choice ResizeArray = ResizeArray ()
- let mutable arg_0 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "a", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "a")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "grab-everything",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- value |> (fun x -> System.Int32.Parse x) |> Choice1Of2 |> arg_1.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_FlagsIntoPositionalArgsIntChoice) (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey -> ()
- | ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_1.AddRange (rest |> Seq.map (fun x -> System.Int32.Parse x) |> Seq.map Choice2Of2)
- | arg :: args ->
- match state with
- | ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey args
- | Error x ->
- if true then
- arg |> (fun x -> System.Int32.Parse x) |> Choice1Of2 |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey args
- else
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey args
- else
- arg |> (fun x -> System.Int32.Parse x) |> Choice1Of2 |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey args
- | ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey (arg :: args)
- else if true then
- key |> (fun x -> System.Int32.Parse x) |> Choice1Of2 |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_FlagsIntoPositionalArgsIntChoice.AwaitingKey args
- let arg_1 = arg_1 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "a")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- A = arg_0
- GrabEverything = arg_1
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ let parse' (getEnvironmentVariable : string -> string) (args : string list) : BasicWithIntPositionals =
+ failwith "todo"
- static member parse (args : string list) : FlagsIntoPositionalArgsIntChoice =
- FlagsIntoPositionalArgsIntChoice.parse' System.Environment.GetEnvironmentVariable args
+ let parse (args : string list) : BasicWithIntPositionals =
+ parse' System.Environment.GetEnvironmentVariable args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
-/// Methods to parse arguments for the type FlagsIntoPositionalArgs'
-[]
-module FlagsIntoPositionalArgs'ArgParse =
- type private ParseState_FlagsIntoPositionalArgs' =
+/// Methods to parse arguments for the type Basic
+[]
+module Basic =
+ type private ParseState_Basic =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
- /// Extension methods for argument parsing
- type FlagsIntoPositionalArgs' with
+ let parse' (getEnvironmentVariable : string -> string) (args : string list) : Basic = failwith "todo"
- static member parse'
- (getEnvironmentVariable : string -> string)
- (args : string list)
- : FlagsIntoPositionalArgs'
- =
- let ArgParser_errors = ResizeArray ()
-
- let helpText () =
- [
- (sprintf "%s string%s%s" (sprintf "--%s" "a") "" "")
- (sprintf
- "%s string%s%s"
- (sprintf "--%s" "dont-grab-everything")
- " (positional args) (can be repeated)"
- "")
- ]
- |> String.concat "\n"
-
- let arg_1 : string ResizeArray = ResizeArray ()
- let mutable arg_0 : string option = None
-
- /// Processes the key-value pair, returning Error if no key was matched.
- /// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error().
- /// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
- let processKeyValue (key : string) (value : string) : Result =
- if System.String.Equals (key, sprintf "--%s" "a", System.StringComparison.OrdinalIgnoreCase) then
- match arg_0 with
- | Some x ->
- sprintf
- "Argument '%s' was supplied multiple times: %s and %s"
- (sprintf "--%s" "a")
- (x.ToString ())
- (value.ToString ())
- |> ArgParser_errors.Add
-
- Ok ()
- | None ->
- try
- arg_0 <- value |> (fun x -> x) |> Some
- Ok ()
- with _ as exc ->
- exc.Message |> Some |> Error
- else if
- System.String.Equals (
- key,
- sprintf "--%s" "dont-grab-everything",
- System.StringComparison.OrdinalIgnoreCase
- )
- then
- value |> (fun x -> x) |> arg_1.Add
- () |> Ok
- else
- Error None
-
- /// Returns false if we didn't set a value.
- let setFlagValue (key : string) : bool = false
-
- let rec go (state : ParseState_FlagsIntoPositionalArgs') (args : string list) =
- match args with
- | [] ->
- match state with
- | ParseState_FlagsIntoPositionalArgs'.AwaitingKey -> ()
- | ParseState_FlagsIntoPositionalArgs'.AwaitingValue key ->
- if setFlagValue key then
- ()
- else
- sprintf
- "Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
- key
- |> ArgParser_errors.Add
- | "--" :: rest -> arg_1.AddRange (rest |> Seq.map (fun x -> x))
- | arg :: args ->
- match state with
- | ParseState_FlagsIntoPositionalArgs'.AwaitingKey ->
- if arg.StartsWith ("--", System.StringComparison.Ordinal) then
- if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
- else
- let equals = arg.IndexOf (char 61)
-
- if equals < 0 then
- args |> go (ParseState_FlagsIntoPositionalArgs'.AwaitingValue arg)
- else
- let key = arg.[0 .. equals - 1]
- let value = arg.[equals + 1 ..]
-
- match processKeyValue key value with
- | Ok () -> go ParseState_FlagsIntoPositionalArgs'.AwaitingKey args
- | Error x ->
- if false then
- arg |> (fun x -> x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgs'.AwaitingKey args
- else
- match x with
- | None ->
- failwithf
- "Unable to process argument %s as key %s and value %s"
- arg
- key
- value
- | Some msg ->
- sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
- go ParseState_FlagsIntoPositionalArgs'.AwaitingKey args
- else
- arg |> (fun x -> x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgs'.AwaitingKey args
- | ParseState_FlagsIntoPositionalArgs'.AwaitingValue key ->
- match processKeyValue key arg with
- | Ok () -> go ParseState_FlagsIntoPositionalArgs'.AwaitingKey args
- | Error exc ->
- if setFlagValue key then
- go ParseState_FlagsIntoPositionalArgs'.AwaitingKey (arg :: args)
- else if false then
- key |> (fun x -> x) |> arg_1.Add
- go ParseState_FlagsIntoPositionalArgs'.AwaitingKey (arg :: args)
- else
- match exc with
- | None ->
- failwithf
- "Unable to process supplied arg %s. Help text follows.\n%s"
- key
- (helpText ())
- | Some msg -> msg |> ArgParser_errors.Add
-
- go ParseState_FlagsIntoPositionalArgs'.AwaitingKey args
- let arg_1 = arg_1 |> Seq.toList
-
- let arg_0 =
- match arg_0 with
- | None ->
- sprintf "Required argument '%s' received no value" (sprintf "--%s" "a")
- |> ArgParser_errors.Add
-
- Unchecked.defaultof<_>
- | Some x -> x
-
- if 0 = ArgParser_errors.Count then
- {
- A = arg_0
- DontGrabEverything = arg_1
- }
- else
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
+ let parse (args : string list) : Basic =
+ parse' System.Environment.GetEnvironmentVariable args
+namespace ConsumePlugin
- static member parse (args : string list) : FlagsIntoPositionalArgs' =
- FlagsIntoPositionalArgs'.parse' System.Environment.GetEnvironmentVariable args
+open System
+open System.IO
+open WoofWare.Myriad.Plugins
+
+/// Methods to parse arguments for the type BasicNoPositionals
+[]
+module BasicNoPositionals =
+ type private ParseState_BasicNoPositionals =
+ /// Ready to consume a key or positional arg
+ | AwaitingKey
+ /// Waiting to receive a value for the key we've already consumed
+ | AwaitingValue of key : string
+
+ let parse' (getEnvironmentVariable : string -> string) (args : string list) : BasicNoPositionals = failwith "todo"
+
+ let parse (args : string list) : BasicNoPositionals =
+ parse' System.Environment.GetEnvironmentVariable args
diff --git a/Playground/Domain.fs b/Playground/Domain.fs
index 425d27b6..9a0b80de 100644
--- a/Playground/Domain.fs
+++ b/Playground/Domain.fs
@@ -45,4 +45,3 @@ type Args =
[]
OtherArgs : string list
}
-
diff --git a/Playground/Library.fs b/Playground/Library.fs
index 7dd79002..3951abda 100644
--- a/Playground/Library.fs
+++ b/Playground/Library.fs
@@ -1,11 +1,12 @@
-// The following code was mostly generated by Gemini 2.5 Pro (Experimental).
-// I have not reviewed it at all yet; I have simply made it compile and tightened up the types.
+//------------------------------------------------------------------------------
+// This code was generated by myriad.
+// Changes to this file will be lost when the code is regenerated.
+//------------------------------------------------------------------------------
-namespace GeneratedParsers // Assuming a namespace
+namespace Playground // Assuming a namespace
open System
open System.IO
-open Playground
open WoofWare.Myriad.Plugins // Assuming attributes are here
// Assume original type definitions are accessible here
@@ -18,341 +19,545 @@ open WoofWare.Myriad.Plugins // Assuming attributes are here
/// Methods to parse arguments for the type Args
-[]
-module ArgsModule =
+[]
+module Args =
//--------------------------------------------------------------------------
- // Internal state definitions for the multi-candidate DU parser
+ // Internal state definitions (Non-Flattened with combined Assemble/Validate)
//--------------------------------------------------------------------------
- /// State representing the parse progress for Mode1
- type private State_Mode1 = {
- mutable Things_Info1 : int option
- mutable Things_Info2 : string option
- Things_Rest : ResizeArray // Corresponds to --rest for Mode1
- mutable Whatnot : int option
- } with
- static member Create() = {
- Things_Info1 = None
- Things_Info2 = None
- Things_Rest = ResizeArray()
- Whatnot = None
+ /// State representing the parse progress for SubMode1 record
+ type private State_SubMode1 =
+ {
+ mutable Info1 : int option
+ mutable Info2 : string option
+ Rest : ResizeArray // Corresponds to --rest
}
- /// State representing the parse progress for Mode2
- type private State_Mode2 = {
- mutable Things_Info1 : int option
- mutable Things_Info2 : string option
- Things_Rest : ResizeArray // Corresponds to --rest for Mode2
- mutable Whatnot : DateTime option
- } with
- static member Create() = {
- Things_Info1 = None
- Things_Info2 = None
- Things_Rest = ResizeArray()
- Whatnot = None
+ static member Create () =
+ {
+ Info1 = None
+ Info2 = None
+ Rest = ResizeArray ()
+ }
+
+ /// Check completeness and assemble the SubMode1 record from state.
+ member this.Assemble () : Result =
+ let errors = ResizeArray ()
+ let mutable complete = true
+
+ if this.Info1.IsNone then
+ complete <- false
+ errors.Add ("Argument '--info1' is required.")
+
+ if this.Info2.IsNone then
+ complete <- false
+ errors.Add ("Argument '--info2' is required.")
+ // Rest is list, always 'complete'
+
+ if complete then
+ Ok
+ {
+ Info1 = this.Info1.Value
+ Info2 = this.Info2.Value
+ Rest = this.Rest |> Seq.toList
+ }
+ else
+ Error (errors |> Seq.toList)
+
+ /// State representing the parse progress for SubMode2 record
+ type private State_SubMode2 =
+ {
+ mutable Info1 : int option
+ mutable Info2 : string option
+ Rest : ResizeArray // Corresponds to --rest
}
- type private CandidateParseStateContents =
- | Mode1 of State_Mode1
- | Mode2 of State_Mode2
-
- /// State for a single candidate parse path for the Modes DU
- type private CandidateParseState_Modes = {
- mutable IsViable : bool
- Errors : ResizeArray // Errors specific to this candidate's path
- ConsumedArgIndices : System.Collections.Generic.HashSet // Indices consumed *by this candidate*
- CaseState : CandidateParseStateContents
- CaseName : string
- } with
- static member CreateMode1() = {
- IsViable = true
- Errors = ResizeArray()
- ConsumedArgIndices = System.Collections.Generic.HashSet()
- CaseState = State_Mode1.Create() |> CandidateParseStateContents.Mode1
- CaseName = "Mode1"
- }
- static member CreateMode2() = {
- IsViable = true
- Errors = ResizeArray()
- ConsumedArgIndices = System.Collections.Generic.HashSet()
- CaseState = State_Mode2.Create() |> CandidateParseStateContents.Mode2
- CaseName = "Mode2"
- }
+ static member Create () =
+ {
+ Info1 = None
+ Info2 = None
+ Rest = ResizeArray ()
+ }
+
+ /// Check completeness and assemble the SubMode2 record from state.
+ member this.Assemble () : Result =
+ let errors = ResizeArray ()
+
+ if this.Info1.IsNone then
+ errors.Add ("Argument '--info1' is required.")
+
+ if this.Info2.IsNone then
+ errors.Add ("Argument '--info2' is required.")
+ // Rest is list, always 'complete'
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Info1 = this.Info1.Value
+ Info2 = this.Info2.Value
+ Rest = this.Rest |> Seq.toList
+ }
+ else
+ Error (errors |> Seq.toList)
+
+
+ /// State representing the parse progress for Mode1 record (references SubMode1 state)
+ type private State_Mode1 =
+ {
+ ThingsState : State_SubMode1 // Holds state for the nested record
+ mutable Whatnot : int option
+ }
+
+ static member Create () =
+ {
+ ThingsState = State_SubMode1.Create ()
+ Whatnot = None
+ }
+
+ /// Check completeness and assemble the Mode1 record from state (including nested).
+ member this.Assemble () : Result =
+ let errors = ResizeArray ()
+
+ // Check direct fields
+ if this.Whatnot.IsNone then
+ errors.Add ("Argument '--whatnot' is required for Mode1.")
+
+ // Assemble nested state (which includes its own validation)
+ let thingsResult = this.ThingsState.Assemble ()
+ let mutable thingsValue = None
+
+ match thingsResult with
+ | Ok v -> thingsValue <- Some v
+ | Error nestedErrors -> errors.AddRange (nestedErrors |> List.map (sprintf "Things: %s")) // Add context
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Things = thingsValue.Value
+ Whatnot = this.Whatnot.Value
+ }
+ else
+ Error (errors |> Seq.toList)
+
+
+ /// State representing the parse progress for Mode2 record (references SubMode2 state)
+ type private State_Mode2 =
+ {
+ ThingsState : State_SubMode2 // Holds state for the nested record
+ mutable Whatnot : DateTime option
+ }
+
+ static member Create () =
+ {
+ ThingsState = State_SubMode2.Create ()
+ Whatnot = None
+ }
+
+ /// Check completeness and assemble the Mode2 record from state (including nested).
+ member this.Assemble () : Result =
+ let errors = ResizeArray ()
+
+ // Check direct fields
+ if this.Whatnot.IsNone then
+ errors.Add ("Argument '--whatnot' is required for Mode2.")
+
+ // Assemble nested state (which includes its own validation)
+ let thingsResult = this.ThingsState.Assemble ()
+ let mutable thingsValue = Unchecked.defaultof<_>
+
+ match thingsResult with
+ | Ok v -> thingsValue <- v
+ | Error nestedErrors -> errors.AddRange (nestedErrors |> List.map (sprintf "Things: %s")) // Add context
+
+ if errors.Count = 0 then
+ {
+ Things = thingsValue
+ Whatnot = this.Whatnot.Value
+ }
+ |> Ok
+ else
+ Error (errors |> Seq.toList)
+
+
+ /// State for a single candidate parse path for the Modes DU (Structure unchanged)
+ type private CandidateParseState_Modes =
+ {
+ CaseName : string // "Mode1" or "Mode2"
+ mutable IsViable : bool
+ Errors : ResizeArray // Errors specific to this candidate's path
+ ConsumedArgIndices : System.Collections.Generic.HashSet // Indices consumed *by this candidate*
+ CaseState : obj // Holds either State_Mode1 or State_Mode2
+ }
+
+ static member CreateMode1 () =
+ {
+ CaseName = "Mode1"
+ IsViable = true
+ Errors = ResizeArray ()
+ ConsumedArgIndices = System.Collections.Generic.HashSet ()
+ CaseState = State_Mode1.Create () :> obj
+ }
+
+ static member CreateMode2 () =
+ {
+ CaseName = "Mode2"
+ IsViable = true
+ Errors = ResizeArray ()
+ ConsumedArgIndices = System.Collections.Generic.HashSet ()
+ CaseState = State_Mode2.Create () :> obj
+ }
//--------------------------------------------------------------------------
// Main Parser Logic
//--------------------------------------------------------------------------
type private ParseState_Args =
- /// Ready to consume a key or positional arg
| AwaitingArg
- /// Waiting to receive a value for the key we've already consumed (at given index)
- | AwaitingValue of keyIndex: int * key: string
+ | AwaitingValue of keyIndex : int * key : string
- let parse' (getEnvironmentVariable: string -> string) (args: string list) : Args =
- let ArgParser_errors = ResizeArray() // Global errors
+ let parse' (getEnvironmentVariable : string -> string) (args : string list) : Args =
+ let ArgParser_errors = ResizeArray () // Global errors accumulator
let helpText () =
- // Note: Help text generation for DUs needs careful thought.
- // This version lists all possible args, but doesn't specify Mode context well.
- [ (sprintf "%s int32%s%s" (sprintf "--%s" "info1") "" " (for Mode1/Mode2 Things)")
- (sprintf "%s string%s%s" (sprintf "--%s" "info2") "" " (for Mode1/Mode2 Things)")
- (sprintf "%s string%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode1 Things)")
- (sprintf "%s int32%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode2 Things)")
- (sprintf "%s int32%s%s" (sprintf "--%s" "whatnot") "" " (for Mode1)")
- (sprintf "%s DateTime%s%s" (sprintf "--%s" "whatnot") "" " (for Mode2)")
- (sprintf "%s string%s%s" (sprintf "--%s" "other-args") " (positional args) (can be repeated)" "")
+ // Help text generation unchanged
+ [
+ (sprintf "%s int32%s%s" (sprintf "--%s" "info1") "" " (for Mode1/Mode2 Things)")
+ (sprintf "%s string%s%s" (sprintf "--%s" "info2") "" " (for Mode1/Mode2 Things)")
+ (sprintf "%s string%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode1 Things)")
+ (sprintf "%s int32%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode2 Things)")
+ (sprintf "%s int32%s%s" (sprintf "--%s" "whatnot") "" " (for Mode1)")
+ (sprintf "%s DateTime%s%s" (sprintf "--%s" "whatnot") "" " (for Mode2)")
+ (sprintf "%s string%s%s" (sprintf "--%s" "other-args") " (positional args) (can be repeated)" "")
]
|> String.concat "\n"
- // State for top-level fields
- let arg_OtherArgs: string ResizeArray = ResizeArray()
- let mutable candidates_WhatToDo: CandidateParseState_Modes list =
- [ CandidateParseState_Modes.CreateMode1()
- CandidateParseState_Modes.CreateMode2() ]
- // Keep track of args consumed by *any* viable candidate for the DU
- let consumedArgIndices_WhatToDo = System.Collections.Generic.HashSet()
+ let arg_OtherArgs : string ResizeArray = ResizeArray ()
+
+ let mutable candidates_WhatToDo : CandidateParseState_Modes list =
+ [
+ CandidateParseState_Modes.CreateMode1 ()
+ CandidateParseState_Modes.CreateMode2 ()
+ ]
+
+ let consumedArgIndices_WhatToDo = System.Collections.Generic.HashSet ()
//----------------------------------------------------------------------
- // Helper functions for applying args to DU candidates
+ // Helper functions for applying args (applyKeyValueToSubModeXState unchanged)
//----------------------------------------------------------------------
+ let applyKeyValueToSubMode1State
+ (argIndex : int)
+ (keyIndex : int)
+ (key : string)
+ (value : string)
+ (subState : State_SubMode1)
+ (candidate : CandidateParseState_Modes)
+ : unit
+ =
+ // ... (Implementation identical to previous version) ...
+ if String.Equals (key, "--info1", StringComparison.OrdinalIgnoreCase) then
+ match subState.Info1 with
+ | Some _ ->
+ candidate.Errors.Add (sprintf "Argument '--info1' supplied multiple times (SubMode1)")
+ candidate.IsViable <- false
+ | None ->
+ try
+ subState.Info1 <- Some (Int32.Parse value)
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex ->
+ candidate.Errors.Add (
+ sprintf "Failed to parse '%s' for --info1 (SubMode1): %s" value ex.Message
+ )
- /// Tries to apply a key-value pair to a single candidate. Updates candidate state.
- let applyKeyValueToCandidate (argIndex: int, keyIndex: int, key: string, value: string) (candidate: CandidateParseState_Modes) : unit =
- if not candidate.IsViable then () else
+ candidate.IsViable <- false
+ elif String.Equals (key, "--info2", StringComparison.OrdinalIgnoreCase) then
+ match subState.Info2 with
+ | Some _ ->
+ candidate.Errors.Add (sprintf "Argument '--info2' supplied multiple times (SubMode1)")
+ candidate.IsViable <- false
+ | None ->
+ subState.Info2 <- Some value
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ elif String.Equals (key, "--rest", StringComparison.OrdinalIgnoreCase) then
+ subState.Rest.Add value
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ else
+ ()
+
+ let applyKeyValueToSubMode2State
+ (argIndex : int)
+ (keyIndex : int)
+ (key : string)
+ (value : string)
+ (subState : State_SubMode2)
+ (candidate : CandidateParseState_Modes)
+ : unit
+ =
+ // ... (Implementation identical to previous version) ...
+ if String.Equals (key, "--info1", StringComparison.OrdinalIgnoreCase) then
+ match subState.Info1 with
+ | Some _ ->
+ candidate.Errors.Add (sprintf "Argument '--info1' supplied multiple times (SubMode2)")
+ candidate.IsViable <- false
+ | None ->
+ try
+ subState.Info1 <- Some (Int32.Parse value)
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex ->
+ candidate.Errors.Add (
+ sprintf "Failed to parse '%s' for --info1 (SubMode2): %s" value ex.Message
+ )
+
+ candidate.IsViable <- false
+ elif String.Equals (key, "--info2", StringComparison.OrdinalIgnoreCase) then
+ match subState.Info2 with
+ | Some _ ->
+ candidate.Errors.Add (sprintf "Argument '--info2' supplied multiple times (SubMode2)")
+ candidate.IsViable <- false
+ | None ->
+ subState.Info2 <- Some value
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ elif String.Equals (key, "--rest", StringComparison.OrdinalIgnoreCase) then
+ try
+ subState.Rest.Add (Int32.Parse value)
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex ->
+ candidate.Errors.Add (
+ sprintf "Failed to parse '%s' as int32 for --rest (SubMode2): %s" value ex.Message
+ )
+
+ candidate.IsViable <- false
+ else
+ ()
- match candidate.CaseState with
- | Mode1 state ->
- if String.Equals(key, "--info1", StringComparison.OrdinalIgnoreCase) then
- match state.Things_Info1 with
- | Some _ -> candidate.Errors.Add(sprintf "Argument '--info1' supplied multiple times for Mode1 candidate"); candidate.IsViable <- false
+ //----------------------------------------------------------------------
+ // Routing and Main Application Logic (applyKeyValueToCandidate unchanged)
+ //----------------------------------------------------------------------
+ let applyKeyValueToCandidate
+ (argIndex : int, keyIndex : int, key : string, value : string)
+ (candidate : CandidateParseState_Modes)
+ : unit
+ =
+ // ... (Implementation identical to previous version, calling sub-state helpers) ...
+ if not candidate.IsViable then
+ ()
+ else
+
+ match candidate.CaseName with
+ | "Mode1" ->
+ let state = candidate.CaseState :?> State_Mode1
+
+ if String.Equals (key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
+ match state.Whatnot with
+ | Some _ ->
+ candidate.Errors.Add (
+ sprintf "Argument '--whatnot' supplied multiple times for Mode1 candidate"
+ )
+
+ candidate.IsViable <- false
| None ->
- try state.Things_Info1 <- Some(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' for --info1 (Mode1): %s" value ex.Message); candidate.IsViable <- false
- elif String.Equals(key, "--info2", StringComparison.OrdinalIgnoreCase) then
- match state.Things_Info2 with
- | Some _ -> candidate.Errors.Add(sprintf "Argument '--info2' supplied multiple times for Mode1 candidate"); candidate.IsViable <- false
- | None -> state.Things_Info2 <- Some value; candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- elif String.Equals(key, "--rest", StringComparison.OrdinalIgnoreCase) then
- // String list for Mode1
- state.Things_Rest.Add value; candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- elif String.Equals(key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
- match state.Whatnot with
- | Some _ -> candidate.Errors.Add(sprintf "Argument '--whatnot' supplied multiple times for Mode1 candidate"); candidate.IsViable <- false
- | None ->
- try state.Whatnot <- Some(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' as int32 for --whatnot (Mode1): %s" value ex.Message); candidate.IsViable <- false
+ try
+ state.Whatnot <- Some (Int32.Parse value)
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex ->
+ candidate.Errors.Add (
+ sprintf "Failed to parse '%s' as int32 for --whatnot (Mode1): %s" value ex.Message
+ )
+
+ candidate.IsViable <- false
+ elif key = "--info1" || key = "--info2" || key = "--rest" then
+ applyKeyValueToSubMode1State argIndex keyIndex key value state.ThingsState candidate
else
- // Key not relevant to Mode1, ignore it for this candidate
()
+ | "Mode2" ->
+ let state = candidate.CaseState :?> State_Mode2
+
+ if String.Equals (key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
+ match state.Whatnot with
+ | Some _ ->
+ candidate.Errors.Add (
+ sprintf "Argument '--whatnot' supplied multiple times for Mode2 candidate"
+ )
- | Mode2 state ->
- if String.Equals(key, "--info1", StringComparison.OrdinalIgnoreCase) then
- match state.Things_Info1 with
- | Some _ -> candidate.Errors.Add(sprintf "Argument '--info1' supplied multiple times for Mode2 candidate"); candidate.IsViable <- false
+ candidate.IsViable <- false
| None ->
- try state.Things_Info1 <- Some(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' for --info1 (Mode2): %s" value ex.Message); candidate.IsViable <- false
- elif String.Equals(key, "--info2", StringComparison.OrdinalIgnoreCase) then
- match state.Things_Info2 with
- | Some _ -> candidate.Errors.Add(sprintf "Argument '--info2' supplied multiple times for Mode2 candidate"); candidate.IsViable <- false
- | None -> state.Things_Info2 <- Some value; candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- elif String.Equals(key, "--rest", StringComparison.OrdinalIgnoreCase) then
- // Int list for Mode2
- try state.Things_Rest.Add(Int32.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' as int32 for --rest (Mode2): %s" value ex.Message); candidate.IsViable <- false
- elif String.Equals(key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
- match state.Whatnot with
- | Some _ -> candidate.Errors.Add(sprintf "Argument '--whatnot' supplied multiple times for Mode2 candidate"); candidate.IsViable <- false
- | None ->
- try state.Whatnot <- Some(DateTime.Parse value); candidate.ConsumedArgIndices.Add argIndex |> ignore; candidate.ConsumedArgIndices.Add keyIndex |> ignore
- with ex -> candidate.Errors.Add(sprintf "Failed to parse '%s' as DateTime for --whatnot (Mode2): %s" value ex.Message); candidate.IsViable <- false
+ try
+ state.Whatnot <- Some (DateTime.Parse value)
+ candidate.ConsumedArgIndices.Add argIndex |> ignore
+ candidate.ConsumedArgIndices.Add keyIndex |> ignore
+ with ex ->
+ candidate.Errors.Add (
+ sprintf "Failed to parse '%s' as DateTime for --whatnot (Mode2): %s" value ex.Message
+ )
+
+ candidate.IsViable <- false
+ elif key = "--info1" || key = "--info2" || key = "--rest" then
+ applyKeyValueToSubMode2State argIndex keyIndex key value state.ThingsState candidate
else
- // Key not relevant to Mode2, ignore it for this candidate
()
+ | _ -> failwith "Internal error: Unknown case name"
- /// Processes a key-value pair across all candidates. Returns true if handled by *any* viable candidate.
- let processKeyValue (keyIndex: int, key: string, valueIndex: int, value: string) : bool =
+ // processKeyValue, setFlagValue, and main loop `go` are identical to previous version
+ let processKeyValue (keyIndex : int, key : string, valueIndex : int, value : string) : bool =
let mutable handled = false
+
for candidate in candidates_WhatToDo do
let initialConsumedCount = candidate.ConsumedArgIndices.Count
- applyKeyValueToCandidate (valueIndex, keyIndex, key, value) candidate
+
+ if candidate.IsViable then
+ applyKeyValueToCandidate (valueIndex, keyIndex, key, value) candidate
+
if candidate.IsViable && candidate.ConsumedArgIndices.Count > initialConsumedCount then
- // Mark as handled if *any* viable candidate consumed it
handled <- true
- // Add consumed indices to the global set for leftover detection later
consumedArgIndices_WhatToDo.Add keyIndex |> ignore
consumedArgIndices_WhatToDo.Add valueIndex |> ignore
- handled
- /// Processes a flag across all candidates. Returns true if handled by *any* viable candidate.
- /// Note: No boolean flags defined in this example, so this is trivial.
- let setFlagValue (keyIndex: int, key: string) : bool =
- let mutable handled = false
- // Example: If --info1 were a flag for Mode1
- // for candidate in candidates_WhatToDo do
- // if candidate.IsViable && candidate.CaseName = "Mode1" then
- // let state = candidate.CaseState :?> State_Mode1
- // if String.Equals(key, "--info1", StringComparison.OrdinalIgnoreCase) then
- // match state.Things_Info1 with // Assuming it was bool option
- // | Some _ -> candidate.Errors.Add(...) ; candidate.IsViable <- false
- // | None -> state.Things_Info1 <- Some true; candidate.ConsumedArgIndices.Add keyIndex |> ignore; handled <- true
- // if handled then consumedArgIndices_WhatToDo.Add keyIndex |> ignore
- handled // No flags in this specific schema
+ handled
+ let setFlagValue (keyIndex : int) (key : string) : bool = false // No flags
- //----------------------------------------------------------------------
- // Main parsing loop
- //----------------------------------------------------------------------
- let rec go (state: ParseState_Args) (args: (int * string) list) =
+ let rec go (state : ParseState_Args) (args : (int * string) list) =
+ // ... (Implementation identical to previous version) ...
match args with
- | [] -> // End of arguments
+ | [] ->
match state with
- | ParseState_Args.AwaitingArg -> () // Expected state
- | ParseState_Args.AwaitingValue (keyIndex, key) ->
- // Trailing key without value
- if not (setFlagValue (keyIndex, key)) then
- // Not a flag either, report error
- ArgParser_errors.Add (sprintf "Trailing argument '%s' (at index %d) requires a value." key keyIndex)
-
- | (argIndex, arg) :: remainingArgs ->
+ | ParseState_Args.AwaitingArg -> ()
+ | ParseState_Args.AwaitingValue (i, k) ->
+ if not (setFlagValue i k) then
+ ArgParser_errors.Add (sprintf "Trailing argument '%s' (at index %d) requires a value." k i)
+ | (idx, arg) :: rest ->
match state with
| ParseState_Args.AwaitingArg ->
if arg = "--" then
- // Consume rest as positional
- remainingArgs |> List.iter (fun (i, positionalArg) ->
- // Check if arg was potentially consumed by DU before adding
+ rest
+ |> List.iter (fun (i, v) ->
if not (consumedArgIndices_WhatToDo.Contains i) then
- arg_OtherArgs.Add positionalArg
+ arg_OtherArgs.Add v
)
- go ParseState_Args.AwaitingArg [] // Go to end state
- elif arg.StartsWith("--", StringComparison.Ordinal) then
+ go ParseState_Args.AwaitingArg []
+ elif arg.StartsWith ("--") then
if arg = "--help" then
- helpText () |> failwithf "Help text requested.\n%s"
+ helpText () |> failwithf "Help text requested:\n%s"
else
- let equalsPos = arg.IndexOf('=')
- if equalsPos > 0 then
- // --key=value format
- let key = arg.[0 .. equalsPos - 1]
- let value = arg.[equalsPos + 1 ..]
- if not (processKeyValue (argIndex, key, argIndex, value)) then
- // Key-value not handled by DU candidates, check if it belongs elsewhere (none in this example)
- // If still not handled, consider it potentially positional only if not consumed by DU
- if not (consumedArgIndices_WhatToDo.Contains argIndex) then
- arg_OtherArgs.Add arg // Treat unhandled --key=value as positional
- go ParseState_Args.AwaitingArg remainingArgs
+ let eq = arg.IndexOf ('=')
+
+ if eq > 0 then
+ let k = arg.[.. eq - 1]
+ let v = arg.[eq + 1 ..]
+
+ if not (processKeyValue (idx, k, idx, v)) then
+ if not (consumedArgIndices_WhatToDo.Contains idx) then
+ arg_OtherArgs.Add arg
+
+ go ParseState_Args.AwaitingArg rest
+ elif setFlagValue idx arg then
+ consumedArgIndices_WhatToDo.Add idx |> ignore
+ go ParseState_Args.AwaitingArg rest
else
- // --key format (potential flag or key needing subsequent value)
- if setFlagValue (argIndex, arg) then
- consumedArgIndices_WhatToDo.Add argIndex |> ignore
- go ParseState_Args.AwaitingArg remainingArgs // Flag consumed
- else
- go (ParseState_Args.AwaitingValue (argIndex, arg)) remainingArgs // Expect value next
-
- else // Positional argument
- // Add positional arg *only if* it hasn't been consumed by the DU logic
- if not (consumedArgIndices_WhatToDo.Contains argIndex) then
+ go (ParseState_Args.AwaitingValue (idx, arg)) rest
+ else
+ if not (consumedArgIndices_WhatToDo.Contains idx) then
arg_OtherArgs.Add arg
- go ParseState_Args.AwaitingArg remainingArgs
-
- | ParseState_Args.AwaitingValue (keyIndex, key) ->
- // We have a key, current arg is its potential value
- if processKeyValue (keyIndex, key, argIndex, arg) then
- go ParseState_Args.AwaitingArg remainingArgs // Key-value pair consumed
- else
- // Value wasn't parseable/applicable for the key via DU candidates.
- // Could the key have been a flag?
- if setFlagValue (keyIndex, key) then
- consumedArgIndices_WhatToDo.Add keyIndex |> ignore
- // Flag consumed, reprocess the current arg in AwaitingArg state
- go ParseState_Args.AwaitingArg ((argIndex, arg) :: remainingArgs)
- else
- // Not a flag, not a valid value. Error reported by processKeyValue/apply...
- // Treat *both* key and arg as positional if not consumed by DU.
- if not (consumedArgIndices_WhatToDo.Contains keyIndex) then
- arg_OtherArgs.Add key
- if not (consumedArgIndices_WhatToDo.Contains argIndex) then
- arg_OtherArgs.Add arg
- go ParseState_Args.AwaitingArg remainingArgs
+ go ParseState_Args.AwaitingArg rest
+ | ParseState_Args.AwaitingValue (keyIdx, key) ->
+ if processKeyValue (keyIdx, key, idx, arg) then
+ go ParseState_Args.AwaitingArg rest
+ elif setFlagValue keyIdx key then
+ consumedArgIndices_WhatToDo.Add keyIdx |> ignore
+ go ParseState_Args.AwaitingArg ((idx, arg) :: rest) // Reprocess arg
+ elif not (consumedArgIndices_WhatToDo.Contains keyIdx) then
+ arg_OtherArgs.Add key
+
+ if not (consumedArgIndices_WhatToDo.Contains idx) then
+ arg_OtherArgs.Add arg
+
+ go ParseState_Args.AwaitingArg rest
args |> List.mapi (fun i s -> (i, s)) |> go ParseState_Args.AwaitingArg
//----------------------------------------------------------------------
- // Final Validation and Assembly
+ // Final Validation and Assembly (Uses new Assemble methods)
//----------------------------------------------------------------------
-
- // 1. Validate and Assemble the DU 'WhatToDo'
- let viableWinners =
- candidates_WhatToDo
- |> List.filter (fun c -> c.IsViable)
- // Further filter: ensure all required args *for the specific case* are present
- // And ensure no args were left unconsumed *relative to this candidate*
- |> List.filter (fun c ->
- let mutable caseComplete = true
- let caseErrors = ResizeArray()
-
- // Check required fields based on case
- match c.CaseState with
- | Mode1 state ->
- if state.Things_Info1.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info1' is required for Mode1.")
- if state.Things_Info2.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info2' is required for Mode1.")
- // Rest is list, always 'complete'
- if state.Whatnot.IsNone then caseComplete <- false; caseErrors.Add("Argument '--whatnot' is required for Mode1.")
- | Mode2 state ->
- if state.Things_Info1.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info1' is required for Mode2.")
- if state.Things_Info2.IsNone then caseComplete <- false; caseErrors.Add("Argument '--info2' is required for Mode2.")
- // Rest is list, always 'complete'
- if state.Whatnot.IsNone then caseComplete <- false; caseErrors.Add("Argument '--whatnot' is required for Mode2.")
-
- // Check for relative leftovers: Ensure all args were either consumed by this candidate or the top-level positional args
- let isLeftover (i: int, _:string) =
- not (c.ConsumedArgIndices.Contains i) && // Not consumed by this candidate
- not (arg_OtherArgs.Contains (args.[i])) // Not consumed by top-level positional (approx check) - better check indices!
- // A more accurate leftover check requires comparing consumed sets properly
- let hasRelativeLeftovers = false // Simplified: Assume validation handles required fields, and global positional catches others.
-
- if not caseComplete then c.Errors.AddRange caseErrors
- caseComplete && not hasRelativeLeftovers
- )
+ let viableWinners = candidates_WhatToDo |> List.filter (fun c -> c.IsViable)
+ // No longer filter based on IsComplete here; Assemble handles it.
+ // Still need to check for relative leftovers if that logic were implemented.
let whatToDoResult =
match viableWinners with
| [] ->
- ArgParser_errors.Add("No valid parse found for 'WhatToDo'.")
- // Add specific errors from candidates if available
+ // Add specific errors from candidates that were viable *before* Assemble check
+ ArgParser_errors.Add ("No valid parse found for 'WhatToDo'.")
+
candidates_WhatToDo
- |> List.iter (fun c -> if c.Errors.Count <> 0 then ArgParser_errors.Add(sprintf " Candidate %s errors: %s" c.CaseName (String.concat "; " c.Errors)))
+ |> List.iter (fun c ->
+ if c.Errors.Count <> 0 then
+ ArgParser_errors.Add (
+ sprintf " Candidate %s parse errors: %s" c.CaseName (String.concat "; " c.Errors)
+ )
+ // Potentially try to Assemble even non-viable ones to get completion errors? Maybe too complex.
+ )
+
Unchecked.defaultof<_> // Error path
- | [winner] ->
- // Assemble the winning case
- match winner.CaseState with
- | Mode1 state ->
- // We know required fields are Some(_) due to filter above
- let subMode1: SubMode1 = { Info1 = state.Things_Info1.Value; Info2 = state.Things_Info2.Value; Rest = state.Things_Rest |> Seq.toList }
- let mode1: Mode1 = { Things = subMode1; Whatnot = state.Whatnot.Value }
- Modes.Mode1 mode1
- | Mode2 state ->
- let subMode2 = { Info1 = state.Things_Info1.Value; Info2 = state.Things_Info2.Value; Rest = state.Things_Rest |> Seq.toList }
- let mode2 = { Things = subMode2; Whatnot = state.Whatnot.Value }
- Modes.Mode2 mode2
+
+ | [ winner ] ->
+ // Assemble the winning case, checking the Result for completion errors
+ match winner.CaseName with
+ | "Mode1" ->
+ match (winner.CaseState :?> State_Mode1).Assemble () with
+ | Ok mode1Value -> Modes.Mode1 mode1Value
+ | Error completionErrors ->
+ ArgParser_errors.Add (sprintf "Validation failed for selected candidate Mode1:")
+ ArgParser_errors.AddRange completionErrors
+ Unchecked.defaultof<_> // Error path
+ | "Mode2" ->
+ match (winner.CaseState :?> State_Mode2).Assemble () with
+ | Ok mode2Value -> Modes.Mode2 mode2Value
+ | Error completionErrors ->
+ ArgParser_errors.Add (sprintf "Validation failed for selected candidate Mode2:")
+ ArgParser_errors.AddRange completionErrors
+ Unchecked.defaultof<_> // Error path
+ | _ -> failwith "Internal error: Unknown winning case name"
| winners -> // Ambiguous parse
- ArgParser_errors.Add("Ambiguous parse for 'WhatToDo'. Multiple modes matched:")
- winners |> List.iter (fun c -> ArgParser_errors.Add(sprintf " - %s" c.CaseName))
+ ArgParser_errors.Add ("Ambiguous parse for 'WhatToDo'. Multiple modes potentially viable:")
+
+ winners
+ |> List.iter (fun c ->
+ ArgParser_errors.Add (
+ sprintf
+ " - %s (Initial Errors: %s)"
+ c.CaseName
+ (if c.Errors.Count = 0 then
+ "None"
+ else
+ String.concat "; " c.Errors)
+ )
+ )
+
Unchecked.defaultof<_> // Error path
- // 2. Finalize OtherArgs
+ // Finalize OtherArgs (unchanged)
let otherArgsResult = arg_OtherArgs |> Seq.toList
- // 3. Assemble Final Result or Fail
+ // Assemble Final Result or Fail (unchanged)
if ArgParser_errors.Count > 0 then
- ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s\n\nHelp Text:\n%s" (helpText())
+ ArgParser_errors
+ |> String.concat "\n"
+ |> failwithf "Errors during parse!\n%s\n\nHelp Text:\n%s" (helpText ())
else
- { WhatToDo = whatToDoResult; OtherArgs = otherArgsResult }
+ {
+ WhatToDo = whatToDoResult
+ OtherArgs = otherArgsResult
+ }
- /// Parses the command line arguments into an Args record.
- let parse (args: string list) : Args =
+ let parse (args : string list) : Args =
parse' System.Environment.GetEnvironmentVariable args
diff --git a/Playground/Program.fs b/Playground/Program.fs
index a96d3b55..81255ece 100644
--- a/Playground/Program.fs
+++ b/Playground/Program.fs
@@ -1,11 +1,10 @@
namespace Playground
-open GeneratedParsers
-
module Program =
[]
let main argv =
- ["--whatnot=2024-01-12";"--info1=4";"--info2=hi"]
- |> ArgsModule.parse
+ [ "--whatnot=2024-01-12" ; "--info1=4" ; "--info2=hi" ]
+ |> Args.parse
|> printfn "%O"
+
0
diff --git a/WoofWare.Myriad.Plugins/ShibaGenerator.fs b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
new file mode 100644
index 00000000..0df4735f
--- /dev/null
+++ b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
@@ -0,0 +1,629 @@
+namespace WoofWare.Myriad.Plugins
+
+open System
+open System.Text
+open Fantomas.FCS.Syntax
+open Fantomas.FCS.Text.Range
+open TypeEquality
+open WoofWare.Whippet.Fantomas
+
+type internal ArgParserOutputSpec =
+ {
+ ExtensionMethods : bool
+ }
+
+type internal FlagDu =
+ {
+ Name : Ident
+ Case1Name : Ident
+ Case2Name : Ident
+ /// Hopefully this is simply the const bool True or False, but it might e.g. be a literal
+ Case1Arg : SynExpr
+ /// Hopefully this is simply the const bool True or False, but it might e.g. be a literal
+ Case2Arg : SynExpr
+ }
+
+ static member FromBoolean (flagDu : FlagDu) (value : SynExpr) =
+ SynExpr.ifThenElse
+ (SynExpr.equals value flagDu.Case1Arg)
+ (SynExpr.createLongIdent' [ flagDu.Name ; flagDu.Case2Name ])
+ (SynExpr.createLongIdent' [ flagDu.Name ; flagDu.Case1Name ])
+
+/// The default value of an argument which admits default values can be pulled from different sources.
+/// This defines which source a particular default value comes from.
+type private ArgumentDefaultSpec =
+ /// From parsing the environment variable with the given name (e.g. "WOOFWARE_DISABLE_FOO" or whatever).
+ | EnvironmentVariable of name : SynExpr
+ /// From calling the static member `{typeWeParseInto}.Default{name}()`
+ /// For example, if `type MyArgs = { Thing : Choice }`, then
+ /// we would use `MyArgs.DefaultThing () : int`.
+ ///
+ | FunctionCall of name : Ident
+
+type private Accumulation<'choice> =
+ | Required
+ | Optional
+ | Choice of 'choice
+ | List of Accumulation<'choice>
+
+type private ParseFunction<'acc> =
+ {
+ FieldName : Ident
+ TargetVariable : Ident
+ /// Any of the forms in this set are acceptable, but make sure they all start with a dash, or we might
+ /// get confused with positional args or something! I haven't thought that hard about this.
+ /// In the default case, this is `Const("arg-name")` for the `ArgName : blah` field; note that we have
+ /// omitted the initial `--` that will be required at runtime.
+ ArgForm : SynExpr list
+ /// If this is a boolean-like field (e.g. a bool or a flag DU), the help text should look a bit different:
+ /// we should lie to the user about the value of the cases there.
+ /// Similarly, if we're reading from an environment variable with the laxer parsing rules of accepting e.g.
+ /// "0" instead of "false", we need to know if we're reading a bool.
+ /// In that case, `boolCases` is Some, and contains the construction of the flag (or boolean, in which case
+ /// you get no data).
+ BoolCases : Choice option
+ Help : SynExpr option
+ /// A function string -> %TargetType%, where TargetVariable is probably a `%TargetType% option`.
+ /// (Depending on `Accumulation`, we'll remove the `option` at the end of the parse, asserting that the
+ /// argument was supplied.)
+ /// This is allowed to throw if it fails to parse.
+ Parser : SynExpr
+ /// If `Accumulation` is `List`, then this is the type of the list *element*; analogously for optionals
+ /// and choices and so on.
+ TargetType : SynType
+ Accumulation : 'acc
+ }
+
+ /// A SynExpr of type `string` which we can display to the user at generated-program runtime to display all
+ /// the ways they can refer to this arg.
+ member arg.HumanReadableArgForm : SynExpr =
+ let formatString = List.replicate arg.ArgForm.Length "--%s" |> String.concat " / "
+
+ (SynExpr.applyFunction (SynExpr.createIdent "sprintf") (SynExpr.CreateConst formatString), arg.ArgForm)
+ ||> List.fold SynExpr.applyFunction
+ |> SynExpr.paren
+
+
+module internal ShibaGenerator =
+ open SynTypePatterns
+
+ type RecognisedType =
+ | Union of UnionType
+ | Record of RecordType
+
+ member this.Name : Ident =
+ match this with
+ | Union unionType -> unionType.Name
+ | Record recordType -> recordType.Name
+
+ /// Some types don't have in-progress equivalents (e.g. a no-data DU, which is "basically primitive");
+ /// hence the `option`.
+ let createInProgressRecognisedType
+ (flagDuNames : string list)
+ (allKnownTypeIdents : string list)
+ (ty : RecognisedType)
+ : RecordType option
+ =
+ let getInProgressTypeName (ty : LongIdent) : SynType =
+ // TODO: this is super jank
+ let ident = List.last ty
+
+ if List.contains ident.idText flagDuNames then
+ // Flag DUs have no in-progress form as such
+ SynType.createLongIdent ty |> SynType.option
+ elif List.contains ident.idText allKnownTypeIdents then
+ SynType.createLongIdent [ ident.idText + "_InProgress" |> Ident.create ]
+ else
+ // TODO: this is just nonsense, probably
+ SynType.createLongIdent ty |> SynType.option
+
+ let makeType (attrs : SynAttribute list) (ty : SynType) (id : Ident) : SynField option =
+ match ty with
+ | ChoiceType [ left ; right ] ->
+ if not (SynType.provablyEqual left right) then
+ failwith
+ $"ArgParser was unable to prove types %O{left} and %O{right} to be equal in a Choice. We require them to be equal."
+
+ {
+ Attrs = []
+ Ident = Some id
+ Type = SynType.option left
+ }
+ |> SynField.make
+ |> Some
+ | ChoiceType _ ->
+ failwith
+ $"Only `Choice`s with exactly two args are supported, and they must have the same type on each side (field name: %s{id.idText})"
+ | ListType contents ->
+ // TODO: jank conditional
+ if
+ attrs
+ |> List.exists (fun x -> List.last(x.TypeName.LongIdent).idText.StartsWith "PositionalArgs")
+ then
+ // Omit positional args, they are treated in the Finalise
+ None
+ else
+
+ {
+ Attrs = []
+ Ident = Some id
+ Type =
+ // Parser will take strings later, when finalising
+ SynType.list SynType.string
+ }
+ |> SynField.make
+ |> Some
+ | PrimitiveType ty ->
+ {
+ Attrs = []
+ Ident = Some id
+ Type = SynType.option (SynType.createLongIdent ty)
+ }
+ |> SynField.make
+ |> Some
+ | OptionType ty ->
+ {
+ Attrs = []
+ Ident = Some id
+ Type =
+ // an `option` is its own in-progress
+ SynType.option ty
+ }
+ |> SynField.make
+ |> Some
+ | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
+ // Assume this is in-progress
+ {
+ Attrs = []
+ Ident = Some id
+ Type = getInProgressTypeName ident
+ }
+ |> SynField.make
+ |> Some
+ | ty -> failwith $"TODO: %O{ty}"
+
+ match ty with
+ | RecognisedType.Union union ->
+ if union.Cases |> List.forall (fun case -> case.Fields.IsEmpty) then
+ None
+ else
+
+ {
+ Name = union.Name.idText + "_InProgress" |> Ident.create
+ XmlDoc = PreXmlDoc.create $"A partially-parsed %s{union.Name.idText}." |> Some
+ Members =
+ SynExpr.CreateConst "TODO: now construct the object"
+ |> SynBinding.basic
+ [ Ident.create "this" ; Ident.create "Assemble" ]
+ [
+ SynPat.annotateType (SynType.list SynType.string) (SynPat.named "positionals")
+ ]
+ |> SynBinding.withReturnAnnotation (SynType.createLongIdent [ union.Name ])
+ |> SynMemberDefn.memberImplementation
+ |> List.singleton
+ |> Some
+ Fields =
+ union.Cases
+ |> List.mapi (fun i data -> i, data)
+ |> List.choose (fun (caseNum, case) ->
+ match case.Fields with
+ | [] ->
+ failwith
+ $"Union type %s{union.Name.idText} has case %s{case.Name.idText} with no data; we require all cases to have exactly one field, or else all cases to be empty."
+ | [ x ] -> makeType x.Attrs x.Type (Ident.create $"Case_%i{caseNum}")
+ | _ ->
+ failwith
+ $"Union type %s{union.Name.idText} has case %s{case.Name.idText} with multiple fields; we require all cases to have exactly one field, or else all cases to be empty. Define a record type to hold the contents."
+ )
+ |> fun l ->
+ if l.IsEmpty then
+ [
+ SynField.make
+ {
+ Attrs = []
+ Ident = Some (Ident.create "_Dummy")
+ Type = SynType.unit
+ }
+ ]
+ else
+ l
+ Generics =
+ match union.Generics with
+ | None -> None
+ | Some _ -> failwith $"Union type %s{union.Name.idText} had generics, which we don't support."
+ TypeAccessibility = Some (SynAccess.Private range0)
+ ImplAccessibility = None
+ Attributes = []
+ }
+ |> Some
+ | RecognisedType.Record record ->
+ {
+ Name = record.Name.idText + "_InProgress" |> Ident.create
+ Fields =
+ record.Fields
+ |> List.choose (fun (SynField.SynField (attrs, _, id, ty, _, _, _, _, _)) ->
+ match id with
+ | None ->
+ failwith $"expected field in record %s{record.Name.idText} to have a name, but it did not"
+ | Some id -> makeType (SynAttributes.toAttrs attrs) ty id
+ )
+ |> fun l ->
+ if l.IsEmpty then
+ [
+ SynField.make
+ {
+ Attrs = []
+ Ident = Some (Ident.create "_Dummy")
+ Type = SynType.unit
+ }
+ ]
+ else
+ l
+ Members =
+ SynExpr.CreateConst "TODO: now construct the object"
+ |> SynBinding.basic
+ [ Ident.create "this" ; Ident.create "Assemble" ]
+ [
+ SynPat.annotateType (SynType.list SynType.string) (SynPat.named "positionals")
+ ]
+ |> SynBinding.withReturnAnnotation (SynType.createLongIdent [ record.Name ])
+ |> SynMemberDefn.memberImplementation
+ |> List.singleton
+ |> Some
+ XmlDoc = PreXmlDoc.create $"A partially-parsed %s{record.Name.idText}." |> Some
+ Generics =
+ match record.Generics with
+ | None -> None
+ | Some _ -> failwith $"Record type %s{record.Name.idText} had generics, which we don't support."
+ TypeAccessibility = Some (SynAccess.Private range0)
+ ImplAccessibility = None
+ Attributes = []
+ }
+ |> Some
+
+ let createHelpersModule
+ (opens : SynOpenDeclTarget list)
+ (ns : LongIdent)
+ (allUnionTypes : UnionType list)
+ (allRecordTypes : RecordType list)
+ : SynModuleDecl
+ =
+ let flagDus =
+ allUnionTypes
+ |> List.choose (fun ty ->
+ match ty.Cases with
+ | [ c1 ; c2 ] ->
+ let c1Attr =
+ c1.Attributes
+ |> List.tryPick (fun attr ->
+ match attr.TypeName with
+ | SynLongIdent.SynLongIdent (id, _, _) ->
+ match id |> List.last |> _.idText with
+ | "ArgumentFlagAttribute"
+ | "ArgumentFlag" -> Some (SynExpr.stripOptionalParen attr.ArgExpr)
+ | _ -> None
+ )
+
+ let c2Attr =
+ c2.Attributes
+ |> List.tryPick (fun attr ->
+ match attr.TypeName with
+ | SynLongIdent.SynLongIdent (id, _, _) ->
+ match id |> List.last |> _.idText with
+ | "ArgumentFlagAttribute"
+ | "ArgumentFlag" -> Some (SynExpr.stripOptionalParen attr.ArgExpr)
+ | _ -> None
+ )
+
+ match c1Attr, c2Attr with
+ | Some _, None
+ | None, Some _ ->
+ failwith
+ "[] must be placed on both cases of a two-case discriminated union, with opposite argument values on each case."
+ | None, None -> None
+ | Some c1Attr, Some c2Attr ->
+
+ // Sanity check where possible
+ match c1Attr, c2Attr with
+ | SynExpr.Const (SynConst.Bool b1, _), SynExpr.Const (SynConst.Bool b2, _) ->
+ if b1 = b2 then
+ failwith
+ "[] must have opposite argument values on each case in a two-case discriminated union."
+ | _, _ -> ()
+
+ match c1.Fields, c2.Fields with
+ | [], [] ->
+ {
+ Name = ty.Name
+ Case1Name = c1.Name
+ Case1Arg = c1Attr
+ Case2Name = c2.Name
+ Case2Arg = c2Attr
+ }
+ |> Some
+ | _, _ ->
+ failwith "[] may only be placed on discriminated union members with no data."
+ | _ -> None
+ )
+
+ let modName =
+ let ns = ns |> List.map _.idText |> String.concat "_"
+ Ident.create $"ArgParseHelpers_%s{ns}"
+
+ let modInfo =
+ SynComponentInfo.create modName
+ |> SynComponentInfo.withAccessibility (SynAccess.Private range0)
+ |> SynComponentInfo.withDocString (PreXmlDoc.create $"Helper types for arg parsing")
+
+ let allKnownTypeIdents =
+ let uts = allUnionTypes |> List.map _.Name.idText
+ let rts = allRecordTypes |> List.map _.Name.idText
+ uts @ rts
+
+ let flagDuNames = flagDus |> List.map _.Name.idText
+
+ let reducedRecordTypes =
+ allRecordTypes
+ |> List.choose (fun rt ->
+ // TODO: just split these into different functions and get rid of RecognisedType
+ createInProgressRecognisedType flagDuNames allKnownTypeIdents (RecognisedType.Record rt)
+ |> Option.map RecordType.ToAst
+ )
+
+ let reducedUnionTypes =
+ allUnionTypes
+ |> List.choose (fun ut ->
+ // TODO: just split these into different functions and get rid of RecognisedType
+ createInProgressRecognisedType flagDuNames allKnownTypeIdents (RecognisedType.Union ut)
+ |> Option.map RecordType.ToAst
+ )
+
+ let taggedMod =
+ [
+ for openStatement in opens do
+ yield SynModuleDecl.openAny openStatement
+ yield SynModuleDecl.openAny (SynOpenDeclTarget.ModuleOrNamespace (SynLongIdent.create ns, range0))
+
+ yield (reducedRecordTypes @ reducedUnionTypes) |> SynModuleDecl.createTypes
+ ]
+ |> SynModuleDecl.nestedModule modInfo
+
+ taggedMod
+
+ // The type for which we're generating args may refer to any of the supplied records/unions.
+ let createModule
+ (opens : SynOpenDeclTarget list)
+ (ns : LongIdent)
+ ((taggedType : SynTypeDefn, spec : ArgParserOutputSpec))
+ (allUnionTypes : UnionType list)
+ (allRecordTypes : RecordType list)
+ : SynModuleOrNamespace
+ =
+ let taggedType =
+ match taggedType with
+ | SynTypeDefn.SynTypeDefn (sci,
+ SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (access, fields, _), _),
+ smd,
+ _,
+ _,
+ _) -> RecordType.OfRecord sci smd access fields
+ | _ -> failwith "[] currently only supports being placed on records."
+
+ let modAttrs, modName =
+ if spec.ExtensionMethods then
+ [ SynAttribute.autoOpen ], Ident.create (taggedType.Name.idText + "ArgParse")
+ else
+ [ SynAttribute.requireQualifiedAccess ; SynAttribute.compilationRepresentation ], taggedType.Name
+
+ let modInfo =
+ SynComponentInfo.create modName
+ |> SynComponentInfo.withDocString (
+ PreXmlDoc.create $"Methods to parse arguments for the type %s{taggedType.Name.idText}"
+ )
+ |> SynComponentInfo.addAttributes modAttrs
+
+ let parseStateIdent = Ident.create $"ParseState_%s{taggedType.Name.idText}"
+
+ let parseStateType =
+ [
+ SynUnionCase.create
+ {
+ Attributes = []
+ Fields = []
+ Name = Ident.create "AwaitingKey"
+ XmlDoc = Some (PreXmlDoc.create "Ready to consume a key or positional arg")
+ Access = None
+ }
+ SynUnionCase.create
+ {
+ Attributes = []
+ Fields =
+ [
+ {
+ Attrs = []
+ Ident = Some (Ident.create "key")
+ Type = SynType.string
+ }
+ ]
+ Name = Ident.create "AwaitingValue"
+ XmlDoc = Some (PreXmlDoc.create "Waiting to receive a value for the key we've already consumed")
+ Access = None
+ }
+ ]
+ |> SynTypeDefnRepr.union
+ |> SynTypeDefn.create (
+ SynComponentInfo.create parseStateIdent
+ |> SynComponentInfo.setAccessibility (Some (SynAccess.Private range0))
+ )
+ |> List.singleton
+ |> SynModuleDecl.createTypes
+
+ let taggedMod =
+ let argsParam =
+ SynPat.named "args"
+ |> SynPat.annotateType (SynType.appPostfix "list" SynType.string)
+
+ let parsePrime =
+ SynExpr.CreateConst "todo"
+ |> SynExpr.applyFunction (SynExpr.createIdent "failwith")
+ |> SynBinding.basic
+ [ Ident.create "parse'" ]
+ [
+ SynPat.named "getEnvironmentVariable"
+ |> SynPat.annotateType (SynType.funFromDomain SynType.string SynType.string)
+ argsParam
+ ]
+ |> SynBinding.withReturnAnnotation (SynType.createLongIdent [ taggedType.Name ])
+
+ let parsePrimeCall =
+ if spec.ExtensionMethods then
+ // need to fully qualify
+ [ taggedType.Name ; Ident.create "parse'" ]
+ else
+ [ Ident.create "parse'" ]
+
+ let parse =
+ SynExpr.createLongIdent' parsePrimeCall
+ |> SynExpr.applyTo (SynExpr.createLongIdent [ "System" ; "Environment" ; "GetEnvironmentVariable" ])
+ |> SynExpr.applyTo (SynExpr.createIdent "args")
+ |> SynBinding.basic [ Ident.create "parse" ] [ argsParam ]
+ |> SynBinding.withReturnAnnotation (SynType.createLongIdent [ taggedType.Name ])
+
+ [
+ yield parseStateType
+
+ if spec.ExtensionMethods then
+ let bindingPrime = parsePrime |> SynMemberDefn.staticMember
+
+ let binding = parse |> SynMemberDefn.staticMember
+
+ let componentInfo =
+ SynComponentInfo.create taggedType.Name
+ |> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for argument parsing")
+
+ let containingType =
+ SynTypeDefnRepr.augmentation ()
+ |> SynTypeDefn.create componentInfo
+ |> SynTypeDefn.withMemberDefns [ bindingPrime ; binding ]
+
+ yield SynModuleDecl.createTypes [ containingType ]
+ else
+ yield SynModuleDecl.createLet parsePrime
+
+ yield SynModuleDecl.createLet parse
+ ]
+ |> SynModuleDecl.nestedModule modInfo
+
+ [
+ for openStatement in opens do
+ yield SynModuleDecl.openAny openStatement
+ yield taggedMod
+ ]
+ |> SynModuleOrNamespace.createNamespace ns
+
+open Myriad.Core
+
+/// Myriad generator that provides a catamorphism for an algebraic data type.
+[]
+type ArgParserGenerator () =
+
+ interface IMyriadGenerator with
+ member _.ValidInputExtensions = [ ".fs" ]
+
+ member _.Generate (context : GeneratorContext) =
+ let ast, _ =
+ Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
+
+ let types =
+ // Bug in WoofWare.Whippet, probably: we return types in the wrong order
+ Ast.getTypes ast |> List.map (fun (ns, types) -> ns, List.rev types)
+
+ let opens = AstHelper.extractOpens ast
+
+ let namespaceAndTypes =
+ types
+ |> List.collect (fun (ns, types) ->
+ let typeWithAttr =
+ types
+ |> List.choose (fun ty ->
+ match SynTypeDefn.getAttribute typeof.Name ty with
+ | None -> None
+ | Some attr ->
+ let arg =
+ match SynExpr.stripOptionalParen attr.ArgExpr with
+ | SynExpr.Const (SynConst.Bool value, _) -> value
+ | SynExpr.Const (SynConst.Unit, _) -> ArgParserAttribute.DefaultIsExtensionMethod
+ | arg ->
+ failwith
+ $"Unrecognised argument %+A{arg} to [<%s{nameof ArgParserAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
+
+ let spec =
+ {
+ ExtensionMethods = arg
+ }
+
+ Some (ty, spec)
+ )
+
+ typeWithAttr
+ |> List.map (fun taggedType ->
+ let unions, records, others =
+ (([], [], []), types)
+ ||> List.fold (fun
+ (unions, records, others)
+ (SynTypeDefn.SynTypeDefn (sci, repr, smd, _, _, _) as ty) ->
+ match repr with
+ | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (access, cases, _), _) ->
+ UnionType.OfUnion sci smd access cases :: unions, records, others
+ | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (access, fields, _), _) ->
+ unions, RecordType.OfRecord sci smd access fields :: records, others
+ | _ -> unions, records, ty :: others
+ )
+
+ if not others.IsEmpty then
+ failwith
+ $"Error: all types recursively defined together with an ArgParserGenerator type must be discriminated unions or records. %+A{others}"
+
+ (ns, taggedType, unions, records)
+ )
+ )
+
+ let unionsAndRecordsByNs =
+ (Map.empty, namespaceAndTypes)
+ ||> List.fold (fun types (ns, _, unions, records) ->
+ let nsKey = ns |> List.map _.idText |> String.concat "."
+
+ types
+ |> Map.change
+ nsKey
+ (fun v ->
+ match v with
+ | None -> Some (unions, records)
+ | Some (u, r) -> Some (unions @ u, records @ r)
+ )
+ )
+
+ let helpersMod =
+ unionsAndRecordsByNs
+ |> Map.toSeq
+ |> Seq.map (fun (ns, (unions, records)) ->
+ let unions = unions |> List.distinctBy (fun u -> u.Name.idText)
+ let records = records |> List.distinctBy (fun r -> r.Name.idText)
+
+ ShibaGenerator.createHelpersModule
+ opens
+ (ns.Split '.' |> Seq.map Ident.create |> List.ofSeq)
+ unions
+ records
+ )
+ |> Seq.toList
+ |> fun l -> [ yield! l ]
+ |> SynModuleOrNamespace.createNamespace [ Ident.create "ArgParserHelpers" ]
+
+ let modules =
+ namespaceAndTypes
+ |> List.map (fun (ns, taggedType, unions, records) ->
+ ShibaGenerator.createModule opens ns taggedType unions records
+ )
+
+ Output.Ast (helpersMod :: modules)
diff --git a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
index 06978b63..8aaf294d 100644
--- a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
+++ b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
@@ -40,7 +40,8 @@
-
+
+
From df6079e7636fa734a000bbfa85f0c4d6710bd32f Mon Sep 17 00:00:00 2001
From: Smaug123 <3138005+Smaug123@users.noreply.github.com>
Date: Sun, 13 Apr 2025 20:34:52 +0100
Subject: [PATCH 03/26] WIP
---
ConsumePlugin/GeneratedArgs.fs | 643 +++++++++++++++++++++-
WoofWare.Myriad.Plugins/ShibaGenerator.fs | 385 ++++++++++++-
2 files changed, 1001 insertions(+), 27 deletions(-)
diff --git a/ConsumePlugin/GeneratedArgs.fs b/ConsumePlugin/GeneratedArgs.fs
index f852cda1..18681c8b 100644
--- a/ConsumePlugin/GeneratedArgs.fs
+++ b/ConsumePlugin/GeneratedArgs.fs
@@ -26,7 +26,42 @@ module private ArgParseHelpers_ConsumePlugin =
Rest : string list
}
- member this.Assemble (positionals : string list) : BasicNoPositionals = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : int =
+ match this.Foo with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Foo"
+ Unchecked.defaultof<_>
+
+ let arg1 : string =
+ match this.Bar with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Bar"
+ Unchecked.defaultof<_>
+
+ let arg2 : bool =
+ match this.Baz with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Baz"
+ Unchecked.defaultof<_>
+
+ let arg3 : int list = this.Rest
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Foo = arg0
+ Bar = arg1
+ Baz = arg2
+ Rest = arg3
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed Basic.
type private Basic_InProgress =
@@ -36,7 +71,43 @@ module private ArgParseHelpers_ConsumePlugin =
Baz : System.Boolean option
}
- member this.Assemble (positionals : string list) : Basic = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : int =
+ match this.Foo with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Foo"
+ Unchecked.defaultof<_>
+
+ let arg1 : string =
+ match this.Bar with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Bar"
+ Unchecked.defaultof<_>
+
+ let arg2 : bool =
+ match this.Baz with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Baz"
+ Unchecked.defaultof<_>
+
+ let arg3 : string list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Foo = arg0
+ Bar = arg1
+ Baz = arg2
+ Rest = arg3
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed BasicWithIntPositionals.
type private BasicWithIntPositionals_InProgress =
@@ -46,7 +117,43 @@ module private ArgParseHelpers_ConsumePlugin =
Baz : System.Boolean option
}
- member this.Assemble (positionals : string list) : BasicWithIntPositionals = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : int =
+ match this.Foo with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Foo"
+ Unchecked.defaultof<_>
+
+ let arg1 : string =
+ match this.Bar with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Bar"
+ Unchecked.defaultof<_>
+
+ let arg2 : bool =
+ match this.Baz with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Baz"
+ Unchecked.defaultof<_>
+
+ let arg3 : int list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Foo = arg0
+ Bar = arg1
+ Baz = arg2
+ Rest = arg3
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed LoadsOfTypes.
type private LoadsOfTypes_InProgress =
@@ -63,7 +170,81 @@ module private ArgParseHelpers_ConsumePlugin =
YetAnotherOptionalThing : string option
}
- member this.Assemble (positionals : string list) : LoadsOfTypes = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : int =
+ match this.Foo with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Foo"
+ Unchecked.defaultof<_>
+
+ let arg1 : string =
+ match this.Bar with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Bar"
+ Unchecked.defaultof<_>
+
+ let arg2 : bool =
+ match this.Baz with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Baz"
+ Unchecked.defaultof<_>
+
+ let arg3 : FileInfo =
+ match this.SomeFile with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg4 : DirectoryInfo =
+ match this.SomeDirectory with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg5 : DirectoryInfo list = this.SomeList
+ let arg6 : int option = this.OptionalThingWithNoDefault
+ let arg7 : int list = positionals
+ let positionals = ()
+
+ let arg8 : Choice =
+ match this.OptionalThing with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ let arg9 : Choice =
+ match this.AnotherOptionalThing with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ let arg10 : Choice =
+ match this.YetAnotherOptionalThing with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Foo = arg0
+ Bar = arg1
+ Baz = arg2
+ SomeFile = arg3
+ SomeDirectory = arg4
+ SomeList = arg5
+ OptionalThingWithNoDefault = arg6
+ Positionals = arg7
+ OptionalThing = arg8
+ AnotherOptionalThing = arg9
+ YetAnotherOptionalThing = arg10
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed LoadsOfTypesNoPositionals.
type private LoadsOfTypesNoPositionals_InProgress =
@@ -80,7 +261,78 @@ module private ArgParseHelpers_ConsumePlugin =
YetAnotherOptionalThing : string option
}
- member this.Assemble (positionals : string list) : LoadsOfTypesNoPositionals = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : int =
+ match this.Foo with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Foo"
+ Unchecked.defaultof<_>
+
+ let arg1 : string =
+ match this.Bar with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Bar"
+ Unchecked.defaultof<_>
+
+ let arg2 : bool =
+ match this.Baz with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Baz"
+ Unchecked.defaultof<_>
+
+ let arg3 : FileInfo =
+ match this.SomeFile with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg4 : DirectoryInfo =
+ match this.SomeDirectory with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg5 : DirectoryInfo list = this.SomeList
+ let arg6 : int option = this.OptionalThingWithNoDefault
+
+ let arg7 : Choice =
+ match this.OptionalThing with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ let arg8 : Choice =
+ match this.AnotherOptionalThing with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ let arg9 : Choice =
+ match this.YetAnotherOptionalThing with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Foo = arg0
+ Bar = arg1
+ Baz = arg2
+ SomeFile = arg3
+ SomeDirectory = arg4
+ SomeList = arg5
+ OptionalThingWithNoDefault = arg6
+ OptionalThing = arg7
+ AnotherOptionalThing = arg8
+ YetAnotherOptionalThing = arg9
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed DatesAndTimes.
type private DatesAndTimes_InProgress =
@@ -91,7 +343,47 @@ module private ArgParseHelpers_ConsumePlugin =
InvariantExact : TimeSpan option
}
- member this.Assemble (positionals : string list) : DatesAndTimes = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : TimeSpan =
+ match this.Plain with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg1 : TimeSpan =
+ match this.Invariant with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg2 : TimeSpan =
+ match this.Exact with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg3 : TimeSpan =
+ match this.InvariantExact with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Plain = arg0
+ Invariant = arg1
+ Exact = arg2
+ InvariantExact = arg3
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ChildRecord.
type private ChildRecord_InProgress =
@@ -100,7 +392,31 @@ module private ArgParseHelpers_ConsumePlugin =
Thing2 : System.String option
}
- member this.Assemble (positionals : string list) : ChildRecord = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : int =
+ match this.Thing1 with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Thing1"
+ Unchecked.defaultof<_>
+
+ let arg1 : string =
+ match this.Thing2 with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Thing2"
+ Unchecked.defaultof<_>
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Thing1 = arg0
+ Thing2 = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ParentRecord.
type private ParentRecord_InProgress =
@@ -109,7 +425,31 @@ module private ArgParseHelpers_ConsumePlugin =
AndAnother : System.Boolean option
}
- member this.Assemble (positionals : string list) : ParentRecord = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : ChildRecord =
+ match this.Child with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg1 : bool =
+ match this.AndAnother with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for AndAnother"
+ Unchecked.defaultof<_>
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Child = arg0
+ AndAnother = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ChildRecordWithPositional.
type private ChildRecordWithPositional_InProgress =
@@ -117,7 +457,27 @@ module private ArgParseHelpers_ConsumePlugin =
Thing1 : System.Int32 option
}
- member this.Assemble (positionals : string list) : ChildRecordWithPositional = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : int =
+ match this.Thing1 with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for Thing1"
+ Unchecked.defaultof<_>
+
+ let arg1 : Uri list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Thing1 = arg0
+ Thing2 = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ParentRecordChildPos.
type private ParentRecordChildPos_InProgress =
@@ -126,7 +486,31 @@ module private ArgParseHelpers_ConsumePlugin =
AndAnother : System.Boolean option
}
- member this.Assemble (positionals : string list) : ParentRecordChildPos = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : ChildRecordWithPositional =
+ match this.Child with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg1 : bool =
+ match this.AndAnother with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for AndAnother"
+ Unchecked.defaultof<_>
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Child = arg0
+ AndAnother = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ParentRecordSelfPos.
type private ParentRecordSelfPos_InProgress =
@@ -134,7 +518,27 @@ module private ArgParseHelpers_ConsumePlugin =
Child : ChildRecord_InProgress
}
- member this.Assemble (positionals : string list) : ParentRecordSelfPos = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : ChildRecord =
+ match this.Child with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ let arg1 : bool list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Child = arg0
+ AndAnother = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ChoicePositionals.
type private ChoicePositionals_InProgress =
@@ -142,7 +546,18 @@ module private ArgParseHelpers_ConsumePlugin =
_Dummy : unit
}
- member this.Assemble (positionals : string list) : ChoicePositionals = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+ let arg0 : Choice list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ Args = arg0
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ContainsBoolEnvVar.
type private ContainsBoolEnvVar_InProgress =
@@ -150,7 +565,21 @@ module private ArgParseHelpers_ConsumePlugin =
BoolVar : bool option
}
- member this.Assemble (positionals : string list) : ContainsBoolEnvVar = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : Choice =
+ match this.BoolVar with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ if errors.Count = 0 then
+ Ok
+ {
+ BoolVar = arg0
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed WithFlagDu.
type private WithFlagDu_InProgress =
@@ -158,7 +587,23 @@ module private ArgParseHelpers_ConsumePlugin =
DryRun : DryRunMode option
}
- member this.Assemble (positionals : string list) : WithFlagDu = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : DryRunMode =
+ match this.DryRun with
+ | Ok result -> result
+ | Error err ->
+ err.AddRange errors
+ Unchecked.defaultof<_>
+
+ if errors.Count = 0 then
+ Ok
+ {
+ DryRun = arg0
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ContainsFlagEnvVar.
type private ContainsFlagEnvVar_InProgress =
@@ -166,7 +611,21 @@ module private ArgParseHelpers_ConsumePlugin =
DryRun : DryRunMode option
}
- member this.Assemble (positionals : string list) : ContainsFlagEnvVar = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : Choice =
+ match this.DryRun with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ if errors.Count = 0 then
+ Ok
+ {
+ DryRun = arg0
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ContainsFlagDefaultValue.
type private ContainsFlagDefaultValue_InProgress =
@@ -174,7 +633,21 @@ module private ArgParseHelpers_ConsumePlugin =
DryRun : DryRunMode option
}
- member this.Assemble (positionals : string list) : ContainsFlagDefaultValue = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : Choice =
+ match this.DryRun with
+ | Some result -> Choice1Of2 result
+ | None -> Choice2Of2 "TODO"
+
+ if errors.Count = 0 then
+ Ok
+ {
+ DryRun = arg0
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed ManyLongForms.
type private ManyLongForms_InProgress =
@@ -183,7 +656,31 @@ module private ArgParseHelpers_ConsumePlugin =
SomeFlag : System.Boolean option
}
- member this.Assemble (positionals : string list) : ManyLongForms = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : string =
+ match this.DoTheThing with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for DoTheThing"
+ Unchecked.defaultof<_>
+
+ let arg1 : bool =
+ match this.SomeFlag with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for SomeFlag"
+ Unchecked.defaultof<_>
+
+ if errors.Count = 0 then
+ Ok
+ {
+ DoTheThing = arg0
+ SomeFlag = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed FlagsIntoPositionalArgs.
type private FlagsIntoPositionalArgs_InProgress =
@@ -191,7 +688,27 @@ module private ArgParseHelpers_ConsumePlugin =
A : System.String option
}
- member this.Assemble (positionals : string list) : FlagsIntoPositionalArgs = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : string =
+ match this.A with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for A"
+ Unchecked.defaultof<_>
+
+ let arg1 : string list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ A = arg0
+ GrabEverything = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed FlagsIntoPositionalArgsChoice.
type private FlagsIntoPositionalArgsChoice_InProgress =
@@ -199,8 +716,27 @@ module private ArgParseHelpers_ConsumePlugin =
A : System.String option
}
- member this.Assemble (positionals : string list) : FlagsIntoPositionalArgsChoice =
- "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : string =
+ match this.A with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for A"
+ Unchecked.defaultof<_>
+
+ let arg1 : Choice list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ A = arg0
+ GrabEverything = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed FlagsIntoPositionalArgsInt.
type private FlagsIntoPositionalArgsInt_InProgress =
@@ -208,7 +744,27 @@ module private ArgParseHelpers_ConsumePlugin =
A : System.String option
}
- member this.Assemble (positionals : string list) : FlagsIntoPositionalArgsInt = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : string =
+ match this.A with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for A"
+ Unchecked.defaultof<_>
+
+ let arg1 : int list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ A = arg0
+ GrabEverything = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed FlagsIntoPositionalArgsIntChoice.
type private FlagsIntoPositionalArgsIntChoice_InProgress =
@@ -216,8 +772,27 @@ module private ArgParseHelpers_ConsumePlugin =
A : System.String option
}
- member this.Assemble (positionals : string list) : FlagsIntoPositionalArgsIntChoice =
- "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : string =
+ match this.A with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for A"
+ Unchecked.defaultof<_>
+
+ let arg1 : Choice list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ A = arg0
+ GrabEverything = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
/// A partially-parsed FlagsIntoPositionalArgs'.
type private FlagsIntoPositionalArgs'_InProgress =
@@ -225,7 +800,27 @@ module private ArgParseHelpers_ConsumePlugin =
A : System.String option
}
- member this.Assemble (positionals : string list) : FlagsIntoPositionalArgs' = "TODO: now construct the object"
+ member this.Assemble (positionals : string list) : Result =
+ let errors = ResizeArray ()
+
+ let arg0 : string =
+ match this.A with
+ | Some result -> result
+ | None ->
+ errors.Add "no value provided for A"
+ Unchecked.defaultof<_>
+
+ let arg1 : string list = positionals
+ let positionals = ()
+
+ if errors.Count = 0 then
+ Ok
+ {
+ A = arg0
+ DontGrabEverything = arg1
+ }
+ else
+ errors |> Seq.toList |> Error
namespace ConsumePlugin
open System
diff --git a/WoofWare.Myriad.Plugins/ShibaGenerator.fs b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
index 0df4735f..00882739 100644
--- a/WoofWare.Myriad.Plugins/ShibaGenerator.fs
+++ b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
@@ -96,6 +96,244 @@ module internal ShibaGenerator =
| Union unionType -> unionType.Name
| Record recordType -> recordType.Name
+ let private identifyAsFlag (flagDus : FlagDu list) (ty : SynType) : FlagDu option =
+ match ty with
+ | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
+ flagDus
+ |> List.tryPick (fun du ->
+ let duName = du.Name.idText
+ let ident = List.last(ident).idText
+ if duName = ident then Some du else None
+ )
+ | _ -> None
+
+ type private ParseFunctionSpec<'choice> =
+ /// A leaf node, e.g. `--foo=3`. Call the `parseFn` to turn the input `"3"` into the `typeAfterParse` (here, `int`).
+ /// `Accumulation` represents essentially how many times this leaf is expected to be called.
+ | Leaf of parseFn : SynExpr * acc : Accumulation<'choice> * typeAfterParse : SynType
+ /// An opaque node we didn't recognise: e.g. `Foo : SomeType`.
+ /// We're probably going to stamp out an "in-progress" type for this node.
+ /// (Either that, or it's just a type we don't recognise, and then compilation will fail.)
+ | UserDefined
+ /// An optional opaque node we didn't recognise: e.g. `Foo : SomeType option`.
+ /// We're probably going to stamp out an "in-progress" type for this node.
+ /// (Either that, or it's just a type we don't recognise, and then compilation will fail.)
+ | OptionOfUserDefined
+
+ /// Builds a function or lambda of one string argument, which returns a `ty` (as modified by the `Accumulation`;
+ /// for example, maybe it returns a `ty option` or a `ty list`).
+ /// The resulting SynType, if you get one, is the type of the *element* being parsed; so if the Accumulation is List, the SynType
+ /// is the list element.
+ let rec private createParseFunction<'choice>
+ (choice : ArgumentDefaultSpec option -> 'choice)
+ (flagDus : FlagDu list)
+ (fieldName : Ident)
+ (attrs : SynAttribute list)
+ (ty : SynType)
+ : ParseFunctionSpec<'choice>
+ =
+ match ty with
+ | String ->
+ ParseFunctionSpec.Leaf (
+ SynExpr.createLambda "x" (SynExpr.createIdent "x"),
+ Accumulation.Required,
+ SynType.string
+ )
+ | PrimitiveType pt ->
+ ParseFunctionSpec.Leaf (
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction
+ (SynExpr.createLongIdent' (pt @ [ Ident.create "Parse" ]))
+ (SynExpr.createIdent "x")),
+ Accumulation.Required,
+ ty
+ )
+ | Uri ->
+ ParseFunctionSpec.Leaf (
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Uri" ]) (SynExpr.createIdent "x")),
+ Accumulation.Required,
+ ty
+ )
+ | TimeSpan ->
+ let parseExact =
+ attrs
+ |> List.tryPick (fun attr ->
+ match attr.TypeName with
+ | SynLongIdent.SynLongIdent (idents, _, _) ->
+ match idents |> List.map (fun i -> i.idText) |> List.tryLast with
+ | Some "ParseExactAttribute"
+ | Some "ParseExact" -> Some attr.ArgExpr
+ | _ -> None
+ )
+
+ let culture =
+ attrs
+ |> List.tryPick (fun attr ->
+ match attr.TypeName with
+ | SynLongIdent.SynLongIdent (idents, _, _) ->
+ match idents |> List.map (fun i -> i.idText) |> List.tryLast with
+ | Some "InvariantCultureAttribute"
+ | Some "InvariantCulture" -> Some ()
+ | _ -> None
+ )
+
+ let parser =
+ match parseExact, culture with
+ | None, None ->
+ SynExpr.createIdent "x"
+ |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "TimeSpan" ; "Parse" ])
+ | Some format, None ->
+ [
+ SynExpr.createIdent "x"
+ format
+ SynExpr.createLongIdent [ "System" ; "Globalization" ; "CultureInfo" ; "CurrentCulture" ]
+ ]
+ |> SynExpr.tuple
+ |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "TimeSpan" ; "ParseExact" ])
+ | None, Some () ->
+ [
+ SynExpr.createIdent "x"
+ SynExpr.createLongIdent [ "System" ; "Globalization" ; "CultureInfo" ; "InvariantCulture" ]
+ ]
+ |> SynExpr.tuple
+ |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "TimeSpan" ; "Parse" ])
+ | Some format, Some () ->
+ [
+ SynExpr.createIdent "x"
+ format
+ SynExpr.createLongIdent [ "System" ; "Globalization" ; "CultureInfo" ; "InvariantCulture" ]
+ ]
+ |> SynExpr.tuple
+ |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "TimeSpan" ; "ParseExact" ])
+ |> SynExpr.createLambda "x"
+
+ ParseFunctionSpec.Leaf (parser, Accumulation.Required, ty)
+ | FileInfo ->
+ ParseFunctionSpec.Leaf (
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction
+ (SynExpr.createLongIdent [ "System" ; "IO" ; "FileInfo" ])
+ (SynExpr.createIdent "x")),
+ Accumulation.Required,
+ ty
+ )
+ | DirectoryInfo ->
+ ParseFunctionSpec.Leaf (
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction
+ (SynExpr.createLongIdent [ "System" ; "IO" ; "DirectoryInfo" ])
+ (SynExpr.createIdent "x")),
+ Accumulation.Required,
+ ty
+ )
+ | OptionType eltTy ->
+ match createParseFunction choice flagDus fieldName attrs eltTy with
+ | ParseFunctionSpec.Leaf (parseElt, acc, childTy) ->
+ match acc with
+ | Accumulation.Optional ->
+ failwith
+ $"ArgParser does not support optionals containing options at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.Choice _ ->
+ failwith
+ $"ArgParser does not support optionals containing choices at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.List _ ->
+ failwith $"ArgParser does not support optional lists at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.Required -> ParseFunctionSpec.Leaf (parseElt, Accumulation.Optional, childTy)
+ | ParseFunctionSpec.UserDefined -> ParseFunctionSpec.OptionOfUserDefined
+ | ParseFunctionSpec.OptionOfUserDefined ->
+ failwith $"ArgParser does not support lists of options at field %s{fieldName.idText}"
+ | ChoiceType elts ->
+ match elts with
+ | [ elt1 ; elt2 ] ->
+ if not (SynType.provablyEqual elt1 elt2) then
+ failwith
+ $"ArgParser was unable to prove types %O{elt1} and %O{elt2} to be equal in a Choice. We require them to be equal."
+
+ match createParseFunction choice flagDus fieldName attrs elt1 with
+ | ParseFunctionSpec.Leaf (parseElt, acc, childTy) ->
+ match acc with
+ | Accumulation.Optional ->
+ failwith
+ $"ArgParser does not support choices containing options at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.List _ ->
+ failwith
+ $"ArgParser does not support choices containing lists at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.Choice _ ->
+ failwith
+ $"ArgParser does not support choices containing choices at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.Required ->
+
+ let relevantAttrs =
+ attrs
+ |> List.choose (fun attr ->
+ let (SynLongIdent.SynLongIdent (name, _, _)) = attr.TypeName
+
+ match name |> List.map _.idText with
+ | [ "ArgumentDefaultFunction" ]
+ | [ "ArgumentDefaultFunctionAttribute" ]
+ | [ "Plugins" ; "ArgumentDefaultFunction" ]
+ | [ "Plugins" ; "ArgumentDefaultFunctionAttribute" ]
+ | [ "Myriad" ; "Plugins" ; "ArgumentDefaultFunction" ]
+ | [ "Myriad" ; "Plugins" ; "ArgumentDefaultFunctionAttribute" ]
+ | [ "WoofWare" ; "Myriad" ; "Plugins" ; "ArgumentDefaultFunction" ]
+ | [ "WoofWare" ; "Myriad" ; "Plugins" ; "ArgumentDefaultFunctionAttribute" ] ->
+ ArgumentDefaultSpec.FunctionCall (Ident.create ("Default" + fieldName.idText))
+ |> Some
+ | [ "ArgumentDefaultEnvironmentVariable" ]
+ | [ "ArgumentDefaultEnvironmentVariableAttribute" ]
+ | [ "Plugins" ; "ArgumentDefaultEnvironmentVariable" ]
+ | [ "Plugins" ; "ArgumentDefaultEnvironmentVariableAttribute" ]
+ | [ "Myriad" ; "Plugins" ; "ArgumentDefaultEnvironmentVariable" ]
+ | [ "Myriad" ; "Plugins" ; "ArgumentDefaultEnvironmentVariableAttribute" ]
+ | [ "WoofWare" ; "Myriad" ; "Plugins" ; "ArgumentDefaultEnvironmentVariable" ]
+ | [ "WoofWare" ; "Myriad" ; "Plugins" ; "ArgumentDefaultEnvironmentVariableAttribute" ] ->
+
+ ArgumentDefaultSpec.EnvironmentVariable attr.ArgExpr |> Some
+ | _ -> None
+ )
+
+ let relevantAttr =
+ match relevantAttrs with
+ | [] -> None
+ | [ x ] -> Some x
+ | _ ->
+ failwith
+ $"Expected Choice to be annotated with at most one ArgumentDefaultFunction or similar, but it was annotated with multiple. Field: %s{fieldName.idText}"
+
+ ParseFunctionSpec.Leaf (parseElt, Accumulation.Choice (choice relevantAttr), childTy)
+ | _ ->
+ failwith
+ $"Choices are only allowed to contain leaves; at %s{fieldName.idText}, got type %s{SynType.toHumanReadableString elt1}"
+ | elts ->
+ let elts = elts |> List.map string |> String.concat ", "
+
+ failwith
+ $"ArgParser requires Choice to be of the form Choice<'a, 'a>; that is, two arguments, both the same. For field %s{fieldName.idText}, got: %s{elts}"
+ | ListType eltTy ->
+ match createParseFunction choice flagDus fieldName attrs eltTy with
+ | ParseFunctionSpec.Leaf (parseElt, acc, childTy) ->
+ ParseFunctionSpec.Leaf (parseElt, Accumulation.List acc, childTy)
+ | _ ->
+ failwith
+ $"Lists are only allowed to contain leaves; at %s{fieldName.idText}, got type %s{SynType.toHumanReadableString eltTy}"
+ | ty ->
+ match identifyAsFlag flagDus ty with
+ | None -> ParseFunctionSpec.UserDefined
+ | Some flagDu ->
+ // Parse as a bool, and then do the `if-then` dance.
+ let parser =
+ SynExpr.createIdent "x"
+ |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Boolean" ; "Parse" ])
+ |> FlagDu.FromBoolean flagDu
+ |> SynExpr.createLambda "x"
+
+ ParseFunctionSpec.Leaf (parser, Accumulation.Required, ty)
+
/// Some types don't have in-progress equivalents (e.g. a no-data DU, which is "basically primitive");
/// hence the `option`.
let createInProgressRecognisedType
@@ -104,6 +342,7 @@ module internal ShibaGenerator =
(ty : RecognisedType)
: RecordType option
=
+ /// Get the "in-progress type" corresponding to the type with this name.
let getInProgressTypeName (ty : LongIdent) : SynType =
// TODO: this is super jank
let ident = List.last ty
@@ -198,7 +437,9 @@ module internal ShibaGenerator =
[
SynPat.annotateType (SynType.list SynType.string) (SynPat.named "positionals")
]
- |> SynBinding.withReturnAnnotation (SynType.createLongIdent [ union.Name ])
+ |> SynBinding.withReturnAnnotation (
+ SynType.app "Result" [ SynType.createLongIdent [ union.Name ] ; SynType.list SynType.string ]
+ )
|> SynMemberDefn.memberImplementation
|> List.singleton
|> Some
@@ -260,13 +501,151 @@ module internal ShibaGenerator =
else
l
Members =
- SynExpr.CreateConst "TODO: now construct the object"
+ // for each field `FieldName` in order, we've made a variable `arg%i`
+ // which has done the optionality check
+ let instantiation =
+ record.Fields
+ |> List.mapi (fun i (SynField.SynField (idOpt = ident)) ->
+ match ident with
+ | None ->
+ failwith
+ $"expected field in record %s{record.Name.idText} to have a name, but it did not"
+ | Some ident -> SynLongIdent.create [ ident ], SynExpr.createIdent $"arg%i{i}"
+ )
+ |> AstHelper.instantiateRecord
+ |> SynExpr.applyFunction (SynExpr.createIdent "Ok")
+
+ let defaultOf =
+ SynExpr.typeApp [ SynType.anon ] (SynExpr.createLongIdent [ "Unchecked" ; "defaultof" ])
+
+ let assignVariables =
+ record.Fields
+ |> List.mapi (fun i f -> (i, f))
+ |> List.collect (fun
+ (i, SynField.SynField (attributes = attrs ; fieldType = ty ; idOpt = ident)) ->
+ match ident with
+ | None ->
+ failwith
+ $"expected field in record %s{record.Name.idText} to have a name, but it did not"
+ | Some ident ->
+ // TODO: jank conditional
+ if
+ attrs
+ |> SynAttributes.toAttrs
+ |> List.exists (fun x ->
+ List.last(x.TypeName.LongIdent).idText.StartsWith "PositionalArgs"
+ )
+ then
+ // Positional args carried in from external argument
+ [
+ SynBinding.basic
+ [ Ident.create $"arg%i{i}" ]
+ []
+ (SynExpr.createIdent "positionals")
+ |> SynBinding.withReturnAnnotation ty
+ // prevent further usages of positional args
+ SynBinding.basic [ Ident.create "positionals" ] [] (SynExpr.CreateConst ())
+ ]
+ else
+ let extract =
+ match ty with
+ | ChoiceType [ _ ; _ ] ->
+ [
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Some" ]
+ (SynArgPats.create [ SynPat.named "result" ]))
+ (SynExpr.applyFunction
+ (SynExpr.createIdent "Choice1Of2")
+ (SynExpr.createIdent "result"))
+ SynMatchClause.create
+ (SynPat.identWithArgs [ Ident.create "None" ] (SynArgPats.create []))
+ (SynExpr.CreateConst "TODO"
+ |> SynExpr.applyFunction (SynExpr.createIdent "Choice2Of2"))
+ ]
+ |> SynExpr.createMatch (
+ SynExpr.dotGet ident.idText (SynExpr.createIdent "this")
+ )
+ | ListType _ -> SynExpr.dotGet ident.idText (SynExpr.createIdent "this")
+ | PrimitiveType _ ->
+ [
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Some" ]
+ (SynArgPats.create [ SynPat.named "result" ]))
+ (SynExpr.createIdent "result")
+ SynMatchClause.create
+ (SynPat.identWithArgs [ Ident.create "None" ] (SynArgPats.create []))
+ (SynExpr.sequential
+ [
+ SynExpr.callMethodArg
+ "Add"
+ (SynExpr.CreateConst
+ $"no value provided for %s{ident.idText}")
+ (SynExpr.createIdent "errors")
+ defaultOf
+ ])
+ ]
+ |> SynExpr.createMatch (
+ SynExpr.dotGet ident.idText (SynExpr.createIdent "this")
+ )
+ | OptionType _ -> SynExpr.dotGet ident.idText (SynExpr.createIdent "this")
+ | SynType.LongIdent (SynLongIdent.SynLongIdent _) ->
+ // TODO: need to know if it has positionals
+ [
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Ok" ]
+ (SynArgPats.create [ SynPat.named "result" ]))
+ (SynExpr.createIdent "result")
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Error" ]
+ (SynArgPats.create [ SynPat.named "err" ]))
+ (SynExpr.sequential
+ [
+ SynExpr.callMethodArg
+ "AddRange"
+ (SynExpr.createIdent "errors")
+ (SynExpr.createIdent "err")
+ defaultOf
+ ])
+ ]
+ |> SynExpr.createMatch (
+ SynExpr.dotGet ident.idText (SynExpr.createIdent "this")
+ )
+ | ty -> failwith $"TODO: got type {ty} which we don't know how to handle"
+
+ extract
+ |> SynBinding.basic [ Ident.create $"arg%i{i}" ] []
+ |> SynBinding.withReturnAnnotation ty
+ |> List.singleton
+ )
+
+ SynExpr.ifThenElse
+ (SynExpr.equals (SynExpr.dotGet "Count" (SynExpr.createIdent "errors")) (SynExpr.CreateConst 0))
+ (SynExpr.createIdent "errors"
+ |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "Seq" ; "toList" ])
+ |> SynExpr.pipeThroughFunction (SynExpr.createIdent "Error"))
+ instantiation
+ |> SynExpr.createLet assignVariables
+ |> SynExpr.createLet
+ [
+ SynBinding.basic
+ [ Ident.create "errors" ]
+ []
+ (SynExpr.applyFunction
+ (SynExpr.typeApp [ SynType.string ] (SynExpr.createIdent "ResizeArray"))
+ (SynExpr.CreateConst ()))
+ ]
|> SynBinding.basic
[ Ident.create "this" ; Ident.create "Assemble" ]
[
SynPat.annotateType (SynType.list SynType.string) (SynPat.named "positionals")
]
- |> SynBinding.withReturnAnnotation (SynType.createLongIdent [ record.Name ])
+ |> SynBinding.withReturnAnnotation (
+ SynType.app "Result" [ SynType.createLongIdent [ record.Name ] ; SynType.list SynType.string ]
+ )
|> SynMemberDefn.memberImplementation
|> List.singleton
|> Some
From 8535481e0ddb0ae809bbb57eda67a5bb2206b884 Mon Sep 17 00:00:00 2001
From: Smaug123 <3138005+Smaug123@users.noreply.github.com>
Date: Sun, 13 Apr 2025 21:09:12 +0100
Subject: [PATCH 04/26] Bump Whippet
---
WoofWare.Myriad.Plugins/ArgParserGenerator.fs | 2 +-
WoofWare.Myriad.Plugins/AstHelper.fs | 7 -------
WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs | 2 +-
WoofWare.Myriad.Plugins/JsonParseGenerator.fs | 2 +-
WoofWare.Myriad.Plugins/RemoveOptionsGenerator.fs | 2 +-
WoofWare.Myriad.Plugins/ShibaGenerator.fs | 2 +-
WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj | 2 +-
7 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/WoofWare.Myriad.Plugins/ArgParserGenerator.fs b/WoofWare.Myriad.Plugins/ArgParserGenerator.fs
index 0235b111..fb33747d 100644
--- a/WoofWare.Myriad.Plugins/ArgParserGenerator.fs
+++ b/WoofWare.Myriad.Plugins/ArgParserGenerator.fs
@@ -673,7 +673,7 @@ module internal ArgParserGenerator =
args
|> Map.toList
|> List.map (fun (ident, expr) -> SynLongIdent.create [ Ident.create ident ], expr)
- |> AstHelper.instantiateRecord
+ |> SynExpr.createRecord None
)
tree, counter
diff --git a/WoofWare.Myriad.Plugins/AstHelper.fs b/WoofWare.Myriad.Plugins/AstHelper.fs
index 79a14b8d..d1686722 100644
--- a/WoofWare.Myriad.Plugins/AstHelper.fs
+++ b/WoofWare.Myriad.Plugins/AstHelper.fs
@@ -36,13 +36,6 @@ module internal AstHelper =
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum _, _) -> true
| _ -> false
- let instantiateRecord (fields : (SynLongIdent * SynExpr) list) : SynExpr =
- let fields =
- fields
- |> List.map (fun (rfn, synExpr) -> SynExprRecordField ((rfn, true), Some range0, Some synExpr, None))
-
- SynExpr.Record (None, None, fields, range0)
-
let defineRecordType (record : RecordType) : SynTypeDefn =
let name =
SynComponentInfo.create record.Name
diff --git a/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs b/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs
index a1888285..8ccb6de9 100644
--- a/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs
+++ b/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs
@@ -89,7 +89,7 @@ module internal InterfaceMockGenerator =
[]
else
[ SynPat.unit ])
- (AstHelper.instantiateRecord constructorFields)
+ (SynExpr.createRecord None constructorFields)
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every method throws.")
|> SynBinding.withReturnAnnotation constructorReturnType
|> SynMemberDefn.staticMember
diff --git a/WoofWare.Myriad.Plugins/JsonParseGenerator.fs b/WoofWare.Myriad.Plugins/JsonParseGenerator.fs
index 6e3a57c8..173c5f88 100644
--- a/WoofWare.Myriad.Plugins/JsonParseGenerator.fs
+++ b/WoofWare.Myriad.Plugins/JsonParseGenerator.fs
@@ -480,7 +480,7 @@ module internal JsonParseGenerator =
let finalConstruction =
fields
|> List.mapi (fun i fieldData -> SynLongIdent.createI fieldData.Ident, SynExpr.createIdent $"arg_%i{i}")
- |> AstHelper.instantiateRecord
+ |> SynExpr.createRecord None
(finalConstruction, assignments)
||> List.fold (fun final assignment -> SynExpr.createLet [ assignment ] final)
diff --git a/WoofWare.Myriad.Plugins/RemoveOptionsGenerator.fs b/WoofWare.Myriad.Plugins/RemoveOptionsGenerator.fs
index 3ff25ab6..5039a012 100644
--- a/WoofWare.Myriad.Plugins/RemoveOptionsGenerator.fs
+++ b/WoofWare.Myriad.Plugins/RemoveOptionsGenerator.fs
@@ -97,7 +97,7 @@ module internal RemoveOptionsGenerator =
SynLongIdent.createI fieldData.Ident, body
)
- |> AstHelper.instantiateRecord
+ |> SynExpr.createRecord None
SynBinding.basic
[ functionName ]
diff --git a/WoofWare.Myriad.Plugins/ShibaGenerator.fs b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
index 00882739..bb0355a5 100644
--- a/WoofWare.Myriad.Plugins/ShibaGenerator.fs
+++ b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
@@ -512,7 +512,7 @@ module internal ShibaGenerator =
$"expected field in record %s{record.Name.idText} to have a name, but it did not"
| Some ident -> SynLongIdent.create [ ident ], SynExpr.createIdent $"arg%i{i}"
)
- |> AstHelper.instantiateRecord
+ |> SynExpr.createRecord None
|> SynExpr.applyFunction (SynExpr.createIdent "Ok")
let defaultOf =
diff --git a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
index 8aaf294d..da03107f 100644
--- a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
+++ b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
@@ -22,7 +22,7 @@
-
+
From 0c5ddf9df7e94f9f89500ab9776b125f3e180491 Mon Sep 17 00:00:00 2001
From: Smaug123 <3138005+Smaug123@users.noreply.github.com>
Date: Mon, 14 Apr 2025 00:01:55 +0100
Subject: [PATCH 05/26] WIP
---
ConsumePlugin/GeneratedArgs.fs | 106 +--
.../ArgParserAttributes.fs | 5 +
WoofWare.Myriad.Plugins/ArgParserGenerator.fs | 137 +--
WoofWare.Myriad.Plugins/ShibaGenerator.fs | 848 ++++++++++++++----
.../WoofWare.Myriad.Plugins.fsproj | 4 +-
5 files changed, 740 insertions(+), 360 deletions(-)
diff --git a/ConsumePlugin/GeneratedArgs.fs b/ConsumePlugin/GeneratedArgs.fs
index 18681c8b..2cfb5b68 100644
--- a/ConsumePlugin/GeneratedArgs.fs
+++ b/ConsumePlugin/GeneratedArgs.fs
@@ -20,10 +20,10 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed BasicNoPositionals.
type private BasicNoPositionals_InProgress =
{
- Foo : System.Int32 option
- Bar : System.String option
- Baz : System.Boolean option
- Rest : string list
+ mutable Foo : System.Int32 option
+ mutable Bar : System.String option
+ mutable Baz : System.Boolean option
+ mutable Rest : string list
}
member this.Assemble (positionals : string list) : Result =
@@ -66,9 +66,9 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed Basic.
type private Basic_InProgress =
{
- Foo : System.Int32 option
- Bar : System.String option
- Baz : System.Boolean option
+ mutable Foo : System.Int32 option
+ mutable Bar : System.String option
+ mutable Baz : System.Boolean option
}
member this.Assemble (positionals : string list) : Result =
@@ -112,9 +112,9 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed BasicWithIntPositionals.
type private BasicWithIntPositionals_InProgress =
{
- Foo : System.Int32 option
- Bar : System.String option
- Baz : System.Boolean option
+ mutable Foo : System.Int32 option
+ mutable Bar : System.String option
+ mutable Baz : System.Boolean option
}
member this.Assemble (positionals : string list) : Result =
@@ -158,16 +158,16 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed LoadsOfTypes.
type private LoadsOfTypes_InProgress =
{
- Foo : System.Int32 option
- Bar : System.String option
- Baz : System.Boolean option
- SomeFile : FileInfo option
- SomeDirectory : DirectoryInfo option
- SomeList : string list
- OptionalThingWithNoDefault : int option
- OptionalThing : bool option
- AnotherOptionalThing : int option
- YetAnotherOptionalThing : string option
+ mutable Foo : System.Int32 option
+ mutable Bar : System.String option
+ mutable Baz : System.Boolean option
+ mutable SomeFile : FileInfo option
+ mutable SomeDirectory : DirectoryInfo option
+ mutable SomeList : string list
+ mutable OptionalThingWithNoDefault : int option
+ mutable OptionalThing : bool option
+ mutable AnotherOptionalThing : int option
+ mutable YetAnotherOptionalThing : string option
}
member this.Assemble (positionals : string list) : Result =
@@ -249,16 +249,16 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed LoadsOfTypesNoPositionals.
type private LoadsOfTypesNoPositionals_InProgress =
{
- Foo : System.Int32 option
- Bar : System.String option
- Baz : System.Boolean option
- SomeFile : FileInfo option
- SomeDirectory : DirectoryInfo option
- SomeList : string list
- OptionalThingWithNoDefault : int option
- OptionalThing : bool option
- AnotherOptionalThing : int option
- YetAnotherOptionalThing : string option
+ mutable Foo : System.Int32 option
+ mutable Bar : System.String option
+ mutable Baz : System.Boolean option
+ mutable SomeFile : FileInfo option
+ mutable SomeDirectory : DirectoryInfo option
+ mutable SomeList : string list
+ mutable OptionalThingWithNoDefault : int option
+ mutable OptionalThing : bool option
+ mutable AnotherOptionalThing : int option
+ mutable YetAnotherOptionalThing : string option
}
member this.Assemble (positionals : string list) : Result =
@@ -337,10 +337,10 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed DatesAndTimes.
type private DatesAndTimes_InProgress =
{
- Plain : TimeSpan option
- Invariant : TimeSpan option
- Exact : TimeSpan option
- InvariantExact : TimeSpan option
+ mutable Plain : TimeSpan option
+ mutable Invariant : TimeSpan option
+ mutable Exact : TimeSpan option
+ mutable InvariantExact : TimeSpan option
}
member this.Assemble (positionals : string list) : Result =
@@ -388,8 +388,8 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ChildRecord.
type private ChildRecord_InProgress =
{
- Thing1 : System.Int32 option
- Thing2 : System.String option
+ mutable Thing1 : System.Int32 option
+ mutable Thing2 : System.String option
}
member this.Assemble (positionals : string list) : Result =
@@ -421,8 +421,8 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ParentRecord.
type private ParentRecord_InProgress =
{
- Child : ChildRecord_InProgress
- AndAnother : System.Boolean option
+ mutable Child : ChildRecord_InProgress
+ mutable AndAnother : System.Boolean option
}
member this.Assemble (positionals : string list) : Result =
@@ -454,7 +454,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ChildRecordWithPositional.
type private ChildRecordWithPositional_InProgress =
{
- Thing1 : System.Int32 option
+ mutable Thing1 : System.Int32 option
}
member this.Assemble (positionals : string list) : Result =
@@ -482,8 +482,8 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ParentRecordChildPos.
type private ParentRecordChildPos_InProgress =
{
- Child : ChildRecordWithPositional_InProgress
- AndAnother : System.Boolean option
+ mutable Child : ChildRecordWithPositional_InProgress
+ mutable AndAnother : System.Boolean option
}
member this.Assemble (positionals : string list) : Result =
@@ -515,7 +515,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ParentRecordSelfPos.
type private ParentRecordSelfPos_InProgress =
{
- Child : ChildRecord_InProgress
+ mutable Child : ChildRecord_InProgress
}
member this.Assemble (positionals : string list) : Result =
@@ -562,7 +562,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ContainsBoolEnvVar.
type private ContainsBoolEnvVar_InProgress =
{
- BoolVar : bool option
+ mutable BoolVar : bool option
}
member this.Assemble (positionals : string list) : Result =
@@ -584,7 +584,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed WithFlagDu.
type private WithFlagDu_InProgress =
{
- DryRun : DryRunMode option
+ mutable DryRun : DryRunMode option
}
member this.Assemble (positionals : string list) : Result =
@@ -608,7 +608,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ContainsFlagEnvVar.
type private ContainsFlagEnvVar_InProgress =
{
- DryRun : DryRunMode option
+ mutable DryRun : DryRunMode option
}
member this.Assemble (positionals : string list) : Result =
@@ -630,7 +630,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ContainsFlagDefaultValue.
type private ContainsFlagDefaultValue_InProgress =
{
- DryRun : DryRunMode option
+ mutable DryRun : DryRunMode option
}
member this.Assemble (positionals : string list) : Result =
@@ -652,8 +652,8 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed ManyLongForms.
type private ManyLongForms_InProgress =
{
- DoTheThing : System.String option
- SomeFlag : System.Boolean option
+ mutable DoTheThing : System.String option
+ mutable SomeFlag : System.Boolean option
}
member this.Assemble (positionals : string list) : Result =
@@ -685,7 +685,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed FlagsIntoPositionalArgs.
type private FlagsIntoPositionalArgs_InProgress =
{
- A : System.String option
+ mutable A : System.String option
}
member this.Assemble (positionals : string list) : Result =
@@ -713,7 +713,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed FlagsIntoPositionalArgsChoice.
type private FlagsIntoPositionalArgsChoice_InProgress =
{
- A : System.String option
+ mutable A : System.String option
}
member this.Assemble (positionals : string list) : Result =
@@ -741,7 +741,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed FlagsIntoPositionalArgsInt.
type private FlagsIntoPositionalArgsInt_InProgress =
{
- A : System.String option
+ mutable A : System.String option
}
member this.Assemble (positionals : string list) : Result =
@@ -769,7 +769,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed FlagsIntoPositionalArgsIntChoice.
type private FlagsIntoPositionalArgsIntChoice_InProgress =
{
- A : System.String option
+ mutable A : System.String option
}
member this.Assemble (positionals : string list) : Result =
@@ -797,7 +797,7 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed FlagsIntoPositionalArgs'.
type private FlagsIntoPositionalArgs'_InProgress =
{
- A : System.String option
+ mutable A : System.String option
}
member this.Assemble (positionals : string list) : Result =
diff --git a/WoofWare.Myriad.Plugins.Attributes/ArgParserAttributes.fs b/WoofWare.Myriad.Plugins.Attributes/ArgParserAttributes.fs
index f9b34c25..1ca3e63a 100644
--- a/WoofWare.Myriad.Plugins.Attributes/ArgParserAttributes.fs
+++ b/WoofWare.Myriad.Plugins.Attributes/ArgParserAttributes.fs
@@ -26,6 +26,11 @@ type ArgParserAttribute (isExtensionMethod : bool) =
/// an argument which looks like a flag but which we don't recognise.)
/// We will still interpret `--help` as requesting help, unless it comes after
/// a standalone `--` separator.
+///
+/// If the type of the PositionalArgs field is `Choice<'a, 'a>`, then we will
+/// tell you whether each arg came before or after a standalone `--` separator.
+/// For example, `MyApp foo bar -- baz` with PositionalArgs of `Choice`
+/// would yield `Choice1Of2 foo, Choice1Of2 bar, Choice2Of2 baz`.
type PositionalArgsAttribute (includeFlagLike : bool) =
inherit Attribute ()
diff --git a/WoofWare.Myriad.Plugins/ArgParserGenerator.fs b/WoofWare.Myriad.Plugins/ArgParserGenerator.fs
index fb33747d..dd075d0f 100644
--- a/WoofWare.Myriad.Plugins/ArgParserGenerator.fs
+++ b/WoofWare.Myriad.Plugins/ArgParserGenerator.fs
@@ -7,82 +7,6 @@ open Fantomas.FCS.Text.Range
open TypeEquality
open WoofWare.Whippet.Fantomas
-type internal ArgParserOutputSpec =
- {
- ExtensionMethods : bool
- }
-
-type internal FlagDu =
- {
- Name : Ident
- Case1Name : Ident
- Case2Name : Ident
- /// Hopefully this is simply the const bool True or False, but it might e.g. be a literal
- Case1Arg : SynExpr
- /// Hopefully this is simply the const bool True or False, but it might e.g. be a literal
- Case2Arg : SynExpr
- }
-
- static member FromBoolean (flagDu : FlagDu) (value : SynExpr) =
- SynExpr.ifThenElse
- (SynExpr.equals value flagDu.Case1Arg)
- (SynExpr.createLongIdent' [ flagDu.Name ; flagDu.Case2Name ])
- (SynExpr.createLongIdent' [ flagDu.Name ; flagDu.Case1Name ])
-
-/// The default value of an argument which admits default values can be pulled from different sources.
-/// This defines which source a particular default value comes from.
-type private ArgumentDefaultSpec =
- /// From parsing the environment variable with the given name (e.g. "WOOFWARE_DISABLE_FOO" or whatever).
- | EnvironmentVariable of name : SynExpr
- /// From calling the static member `{typeWeParseInto}.Default{name}()`
- /// For example, if `type MyArgs = { Thing : Choice }`, then
- /// we would use `MyArgs.DefaultThing () : int`.
- ///
- | FunctionCall of name : Ident
-
-type private Accumulation<'choice> =
- | Required
- | Optional
- | Choice of 'choice
- | List of Accumulation<'choice>
-
-type private ParseFunction<'acc> =
- {
- FieldName : Ident
- TargetVariable : Ident
- /// Any of the forms in this set are acceptable, but make sure they all start with a dash, or we might
- /// get confused with positional args or something! I haven't thought that hard about this.
- /// In the default case, this is `Const("arg-name")` for the `ArgName : blah` field; note that we have
- /// omitted the initial `--` that will be required at runtime.
- ArgForm : SynExpr list
- /// If this is a boolean-like field (e.g. a bool or a flag DU), the help text should look a bit different:
- /// we should lie to the user about the value of the cases there.
- /// Similarly, if we're reading from an environment variable with the laxer parsing rules of accepting e.g.
- /// "0" instead of "false", we need to know if we're reading a bool.
- /// In that case, `boolCases` is Some, and contains the construction of the flag (or boolean, in which case
- /// you get no data).
- BoolCases : Choice option
- Help : SynExpr option
- /// A function string -> %TargetType%, where TargetVariable is probably a `%TargetType% option`.
- /// (Depending on `Accumulation`, we'll remove the `option` at the end of the parse, asserting that the
- /// argument was supplied.)
- /// This is allowed to throw if it fails to parse.
- Parser : SynExpr
- /// If `Accumulation` is `List`, then this is the type of the list *element*; analogously for optionals
- /// and choices and so on.
- TargetType : SynType
- Accumulation : 'acc
- }
-
- /// A SynExpr of type `string` which we can display to the user at generated-program runtime to display all
- /// the ways they can refer to this arg.
- member arg.HumanReadableArgForm : SynExpr =
- let formatString = List.replicate arg.ArgForm.Length "--%s" |> String.concat " / "
-
- (SynExpr.applyFunction (SynExpr.createIdent "sprintf") (SynExpr.CreateConst formatString), arg.ArgForm)
- ||> List.fold SynExpr.applyFunction
- |> SynExpr.paren
-
[]
type private ChoicePositional =
| Normal of includeFlagLike : SynExpr option
@@ -114,14 +38,14 @@ type private ParseTree<'hasPositional> =
/// `assemble` takes the SynExpr's (e.g. each record field contents) corresponding to each `Ident` in
/// the branch (e.g. each record field name),
/// and composes them into a `SynExpr` (e.g. the record-typed object).
- | Branch of
+ | DescendRecord of
fields : (Ident * ParseTree) list *
assemble : (Map -> SynExpr) *
Teq<'hasPositional, HasNoPositional>
/// `assemble` takes the SynExpr's (e.g. each record field contents) corresponding to each `Ident` in
/// the branch (e.g. each record field name),
/// and composes them into a `SynExpr` (e.g. the record-typed object).
- | BranchPos of
+ | DescendRecordPos of
posField : Ident *
fields : ParseTree *
(Ident * ParseTree) list *
@@ -184,63 +108,6 @@ module private ParseTree =
go None ([], None) subs
- let rec accumulatorsNonPos (tree : ParseTree) : ParseFunctionNonPositional list =
- match tree with
- | ParseTree.PositionalLeaf (_, teq) -> exFalso teq
- | ParseTree.BranchPos (_, _, _, _, teq) -> exFalso teq
- | ParseTree.NonPositionalLeaf (pf, _) -> [ pf ]
- | ParseTree.Branch (trees, _, _) -> trees |> List.collect (snd >> accumulatorsNonPos)
-
- /// Returns the positional arg separately.
- let rec accumulatorsPos
- (tree : ParseTree)
- : ParseFunctionNonPositional list * ParseFunctionPositional
- =
- match tree with
- | ParseTree.PositionalLeaf (pf, _) -> [], pf
- | ParseTree.NonPositionalLeaf (_, teq) -> exFalso' teq
- | ParseTree.Branch (_, _, teq) -> exFalso' teq
- | ParseTree.BranchPos (_, tree, trees, _, _) ->
- let nonPos = trees |> List.collect (snd >> accumulatorsNonPos)
-
- let nonPos2, pos = accumulatorsPos tree
- nonPos @ nonPos2, pos
-
- /// Collect all the ParseFunctions which are necessary to define variables, throwing away
- /// all information relevant to composing the resulting variables into records.
- /// Returns the list of non-positional parsers, and any positional parser that exists.
- let accumulators<'a> (tree : ParseTree<'a>) : ParseFunctionNonPositional list * ParseFunctionPositional option =
- // Sad duplication of some code here, but it was the easiest way to make it type-safe :(
- match tree with
- | ParseTree.PositionalLeaf (pf, _) -> [], Some pf
- | ParseTree.NonPositionalLeaf (pf, _) -> [ pf ], None
- | ParseTree.Branch (trees, _, _) -> trees |> List.collect (snd >> accumulatorsNonPos) |> (fun i -> i, None)
- | ParseTree.BranchPos (_, tree, trees, _, _) ->
- let nonPos = trees |> List.collect (snd >> accumulatorsNonPos)
-
- let nonPos2, pos = accumulatorsPos tree
- nonPos @ nonPos2, Some pos
-
- |> fun (nonPos, pos) ->
- let duplicateArgs =
- // This is best-effort. We can't necessarily detect all SynExprs here, but usually it'll be strings.
- Option.toList (pos |> Option.map _.ArgForm) @ (nonPos |> List.map _.ArgForm)
- |> Seq.concat
- |> Seq.choose (fun expr ->
- match expr |> SynExpr.stripOptionalParen with
- | SynExpr.Const (SynConst.String (s, _, _), _) -> Some s
- | _ -> None
- )
- |> List.ofSeq
- |> List.groupBy id
- |> List.choose (fun (key, v) -> if v.Length > 1 then Some key else None)
-
- match duplicateArgs with
- | [] -> nonPos, pos
- | dups ->
- let dups = dups |> String.concat " "
- failwith $"Duplicate args detected! %s{dups}"
-
/// Build the return value.
let rec instantiate<'a> (tree : ParseTree<'a>) : SynExpr =
match tree with
diff --git a/WoofWare.Myriad.Plugins/ShibaGenerator.fs b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
index bb0355a5..521c1dc5 100644
--- a/WoofWare.Myriad.Plugins/ShibaGenerator.fs
+++ b/WoofWare.Myriad.Plugins/ShibaGenerator.fs
@@ -1,10 +1,12 @@
namespace WoofWare.Myriad.Plugins
open System
+open System.Collections.Generic
open System.Text
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
open TypeEquality
+open WoofWare.Myriad.Plugins
open WoofWare.Whippet.Fantomas
type internal ArgParserOutputSpec =
@@ -31,19 +33,19 @@ type internal FlagDu =
/// The default value of an argument which admits default values can be pulled from different sources.
/// This defines which source a particular default value comes from.
-type private ArgumentDefaultSpec =
+type internal ArgumentDefaultSpec =
/// From parsing the environment variable with the given name (e.g. "WOOFWARE_DISABLE_FOO" or whatever).
| EnvironmentVariable of name : SynExpr
/// From calling the static member `{typeWeParseInto}.Default{name}()`
/// For example, if `type MyArgs = { Thing : Choice }`, then
/// we would use `MyArgs.DefaultThing () : int`.
- ///
| FunctionCall of name : Ident
-type private Accumulation<'choice> =
+type internal Accumulation<'choice> =
| Required
| Optional
| Choice of 'choice
+ | ChoicePositional of attrContents : SynExpr option
| List of Accumulation<'choice>
type private ParseFunction<'acc> =
@@ -85,8 +87,7 @@ type private ParseFunction<'acc> =
module internal ShibaGenerator =
- open SynTypePatterns
-
+ //let log (s : string) = System.IO.File.AppendAllText ("/tmp/myriad.log", s + "\n")
type RecognisedType =
| Union of UnionType
| Record of RecordType
@@ -107,14 +108,40 @@ module internal ShibaGenerator =
)
| _ -> None
+ /// Convert e.g. "Foo" into "--foo".
+ let argify (ident : Ident) : string =
+ let result = StringBuilder ()
+
+ for c in ident.idText do
+ if Char.IsUpper c then
+ result.Append('-').Append (Char.ToLowerInvariant c) |> ignore
+ else
+ result.Append c |> ignore
+
+ result.ToString().TrimStart '-'
+
+ type LeafData<'choice> =
+ {
+ /// Call this function to turn the input into the `TypeAfterParse`.
+ /// For example, `--foo=3` would have TypeAfterParse of `int`, and
+ /// `ParseFn` would be a function `string -> int`.
+ ParseFn : SynExpr
+ /// The type of this field, as it will appear in the final user's record.
+ TypeAfterParse : SynType
+ /// Essentially, how many times this leaf is expected to appear.
+ Acc : Accumulation<'choice>
+ /// `None` if not positional. `Some None` if positional and the PositionalArgs attribute had no contents.
+ /// `Some Some` if the PositionalArgs attribute had an argument.
+ Positional : SynExpr option option
+ }
+
type private ParseFunctionSpec<'choice> =
- /// A leaf node, e.g. `--foo=3`. Call the `parseFn` to turn the input `"3"` into the `typeAfterParse` (here, `int`).
- /// `Accumulation` represents essentially how many times this leaf is expected to be called.
- | Leaf of parseFn : SynExpr * acc : Accumulation<'choice> * typeAfterParse : SynType
+ /// A leaf node, e.g. `--foo=3`.
+ | Leaf of LeafData<'choice>
/// An opaque node we didn't recognise: e.g. `Foo : SomeType`.
/// We're probably going to stamp out an "in-progress" type for this node.
/// (Either that, or it's just a type we don't recognise, and then compilation will fail.)
- | UserDefined
+ | UserDefined of isRecord : bool * typeName : Ident
/// An optional opaque node we didn't recognise: e.g. `Foo : SomeType option`.
/// We're probably going to stamp out an "in-progress" type for this node.
/// (Either that, or it's just a type we don't recognise, and then compilation will fail.)
@@ -127,36 +154,58 @@ module internal ShibaGenerator =
let rec private createParseFunction<'choice>
(choice : ArgumentDefaultSpec option -> 'choice)
(flagDus : FlagDu list)
+ (userDefinedRecordTypesWithParser : IEnumerable)
+ (userDefinedUnionTypesWithParser : IEnumerable)
(fieldName : Ident)
(attrs : SynAttribute list)
(ty : SynType)
: ParseFunctionSpec<'choice>
=
+ let positional =
+ attrs
+ |> List.tryPick (fun a ->
+ match (List.last a.TypeName.LongIdent).idText with
+ | "PositionalArgsAttribute"
+ | "PositionalArgs" ->
+ match a.ArgExpr with
+ | SynExpr.Const (SynConst.Unit, _) -> Some None
+ | a -> Some (Some a)
+ | _ -> None
+ )
+
match ty with
| String ->
- ParseFunctionSpec.Leaf (
- SynExpr.createLambda "x" (SynExpr.createIdent "x"),
- Accumulation.Required,
- SynType.string
- )
+ {
+ ParseFn = SynExpr.createLambda "x" (SynExpr.createIdent "x")
+ Acc = Accumulation.Required
+ TypeAfterParse = SynType.string
+ Positional = positional
+ }
+ |> ParseFunctionSpec.Leaf
| PrimitiveType pt ->
- ParseFunctionSpec.Leaf (
- SynExpr.createLambda
- "x"
- (SynExpr.applyFunction
- (SynExpr.createLongIdent' (pt @ [ Ident.create "Parse" ]))
- (SynExpr.createIdent "x")),
- Accumulation.Required,
- ty
- )
+ {
+ ParseFn =
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction
+ (SynExpr.createLongIdent' (pt @ [ Ident.create "Parse" ]))
+ (SynExpr.createIdent "x"))
+ Acc = Accumulation.Required
+ TypeAfterParse = ty
+ Positional = positional
+ }
+ |> ParseFunctionSpec.Leaf
| Uri ->
- ParseFunctionSpec.Leaf (
- SynExpr.createLambda
- "x"
- (SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Uri" ]) (SynExpr.createIdent "x")),
- Accumulation.Required,
- ty
- )
+ {
+ ParseFn =
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Uri" ]) (SynExpr.createIdent "x"))
+ Acc = Accumulation.Required
+ TypeAfterParse = ty
+ Positional = positional
+ }
+ |> ParseFunctionSpec.Leaf
| TimeSpan ->
let parseExact =
attrs
@@ -210,41 +259,67 @@ module internal ShibaGenerator =
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "TimeSpan" ; "ParseExact" ])
|> SynExpr.createLambda "x"
- ParseFunctionSpec.Leaf (parser, Accumulation.Required, ty)
+ {
+ ParseFn = parser
+ Acc = Accumulation.Required
+ TypeAfterParse = ty
+ Positional = positional
+ }
+ |> ParseFunctionSpec.Leaf
| FileInfo ->
- ParseFunctionSpec.Leaf (
- SynExpr.createLambda
- "x"
- (SynExpr.applyFunction
- (SynExpr.createLongIdent [ "System" ; "IO" ; "FileInfo" ])
- (SynExpr.createIdent "x")),
- Accumulation.Required,
- ty
- )
+ {
+ ParseFn =
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction
+ (SynExpr.createLongIdent [ "System" ; "IO" ; "FileInfo" ])
+ (SynExpr.createIdent "x"))
+ Acc = Accumulation.Required
+ TypeAfterParse = ty
+ Positional = positional
+ }
+ |> ParseFunctionSpec.Leaf
| DirectoryInfo ->
- ParseFunctionSpec.Leaf (
- SynExpr.createLambda
- "x"
- (SynExpr.applyFunction
- (SynExpr.createLongIdent [ "System" ; "IO" ; "DirectoryInfo" ])
- (SynExpr.createIdent "x")),
- Accumulation.Required,
- ty
- )
+ {
+ ParseFn =
+ SynExpr.createLambda
+ "x"
+ (SynExpr.applyFunction
+ (SynExpr.createLongIdent [ "System" ; "IO" ; "DirectoryInfo" ])
+ (SynExpr.createIdent "x"))
+ Acc = Accumulation.Required
+ TypeAfterParse = ty
+ Positional = positional
+ }
+ |> ParseFunctionSpec.Leaf
| OptionType eltTy ->
- match createParseFunction choice flagDus fieldName attrs eltTy with
- | ParseFunctionSpec.Leaf (parseElt, acc, childTy) ->
- match acc with
+ match
+ createParseFunction
+ choice
+ flagDus
+ userDefinedRecordTypesWithParser
+ userDefinedUnionTypesWithParser
+ fieldName
+ attrs
+ eltTy
+ with
+ | ParseFunctionSpec.Leaf data ->
+ match data.Acc with
| Accumulation.Optional ->
failwith
$"ArgParser does not support optionals containing options at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.ChoicePositional _
| Accumulation.Choice _ ->
failwith
$"ArgParser does not support optionals containing choices at field %s{fieldName.idText}: %O{ty}"
| Accumulation.List _ ->
failwith $"ArgParser does not support optional lists at field %s{fieldName.idText}: %O{ty}"
- | Accumulation.Required -> ParseFunctionSpec.Leaf (parseElt, Accumulation.Optional, childTy)
- | ParseFunctionSpec.UserDefined -> ParseFunctionSpec.OptionOfUserDefined
+ | Accumulation.Required ->
+ ParseFunctionSpec.Leaf
+ { data with
+ Acc = Accumulation.Optional
+ }
+ | ParseFunctionSpec.UserDefined _ -> ParseFunctionSpec.OptionOfUserDefined
| ParseFunctionSpec.OptionOfUserDefined ->
failwith $"ArgParser does not support lists of options at field %s{fieldName.idText}"
| ChoiceType elts ->
@@ -254,15 +329,25 @@ module internal ShibaGenerator =
failwith
$"ArgParser was unable to prove types %O{elt1} and %O{elt2} to be equal in a Choice. We require them to be equal."
- match createParseFunction choice flagDus fieldName attrs elt1 with
- | ParseFunctionSpec.Leaf (parseElt, acc, childTy) ->
- match acc with
+ match
+ createParseFunction
+ choice
+ flagDus
+ userDefinedRecordTypesWithParser
+ userDefinedUnionTypesWithParser
+ fieldName
+ attrs
+ elt1
+ with
+ | ParseFunctionSpec.Leaf data ->
+ match data.Acc with
| Accumulation.Optional ->
failwith
$"ArgParser does not support choices containing options at field %s{fieldName.idText}: %O{ty}"
| Accumulation.List _ ->
failwith
$"ArgParser does not support choices containing lists at field %s{fieldName.idText}: %O{ty}"
+ | Accumulation.ChoicePositional _
| Accumulation.Choice _ ->
failwith
$"ArgParser does not support choices containing choices at field %s{fieldName.idText}: %O{ty}"
@@ -305,7 +390,17 @@ module internal ShibaGenerator =
failwith
$"Expected Choice to be annotated with at most one ArgumentDefaultFunction or similar, but it was annotated with multiple. Field: %s{fieldName.idText}"
- ParseFunctionSpec.Leaf (parseElt, Accumulation.Choice (choice relevantAttr), childTy)
+ match positional with
+ | Some positional ->
+ ParseFunctionSpec.Leaf
+ { data with
+ Acc = Accumulation.ChoicePositional positional
+ }
+ | None ->
+ ParseFunctionSpec.Leaf
+ { data with
+ Acc = Accumulation.Choice (choice relevantAttr)
+ }
| _ ->
failwith
$"Choices are only allowed to contain leaves; at %s{fieldName.idText}, got type %s{SynType.toHumanReadableString elt1}"
@@ -315,15 +410,41 @@ module internal ShibaGenerator =
failwith
$"ArgParser requires Choice to be of the form Choice<'a, 'a>; that is, two arguments, both the same. For field %s{fieldName.idText}, got: %s{elts}"
| ListType eltTy ->
- match createParseFunction choice flagDus fieldName attrs eltTy with
- | ParseFunctionSpec.Leaf (parseElt, acc, childTy) ->
- ParseFunctionSpec.Leaf (parseElt, Accumulation.List acc, childTy)
+ match
+ createParseFunction
+ choice
+ flagDus
+ userDefinedRecordTypesWithParser
+ userDefinedUnionTypesWithParser
+ fieldName
+ attrs
+ eltTy
+ with
+ | ParseFunctionSpec.Leaf data ->
+ ParseFunctionSpec.Leaf
+ { data with
+ Acc = Accumulation.List data.Acc
+ }
| _ ->
failwith
$"Lists are only allowed to contain leaves; at %s{fieldName.idText}, got type %s{SynType.toHumanReadableString eltTy}"
| ty ->
match identifyAsFlag flagDus ty with
- | None -> ParseFunctionSpec.UserDefined
+ | None ->
+ match ty with
+ | SynType.LongIdent (SynLongIdent.SynLongIdent (id = id)) ->
+ let typeName = List.last id
+
+ if Seq.contains typeName.idText userDefinedRecordTypesWithParser then
+ ParseFunctionSpec.UserDefined (true, typeName)
+ elif Seq.contains (List.last id).idText userDefinedUnionTypesWithParser then
+ ParseFunctionSpec.UserDefined (false, typeName)
+ else
+ failwith
+ $"we did not recognise the type %s{SynType.toHumanReadableString ty} as something we could build a parser for"
+ | _ ->
+ failwith
+ $"we did not recognise the type %s{SynType.toHumanReadableString ty} as something we could build a parser for"
| Some flagDu ->
// Parse as a bool, and then do the `if-then` dance.
let parser =
@@ -332,12 +453,473 @@ module internal ShibaGenerator =
|> FlagDu.FromBoolean flagDu
|> SynExpr.createLambda "x"
- ParseFunctionSpec.Leaf (parser, Accumulation.Required, ty)
+ {
+ ParseFn = parser
+ Acc = Accumulation.Required
+ TypeAfterParse = ty
+ Positional = positional
+ }
+ |> ParseFunctionSpec.Leaf
+
+ type internal DatalessUnion =
+ {
+ Cases : (string * SynAttribute list) list
+ }
+
+ type internal ParsedRecordStructure<'choice> =
+ {
+ Original : RecordType
+ /// Map of field name to parser for that field
+ LeafNodes : Map>
+ Records : Map>
+ Unions : Map>
+ }
+
+ and internal ParsedUnionStructure<'choice> =
+ {
+ Original : UnionType
+ Cases : Map>
+ }
+
+ /// Build the "in-progress record" which is basically "the input record, but with all fields mutable and optional".
+ let private inProgressRecordType (record : ParsedRecordStructure<'choice>) : RecordType =
+ let leafFields =
+ record.LeafNodes
+ |> Map.toSeq
+ |> Seq.map (fun (ident, data) -> failwith "TODO")
+ |> Seq.toList
+
+ let unionFields =
+ record.Unions
+ |> Map.toSeq
+ |> Seq.map (fun (ident, data) -> failwith "TODO")
+ |> Seq.toList
+
+ let recordFields =
+ record.Records
+ |> Map.toSeq
+ |> Seq.map (fun (ident, data) -> failwith "TODO")
+ |> Seq.toList
+
+ let fields =
+ leafFields @ unionFields @ recordFields
+ |> fun l ->
+ if l.IsEmpty then
+ {
+ Attrs = []
+ Ident = Some (Ident.create "_Dummy")
+ Type = SynType.unit
+ }
+ |> SynField.make
+ |> List.singleton
+ else
+ l |> List.map (SynField.withMutability true)
+
+ let members =
+ // for each field `FieldName` in order, we've made a variable `arg%i`
+ // which has done the optionality check
+ let instantiation =
+ record.Original.Fields
+ |> List.mapi (fun i (SynField.SynField (idOpt = ident)) ->
+ match ident with
+ | None ->
+ failwith
+ $"expected field in record %s{record.Original.Name.idText} to have a name, but it did not"
+ | Some ident -> SynLongIdent.create [ ident ], SynExpr.createIdent $"arg%i{i}"
+ )
+ |> SynExpr.createRecord None
+ |> SynExpr.applyFunction (SynExpr.createIdent "Ok")
+
+ let defaultOf =
+ SynExpr.typeApp [ SynType.anon ] (SynExpr.createLongIdent [ "Unchecked" ; "defaultof" ])
+
+ let assignVariables =
+ record.Original.Fields
+ |> List.mapi (fun i f -> (i, f))
+ |> List.collect (fun (i, SynField.SynField (attributes = attrs ; fieldType = ty ; idOpt = ident)) ->
+ match ident with
+ | None ->
+ failwith
+ $"expected field in record %s{record.Original.Name.idText} to have a name, but it did not"
+ | Some ident ->
+
+ let valueForThisVar =
+ match record.Records |> Map.tryFind ident.idText with
+ | Some subRecord ->
+ // This was a record; defer to its parser.
+ // TODO: need to know if it has positionals
+ [
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Ok" ]
+ (SynArgPats.create [ SynPat.named "result" ]))
+ (SynExpr.createIdent "result")
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Error" ]
+ (SynArgPats.create [ SynPat.named "err" ]))
+ (SynExpr.sequential
+ [
+ SynExpr.callMethodArg
+ "AddRange"
+ (SynExpr.createIdent "errors")
+ (SynExpr.createIdent "err")
+ defaultOf
+ ])
+ ]
+ |> SynExpr.createMatch (SynExpr.dotGet ident.idText (SynExpr.createIdent "this"))
+ | None ->
+
+ match record.Unions |> Map.tryFind ident.idText with
+ | Some union ->
+ // This was a union; defer to its parser.
+ failwith "TODO"
+ | None ->
+
+ match record.LeafNodes |> Map.tryFind ident.idText with
+ | Some leaf ->
+ match leaf.Positional with
+ | Some pos ->
+ // Positional args carried in from external argument.
+ // TODO: register whether they came before or after separator
+ SynExpr.createIdent "positionals"
+ | None ->
+
+ let extract =
+ match leaf.TypeAfterParse with
+ | ChoiceType [ _ ; _ ] ->
+ [
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Some" ]
+ (SynArgPats.create [ SynPat.named "result" ]))
+ (SynExpr.applyFunction
+ (SynExpr.createIdent "Choice1Of2")
+ (SynExpr.createIdent "result"))
+ SynMatchClause.create
+ (SynPat.identWithArgs [ Ident.create "None" ] (SynArgPats.create []))
+ (SynExpr.CreateConst "TODO"
+ |> SynExpr.applyFunction (SynExpr.createIdent "Choice2Of2"))
+ ]
+ |> SynExpr.createMatch (SynExpr.dotGet ident.idText (SynExpr.createIdent "this"))
+ | ListType _ -> SynExpr.dotGet ident.idText (SynExpr.createIdent "this")
+ | PrimitiveType _ ->
+ [
+ SynMatchClause.create
+ (SynPat.identWithArgs
+ [ Ident.create "Some" ]
+ (SynArgPats.create [ SynPat.named "result" ]))
+ (SynExpr.createIdent "result")
+ SynMatchClause.create
+ (SynPat.identWithArgs [ Ident.create "None" ] (SynArgPats.create []))
+ (SynExpr.sequential
+ [
+ SynExpr.callMethodArg
+ "Add"
+ (SynExpr.CreateConst $"no value provided for %s{ident.idText}")
+ (SynExpr.createIdent "errors")
+ defaultOf
+ ])
+ ]
+ |> SynExpr.createMatch (SynExpr.dotGet ident.idText (SynExpr.createIdent "this"))
+ | OptionType _ -> SynExpr.dotGet ident.idText (SynExpr.createIdent "this")
+ | ty -> failwith $"Could not convert type %s{SynType.toHumanReadableString ty}"
+
+ extract
+ | None ->
+ failwith
+ $"somehow we never classified the field %s{ident.idText} of %s{record.Original.Name.idText}"
+
+ valueForThisVar
+ |> SynBinding.basic [ Ident.create $"arg%i{i}" ] []
+ |> SynBinding.withReturnAnnotation ty
+ |> List.singleton
+ )
+
+ SynExpr.ifThenElse
+ (SynExpr.equals (SynExpr.dotGet "Count" (SynExpr.createIdent "errors")) (SynExpr.CreateConst 0))
+ (SynExpr.createIdent "errors"
+ |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "Seq" ; "toList" ])
+ |> SynExpr.pipeThroughFunction (SynExpr.createIdent "Error"))
+ instantiation
+ |> SynExpr.createLet assignVariables
+ |> SynExpr.createLet
+ [
+ SynBinding.basic
+ [ Ident.create "errors" ]
+ []
+ (SynExpr.applyFunction
+ (SynExpr.typeApp [ SynType.string ] (SynExpr.createIdent "ResizeArray"))
+ (SynExpr.CreateConst ()))
+ ]
+ |> SynBinding.basic
+ [ Ident.create "this" ; Ident.create "Assemble" ]
+ [
+ SynPat.annotateType (SynType.list SynType.string) (SynPat.named "positionals")
+ ]
+ |> SynBinding.withReturnAnnotation (
+ SynType.app
+ "Result"
+ [
+ SynType.createLongIdent [ record.Original.Name ]
+ SynType.list SynType.string
+ ]
+ )
+
+ {
+ Name = record.Original.Name.idText + "_InProgress" |> Ident.create
+ Fields = fields
+ Members = members |> SynMemberDefn.memberImplementation |> List.singleton |> Some
+ XmlDoc = PreXmlDoc.create $"A partially-parsed %s{record.Original.Name.idText}." |> Some
+ Generics =
+ match record.Original.Generics with
+ | None -> None
+ | Some _ ->
+ failwith $"Record type %s{record.Original.Name.idText} had generics, which we don't support."
+ TypeAccessibility = Some (SynAccess.Private range0)
+ ImplAccessibility = None
+ Attributes = []
+ }
+
+ type internal AllInfo =
+ {
+ /// Map of identifier to parser
+ RecordParsers : IReadOnlyDictionary>
+ /// Map of identifier to parser
+ UnionParsers : IReadOnlyDictionary>
+ /// Map of identifier to DU information
+ FlagDus : Map
+ /// Map of identifier to DU information
+ DatalessUnions : Map
+ }
+
+ /// Returns None if we haven't yet obtained parse structures for the dependencies of this record.
+ let private parseRecord
+ (knownRecordParserTypes : IReadOnlyDictionary>)
+ (knownUnionParserTypes : IReadOnlyDictionary>)
+ (flagDus : FlagDu list)
+ (rt : RecordType)
+ : ParsedRecordStructure option
+ =
+ let getChoice (spec : ArgumentDefaultSpec option) : ArgumentDefaultSpec =
+ match spec with
+ | None ->
+ failwith
+ $"Non-positional Choice args must have an `[]` attribute on them, in record {rt.Name.idText}."
+ | Some spec -> spec
+
+ let aggregated =
+ (Some ([], [], []), rt.Fields)
+ ||> List.fold (fun aggr (SynField.SynField (idOpt = ident ; attributes = attrs ; fieldType = ty)) ->
+ match aggr with
+ | None -> None
+ | Some (leaf, records, unions) ->
+
+ match ident with
+ | None ->
+ failwith
+ $"expected all fields on record type %s{rt.Name.idText} to have a name, but at least one did not"
+ | Some ident ->
+
+ let spec =
+ createParseFunction
+ getChoice
+ flagDus
+ knownRecordParserTypes.Keys
+ knownUnionParserTypes.Keys
+ ident
+ (SynAttributes.toAttrs attrs)
+ ty
+
+ match spec with
+ | Leaf data -> ((ident.idText, data) :: leaf, records, unions) |> Some
+ | UserDefined (isRecord, typeName) ->
+ if isRecord then
+ match knownRecordParserTypes.TryGetValue typeName.idText with
+ | false, _ -> None
+ | true, v -> (leaf, (ident.idText, v) :: records, unions) |> Some
+ else
+ match knownUnionParserTypes.TryGetValue typeName.idText with
+ | false, _ -> None
+ | true, v -> (leaf, records, (ident.idText, v) :: unions) |> Some
+ | OptionOfUserDefined -> failwith "todo"
+ )
+
+ match aggregated with
+ | None -> None
+ | Some (leaf, records, unions) ->
+ {
+ Original = rt
+ LeafNodes = leaf |> Map.ofList
+ Records = records |> Map.ofList
+ Unions = unions |> Map.ofList
+ }
+ |> Some
+
+ /// Returns None if we haven't yet obtained parse structures for the dependencies of this union.
+ /// This function already knows that it's a parser: that is, every case has exactly one field.
+ /// It doesn't necessarily know that those fields can be parsed as records.
+ let private parseUnion
+ (knownRecordTypes : IReadOnlyDictionary>)
+ (ut : UnionType)
+ : ParsedUnionStructure option
+ =
+ ut.Cases
+ |> List.map (fun case ->
+ let field =
+ match case.Fields with
+ | [ x ] -> x
+ | [] ->
+ failwith
+ $"Logic error: expected case %s{case.Name.idText} to have exactly one field, but it had none"
+ | _ ->
+ failwith
+ $"Logic error: expected case %s{case.Name.idText} to have exactly one field, but it had more than one"
+
+ match field.Type with
+ | SynType.LongIdent (SynLongIdent.SynLongIdent (id = id)) ->
+ match knownRecordTypes.TryGetValue (List.last id).idText with
+ | false, _ -> None
+ | true, v -> Some (case.Name.idText, v)
+ | _ ->
+ failwith
+ "ArgParser generator requires discriminated union cases to each contain exactly one field which is a record type, to hold their data."
+ )
+ |> List.allSome
+ |> Option.map Map.ofList
+ |> Option.map (fun x ->
+ {
+ Original = ut
+ Cases = x
+ }
+ )
+
+ let internal parseStructureWithinNs (unions : UnionType list) (records : RecordType list) : AllInfo =
+ let flagDus, datalessUnions, parserUnions =
+ (([], [], []), unions)
+ ||> List.fold (fun (flagDus, datalessUnions, unions) union ->
+ match union.Cases |> List.tryFind (fun case -> not case.Fields.IsEmpty) with
+ | Some dataCarryingCase ->
+ match union.Cases |> List.tryFind (fun case -> case.Fields.Length <> 1) with
+ | Some badCase ->
+ failwith
+ $"Unions must either be dataless or every field must have exactly one member. Type %s{union.Name.idText} has case %s{dataCarryingCase.Name.idText} with data, but case %s{badCase.Name.idText} doesn't have exactly one field."
+ | None ->
+ // OK, all cases have exactly one field.
+ flagDus, datalessUnions, union :: unions
+ | None ->
+
+ let datalessUnionBranch () =
+ let datalessUnion =
+ {
+ DatalessUnion.Cases =
+ union.Cases |> List.map (fun case -> case.Name.idText, case.Attributes)
+ }
+
+ flagDus, (union.Name.idText, datalessUnion) :: datalessUnions, unions
+
+ // dataless or flag
+ match union.Cases with
+ | [ c1 ; c2 ] ->
+ let c1Attr =
+ c1.Attributes
+ |> List.tryPick (fun attr ->
+ match attr.TypeName with
+ | SynLongIdent.SynLongIdent (id, _, _) ->
+ match id |> List.last |> _.idText with
+ | "ArgumentFlagAttribute"
+ | "ArgumentFlag" -> Some (SynExpr.stripOptionalParen attr.ArgExpr)
+ | _ -> None
+ )
+
+ let c2Attr =
+ c2.Attributes
+ |> List.tryPick (fun attr ->
+ match attr.TypeName with
+ | SynLongIdent.SynLongIdent (id, _, _) ->
+ match id |> List.last |> _.idText with
+ | "ArgumentFlagAttribute"
+ | "ArgumentFlag" -> Some (SynExpr.stripOptionalParen attr.ArgExpr)
+ | _ -> None
+ )
+
+ match c1Attr, c2Attr with
+ | Some _, None
+ | None, Some _ ->
+ failwith
+ "[] must be placed on both cases of a two-case discriminated union, with opposite argument values on each case."
+ | None, None ->
+ // actually a dataless union
+ datalessUnionBranch ()
+ | Some c1Attr, Some c2Attr ->
+
+ // Sanity check where possible
+ match c1Attr, c2Attr with
+ | SynExpr.Const (SynConst.Bool b1, _), SynExpr.Const (SynConst.Bool b2, _) ->
+ if b1 = b2 then
+ failwith
+ "[] must have opposite argument values on each case in a two-case discriminated union."
+ | _, _ -> ()
+
+ match c1.Fields, c2.Fields with
+ | [], [] ->
+ let flagDu =
+ {
+ Name = union.Name
+ Case1Name = c1.Name
+ Case1Arg = c1Attr
+ Case2Name = c2.Name
+ Case2Arg = c2Attr
+ }
+
+ (union.Name.idText, flagDu) :: flagDus, datalessUnions, unions
+ | _, _ ->
+ failwith "[] may only be placed on discriminated union members with no data."
+ | _ -> datalessUnionBranch ()
+ )
+
+ let allKnownUnionTypes = Dictionary ()
+ let allKnownRecordTypes = Dictionary ()
+
+ let mutable keepLooping = true
+
+ while keepLooping do
+ keepLooping <- false
+ let mutable madeAChange = false
+
+ for record in records do
+ if not (allKnownRecordTypes.ContainsKey record.Name.idText) then
+ match parseRecord allKnownRecordTypes allKnownUnionTypes (flagDus |> List.map snd) record with
+ | None -> keepLooping <- true
+ | Some v ->
+ allKnownRecordTypes.Add (record.Name.idText, v)
+ madeAChange <- true
+
+ for union in parserUnions do
+ match parseUnion allKnownRecordTypes union with
+ | None -> keepLooping <- true
+ | Some v ->
+ allKnownUnionTypes.Add (union.Name.idText, v)
+ madeAChange <- true
+
+ if not madeAChange then
+ let knownRecords = allKnownRecordTypes.Keys |> String.concat ","
+ let knownUnions = allKnownUnionTypes.Keys |> String.concat ","
+
+ failwith
+ $"Cyclic dependency detected which we can't break. Known records:\n%s{knownRecords}\nKnown unions:\n%s{knownUnions}"
+
+ {
+ RecordParsers = allKnownRecordTypes
+ UnionParsers = allKnownUnionTypes
+ FlagDus = Map.ofList flagDus
+ DatalessUnions = Map.ofList datalessUnions
+ }
/// Some types don't have in-progress equivalents (e.g. a no-data DU, which is "basically primitive");
/// hence the `option`.
let createInProgressRecognisedType
- (flagDuNames : string list)
+ (flagDuNames : string ICollection)
(allKnownTypeIdents : string list)
(ty : RecognisedType)
: RecordType option
@@ -347,7 +929,7 @@ module internal ShibaGenerator =
// TODO: this is super jank
let ident = List.last ty
- if List.contains ident.idText flagDuNames then
+ if flagDuNames.Contains ident.idText then
// Flag DUs have no in-progress form as such
SynType.createLongIdent ty |> SynType.option
elif List.contains ident.idText allKnownTypeIdents then
@@ -467,7 +1049,7 @@ module internal ShibaGenerator =
}
]
else
- l
+ l |> List.map (SynField.withMutability true)
Generics =
match union.Generics with
| None -> None
@@ -490,16 +1072,15 @@ module internal ShibaGenerator =
)
|> fun l ->
if l.IsEmpty then
- [
- SynField.make
- {
- Attrs = []
- Ident = Some (Ident.create "_Dummy")
- Type = SynType.unit
- }
- ]
+ {
+ Attrs = []
+ Ident = Some (Ident.create "_Dummy")
+ Type = SynType.unit
+ }
+ |> SynField.make
+ |> List.singleton
else
- l
+ l |> List.map (SynField.withMutability true)
Members =
// for each field `FieldName` in order, we've made a variable `arg%i`
// which has done the optionality check
@@ -660,71 +1241,7 @@ module internal ShibaGenerator =
}
|> Some
- let createHelpersModule
- (opens : SynOpenDeclTarget list)
- (ns : LongIdent)
- (allUnionTypes : UnionType list)
- (allRecordTypes : RecordType list)
- : SynModuleDecl
- =
- let flagDus =
- allUnionTypes
- |> List.choose (fun ty ->
- match ty.Cases with
- | [ c1 ; c2 ] ->
- let c1Attr =
- c1.Attributes
- |> List.tryPick (fun attr ->
- match attr.TypeName with
- | SynLongIdent.SynLongIdent (id, _, _) ->
- match id |> List.last |> _.idText with
- | "ArgumentFlagAttribute"
- | "ArgumentFlag" -> Some (SynExpr.stripOptionalParen attr.ArgExpr)
- | _ -> None
- )
-
- let c2Attr =
- c2.Attributes
- |> List.tryPick (fun attr ->
- match attr.TypeName with
- | SynLongIdent.SynLongIdent (id, _, _) ->
- match id |> List.last |> _.idText with
- | "ArgumentFlagAttribute"
- | "ArgumentFlag" -> Some (SynExpr.stripOptionalParen attr.ArgExpr)
- | _ -> None
- )
-
- match c1Attr, c2Attr with
- | Some _, None
- | None, Some _ ->
- failwith
- "[] must be placed on both cases of a two-case discriminated union, with opposite argument values on each case."
- | None, None -> None
- | Some c1Attr, Some c2Attr ->
-
- // Sanity check where possible
- match c1Attr, c2Attr with
- | SynExpr.Const (SynConst.Bool b1, _), SynExpr.Const (SynConst.Bool b2, _) ->
- if b1 = b2 then
- failwith
- "[] must have opposite argument values on each case in a two-case discriminated union."
- | _, _ -> ()
-
- match c1.Fields, c2.Fields with
- | [], [] ->
- {
- Name = ty.Name
- Case1Name = c1.Name
- Case1Arg = c1Attr
- Case2Name = c2.Name
- Case2Arg = c2Attr
- }
- |> Some
- | _, _ ->
- failwith "[] may only be placed on discriminated union members with no data."
- | _ -> None
- )
-
+ let createHelpersModule (opens : SynOpenDeclTarget list) (ns : LongIdent) (info : AllInfo) : SynModuleDecl =
let modName =
let ns = ns |> List.map _.idText |> String.concat "_"
Ident.create $"ArgParseHelpers_%s{ns}"
@@ -734,28 +1251,17 @@ module internal ShibaGenerator =
|> SynComponentInfo.withAccessibility (SynAccess.Private range0)
|> SynComponentInfo.withDocString (PreXmlDoc.create $"Helper types for arg parsing")
- let allKnownTypeIdents =
- let uts = allUnionTypes |> List.map _.Name.idText
- let rts = allRecordTypes |> List.map _.Name.idText
- uts @ rts
-
- let flagDuNames = flagDus |> List.map _.Name.idText
+ let flagDuNames = info.FlagDus.Keys
let reducedRecordTypes =
- allRecordTypes
- |> List.choose (fun rt ->
- // TODO: just split these into different functions and get rid of RecognisedType
- createInProgressRecognisedType flagDuNames allKnownTypeIdents (RecognisedType.Record rt)
- |> Option.map RecordType.ToAst
- )
+ info.RecordParsers
+ |> Seq.map (fun (KeyValue (_, record)) -> inProgressRecordType record |> RecordType.ToAst)
+ |> Seq.toList
let reducedUnionTypes =
- allUnionTypes
- |> List.choose (fun ut ->
- // TODO: just split these into different functions and get rid of RecognisedType
- createInProgressRecognisedType flagDuNames allKnownTypeIdents (RecognisedType.Union ut)
- |> Option.map RecordType.ToAst
- )
+ info.UnionParsers
+ |> Seq.map (fun (KeyValue (_, union)) -> failwith "TODO")
+ |> Seq.toList
let taggedMod =
[
@@ -904,12 +1410,17 @@ open Myriad.Core
/// Myriad generator that provides a catamorphism for an algebraic data type.
[]
-type ArgParserGenerator () =
+type ShibaGenerator () =
interface IMyriadGenerator with
member _.ValidInputExtensions = [ ".fs" ]
member _.Generate (context : GeneratorContext) =
+ // try
+ // System.IO.File.Delete "/tmp/myriad.log"
+ // with
+ // | _ -> ()
+
let ast, _ =
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
@@ -961,7 +1472,7 @@ type ArgParserGenerator () =
if not others.IsEmpty then
failwith
- $"Error: all types recursively defined together with an ArgParserGenerator type must be discriminated unions or records. %+A{others}"
+ $"Error: all types recursively defined together with a ShibaGenerator type must be discriminated unions or records. %+A{others}"
(ns, taggedType, unions, records)
)
@@ -982,18 +1493,15 @@ type ArgParserGenerator () =
)
)
- let helpersMod =
+ let structuresWithinNs =
unionsAndRecordsByNs
+ |> Map.map (fun _ (us, rs) -> ShibaGenerator.parseStructureWithinNs us rs)
+
+ let helpersMod =
+ structuresWithinNs
|> Map.toSeq
- |> Seq.map (fun (ns, (unions, records)) ->
- let unions = unions |> List.distinctBy (fun u -> u.Name.idText)
- let records = records |> List.distinctBy (fun r -> r.Name.idText)
-
- ShibaGenerator.createHelpersModule
- opens
- (ns.Split '.' |> Seq.map Ident.create |> List.ofSeq)
- unions
- records
+ |> Seq.map (fun (ns, info) ->
+ ShibaGenerator.createHelpersModule opens (ns.Split '.' |> Seq.map Ident.create |> List.ofSeq) info
)
|> Seq.toList
|> fun l -> [ yield! l ]
diff --git a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
index da03107f..468a892e 100644
--- a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
+++ b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
@@ -22,7 +22,7 @@
-
+
@@ -40,8 +40,8 @@
-
+
From 67eb89cfc0ba966a114f31ae415af31a44e0d792 Mon Sep 17 00:00:00 2001
From: Smaug123 <3138005+Smaug123@users.noreply.github.com>
Date: Mon, 14 Apr 2025 22:27:57 +0100
Subject: [PATCH 06/26] Getting there
---
ConsumePlugin/GeneratedArgs.fs | 438 ++++++++++++++------
WoofWare.Myriad.Plugins/ShibaGenerator.fs | 471 ++++++----------------
2 files changed, 440 insertions(+), 469 deletions(-)
diff --git a/ConsumePlugin/GeneratedArgs.fs b/ConsumePlugin/GeneratedArgs.fs
index 2cfb5b68..c3decc8c 100644
--- a/ConsumePlugin/GeneratedArgs.fs
+++ b/ConsumePlugin/GeneratedArgs.fs
@@ -20,13 +20,17 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed BasicNoPositionals.
type private BasicNoPositionals_InProgress =
{
- mutable Foo : System.Int32 option
- mutable Bar : System.String option
- mutable Baz : System.Boolean option
- mutable Rest : string list
+ mutable Bar : string option
+ mutable Baz : bool option
+ mutable Foo : int option
+ mutable Rest : ResizeArray
}
- member this.Assemble (positionals : string list) : Result =
+ member this.Assemble
+ (getEnvironmentVariable : string -> string)
+ (positionals : Choice list)
+ : Result
+ =
let errors = ResizeArray ()
let arg0 : int =
@@ -50,7 +54,7 @@ module private ArgParseHelpers_ConsumePlugin =
errors.Add "no value provided for Baz"
Unchecked.defaultof<_>
- let arg3 : int list = this.Rest
+ let arg3 : int list = this.Rest |> Seq.toList
if errors.Count = 0 then
Ok
@@ -66,12 +70,17 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed Basic.
type private Basic_InProgress =
{
- mutable Foo : System.Int32 option
- mutable Bar : System.String option
- mutable Baz : System.Boolean option
+ mutable Bar : string option
+ mutable Baz : bool option
+ mutable Foo : int option
+ mutable Rest : ResizeArray
}
- member this.Assemble (positionals : string list) : Result =
+ member this.Assemble
+ (getEnvironmentVariable : string -> string)
+ (positionals : Choice list)
+ : Result
+ =
let errors = ResizeArray ()
let arg0 : int =
@@ -95,8 +104,14 @@ module private ArgParseHelpers_ConsumePlugin =
errors.Add "no value provided for Baz"
Unchecked.defaultof<_>
- let arg3 : string list = positionals
- let positionals = ()
+ let arg3 : string list =
+ positionals
+ |> List.map (fun x ->
+ match x with
+ | Choice1Of2 x -> x
+ | Choice2Of2 x -> x
+ )
+ |> List.map (fun x -> x)
if errors.Count = 0 then
Ok
@@ -112,12 +127,17 @@ module private ArgParseHelpers_ConsumePlugin =
/// A partially-parsed BasicWithIntPositionals.
type private BasicWithIntPositionals_InProgress =
{
- mutable Foo : System.Int32 option
- mutable Bar : System.String option
- mutable Baz : System.Boolean option
+ mutable Bar : string option
+ mutable Baz : bool option
+ mutable Foo : int option
+ mutable Rest : ResizeArray
}
- member this.Assemble (positionals : string list) : Result =
+ member this.Assemble
+ (getEnvironmentVariable : string -> string)
+ (positionals : Choice list)
+ : Result