Nothing
# dot.R: functions to access dot arguments
# Stephen Milborrow Mar 2015 Durban
#
# TODO when match.call is fixed (R 3.2.1), remove the dots arg in all
# these funcs i.e. use the parent's dots
#-----------------------------------------------------------------------------
# dota() returns the value of the arg in dots that matches ARGNAME.
# Returns DEF if no match (default is NA).
# Issues an error message if multiple dot arguments match ARGNAME.
#
# ARGNAME must specify the full argument name (not abbreviated).
# ARGNAME can be a vector of argument names. Example:
# dotarg(c("name1", "name2"), ...)
# First we look for a dot arg matching the first name in the ARGNAME vector.
# If that fails we look for a match against the second name. And so on
# for further names in ARGNAME. If nothing matches, DEFAULT is returned.
# EXACT can also be a vector, with elements corresponding to the elements
# of ARGNAME. Example:
# dotarg(c("name1", "name2"), ..., EXACT=c(FALSE, TRUE))
#
# Common mistake: Using dotarg(xlab, ...) instead of dotarg("xlab", ...).
# The former usually causes the error message: object 'xlab' not found.
#
# If EX is TRUE then the name in dots must match ARGNAME exactly.
# If EX is FALSE match partial names in dots against ARGNAME following the
# standard R argname matching rules ("Argument Matching" in the R Language
# Definition). But here were are matching against only a single "formal"
# argument name, instead of all formal argnames simultaneously.
#
# NEW is currently unused (but will be for processing deprecated args).
# "NEW" is used instead of say "DEP" (for deprecated) so it is easily
# distinguishable from "DEF".
#
# Note that this function invokes eval to force the argument promise.
# The uppercase formal argnames prevent aliasing with names in dots.
#
# TODO I wanted to call this function dot but in base R there is
# already a function dot (plotmath).
dota <- function(ARGNAME, ..., DEF=NA, EX=TRUE, NEW=NA)
{
dots <- drop.unnamed.dots(match.call(expand.dots=FALSE)$...)
argname <- process.argname(ARGNAME)
exact <- process.exact(argname, EX)
new <- process.new(NEW, argname, deparse(substitute(DEF)))
for(i in seq_along(argname)) {
idot <- dotindex.aux(argname[i], dots, exact[i])
if(!anyNA(idot)) {
argval <- try(eval(dots[[idot]], parent.frame(1)))
if(is.try.err(argval))
stop0("cannot evaluate '", argname[i], "'")
dotname <- names(dots)[idot]
# TODO following commented out until we want to start
# issuing deprecated messages for earth and plotmo
# maybe.deprecate.arg(dotname, new, argname[i])
return(argval)
}
}
DEF
}
# Like dota() but default is existing value of ARGNAME.
# For example, dotd("xlab", ...) is equivalent to dota("xlab", DEF=xlab, ...).
# TODO add to test suite
dotd <- function(ARGNAME, ..., EX=TRUE)
{
if(is.dot("DEF", ...))
stop0("'DEF' cannot be used with dotd")
if(is.dot(ARGNAME, ..., EX=EX))
dota(ARGNAME, ..., EX=EX)
else # use the current value of ARGNAME as the default
eval(as.name(ARGNAME), parent.frame(1))
}
# Does a dot argument match ARGNAME? Return TRUE or FALSE, never NA.
# Issue an error message if there are multiple matches.
is.dot <- function(ARGNAME, ..., EX=TRUE)
{
dots <- drop.unnamed.dots(match.call(expand.dots=FALSE)$...)
argname <- process.argname(ARGNAME)
exact <- process.exact(argname, EX)
for(i in seq_along(argname))
if(!anyNA(dotindex.aux(argname[i], dots, exact[i])))
return(TRUE)
FALSE
}
# Return the index of the dot argname that matches ARGNAME.
# Return NA if no dot argument matches ARGNAME.
# Issue an error message if there are multiple matches.
dotindex <- function(ARGNAME, ..., EX=TRUE)
{
dots <- drop.unnamed.dots(match.call(expand.dots=FALSE)$...)
argname <- process.argname(ARGNAME)
exact <- process.exact(argname, EX)
for(i in seq_along(argname)) {
idot <- dotindex.aux(argname[i], dots, exact[i])
if(!anyNA(idot))
return(idot)
}
NA
}
drop.unnamed.dots <- function(dots)
{
dots[which(names(dots) == "")] <- NULL
dots
}
# allow comma or space separated argnames
# e.g. convert c("a", "b,c d") to c("a", "b", "c", "d")
process.argname <- function(argname)
{
stopifnot(is.character(argname))
argname <- gsub(" +|,+", ",", argname) # convert space or multi commas to comma
argname <- gsub("^,+|,+$", "", argname) # drop leading and trailing commas
if(any(!nzchar(argname)))
stop0("empty string in ARGNAME")
unlist(strsplit(argname, split=",")) # convert to a vector
}
process.exact <- function(argname, exact)
{
stopifnot(is.numeric(exact) || is.logical(exact),
all((exact == 0) | (exact == 1)))
if(length(exact) > length(argname))
stop0("length(EX)=", length(exact),
" is greater than length(ARGNAME)=", length(argname))
recycle(exact, argname)
}
process.new <- function(new, argname, defname) # returns NA or a string
{
if(anyNA(new))
return(NA)
if(is.numeric(new)) {
if(length(new) != 1)
stop0("length(NEW) != 1")
if(new < 0 || floor(new) != new)
stop0("NEW=", new, " is not allowed")
if(new == 0) {
if(!grepl("^[[:alnum:]._]+$", defname))
stop0("NEW=0 cannot be used when DEF=",
defname, " (not an identifier)")
# following helps prevent mistakes when e.g. defname=NA or NULL
if(grepl("^[A-Z]+$", defname)) # all upper case
stop0("NEW=0 cannot be used when DEF=", defname)
return(defname)
}
if(new > length(argname))
stop0("NEW=", new, " but length(ARGNAME) is only ", length(argname))
return(argname[new])
}
# new is a string
stopifnot.identifier(new, "NEW")
new
}
dotindex.aux <- function(argname, dots, exact=FALSE) # workhorse
{
stopifnot.identifier(argname, "ARGNAME")
if(length(dots) == 0)
return(NA)
# first look for an exact match
caller <- callers.name(n=2)
index <- which(argname == names(dots))
if(length(index) > 1) # multiple exact matches?
stop0("argument '", argname, "' for ", caller, "() is duplicated")
if(length(index) == 0) # no exact match
index <- NA
if(!anyNA(index) || exact)
return(index)
# look for a partial match
index <- which(!is.na(charmatch(names(dots), argname)))
if(length(index) == 0) # no match
return(NA)
if(length(index) == 1) # single match
return(index)
# length(index) > 1 multiple matches
stopifnot(all(index >= 0))
name1 <- names(dots)[index[1]]
name2 <- names(dots)[index[2]]
if(name1 == name2) # e.g. foo("abc", a=1, a=2)
stop0("argument '", name1, "' for ", caller, "() is duplicated")
# e.g. arguments 'a' and 'ab' both match 'abc' in foo()
stop0("arguments '", name1, "' and '", name2,
"' both match '", argname, "' in ", caller)
}
maybe.deprecate.arg <- function(dotname, new, argname)
{
if(is.specified(new) && argname != new) {
# require.period prevents a warning if user uses say a
# dot arg of plain 'col' when ARGNAME="pt.col col.pt col"
require.period <- grepl("\\.", argname)
if(!require.period || grepl("\\.", dotname))
warning0("'", dotname,
"' is deprecated, please use '", new, "' instead")
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.