treat builtin flags different

This commit is contained in:
Daylin Morgan 2024-11-09 16:46:02 -06:00
parent 7f53ef5df9
commit 7cf4c2f537
Signed by: daylin
GPG key ID: 950D13E9719334AD

View file

@ -121,6 +121,12 @@ proc `$`*(cli: HwylCliHelp): string =
type type
CliSetting = enum CliSetting = enum
NoHelpFlag, NoArgsShowHelp NoHelpFlag, NoArgsShowHelp
BuiltinFlag = object
name*: string
short*: char
long*: string
help*: NimNode
node: NimNode
CliFlag = object CliFlag = object
name*: string name*: string
ident*: string ident*: string
@ -141,13 +147,17 @@ type
subName*: string # used for help the generator subName*: string # used for help the generator
version*, usage*: NimNode version*, usage*: NimNode
flags*: seq[CliFlag] flags*: seq[CliFlag]
builtinFlags*: seq[BuiltinFlag]
required*: seq[string] required*: seq[string]
globalFlags*: seq[CliFlag] globalFlags*: seq[CliFlag]
{.push hint[XDeclaredButNotUsed]:off .} {.push hint[XDeclaredButNotUsed]:off .}
func peekNode(n: NimNode) = # some debug procs I use to wrap my ahead aroung the magic of *macro*
func `<<<`(n: NimNode) =
## for debugging macros ## for debugging macros
debugEcho treeRepr n debugEcho treeRepr n
func `<<<`(s: string) =
debugEcho s
{.pop.} {.pop.}
# TODO: do i need this? # TODO: do i need this?
@ -243,11 +253,20 @@ func parseCliFlag(n: NimNode): CliFlag =
if result.typeSym == "": if result.typeSym == "":
result.typeSym = "string" result.typeSym = "string"
# TODO: handle flag groups here
# cfg.flagGroups = Table[string, seq[CliFlag]]?
func parseCliFlags(flags: NimNode): seq[CliFlag] = func parseCliFlags(flags: NimNode): seq[CliFlag] =
# <<< flags
expectKind flags, nnkStmtList expectKind flags, nnkStmtList
for f in flags: for f in flags:
result.add parseCliFlag(f) case f.kind
of nnkCall, nnkCommand:
result.add parseCliFlag(f)
# of nnkPrefix:
# <<< "reached Prefix!"
# <<< f
else: error "unexpected node kind parsing flags"
func parseCliSetting(s: string): CliSetting = func parseCliSetting(s: string): CliSetting =
try: parseEnum[CliSetting](s) try: parseEnum[CliSetting](s)
@ -342,6 +361,38 @@ func parseHiddenFlags(cfg: var CliCfg, node: NimNode) =
cfg.hidden.add n.strVal cfg.hidden.add n.strVal
else: assert false else: assert false
func addBuiltinFlags(cfg: var CliCfg) =
# duplicated with below :/
let shorts = cfg.flags.mapIt(it.short).toHashSet()
let
name = cfg.name.replace(" ", "")
printHelpName = ident("print" & name & "Help")
if NoHelpFlag notin cfg.settings:
let helpNode = quote do:
`printHelpName`(); quit 0
cfg.builtinFlags.add BuiltinFlag(
name: "help",
long: "help",
help: newLit("show this help"),
short: if 'h' notin shorts: 'h' else: '\x00',
node: helpNode
)
if cfg.version != nil:
let version = cfg.version
let versionNode = quote do:
echo `version`; quit 0
cfg.builtinFlags.add BuiltinFlag(
name:"version",
long: "version",
help: newLit("print version"),
short: if 'V' notin shorts: 'V' else: '\x00',
node: versionNode
)
func parseCliBody(body: NimNode, name = ""): CliCfg = func parseCliBody(body: NimNode, name = ""): CliCfg =
result.name = name result.name = name
for call in body: for call in body:
@ -392,32 +443,25 @@ func parseCliBody(body: NimNode, name = ""): CliCfg =
if result.name == "": if result.name == "":
error "missing required option: name" error "missing required option: name"
# TODO: here an elsewhere make help/version less special result.addBuiltinFlags()
# and just append to "opts" at that point
# check for h,V in existing opts and use if available func flagToTuple(f: CliFlag | BuiltinFlag): NimNode =
let
short =
if f.short != '\x00': newLit($f.short)
else: newLit("")
long = newLit(f.long)
help = f.help
quote do:
(`short`, `long`, `help`)
func flagsArray(cfg: CliCfg): NimNode = func flagsArray(cfg: CliCfg): NimNode =
result = newTree(nnkBracket) result = newTree(nnkBracket)
for f in cfg.flags: for f in cfg.flags:
if f.name in cfg.hidden: continue if f.name in cfg.hidden: continue
let result.add f.flagToTuple()
help = f.help for f in cfg.builtinFlags:
long = newLit(f.long) result.add f.flagToTuple()
short =
if f.short != '\x00': newLit($f.short)
else: newLit("")
result.add quote do:
(`short`, `long`, `help`)
if NoHelpFlag notin cfg.settings:
result.add quote do:
("h", "help", "show this help")
if cfg.version != nil:
result.add quote do:
("V", "version", "print version")
func subCmdsArray(cfg: CliCfg): NimNode = func subCmdsArray(cfg: CliCfg): NimNode =
result = newTree(nnkBracket) result = newTree(nnkBracket)
@ -439,7 +483,6 @@ func defaultUsage(cfg: CliCfg): NimNode =
func generateCliHelperProc(cfg: CliCfg, printHelpName: NimNode): NimNode = func generateCliHelperProc(cfg: CliCfg, printHelpName: NimNode): NimNode =
let let
# name = newLit(cfg.name)
desc = cfg.desc or newLit("") desc = cfg.desc or newLit("")
usage = cfg.usage or defaultUsage(cfg) usage = cfg.usage or defaultUsage(cfg)
helpFlags = cfg.flagsArray() helpFlags = cfg.flagsArray()
@ -521,19 +564,13 @@ func shortLongCaseStmt(cfg: CliCfg, printHelpName: NimNode, version: NimNode): N
var caseStmt = nnkCaseStmt.newTree(ident("key")) var caseStmt = nnkCaseStmt.newTree(ident("key"))
caseStmt.add nnkOfBranch.newTree(newLit(""), quote do: hwylCliError("empty flag not supported currently")) caseStmt.add nnkOfBranch.newTree(newLit(""), quote do: hwylCliError("empty flag not supported currently"))
if NoHelpFlag notin cfg.settings: for f in cfg.builtinFlags:
caseStmt.add nnkOfBranch.newTree( var branch = nnkOfBranch.newTree()
newLit("h"), newLit("help"), if f.long != "": branch.add(newLit(f.long))
quote do: if f.short != '\x00': branch.add(newLit($f.short))
`printHelpName`(); quit 0 branch.add f.node
) caseStmt.add branch
if cfg.version != nil:
caseStmt.add nnkOfBranch.newTree(
newLit("V"), newLit("version"),
quote do:
echo `version`; quit 0
)
# add flags # add flags
for f in cfg.flags: for f in cfg.flags:
@ -546,26 +583,16 @@ func shortLongCaseStmt(cfg: CliCfg, printHelpName: NimNode, version: NimNode): N
caseStmt.add nnkElse.newTree(quote do: hwylCliError("unknown flag: [b]" & key)) caseStmt.add nnkElse.newTree(quote do: hwylCliError("unknown flag: [b]" & key))
result = nnkStmtList.newTree(caseStmt) result = nnkStmtList.newTree(caseStmt)
func getNoVals(cfg: CliCfg): tuple[long: NimNode, short: NimNode] = func getNoVals(cfg: CliCfg): tuple[long: NimNode, short: NimNode] =
var long = nnkBracket.newTree() let boolFlags = cfg.flags.filterIt(it.typeSym == "bool")
var short = nnkCurly.newTree() let long =
nnkBracket.newTree(
if NoHelpFlag notin cfg.settings: (boolFlags.mapIt(it.long) & cfg.builtinFlags.mapIt(it.long)).filterIt(it != "").mapIt(newLit(it))
long.add newLit("help") )
short.add newLit('h') let short =
nnkCurly.newTree(
if cfg.version != nil: (boolFlags.mapIt(it.short) & cfg.builtinFlags.mapIt(it.short)).filterIt(it != '\x00').mapIt(newLit(it))
long.add newLit("version") )
short.add newLit('V')
for f in cfg.flags:
if f.typeSym == "bool":
if f.long != "":
long.add newLit(f.long)
if f.short != '\x00':
short.add newLit(f.short)
result = (nnkPrefix.newTree(ident"@",long), short) result = (nnkPrefix.newTree(ident"@",long), short)
func setFlagVars(cfg: CliCfg): NimNode = func setFlagVars(cfg: CliCfg): NimNode =
@ -606,8 +633,6 @@ func hwylCliImpl(cfg: CliCfg, root = false): NimNode =
printHelperProc = generateCliHelperProc(cfg, printHelpName) printHelperProc = generateCliHelperProc(cfg, printHelpName)
flagVars = setFlagVars(cfg) flagVars = setFlagVars(cfg)
# result.add setFlagVars(cfg)
var parserBody = nnkStmtList.newTree() var parserBody = nnkStmtList.newTree()
let let
optParser = ident("p") optParser = ident("p")
@ -726,18 +751,16 @@ when isMainModule:
hwylCli: hwylCli:
name "hwylterm" name "hwylterm"
version "0.1.0"
... "a description of hwylterm" ... "a description of hwylterm"
flags: flags:
check: check:
T bool T bool
? "load config and exit" ? "load config and exit"
- c
# --- other
config: config:
T seq[string] T seq[string]
? "path to config file" ? "path to config file"
* @["config.yml"] * @["config.yml"]
# ^ other
run: run:
echo "hello from the main command" echo "hello from the main command"
echo fmt"{config=}, {check=}" echo fmt"{config=}, {check=}"