Skip to content

Commit 6d1abba

Browse files
committed
Revert reg_*_list() functions to use lists of lists rather than lists of vectors. (Lists allow multiple data types, while vectors coerce to a single type.)
1 parent afac76c commit 6d1abba

7 files changed

Lines changed: 109 additions & 107 deletions

File tree

R/parse_command_line.R

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77

88
pkg.globals <- new.env()
9+
pkg.globals$initialized <- FALSE
910

1011
#' Define an enum for different modes; access with argsType$<enum_element>
1112
#' ht https://stackoverflow.com/questions/33838392/enum-like-arguments-in-r
@@ -136,16 +137,20 @@ usage <- function() {
136137
#' @examples
137138
#' init_command_line_parser('MyCheckbook.R','My checkbook program', '1.0.0')
138139
init_command_line_parser <- function (script, desc, ver = NA) {
140+
pkg.globals$initialized <- TRUE
139141
pkg.globals$script <- script
140142
pkg.globals$desc_str <- desc
141143
pkg.globals$ver <- ver
142144
# tables to hold the possible command line params
143-
pkg.globals$args_table <- data.frame(lparam = c(NA,'--help'), sparam = c(NA,'-?'), var = c(NA,'help'),
144-
default = c(NA,FALSE), argType = c(NA,argsType$TypeBool),
145-
help = c(NA,"Display help message"), stringsAsFactors = FALSE)
145+
pkg.globals$args_table <- data.frame(lparam = NA, sparam = NA, var = NA, default = NA, argType = NA,
146+
help = NA, stringsAsFactors = FALSE)
146147
pkg.globals$cmds_table <- data.frame(cmd = NA, help = NA, stringsAsFactors = FALSE)
147148
pkg.globals$subcmds_table <- data.frame(subcmd = NA, parent = NA, help = NA, stringsAsFactors = FALSE)
148149

150+
# add a 'help' param
151+
reg_argument(lparam = '--help', sparam = '-?', var = 'help', default = FALSE, argType = argsType$TypeBool,
152+
help = 'Display help message')
153+
149154
} # init_command_line_parser
150155

151156

@@ -160,7 +165,8 @@ init_command_line_parser <- function (script, desc, ver = NA) {
160165
## help: help string for the param, for usage()
161166
##
162167
reg_command <- function(cmd, help = '') {
163-
if (is.na(pkg.globals$desc_str)) {
168+
# if (is.na(pkg.globals$desc_str)) {
169+
if (!pkg.globals$initialized) {
164170
stop("Error: reg_command(): Command line parser not initialized.", call. = FALSE)
165171
}
166172

@@ -175,24 +181,24 @@ reg_command <- function(cmd, help = '') {
175181

176182
#' Register commands using a list
177183
#'
178-
#' @param clist list of commands
184+
#' @param clist list of lists of commands: command, help string
179185
#'
180186
#' @export
181187
#'
182188
#' @examples
183189
#' cmds <- list(
184-
#' c("withdraw", "add a withdrawal"),
185-
#' c("plot", "graph output"),
186-
#' c("deposit", "add a deposit"),
187-
#' c("edit", "update a record"),
188-
#' c("find", "find a record")
190+
#' list("withdraw", "add a withdrawal"),
191+
#' list("plot", "graph output"),
192+
#' list("deposit", "add a deposit"),
193+
#' list("edit", "update a record"),
194+
#' list("find", "find a record")
189195
#' )
190196
#' reg_command_list(cmds)
191197
reg_command_list <- function(clist) {
192198
ids <- c("cmd","help")
193199
for (c in clist) {
194200
stopifnot(length(c) == length(ids))
195-
reg_command(cmd = c[1], help = c[2])
201+
reg_command(cmd = c[[1]], help = c[[2]])
196202
}
197203
} # reg_command_list
198204

@@ -209,7 +215,8 @@ reg_command_list <- function(clist) {
209215
## help: help string for the param, for usage()
210216
##
211217
reg_subcmd <- function(subcmd = subcmd, parent = parent, help = '') {
212-
if (is.na(pkg.globals$desc_str)) {
218+
# if (is.na(pkg.globals$desc_str)) {
219+
if (!pkg.globals$initialized) {
213220
stop("Error: reg_subcmd(): Command line parser not initialized.", call. = FALSE)
214221
}
215222

@@ -225,23 +232,23 @@ reg_subcmd <- function(subcmd = subcmd, parent = parent, help = '') {
225232

226233
#' Register subcommands using a list
227234
#'
228-
#' @param slist list of subcommands
235+
#' @param slist list of lists of subcommands: subcmd, parent, help string
229236
#'
230237
#' @export
231238
#'
232239
#' @examples
233240
#' subcmds <- list(
234-
#' c("paycheck", "deposit", "add a paycheck deposit"),
235-
#' c("reimbursement", "deposit", "add a reimbursement"),
236-
#' c("bankfee", "withdraw", "add a bank fee"),
237-
#' c("check", "deposit", "add a check deposit")
241+
#' list("paycheck", "deposit", "add a paycheck deposit"),
242+
#' list("reimbursement", "deposit", "add a reimbursement"),
243+
#' list("bankfee", "withdraw", "add a bank fee"),
244+
#' list("check", "deposit", "add a check deposit")
238245
#' )
239246
#' reg_subcmd_list(subcmds)
240247
reg_subcmd_list <- function(slist) {
241248
ids <- c("subcmd","parent","help")
242249
for (s in slist) {
243250
stopifnot(length(s) == length(ids))
244-
reg_subcmd(subcmd = s[1], parent = s[2], help = s[3])
251+
reg_subcmd(subcmd = s[[1]], parent = s[[2]], help = s[[3]])
245252
}
246253
} # reg_subcmd_list
247254

@@ -264,7 +271,8 @@ reg_subcmd_list <- function(slist) {
264271
## eg, "c("command1|subcmd1", "command2")
265272
##
266273
reg_argument <- function(lparam, sparam, var, default, argType, help) {
267-
if (is.na(pkg.globals$desc_str)) {
274+
# if (is.na(pkg.globals$desc_str)) {
275+
if (!pkg.globals$initialized) {
268276
stop("Error: reg_argument(): Command line parser not initialized.", call. = FALSE)
269277
}
270278

@@ -273,6 +281,11 @@ reg_argument <- function(lparam, sparam, var, default, argType, help) {
273281
stop(paste("Error: reg_argument(): duplicated param:", lparam, sparam), call. = FALSE)
274282
}
275283

284+
if (!argType %in% c(argsType$TypeBool, argsType$TypeValue, argsType$TypeMultiVal,
285+
argsType$TypeCount, argsType$TypeRange, argsType$TypePositional))
286+
stop(paste("Error: reg_argument(): invalid argType:", argType))
287+
if (argType == argsType$TypeBool) default <- as.logical(default)
288+
276289
my_df <- data.frame(lparam = lparam, sparam = sparam, var = var, default = default, argType = argType,
277290
help = help, stringsAsFactors = FALSE)
278291
pkg.globals$args_table <- rbind(pkg.globals$args_table, my_df)
@@ -281,51 +294,55 @@ reg_argument <- function(lparam, sparam, var, default, argType, help) {
281294

282295
#' Register command line arguments
283296
#'
284-
#' @param plist list of arguments
297+
#' @param plist list of lists of arguments: lparam, sparam, var, default, argType, help string
285298
#'
286299
#' @export
287300
#'
288301
#' @examples
289302
#' arguments <- list(
290-
#' c("--outfile","-o","outfile",NA,argsType$TypeValue,'location of output file'),
291-
#' c("--date","-d","date",NA,argsType$TypeValue,'specify date'),
292-
#' c("--msg","-m","msg",NA,argsType$TypeValue,'memo line message'),
293-
#' c("--amount","-a","amount",NA,argsType$TypeValue,'specify dollar amount'),
294-
#' c("--payee","-p","payee",NA,argsType$TypeValue,'specify payee'))
303+
#' list("--outfile","-o","outfile",NA,argsType$TypeValue,'location of output file'),
304+
#' list("--date","-d","date",NA,argsType$TypeValue,'specify date'),
305+
#' list("--msg","-m","msg",NA,argsType$TypeValue,'memo line message'),
306+
#' list("--amount","-a","amount",NA,argsType$TypeValue,'specify dollar amount'),
307+
#' list("--payee","-p","payee",NA,argsType$TypeValue,'specify payee'))
295308
#' reg_argument_list(arguments)
296309
reg_argument_list <- function(plist) {
297310
# scope is not required. So, check for the 6 required params, and if no scope provided, set to NA
298311
ids <- c("lparam","sparam","var","default","argType","help")
299312

300313
for (p in plist) {
301314
stopifnot (length(p) == length(ids))
302-
reg_argument (lparam = p[1], sparam = p[2], var = p[3], default = p[4],
303-
argType = p[5], help = p[6])
315+
reg_argument (lparam = p[[1]], sparam = p[[2]], var = p[[3]], default = p[[4]],
316+
argType = p[[5]], help = p[[6]])
304317
}
305318
} # reg_argument_list
306319

307320

308321
#
309322
# Register a 'positional' command line argument (ie, the last argument in the list)
310323
reg_positionals <- function(var, help) {
324+
if (!pkg.globals$initialized) {
325+
stop("Error: reg_positionals(): Command line parser not initialized.", call. = FALSE)
326+
}
327+
311328
reg_argument (lparam = NA, sparam = NA, var = var, default = NA, argType = argsType$TypePositional, help = help)
312329
} # reg_positionals
313330

314331

315332
#' Register a list of 'positional' arguments
316333
#'
317-
#' @param plist list of positional arguments: variable name, help text
334+
#' @param plist list of lists of positional arguments: variable name, help text
318335
#'
319336
#' @export
320337
#'
321338
#' @examples
322-
#' args <- list(c("infile","input file"))
339+
#' args <- list(list("infile","input file"))
323340
reg_positionals_list <- function(plist) {
324341
ids <- c("var","help")
325342

326343
for (p in plist) {
327344
stopifnot(length(p) == length(ids))
328-
reg_positionals(var = p[1], help = p[2])
345+
reg_positionals(var = p[[1]], help = p[[2]])
329346
}
330347
} # reg_positionals_list
331348

@@ -403,6 +420,10 @@ parse_date <- function(d) {
403420
#' # writeLines (paste("infile:", mydata$infile))
404421
#' # writeLines (paste("outfile:",mydata$outfile))
405422
parse_command_line <- function(args) {
423+
if (!pkg.globals$initialized) {
424+
stop("Error: parse_command_line(): Command line parser not initialized.", call. = FALSE)
425+
}
426+
406427
# remove the first line of the tables, which are all NA
407428
args_table <- pkg.globals$args_table[-1,]
408429
cmds_table <- pkg.globals$cmds_table[-1,]
@@ -413,7 +434,7 @@ parse_command_line <- function(args) {
413434
# if neither reg_arguments() nor reg_command() has been called, there's no table to process;
414435
# return the args as a list under the name 'unknowns'
415436
if (nrow(args_table) == 0 && nrow(cmds_table) == 0) {
416-
writeLines ("Warning: new_parse_command_line(): no cmdline params or commands registered.")
437+
writeLines ("Warning: parse_command_line(): no cmdline params or commands registered.")
417438
return (list(unknowns = args))
418439
}
419440

@@ -436,10 +457,10 @@ parse_command_line <- function(args) {
436457
mydata <- vector("list", nrow(args_table))
437458
names(mydata) <- args_table$var
438459
for (name in names(mydata)) {
439-
mydata[[name]] <- args_table$default[args_table$var == name]
440-
if (args_table$argType[args_table$var == name] == argsType$TypeBool) {
441-
mydata[[name]] <- as.logical(mydata[[name]])
442-
}
460+
myrow <- args_table[args_table$var == name,]
461+
mydata[[name]] <- myrow$default
462+
# ensure TypeBool is in fact a Bool
463+
if (myrow$argType == argsType$TypeBool) mydata[[name]] <- as.logical(mydata[[name]])
443464
}
444465

445466
# counter
@@ -610,37 +631,12 @@ parse_command_line <- function(args) {
610631
# ie, if more positionals provided than expected, copy the remainder into the last positional variable
611632
mydata[[myrow$var]] <- c(mydata[[myrow$var]], positionals[(i+1):length(positionals)])
612633
}
613-
# # if positional arguments are missing...
614-
# if (length(index) > length(positionals)) {
615-
# usage()
616-
# writeLines(paste0("parse_command_line(): one or more positional arguments missing"))
617-
# stop(call. = FALSE)
618-
# }
619-
# else if (length(index) == length(positionals)) {
620-
# for (i in seq_along(index)) {
621-
# myrow <- args_table[index[i],]
622-
# mydata[[myrow$var]] <- positionals[i]
623-
# }
624-
# }
625-
# else { # there are more positionals provided than required.
626-
# for (i in seq_along(index)) {
627-
# myrow <- args_table[index[i],]
628-
# mydata[[myrow$var]] <- positionals[i]
629-
# }
630-
# # copy the remaining values into the last positional argument
631-
# mydata[[myrow$var]] <- c(mydata[[myrow$var]], positionals[(i+1):length(positionals)])
632-
# }
633634
} # positionals
634635
return (mydata)
635636
} # new_parse_command_line
636637

637638

638639
# HELPER FUNCTIONS
639-
remove_dashes <- function(arg) {
640-
return (gsub('-', '', arg))
641-
} # remove_dashes
642-
643-
644640
is_lparam <- function(arg) {
645641
return (grepl('^--', arg))
646642
} # is_lparam

README.md

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,34 +48,35 @@ main <- function() {
4848

4949
cmds <- list(
5050
# command, help string
51-
c('add', 'Add something'),
52-
c('delete', 'Delete something')
51+
list('add', 'Add something'),
52+
list('delete', 'Delete something')
5353
)
5454
reg_command_list(cmds)
5555

5656
subcmds <- list(
5757
# subcommand, parent command, help string
58-
c('name','add','Add a name'),
59-
c('file','add','Add a file'),
60-
c('name','delete','Delete a name'),
61-
c('file','delete','Delete a file')
58+
list('name','add','Add a name'),
59+
list('file','add','Add a file'),
60+
list('name','delete','Delete a name'),
61+
list('file','delete','Delete a file')
6262
)
6363
reg_subcmd_list(subcmds)
6464

6565
args <- list(
6666
# long parameter form, short parameter form, variable name, default value, argument type, help string
67-
c('--config','-c','config','~/myconfigfile.txt',argsType$TypeValue,'Configuration file'),
68-
c('--debug','-d','debug',FALSE,argsType$TypeBool,'Display debug messages'),
69-
c('--keywords','-k','keywords',NA,argsType$TypeMultiVal,'Search keywords'),
70-
c('--daterange','-r','daterange',NA,argsType$TypeRange,'Date range'),
71-
c('--verbose','-v','verbose',0,argsType$TypeCount,'Verbosity level')
67+
list('--config','-c','config','~/myconfigfile.txt',argsType$TypeValue,'Configuration file'),
68+
list('--debug','-d','debug',FALSE,argsType$TypeBool,'Display debug messages'),
69+
list('--keywords','-k','keywords',NA,argsType$TypeMultiVal,'Search keywords'),
70+
list('--daterange','-r','daterange',NA,argsType$TypeRange,'Date range'),
71+
list('--username','-u','username',NA,argsType$TypeValue,'User name'),
72+
list('--verbose','-v','verbose',0,argsType$TypeCount,'Verbosity level')
7273
)
7374
reg_argument_list(args)
7475

7576
pos <- list(
7677
# variable name, help string
77-
c('outfile','Output filename'),
78-
c('infiles','Input filename(s)')
78+
list('outfile','Output filename'),
79+
list('infiles','Input filename(s)')
7980
)
8081
reg_positionals_list(pos)
8182

@@ -96,10 +97,10 @@ Rscript test_cmdparseR.R add name -dvvv -r 2020:2022 -z -k key1 -k key2 outfile.
9697

9798
you should see the following:
9899
```
99-
$ Rscript test_cmdparseR.R add name -dvvv -r 2020:2022 -z -k key1 -k key2 outfile.txt infile1.txt infile2.txt infile3.txt
100+
Rscript test_cmdparseR.R add name -dvvv -r 2020:2022 -z -k key1 -k key2 outfile.txt infile1.txt infile2.txt infile3.txt
100101
Warning: parse_command_line(): unknown param: -z
101102
$help
102-
[1] "FALSE"
103+
[1] FALSE
103104
104105
$config
105106
[1] "~/myconfigfile.txt"
@@ -113,6 +114,9 @@ $keywords
113114
$daterange
114115
[1] "2020:2022"
115116
117+
$username
118+
[1] NA
119+
116120
$verbose
117121
[1] 3
118122
@@ -171,6 +175,7 @@ test_cmdparseR: Test cmdparseR package
171175
--help (-?): Display help message
172176
default: FALSE
173177
--keywords (-k): Search keywords
178+
--username (-u): User name
174179
--verbose (-v): Verbosity level
175180
default: 0
176181
Error:

man/reg_argument_list.Rd

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)