diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java index 0727968f1c6d6eebc176930a48abba8e4ea1d66c..9fa877816e429b2dc84e80f0f560d7bdbc540dea 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java @@ -64,6 +64,8 @@ import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.Engine; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; +import com.oracle.truffle.r.runtime.data.RAttributable; +import com.oracle.truffle.r.runtime.data.RAttributes; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RLanguage; @@ -244,7 +246,7 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { sigNames[i - 1] = formal != null && formal.length() > 0 ? formal : null; } RLanguage result = RDataFactory.createLanguage(RASTUtils.createCall(fn, false, ArgumentsSignature.get(sigNames), arguments).asRNode()); - return result; + return addAttributes(result, list); } else if (repType == RLanguage.RepType.FUNCTION) { RList argsList = (RList) list.getDataAt(1); RSyntaxNode body = (RSyntaxNode) unwrapToRNode(list.getDataAt(2)); @@ -259,12 +261,20 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { RootCallTarget rootCallTarget = new RASTBuilder().rootFunction(RSyntaxNode.LAZY_DEPARSE, resArgs, body, null); FunctionExpressionNode fnExprNode = FunctionExpressionNode.create(RSyntaxNode.LAZY_DEPARSE, rootCallTarget); RLanguage result = RDataFactory.createLanguage(fnExprNode); - return result; + return addAttributes(result, list); } else { throw RInternalError.shouldNotReachHere("unexpected type"); } } + private static Object addAttributes(RAttributable result, RList list) { + RAttributes attrs = list.getAttributes(); + if (attrs != null && !attrs.isEmpty()) { + result.initAttributes(attrs.copy()); + } + return result; + } + private static RNode unwrapToRNode(Object objArg) { Object obj = objArg; // obj is RSymbol or a primitive value. diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R index f4544c4bb9c806ba2997506d0f8696e36a8b43e5..e0ebabafddce019be410544ca68bb4ccc6a5cf70 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R @@ -20,6 +20,8 @@ # The structure of the implementation somewhat reflects GnuR so that # it is easier to update this code, should the code in GnuR be changed. # +# Please run tests in modelTests.R when updating this file! +# # ================================================================ # R reimplementations of C utility functions @@ -128,7 +130,10 @@ InstallVar <- function(var) { return(index) } } - varlist <<- c(varlist, var) + + # as.list is necessary here until FastR is fixed not to + # cast c(symbol, language) to expressions, we need a list + varlist <<- as.list(c(varlist, var)) return(index + 1L) } @@ -143,13 +148,11 @@ ExtractVars <- function (formula, checkonly=FALSE) { v <- NULL if (is.symbol(formula)) { - haveDot <- identical(formula, quote(`.`)) + haveDot <<- identical(formula, quote(`.`)) if (!checkonly) { if (identical(formula, quote(`.`)) && !is.null(framenames)) { for (framename in framenames) { - if (!MatchVar(framename, varlist)) { - InstallVar(framename) - } + InstallVar(as.symbol(framename)) } } else { InstallVar(formula) @@ -215,16 +218,9 @@ CheckRHS <- function (v) { for (e in v) { CheckRHS(e) } - } - if (is.symbol(v)) { - for (i in seq_along(framenames)) { - framename <- framenames[[i]]; - # TODO is this check good enough? - # its a raw check in GNUR - if (identical(framename, v)) { - framenames <<- framenames[[-i]] - } - } + } else if (is.symbol(v)) { + vchar <- deparse(v) + framenames <<- framenames[framenames != vchar] } } AllocTerm <- function() { @@ -403,7 +399,25 @@ EncodeVars <- function(formula) { return(NULL) } else if (is.symbol(formula)) { if (identical(formula, quote(`.`)) && !is.null(framenames)) { - error("termsform: not implemented when formula='.' and there are framenames") + if (length(framenames) == 0L) { + return(NULL) + } + result <- vector("list", length(framenames)) + for (i in 1:length(framenames)) { + name <- framenames[[i]] + if (i > 1) { + for (j in 1:(i-1)) { + if (name == framenames[[j]]) { + error(paste0("duplicated name '", name, "' in data frame using '.'")) + } + } + } + idx <- InstallVar(as.symbol(name)) + term <- AllocTerm(); + term[[idx]] <- TRUE + result[[i]] <- term + } + return(result); } else { term <- AllocTerm() term[[InstallVar(formula)]] <- TRUE @@ -479,6 +493,22 @@ TermCode <- function(formula, callIdx, varIndex) { return(2L); } +# gets the formula as parameter and returns the same formula, where +# dot symbol is replaced with (a+b+c+...) where a,b,c.. are framenames. +ExpandDots <- function(x) { + if (is.symbol(x)) { + if (identical(x, quote(`.`))) { + return(parse(text=paste(framenames, collapse="+"))[[1]]) + } + return(x) + } + + for (i in seq_along(x)) { + x[[i]] <- ExpandDots(x[[i]]); + } + x +} + # PUBLIC: termsform # @@ -546,12 +576,10 @@ termsform <- function (x, specials, data, keep.order, allowDotAsName) { attr(x, "variables") <- vars # Note: GnuR uses bitvector of integers and variable nwords to denote its size, we do not need that - # EncodeVars may have stretched varlist becuase it is a global variable (to reflect GnuR's implementation) nvar <<- length(varlist) - formula <- EncodeVars(x) - # EncodeVars may have stretched the varlist global variable + # EncodeVars may have stretched varlist becuase it is a global variable (to reflect GnuR's implementation) nvar <<- length(varlist) # Step 2a: Compute variable names @@ -560,12 +588,13 @@ termsform <- function (x, specials, data, keep.order, allowDotAsName) { # Step 2b: Find and remove any offset(s) # first see if any of the variables are offsets - k <- sum(substr(varnames, 0, 7) == "offset(") - if (k > 0L) { - offsets <- integer(k) - # TODO remove the offset terms from the formula - error("termsform: not implemented - remove the offset terms from formula") - attr(x, "offset") <- offsets + offsets <- substr(varnames, 0, 7) == "offset(" + if (any(offsets)) { + indices <- which(offsets) + attr(x, "offset") <- indices + # remove the offset terms from the formula, that is terms that contain one of the offset vars + keepIndices = vapply(formula, function(f) !any(f[indices]), FALSE) + formula <- formula[which(keepIndices)] } nterm <<- length(formula); @@ -579,7 +608,7 @@ termsform <- function (x, specials, data, keep.order, allowDotAsName) { ord <- sCounts; } else { pattern <- formula # save original formula - callIdx <- 1L # on the top of the two loop below, we iterate through formula. In GnuR this is done with CDR + callIdx <- 1L ord <- integer(nterm) for (i in 0:bitmax) { for (n in 1:nterm) { @@ -622,15 +651,22 @@ termsform <- function (x, specials, data, keep.order, allowDotAsName) { attr(x, "factors") <- pattern if (!is.null(specials)) { - # TODO -- if there are specials stick them in here - error("termsform: not implemented when !is.null(specials)") + specialsAttr <- vector("pairlist", length(specials)) + names(specialsAttr) <- specials + for (i in 1:length(specials)) { + s <- specials[[i]] + indices <- substring(varnames, 0, nchar(s) + 1) == paste0(s, '('); + if (any(indices)) { + specialsAttr[[i]] <- which(indices); + } + } + attr(x, 'specials') <- specialsAttr } # Step 6: Fix up the formula by substituting for dot, which should be # the framenames joined by + if (haveDot) { - # TODO - error("termsform: not implemented when haveDot") + x <- ExpandDots(x) } attr(x, "order") <- ord diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java index d6946e21ab8292c4de09326bff5901cafe4a94a5..d192337e3792cb3a2d984f9081c02eaf12713e9d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java @@ -31,6 +31,8 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.unary.CastStringNode; import com.oracle.truffle.r.nodes.unary.CastStringNodeGen; import com.oracle.truffle.r.runtime.RBuiltin; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RStringVector; @@ -71,6 +73,8 @@ public abstract class UpdateNames extends RBuiltinNode { RAbstractContainer result = (RAbstractContainer) container.getNonShared(); if (stringVector.getLength() < result.getLength()) { stringVector = stringVector.copyResized(result.getLength(), true); + } else if (stringVector.getLength() > result.getLength()) { + throw RError.error(this, Message.NAMES_LONGER, stringVector.getLength(), result.getLength()); } else if (stringVector == container) { stringVector = (RStringVector) stringVector.copy(); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java index e8e0fa598c1fb7e9b2e142a17dfcd551a3b1dd7c..6d314f2c96ab4922cae488b2172ca3cfe206a09f 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java @@ -34,7 +34,7 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RBuiltin; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RType; -import com.oracle.truffle.r.runtime.data.model.RAbstractVector; +import com.oracle.truffle.r.runtime.data.RDataFactory; @RBuiltin(name = "vector", kind = INTERNAL, parameterNames = {"mode", "length"}) public abstract class Vector extends RBuiltinNode { @@ -50,7 +50,7 @@ public abstract class Vector extends RBuiltinNode { protected RType modeToType(String mode) { RType type = typeFromMode.execute(mode); - if (!type.isVector()) { + if (type != RType.PairList && !type.isVector()) { throw RError.error(this, RError.Message.CANNOT_MAKE_VECTOR_OF_MODE, mode); } return type; @@ -58,13 +58,23 @@ public abstract class Vector extends RBuiltinNode { @SuppressWarnings("unused") @Specialization(guards = {"mode == cachedMode"}, limit = CACHED_MODES_LIMIT) - RAbstractVector vectorCached(String mode, int length, @Cached("mode") String cachedMode, @Cached("modeToType(mode)") RType type) { - return type.create(length, false); + Object vectorCached(String mode, int length, @Cached("mode") String cachedMode, @Cached("modeToType(mode)") RType type) { + return createType(type, length); } @Specialization(contains = "vectorCached") @TruffleBoundary - protected RAbstractVector vector(String mode, int length) { - return modeToType(mode).create(length, false); + protected Object vector(String mode, int length) { + return createType(modeToType(mode), length); + } + + // Note: we have to handle RPairList separately. In other circumstances it is not seen as a + // vector, e.g. is.vector(vector('pairlist',1)) is FALSE, so we cannot just turn it into + // RAbstractVector. Note2: pair list of size == 0 is RNull -> we have to return Object. + private static Object createType(RType type, int length) { + if (type == RType.PairList) { + return RDataFactory.createPairList(length); + } + return type.create(length, false); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java index eaf18d8190e12304201d808d12206aa25a2efd3d..05330a7916efeaf31dc4e8eb49873fa2861afb3d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java @@ -42,6 +42,7 @@ import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RAttributeProfiles; +import com.oracle.truffle.r.runtime.data.RAttributes; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RExpression; import com.oracle.truffle.r.runtime.data.RLanguage; @@ -77,6 +78,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode { private final ConditionProfile valueIsNA = ConditionProfile.createBinaryProfile(); private final BranchProfile resizeProfile = BranchProfile.create(); private final BranchProfile sharedProfile = BranchProfile.create(); + private final ConditionProfile rlanguageAttributesProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile valueLengthOneProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile emptyReplacementProfile = ConditionProfile.createBinaryProfile(); @@ -187,6 +189,10 @@ final class CachedReplaceVectorNode extends CachedVectorNode { case Language: repType = RContext.getRRuntimeASTAccess().getRepType((RLanguage) castVector); vector = RContext.getRRuntimeASTAccess().asList((RLanguage) castVector); + RAttributes attrs = ((RLanguage) castVector).getAttributes(); + if (rlanguageAttributesProfile.profile(attrs != null && !attrs.isEmpty())) { + vector.initAttributes(attrs.copy()); + } break; case Expression: vector = ((RExpression) castVector).getList(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index cf51791fc2c4c9b84042df5f92cac319f784155d..7df6df646122ce7708b1d47e5ebf37e5451f7091 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -320,6 +320,7 @@ public final class RError extends RuntimeException { INVALID_UNNAMED_ARGUMENT("invalid argument"), INVALID_UNNAMED_VALUE("invalid value"), NAMES_NONVECTOR("names() applied to a non-vector"), + NAMES_LONGER("'names' attribute [%d] must be the same length as the vector [%d]"), ONLY_FIRST_VARIABLE_NAME("only the first element is used as variable name"), INVALID_FIRST_ARGUMENT("invalid first argument"), NO_ENCLOSING_ENVIRONMENT("no enclosing environment"), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalCode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalCode.java index a49ae23d32377e6e7cf4a45916e8bda0fa38ea02..c24bbb279baa2771219151833783a6ca05bb8a81 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalCode.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalCode.java @@ -58,7 +58,7 @@ public final class RInternalCode { public static Source loadSourceRelativeTo(Class<?> clazz, String fileName) { URL url = clazz.getResource("/" + clazz.getPackage().getName().replaceAll("\\.", "//") + "/" + fileName); try { - return Source.fromURL(url, fileName); + return Source.newBuilder(url).name(fileName).internal().build(); } catch (IOException e) { throw RInternalError.shouldNotReachHere(e, "Internal R script failed to load."); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java index 626678b772d10f6eef8125b82b1b581cc1e6c585..a50f0f393f346949f4cfa5da26c9dfbf0b8a5d96 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java @@ -436,6 +436,10 @@ public final class RDataFactory { return traceDataCreated(new RPromise.PromisedPromise(exprClosure, eagerValue, notChangedNonLocally, targetFrame, feedback)); } + public static Object createPairList(int size) { + return size == 0 ? RNull.instance : traceDataCreated(RPairList.create(size)); + } + public static RPairList createPairList() { return traceDataCreated(new RPairList()); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java index bc3fe353635825c7ea4c1a29f1243ce8e3efc234..f9fa07da42f2a84b0d2d7bc09c111a696e59cec3 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java @@ -39,7 +39,7 @@ import com.oracle.truffle.r.runtime.gnur.SEXPTYPE; * * {@code null} is never allowed as a value for the tag, car or cdr, only the type. */ -public class RPairList extends RAttributeStorage implements RAbstractContainer { +public class RPairList extends RSharingAttributeStorage implements RAbstractContainer { private Object car = RNull.instance; private Object cdr = RNull.instance; /** @@ -72,6 +72,21 @@ public class RPairList extends RAttributeStorage implements RAbstractContainer { this.type = type; } + /** + * Creates a new pair list of given size > 0. Note: pair list of size 0 is NULL. + */ + @TruffleBoundary + public static Object create(int size) { + assert size > 0 : "a pair list of size = 0 does not exist, it should be NULL"; + RPairList result = new RPairList(); + for (int i = 1; i < size; i++) { + RPairList tmp = result; + result = new RPairList(); + result.cdr = tmp; + } + return result; + } + @Override public RType getRType() { return RType.PairList; @@ -110,10 +125,14 @@ public class RPairList extends RAttributeStorage implements RAbstractContainer { for (int i = 0; i < len; i++) { data[i] = plt.car(); if (named) { - if (plt.isNullTag()) { + Object tag = plt.tag; + if (isNull(tag)) { names[i] = RRuntime.NAMES_ATTR_EMPTY_VALUE; + } else if (tag instanceof RSymbol) { + names[i] = ((RSymbol) tag).getName(); } else { - names[i] = ((RSymbol) plt.getTag()).getName(); + names[i] = RRuntime.asString(tag); + assert names[i] != null : "unexpected type of tag in RPairList"; } } if (i < len - 1) { @@ -236,7 +255,22 @@ public class RPairList extends RAttributeStorage implements RAbstractContainer { } @Override - public RVector getNonShared() { + public RShareable copy() { + RPairList result = new RPairList(); + Object original = this; + while (!isNull(original)) { + RPairList origList = (RPairList) original; + result.car = origList.car; + result.tag = origList.tag; + result.cdr = new RPairList(); + result = (RPairList) result.cdr; + original = origList.cdr; + } + return result; + } + + @Override + public RShareable deepCopy() { RInternalError.shouldNotReachHere(); return null; } @@ -285,27 +319,32 @@ public class RPairList extends RAttributeStorage implements RAbstractContainer { @Override public void setNames(RStringVector newNames) { - throw RInternalError.shouldNotReachHere(); + Object p = this; + for (int i = 0; i < newNames.getLength() && !isNull(p); i++) { + RPairList pList = (RPairList) p; + pList.tag = newNames.getDataAt(i); + p = pList.cdr; + } } @Override public RList getDimNames(RAttributeProfiles attrProfiles) { - throw RInternalError.shouldNotReachHere(); + return null; } @Override public void setDimNames(RList newDimNames) { - throw RInternalError.shouldNotReachHere(); + throw RInternalError.unimplemented(); } @Override public Object getRowNames(RAttributeProfiles attrProfiles) { - throw RInternalError.shouldNotReachHere(); + throw RInternalError.unimplemented(); } @Override public void setRowNames(RAbstractVector rowNames) { - throw RInternalError.shouldNotReachHere(); + throw RInternalError.unimplemented(); } @Override diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index dd74b118a4f09e8f68bc4e1d12825eeae1526b69..1b45dc72d1af750e73d4b2b8ec3d1ca16e7b9f98 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -19207,6 +19207,12 @@ Levels: A B C D E #tmp <- c(1,8,NA,3); pivot <- c(1,2,4,3); tmp[pivot] <- tmp; tmp [1] 1 8 3 NA +##com.oracle.truffle.r.test.builtins.TestBuiltin_extract_replace.replaceInLanguagePreservesAttributes +#f <- quote(a+b); attr(f, 'mya') <- 42; f[[2]] <- quote(q); f +q + b +attr(,"mya") +[1] 42 + ##com.oracle.truffle.r.test.builtins.TestBuiltin_factor.testFactor #{ as.logical(factor(c("a", "b", "a"))) } [1] NA NA NA @@ -30704,6 +30710,32 @@ NULL #argv <- list(structure(list(sec = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), min = c(0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), hour = c(20L, 19L, 19L, 19L, 19L, 19L, 19L, 19L, 19L, 20L, 20L, 20L, 20L, 19L, 19L, 19L, 20L, 20L, 20L, 19L, 20L, 19L, 19L, 19L, 20L), mday = c(30L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 30L, 30L, 30L, 30L, 31L, 31L, 31L, 30L, 30L, 30L, 31L, 30L, 31L, 31L, 31L, 30L), mon = c(5L, 11L, 11L, 11L, 11L, 11L, 11L, 11L, 11L, 5L, 5L, 5L, 5L, 11L, 11L, 11L, 5L, 5L, 5L, 11L, 5L, 11L, 11L, 11L, 5L), year = c(72L, 72L, 73L, 74L, 75L, 76L, 77L, 78L, 79L, 81L, 82L, 83L, 85L, 87L, 89L, 90L, 92L, 93L, 94L, 95L, 97L, 98L, 105L, 108L, 112L), wday = c(5L, 0L, 1L, 2L, 3L, 5L, 6L, 0L, 1L, 2L, 3L, 4L, 0L, 4L, 0L, 1L, 2L, 3L, 4L, 0L, 1L, 4L, 6L, 3L, 6L), yday = c(181L, 365L, 364L, 364L, 364L, 365L, 364L, 364L, 364L, 180L, 180L, 180L, 180L, 364L, 364L, 364L, 181L, 180L, 180L, 364L, 180L, 364L, 364L, 365L, 181L), isdst = c(1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 0L, 0L, 0L, 1L, 1L, 1L, 0L, 1L, 0L, 0L, 0L, 1L)), .Names = c('sec', 'min', 'hour', 'mday', 'mon', 'year', 'wday', 'yday', 'isdst'), class = c('POSIXlt', 'POSIXt'), tzone = c('', 'EST', 'EDT')));names(argv[[1]]); NULL +##com.oracle.truffle.r.test.builtins.TestBuiltin_namesassign.testUpdateDimnamesDifferentSize +#{ l <- list(1,2,3); names(l) <- list('a','b'); l } +$a +[1] 1 + +$b +[1] 2 + +$<NA> +[1] 3 + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_namesassign.testUpdateDimnamesDifferentSize +#{ l <- list(1,2,3); names(l) <- list('a','b','c','d'); l } +Error in names(l) <- list("a", "b", "c", "d") : + 'names' attribute [4] must be the same length as the vector [3] + +##com.oracle.truffle.r.test.builtins.TestBuiltin_namesassign.testUpdateDimnamesPairlist +#{ l <- vector('pairlist',2); names(l)<-c('a','b'); l; } +$a +NULL + +$b +NULL + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_namesassign.testUpdateNames #{ x <- 1:2 ; names(x) <- c("hello"); names(x) } [1] "hello" NA @@ -51113,6 +51145,22 @@ logical(0) ##com.oracle.truffle.r.test.builtins.TestBuiltin_vector.testVectorConstructor #{ x<-as.vector(3); y<-vector(length=x) } +##com.oracle.truffle.r.test.builtins.TestBuiltin_vector.testVectorWithPairlist +#vector('pairlist', 0) +NULL + +##com.oracle.truffle.r.test.builtins.TestBuiltin_vector.testVectorWithPairlist +#vector('pairlist', 3) +[[1]] +NULL + +[[2]] +NULL + +[[3]] +NULL + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_vector.testvector1 #argv <- list('integer', 0L); .Internal(vector(argv[[1]], argv[[2]])) integer(0) @@ -107352,6 +107400,92 @@ attr(,"assign") 15 2 2 99.190 16 2 2 16.000 +##com.oracle.truffle.r.test.library.stats.TestFormulae.testExpandDostsAndSpecialsTermsform +#f <- terms.formula(cyl~myfun(mpg)+., specials=c('myfun'), data=mtcars); attrs <- attributes(f); envIdx <- which(names(attrs)=='.Environment'); print(attrs[envIdx]); attrs[sort(names(attrs[-envIdx]))] +$.Environment +<environment: R_GlobalEnv> + +$class +[1] "terms" "formula" + +$factors + myfun(mpg) mpg disp hp drat wt qsec vs am gear carb +cyl 0 0 0 0 0 0 0 0 0 0 0 +myfun(mpg) 1 0 0 0 0 0 0 0 0 0 0 +mpg 0 1 0 0 0 0 0 0 0 0 0 +disp 0 0 1 0 0 0 0 0 0 0 0 +hp 0 0 0 1 0 0 0 0 0 0 0 +drat 0 0 0 0 1 0 0 0 0 0 0 +wt 0 0 0 0 0 1 0 0 0 0 0 +qsec 0 0 0 0 0 0 1 0 0 0 0 +vs 0 0 0 0 0 0 0 1 0 0 0 +am 0 0 0 0 0 0 0 0 1 0 0 +gear 0 0 0 0 0 0 0 0 0 1 0 +carb 0 0 0 0 0 0 0 0 0 0 1 + +$intercept +[1] 1 + +$order + [1] 1 1 1 1 1 1 1 1 1 1 1 + +$response +[1] 1 + +$specials +$specials$myfun +[1] 2 + + +$term.labels + [1] "myfun(mpg)" "mpg" "disp" "hp" "drat" + [6] "wt" "qsec" "vs" "am" "gear" +[11] "carb" + +$variables +list(cyl, myfun(mpg), mpg, disp, hp, drat, wt, qsec, vs, am, + gear, carb) + + +##com.oracle.truffle.r.test.library.stats.TestFormulae.testExpandDostsTermsform +#f <- terms.formula(cyl~hp*mpg+., data=mtcars); attrs <- attributes(f);envIdx <- which(names(attrs)=='.Environment'); print(attrs[envIdx]); attrs[sort(names(attrs[-envIdx]))] +$.Environment +<environment: R_GlobalEnv> + +$class +[1] "terms" "formula" + +$factors + hp mpg disp drat wt qsec vs am gear carb hp:mpg +cyl 0 0 0 0 0 0 0 0 0 0 0 +hp 1 0 0 0 0 0 0 0 0 0 1 +mpg 0 1 0 0 0 0 0 0 0 0 1 +disp 0 0 1 0 0 0 0 0 0 0 0 +drat 0 0 0 1 0 0 0 0 0 0 0 +wt 0 0 0 0 1 0 0 0 0 0 0 +qsec 0 0 0 0 0 1 0 0 0 0 0 +vs 0 0 0 0 0 0 1 0 0 0 0 +am 0 0 0 0 0 0 0 1 0 0 0 +gear 0 0 0 0 0 0 0 0 1 0 0 +carb 0 0 0 0 0 0 0 0 0 1 0 + +$intercept +[1] 1 + +$order + [1] 1 1 1 1 1 1 1 1 1 1 2 + +$response +[1] 1 + +$term.labels + [1] "hp" "mpg" "disp" "drat" "wt" "qsec" "vs" "am" + [9] "gear" "carb" "hp:mpg" + +$variables +list(cyl, hp, mpg, disp, drat, wt, qsec, vs, am, gear, carb) + + ##com.oracle.truffle.r.test.library.stats.TestFormulae.testModelFrame #{y<-0:9;z<-1:10;k<-2:11;w<-3:12;m<-4:13;u<-5:14;v<-6:15;; model.frame(terms.formula(u~z*k+w*m)) } u z k w m @@ -108326,6 +108460,50 @@ attr(,"contrasts")$z [1] "contr.treatment" +##com.oracle.truffle.r.test.library.stats.TestFormulae.testSpecialsTermsform +#f <- terms.formula(y~myfun(z)+x, c('myfun')); attrs <- attributes(f); envIdx <- which(names(attrs)=='.Environment'); print(attrs[envIdx]); attrs[sort(names(attrs[-envIdx]))] +$.Environment +<environment: R_GlobalEnv> + +$class +[1] "terms" "formula" + +$factors + myfun(z) x +y 0 0 +myfun(z) 1 0 +x 0 1 + +$intercept +[1] 1 + +$order +[1] 1 1 + +$response +[1] 1 + +$specials +$specials$myfun +[1] 2 + + +$term.labels +[1] "myfun(z)" "x" + +$variables +list(y, myfun(z), x) + + +##com.oracle.truffle.r.test.library.stats.TestFormulae.testSubsettingModelframe +#{x<-y<-1:10; model.frame.default(x~y, subset=3:7); } + x y +3 3 3 +4 4 4 +5 5 5 +6 6 6 +7 7 7 + ##com.oracle.truffle.r.test.library.stats.TestFormulae.testTermsform #f <- terms.formula(u~z*k+w*m); attrs <- attributes(f); envIdx <- which(names(attrs)=='.Environment'); print(attrs[envIdx]); attrs[sort(names(attrs[-envIdx]))] $.Environment diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_extract_replace.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_extract_replace.java index 42d60834f20231a063edd61aad795b547d0b39ec..f841282b012391086fc26223296274ea5b1128be 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_extract_replace.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_extract_replace.java @@ -37,4 +37,9 @@ public class TestBuiltin_extract_replace extends TestBase { public void extractAndReplaceByItself() { assertEval("tmp <- c(1,8,NA,3); pivot <- c(1,2,4,3); tmp[pivot] <- tmp; tmp"); } + + @Test + public void replaceInLanguagePreservesAttributes() { + assertEval("f <- quote(a+b); attr(f, 'mya') <- 42; f[[2]] <- quote(q); f"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_namesassign.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_namesassign.java index b34f10272333d7e7a691c19fd5c65e0f4baa5ec0..5c16751d89b35289fffe98a204517eb66b9a43f3 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_namesassign.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_namesassign.java @@ -112,8 +112,8 @@ public class TestBuiltin_namesassign extends TestBase { assertEval("{ x<-c(1, 2); names(x)<-c(\"a\", \"b\"); attr(x, \"names\")<-NULL; x }"); assertEval("{ x<-c(1, 2); names(x)<-42; x }"); assertEval("{ x<-c(1, 2); names(x)<-c(TRUE, FALSE); x }"); - assertEval(Output.IgnoreErrorContext, "{ x<-c(1,2); names(x) <- 42:44; x }"); - assertEval(Output.IgnoreErrorContext, "{ x<-c(1,2); attr(x, \"names\") <- 42:45; x }"); + assertEval("{ x<-c(1,2); names(x) <- 42:44; x }"); + assertEval("{ x<-c(1,2); attr(x, \"names\") <- 42:45; x }"); assertEval("{ x<-list(1,2); names(x)<-c(\"a\",NA); x }"); assertEval("{ x<-list(1,2); names(x)<-c(\"a\",\"$\"); x }"); assertEval("{ x<-list(1,2); names(x)<-c(\"a\",\"b\"); x }"); @@ -137,8 +137,19 @@ public class TestBuiltin_namesassign extends TestBase { assertEval("{ x <- c(1,2); names(x) <- c(\"A\", \"B\") ; x + 1 }"); assertEval("{ x <- 1:2; names(x) <- c(\"A\", \"B\") ; y <- c(1,2,3,4) ; names(y) <- c(\"X\", \"Y\", \"Z\") ; x + y }"); - assertEval(Output.IgnoreErrorContext, "{ x <- quote(plot(x = age, y = weight)); names(x)<- c(\"\", \"a\", \"b\", \"d\")}"); + assertEval("{ x <- quote(plot(x = age, y = weight)); names(x)<- c(\"\", \"a\", \"b\", \"d\")}"); assertEval("{ x <- quote(plot(x = age, y = weight)); names(x)<- c(\"\", \"a\", \"b\"); x}"); assertEval("{ x <- quote(plot(x = age, y = weight)); x$x <- \"random\"; x}"); } + + @Test + public void testUpdateDimnamesDifferentSize() { + assertEval("{ l <- list(1,2,3); names(l) <- list('a','b','c','d'); l }"); // longer + assertEval("{ l <- list(1,2,3); names(l) <- list('a','b'); l }"); // shorter + } + + @Test + public void testUpdateDimnamesPairlist() { + assertEval("{ l <- vector('pairlist',2); names(l)<-c('a','b'); l; }"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vector.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vector.java index 75ee509bceef16070ba1360ce239e12f1fd4c029..5e7c002f47917a4bb563a2971a52e36a7120e6d1 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vector.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vector.java @@ -66,4 +66,10 @@ public class TestBuiltin_vector extends TestBase { assertEval("{ vector(length=3) }"); assertEval("{ x<-as.vector(3); y<-vector(length=x) }"); } + + @Test + public void testVectorWithPairlist() { + assertEval("vector('pairlist', 0)"); + assertEval("vector('pairlist', 3)"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java index 6fa8a297e1e1959989bd8fde81d5d4e5e37733a0..deb60b64d75a815886b0db2f360e6c4c3489d4c1 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestFormulae.java @@ -84,4 +84,24 @@ public class TestFormulae extends TestBase { public void testModelMatrix() { assertEval(template("{%0; model.matrix(model.frame(terms.formula(%1))) }", INITS, FORMULAE)); } + + @Test + public void testSubsettingModelframe() { + assertEval("{x<-y<-1:10; model.frame.default(x~y, subset=3:7); }"); + } + + @Test + public void testSpecialsTermsform() { + assertEval("f <- terms.formula(y~myfun(z)+x, c('myfun')); attrs <- attributes(f); " + SHOW_ATTRS); + } + + @Test + public void testExpandDostsTermsform() { + assertEval("f <- terms.formula(cyl~hp*mpg+., data=mtcars); attrs <- attributes(f);" + SHOW_ATTRS); + } + + @Test + public void testExpandDostsAndSpecialsTermsform() { + assertEval("f <- terms.formula(cyl~myfun(mpg)+., specials=c('myfun'), data=mtcars); attrs <- attributes(f); " + SHOW_ATTRS); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R index 536fd3891d5f57cd0a4223f9768ad55d5917940c..6fad3bc8dbd278793259a4daa352a4f4e6a0e757 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R @@ -59,7 +59,7 @@ check <- function(expected, actual, name) { tests <- c(y~z, y~1+z, y~0+z, y~-1+z, y~z*k, y~z*k+w*m, u~z*k+w*m, y~z:k) tests <- c(tests, y~z^2, y~(z+k)^2, y~z*((m+w)^3), y~(z+k)*(w+u)) tests <- c(tests, y~w%in%v, y~w/k, y~(1 + w/k)) -ignoremm <- c(y~log(z), y~z+I(k+4)) +ignoremm <- c(y~log(z), y~z+I(k+4), y~z+I(k^2), y~z+offset(log(z))) ignoremf <- NULL tests <- c(tests, ignoremm) @@ -106,4 +106,17 @@ print(y~z) mf <- model.frame.default(y~z, subset=3:7) check(mf, modelframedefault(y~z, subset=3:7), "model.frame.default with subset") +# check specials +t <- y~myfun(z)+x +print(t) +check(terms.formula(t, c('myfun')), termsform(t, c('myfun'), NULL, FALSE, FALSE), "termsform with specials") +# check expand dots +t <- cyl~hp*mpg+. +print(t) +check(terms.formula(t, data=mtcars), termsform(t, NULL, mtcars, FALSE, FALSE), "termsform with expandDots") + +# check specials and expand dots +t <- cyl~mufun(mpg)+. +print(t) +check(terms.formula(t, specials=c('myfun'), data=mtcars), termsform(t, c('myfun'), mtcars, FALSE, FALSE), "termsform with specials and expandDots")