mirror of
https://github.com/daylinmorgan/hwylterm.git
synced 2024-11-16 06:28:32 -06:00
handle flag groups differently
This commit is contained in:
parent
d228123195
commit
8b37c0dad9
2 changed files with 79 additions and 77 deletions
|
@ -118,6 +118,8 @@ proc bb*(cli: HwylCliHelp): BbString =
|
||||||
proc `$`*(cli: HwylCliHelp): string =
|
proc `$`*(cli: HwylCliHelp): string =
|
||||||
result = $bb(cli)
|
result = $bb(cli)
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
CliSetting = enum
|
CliSetting = enum
|
||||||
NoHelpFlag, NoArgsShowHelp
|
NoHelpFlag, NoArgsShowHelp
|
||||||
|
@ -131,7 +133,6 @@ type
|
||||||
name*: string
|
name*: string
|
||||||
ident*: NimNode
|
ident*: NimNode
|
||||||
default*: NimNode
|
default*: NimNode
|
||||||
typeSym*: string
|
|
||||||
typeNode*: NimNode
|
typeNode*: NimNode
|
||||||
short*: char
|
short*: char
|
||||||
long*: string
|
long*: string
|
||||||
|
@ -149,8 +150,9 @@ type
|
||||||
version*, usage*: NimNode
|
version*, usage*: NimNode
|
||||||
flags*: seq[CliFlag]
|
flags*: seq[CliFlag]
|
||||||
builtinFlags*: seq[BuiltinFlag]
|
builtinFlags*: seq[BuiltinFlag]
|
||||||
|
flagGroups: Table[string, seq[CliFlag]]
|
||||||
required*: seq[string]
|
required*: seq[string]
|
||||||
globalFlags*: seq[CliFlag]
|
inheritFlags*: seq[string]
|
||||||
|
|
||||||
{.push hint[XDeclaredButNotUsed]:off .}
|
{.push hint[XDeclaredButNotUsed]:off .}
|
||||||
# some debug procs I use to wrap my ahead aroung the magic of *macro*
|
# some debug procs I use to wrap my ahead aroung the magic of *macro*
|
||||||
|
@ -159,75 +161,49 @@ func `<<<`(n: NimNode) =
|
||||||
debugEcho treeRepr n
|
debugEcho treeRepr n
|
||||||
func `<<<`(s: string) =
|
func `<<<`(s: string) =
|
||||||
debugEcho s
|
debugEcho s
|
||||||
{.pop.}
|
|
||||||
|
|
||||||
# TODO: do i need this?
|
|
||||||
func newCliFlag(): CliFlag =
|
|
||||||
result.help = newLit("")
|
|
||||||
|
|
||||||
func bad(n: NimNode, argument: string = "") =
|
func bad(n: NimNode, argument: string = "") =
|
||||||
|
|
||||||
var msg = "unexpected node kind: " & $n.kind
|
var msg = "unexpected node kind: " & $n.kind
|
||||||
if argument != "":
|
if argument != "":
|
||||||
msg &= " for argument: " & argument
|
msg &= " for argument: " & argument
|
||||||
|
|
||||||
# error "unexpected node kind: " & $n.kind
|
|
||||||
error msg
|
error msg
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
func typeSymFromNode(node: NimNode): string =
|
func getFlagParamNode(node: NimNode): NimNode =
|
||||||
case node.kind
|
case node.kind
|
||||||
of nnkIdent, nnkStrLit:
|
|
||||||
result = node.strVal
|
|
||||||
of nnkBracketExpr:
|
|
||||||
result = node[0].strVal & "[" & node[1].strVal & "]"
|
|
||||||
else: bad node
|
|
||||||
|
|
||||||
func getOptTypeSym(node: NimNode): string =
|
|
||||||
case node.kind:
|
|
||||||
of nnkCommand:
|
|
||||||
result = typeSymFromNode(node[1]) # [0] is T
|
|
||||||
of nnkCall:
|
|
||||||
result = typeSymFromNode(node[1][0]) # [1] is stmtlist [0] is the type
|
|
||||||
else: error "unexpected node kind: " & $node.kind
|
|
||||||
|
|
||||||
func getOptOptNode(optOptValue: NimNode): NimNode =
|
|
||||||
case optOptValue.kind
|
|
||||||
of nnkStrLit:
|
of nnkStrLit:
|
||||||
result = optOptValue
|
result = node
|
||||||
of nnkStmtList:
|
of nnkStmtList:
|
||||||
result = optOptValue[0]
|
result = node[0]
|
||||||
of nnkCommand:
|
of nnkCommand:
|
||||||
result = optOptValue[1]
|
result = node[1]
|
||||||
of nnkPrefix: # NOTE: should i double check prefix value?
|
of nnkPrefix: # NOTE: should i double check prefix value?
|
||||||
result = optOptValue[1]
|
result = node[1]
|
||||||
else: error "unexpected node kind: " & $optOptValue.kind
|
else: bad(node, "flag param")
|
||||||
|
|
||||||
# TODO: don't use the confusing name optOpts here and above
|
func parseFlagParams(f: var CliFlag, node: NimNode) =
|
||||||
func parseOptOpts(opt: var CliFlag, optOpts: NimNode) =
|
expectKind node, nnkStmtList
|
||||||
expectKind optOpts, nnkStmtList
|
for n in node:
|
||||||
for optOpt in optOpts:
|
case n.kind
|
||||||
case optOpt.kind
|
|
||||||
of nnkCall, nnkCommand, nnkPrefix:
|
of nnkCall, nnkCommand, nnkPrefix:
|
||||||
case optOpt[0].strVal
|
case n[0].strVal
|
||||||
of "help","?":
|
of "help","?":
|
||||||
opt.help = getOptOptNode(optOpt[1])
|
f.help = getFlagParamNode(n[1])
|
||||||
of "short", "-":
|
of "short", "-":
|
||||||
let val = getOptOptNode(optOpt).strVal
|
let val = getFlagParamNode(n).strVal
|
||||||
if val.len > 1:
|
if val.len > 1:
|
||||||
error "short flag must be a char"
|
error "short flag must be a char"
|
||||||
opt.short = val[0].char
|
f.short = val[0].char
|
||||||
of "*", "default":
|
of "*", "default":
|
||||||
opt.default = getOptOptNode(optOpt)
|
f.default = getFlagParamNode(n)
|
||||||
of "i", "ident":
|
of "i", "ident":
|
||||||
opt.ident = getOptOptNode(optOpt).strVal.ident
|
f.ident = getFlagParamNode(n).strVal.ident
|
||||||
of "T":
|
of "T":
|
||||||
opt.typeNode = optOpt[1]
|
f.typeNode = n[1]
|
||||||
# TODO: remove this...
|
|
||||||
opt.typeSym = getOptTypeSym(optOpt)
|
|
||||||
else:
|
else:
|
||||||
error "unexpected option setting: " & optOpt[0].strVal
|
error "unexpected setting: " & n[0].strVal
|
||||||
else:
|
else:
|
||||||
error "unexpected option node type: " & $optOpt.kind
|
bad(n, "flag params")
|
||||||
|
|
||||||
func startFlag(f: var CliFlag, n: NimNode) =
|
func startFlag(f: var CliFlag, n: NimNode) =
|
||||||
f.name =
|
f.name =
|
||||||
|
@ -236,6 +212,8 @@ func startFlag(f: var CliFlag, n: NimNode) =
|
||||||
of nnkAccQuoted: collect(for c in n[0]: c.strVal).join("")
|
of nnkAccQuoted: collect(for c in n[0]: c.strVal).join("")
|
||||||
else: error "unexpected node kind for option"
|
else: error "unexpected node kind for option"
|
||||||
|
|
||||||
|
f.help = newLit("") # by default no string
|
||||||
|
|
||||||
# assume a single character is a short flag
|
# assume a single character is a short flag
|
||||||
if f.name.len == 1:
|
if f.name.len == 1:
|
||||||
f.short = f.name[0].char
|
f.short = f.name[0].char
|
||||||
|
@ -244,10 +222,8 @@ func startFlag(f: var CliFlag, n: NimNode) =
|
||||||
|
|
||||||
func parseCliFlag(n: NimNode): CliFlag =
|
func parseCliFlag(n: NimNode): CliFlag =
|
||||||
if n.kind notin [nnkCommand, nnkCall]:
|
if n.kind notin [nnkCommand, nnkCall]:
|
||||||
error "unexpected node kind: " & $n.kind
|
bad(n, "flags")
|
||||||
|
|
||||||
# deduplicate these...
|
|
||||||
result = newCliFlag()
|
|
||||||
startFlag(result, n)
|
startFlag(result, n)
|
||||||
# option "some help desc"
|
# option "some help desc"
|
||||||
if n.kind == nnkCommand:
|
if n.kind == nnkCommand:
|
||||||
|
@ -255,29 +231,43 @@ func parseCliFlag(n: NimNode): CliFlag =
|
||||||
# option:
|
# option:
|
||||||
# help "some help description"
|
# help "some help description"
|
||||||
else:
|
else:
|
||||||
parseOptOpts(result, n[1])
|
parseFlagParams(result, n[1])
|
||||||
|
|
||||||
if result.ident == nil:
|
if result.ident == nil:
|
||||||
result.ident = result.name.ident
|
result.ident = result.name.ident
|
||||||
if result.typeNode == nil:
|
if result.typeNode == nil:
|
||||||
result.typeNode = ident"string"
|
result.typeNode = ident"string"
|
||||||
if result.typeSym == "":
|
|
||||||
result.typeSym = "string"
|
|
||||||
|
|
||||||
# TODO: handle flag groups here
|
# TODO: change how this works?
|
||||||
# cfg.flagGroups = Table[string, seq[CliFlag]]?
|
func parseCliFlags(cfg: var CliCfg, node: NimNode) =
|
||||||
func parseCliFlags(flags: NimNode): seq[CliFlag] =
|
var group: string
|
||||||
# <<< flags
|
expectKind node, nnkStmtList
|
||||||
expectKind flags, nnkStmtList
|
for n in node:
|
||||||
for f in flags:
|
var flag: CliFlag
|
||||||
case f.kind
|
case n.kind
|
||||||
of nnkCall, nnkCommand:
|
of nnkCall, nnkCommand:
|
||||||
result.add parseCliFlag(f)
|
flag = parseCliFlag(n)
|
||||||
|
if group == "":
|
||||||
|
cfg.flags.add flag
|
||||||
|
else:
|
||||||
|
if group notin cfg.flagGroups: cfg.flagGroups[group] = @[flag]
|
||||||
|
else: cfg.flagGroups[group].add flag
|
||||||
|
of nnkBracket:
|
||||||
|
group = n[0].strVal
|
||||||
|
continue
|
||||||
|
of nnkPrefix:
|
||||||
|
if n[0].kind != nnkIdent and n[0].strVal != "^":
|
||||||
|
error "unexpected node in flags: " & $n.kind
|
||||||
|
expectKind n[1], nnkBracket
|
||||||
|
cfg.inheritFlags.add n[1][0].strVal
|
||||||
# of nnkPrefix:
|
# of nnkPrefix:
|
||||||
# <<< "reached Prefix!"
|
# if n[0].strVal != "---":
|
||||||
# <<< f
|
# bad(n[0], "flag group prefix")
|
||||||
else: error "unexpected node kind parsing flags"
|
# group = n[1].strVal
|
||||||
|
# continue
|
||||||
|
else: bad(n, "flag")
|
||||||
|
|
||||||
|
debugEcho cfg.flagGroups.keys().toSeq()
|
||||||
|
|
||||||
func parseCliSetting(s: string): CliSetting =
|
func parseCliSetting(s: string): CliSetting =
|
||||||
try: parseEnum[CliSetting](s)
|
try: parseEnum[CliSetting](s)
|
||||||
|
@ -337,13 +327,25 @@ func sliceStmts(node: NimNode): seq[
|
||||||
start = i + 1
|
start = i + 1
|
||||||
|
|
||||||
|
|
||||||
func addGlobalFlagsFrom(child: var CliCfg, parent: CliCfg) =
|
func addInheritedFlags(child: var CliCfg, parent: CliCfg, self = false) =
|
||||||
let names = child.flags.mapIt(it.name)
|
let names = child.flags.mapIt(it.name)
|
||||||
for f in parent.globalFlags:
|
var groups: seq[string]
|
||||||
if f.name in names:
|
if not self:
|
||||||
error "global flag " & f.name & " conflicts with command flag"
|
groups.add child.inheritFlags
|
||||||
child.flags.add f
|
|
||||||
|
|
||||||
|
# autoinherit the "global" flags
|
||||||
|
if "global" in parent.flagGroups:
|
||||||
|
groups.add "global"
|
||||||
|
|
||||||
|
for g in groups:
|
||||||
|
if g notin parent.flagGroups:
|
||||||
|
debugEcho parent.flagGroups.keys().toSeq()
|
||||||
|
error "expected flag group: " & g & " to exist in parent command"
|
||||||
|
for f in parent.flagGroups[g]:
|
||||||
|
if f.name in names:
|
||||||
|
error "global flag " & f.name & " conflicts with command flag"
|
||||||
|
child.flags.add f
|
||||||
|
|
||||||
func parseCliSubcommands(cfg: var CliCfg, node: NimNode) =
|
func parseCliSubcommands(cfg: var CliCfg, node: NimNode) =
|
||||||
expectKind node[1], nnkStmtList
|
expectKind node[1], nnkStmtList
|
||||||
for (name, s) in sliceStmts(node[1]):
|
for (name, s) in sliceStmts(node[1]):
|
||||||
|
@ -352,8 +354,7 @@ func parseCliSubcommands(cfg: var CliCfg, node: NimNode) =
|
||||||
nnkStmtList.newTree(node[1][s]), cfg.name & " " & name
|
nnkStmtList.newTree(node[1][s]), cfg.name & " " & name
|
||||||
)
|
)
|
||||||
subCfg.subName = name
|
subCfg.subName = name
|
||||||
subCfg.addGlobalFlagsFrom(cfg)
|
subCfg.addInheritedFlags(cfg)
|
||||||
|
|
||||||
cfg.subcommands.add subCfg
|
cfg.subcommands.add subCfg
|
||||||
|
|
||||||
func parseHiddenFlags(cfg: var CliCfg, node: NimNode) =
|
func parseHiddenFlags(cfg: var CliCfg, node: NimNode) =
|
||||||
|
@ -420,10 +421,8 @@ func parseCliBody(body: NimNode, name = ""): CliCfg =
|
||||||
result.usage = call[1]
|
result.usage = call[1]
|
||||||
of "description", "...":
|
of "description", "...":
|
||||||
result.desc = call[1]
|
result.desc = call[1]
|
||||||
of "globalFlags":
|
|
||||||
result.globalFlags = parseCliFlags(call[1])
|
|
||||||
of "flags":
|
of "flags":
|
||||||
result.flags = parseCliFlags(call[1])
|
parseCliFlags(result, call[1])
|
||||||
of "settings":
|
of "settings":
|
||||||
parseCliSettings(result, call)
|
parseCliSettings(result, call)
|
||||||
of "stopWords":
|
of "stopWords":
|
||||||
|
@ -449,7 +448,7 @@ func parseCliBody(body: NimNode, name = ""): CliCfg =
|
||||||
sub.pre = result.preSub
|
sub.pre = result.preSub
|
||||||
sub.post = result.postSub
|
sub.post = result.postSub
|
||||||
|
|
||||||
result.addGlobalFlagsFrom(result)
|
result.addInheritedFlags(result, self = true)
|
||||||
|
|
||||||
if result.name == "":
|
if result.name == "":
|
||||||
error "missing required option: name"
|
error "missing required option: name"
|
||||||
|
|
|
@ -9,6 +9,7 @@ hwylCli:
|
||||||
yes:
|
yes:
|
||||||
T bool
|
T bool
|
||||||
? "set flag to yes"
|
? "set flag to yes"
|
||||||
|
[global]
|
||||||
config:
|
config:
|
||||||
T seq[string]
|
T seq[string]
|
||||||
? "path to config file"
|
? "path to config file"
|
||||||
|
@ -24,7 +25,6 @@ hwylCli:
|
||||||
`long-flag` "some help"
|
`long-flag` "some help"
|
||||||
flag:
|
flag:
|
||||||
? "some other help"
|
? "some other help"
|
||||||
|
|
||||||
run:
|
run:
|
||||||
echo "hello from `example one` command!"
|
echo "hello from `example one` command!"
|
||||||
echo "long-flag and flag are: " & `long-flag` & "," & `flag` & " by default strings"
|
echo "long-flag and flag are: " & `long-flag` & "," & `flag` & " by default strings"
|
||||||
|
@ -43,6 +43,9 @@ hwylCli:
|
||||||
bflag:
|
bflag:
|
||||||
T seq[float]
|
T seq[float]
|
||||||
? "multiple floats"
|
? "multiple floats"
|
||||||
|
c:
|
||||||
|
? "this should be a single flag"
|
||||||
|
h "overwrite the short h from help"
|
||||||
run:
|
run:
|
||||||
echo "hello from `example b` command"
|
echo "hello from `example b` command"
|
||||||
echo fmt"{aflag=}, {bflag=}"
|
echo fmt"{aflag=}, {bflag=}"
|
||||||
|
|
Loading…
Reference in a new issue