From 2b12f779655fb1582255ec4077aa8592e7ad8f43 Mon Sep 17 00:00:00 2001
From: Zbynek Slajchrt <zbynek.slajchrt@oracle.com>
Date: Thu, 25 Feb 2016 16:58:47 +0100
Subject: [PATCH] The new pretty-printer skeleton implementation integrated
 with the FastR. It supports the following data types: named and unnamed
 vectors, matrices, String and Double.

---
 .../truffle/r/nodes/builtin/base/Format.java  |   8 +-
 .../r/nodes/builtin/base/Inherits.java        |   2 +-
 .../truffle/r/nodes/builtin/base/IsS4.java    |   2 +
 .../r/nodes/builtin/base/IsTypeFunctions.java |   6 +
 .../nodes/builtin/base/PrettyPrinterNode.java |   3 +-
 .../r/nodes/builtin/base/PrintFunctions.java  |  44 +-
 .../base/printer/AbstractValuePrinter.java    |  48 ++
 .../base/printer/AttributesPrinter.java       | 106 +++
 .../builtin/base/printer/DoublePrinter.java   | 216 ++++++
 .../base/printer/DoubleVectorPrinter.java     | 218 ++++++
 .../builtin/base/printer/NullPrinter.java     |  35 +
 .../builtin/base/printer/PrintContext.java    |  54 ++
 .../base/printer/PrintJustification.java      |  30 +
 .../builtin/base/printer/PrintParameters.java | 237 +++++++
 .../builtin/base/printer/RBufferedWriter.java |  54 ++
 .../r/nodes/builtin/base/printer/RWriter.java |  45 ++
 .../builtin/base/printer/StringPrinter.java   | 100 +++
 .../base/printer/StringVectorPrinter.java     | 115 ++++
 .../r/nodes/builtin/base/printer/Utils.java   |  46 ++
 .../builtin/base/printer/ValuePrinter.java    |  30 +
 .../base/printer/ValuePrinterNode.java        | 106 +++
 .../builtin/base/printer/ValuePrinters.java   |  69 ++
 .../builtin/base/printer/VectorPrinter.java   | 633 ++++++++++++++++++
 .../oracle/truffle/r/runtime/RRuntime.java    |   4 +
 .../truffle/r/test/ExpectedTestOutput.test    |   4 +
 .../r/test/builtins/TestBuiltin_format.java   |  13 +-
 .../library/base/TestSimpleArithmetic.java    |   3 +-
 .../r/test/rpackages/TestRFFIPackage.java     |  24 +-
 mx.fastr/copyrights/overrides                 |   7 +
 mx.fastr/mx_fastr.py                          |   3 +
 30 files changed, 2249 insertions(+), 16 deletions(-)
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AbstractValuePrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoublePrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/NullPrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintContext.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintJustification.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintParameters.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RBufferedWriter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RWriter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringPrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringVectorPrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/Utils.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinter.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
index 7c72e604d8..9d3bf751e0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
@@ -31,6 +31,12 @@ public abstract class Format extends RBuiltinNode {
 
     protected final BranchProfile errorProfile = BranchProfile.create();
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    /**
+     * This is just a dummy object used to invoke getNames on RAbstractDoubleVector. The
+     * RAbstractDoubleVector has no method for obtaining the names associated with the values in the
+     * vector other than getNames(RAttributeProfiles).
+     */
+    private static final RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
 
     public static final int R_MAX_DIGITS_OPT = 22;
     public static final int R_MIN_DIGITS_OPT = 0;
@@ -216,7 +222,7 @@ public abstract class Format extends RBuiltinNode {
             addSpaces(data, width);
         }
         // vector is complete because strings are created by string builder
-        return RDataFactory.createStringVector(data, complete);
+        return RDataFactory.createStringVector(data, complete, value.getNames(dummyAttrProfiles));
     }
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
index 0f913c0a1f..5ae44f5d05 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
@@ -28,7 +28,7 @@ public abstract class Inherits extends RBuiltinNode {
 
     protected final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
-    protected abstract Object execute(Object x, Object what, Object which);
+    public abstract Object execute(Object x, Object what, Object which);
 
     @Child private InheritsNode inheritsNode;
     @Child private Inherits recursiveInherits;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java
index 6e8406648d..ddd0b6e143 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java
@@ -39,6 +39,8 @@ import com.oracle.truffle.r.runtime.data.model.*;
 @RBuiltin(name = "isS4", kind = PRIMITIVE, parameterNames = {"object"})
 public abstract class IsS4 extends RBuiltinNode {
 
+    public abstract byte execute(Object value);
+
     @Specialization
     protected byte isS4(RNull object) {
         return RRuntime.asLogical(RContext.getInstance().isNullS4Object());
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
index 939a829866..a5299f05f8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
@@ -63,6 +63,8 @@ public class IsTypeFunctions {
     @RBuiltin(name = "is.array", kind = PRIMITIVE, parameterNames = {"x"})
     public abstract static class IsArray extends MissingAdapter {
 
+        public abstract byte execute(Object value);
+
         @Specialization
         protected byte isType(RAbstractVector vector) {
             controlVisibility();
@@ -302,6 +304,8 @@ public class IsTypeFunctions {
 
         private final ConditionProfile isListProfile = ConditionProfile.createBinaryProfile();
 
+        public abstract byte execute(Object value);
+
         @Specialization
         protected byte isType(RList value) {
             controlVisibility();
@@ -435,6 +439,8 @@ public class IsTypeFunctions {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
+        public abstract byte execute(Object value);
+
         @Specialization
         protected byte isObject(RAttributable arg) {
             controlVisibility();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
index 50385f5b1a..697b37e0c2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
@@ -1842,7 +1842,8 @@ public abstract class PrettyPrinterNode extends RNode {
                 for (int dimInd = 0; dimInd < dimSize; dimInd++) {
                     int newArrayBase = arrayBase + newAccDimensions * dimInd;
                     String dimId = getDimId(vector, currentDimLevel, dimInd, attrProfiles);
-                    String innerDims = printDimRecursive(vector, isListOrStringVector, isComplexOrRawVector, currentDimLevel - 1, newArrayBase, newAccDimensions, concat(dimId, ", ", header), isQuoted);
+                    String innerDims = printDimRecursive(vector, isListOrStringVector, isComplexOrRawVector, currentDimLevel - 1, newArrayBase, newAccDimensions, concat(dimId, ", ", header),
+                                    isQuoted);
                     if (innerDims == null) {
                         return null;
                     } else {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
index e10d120fcb..a0ff2ccd9a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
@@ -25,6 +25,8 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.Truffle;
@@ -33,11 +35,20 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RInvisibleBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.printer.PrintContext;
+import com.oracle.truffle.r.nodes.builtin.base.printer.PrintParameters;
+import com.oracle.truffle.r.nodes.builtin.base.printer.RBufferedWriter;
+import com.oracle.truffle.r.nodes.builtin.base.printer.RWriter;
+import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNode;
+import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinters;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
@@ -49,6 +60,8 @@ import com.oracle.truffle.r.runtime.data.RTypedValue;
 public class PrintFunctions {
     public abstract static class PrintAdapter extends RInvisibleBuiltinNode {
         @Child protected PrettyPrinterNode prettyPrinter = PrettyPrinterNodeGen.create(null, null, null, null, false);
+        // The new pretty-printer
+        @Child protected ValuePrinterNode valuePrinter = ValuePrinterNodeGen.create(null, null, null, null, null, null, null, null, null);
 
         @TruffleBoundary
         protected void printHelper(String string) {
@@ -66,13 +79,30 @@ public class PrintFunctions {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
-        @SuppressWarnings("unused")
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            super.createCasts(casts);
+            casts.firstBoolean(2);
+            casts.firstBoolean(5);
+            casts.firstBoolean(7);
+            casts.firstBoolean(8);
+        }
+
+        @SuppressWarnings({"unchecked"})
         @Specialization(guards = "!isS4(o)")
-        protected Object printDefault(Object o, Object digits, byte quote, Object naPrint, Object printGap, byte right, Object max, Object useSource, Object noOpt) {
-            String s = (String) prettyPrinter.executeString(o, null, quote, right);
-            if (s != null && !s.isEmpty()) {
-                printHelper(s);
+        protected Object printDefault(Object o, Object digits, boolean quote, Object naPrint, Object printGap, boolean right, Object max, boolean useSource, boolean noOpt) {
+            try {
+                // Invoking the new pretty-printer. In contrast to the previous one, the new one
+                // does not return the output value and prints directly to the output.
+                valuePrinter.executeString(o, digits, quote, naPrint, printGap, right, max, useSource, noOpt);
+            } catch (UnsupportedOperationException e) {
+                // The original pretty printing code
+                String s = (String) prettyPrinter.executeString(o, null, RRuntime.asLogical(quote), RRuntime.asLogical(right));
+                if (s != null && !s.isEmpty()) {
+                    printHelper(s);
+                }
             }
+
             controlVisibility();
             return o;
         }
@@ -92,14 +122,14 @@ public class PrintFunctions {
         @SuppressWarnings("unused")
         @TruffleBoundary
         @Specialization(guards = "isS4(o)")
-        protected Object printDefaultS4(RTypedValue o, Object digits, byte quote, Object naPrint, Object printGap, byte right, Object max, Object useSource, Object noOpt,
+        protected Object printDefaultS4(RTypedValue o, Object digits, boolean quote, Object naPrint, Object printGap, boolean right, Object max, boolean useSource, boolean noOpt,
                         @Cached("createShowFind()") ReadVariableNode showFind, @Cached("createShowFunction(showFind)") RFunction showFunction) {
             RContext.getEngine().evalFunction(showFunction, null, o);
             return null;
         }
 
         protected boolean isS4(Object o) {
-            // chacking for class attribute is a bit of a hack but GNU R has a hack in place here as
+            // checking for class attribute is a bit of a hack but GNU R has a hack in place here as
             // well to avoid recursively calling show via print in showDefault (we just can't use
             // the same hack at this point - for details see definition of showDefault in show.R)
             return o instanceof RAttributable && ((RAttributable) o).isS4() && ((RAttributable) o).getClassAttr(attrProfiles) != null;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AbstractValuePrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AbstractValuePrinter.java
new file mode 100644
index 0000000000..cee4b9608c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AbstractValuePrinter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import java.io.IOException;
+
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
+import com.oracle.truffle.r.runtime.data.RAttributes;
+
+public abstract class AbstractValuePrinter<T> implements ValuePrinter<T> {
+
+    public void print(T value, PrintContext printCtx) throws IOException {
+        printValue(value, printCtx);
+        printAttributes(value, printCtx);
+    }
+
+    private void printAttributes(T value, PrintContext printCtx) throws IOException {
+        if (value instanceof RAttributeStorage) {
+            AttributesPrinter.INSTANCE.print((RAttributeStorage) value, printCtx);
+        }
+    }
+
+    protected void printAttributes(T value, RAttributes attrs, PrintContext printCtx) throws IOException {
+    }
+
+    protected abstract void printValue(T value, PrintContext printCtx) throws IOException;
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java
new file mode 100644
index 0000000000..0a27ac8f00
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java
@@ -0,0 +1,106 @@
+/*
+ * 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, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2013,  The R Core Team
+ * Copyright (c) 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+
+package com.oracle.truffle.r.nodes.builtin.base.printer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
+import com.oracle.truffle.r.runtime.data.RAttributes;
+import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
+import com.oracle.truffle.r.runtime.data.RDataFrame;
+
+//Transcribed from GnuR, src/main/print.c
+
+public class AttributesPrinter implements ValuePrinter<RAttributable> {
+
+    public static final AttributesPrinter INSTANCE = new AttributesPrinter(false);
+
+    private final boolean useSlots;
+
+    public AttributesPrinter(boolean useSlots) {
+        super();
+        this.useSlots = useSlots;
+    }
+
+    public void print(RAttributable value, PrintContext printCtx) throws IOException {
+        RAttributes attrs = value.getAttributes();
+        if (attrs == null) {
+            return;
+        }
+
+        for (RAttribute a : attrs) {
+            if (useSlots && RRuntime.CLASS_SYMBOL.equals(a.getName())) {
+                continue;
+            }
+            ValuePrinterNode utils = printCtx.printerNode();
+            if (utils.isArray(value) || utils.isList(value)) {
+                if (RRuntime.DIM_ATTR_KEY.equals(a.getName()) || RRuntime.DIMNAMES_ATTR_KEY.equals(a.getName())) {
+                    continue;
+                }
+            }
+            if (utils.inherits(value, "factor", RRuntime.LOGICAL_FALSE)) {
+                if (RRuntime.LEVELS_ATTR_KEY.equals(a.getName())) {
+                    continue;
+                }
+                if (RRuntime.CLASS_ATTR_KEY.equals(a.getName())) {
+                    continue;
+                }
+            }
+            if (value instanceof RDataFrame) {
+                if (RRuntime.ROWNAMES_ATTR_KEY.equals(a.getName())) {
+                    continue;
+                }
+            }
+            if (!utils.isArray(value)) {
+                if (RRuntime.NAMES_ATTR_KEY.equals(a.getName())) {
+                    continue;
+                }
+            }
+            if (RRuntime.R_COMMENT.equals(a.getName()) || RRuntime.R_SOURCE.equals(a.getName()) ||
+                            RRuntime.R_SRCREF.equals(a.getName()) || RRuntime.R_WHOLE_SRCREF.equals(a.getName()) ||
+                            RRuntime.R_SRCFILE.equals(a.getName())) {
+                continue;
+            }
+
+            final PrintWriter out = printCtx.output();
+            final String tag;
+            if (useSlots) {
+                tag = String.format("Slot \"%s\":", a.getName());
+            } else {
+                tag = String.format("attr(,\"%s\")", a.getName());
+            }
+            out.println(tag);
+
+            if (RRuntime.ROWNAMES_ATTR_KEY.equals(a.getName())) {
+                /* need special handling AND protection */
+                Object val = a.getValue();
+                ValuePrinters.printValue(val, printCtx);
+                continue;
+            }
+            if (RContext.getInstance().isMethodTableDispatchOn() && utils.isS4(value)) {
+                throw new UnsupportedOperationException("TODO");
+            }
+            if (utils.isObject(value)) {
+                throw new UnsupportedOperationException("TODO");
+            }
+
+            ValuePrinters.printValue(a.getValue(), printCtx);
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoublePrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoublePrinter.java
new file mode 100644
index 0000000000..0573958c7d
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoublePrinter.java
@@ -0,0 +1,216 @@
+/*
+ * 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, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2013,  The R Core Team
+ * Copyright (c) 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.printer;
+
+import static com.oracle.truffle.r.nodes.builtin.base.printer.Utils.snprintf;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.r.nodes.builtin.base.printer.DoubleVectorPrinter.DoubleVectorMetrics;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RDouble;
+
+//Transcribed from GnuR, src/main/printutils.c
+
+public final class DoublePrinter extends AbstractValuePrinter<Double> {
+
+    public static final DoublePrinter INSTANCE = new DoublePrinter();
+
+    @Override
+    protected void printValue(Double value, PrintContext printCtx) throws IOException {
+        double x = value;
+
+        PrintWriter out = printCtx.output();
+        out.print("[1] ");
+        DoubleVectorMetrics dm = DoubleVectorPrinter.formatDoubleVector(RDouble.valueOf(x), 0, 1, 0, printCtx.parameters());
+        out.println(encodeReal(x, dm.maxWidth, dm.d, dm.e, '.', printCtx.parameters()));
+    }
+
+    private static final int DBL_DIG = 15;
+
+    private static final double[] tbl = {
+                    1e-1,
+                    1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
+                    1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+                    1e20, 1e21, 1e22
+    };
+    private static final int KP_MAX = 22;
+    private static final int R_dec_min_exponent = -308;
+    private static final int NB = 1000;
+
+    public static class ScientificDouble {
+        public final int sgn;
+        public final int kpower;
+        public final int nsig;
+        public final boolean roundingwidens;
+
+        public ScientificDouble(int sgn, int kpower, int nsig, boolean roundingwidens) {
+            super();
+            this.sgn = sgn;
+            this.kpower = kpower;
+            this.nsig = nsig;
+            this.roundingwidens = roundingwidens;
+        }
+
+    }
+
+    public static ScientificDouble scientific(double x, PrintParameters pp) {
+        /*
+         * for a number x , determine sgn = 1_{x < 0} {0/1} kpower = Exponent of 10; nsig =
+         * min(R_print.digits, #{significant digits of alpha}) roundingwidens = 1 if rounding causes
+         * x to increase in width, 0 otherwise
+         *
+         * where |x| = alpha * 10^kpower and 1 <= alpha < 10
+         */
+        double alpha;
+        double r;
+        int kp;
+        int j;
+
+        // output arguments
+        int sgn;
+        int kpower;
+        int nsig;
+        boolean roundingwidens;
+
+        if (x == 0.0) {
+            kpower = 0;
+            nsig = 1;
+            sgn = 0;
+            roundingwidens = false;
+            r = 0.0;
+        } else {
+            if (x < 0.0) {
+                sgn = 1;
+                r = -x;
+            } else {
+                sgn = 0;
+                r = x;
+            }
+
+            if (pp.getDigits() >= DBL_DIG + 1) {
+                // TODO:
+                // format_via_sprintf(r, pp.getDigits(), kpower, nsig);
+                roundingwidens = false;
+                // return;
+                throw new UnsupportedOperationException();
+            }
+
+            kp = (int) Math.floor(Math.log10(r)) - pp.getDigits() + 1; // r = |x|;
+                                                                       // 10^(kp + digits - 1) <= r
+
+            double r_prec = r;
+            /* use exact scaling factor in double precision, if possible */
+            if (Math.abs(kp) <= 22) {
+                if (kp >= 0) {
+                    r_prec /= tbl[kp + 1];
+                } else {
+                    r_prec *= tbl[-kp + 1];
+                }
+            }
+            /*
+             * on IEEE 1e-308 is not representable except by gradual underflow. Shifting by 303
+             * allows for any potential denormalized numbers x, and makes the reasonable assumption
+             * that R_dec_min_exponent+303 is in range. Representation of 1e+303 has low error.
+             */
+
+            else if (kp <= R_dec_min_exponent) {
+                r_prec = (r_prec * 1e+303) / Math.pow(10, kp + 303);
+            } else {
+                r_prec /= Math.pow(10, kp);
+            }
+            if (r_prec < tbl[pp.getDigits()]) {
+                r_prec *= 10.0;
+                kp--;
+            }
+            /* round alpha to integer, 10^(digits-1) <= alpha <= 10^digits */
+            /*
+             * accuracy limited by double rounding problem, alpha already rounded to 53 bits
+             */
+            alpha = Math.round(r_prec);
+
+            nsig = pp.getDigits();
+            for (j = 1; j <= pp.getDigits(); j++) {
+                alpha /= 10.0;
+                if (alpha == Math.floor(alpha)) {
+                    nsig--;
+                } else {
+                    break;
+                }
+            }
+            if (nsig == 0 && pp.getDigits() > 0) {
+                nsig = 1;
+                kp += 1;
+            }
+            kpower = kp + pp.getDigits() - 1;
+
+            /*
+             * Scientific format may do more rounding than fixed format, e.g. 9996 with 3 digits is
+             * 1e+04 in scientific, but 9996 in fixed. This happens when the true value r is less
+             * than 10^(kpower+1) and would not round up to it in fixed format. Here rgt is the
+             * decimal place that will be cut off by rounding
+             */
+
+            int rgt = pp.getDigits() - kpower;
+            /* bound rgt by 0 and KP_MAX */
+            rgt = rgt < 0 ? 0 : rgt > KP_MAX ? KP_MAX : rgt;
+            double fuzz = 0.5 / tbl[1 + rgt];
+            // kpower can be bigger than the table.
+            roundingwidens = kpower > 0 && kpower <= KP_MAX && r < tbl[kpower + 1] - fuzz;
+
+        }
+
+        return new ScientificDouble(sgn, kpower, nsig, roundingwidens);
+    }
+
+    public static String encodeReal(double x, int w, int d, int e, char cdec, PrintParameters pp) {
+        final String buff;
+        String fmt;
+
+        /* IEEE allows signed zeros (yuck!) */
+        if (x == 0.0) {
+            x = 0.0;
+        }
+        if (!RRuntime.isFinite(x)) {
+            int numBlanks = Math.min(w, (NB - 1));
+            String naFmt = "%" + numBlanks + "s";
+            if (RRuntime.isNA(x)) {
+                buff = snprintf(NB, naFmt, pp.getNa_string());
+            } else if (RRuntime.isNAorNaN(x)) {
+                buff = snprintf(NB, naFmt, "NaN");
+            } else if (x > 0) {
+                buff = snprintf(NB, naFmt, "Inf");
+            } else {
+                buff = snprintf(NB, naFmt, "-Inf");
+            }
+        } else if (e != 0) {
+            if (d != 0) {
+                fmt = String.format("%%#%d.%de", Math.min(w, (NB - 1)), d);
+                buff = snprintf(NB, fmt, x);
+            } else {
+                fmt = String.format("%%%d.%de", Math.min(w, (NB - 1)), d);
+                buff = snprintf(NB, fmt, x);
+            }
+        } else { /* e = 0 */
+            fmt = String.format("%%%d.%df", Math.min(w, (NB - 1)), d);
+            buff = snprintf(NB, fmt, x);
+        }
+
+        if (cdec != '.') {
+            buff.replace('.', cdec);
+        }
+
+        return buff;
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
new file mode 100644
index 0000000000..603abc861f
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
@@ -0,0 +1,218 @@
+/*
+ * 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, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2013,  The R Core Team
+ * Copyright (c) 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.printer;
+
+import java.io.IOException;
+
+import com.oracle.truffle.r.nodes.builtin.base.printer.DoublePrinter.ScientificDouble;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+//Transcribed from GnuR, src/main/format.c
+
+public final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVector> {
+
+    public static final DoubleVectorPrinter INSTANCE = new DoubleVectorPrinter();
+
+    @Override
+    protected VectorPrinter<RAbstractDoubleVector>.VectorPrintJob createJob(RAbstractDoubleVector vector, int indx, boolean quote, PrintContext printCtx) {
+        return new DoubleVectorPrintJob(vector, indx, quote, printCtx);
+    }
+
+    private class DoubleVectorPrintJob extends VectorPrintJob {
+
+        protected DoubleVectorPrintJob(RAbstractDoubleVector vector, int indx, boolean quote, PrintContext printCtx) {
+            super(vector, indx, quote, printCtx);
+        }
+
+        @Override
+        protected DoubleVectorMetrics formatVector(int offs, int len) {
+            return formatDoubleVector(vector, offs, len, 0, printCtx.parameters());
+        }
+
+        @Override
+        protected void printElement(int i, FormatMetrics fm) throws IOException {
+            DoubleVectorMetrics dfm = (DoubleVectorMetrics) fm;
+            String v = DoublePrinter.encodeReal(vector.getDataAt(i), dfm.maxWidth, dfm.d, dfm.e, '.', printCtx.parameters());
+            out.print(v);
+        }
+
+        @Override
+        protected void printCell(int i, FormatMetrics fm) throws IOException {
+            printElement(i, fm);
+        }
+
+        @Override
+        protected void printEmptyVector() throws IOException {
+            out.println("numeric(0)");
+        }
+
+    }
+
+    public static class DoubleVectorMetrics extends FormatMetrics {
+        public final int d;
+        public final int e;
+
+        public DoubleVectorMetrics(int w, int d, int e) {
+            super(w);
+            this.d = d;
+            this.e = e;
+        }
+
+    }
+
+    public static DoubleVectorMetrics formatDoubleVector(RAbstractDoubleVector x, int offs, int n, int nsmall, PrintParameters pp) {
+        int left;
+        int right;
+        int sleft;
+        int mnl;
+        int mxl;
+        int rgt;
+        int mxsl;
+        int mxns;
+        int wF;
+        int neg;
+        int sgn;
+        int kpower;
+        int nsig;
+        boolean roundingwidens;
+        boolean naflag;
+        boolean nanflag;
+        boolean posinf;
+        boolean neginf;
+
+        // output arguments
+        int w;
+        int d;
+        int e;
+
+        nanflag = false;
+        naflag = false;
+        posinf = false;
+        neginf = false;
+        neg = 0;
+        rgt = mxl = mxsl = mxns = RRuntime.INT_MIN_VALUE;
+        mnl = RRuntime.INT_MAX_VALUE;
+
+        for (int i = 0; i < n; i++) {
+            double xi = x.getDataAt(i + offs);
+            if (!RRuntime.isFinite(xi)) {
+                if (RRuntime.isNA(xi)) {
+                    naflag = true;
+                } else if (RRuntime.isNAorNaN(xi)) {
+                    nanflag = true;
+                } else if (xi > 0) {
+                    posinf = true;
+                } else {
+                    neginf = true;
+                }
+            } else {
+                ScientificDouble sd = DoublePrinter.scientific(xi, pp);
+                sgn = sd.sgn;
+                nsig = sd.nsig;
+                kpower = sd.kpower;
+                roundingwidens = sd.roundingwidens;
+
+                left = kpower + 1;
+                if (roundingwidens) {
+                    left--;
+                }
+
+                sleft = sgn + ((left <= 0) ? 1 : left); /* >= 1 */
+                right = nsig - left; /* #{digits} right of '.' ( > 0 often) */
+                if (sgn > 0) {
+                    neg = 1; /* if any < 0, need extra space for sign */
+                }
+
+                /* Infinite precision "F" Format : */
+                if (right > rgt) {
+                    rgt = right; /* max digits to right of . */
+                }
+                if (left > mxl) {
+                    mxl = left; /* max digits to left of . */
+                }
+                if (left < mnl) {
+                    mnl = left; /* min digits to left of . */
+                }
+                if (sleft > mxsl) {
+                    mxsl = sleft; /*
+                                   * max left includingimport static
+                                   * com.oracle.truffle.r.nodes.builtin.base.printer.Utils.*;
+                                   * sign(s)
+                                   */
+                }
+                if (nsig > mxns) {
+                    mxns = nsig; /* max sig digits */
+                }
+            }
+        }
+        /*
+         * F Format: use "F" format WHENEVER we use not more space than 'E' and still satisfy
+         * 'R_print.digits' {but as if nsmall==0 !}
+         *
+         * E Format has the form [S]X[.XXX]E+XX[X]
+         *
+         * This is indicated by setting *e to non-zero (usually 1) If the additional exponent digit
+         * is required *e is set to 2
+         */
+
+        /*-- These 'mxsl' & 'rgt' are used in F Format
+        * AND in the ____ if(.) "F" else "E" ___ below: */
+        if (pp.getDigits() == 0) {
+            rgt = 0;
+        }
+        if (mxl < 0) {
+            mxsl = 1 + neg; /* we use %#w.dg, so have leading zero */
+        }
+
+        /* use nsmall only *after* comparing "F" vs "E": */
+        if (rgt < 0) {
+            rgt = 0;
+        }
+        wF = mxsl + rgt + (rgt != 0 ? 1 : 0); /* width for F format */
+
+        /*-- 'see' how "E" Exponential format would be like : */
+        e = (mxl > 100 || mnl <= -99) ? 2 /* 3 digit exponent */ : 1;
+        if (mxns != RRuntime.INT_MIN_VALUE) {
+            d = mxns - 1;
+            w = neg + (d != 0 ? 1 : 1) + d + 4 + e; /* width for E format */
+            if (wF <= w + pp.getScipen()) { /* Fixpoint if it needs less space */
+                e = 0;
+                if (nsmall > rgt) {
+                    rgt = nsmall;
+                    wF = mxsl + rgt + (rgt != 0 ? 1 : 0);
+                }
+                d = rgt;
+                w = wF;
+            } /* else : "E" Exponential format -- all done above */
+        } else { /* when all x[i] are non-finite */
+            w = 0; /* to be increased */
+            d = 0;
+            e = 0;
+        }
+        if (naflag && w < pp.getNa_width()) {
+            w = pp.getNa_width();
+        }
+        if (nanflag && w < 3) {
+            w = 3;
+        }
+        if (posinf && w < 3) {
+            w = 3;
+        }
+        if (neginf && w < 4) {
+            w = 4;
+        }
+
+        return new DoubleVectorMetrics(w, d, e);
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/NullPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/NullPrinter.java
new file mode 100644
index 0000000000..e91de12c80
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/NullPrinter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import com.oracle.truffle.r.runtime.RRuntime;
+
+public final class NullPrinter implements ValuePrinter<Object> {
+
+    public static NullPrinter INSTANCE = new NullPrinter();
+
+    public void print(Object value, PrintContext printCtx) {
+        printCtx.output().println(RRuntime.NULL);
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintContext.java
new file mode 100644
index 0000000000..404481be2a
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintContext.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import java.io.PrintWriter;
+
+public class PrintContext {
+    private final ValuePrinterNode pn;
+    private final PrintParameters params;
+    private final PrintWriter out;
+
+    public PrintContext(ValuePrinterNode printerNode, PrintParameters parameters, PrintWriter output) {
+        this.pn = printerNode;
+        this.params = parameters;
+        this.out = output;
+    }
+
+    public PrintParameters parameters() {
+        return params;
+    }
+
+    public ValuePrinterNode printerNode() {
+        return pn;
+    }
+
+    public PrintWriter output() {
+        return out;
+    }
+
+    public PrintContext cloneContext() {
+        return new PrintContext(pn, params.cloneParameters(), out);
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintJustification.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintJustification.java
new file mode 100644
index 0000000000..f192492149
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintJustification.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+public enum PrintJustification {
+    left,
+    center,
+    right,
+    none
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintParameters.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintParameters.java
new file mode 100644
index 0000000000..400901cb85
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintParameters.java
@@ -0,0 +1,237 @@
+/*
+ * 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, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2013,  The R Core Team
+ * Copyright (c) 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.printer;
+
+//Transcribed from GnuR, src/include/Print.h
+
+import com.oracle.truffle.r.nodes.builtin.base.Format;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RString;
+
+public class PrintParameters {
+    private int width;
+    private int na_width;
+    private int na_width_noquote;
+    private int digits;
+    private int scipen;
+    private int gap;
+    private boolean quote;
+    private boolean right;
+    private int max;
+    private String na_string;
+    private String na_string_noquote;
+    private boolean useSource;
+    private int cutoff; // for deparsed language objects
+
+    public PrintParameters() {
+    }
+
+    public PrintParameters(Object digits, boolean quote, Object naPrint,
+                    Object printGap, boolean right, Object max, boolean useSource, boolean noOpt) {
+
+        setDefaults();
+
+        if (digits != RNull.instance) {
+            this.digits = RRuntime.asInteger(digits);
+            if (this.digits == RRuntime.INT_NA ||
+                            this.digits < Format.R_MIN_DIGITS_OPT ||
+                            this.digits > Format.R_MAX_DIGITS_OPT) {
+                throw new IllegalArgumentException(String.format("invalid '%s' argument", "digits"));
+            }
+        }
+
+        this.quote = quote;
+
+        if (naPrint != RNull.instance) {
+            // TODO: Although the original code in print.c at line 253 contains the following
+            // condition, the GnuR application ignores that condition. It was revealed when running
+            // test com.oracle.truffle.r.test.builtins.TestBuiltin_printdefault, which fails if the
+            // condition is present complaining about an invalid na.print specification.
+            // if (!(naPrint instanceof RString) || ((RString) naPrint).getValue().length() < 1)
+// throw new IllegalArgumentException(String.format("invalid 'na.print' specification"));
+            String nav = naPrint.toString();
+            if (!"".equals(nav)) {
+                this.na_string = this.na_string_noquote = ((RString) naPrint).getValue();
+                this.na_width = this.na_width_noquote = this.na_string.length();
+            }
+        }
+
+        if (printGap != RNull.instance) {
+            this.gap = RRuntime.asInteger(printGap);
+            if (this.gap == RRuntime.INT_NA || this.gap < 0) {
+                throw new IllegalArgumentException(String.format("'gap' must be non-negative integer"));
+            }
+        }
+
+        this.right = right;
+
+        if (max != RNull.instance) {
+            this.max = RRuntime.asInteger(max);
+            if (this.max == RRuntime.INT_NA || this.max < 0) {
+                throw new IllegalArgumentException(String.format("invalid '%s' argument", "max"));
+            } else if (this.max == RRuntime.INT_MAX_VALUE) {
+                this.max--; // so we can add
+            }
+        }
+
+        this.useSource = useSource;
+    }
+
+    public PrintParameters cloneParameters() {
+        PrintParameters cloned = new PrintParameters();
+        cloned.na_string = this.na_string;
+        cloned.na_string_noquote = this.na_string_noquote;
+        cloned.na_width = this.na_width;
+        cloned.na_width_noquote = this.na_string_noquote.length();
+        cloned.quote = this.quote;
+        cloned.right = this.right;
+        cloned.digits = this.digits;
+        cloned.scipen = this.scipen;
+        cloned.max = this.max;
+        cloned.gap = this.gap;
+        cloned.width = this.width;
+        cloned.useSource = this.useSource;
+        cloned.cutoff = this.cutoff;
+        return cloned;
+    }
+
+    private void setDefaults() {
+        this.na_string = RRuntime.STRING_NA;
+        this.na_string_noquote = "<NA>";
+        this.na_width = this.na_string.length();
+        this.na_width_noquote = this.na_string_noquote.length();
+        this.quote = true;
+        this.right = false;
+        this.digits = RRuntime.asInteger(RContext.getInstance().stateROptions.getValue("digits"));
+        this.scipen = RRuntime.asInteger(RContext.getInstance().stateROptions.getValue("scipen"));
+        if (this.scipen == RRuntime.INT_NA) {
+            this.scipen = 0;
+        }
+        this.max = RRuntime.asInteger(RContext.getInstance().stateROptions.getValue("max.print"));
+        if (this.max == RRuntime.INT_NA || this.max < 0) {
+            this.max = 99999;
+        } else if (this.max == RRuntime.INT_NA) {
+            this.max--; // so we can add
+        }
+        this.gap = 1;
+        this.width = RRuntime.asInteger(RContext.getInstance().stateROptions.getValue("width"));
+        this.useSource = true;
+        this.cutoff = RRuntime.asInteger(RContext.getInstance().stateROptions.getValue("deparse.cutoff"));
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getNa_width() {
+        return na_width;
+    }
+
+    public void setNa_width(int na_width) {
+        this.na_width = na_width;
+    }
+
+    public int getNa_width_noquote() {
+        return na_width_noquote;
+    }
+
+    public void setNa_width_noquote(int na_width_noquote) {
+        this.na_width_noquote = na_width_noquote;
+    }
+
+    public int getDigits() {
+        return digits;
+    }
+
+    public void setDigits(int digits) {
+        this.digits = digits;
+    }
+
+    public int getScipen() {
+        return scipen;
+    }
+
+    public void setScipen(int scipen) {
+        this.scipen = scipen;
+    }
+
+    public int getGap() {
+        return gap;
+    }
+
+    public void setGap(int gap) {
+        this.gap = gap;
+    }
+
+    public boolean getQuote() {
+        return quote;
+    }
+
+    public void setQuote(boolean quote) {
+        this.quote = quote;
+    }
+
+    public boolean getRight() {
+        return right;
+    }
+
+    public void setRight(boolean right) {
+        this.right = right;
+    }
+
+    public int getMax() {
+        return max;
+    }
+
+    public void setMax(int max) {
+        this.max = max;
+    }
+
+    public String getNa_string() {
+        return na_string;
+    }
+
+    public void setNa_string(String na_string) {
+        this.na_string = na_string;
+    }
+
+    public String getNa_string_noquote() {
+        return na_string_noquote;
+    }
+
+    public void setNa_string_noquote(String na_string_noquote) {
+        this.na_string_noquote = na_string_noquote;
+    }
+
+    public boolean getUseSource() {
+        return useSource;
+    }
+
+    public void setUseSource(boolean useSource) {
+        this.useSource = useSource;
+    }
+
+    public int getCutoff() {
+        return cutoff;
+    }
+
+    public void setCutoff(int cutoff) {
+        this.cutoff = cutoff;
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RBufferedWriter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RBufferedWriter.java
new file mode 100644
index 0000000000..460a44e314
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RBufferedWriter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import com.oracle.truffle.r.runtime.conn.StdConnections;
+
+public class RBufferedWriter extends Writer {
+
+    private final StringWriter w = new StringWriter();
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        w.write(cbuf, off, len);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        w.flush();
+    }
+
+    public void commit() throws IOException {
+        StdConnections.getStdout().writeString(w.toString(), false);
+    }
+
+    @Override
+    public void close() throws IOException {
+        w.close();
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RWriter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RWriter.java
new file mode 100644
index 0000000000..a5da7f4618
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/RWriter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import com.oracle.truffle.r.runtime.conn.StdConnections;
+
+public class RWriter extends Writer {
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        StdConnections.getStdout().writeString(new String(cbuf, off, len), false);
+    }
+
+    @Override
+    public void flush() throws IOException {
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringPrinter.java
new file mode 100644
index 0000000000..f64fb64023
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringPrinter.java
@@ -0,0 +1,100 @@
+/*
+ * 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, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2013,  The R Core Team
+ * Copyright (c) 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.printer;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.conn.RConnection;
+
+//Transcribed from GnuR, src/main/printutils.c
+
+public final class StringPrinter extends AbstractValuePrinter<String> {
+
+    public static final StringPrinter INSTANCE = new StringPrinter();
+
+    public static String encode(String value, int w, PrintParameters pp) {
+        final boolean quote = pp.getQuote();
+        final String s;
+        if (quote) {
+            if (RRuntime.isNA(value)) {
+                s = pp.getNa_string();
+            } else {
+                s = RRuntime.quoteString(value);
+            }
+        } else {
+            if (RRuntime.isNA(value)) {
+                s = pp.getNa_string_noquote();
+            } else {
+                s = value;
+            }
+        }
+        return encode(s, w, pp.getRight() ? PrintJustification.right : PrintJustification.left);
+    }
+
+    public static String encode(String s, int w, PrintJustification justify) {
+        // justification
+        final int b = w - s.length(); // total amount of blanks
+        int bl = 0; // left blanks
+        int br = 0; // right blanks
+
+        switch (justify) {
+            case left:
+                br = b;
+                break;
+            case center:
+                bl = b / 2;
+                br = b - bl;
+                break;
+            case right:
+                bl = b;
+                break;
+            case none:
+                break;
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        if (bl > 0) {
+            char[] sp = new char[bl];
+            Arrays.fill(sp, ' ');
+            sb.append(sp);
+        }
+
+        sb.append(s);
+
+        if (br > 0) {
+            char[] sp = new char[br];
+            Arrays.fill(sp, ' ');
+            sb.append(sp);
+        }
+
+        return sb.toString();
+    }
+
+    public static void printString(String s, int w, PrintContext printCtx) {
+        String outS = encode(s, w, printCtx.parameters());
+        printCtx.output().print(outS);
+    }
+
+    @Override
+    protected void printValue(String value, PrintContext printCtx) throws IOException {
+        PrintWriter out = printCtx.output();
+        out.print("[1] ");
+        printString(value, value.length(), printCtx);
+        out.println();
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringVectorPrinter.java
new file mode 100644
index 0000000000..2c8be4d92f
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringVectorPrinter.java
@@ -0,0 +1,115 @@
+/*
+ * 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, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2013,  The R Core Team
+ * Copyright (c) 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.printer;
+
+import java.io.IOException;
+
+import static com.oracle.truffle.r.nodes.builtin.base.printer.Utils.asBlankArg;
+
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+
+//Transcribed from GnuR, src/main/format.c
+
+public final class StringVectorPrinter extends VectorPrinter<RAbstractStringVector> {
+
+    public static final StringVectorPrinter INSTANCE = new StringVectorPrinter();
+
+    @Override
+    protected VectorPrinter<RAbstractStringVector>.VectorPrintJob createJob(RAbstractStringVector vector, int indx, boolean quote, PrintContext printCtx) {
+        return new StringVectorPrintJob(vector, indx, quote, printCtx);
+    }
+
+    private class StringVectorPrintJob extends VectorPrintJob {
+
+        protected StringVectorPrintJob(RAbstractStringVector vector, int indx, boolean quote, PrintContext printCtx) {
+            super(vector, indx, quote, printCtx);
+        }
+
+        @Override
+        protected FormatMetrics formatVector(int offs, int len) {
+            int w = formatString(vector, offs, len, quote, printCtx.parameters());
+            return new FormatMetrics(w);
+        }
+
+        @Override
+        protected void printElement(int i, FormatMetrics fm) throws IOException {
+            String s = vector.getDataAt(i);
+            StringPrinter.printString(s, fm.maxWidth, printCtx);
+        }
+
+        @Override
+        protected void printCell(int i, FormatMetrics fm) throws IOException {
+            String s = vector.getDataAt(i);
+            String outS = StringPrinter.encode(s, fm.maxWidth, printCtx.parameters());
+            int g = printCtx.parameters().getGap();
+            String fmt = "%" + asBlankArg(g) + "s%s";
+            printCtx.output().printf(fmt, "", outS);
+        }
+
+        @Override
+        protected void printEmptyVector() throws IOException {
+            out.println("character(0)");
+        }
+
+        @Override
+        protected void printMatrixColumnLabels(RAbstractStringVector cl, int jmin, int jmax, FormatMetrics[] w) {
+            if (printCtx.parameters().getRight()) {
+                for (int j = jmin; j < jmax; j++) {
+                    rightMatrixColumnLabel(cl, j, w[j].maxWidth);
+                }
+            } else {
+                for (int j = jmin; j < jmax; j++) {
+                    leftMatrixColumnLabel(cl, j, w[j].maxWidth);
+                }
+            }
+        }
+
+        @Override
+        protected int matrixColumnWidthCorrection1() {
+            return 0;
+        }
+
+        @Override
+        protected int matrixColumnWidthCorrection2() {
+            return printCtx.parameters().getGap();
+        }
+
+    }
+
+    public static int formatString(RAbstractStringVector x, int offs, int n, boolean quote, PrintParameters pp) {
+        int xmax = 0;
+        int l;
+
+        // output argument
+        int fieldwidth;
+
+        for (int i = 0; i < n; i++) {
+            String s = x.getDataAt(offs + i);
+            String xi = quote ? RRuntime.quoteString(s) : s;
+
+            if (xi == RRuntime.STRING_NA) {
+                l = quote ? pp.getNa_width() : pp.getNa_width_noquote();
+            } else {
+                l = xi.length();
+            }
+            if (l > xmax) {
+                xmax = l;
+            }
+        }
+
+        fieldwidth = xmax;
+
+        return fieldwidth;
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/Utils.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/Utils.java
new file mode 100644
index 0000000000..97f048a159
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/Utils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public class Utils {
+
+    public static String snprintf(int size, String format, Object... args) {
+        String fs = String.format(format, args);
+        return fs.length() <= size ? fs : fs.substring(0, size);
+    }
+
+    public static String asBlankArg(int blanks) {
+        return blanks == 0 ? "" : blanks + "";
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T castTo(Object x) {
+        if (x instanceof RNull) {
+            return null;
+        } else {
+            return (T) x;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinter.java
new file mode 100644
index 0000000000..a66b8cbf27
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import java.io.IOException;
+
+public interface ValuePrinter<T> {
+
+    void print(T value, PrintContext printCtx) throws IOException;
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
new file mode 100644
index 0000000000..9d2abc010a
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.NodeChild;
+import com.oracle.truffle.api.dsl.NodeChildren;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.base.Inherits;
+import com.oracle.truffle.r.nodes.builtin.base.InheritsNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.IsS4;
+import com.oracle.truffle.r.nodes.builtin.base.IsS4NodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctions.IsArray;
+import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctions.IsList;
+import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctions.IsObject;
+import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctionsFactory.IsArrayNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctionsFactory.IsListNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctionsFactory.IsObjectNodeGen;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@SuppressWarnings("unused")
+@NodeChildren({@NodeChild(value = "operand", type = RNode.class), @NodeChild(value = "digits", type = RNode.class), @NodeChild(value = "quote", type = RNode.class),
+                @NodeChild(value = "naPrint", type = RNode.class), @NodeChild(value = "printGap", type = RNode.class), @NodeChild(value = "right", type = RNode.class),
+                @NodeChild(value = "max", type = RNode.class), @NodeChild(value = "useSource", type = RNode.class), @NodeChild(value = "noOpt", type = RNode.class),
+                @NodeChild(value = "max", type = RNode.class)
+})
+public abstract class ValuePrinterNode extends RNode {
+
+    @Child IsArray isArrayBuiltIn = IsArrayNodeGen.create(null, null, null);
+    @Child IsList isListBuiltIn = IsListNodeGen.create(null, null, null);
+    @Child Inherits inheritsBuiltIn = InheritsNodeGen.create(null, null, null);
+    @Child IsS4 isS4BuiltIn = IsS4NodeGen.create(null, null, null);
+    @Child IsObject isObjectBuiltIn = IsObjectNodeGen.create(null, null, null);
+
+    public boolean isArray(Object o) {
+        return RRuntime.fromLogical(isArrayBuiltIn.execute(o));
+    }
+
+    public boolean isList(Object o) {
+        return RRuntime.fromLogical(isListBuiltIn.execute(o));
+    }
+
+    public boolean inherits(Object o, Object what, byte which) {
+        return RRuntime.fromLogical((Byte) inheritsBuiltIn.execute(o, what, which));
+    }
+
+    public boolean isS4(Object o) {
+        return RRuntime.fromLogical(isS4BuiltIn.execute(o));
+    }
+
+    public boolean isObject(Object o) {
+        return RRuntime.fromLogical(isObjectBuiltIn.execute(o));
+    }
+
+    public abstract Object executeString(Object o, Object digits, boolean quote, Object naPrint, Object printGap, boolean right, Object max, boolean useSource, boolean noOpt);
+
+    // TODO: More specializations should be added
+
+    @TruffleBoundary
+    @Specialization
+    protected String prettyPrint(Object o, Object digits, boolean quote, Object naPrint, Object printGap, boolean right, Object max, boolean useSource, boolean noOpt) {
+        // Until the new code is fully functional we have to use RBufferedWriter. In case
+        // an exception is thrown by the new code, the content accumulated in the
+        // RBufferedWriter is not printed and the old code is invoked to print the value. When
+        // the new code stabilizes the RBufferedWriter will be replaced by RWriter.
+        try (RBufferedWriter rw = new RBufferedWriter(); PrintWriter pw = new PrintWriter(rw)) {
+            // try (RWriter rw = new RWriter(); PrintWriter pw = new PrintWriter(rw)) {
+            PrintContext printCtx = new PrintContext(this, new PrintParameters(digits, quote, naPrint, printGap,
+                            right, max, useSource, noOpt), pw);
+            ValuePrinters.printValue(o, printCtx);
+            pw.flush();
+            rw.commit();
+            return null;
+        } catch (IOException ex) {
+            throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java
new file mode 100644
index 0000000000..67c643551b
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, 2016, 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.nodes.builtin.base.printer;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+
+public final class ValuePrinters implements ValuePrinter<Object> {
+
+    private final Map<Class<?>, ValuePrinter<?>> printers = new HashMap<>();
+
+    private static final ValuePrinters INSTANCE = new ValuePrinters();
+
+    private ValuePrinters() {
+        printers.put(String.class, StringPrinter.INSTANCE);
+        printers.put(Double.class, DoublePrinter.INSTANCE);
+    }
+
+    public static void printValue(Object x, PrintContext printCtx) throws IOException {
+        INSTANCE.print(x, printCtx);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    @Override
+    public void print(Object x, PrintContext printCtx) throws IOException {
+        if (x == null) {
+            NullPrinter.INSTANCE.print(null, printCtx);
+        } else {
+            ValuePrinter printer = printers.get(x.getClass());
+            if (printer == null) {
+                if (x instanceof RAbstractStringVector) {
+                    printer = StringVectorPrinter.INSTANCE;
+                } else if (x instanceof RAbstractDoubleVector) {
+                    printer = DoubleVectorPrinter.INSTANCE;
+                } else {
+                    throw new UnsupportedOperationException("TODO");
+                }
+            }
+            printer.print(x, printCtx);
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java
new file mode 100644
index 0000000000..9ccd9d671c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java
@@ -0,0 +1,633 @@
+/*
+ * 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, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2013,  The R Core Team
+ * Copyright (c) 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.printer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RString;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+import static com.oracle.truffle.r.nodes.builtin.base.printer.Utils.*;
+
+//Transcribed from GnuR, src/main/print.c, src/main/printarray.c, src/main/printvector.c
+
+public abstract class VectorPrinter<T extends RAbstractVector> extends AbstractValuePrinter<T> {
+
+    private static RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
+
+    @Override
+    protected void printValue(T vector, PrintContext printCtx) throws IOException {
+        printVector(vector, 1, printCtx.parameters().getQuote(), printCtx);
+    }
+
+    public void printVector(T vector, int indx, boolean quote, PrintContext printCtx) throws IOException {
+        createJob(vector, indx, quote, printCtx).print();
+    }
+
+    protected abstract VectorPrintJob createJob(T vector, int indx, boolean quote, PrintContext printCtx);
+
+    protected enum JobMode {
+        nonEmpty,
+        empty,
+        named,
+        namedEmpty,
+        matrix,
+        array
+    }
+
+    protected static class FormatMetrics {
+        int maxWidth;
+
+        public FormatMetrics(int maxWidth) {
+            super();
+            this.maxWidth = maxWidth;
+        }
+
+    }
+
+    private static final int R_MIN_LBLOFF = 2;
+
+    protected abstract class VectorPrintJob {
+
+        protected final T vector;
+        protected final int n;
+        protected final int n_pr;
+        protected final int indx;
+        protected final int labwidth;
+        protected final boolean quote;
+        protected final PrintContext printCtx;
+        protected final PrintWriter out;
+        protected final JobMode jobMode;
+        protected final RAbstractStringVector names;
+        protected final String title;
+        protected final MatrixDimNames matrixDimNames;
+        protected final RAbstractIntVector dims;
+
+        protected VectorPrintJob(T vector, int indx, boolean quote, PrintContext printCtx) {
+            this.vector = vector;
+            this.indx = indx;
+            this.quote = quote;
+
+            MatrixDimNames mdn = null;
+
+            Object dimAttr = vector.getAttr(dummyAttrProfiles, RRuntime.DIM_ATTR_KEY);
+            if (dimAttr instanceof RAbstractIntVector) {
+                dims = (RAbstractIntVector) dimAttr;
+                if (dims.getLength() == 1) {
+                    RList t = Utils.<RList> castTo(vector.getAttr(dummyAttrProfiles, RRuntime.DIMNAMES_ATTR_KEY));
+                    if (t != null && t.getDataAt(0) != null) {
+                        RAbstractStringVector nn = toStringVector(t.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY));
+
+                        if (nn != null) {
+                            title = nn.getDataAt(0);
+                        } else {
+                            title = null;
+                        }
+
+                        jobMode = vector.getLength() == 0 ? JobMode.namedEmpty : JobMode.named;
+                        names = toStringVector(t.getDataAt(0));
+                    } else {
+                        title = null;
+                        names = null;
+                        jobMode = vector.getLength() == 0 ? JobMode.empty : JobMode.nonEmpty;
+                    }
+                } else if (dims.getLength() == 2) {
+                    mdn = getMatrixDimnames(vector);
+                    title = null;
+                    names = null;
+                    jobMode = JobMode.matrix;
+                } else {
+                    title = null;
+                    names = null;
+                    jobMode = JobMode.array;
+// SEXP dimnames;
+// dimnames = GetArrayDimnames(s);
+// printArray(s, t, R_print.quote, R_print.right, dimnames);
+                }
+            } else {
+                dims = null;
+                Object namesAttr = Utils.castTo(vector.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY));
+                if (namesAttr != null) {
+                    if (vector.getLength() > 0) {
+                        names = toStringVector(namesAttr);
+                        jobMode = JobMode.named;
+                    } else {
+                        names = null;
+                        jobMode = JobMode.namedEmpty;
+                    }
+                } else if (vector.getLength() > 0) {
+                    jobMode = JobMode.nonEmpty;
+                    names = null;
+                } else {
+                    jobMode = JobMode.empty;
+                    names = null;
+                }
+                title = null;
+            }
+
+            this.printCtx = printCtx.cloneContext();
+            this.printCtx.parameters().setQuote(quote);
+            if (jobMode == JobMode.named) {
+                this.printCtx.parameters().setRight(true);
+            }
+            this.out = this.printCtx.output();
+            this.n = vector.getLength();
+            int max = printCtx.parameters().getMax();
+            this.n_pr = (n <= max + 1) ? n : max;
+            this.labwidth = indexWidth(n) + 2;
+            this.matrixDimNames = mdn;
+        }
+
+        public void print() throws IOException {
+            switch (jobMode) {
+                case empty:
+                    printEmptyVector();
+                    break;
+                case nonEmpty:
+                    printNonEmptyVector();
+                    break;
+                case named:
+                    printNamedVector();
+                    break;
+                case namedEmpty:
+                    printNamedEmptyVector();
+                    break;
+                case matrix:
+                    printMatrix();
+                    break;
+                case array:
+                    throw new UnsupportedOperationException("TODO");
+            }
+        }
+
+        private void printNamedEmptyVector() throws IOException {
+            out.print("named ");
+            printEmptyVector();
+        }
+
+        private void printNonEmptyVector() throws IOException {
+            final int gap = printCtx.parameters().getGap();
+            final int totalWidth = printCtx.parameters().getWidth();
+
+            int width = 0;
+
+            width = doLab(0);
+
+            FormatMetrics fm = formatVector(0, n);
+            final int w = fm.maxWidth;
+
+            for (int i = 0; i < n_pr; i++) {
+                if (i > 0 && width + w + gap > totalWidth) {
+                    out.println();
+                    width = doLab(i);
+                }
+                out.printf("%" + asBlankArg(gap) + "s", "");
+                printElement(i, fm);
+                width += w + gap;
+            }
+            out.println();
+            if (n_pr < n) {
+                out.printf(" [ reached getOption(\"max.print\") -- omitted %d entries ]\n", n - n_pr);
+            }
+        }
+
+        private void printNamedVector() throws IOException {
+            if (title != null) {
+                out.println(title);
+            }
+
+            int i;
+            int j;
+            int k;
+            int nlines;
+            int nperline;
+            int wn;
+
+            FormatMetrics fm = formatVector(0, n);
+
+            PrintParameters pp = printCtx.parameters();
+
+            wn = StringVectorPrinter.formatString(names, 0, n, false, pp);
+            if (fm.maxWidth < wn) {
+                fm.maxWidth = wn;
+            }
+
+            final int w = fm.maxWidth;
+
+            nperline = pp.getWidth() / (w + pp.getGap());
+            if (nperline <= 0) {
+                nperline = 1;
+            }
+            nlines = n / nperline;
+            if (n % nperline != 0) {
+                nlines += 1;
+            }
+
+            int gap = pp.getGap();
+            PrintContext namesPrintCtx = printCtx.cloneContext();
+            namesPrintCtx.parameters().setQuote(false);
+            namesPrintCtx.parameters().setRight(true);
+            for (i = 0; i < nlines; i++) {
+                if (i > 0) {
+                    out.println();
+                }
+                for (j = 0; j < nperline && (k = i * nperline + j) < n; j++) {
+                    StringPrinter.printString(names.getDataAt(k), w, namesPrintCtx);
+                    out.printf("%" + asBlankArg(gap) + "s", "");
+                }
+                out.println();
+                for (j = 0; j < nperline && (k = i * nperline + j) < n; j++) {
+                    printElement(k, fm);
+                    out.printf("%" + asBlankArg(gap) + "s", "");
+                }
+            }
+            out.println();
+        }
+
+        private void printMatrix() throws IOException {
+            printMatrix(0);
+        }
+
+        private void printMatrix(int offset) throws IOException {
+            PrintParameters pp = printCtx.parameters();
+
+            RAbstractStringVector rl = matrixDimNames.rl;
+            RAbstractStringVector cl = matrixDimNames.cl;
+            String rn = matrixDimNames.rn;
+            String cn = matrixDimNames.cn;
+            int r = dims.getDataAt(0);
+            int c = dims.getDataAt(1);
+            int r_pr;
+
+            /* PR#850 */
+            if (rl != null && r > rl.getLength()) {
+                throw RError.error(printCtx.printerNode(), RError.Message.GENERIC, "too few row labels");
+            }
+            if (cl != null && c > cl.getLength()) {
+                throw RError.error(printCtx.printerNode(), RError.Message.GENERIC, "too few column labels");
+            }
+            if (r == 0 && c == 0) { // FIXME? names(dimnames(.)) :
+                out.println("<0 x 0 matrix>");
+                return;
+            }
+            r_pr = r;
+            if (c > 0 && pp.getMax() / c < r) {
+                /* using floor(), not ceil(), since 'c' could be huge: */
+                r_pr = pp.getMax() / c;
+            }
+
+            printMatrix(offset, r_pr, r, c, rl, cl, rn, cn, true);
+
+            if (r_pr < r) {
+                out.printf(" [ reached getOption(\"max.print\") -- omitted %d rows ]\n", r - r_pr);
+            }
+
+        }
+
+        private void printMatrix(int offset, int r_pr, int r, int c,
+                        RAbstractStringVector rl, RAbstractStringVector cl, String rn, String cn,
+                        boolean print_ij) throws IOException {
+            // _PRINT_INIT_rl_rn
+
+            PrintParameters pp = printCtx.parameters();
+
+            FormatMetrics[] w = new FormatMetrics[c];
+            int width;
+            int rlabw = -1;
+            int clabw = -1;
+            int i;
+            int j;
+            int jmin = 0;
+            int jmax = 0;
+            int lbloff = 0;
+
+            if (rl != null) {
+                rlabw = StringVectorPrinter.formatString(rl, 0, r, false, pp);
+            } else {
+                rlabw = indexWidth(r + 1) + 3;
+            }
+
+            if (rn != null) {
+                int rnw = rn.length();
+                if (rnw < rlabw + R_MIN_LBLOFF) {
+                    lbloff = R_MIN_LBLOFF;
+                } else {
+                    lbloff = rnw - rlabw;
+                }
+
+                rlabw += lbloff;
+            }
+
+            // define _COMPUTE_W2_(_FORMAT_j_, _LAST_j_)
+            /* compute w[j] = column-width of j(+1)-th column : */
+            for (j = 0; j < c; j++) {
+                if (print_ij) {
+                    w[j] = formatVector(offset + j * r, r);
+                } else {
+                    w[j] = formatVector(0, 0);
+                }
+
+                if (cl != null) {
+                    String clj = cl.getDataAt(j);
+                    if (RRuntime.isNA(clj)) {
+                        clabw = pp.getNa_width_noquote();
+                    } else {
+                        clabw = clj.length();
+                    }
+                } else {
+                    clabw = indexWidth(j + 1) + 3;
+                }
+
+                if (w[j].maxWidth < clabw) {
+                    w[j].maxWidth = clabw;
+                }
+
+                w[j].maxWidth += matrixColumnWidthCorrection1();
+            }
+
+            // _PRINT_MATRIX_(_W_EXTRA_, DO_COLUMN_LABELS, ENCODE_I_J)
+
+            int wExtra = matrixColumnWidthCorrection2();
+            if (c == 0) {
+                printMatrixRowLab(cn, rn, rlabw);
+                for (i = 0; i < r; i++) {
+                    matrixRowLabel(rl, i, rlabw, lbloff);
+                }
+                out.println();
+            } else {
+                while (jmin < c) {
+                    /* print columns jmin:(jmax-1) where jmax has to be determined first */
+
+                    width = rlabw;
+                    /* initially, jmax = jmin */
+                    do {
+                        width += w[jmax].maxWidth + wExtra;
+                        jmax++;
+                    } while (jmax < c && width + w[jmax].maxWidth + wExtra < pp.getWidth());
+
+                    printMatrixRowLab(cn, rn, rlabw);
+
+                    printMatrixColumnLabels(cl, jmin, jmax, w);
+
+                    for (i = 0; i < r_pr; i++) {
+                        matrixRowLabel(rl, i, rlabw, lbloff); /* starting with an "\n" */
+                        if (print_ij) {
+                            for (j = jmin; j < jmax; j++) {
+                                printCell(i + j * r, w[j]);
+                            }
+                        }
+                    }
+                    out.println();
+                    jmin = jmax;
+                }
+            }
+        }
+
+        protected void printMatrixColumnLabels(RAbstractStringVector cl, int jmin, int jmax, FormatMetrics[] w) {
+            // define STD_ColumnLabels
+            for (int j = jmin; j < jmax; j++) {
+                matrixColumnLabel(cl, j, w[j].maxWidth);
+            }
+        }
+
+        private void printMatrixRowLab(String cn, String rn, int rlabw) {
+            // _PRINT_ROW_LAB
+            if (cn != null) {
+                String fmt = "%" + asBlankArg(rlabw) + "s%s\n";
+                out.printf(fmt, "", cn);
+            }
+            if (rn != null) {
+                String fmt = "%" + asBlankArg(-rlabw) + "s";
+                out.printf(fmt, rn);
+            } else {
+                String fmt = "%" + asBlankArg(rlabw) + "s";
+                out.printf(fmt, "");
+            }
+        }
+
+        private void matrixColumnLabel(RAbstractStringVector cl, int j, int w) {
+            PrintParameters pp = printCtx.parameters();
+
+            if (cl != null) {
+                String tmp = cl.getDataAt(j);
+                int l = (RRuntime.isNA(tmp)) ? pp.getNa_width_noquote() : tmp.length();
+                int gap = w - l;
+                String fmt = "%" + asBlankArg(gap) + "s%s";
+
+                PrintParameters pp2 = printCtx.parameters().cloneParameters();
+                pp2.setQuote(false);
+                pp2.setRight(false);
+                out.printf(fmt, "", StringPrinter.encode(tmp, l, pp2));
+            } else {
+                int gap = w - indexWidth(j + 1) - 3;
+                String fmt = "%" + asBlankArg(gap) + "s[,%d]";
+                out.printf(fmt, "", j + 1);
+            }
+        }
+
+        protected void rightMatrixColumnLabel(RAbstractStringVector cl, int j, int w) {
+            PrintParameters pp = printCtx.parameters();
+
+            if (cl != null) {
+                String tmp = cl.getDataAt(j);
+                int l = (RRuntime.isNA(tmp)) ? pp.getNa_width_noquote() : tmp.length();
+                /*
+                 * This does not work correctly at least on FC3 Rprintf("%*s", R_print.gap+w,
+                 * EncodeString(tmp, l, 0, Rprt_adj_right));
+                 */
+                int g = pp.getGap() + w - l;
+                String fmt = "%" + asBlankArg(g) + "s%s";
+
+                PrintParameters pp2 = printCtx.parameters().cloneParameters();
+                pp2.setQuote(false);
+                pp2.setRight(true);
+                out.printf(fmt, "", StringPrinter.encode(tmp, l, pp2));
+            } else {
+                String g1 = asBlankArg(pp.getGap());
+                String g2 = asBlankArg(w - indexWidth(j + 1) - 3);
+                String fmt = "%" + g1 + "s[,%d]%" + g2 + "s";
+                out.printf(fmt, "", j + 1, "");
+            }
+        }
+
+        protected void leftMatrixColumnLabel(RAbstractStringVector cl, int j, int w) {
+            PrintParameters pp = printCtx.parameters();
+
+            if (cl != null) {
+                String tmp = cl.getDataAt(j);
+                int l = (RRuntime.isNA(tmp)) ? pp.getNa_width_noquote() : tmp.length();
+                String g1 = asBlankArg(pp.getGap());
+                String g2 = asBlankArg(w - l);
+                String fmt = "%" + g1 + "s%s%" + g2 + "s";
+
+                PrintParameters pp2 = printCtx.parameters().cloneParameters();
+                pp2.setQuote(false);
+                pp2.setRight(false);
+                out.printf(fmt, "", StringPrinter.encode(tmp, l, pp2), "");
+            } else {
+                String g1 = asBlankArg(pp.getGap());
+                String g2 = asBlankArg(w - indexWidth(j + 1) - 3);
+                String fmt = "%" + g1 + "s[,%d]%" + g2 + "s";
+                out.printf(fmt, "", j + 1, "");
+            }
+        }
+
+        protected void matrixRowLabel(RAbstractStringVector rl, int i, int rlabw, int lbloff) {
+            PrintParameters pp = printCtx.parameters();
+
+            if (rl != null) {
+                String tmp = rl.getDataAt(i);
+                int l = (RRuntime.isNA(tmp)) ? pp.getNa_width_noquote() : tmp.length();
+                String gap = asBlankArg(rlabw - l - lbloff);
+                String fmt = "\n%" + asBlankArg(lbloff) + "s%s%" + gap + "s";
+
+                PrintParameters pp2 = printCtx.parameters().cloneParameters();
+                pp2.setQuote(false);
+                pp2.setRight(false);
+                String s = StringPrinter.encode(tmp, l, pp2);
+                out.printf(fmt, "", s, "");
+            } else {
+                String gap = asBlankArg(rlabw - 3 - indexWidth(i + 1));
+                String fmt = "\n%" + gap + "s[%d,]";
+                out.printf(fmt, "", i + 1);
+            }
+        }
+
+        /**
+         * @param offs the beginning offset in the internal store
+         * @param len the number of elements to involve in formatting
+         * @return the format metrics containing the width of the widest vector element and possibly
+         *         other data type specific metrics
+         */
+        protected abstract FormatMetrics formatVector(int offs, int len);
+
+        /**
+         * Prints the i-th vector element.
+         *
+         * @param i the element index (zero-based)
+         * @param fm the format metrics produced by the corresponding <code>formatVector</code>
+         *            invocation
+         * @throws IOException
+         */
+        protected abstract void printElement(int i, FormatMetrics fm) throws IOException;
+
+        /**
+         * Prints the matrix cell at position (i,j).
+         *
+         * @param i the index (zero-based) of the cell in the raw data store
+         * @param fm the format metrics produced by the corresponding <code>formatVector</code>
+         *            invocation
+         * @throws IOException
+         */
+        protected abstract void printCell(int i, FormatMetrics fm) throws IOException;
+
+        protected int matrixIndividualCellColumnWidthCorrection() {
+            return 0;
+        }
+
+        protected int matrixColumnWidthCorrection1() {
+            return printCtx.parameters().getGap();
+        }
+
+        protected int matrixColumnWidthCorrection2() {
+            return 0;
+        }
+
+        private int doLab(int i) {
+            if (indx > 0) {
+                printVectorIndex(i + 1, labwidth, out);
+                return labwidth;
+            } else {
+                return 0;
+            }
+        }
+
+        protected abstract void printEmptyVector() throws IOException;
+
+    }
+
+    private static void printVectorIndex(int i, int w, PrintWriter out) {
+        /* print index label "[`i']" , using total width `w' (left filling blanks) */
+        // out.printf("%*s[%ld]", w - indexWidth(i) - 2, "", i);
+        String blanks = asBlankArg(w - indexWidth(i) - 2);
+        String fmt = "%" + blanks + "s[%d]";
+        out.printf(fmt, "", i);
+    }
+
+    private static int indexWidth(int n) {
+        return (int) (Math.log10(n + 0.5) + 1);
+    }
+
+    private static final class MatrixDimNames {
+        final RAbstractStringVector rl;
+        final RAbstractStringVector cl;
+        final String rn;
+        final String cn;
+
+        private MatrixDimNames(RAbstractStringVector rl, RAbstractStringVector cl, String rn, String cn) {
+            super();
+            this.rl = rl;
+            this.cl = cl;
+            this.rn = rn;
+            this.cn = cn;
+        }
+
+    }
+
+    private static MatrixDimNames getMatrixDimnames(RAbstractVector x) {
+        // output parameters
+        RAbstractStringVector rl;
+        RAbstractStringVector cl;
+        String rn;
+        String cn;
+
+        RList dimnames = Utils.<RList> castTo(x.getAttr(dummyAttrProfiles, RRuntime.DIMNAMES_ATTR_KEY));
+
+        if (dimnames == null) {
+            rl = null;
+            cl = null;
+            rn = null;
+            cn = null;
+        } else {
+            rl = getDimNamesAt(dimnames, 0);
+            cl = getDimNamesAt(dimnames, 1);
+            RAbstractStringVector nn = Utils.<RAbstractStringVector> castTo(dimnames.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY));
+            if (nn == null) {
+                rn = null;
+                cn = null;
+            } else {
+                rn = nn.getDataAt(0);
+                cn = nn.getDataAt(1);
+            }
+        }
+
+        return new MatrixDimNames(rl, cl, rn, cn);
+    }
+
+    private static RAbstractStringVector getDimNamesAt(RList dimNames, int dimLevel) {
+        return toStringVector(dimNames.getDataAt(dimLevel));
+    }
+
+    private static RAbstractStringVector toStringVector(Object o) {
+        if (o instanceof String) {
+            return RString.valueOf((String) o);
+        }
+        return Utils.<RAbstractStringVector> castTo(o);
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
index ca7ae5588d..f3221276f6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
@@ -154,6 +154,10 @@ public class RRuntime {
     public static final String R_LOAD_METHOD_NAME = "loadMethod";
     public static final String R_DOT_METHODS = ".Methods";
     public static final String R_SOURCE = "source";
+    public static final String R_COMMENT = "comment";
+    public static final String R_SRCREF = "srcref";
+    public static final String R_WHOLE_SRCREF = "wholeSrcref";
+    public static final String R_SRCFILE = "srcfile";
 
     public static final String NULL = "NULL";
     public static final String UNBOUND = "UNBOUND";
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 f8a032a818..4b8a58d204 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
@@ -19018,6 +19018,10 @@ null.deviance      deviance
 #argv <- structure(list(x = 0.04, digits = 3, nsmall = 3), .Names = c('x',     'digits', 'nsmall'));do.call('format', argv)
 [1] "0.040"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_format.testformat57
+#x <- c(1.0,2.0);names(x) <- c("x","y");argv <- list(x, FALSE, NULL, 0L, NULL, 0L, FALSE, FALSE);names(.Internal(format(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]], argv[[7]], argv[[8]])))
+[1] "x" "y"
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_format.testformat6
 #argv <- list(structure(c(47.97, 57.9, 74.76, 868.88), .Names = c('<none>', '- x4', '- x2', '- x1')), FALSE, 5L, 0L, NULL, 3L, TRUE, NA); .Internal(format(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]], argv[[7]], argv[[8]]))
   <none>     - x4     - x2     - x1
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_format.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_format.java
index e9c6197361..0cc0bf0c29 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_format.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_format.java
@@ -13,6 +13,7 @@ package com.oracle.truffle.r.test.builtins;
 import org.junit.*;
 
 import com.oracle.truffle.r.test.*;
+import com.sun.source.tree.AssertTree;
 
 // Checkstyle: stop line length check
 public class TestBuiltin_format extends TestBase {
@@ -183,7 +184,8 @@ public class TestBuiltin_format extends TestBase {
 
     @Test
     public void testformat33() {
-        assertEval(Ignored.Unknown, "argv <- list(0+1i, TRUE, NULL, 0L, NULL, 3L, TRUE, NA); .Internal(format(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]], argv[[7]], argv[[8]]))");
+        assertEval(Ignored.Unknown,
+                        "argv <- list(0+1i, TRUE, NULL, 0L, NULL, 3L, TRUE, NA); .Internal(format(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]], argv[[7]], argv[[8]]))");
     }
 
     @Test
@@ -307,6 +309,15 @@ public class TestBuiltin_format extends TestBase {
         assertEval(Ignored.Unknown, "argv <- structure(list(x = 0.04, digits = 3, nsmall = 3), .Names = c('x',     'digits', 'nsmall'));do.call('format', argv)");
     }
 
+    /**
+     * This test checks whether the names of double values in a vector are present in the formatted
+     * output.
+     */
+    @Test
+    public void testformat57() {
+        assertEval("x <- c(1.0,2.0);names(x) <- c(\"x\",\"y\");argv <- list(x, FALSE, NULL, 0L, NULL, 0L, FALSE, FALSE);names(.Internal(format(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]], argv[[7]], argv[[8]])))");
+    }
+
     public void testFormat() {
         assertEval("{ format(7) }");
         assertEval("{ format(7.42) }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleArithmetic.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleArithmetic.java
index 546d524198..f92fd6c506 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleArithmetic.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleArithmetic.java
@@ -107,7 +107,8 @@ public class TestSimpleArithmetic extends TestBase {
 
     @Test
     /**
-     * FIXME These expressions evaluate correctly in the shell but produce 1+0i in unit test environment
+     * FIXME These expressions evaluate correctly in the shell but produce 1+0i in unit test
+     * environment
      */
     public void testScalarsComplexIgnore() {
         assertEval("{ (1+2i)^(-2) }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java
index 46b61ff2fa..08fd9a2102 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.test.rpackages;
 
 import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -35,14 +36,28 @@ public class TestRFFIPackage extends TestRPackages {
 
     private static final String[] TEST_PACKAGES = new String[]{"testrffi"};
 
+    @Override
+    public void beforeTest() {
+        // TODO Auto-generated method stub
+        super.beforeTest();
+        setupInstallTestPackages(TEST_PACKAGES);
+    }
+
+    @Override
+    public void afterTest() {
+        // TODO Auto-generated method stub
+        super.afterTest();
+        tearDownUninstallTestPackages(TEST_PACKAGES);
+    }
+
     @BeforeClass
     public static void setupInstallMyTestPackages() {
-        setupInstallTestPackages(TEST_PACKAGES);
+// setupInstallTestPackages(TEST_PACKAGES);
     }
 
     @AfterClass
     public static void tearDownUninstallMyTestPackages() {
-        tearDownUninstallTestPackages(TEST_PACKAGES);
+// tearDownUninstallTestPackages(TEST_PACKAGES);
     }
 
     @Test
@@ -50,8 +65,9 @@ public class TestRFFIPackage extends TestRPackages {
         assertEval(TestBase.template("{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.addInt(2L, 3L);  detach(\"package:testrffi\"); list(r1) }",
                         new String[]{packagePaths.rpackagesLibs.toString()}));
         assertEval(TestBase.template(
-                        "{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.addInt(2L, 3L); r2 <- rffi.addDouble(2, 3); v <- rffi.populateIntVector(5); v2 <- rffi.dotCModifiedArguments(c(0,1,2,3)); "
-                                        + "detach(\"package:testrffi\"); list(r1, r2, v, v2) }", new String[]{packagePaths.rpackagesLibs.toString()}));
+                        "{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.addInt(2L, 3L); r2 <- rffi.addDouble(2, 3); v <- rffi.populateIntVector(5); v2 <- rffi.dotCModifiedArguments(c(0,1,2,3)); " +
+                                        "detach(\"package:testrffi\"); list(r1, r2, v, v2) }",
+                        new String[]{packagePaths.rpackagesLibs.toString()}));
     }
 
     @Test
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index e914bfe324..79381b6535 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -688,3 +688,10 @@ com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleV
 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/data/tree2/incx.r,no.copyright
 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/data/tree2/setx.r,no.copyright
 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java,purdue.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoublePrinter.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintParameters.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringPrinter.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/StringVectorPrinter.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java,gnu_r_gentleman_ihaka2.copyright
\ No newline at end of file
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index 75b81ec0ea..cc027597e6 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -149,6 +149,9 @@ def setREnvironment():
     if not os.environ.has_key('R_HOME'):
         os.environ['R_HOME'] = _fastr_suite.dir
 
+    # Make sure that native code formats numbers consistently
+    os.environ['LC_NUMERIC'] = 'C'
+
     osname = platform.system()
     if osname != 'Darwin':
         lib_env = 'LD_LIBRARY_PATH'
-- 
GitLab