Skip to content

parse/eval and character encoded expressions: How to deal with non-encoding strings?

3 messages · Johannes Graumann, William Dunlap

#
Hi,

I am intending to save a path-describing character object in a slot of a 
class I'm working on. In order to have the option to use "system.file" etc 
in such string-saved path definitions, I wrote this

ExpressionEvaluator <- function(x){
  x <- tryCatch(
    expr=base::parse(text=x),
    error = function(e){return(as.expression(x))},
    finally=TRUE)
  return(x)
}

This produces
expression(system.file("INDEX"))
[1] "/usr/lib/R/library/base/INDEX"

Which is what I want. However,
Error in eval(expr, envir, enclos) : object 'Test' not found

prevents me from general usage (also in cases where "x" does NOT encode an 
expression).

I don't understand why it is that
will return
[1] expression(Test)
while
produces
[1] expression("Test")
which would work with the eval call.

Can anyone point out to me how to solve this generally? How can I feed the 
function a character object and get back an eval-able expression independent 
of whether there was an expression "encoded" in the input or not.

Thank you for any hints.

Sincerely, Joh
#
Instead of saving a string that can be parsed into a language object (and
later evaluated), I would just save something that that could be evaluated
directly.  Note that a literal like "MyFile" or 3.14 evaluates to itself so you
can save a literal string or a language object and use eval() on either.  Unless
you do this there is no good way to know if "TestDir/TestFile*.txt" means to
do some multiplication and division or to use a glob pattern to find a group of files.
E.g.,

  makeFoo <- function(file, ...) {
      structure(c(...), fileExpr=substitute(file), class="Foo")
  }
  getFileFoo <- function(FooObject) {
      eval(attr(FooObject, "fileExpr"))
  }

used as

  > z <- makeFoo(system.file(package="fpp", "DESCRIPTION"), 1, 2, 3)
  > z
  [1] 1 2 3
  attr(,"fileExpr")
  system.file(package = "fpp", "DESCRIPTION")
  attr(,"class")
  [1] "Foo"
  > getFileFoo(z)
  [1] "C:/Program Files/R/R-2.15.2/library/fpp/DESCRIPTION"
or
  > z <- makeFoo("C:/Temp/File.txt", 1, 2, 3)
  > z
  [1] 1 2 3
  attr(,"fileExpr")
  [1] "C:/Temp/File.txt"
  attr(,"class")
  [1] "Foo"
  > getFileFoo(z)
  [1] "C:/Temp/File.txt"

One thing you might worry about is in which environment the language object is
evaluated (the envir= argument to eval()).  If you embed your object in a formula
then environment(formula) tells you where to evaluate it.  If the formula syntax
is distracting then you can attach an attribribute called ".Environment" to your object
that environment(object) will retrieve.  E.g.,

    makeFooEnv <- function(file, ..., envir = parent.frame()) {
         fileExpr <- structure(substitute(file), .Environment = envir)
         structure(c(...), fileExpr = fileExpr, class="Foo")
    }
    getFileFoo <- function(FooObject) {
         fileExpr <- attr(FooObject, "fileExpr")
         eval(fileExpr, envir=environment(fileExpr))
    }

used as
  > fooObjs <- lapply(1:3, function(i)makeFooEnv(paste0("File",i,".txt"), i^2))
  > fooObjs[[2]]
  [1] 4
  attr(,"fileExpr")
  paste0("File", i, ".txt")
  attr(,"fileExpr")attr(,".Environment")
  <environment: 0x000000000b7f8998>
  attr(,"class")
  [1] "Foo"
  > 
  > i <- 17
  > getFileFoo(fooObjs[[2]]) # Note that eval gets value of i as 2, not 17
  [1] "File2.txt"

Bill Dunlap
Spotfire, TIBCO Software
wdunlap tibco.com
#
I said
You can also use
   environment(object) <- envir
instead of
   object <- structure(object, .Environment = envir)
to set the ".Environment" attribute to envir.  This makes the code more
symmetric and you don't have remember the magic attribute name.

Bill Dunlap
Spotfire, TIBCO Software
wdunlap tibco.com