add Numbers arg type

This commit is contained in:
2023-01-19 10:42:01 -07:00
parent 5c311fbfd7
commit 1ba8e282ab
5 changed files with 71 additions and 29 deletions

View File

@@ -21,6 +21,8 @@ enum CommandArgType {
VarText(maxLength:Null<Float>);
// Numerical input, can be forced to align with a fixed step from the minimum
Number(min:Null<Float>, max:Null<Float>, inStepsOf:Null<Float>);
// Any number of numerical inputs, can be forced to align with a fixed step from the minimum
Numbers(min:Null<Float>, max:Null<Float>, inStepsOf:Null<Float>);
// Make the user select Entry(s) when called interactively
OneEntry; // This constructor must be disambiguated from the typedef "Entry"
Entries(min:Null<Int>, max:Null<Int>);

View File

@@ -86,7 +86,46 @@
max
inStepsOf)
(trySubmit (readNumber stream)))))
((Numbers min max inStepsOf)
(unless min (set min Math.NEGATIVE_INFINITY))
(unless max (set max Math.POSITIVE_INFINITY))
(stream.dropWhitespace)
(let [&mut prompt "${arg.name} (${min}-${max}"
collectedNumbers []]
(when inStepsOf
(+= prompt " in steps of ${inStepsOf}"))
(+= prompt "):")
(localFunction :Void enterNumAgain []
(localFunction :Void trySubmit [number]
(stream.dropWhitespace)
(let [minMaxError
"The requested command expected numbers between $min and $max"
stepError
"$minMaxError in steps of $inStepsOf"
youEntered
". You entered: $number"]
(if (Math.isNaN number)
(continuation collectedNumbers)
(if (or
!(<= min number max)
(and inStepsOf !(= 0 (% (- number min) inStepsOf))))
(if inStepsOf
(ui.reportError "${stepError}$youEntered")
(ui.reportError "${minMaxError}$youEntered"))
{(collectedNumbers.push number)
(enterNumAgain)}))))
(if (or (stream.isEmpty) (stream.dropStringIf "_"))
// If no vartext argument was pre-supplied, use the ui for it
(ui.enterNumber
prompt
trySubmit
min
max
inStepsOf
true)
(trySubmit (readNumber stream))))
(enterNumAgain)))
(OneEntry
(ui.chooseEntry
"${arg.name}:"
@@ -220,6 +259,7 @@
(TagsFromAll `:Array<String> ,name)
(TagsFromSelected `:Array<String> ,name)
((Number _ _ _) `:Float ,name)
((Numbers _ _ _) `:Array<Float> ,name)
(Position `:Position ,name)))
commandArgs
(for [name type] argPairs

View File

@@ -34,7 +34,7 @@ interface ArchiveUI {
/**
* Prompt the user to enter a number
*/
function enterNumber(prompt:String, resolve:(Float) -> Void, min:Float, max:Float, ?inStepsOf:Float):Void;
function enterNumber(prompt:String, resolve:(Float) -> Void, min:Float, max:Float, ?inStepsOf:Float, ?allowNaN:Bool):Void;
/**
* Prompt the user to choose a single Entry

View File

@@ -27,34 +27,35 @@
{(resolve entered)
(break)}))))
(method :Void enterNumber [prompt :Float->Void resolve :Float min :Float max &opt :Float inStepsOf]
(method :Void enterNumber [prompt :Float->Void resolve :Float min :Float max &opt :Float inStepsOf :Bool allowNaN]
(Sys.print "$prompt ")
(loop
(let [entered (Std.parseFloat (.toString (.readLine (Sys.stdin))))]
(if
(or
!(<= min entered max)
(and inStepsOf !(= 0 (% (- entered min) inStepsOf))))
(Sys.print "Try again? ")
{(resolve entered)
(break)}))))
(and (or !allowNaN !(Math.isNaN entered))
(or
!(<= min entered max)
(and inStepsOf !(= 0 (% (- entered min) inStepsOf)))))
(Sys.print "Try again? ")
{(resolve entered)
(break)}))))
(method :Void chooseEntry [prompt :Archive archive :Entry->Void resolve]
(_chooseEntry prompt archive resolve ->(chooseEntry "empty name doesn't match any entries. Try again?" archive resolve)))
(method :Void chooseEntry [prompt :Archive archive :Entry->Void resolve]
(_chooseEntry prompt archive resolve ->(chooseEntry "empty name doesn't match any entries. Try again?" archive resolve)))
(method :Void _chooseEntry [prompt :Archive archive :Entry->Void resolve :Void->Void onEmptyString]
// TODO allow narrowing down with a tag string
(enterText "entry name for $prompt"
->:Void name {
(if !name
(onEmptyString)
(let [matchingEntries (controller.nameSystem.getEntries name)]
(case (the Array<Entry> matchingEntries)
([e] (resolve e))
([] (chooseEntry "name $name doesn't match any entries. Try again?" archive resolve))
// TODO disambiguate entries with the same names by listing stringified versions of them and using enterNumber
(multipleEntries (throw "ambiguous between multiple entries"))
(otherwise))))}
(method :Void _chooseEntry [prompt :Archive archive :Entry->Void resolve :Void->Void onEmptyString]
// TODO allow narrowing down with a tag string
(enterText "entry name for $prompt"
->:Void name {
(if !name
(onEmptyString)
(let [matchingEntries (controller.nameSystem.getEntries name)]
(case (the Array<Entry> matchingEntries)
([e] (resolve e))
([] (chooseEntry "name $name doesn't match any entries. Try again?" archive resolve))
// TODO disambiguate entries with the same names by listing stringified versions of them and using enterNumber
(multipleEntries (throw "ambiguous between multiple entries"))
(otherwise))))}
Math.POSITIVE_INFINITY))
(method :Void chooseEntries [prompt archive :Array<nat.Entry>->Void resolve min max]

View File

@@ -149,14 +149,13 @@
(otherwise {}))))
(uiWindow.addControl textInput))
(method :Void enterNumber [prompt resolve min max &opt inStepsOf]
(method :Void enterNumber [prompt resolve min max &opt inStepsOf allowNaN]
(enterText prompt
->:Void [numberStr]
(let [number
(try (Std.parseFloat numberStr)
(catch [e]
(reportError "Not a number: $numberStr")
(return)))]
(let [number (Std.parseFloat numberStr)]
(when (and !allowNaN (Math.isNaN number))
(reportError "Not a number: $numberStr")
(return))
(resolve number))
Math.POSITIVE_INFINITY))