diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java index ec84baa9ed60d1161117164700bfa7e8d2c38e46..fe2eb079f49965a9bee08d91dbbc37b11f1d4a35 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java @@ -282,6 +282,7 @@ public final class REngine implements RContext.Engine { ConsoleHandler ch = singleton.context.getConsoleHandler(); ch.println("Unsupported specialization in node " + use.getNode().getClass().getSimpleName() + " - supplied values: " + Arrays.asList(use.getSuppliedValues()).stream().map(v -> v.getClass().getSimpleName()).collect(Collectors.toList())); + use.printStackTrace(); return null; } catch (RecognitionException | RuntimeException e) { singleton.context.getConsoleHandler().println("Exception while parsing: " + e); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java index 778bf01e2710667f148c82e83c991075bec38dfc..8d22b579121f52be4927c7d92137a1c215b342b5 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java @@ -87,6 +87,7 @@ public abstract class ConnectionFunctions { // TODO remove invisibility when print for RConnection works // TODO implement all open modes // TODO implement missing .Internal functions expected by connections.R + // TODO revisit the use of InputStream for internal use, e.g. in RSerialize /** * Base class for all {@link RConnection} instances. It supports lazy opening, as required by @@ -98,6 +99,8 @@ public abstract class ConnectionFunctions { * */ private abstract static class BaseRConnection extends RConnection { + private static final RStringVector DEFAULT_CLASSHR = RDataFactory.createStringVector(new String[]{"connection"}, RDataFactory.COMPLETE_VECTOR); + protected boolean isOpen; /** * if {@link #isOpen} is {@code true} the {@link OpenMode} that this connection is opened @@ -165,15 +168,32 @@ public abstract class ConnectionFunctions { return theConnection.getInputStream(); } + @Override + public void writeLines(RAbstractStringVector lines, String sep) throws IOException { + checkOpen(); + theConnection.writeLines(lines, sep); + } + + @Override + public void flush() throws IOException { + checkOpen(); + theConnection.flush(); + } + @Override public void close() throws IOException { isOpen = false; - theConnection.close(); + if (theConnection != null) { + theConnection.close(); + } } @Override + /** + * Must always respond to {@code inherits("connection")} even when not open. + */ public RStringVector getClassHierarchy() { - return classHr; + return classHr != null ? classHr : DEFAULT_CLASSHR; } /** @@ -196,6 +216,34 @@ public abstract class ConnectionFunctions { public RStringVector getClassHierarchy() { return base.getClassHierarchy(); } + } + + private abstract static class DelegateReadRConnection extends DelegateRConnection { + protected DelegateReadRConnection(BaseRConnection base) { + super(base); + } + + @Override + public void writeLines(RAbstractStringVector lines, String sep) throws IOException { + throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message); + } + + @Override + public void flush() { + // nothing to do + } + + } + + private abstract static class DelegateWriteRConnection extends DelegateRConnection { + protected DelegateWriteRConnection(BaseRConnection base) { + super(base); + } + + @Override + public String[] readLinesInternal(int n) throws IOException { + throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message); + } } @@ -210,6 +258,11 @@ public abstract class ConnectionFunctions { setClass("terminal"); } + @Override + public boolean isStdin() { + return true; + } + @Override @TruffleBoundary public String[] readLinesInternal(int n) throws IOException { @@ -281,6 +334,9 @@ public abstract class ConnectionFunctions { case Read: delegate = new FileReadTextRConnection(this); break; + case Write: + delegate = new FileWriteTextRConnection(this); + break; default: throw RError.nyi((SourceSection) null, "unimplemented open mode: " + mode); } @@ -288,12 +344,14 @@ public abstract class ConnectionFunctions { } } - private static class FileReadTextRConnection extends DelegateRConnection { + private static class FileReadTextRConnection extends DelegateReadRConnection { + private BufferedInputStream inputStream; private BufferedReader bufferedReader; FileReadTextRConnection(FileRConnection base) throws IOException { super(base); - bufferedReader = new BufferedReader(new FileReader(base.path)); + inputStream = new BufferedInputStream(new FileInputStream(base.path)); + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); } @TruffleBoundary @@ -314,7 +372,7 @@ public abstract class ConnectionFunctions { @Override public InputStream getInputStream() throws IOException { - throw new IOException(); + return inputStream; } @Override @@ -323,6 +381,40 @@ public abstract class ConnectionFunctions { } } + private static class FileWriteTextRConnection extends DelegateWriteRConnection { + private BufferedOutputStream outputStream; + private BufferedWriter bufferedWriter; + + FileWriteTextRConnection(FileRConnection base) throws IOException { + super(base); + outputStream = new BufferedOutputStream(new FileOutputStream(base.path)); + bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); + } + + @Override + public void writeLines(RAbstractStringVector lines, String sep) throws IOException { + for (int i = 0; i < lines.getLength(); i++) { + bufferedWriter.write(lines.getDataAt(i)); + bufferedWriter.write(sep); + } + } + + @Override + public InputStream getInputStream() throws IOException { + throw RInternalError.shouldNotReachHere(); + } + + @Override + public void close() throws IOException { + bufferedWriter.close(); + } + + @Override + public void flush() throws IOException { + bufferedWriter.flush(); + } + } + @RBuiltin(name = "file", kind = INTERNAL, parameterNames = {"description", "open", "blocking", "encoding", "raw"}) public abstract static class File extends RInvisibleBuiltinNode { @Specialization @@ -375,7 +467,7 @@ public abstract class ConnectionFunctions { } } - private static class GZIPInputRConnection extends DelegateRConnection { + private static class GZIPInputRConnection extends DelegateReadRConnection { private GZIPInputStream stream; GZIPInputRConnection(GZIPRConnection base) throws IOException { @@ -449,7 +541,7 @@ public abstract class ConnectionFunctions { } } - private static class TextReadRConnection extends DelegateRConnection { + private static class TextReadRConnection extends DelegateReadRConnection { private String[] lines; private int index; @@ -478,6 +570,12 @@ public abstract class ConnectionFunctions { return result; } + @SuppressWarnings("hiding") + @Override + public void writeLines(RAbstractStringVector lines, String sep) throws IOException { + throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message); + } + @Override public InputStream getInputStream() throws IOException { throw RInternalError.shouldNotReachHere(); @@ -552,6 +650,43 @@ public abstract class ConnectionFunctions { } } + @RBuiltin(name = "writeLines", kind = INTERNAL, parameterNames = {"text", "con", "sep", "useBytes"}) + public abstract static class WriteLines extends RInvisibleBuiltinNode { + @Specialization + @TruffleBoundary + protected RNull writeLines(RAbstractStringVector text, RConnection con, RAbstractStringVector sep, @SuppressWarnings("unused") byte useBytes) { + controlVisibility(); + try { + con.writeLines(text, sep.getDataAt(0)); + } catch (IOException x) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.ERROR_WRITING_CONNECTION, x.getMessage()); + } + return RNull.instance; + } + + @SuppressWarnings("unused") + @Fallback + protected RNull writeLines(Object text, Object con, Object sep, Object useBytes) { + controlVisibility(); + throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_UNNAMED_ARGUMENTS); + } + } + + @RBuiltin(name = "flush", kind = INTERNAL, parameterNames = {"con"}) + public abstract static class Flush extends RInvisibleBuiltinNode { + @TruffleBoundary + @Specialization + protected RNull flush(RConnection con) { + controlVisibility(); + try { + con.flush(); + } catch (IOException x) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.ERROR_FLUSHING_CONNECTION, x.getMessage()); + } + return RNull.instance; + } + } + @RBuiltin(name = "pushBack", kind = INTERNAL, parameterNames = {"data", "connection", "newLine", "type"}) public abstract static class PushBack extends RInvisibleBuiltinNode { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java index 5757cfe6ec0109e474c209212d850f55a12f2c46..41bb9c8adaf5989b8d7c706ea9a6a388d170e6b7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java @@ -22,13 +22,17 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import java.io.*; + import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.*; import com.oracle.truffle.r.nodes.access.*; import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.nodes.builtin.utils.CountFields; import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RError.Message; @@ -401,24 +405,6 @@ public class ForeignFunctions { return RDataFactory.createComplexVector(z, zVec.isComplete(), zVec.getDimensions()); } - /** - * This is an inefficient guard but it matters little unless there are many different calls - * being made within the same evaluation. A {@code NativeSymbolInfo} object would provide a - * more efficient basis. - */ - private static boolean matchName(RList f, String name) { - if (f.getNames() == RNull.instance) { - return false; - } - RStringVector names = (RStringVector) f.getNames(); - for (int i = 0; i < names.getLength(); i++) { - if (names.getDataAt(i).equals("name")) { - return f.getDataAt(i).equals(name) ? true : false; - } - } - return false; - } - public boolean fft(RList f) { return matchName(f, "fft"); } @@ -467,4 +453,208 @@ public class ForeignFunctions { } + /** + * This is an inefficient guard but it matters little unless there are many different calls + * being made within the same evaluation. A {@code NativeSymbolInfo} object would provide a more + * efficient basis. + */ + private static boolean matchName(RList f, String name) { + if (f.getNames() == RNull.instance) { + return false; + } + RStringVector names = (RStringVector) f.getNames(); + for (int i = 0; i < names.getLength(); i++) { + if (names.getDataAt(i).equals("name")) { + return f.getDataAt(i).equals(name) ? true : false; + } + } + return false; + } + + private static String isString(Object arg) { + if (arg instanceof String) { + return (String) arg; + } else if (arg instanceof RStringVector) { + if (((RStringVector) arg).getLength() == 0) { + return null; + } else { + return ((RStringVector) arg).getDataAt(0); + } + } else { + return null; + } + } + + private abstract static class CastAdapter extends RBuiltinNode { + @Child private CastLogicalNode castLogical; + @Child private CastIntegerNode castInt; + + protected byte castLogical(VirtualFrame frame, Object operand) { + if (castLogical == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + castLogical = insert(CastLogicalNodeFactory.create(null, false, false, false)); + } + return (byte) castLogical.executeCast(frame, operand); + } + + protected int castInt(VirtualFrame frame, Object operand) { + if (castInt == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + castInt = insert(CastIntegerNodeFactory.create(null, false, false, false)); + } + return (int) castInt.executeCast(frame, operand); + } + } + + @RBuiltin(name = ".External", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}) + public abstract static class DotExternal extends CastAdapter { + + // Transcribed from GnuR, library/utils/src/io.c + @SuppressWarnings("unused") + @TruffleBoundary + @Specialization(guards = "isCountFields") + protected Object countFields(VirtualFrame frame, RList f, RArgsValuesAndNames args, RMissing packageName) { + controlVisibility(); + Object[] argValues = args.getValues(); + RConnection conn = (RConnection) argValues[0]; + Object sepArg = argValues[1]; + char sepChar; + Object quoteArg = argValues[2]; + int nskip = castInt(frame, argValues[3]); + byte blskip = castLogical(frame, argValues[4]); + String commentCharArg = isString(argValues[5]); + char comChar; + if (!(commentCharArg != null && commentCharArg.length() == 1)) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "comment.char"); + } else { + comChar = commentCharArg.charAt(0); + } + + if (nskip < 0 || nskip == RRuntime.INT_NA) { + nskip = 0; + } + if (blskip == RRuntime.LOGICAL_NA) { + blskip = RRuntime.LOGICAL_TRUE; + } + + if (sepArg instanceof RNull) { + sepChar = 0; + } else { + String s = isString(sepArg); + if (s == null) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "sep"); + } else { + if (s.length() == 0) { + sepChar = 0; + } else { + sepChar = s.charAt(0); + } + } + } + String quoteSet; + if (quoteArg instanceof RNull) { + quoteSet = ""; + } else { + String s = isString(quoteArg); + if (s == null) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "invalid quote symbol set"); + } else { + quoteSet = s; + } + } + try { + return CountFields.execute(conn, sepChar, quoteSet, nskip, RRuntime.fromLogical(blskip), comChar); + } catch (IllegalStateException | IOException ex) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage()); + } + } + + public boolean isCountFields(RList f) { + return matchName(f, "countfields"); + } + } + + @RBuiltin(name = ".External2", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "..."}) + public abstract static class DotExternal2 extends CastAdapter { + // Transcribed from GnuR, library/utils/src/io.c + @TruffleBoundary + @Specialization(guards = "isWriteTable") + protected Object doWriteTable(VirtualFrame frame, RList f, RArgsValuesAndNames args) { + controlVisibility(); + Object[] argValues = args.getValues(); + Object x = argValues[0]; + Object con = argValues[1]; + if (!(con instanceof RConnection)) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "'file' is not a connection"); + } + // TODO check connection writeable + + int nr = castInt(frame, argValues[2]); + int nc = castInt(frame, argValues[3]); + Object rnamesArg = argValues[4]; + Object sepArg = argValues[5]; + Object eolArg = argValues[6]; + Object naArg = argValues[7]; + Object decArg = argValues[8]; + Object quoteArg = argValues[9]; + byte qmethod = castLogical(frame, argValues[10]); + + String sep; + String eol; + String na; + String dec; + + if (nr == RRuntime.INT_NA) { + invalidArgument("nr"); + } + if (nc == RRuntime.INT_NA) { + invalidArgument("nc"); + } + if (!(rnamesArg instanceof RNull) && isString(rnamesArg) == null) { + invalidArgument("rnames"); + } + if ((sep = isString(sepArg)) == null) { + invalidArgument("sep"); + } + if ((eol = isString(eolArg)) == null) { + invalidArgument("eol"); + } + if ((na = isString(naArg)) == null) { + invalidArgument("na"); + } + if ((dec = isString(decArg)) == null) { + invalidArgument("dec"); + } + if (qmethod == RRuntime.LOGICAL_NA) { + invalidArgument("qmethod"); + } + if (dec.length() != 1) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "'dec' must be a single character"); + } + char cdec = dec.charAt(0); + boolean[] quoteCol = new boolean[nc]; + boolean quoteRn = false; + RIntVector quote = (RIntVector) quoteArg; + for (int i = 0; i < quote.getLength(); i++) { + int qi = quote.getDataAt(i); + if (qi == 0) { + quoteRn = true; + } + if (qi > 0) { + quoteCol[qi - 1] = true; + } + } + // TODO call WriteTable + return RNull.instance; + } + + protected void invalidArgument(String name) throws RError { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, name); + } + + public boolean isWriteTable(RList f) { + return matchName(f, "writetable"); + } + } + } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java index ff282cbe1ff9c5a0e385de24d7796c5f4d71273b..de63f19076c91492e70bf0bdd03a898a3a2617b2 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java @@ -13,6 +13,7 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.runtime.RBuiltinKind.*; +import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.nodes.*; @@ -26,6 +27,9 @@ import com.oracle.truffle.r.runtime.data.model.*; public abstract class Lapply extends RBuiltinNode { @Child private CallInlineCacheNode callCache = CallInlineCacheNode.create(3); + @Child private Lapply dataFrameLapply; + + public abstract Object execute(VirtualFrame frame, RAbstractVector x, RFunction fun, RArgsValuesAndNames optionalArgs); @Override public RNode[] getParameterValues() { @@ -76,4 +80,13 @@ public abstract class Lapply extends RBuiltinNode { protected boolean argMissing(RAbstractVector x, RFunction fun, RArgsValuesAndNames optionalArg) { return optionalArg.length() == 1 && optionalArg.getValues()[0] == RMissing.instance; } + + @Specialization + protected Object lapply(VirtualFrame frame, RDataFrame x, RFunction fun, RArgsValuesAndNames optionalArgs) { + if (dataFrameLapply == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + dataFrameLapply = insert(LapplyFactory.create(new RNode[3], getBuiltin(), getSuppliedArgsNames())); + } + return dataFrameLapply.execute(frame, x.getVector(), fun, optionalArgs); + } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java index 5d11df287d82564acac5a77ad4591611d4951e17..0cb8efffbe8476bbedad9d82ca7b1f6a69def170 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java @@ -26,6 +26,7 @@ import static com.oracle.truffle.r.runtime.RBuiltinKind.*; import java.util.*; +import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; @@ -61,6 +62,7 @@ public abstract class Substitute extends RBuiltinNode { private Quote checkQuote() { if (quote == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); quote = insert(QuoteFactory.create(new RNode[1], getBuiltin(), getSuppliedArgsNames())); } return quote; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TypeConvert.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TypeConvert.java index 15e3fe0797bf2c52fba612ea87c133bb35702d9c..03ef8a58d23cd2fabfa762bd1268775a9ead3f8a 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TypeConvert.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TypeConvert.java @@ -165,7 +165,7 @@ public abstract class TypeConvert extends RBuiltinNode { RIntVector res = RDataFactory.createIntVector(data, complete); res.setAttr("levels", RDataFactory.createStringVector(levelsArray, RDataFactory.COMPLETE_VECTOR)); res.setAttr("class", RDataFactory.createStringVector("factor")); - return res; + return RDataFactory.createFactor(res); } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java index 69d3d49b10821db3909ce52d7e07a13a352c5264..9cc09ec1e4d84395cd5dea0798e1d1c1645ca325 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java @@ -102,6 +102,8 @@ public abstract class UpdateAttr extends RInvisibleBuiltinNode { return RVector.setClassAttr(resultVector, null, container.getElementClass() == RDataFrame.class ? container : null, container.getElementClass() == RFactor.class ? container : null); } else if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) { resultVector.setRowNames(null); + } else if (name.equals(RRuntime.LEVELS_ATTR_KEY)) { + resultVector.setLevels(null); } else if (resultVector.getAttributes() != null) { resultVector.getAttributes().remove(name); } @@ -140,6 +142,8 @@ public abstract class UpdateAttr extends RInvisibleBuiltinNode { return setClassAttrFromObject(resultVector, container, value, getEncapsulatingSourceSection()); } else if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) { resultVector.setRowNames(castVector(frame, value)); + } else if (name.equals(RRuntime.LEVELS_ATTR_KEY)) { + resultVector.setLevels(castVector(frame, value)); } else { // generic attribute resultVector.setAttr(name, value); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java index 2dac8d6ef4276fed1818457c4e39bf0d1a6321e7..e057dd8e85058fa8143487c7c56b1901aef861b5 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java @@ -179,11 +179,9 @@ public abstract class UpdateAttributes extends RInvisibleBuiltinNode { UpdateAttr.setClassAttrFromObject(resultVector, container, value, getEncapsulatingSourceSection()); } } else if (attrName.equals(RRuntime.ROWNAMES_ATTR_KEY)) { - if (value == RNull.instance) { - resultVector.setRowNames(null); - } else { - resultVector.setRowNames(castVector(virtualFrame, value)); - } + resultVector.setRowNames(castVector(virtualFrame, value)); + } else if (attrName.equals(RRuntime.LEVELS_ATTR_KEY)) { + resultVector.setLevels(castVector(virtualFrame, value)); } else { if (value == RNull.instance) { resultVector.removeAttr(attrName); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java index b39f7f345d3a1a6ac8eb05fbca3d8800073cea24..47e1c683d5fb66e1d58d4dc5f5020d9bef174a66 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java @@ -11,8 +11,11 @@ package com.oracle.truffle.r.nodes.builtin.base; +import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.nodes.builtin.RInvisibleBuiltinNode; +import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.RBuiltin; import com.oracle.truffle.r.runtime.RBuiltinKind; import com.oracle.truffle.r.runtime.data.*; @@ -24,6 +27,16 @@ import static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; // 2nd parameter is "value", but should not be matched against, so "" public abstract class UpdateLevels extends RInvisibleBuiltinNode { + @Child private CastToVectorNode castVector; + + private RAbstractVector castVector(VirtualFrame frame, Object value) { + if (castVector == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + castVector = insert(CastToVectorNodeFactory.create(null, false, false, false, false)); + } + return (RAbstractVector) castVector.executeObject(frame, value); + } + @Specialization @TruffleBoundary protected RAbstractVector updateLevels(RAbstractVector vector, @SuppressWarnings("unused") RNull levels) { @@ -34,11 +47,10 @@ public abstract class UpdateLevels extends RInvisibleBuiltinNode { } @Specialization - @TruffleBoundary - protected RAbstractVector updateLevels(RAbstractVector vector, Object levels) { + protected RAbstractVector updateLevels(VirtualFrame frame, RAbstractVector vector, Object levels) { controlVisibility(); RVector v = vector.materialize(); - v.setLevels(levels); + v.setLevels(castVector(frame, levels)); return v; } @@ -52,9 +64,9 @@ public abstract class UpdateLevels extends RInvisibleBuiltinNode { @Specialization @TruffleBoundary - protected RFactor updateLevels(RFactor factor, Object levels) { + protected RFactor updateLevels(VirtualFrame frame, RFactor factor, Object levels) { controlVisibility(); - factor.getVector().setLevels(levels); + factor.getVector().setLevels(castVector(frame, levels)); return factor; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/CountFields.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/CountFields.java new file mode 100644 index 0000000000000000000000000000000000000000..5f30642163f5dfc24a1089017606ad8b57401879 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/CountFields.java @@ -0,0 +1,292 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 1995-2012, The R Core Team + * Copyright (c) 2003, The R Foundation + * Copyright (c) 2014, Oracle and/or its affiliates + * + * All rights reserved. + */ +package com.oracle.truffle.r.nodes.builtin.utils; + +import java.io.*; + +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; + +// Transcribed from GnuR, library/utils/src/io.c + +// Checkstyle: stop +@SuppressWarnings("unused") +public class CountFields { + private static final int R_EOF = -1; + private static final int SCAN_BLOCKSIZE = 1000; + + private static class LocalData { + Object NAstrings; + boolean quiet; + int sepchar; /* = 0 *//* This gets compared to ints */ + char decchar = '.'; /* = '.' *//* This only gets compared to chars */ + String quoteset; /* = NULL */ + int comchar = 100000; /* = NO_COMCHAR */ + boolean ttyflag; /* = 0 */ + RConnection con; /* = NULL */ + boolean wasopen; /* = FALSE */ + boolean escapes; /* = FALSE */ + int save; /* = 0; */ + boolean isLatin1; /* = FALSE */ + boolean isUTF8; /* = FALSE */ + boolean skipNul; + byte[] convbuf = new byte[100]; + InputStream is; + + private void checkClose() { + if (wasopen) { + try { + con.close(); + } catch (IOException ex) { + // Ignore + } + } + } + + } + + public static Object execute(RConnection file, char sepChar, String quoteSet, int nskip, boolean blskip, char comChar) throws IOException { + LocalData data = new LocalData(); + data.sepchar = sepChar; + data.comchar = comChar; + data.quoteset = quoteSet; + data.is = file.getInputStream(); + + if (file.isStdin()) { + data.ttyflag = true; + throw new IOException("count.fields not implemented for stdin"); + } else { + data.ttyflag = false; + } + + data.save = 0; + int quote = 0; + int inquote = 0; + int nfields = 0; + int nlines = 0; + int blocksize = SCAN_BLOCKSIZE; + int[] ans = new int[blocksize]; + + while (true) { + int c = scanchar(inquote, data); + if (c == R_EOF) { + if (nfields != 0) { + ans[nlines] = nfields; + } else { + nlines--; + } + break; + } else if (c == '\n') { + if (inquote != 0) { + ans[nlines] = RRuntime.INT_NA; + nlines++; + } else if (nfields > 0 || !blskip) { + ans[nlines] = nfields; + nlines++; + nfields = 0; + inquote = 0; + } + if (nlines == blocksize) { + int[] bns = ans; + blocksize = 2 * blocksize; + ans = new int[blocksize]; + System.arraycopy(bns, 0, ans, 0, bns.length); + } + continue; + } else if (data.sepchar != 0) { + if (nfields == 0) + nfields++; + if (inquote != 0 && c == R_EOF) { + data.checkClose(); + throw new IllegalStateException("quoted string on line " + inquote + " terminated by EOF"); + } + if (inquote != 0 && c == quote) + inquote = 0; + else if (data.quoteset.indexOf(c) > 0) { + inquote = nlines + 1; + quote = c; + } + if (c == data.sepchar && inquote == 0) + nfields++; + } else if (!Rspace(c)) { + if (data.quoteset.indexOf(c) > 0) { + quote = c; + inquote = nlines + 1; + while ((c = scanchar(inquote, data)) != quote) { + if (c == R_EOF) { + data.checkClose(); + throw new IllegalStateException("quoted string on line " + inquote + " terminated by EOF"); + } else if (c == '\n') { + ans[nlines] = RRuntime.INT_NA; + nlines++; + if (nlines == blocksize) { + int[] bns = ans; + blocksize = 2 * blocksize; + ans = new int[blocksize]; + System.arraycopy(bns, 0, ans, 0, bns.length); + } + } + } + inquote = 0; + } else { + do { + // if (dbcslocale && btowc(c) == WEOF) + // scanchar2(&data); + c = scanchar(0, data); + } while (!Rspace(c) && c != R_EOF); + if (c == R_EOF) + c = '\n'; + unscanchar(c, data); + } + nfields++; + } + + } + /* + * we might have a character that was unscanchar-ed. So pushback if possible + */ +// if (data.save && !data.ttyflag && data.wasopen) { +// char line[2] = " "; +// line[0] = (char) data.save; +// con_pushback(data.con, FALSE, line); +// } + data.checkClose(); + + if (nlines < 0) { + return RNull.instance; + } + if (nlines == blocksize) { + return RDataFactory.createIntVector(ans, RDataFactory.COMPLETE_VECTOR); + } + + int[] bns = new int[nlines + 1]; + for (int i = 0; i <= nlines; i++) { + bns[i] = ans[i]; + } + return RDataFactory.createIntVector(bns, RDataFactory.COMPLETE_VECTOR); + + } + + private static int scanchar_raw(LocalData d) throws IOException { + int c = (d.ttyflag) ? -1 : d.is.read(); + if (c == 0) { + if (d.skipNul) { + do { + c = (d.ttyflag) ? -1 : d.is.read(); + } while (c == 0); + } + } + return c; + } + + private static void unscanchar(int c, LocalData d) throws IOException { + d.save = c; + } + + private static int scanchar(int inQuote, LocalData d) throws IOException { + int next; + if (d.save != 0) { + next = d.save; + d.save = 0; + } else { + next = scanchar_raw(d); + } + if (next == d.comchar && inQuote != 0) { + do { + next = scanchar_raw(d); + } while (next != '\n' && next != R_EOF); + } + if (next == '\\' && d.escapes) { + next = scanchar_raw(d); + if ('0' <= next && next <= '8') { + int octal = next - '0'; + if ('0' <= (next = scanchar_raw(d)) && next <= '8') { + octal = 8 * octal + next - '0'; + if ('0' <= (next = scanchar_raw(d)) && next <= '8') { + octal = 8 * octal + next - '0'; + } else { + unscanchar(next, d); + } + } else { + unscanchar(next, d); + } + next = octal; + } else { + switch (next) { + case 'a': + next = 7; + break; + case 'b': + next = '\b'; + break; + case 'f': + next = '\f'; + break; + case 'n': + next = '\n'; + break; + case 'r': + next = '\r'; + break; + case 't': + next = '\t'; + break; + case 'v': + next = 11; + break; + case 'x': { + int val = 0; + int i; + int ext; + for (i = 0; i < 2; i++) { + next = scanchar_raw(d); + if (next >= '0' && next <= '9') { + ext = next - '0'; + } else if (next >= 'A' && next <= 'F') { + ext = next - 'A' + 10; + } else if (next >= 'a' && next <= 'f') { + ext = next - 'a' + 10; + } else { + unscanchar(next, d); + break; + } + val = 16 * val + ext; + } + next = val; + } + break; + + default: + /* + * Any other char and even EOF escapes to itself, but we need to preserve \" + * etc inside quotes. + */ + if (inQuote != 0 && d.quoteset.indexOf(next) >= 0) { + unscanchar(next, d); + next = '\\'; + } + break; + } + } + } + return next; + } + + private static boolean Rspace(int c) { + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + return true; + } + // TODO locale + return false; + } + +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/init.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/init.R new file mode 100644 index 0000000000000000000000000000000000000000..6ab8943e8873938483938866c175b95ae86ce807 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/init.R @@ -0,0 +1,24 @@ +# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# object defined in the utils package describing native countfields function +C_countfields <- list(name="countfields") +C_writetable <- list(name="writetable") diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/readtable.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/readtable.R similarity index 100% rename from com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/readtable.R rename to com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/readtable.R diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/writetable.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/writetable.R new file mode 100644 index 0000000000000000000000000000000000000000..5ac109f11d248f39650fd8888fde93a52c325120 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/writetable.R @@ -0,0 +1,176 @@ +# File src/library/utils/R/write.table.R +# Part of the R package, http://www.R-project.org +# +# Copyright (C) 1995-2012 The R Core Team +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# A copy of the GNU General Public License is available at +# http://www.r-project.org/Licenses/ + +write.table <- + function (x, file = "", append = FALSE, quote = TRUE, sep = " ", + eol = "\n", na = "NA", dec = ".", row.names = TRUE, + col.names = TRUE, qmethod = c("escape", "double"), + fileEncoding = "") +{ + qmethod <- match.arg(qmethod) + if(is.logical(quote) && (length(quote) != 1L || is.na(quote))) + stop("'quote' must be 'TRUE', 'FALSE' or numeric") + ## quote column names unless quote == FALSE (see help). + quoteC <- if(is.logical(quote)) quote else TRUE + qset <- is.logical(quote) && quote + + if(!is.data.frame(x) && !is.matrix(x)) x <- data.frame(x) + + makeRownames <- isTRUE(row.names) + ## need col names if col.names is TRUE or NA + makeColnames <- is.logical(col.names) && !identical(FALSE, col.names) + if(is.matrix(x)) { + ## fix up dimnames as as.data.frame would + p <- ncol(x) + d <- dimnames(x) + if(is.null(d)) d <- list(NULL, NULL) + if(is.null(d[[1L]]) && makeRownames) d[[1L]] <- seq_len(nrow(x)) + if(is.null(d[[2L]]) && makeColnames && p > 0L) + d[[2L]] <- paste0("V", 1L:p) + if(qset) + quote <- if(is.character(x)) seq_len(p) else numeric() + } else { ## data.frame + if(qset) + quote <- if(length(x)) + which(unlist(lapply(x, function(x) + is.character(x) || is.factor(x)))) + else numeric() + ## fix up embedded matrix columns into separate cols: + if(any(sapply(x, function(z) length(dim(z)) == 2 && dim(z)[2L] > 1))) { + c1 <- names(x) + x <- as.matrix(x, rownames.force = makeRownames) + d <- dimnames(x) + if(qset) { + ord <- match(c1, d[[2L]], 0L) + quote <- ord[quote]; quote <- quote[quote > 0L] + } + } + else + d <- list(if(makeRownames) row.names(x), + if(makeColnames) names(x)) + p <- ncol(x) + } + nocols <- p == 0L + + if(is.logical(quote)) # must be false + quote <- NULL + else if(is.numeric(quote)) { + if(any(quote < 1L | quote > p)) + stop("invalid numbers in 'quote'") + } else + stop("invalid 'quote' specification") + + rn <- FALSE + rnames <- NULL + if(is.logical(row.names)) { + if(row.names) {rnames <- as.character(d[[1L]]); rn <- TRUE} + } else { + rnames <- as.character(row.names) + rn <- TRUE + if(length(rnames) != nrow(x)) + stop("invalid 'row.names' specification") + } + if(!is.null(quote) && rn) # quote the row names + quote <- c(0, quote) + + if(is.logical(col.names)) { + if(!rn && is.na(col.names)) + stop("'col.names = NA' makes no sense when 'row.names = FALSE'") + col.names <- if(is.na(col.names) && rn) c("", d[[2L]]) + else if(col.names) d[[2L]] else NULL + } else { + col.names <- as.character(col.names) + if(length(col.names) != p) + stop("invalid 'col.names' specification") + } + + if(file == "") file <- stdout() + else if(is.character(file)) { + file <- if(nzchar(fileEncoding)) + file(file, ifelse(append, "a", "w"), encoding = fileEncoding) + else file(file, ifelse(append, "a", "w")) + on.exit(close(file)) + } else if(!isOpen(file, "w")) { + open(file, "w") + on.exit(close(file)) + } + if(!inherits(file, "connection")) + stop("'file' must be a character string or connection") + + qstring <- # quoted embedded quote string + switch(qmethod, + "escape" = '\\\\"', + "double" = '""') + if(!is.null(col.names)) { + if(append) + warning("appending column names to file") + if(quoteC) + col.names <- paste("\"", gsub('"', qstring, col.names), + "\"", sep = "") + writeLines(paste(col.names, collapse = sep), file, sep = eol) + } + + if (nrow(x) == 0L) return(invisible()) + if (nocols && !rn) return(cat(rep.int(eol, NROW(x)), file=file, sep="")) + + ## convert list matrices to character - maybe not much use? + if(is.matrix(x) && !is.atomic(x)) mode(x) <- "character" + if(is.data.frame(x)) { + ## convert columns we can't handle in C code + x[] <- lapply(x, function(z) { + if(is.object(z) && !is.factor(z)) as.character(z) else z + }) + } + + invisible(.External2(C_writetable, x, file, nrow(x), p, rnames, sep, eol, + na, dec, as.integer(quote), qmethod != "double")) +} + +write.csv <- function(...) +{ + Call <- match.call(expand.dots = TRUE) + for(argname in c("append", "col.names", "sep", "dec", "qmethod")) + if(!is.null(Call[[argname]])) + warning(gettextf("attempt to set '%s' ignored", argname), + domain = NA) + rn <- eval.parent(Call$row.names) + Call$append <- NULL + Call$col.names <- if(is.logical(rn) && !rn) TRUE else NA + Call$sep <- "," + Call$dec <- "." + Call$qmethod <- "double" + Call[[1L]] <- as.name("write.table") + eval.parent(Call) +} + +write.csv2 <- function(...) +{ + Call <- match.call(expand.dots = TRUE) + for(argname in c("append", "col.names", "sep", "dec", "qmethod")) + if(!is.null(Call[[argname]])) + warning(gettextf("attempt to set '%s' ignored", argname), + domain = NA) + rn <- eval.parent(Call$row.names) + Call$append <- NULL + Call$col.names <- if(is.logical(rn) && !rn) TRUE else NA + Call$sep <- ";" + Call$dec <- "," + Call$qmethod <- "double" + Call[[1L]] <- as.name("write.table") + eval.parent(Call) +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java index 261a69b958f4e6f901aa0b54e1cc0bc0194b5bac..8f09abde45a867940c74e2b3971ac9a1328dbc5e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java @@ -158,6 +158,10 @@ public abstract class RNode extends Node { return RTypesGen.RTYPES.expectRDataFrame(execute(frame)); } + public RFactor executeRFactor(VirtualFrame frame) throws UnexpectedResultException { + return RTypesGen.RTYPES.expectRFactor(execute(frame)); + } + public RSymbol executeRSymbol(VirtualFrame frame) throws UnexpectedResultException { return RTypesGen.RTYPES.expectRSymbol(execute(frame)); } @@ -190,7 +194,7 @@ public abstract class RNode extends Node { return RTypesGen.RTYPES.expectRType(execute(frame)); } - public static boolean areSameLength(RAbstractVector a, RAbstractVector b) { + public static boolean areSameLength(RAbstractContainer a, RAbstractContainer b) { return a.getLength() == b.getLength(); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java index 35d65aaf9e88ea41afe39e5cd0d9b0d8fb455df9..a6b85084e2b0933f0ebfad322aa1ce9c45c349c4 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java @@ -60,6 +60,8 @@ public abstract class ConstantNode extends RNode implements VisibilityController return new ConstantVectorNode((RAbstractVector) value); } else if (value instanceof RDataFrame) { return new ConstantDataFrameNode((RDataFrame) value); + } else if (value instanceof RFactor) { + return new ConstantFactorNode((RFactor) value); } else if (value instanceof RRaw) { return new ConstantRawNode((RRaw) value); } else if (value instanceof RFunction) { @@ -304,6 +306,27 @@ public abstract class ConstantNode extends RNode implements VisibilityController } } + private static final class ConstantFactorNode extends ConstantNode { + + private final RFactor factor; + + public ConstantFactorNode(RFactor factor) { + this.factor = factor; + } + + @Override + public RFactor executeRFactor(VirtualFrame frame) { + controlVisibility(); + return factor; + } + + @Override + public Object execute(VirtualFrame frame) { + controlVisibility(); + return factor; + } + } + private static final class ConstantRawNode extends ConstantNode { private final RRaw data; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/CoerceVector.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/CoerceVector.java index b0185087e870bf218343d9f90616e9b536aed93c..eec8bfc66da0e9e94c01b80404bb1c5bad7d1e68 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/CoerceVector.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/CoerceVector.java @@ -42,6 +42,15 @@ public abstract class CoerceVector extends RNode { @Child private CastIntegerNode castInteger; @Child private CastStringNode castString; @Child private CastListNode castList; + @Child private CoerceVector coerceRecursive; + + private Object coerceRecursive(VirtualFrame frame, Object value, Object vector, Object operand) { + if (coerceRecursive == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + coerceRecursive = insert(CoerceVectorFactory.create(null, null, null)); + } + return coerceRecursive.executeEvaluated(frame, value, vector, operand); + } private Object castComplex(VirtualFrame frame, Object vector) { if (castComplex == null) { @@ -309,6 +318,13 @@ public abstract class CoerceVector extends RNode { return (RList) castList(frame, vector); } + // factor value + + @Specialization + protected Object coerce(VirtualFrame frame, RFactor value, RAbstractVector vector, Object operand) { + return coerceRecursive(frame, value.getVector(), vector, operand); + } + // function vector value @Specialization @@ -338,7 +354,7 @@ public abstract class CoerceVector extends RNode { return vector; } - protected boolean isVectorList(RAbstractVector value, RAbstractVector vector) { + protected boolean isVectorList(RAbstractContainer value, RAbstractVector vector) { return vector.getElementClass() == Object.class; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java index 6c714e11d7dd08946f3773235c584862beabdd21..6a1cfd0c93db3532420a91ca67ee01e3931542c6 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java @@ -177,6 +177,11 @@ public abstract class UpdateArrayHelperNode extends RNode { return CastToContainerNodeFactory.create(child, false, false, false, true); } + @Specialization + protected Object update(VirtualFrame frame, Object v, RFactor value, int recLevel, Object positions, Object vector) { + return updateRecursive(frame, v, value.getVector(), vector, positions, recLevel); + } + @Specialization(guards = "emptyValue") protected RAbstractVector update(Object v, RAbstractVector value, int recLevel, Object[] positions, RAbstractVector vector) { if (isSubset) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java index b80217f68c4c03d3253c78571d06e6d9d8bb2f5d..d8c732f9d642aef5e1f54539446aa27a8299a6a0 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java @@ -701,6 +701,78 @@ public abstract class BinaryBooleanNode extends RBuiltinNode { throw RError.error(getEncapsulatingSourceSection(), RError.Message.NON_CONFORMABLE_ARRAYS); } + // factor and scalar + + @Specialization(guards = "!isEq") + protected RLogicalVector doFactorOp(RFactor left, Object right) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.NOT_MEANINGFUL_FOR_FACTORS, logic.opName()); + } + + @Specialization(guards = "!isEq") + protected RLogicalVector doFactorOp(Object left, RFactor right) { + throw RError.error(getEncapsulatingSourceSection(), RError.Message.NOT_MEANINGFUL_FOR_FACTORS, logic.opName()); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RFactor left, int right) { + return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.intToString(right, false), false); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(int left, RFactor right) { + return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.intToString(left, false), true); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RFactor left, double right) { + return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.doubleToString(right), false); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(double left, RFactor right) { + return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.doubleToString(left), true); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RFactor left, byte right) { + return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.logicalToString(right), false); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(byte left, RFactor right) { + return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.logicalToString(left), false); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RFactor left, String right) { + return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), right, false); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(String left, RFactor right) { + return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), left, true); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RFactor left, RComplex right) { + return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.complexToString(right), false); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RComplex left, RFactor right) { + return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.complexToString(left), true); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RFactor left, RRaw right) { + return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.rawToString(right), false); + } + + @Specialization(guards = "isEq") + protected RLogicalVector doFactorOp(RRaw left, RFactor right) { + return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.rawToString(left), true); + } + protected static boolean differentDimensions(RAbstractVector left, RAbstractVector right) { if (!left.hasDimensions() || !right.hasDimensions()) { return false; @@ -791,6 +863,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode { return performStringVectorOpSameLength(left, RClosures.createIntToStringVector(right, rightNACheck)); } + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doIntVectorDifferentLength(RAbstractIntVector left, RFactor right) { + return performStringVectorOpDifferentLength(RClosures.createIntToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doIntVectorSameLength(RAbstractIntVector left, RFactor right) { + return performStringVectorOpSameLength(RClosures.createIntToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doIntVectorDifferentLength(RFactor left, RAbstractIntVector right) { + return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createIntToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doIntVectorSameLength(RFactor left, RAbstractIntVector right) { + return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createIntToStringVector(right, rightNACheck)); + } + @Specialization(guards = "!areSameLength") protected RLogicalVector doIntVectorDifferentLength(RAbstractIntVector left, RComplexVector right) { return performComplexVectorOpDifferentLength(RClosures.createIntToComplexVector(left, leftNACheck), right); @@ -883,6 +975,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode { return performStringVectorOpSameLength(left, RClosures.createDoubleToStringVector(right, rightNACheck)); } + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doDoubleVectorDifferentLength(RAbstractDoubleVector left, RFactor right) { + return performStringVectorOpDifferentLength(RClosures.createDoubleToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doDoubleVectorSameLength(RAbstractDoubleVector left, RFactor right) { + return performStringVectorOpSameLength(RClosures.createDoubleToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doDoubleVectorDifferentLength(RFactor left, RAbstractDoubleVector right) { + return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createDoubleToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doDoubleVectorSameLength(RFactor left, RAbstractDoubleVector right) { + return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createDoubleToStringVector(right, rightNACheck)); + } + @Specialization(guards = "!areSameLength") protected RLogicalVector doDoubleVectorDifferentLength(RAbstractDoubleVector left, RComplexVector right) { return performComplexVectorOpDifferentLength(RClosures.createDoubleToComplexVector(left, leftNACheck), right); @@ -955,6 +1067,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode { return performStringVectorOpSameLength(left, RClosures.createLogicalToStringVector(right, rightNACheck)); } + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doLogicalVectorDifferentLength(RAbstractLogicalVector left, RFactor right) { + return performStringVectorOpDifferentLength(RClosures.createLogicalToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doLogicalVectorSameLength(RAbstractLogicalVector left, RFactor right) { + return performStringVectorOpSameLength(RClosures.createLogicalToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doLogicalVectorDifferentLength(RFactor left, RAbstractLogicalVector right) { + return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createLogicalToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doLogicalVectorSameLength(RFactor left, RAbstractLogicalVector right) { + return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createLogicalToStringVector(right, rightNACheck)); + } + @Specialization(guards = "!areSameLength") protected RLogicalVector doLogicalVectorDifferentLength(RAbstractLogicalVector left, RComplexVector right) { return performComplexVectorOpDifferentLength(RClosures.createLogicalToComplexVector(left, leftNACheck), right); @@ -1007,6 +1139,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode { return performStringVectorOpSameLength(left, right); } + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doStringVectorDifferentLength(RStringVector left, RFactor right) { + return performStringVectorOpDifferentLength(left, RClosures.createFactorToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doStringVectorSameLength(RStringVector left, RFactor right) { + return performStringVectorOpSameLength(left, RClosures.createFactorToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doStringVectorDifferentLength(RFactor left, RStringVector right) { + return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), right); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doStringVectorSameLength(RFactor left, RStringVector right) { + return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), right); + } + @Specialization(guards = "!areSameLength") protected RLogicalVector doStringVectorDifferentLength(RStringVector left, RAbstractComplexVector right) { return performStringVectorOpDifferentLength(left, RClosures.createComplexToStringVector(right, rightNACheck)); @@ -1047,6 +1199,58 @@ public abstract class BinaryBooleanNode extends RBuiltinNode { return performStringVectorOpSameLength(RClosures.createRawToStringVector(left, leftNACheck), right); } + // factor and vectors + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doStringVectorDifferentLength(RFactor left, RFactor right) { + return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createFactorToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doStringVectorSameLength(RFactor left, RFactor right) { + return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createFactorToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doStringVectorDifferentLength(RFactor left, RAbstractComplexVector right) { + return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createComplexToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doStringVectorSameLength(RFactor left, RAbstractComplexVector right) { + return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createComplexToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doStringVectorDifferentLength(RAbstractComplexVector left, RFactor right) { + return performStringVectorOpDifferentLength(RClosures.createComplexToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doStringVectorSameLength(RAbstractComplexVector left, RFactor right) { + return performStringVectorOpSameLength(RClosures.createComplexToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doStringVectorDifferentLength(RFactor left, RRawVector right) { + return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createRawToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doStringVectorSameLength(RFactor left, RRawVector right) { + return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createRawToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"!areSameLength", "isEq"}) + protected RLogicalVector doStringVectorDifferentLengthRRawVector(RRawVector left, RFactor right) { + return performStringVectorOpDifferentLength(RClosures.createRawToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck)); + } + + @Specialization(guards = {"areSameLength", "isEq"}) + protected RLogicalVector doStringVectorSameLengthRRawVector(RRawVector left, RFactor right) { + return performStringVectorOpSameLength(RClosures.createRawToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck)); + } + // complex vector and vectors @Specialization(guards = "!areSameLength") @@ -1134,6 +1338,18 @@ public abstract class BinaryBooleanNode extends RBuiltinNode { // guards + public boolean isEq(RFactor left, RFactor right) { + return logic instanceof BinaryCompare.Equal || logic instanceof BinaryCompare.NotEqual; + } + + public boolean isEq(RFactor left, Object right) { + return logic instanceof BinaryCompare.Equal || logic instanceof BinaryCompare.NotEqual; + } + + public boolean isEq(Object left, RFactor right) { + return !(logic instanceof BinaryCompare.Equal || logic instanceof BinaryCompare.NotEqual); + } + private boolean isVectorizedLogicalOp() { return !(logic instanceof BinaryLogic.And || logic instanceof BinaryLogic.Or); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java index cb3adaf4e8cfe44e27deacdac1635572ecd0e66b..5e51f4a7de834dfd42884873d1abd88e21727c1d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java @@ -95,6 +95,8 @@ public final class FunctionDefinitionNode extends RRootNode { return ex.getResult(); } finally { if (onExitProfile.profile(onExitSlot != null && onExitSlot.hasValue(vf))) { + // Must preserve the visibility state as it may be changed by the on.exit expression + boolean isVisible = RContext.isVisible(); ArrayList<Object> current = getCurrentOnExitList(vf, onExitSlot.executeFrameSlot(vf)); for (Object expr : current) { if (!(expr instanceof RNode)) { @@ -103,6 +105,7 @@ public final class FunctionDefinitionNode extends RRootNode { RNode node = (RNode) expr; onExitExpressionCache.execute(vf, node); } + RContext.setVisible(isVisible); } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java index 5b03886ca5b28b2c524d194c7b2934a84730775c..85cd6acc908fe39553b4105b6070163be01fd21f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java @@ -72,6 +72,11 @@ public abstract class CastToContainerNode extends CastNode { return dataFrame; } + @Specialization + protected RFactor cast(RFactor factor) { + return factor; + } + @Specialization protected RExpression cast(RExpression expression) { return expression; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RConnection.java index bd1343aea27bd569f0b2b6e6557b60f8b98f8694..760444abf70eb99ff0857f3f95f4984de75518e5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RConnection.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RConnection.java @@ -115,6 +115,13 @@ public abstract class RConnection implements RClassHierarchy { } } + /** + * Returns {@code true} if this is the "stdin" connection. + */ + public boolean isStdin() { + return false; + } + /** * Return the underlying input stream (for internal use). */ @@ -163,4 +170,12 @@ public abstract class RConnection implements RClassHierarchy { pushBack = null; } + /** + * Write the {@code lines} to the connection, with {@code sep} appended after each "line". N.B. + * The output will only appear as a sequence of lines if {@code sep == "\n"}. + */ + public abstract void writeLines(RAbstractStringVector lines, String sep) throws IOException; + + public abstract void flush() throws IOException; + } 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 2e7305b88d64ee05f7369d610f0a48c5685742ef..c5ad5fba8bd9cd6ff01f9fa949957950c2136c28 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 @@ -502,6 +502,8 @@ public final class RError extends RuntimeException { CANNOT_ASSIGN_IN_EMPTY_ENV("cannot assign values in the empty environment"), CANNOT_OPEN_CONNECTION("cannot open the connection"), ERROR_READING_CONNECTION("error reading connection: %s"), + ERROR_WRITING_CONNECTION("error writing connection: %s"), + ERROR_FLUSHING_CONNECTION("error flushing connection: %s"), NO_ITEM_NAMED("no item named '%s' on the search list"), INVALID_OBJECT("invalid object for 'as.environment'"), EMPTY_NO_PARENT("the empty environment has no parent"), @@ -553,7 +555,9 @@ public final class RError extends RuntimeException { FIRST_ELEMENT_USED("first element used of '%s' argument"), MUST_BE_COERCIBLE_INTEGER("argument must be coercible to non-negative integer"), DEFAULT_METHOD_NOT_IMPLEMENTED_FOR_TYPE("default method not implemented for type '%s'"), - ADDING_INVALID_CLASS("adding class \"%s\" to an invalid object"); + ADDING_INVALID_CLASS("adding class \"%s\" to an invalid object"), + IS_NA_TO_NON_VECTOR("is.na() applied to non-(list or vector) of type '%s'"), + NOT_MEANINGFUL_FOR_FACTORS("%s not meaningful for factors"); public final String message; private final boolean hasArgs; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java index 6579249bf68bad3afb2bea4b37d09f7ba368ad27..f4a3e81da5c94fa07805b1fa1f5d3920c82baa19 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java @@ -214,6 +214,7 @@ public abstract class RVector extends RBounded implements RShareable, RAbstractV } } + @TruffleBoundary public final void setLevels(Object newLevels) { if (attributes != null && newLevels == null) { // whether it's one dimensional array or not, assigning null always removes the "Levels" diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java index 2dfd14c197005f9bad72fd05df1c43567eb8d944..32b7d1c26275edecf13b3f7f6db3ce9af049c68e 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java @@ -23,6 +23,7 @@ package com.oracle.truffle.r.runtime.data.closures; import com.oracle.truffle.r.runtime.ops.na.NACheck; +import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; public class RClosures { @@ -101,4 +102,10 @@ public class RClosures { return new RComplexToStringVectorClosure(vector, check); } + // Factor to + + public static RAbstractStringVector createFactorToStringVector(RFactor factor, NACheck check) { + return new RFactorToStringVectorClosure(factor, check); + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToStringVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToStringVectorClosure.java new file mode 100644 index 0000000000000000000000000000000000000000..cb2c3ca812dd0859281f3f864c63d0e840635b0a --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToStringVectorClosure.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.data.closures; + +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.ops.na.NACheck; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +/* + * This closure is meant to be used only for implementation of the binary operators. + */ +public class RFactorToStringVectorClosure extends RToStringVectorClosure implements RAbstractStringVector { + + private final RIntVector vector; + private final RAbstractStringVector levels; + + public RFactorToStringVectorClosure(RFactor factor, NACheck naCheck) { + super(factor.getVector(), naCheck); + this.vector = factor.getVector(); + this.levels = (RAbstractStringVector) vector.getAttr(RRuntime.LEVELS_ATTR_KEY); + if (this.levels == null) { + RError.warning(RError.Message.IS_NA_TO_NON_VECTOR, "NULL"); + } + } + + public String getDataAt(int index) { + if (levels == null) { + return RRuntime.STRING_NA; + } else { + return this.levels.getDataAt(vector.getDataAt(index) - 1); + } + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java index 22fc5f2164def5f8c60bafabda94c0163c0039da..0a614dc224f5f5cafc84402517093fa10a96b075 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java @@ -100,7 +100,7 @@ public abstract class BinaryCompare extends BooleanOperation { super(commutative, false); } - private static final class NotEqual extends BinaryCompare { + public static final class NotEqual extends BinaryCompare { public NotEqual() { super(true); @@ -144,7 +144,7 @@ public abstract class BinaryCompare extends BooleanOperation { } } - private static final class Equal extends BinaryCompare { + public static final class Equal extends BinaryCompare { public Equal() { super(true); 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 29ac63cb1f6573978d8cbe4bec0e55ac182ea1bb..ed03a6ef4ed6f6cfe0518fa7de055f0b0ed9eb1a 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 @@ -8348,6 +8348,51 @@ NULL #{ x<-factor(c("a", "b", "a")); attr(x, "levels")<-character(); as.character(x) } [1] NA NA NA +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor +#{ x<-factor(c("a", "b", "a")); x == "a" } +[1] TRUE FALSE TRUE + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor +#{ x<-factor(c("a", "b", "a")); x == c("a", "b") } +[1] TRUE TRUE TRUE +Warning messages: +1: In is.na(e1) | is.na(e2) : + longer object length is not a multiple of shorter object length +2: In `==.default`(x, c("a", "b")) : + longer object length is not a multiple of shorter object length + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor +#{ x<-factor(c("a", "b", "a")); x > "a" } +[1] NA NA NA +Warning message: +In Ops.factor(x, "a") : > not meaningful for factors + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor +#{ x<-factor(c("a", "b", "a")); x > c("a", "b") } +[1] NA NA NA +Warning message: +In Ops.factor(x, c("a", "b")) : > not meaningful for factors + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor +#{ x<-factor(c("a", "b", "a", "c")); x == c("a", "b") } +[1] TRUE TRUE TRUE FALSE + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor +#{ x<-factor(c("c", "b", "a", "c")); y<-c(1); y[1]<-x; y } +[1] 3 +Warning message: +In y[1] <- x : + number of items to replace is not a multiple of replacement length + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor +#{ x<-factor(c("c", "b", "a", "c")); y<-list(1); y[1]<-x; y } +[[1]] +[1] 3 + +Warning message: +In y[1] <- x : + number of items to replace is not a multiple of replacement length + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor #{data = c(1,2,2,3,1,2,3,3,1,2,3,3,1);fdata<-factor(data);levels(fdata) = c('I','II','III');print(fdata);} [1] I II II III I II III III I II III III I @@ -16205,6 +16250,12 @@ $foo #{ x<-list(c(7,42),c(1+1i, 2+2i)); class(x)<-c("foo", "data.frame", "bar"); is.data.frame(x) } [1] TRUE +##com.oracle.truffle.r.test.simple.TestSimpleDataFrames.testLapply +#{ x <- c(1, 2, 3); xa <- as.data.frame(x); lapply(xa, function(x) x > 1) } +$x +[1] FALSE TRUE TRUE + + ##com.oracle.truffle.r.test.simple.TestSimpleDataFrames.testPrint #{ x<-c(1,2); class(x)<-"data.frame"; row.names(x)<-integer(); x } NULL diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java index 9f80add3385e800a2dc0dfc798705ec8260b7c9c..9b0455125ca3d8c3b70d08b06205e5940cf3c436 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java @@ -8133,6 +8133,16 @@ public class AllTests extends TestBase { assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); attr(x, \"levels\")<-character(); as.character(x) }"); } + @Test + public void TestSimpleBuiltins_testFactor_696be2cb6d37235d8e5aa08b6de78b44() { + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); x == \"a\" }"); + } + + @Test + public void TestSimpleBuiltins_testFactor_6b857dc0c28485c71e998f91ad719e79() { + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\", \"c\")); x == c(\"a\", \"b\") }"); + } + @Test public void TestSimpleBuiltins_testFactor_2ef7de52def309425a9b70965111f004() { assertEvalError("{ x<-c(1,2,3); class(x)<-\"factor\"; x }"); @@ -8148,6 +8158,31 @@ public class AllTests extends TestBase { assertEvalError("{ x<-c(1L,2L,3L); class(x)<-\"factor\"; x }"); } + @Test + public void TestSimpleBuiltins_testFactor_8e866be378d6495f8d649996dcb5bb3c() { + assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > \"a\" }"); + } + + @Test + public void TestSimpleBuiltins_testFactor_7cd2b27121f6c77b417a436d60108819() { + assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > c(\"a\", \"b\") }"); + } + + @Test + public void TestSimpleBuiltins_testFactor_61bfd366e4db68e9bda18fe2c3cc87f2() { + assertEvalWarning("{ x<-factor(c(\"a\", \"b\", \"a\")); x == c(\"a\", \"b\") }"); + } + + @Test + public void TestSimpleBuiltins_testFactor_9b48b1721b63ffee900121993a15bb82() { + assertEvalWarning("{ x<-factor(c(\"c\", \"b\", \"a\", \"c\")); y<-list(1); y[1]<-x; y }"); + } + + @Test + public void TestSimpleBuiltins_testFactor_ea50b4927f7021c815fba8b2628b3939() { + assertEvalWarning("{ x<-factor(c(\"c\", \"b\", \"a\", \"c\")); y<-c(1); y[1]<-x; y }"); + } + @Test public void TestSimpleBuiltins_testFileListing_9646bfd3fb553824f1f54cc5d04b8219() { assertEval("{ list.files(\"test/r/simple/data/tree1\") }"); @@ -17038,6 +17073,11 @@ public class AllTests extends TestBase { assertEval("{ x<-c(7,42); class(x)<-\"data.frame\"; attr(x, \"foo\")<-\"foo\"; class(x)<-NULL; attributes(x) }"); } + @Test + public void TestSimpleDataFrames_testLapply_55bc0d568d00ad30f7aace6b015a4fcd() { + assertEval("{ x <- c(1, 2, 3); xa <- as.data.frame(x); lapply(xa, function(x) x > 1) }"); + } + @Test public void TestSimpleDataFrames_testPrint_da9c92f6582f469a3303b14bf936c77e() { assertEval("{x<-c(1,2); class(x)<-\"data.frame\"; x}"); diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java index 9398cf67381741b95c225e52de0dded9dd72f2de..c465bbe1098064b47b8f07f68af78517bd1e177f 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java @@ -3900,6 +3900,16 @@ public class TestSimpleBuiltins extends TestBase { assertEvalError("{ x<-c(1,2,3); class(x)<-\"factor\"; x }"); assertEvalError("{ x<-c(\"1\",\"2\",\"3\"); class(x)<-\"factor\"; x }"); assertEvalError("{ x<-c(1L,2L,3L); class(x)<-\"factor\"; x }"); + + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); x == \"a\" }"); + assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > \"a\" }"); + + assertEvalWarning("{ x<-factor(c(\"a\", \"b\", \"a\")); x == c(\"a\", \"b\") }"); + assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > c(\"a\", \"b\") }"); + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\", \"c\")); x == c(\"a\", \"b\") }"); + + assertEvalWarning("{ x<-factor(c(\"c\", \"b\", \"a\", \"c\")); y<-list(1); y[1]<-x; y }"); + assertEvalWarning("{ x<-factor(c(\"c\", \"b\", \"a\", \"c\")); y<-c(1); y[1]<-x; y }"); } @Test diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleDataFrames.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleDataFrames.java index 2aca72765eee08030935a8b701dacf251a1ecbee..2ec6c750bab29350b5bb620df8ff9112162ff9cd 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleDataFrames.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleDataFrames.java @@ -130,4 +130,9 @@ public class TestSimpleDataFrames extends TestBase { assertEval("{ data.frame(c(1,2)) }"); assertEval("{ data.frame(c(1,2), c(11,12)) }"); } + + @Test + public void testLapply() { + assertEval("{ x <- c(1, 2, 3); xa <- as.data.frame(x); lapply(xa, function(x) x > 1) }"); + } } diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index 46f1105e8a2361b0fb68759a2d45ab705b6ba75f..ae129fa904741d4c8d7e26e63c07b3cc4456613a 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -66,6 +66,7 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/F com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/stats/Qgamma.java,gnu_r_qgamma.copyright com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/stats/Rnorm.java,gnu_r.copyright com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/stats/StatsUtil.java,gnu_r_statsutil.copyright +com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/CountFields.java,gnu_r.copyright com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/UtilsPackage.java,purdue.copyright com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParseException.java,purdue.copyright com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParseUtil.java,purdue.copyright