From 1724375c46144576f15b4d9a02e48fcf947175de Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Fri, 1 Apr 2016 17:34:20 +0200
Subject: [PATCH] deparsing refactoring

---
 .../r/engine/RRuntimeASTAccessImpl.java       |   16 +-
 .../r/library/methods/SubstituteDirect.java   |    4 +-
 .../truffle/r/nodes/builtin/base/Args.java    |    4 +-
 .../r/nodes/builtin/base/AsCharacter.java     |    2 +-
 .../r/nodes/builtin/base/AsFunction.java      |    4 +-
 .../truffle/r/nodes/builtin/base/DPut.java    |    2 +-
 .../truffle/r/nodes/builtin/base/Deparse.java |    2 +-
 .../builtin/base/HiddenInternalFunctions.java |    4 +-
 .../nodes/builtin/base/PrettyPrinterNode.java |   18 +-
 .../r/nodes/builtin/base/Substitute.java      |    5 +-
 .../truffle/r/nodes/builtin/base/Switch.java  |    9 +-
 .../builtin/base/printer/FunctionPrinter.java |    2 +-
 .../builtin/base/printer/LanguagePrinter.java |   16 +-
 .../builtin/base/printer/SymbolPrinter.java   |    4 +-
 .../nodes/builtin/helpers/DebugHandling.java  |    6 +-
 .../com/oracle/truffle/r/nodes/RASTUtils.java |    9 +-
 .../truffle/r/nodes/access/ConstantNode.java  |   47 -
 .../access/ReadVariadicComponentNode.java     |    8 -
 .../access/WriteCurrentVariableNode.java      |    8 -
 .../nodes/access/WriteSuperVariableNode.java  |    8 -
 .../access/WriteVariableNodeSyntaxHelper.java |   10 -
 .../access/variables/ReadVariableNode.java    |    8 -
 .../truffle/r/nodes/control/BlockNode.java    |   20 -
 .../truffle/r/nodes/control/BreakNode.java    |    8 -
 .../truffle/r/nodes/control/ForNode.java      |   13 -
 .../truffle/r/nodes/control/IfNode.java       |   28 -
 .../truffle/r/nodes/control/NextNode.java     |    8 -
 .../r/nodes/control/ReplacementNode.java      |   15 +-
 .../truffle/r/nodes/control/WhileNode.java    |   15 -
 .../function/FunctionDefinitionNode.java      |   30 -
 .../function/FunctionExpressionNode.java      |    8 -
 .../r/nodes/function/GroupDispatchNode.java   |   17 -
 .../truffle/r/nodes/function/PromiseNode.java |    9 -
 .../truffle/r/nodes/function/RCallNode.java   |   80 +-
 .../truffle/r/nodes/runtime/RASTDeparse.java  |  225 --
 .../truffle/r/nodes/unary/CastStringNode.java |    2 +-
 .../r/nodes/unary/GetNonSharedNode.java       |    6 -
 .../oracle/truffle/r/runtime/RDeparse.java    | 1935 +++++++----------
 .../truffle/r/runtime/RRuntimeASTAccess.java  |   11 +-
 .../oracle/truffle/r/runtime/RSerialize.java  |    6 +-
 .../truffle/r/runtime/nodes/RBaseNode.java    |    6 -
 .../truffle/r/runtime/nodes/RSyntaxNode.java  |    6 +-
 .../r/runtime/nodes/RSyntaxNodeSPI.java       |    8 +-
 mx.fastr/copyrights/overrides                 |    1 -
 44 files changed, 793 insertions(+), 1860 deletions(-)
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 1ff20ee23e..0881ec1d11 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -51,12 +51,10 @@ import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
 import com.oracle.truffle.r.nodes.function.GroupDispatchNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RInternalSourceDescriptions;
@@ -363,16 +361,6 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
         }
     }
 
-    @Override
-    public void deparse(State state, RLanguage rl) {
-        RASTDeparse.deparse(state, rl);
-    }
-
-    @Override
-    public void deparse(State state, RFunction f) {
-        RASTDeparse.deparse(state, f);
-    }
-
     @Override
     public Object callback(RFunction f, Object[] args) {
         boolean gd = DebugHandling.globalDisable(true);
@@ -545,6 +533,10 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
         }
     }
 
+    public RSyntaxFunction getSyntaxFunction(RFunction f) {
+        return (FunctionDefinitionNode) f.getTarget().getRootNode();
+    }
+
     private static Object getCallerFromFrame(Frame frame) {
         if (frame == null) {
             // parser error
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
index 4978cc5bcc..8082bff689 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.RList2EnvNodeGen;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
+import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -48,7 +48,7 @@ public abstract class SubstituteDirect extends RExternalBuiltinNode.Arg2 {
             RSyntaxNode snode = lang.getRep().asRSyntaxNode();
             RSyntaxNode subRNode = snode.substituteImpl(env);
             // create source for entire tree
-            RASTDeparse.ensureSourceSection(subRNode);
+            RDeparse.ensureSourceSection(subRNode);
             return RASTUtils.createLanguageElement(subRNode.asRNode());
         } else {
             return object;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java
index 0a790ec670..65ff6a5714 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java
@@ -35,9 +35,9 @@ import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.SaveArgumentsNode;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -87,7 +87,7 @@ public abstract class Args extends RBuiltinNode {
         String newDesc = "args(" + rootNode.getDescription() + ")";
         FunctionDefinitionNode newNode = FunctionDefinitionNode.create(RSyntaxNode.EAGER_DEPARSE, rootNode.getFrameDescriptor(), null, SaveArgumentsNode.NO_ARGS,
                         ConstantNode.create(RSyntaxNode.EAGER_DEPARSE, RNull.instance), formals, newDesc, null);
-        RASTDeparse.ensureSourceSection(newNode);
+        RDeparse.ensureSourceSection(newNode);
         return RDataFactory.createFunction(newDesc, Truffle.getRuntime().createCallTarget(newNode), null, REnvironment.globalEnv().getFrame(), null, false);
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
index 0aa2b5bd86..00ad207d38 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
@@ -141,7 +141,7 @@ public abstract class AsCharacter extends RBuiltinNode {
             } else if (elem instanceof RStringVector && ((RStringVector) elem).getLength() == 1) {
                 data[i] = ((RStringVector) elem).getDataAt(0);
             } else {
-                data[i] = RDeparse.deparse1Line(elem, false);
+                data[i] = RDeparse.deparse(elem, RDeparse.MAX_Cutoff, true, 0, -1);
             }
             if (RRuntime.isNA(data[i])) {
                 complete = RDataFactory.INCOMPLETE_VECTOR;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java
index ed87bf4a51..73823c9215 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java
@@ -36,10 +36,10 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.SaveArgumentsNode;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -135,7 +135,7 @@ public abstract class AsFunction extends RBuiltinNode {
         FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<as.function.default>", descriptor);
         FrameSlotChangeMonitor.initializeEnclosingFrame(descriptor, envir.getFrame());
         FunctionDefinitionNode rootNode = FunctionDefinitionNode.create(RSyntaxNode.EAGER_DEPARSE, descriptor, null, saveArguments, (RSyntaxNode) body, formals, "from AsFunction", null);
-        RASTDeparse.ensureSourceSection(rootNode);
+        RDeparse.ensureSourceSection(rootNode);
         RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
         boolean containsDispatch = ((FunctionDefinitionNode) callTarget.getRootNode()).containsDispatch();
         return RDataFactory.createFunction(RFunction.NO_NAME, callTarget, null, envir.getFrame(), null, containsDispatch);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
index 825387ba53..89da1c8285 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
@@ -51,7 +51,7 @@ public abstract class DPut extends RInvisibleBuiltinNode {
     protected Object dput(Object x, RConnection file, int opts) {
         controlVisibility();
 
-        String string = RDeparse.deparse1Line(x, false, RDeparse.DEFAULT_Cutoff, opts);
+        String string = RDeparse.deparse(x, RDeparse.DEFAULT_Cutoff, true, opts, -1);
         try (RConnection openConn = file.forceOpen("wt")) {
             openConn.writeString(string, true);
         } catch (IOException ex) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java
index dca97289df..d9914efa4c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java
@@ -47,7 +47,7 @@ public abstract class Deparse extends RBuiltinNode {
             widthCutoff = RDeparse.DEFAULT_Cutoff;
         }
 
-        String[] data = RDeparse.deparse(expr, widthCutoff, RRuntime.fromLogical(backtick.getDataAt(0)), control, nlines);
+        String[] data = RDeparse.deparse(expr, widthCutoff, RRuntime.fromLogical(backtick.getDataAt(0)), control, nlines).split("\n");
         return RDataFactory.createStringVector(data, RDataFactory.COMPLETE_VECTOR);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
index ae2c1e1935..e1eb81169e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
@@ -38,7 +38,6 @@ import com.oracle.truffle.r.nodes.builtin.base.EvalFunctions.Eval;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.function.SubstituteVirtualFrame;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
@@ -47,6 +46,7 @@ import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RCompression;
+import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -115,7 +115,7 @@ public class HiddenInternalFunctions {
                 RCallNode expr0 = RCallNode.createCloneReplacingArgs(callNode, vecNode);
                 try {
                     // We want this call to have a SourceSection
-                    RASTDeparse.ensureSourceSection(expr0);
+                    RDeparse.ensureSourceSection(expr0);
                     aenv.put(name, RDataFactory.createPromise(expr0, eenv));
                 } catch (PutException ex) {
                     /*
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 6b74a77c2f..931e2cf1a4 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
@@ -291,7 +291,7 @@ public abstract class PrettyPrinterNode extends RNode {
         } else {
             String source = ((RRootNode) operand.getTarget().getRootNode()).getSourceCode();
             if (source == null || !useSource) {
-                source = RDeparse.deparseForPrint(operand);
+                source = RDeparse.deparse(operand);
             }
             REnvironment env = RArguments.getEnvironment(operand.getEnclosingFrame());
             if (env != null && env.isNamespaceEnv()) {
@@ -358,21 +358,7 @@ public abstract class PrettyPrinterNode extends RNode {
     }
 
     private static String prettyPrintLanguageInternal(RLanguage language) {
-        String[] lines = RDeparse.deparse(language, 60, false, 0, -1);
-        if (lines.length == 1) {
-            return lines[0];
-        } else {
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < lines.length; i++) {
-                sb.append(lines[i]);
-                if (i == lines.length - 1) {
-                    continue;
-                }
-                sb.append('\n');
-            }
-            return sb.toString();
-
-        }
+        return RDeparse.deparse(language, 60, false, 0, -1);
     }
 
     private static String prettyPrintPromise(RPromise promise) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
index fb81a1b1a0..49c1914483 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
@@ -33,8 +33,8 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.control.IfNode;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
 import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -126,7 +126,8 @@ public abstract class Substitute extends RBuiltinNode {
         RSyntaxNode rNode = (RSyntaxNode) RASTUtils.cloneNode(node);
         RSyntaxNode subRNode = rNode.substituteImpl(env);
         // create source for entire tree
-        RASTDeparse.ensureSourceSection(subRNode);
+        subRNode.setSourceSection(RSyntaxNode.EAGER_DEPARSE);
+        RDeparse.ensureSourceSection(subRNode);
         return RASTUtils.createLanguageElement(subRNode.asRNode());
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
index 73ea368e25..a6f10226ce 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
@@ -26,14 +26,12 @@ import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
  * The {@code switch} builtin. When called directly, the "..." arguments are not evaluated before
@@ -123,12 +121,9 @@ public abstract class Switch extends RBuiltinNode {
         if (arg instanceof RPromise) {
             // We do not want to evaluate the promise,just display the rep
             RPromise p = (RPromise) arg;
-            RBaseNode node = p.getRep();
-            State state = State.createPrintableState();
-            node.deparse(state);
-            return state.toString();
+            return RDeparse.deparseSyntaxElement(p.getRep().asRSyntaxNode());
         } else {
-            return RDeparse.deparseForPrint(arg);
+            return RDeparse.deparse(arg);
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java
index 493318dac7..e8c0bc6d2b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java
@@ -76,7 +76,7 @@ final class FunctionPrinter extends AbstractValuePrinter<RFunction> {
             final boolean useSource = printCtx.parameters().getUseSource();
             String source = ((RRootNode) operand.getTarget().getRootNode()).getSourceCode();
             if (source == null || !useSource) {
-                source = RDeparse.deparseForPrint(operand);
+                source = RDeparse.deparse(operand);
             }
             REnvironment env = RArguments.getEnvironment(operand.getEnclosingFrame());
             if (env != null && env.isNamespaceEnv()) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LanguagePrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LanguagePrinter.java
index 182aa7319f..f47b7a21b9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LanguagePrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/LanguagePrinter.java
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.nodes.builtin.base.printer;
 
 import java.io.IOException;
-import java.io.PrintWriter;
 
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -38,19 +37,6 @@ final class LanguagePrinter extends AbstractValuePrinter<RLanguage> {
 
     @Override
     protected void printValue(RLanguage language, PrintContext printCtx) throws IOException {
-        final PrintWriter out = printCtx.output();
-
-        String[] lines = RDeparse.deparse(language, 60, false, 0, -1);
-        if (lines.length == 1) {
-            out.print(lines[0]);
-        } else {
-            for (int i = 0; i < lines.length; i++) {
-                out.print(lines[i]);
-                if (i == lines.length - 1) {
-                    continue;
-                }
-                out.println();
-            }
-        }
+        printCtx.output().print(RDeparse.deparse(language, RDeparse.DEFAULT_Cutoff, false, 0, -1));
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/SymbolPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/SymbolPrinter.java
index 8b8b38c8e2..7f8080ae07 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/SymbolPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/SymbolPrinter.java
@@ -37,8 +37,6 @@ final class SymbolPrinter extends AbstractValuePrinter<RSymbol> {
 
     @Override
     protected void printValue(RSymbol value, PrintContext printCtx) throws IOException {
-        String[] dp = RDeparse.deparse(value, RDeparse.DEFAULT_Cutoff, false,
-                        RDeparse.SIMPLEDEPARSE, -1);
-        printCtx.output().print(dp[0]);
+        printCtx.output().print(RDeparse.deparse(value, RDeparse.DEFAULT_Cutoff, true, RDeparse.SIMPLEDEPARSE, -1));
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
index 09fcc78f79..ad0d6d9ee5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
@@ -36,8 +36,8 @@ import com.oracle.truffle.api.instrumentation.Instrumenter;
 import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
 import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.nodes.control.AbstractLoopNode;
@@ -440,9 +440,7 @@ public class DebugHandling {
          * N.B. It would seem that GnuR does a deparse that because, e.g., a function that ends with
          * } without a preceding newline prints with one and indentation is standardized.
          */
-        RDeparse.State state = RDeparse.State.createPrintableState();
         RBaseNode rNode = (RBaseNode) node;
-        rNode.deparse(state);
         boolean curly = RSyntaxCall.isCallTo((RSyntaxElement) node, "{");
 
         if (startFunction && !curly) {
@@ -455,7 +453,7 @@ public class DebugHandling {
             }
             consoleHandler.print("debug at " + path + "#" + source.getStartLine() + ": ");
         }
-        consoleHandler.print(state.toString());
+        consoleHandler.print(RDeparse.deparseSyntaxElement(rNode.asRSyntaxNode()));
         consoleHandler.print("\n");
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
index 24ed425eb6..86b18a11af 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
@@ -44,7 +44,6 @@ import com.oracle.truffle.r.nodes.function.WrapArgumentNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -278,9 +277,7 @@ public class RASTUtils {
         } else {
             // TODO This should really fail in some way as (clearly) this is not a "name"
             // some more complicated expression, just deparse it
-            RDeparse.State state = RDeparse.State.createPrintableState();
-            child.deparse(state);
-            return RDataFactory.createSymbolInterned(state.toString());
+            return RDataFactory.createSymbolInterned(RDeparse.deparse(child));
         }
     }
 
@@ -380,10 +377,6 @@ public class RASTUtils {
      * Marker class for special '...' handling.
      */
     private abstract static class DotsNode extends RNode implements RSyntaxNode {
-        @Override
-        public void deparseImpl(State state) {
-            throw RInternalError.unimplemented();
-        }
 
         @Override
         public RSyntaxNode substituteImpl(REnvironment env) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
index a62a976bf5..b71392f90e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
@@ -23,12 +23,8 @@
 package com.oracle.truffle.r.nodes.access;
 
 import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.RASTUtils;
-import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
@@ -75,14 +71,6 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
         return getValue();
     }
 
-    @Override
-    @TruffleBoundary
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        RDeparse.deparse2buff(state, getValue());
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public RSyntaxNode substituteImpl(REnvironment env) {
         return this;
@@ -201,41 +189,6 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
             return value;
         }
 
-        @Override
-        @TruffleBoundary
-        public void deparseImpl(RDeparse.State state) {
-            if (value == RMissing.instance || value instanceof RArgsValuesAndNames) {
-                state.startNodeDeparse(this);
-                if (value == RMissing.instance) {
-                    // nothing to do
-                } else if (value instanceof RArgsValuesAndNames) {
-                    RArgsValuesAndNames args = (RArgsValuesAndNames) value;
-                    Object[] values = args.getArguments();
-                    for (int i = 0; i < values.length; i++) {
-                        String name = args.getSignature().getName(i);
-                        if (name != null) {
-                            state.append(name);
-                            state.append(" = ");
-                        }
-                        Object argValue = values[i];
-                        if (argValue instanceof RSyntaxNode) {
-                            ((RSyntaxNode) argValue).deparseImpl(state);
-                        } else if (argValue instanceof RPromise) {
-                            RASTUtils.unwrap(((RPromise) argValue).getRep()).deparse(state);
-                        } else {
-                            RInternalError.shouldNotReachHere();
-                        }
-                        if (i < values.length - 1) {
-                            state.append(", ");
-                        }
-                    }
-                }
-                state.endNodeDeparse(this);
-            } else {
-                super.deparseImpl(state);
-            }
-        }
-
         @Override
         public void serializeImpl(RSerialize.State state) {
             if (value == RMissing.instance) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
index 28043f5fa1..3c16440037 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RType;
@@ -98,13 +97,6 @@ public class ReadVariadicComponentNode extends RSourceSectionNode implements RSy
         return ".." + Integer.toString(index + 1);
     }
 
-    @Override
-    public void deparseImpl(State state) {
-        state.startNodeDeparse(this);
-        state.append(getPrintForm());
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public RSyntaxNode substituteImpl(REnvironment env) {
         throw RInternalError.unimplemented();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
index 027986264b..e1cfa015e0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
@@ -28,7 +28,6 @@ import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -77,13 +76,6 @@ public class WriteCurrentVariableNode extends WriteVariableNodeSyntaxHelper impl
         writeLocalFrameVariableNode.execute(frame, value);
     }
 
-    @Override
-    public void deparseImpl(State state) {
-        state.startNodeDeparse(this);
-        deparseHelper(state, " <- ");
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         serializeHelper(state, "<-");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
index 249cdaf317..ae6f6b56e5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
@@ -27,7 +27,6 @@ import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
@@ -83,13 +82,6 @@ public class WriteSuperVariableNode extends WriteVariableNodeSyntaxHelper implem
         writeSuperFrameVariableNode.execute(frame, value);
     }
 
-    @Override
-    public void deparseImpl(State state) {
-        state.startNodeDeparse(this);
-        deparseHelper(state, " <<- ");
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         serializeHelper(state, "<<-");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java
index ba559c9f03..27f7ff678e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNodeSyntaxHelper.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.nodes.access;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -37,15 +36,6 @@ abstract class WriteVariableNodeSyntaxHelper extends WriteVariableNode {
         this.sourceSectionR = sourceSection;
     }
 
-    protected void deparseHelper(RDeparse.State state, String op) {
-        state.append(getName().toString());
-        RNode rhs = getRhs();
-        if (rhs != null) {
-            state.append(op);
-            getRhs().deparse(state);
-        }
-    }
-
     protected void serializeHelper(RSerialize.State state, String op) {
         RNode rhs = getRhs();
         if (rhs == null) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
index 7ec2ccab2b..cdf8187d4b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
@@ -52,7 +52,6 @@ import com.oracle.truffle.r.runtime.AnonymousFrameVariable;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -163,13 +162,6 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         return mode;
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        state.append(RDeparse.quotify(identifierAsString, state));
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setCarAsSymbol(identifierAsString);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
index 48e1397b39..a153a33b36 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
@@ -28,7 +28,6 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -70,25 +69,6 @@ public final class BlockNode extends RSourceSectionNode implements RSyntaxNode,
         return lastResult;
     }
 
-    @TruffleBoundary
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        // empty deparses as {}
-        state.writeOpenCurlyNLIncIndent();
-        for (int i = 0; i < sequence.length; i++) {
-            state.mark();
-            sequence[i].deparse(state);
-            if (state.changed()) {
-                // not all nodes will produce output
-                state.writeline();
-                state.mark(); // in case last
-            }
-        }
-        state.decIndentWriteCloseCurly();
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsLangType();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
index 5c0105bc3d..50bd80a94b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
@@ -25,7 +25,6 @@ package com.oracle.truffle.r.nodes.control;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -41,13 +40,6 @@ public final class BreakNode extends RSourceSectionNode implements RSyntaxNode,
         super(src);
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        state.append("break");
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsBuiltin("break");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
index f01f41a695..b2f6ec6cba 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
@@ -38,7 +38,6 @@ import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.runtime.AnonymousFrameVariable;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -97,18 +96,6 @@ public final class ForNode extends AbstractLoopNode implements VisibilityControl
         return getForRepeatingNode().body;
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        state.append("for (");
-        getCvar().deparse(state);
-        state.append(" in ");
-        getRange().deparse(state);
-        state.append(") ");
-        getBody().deparse(state);
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsBuiltin("for");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java
index 92a14b0d2e..dd489256d7 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java
@@ -26,10 +26,8 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.unary.ConvertBooleanNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
@@ -108,32 +106,6 @@ public final class IfNode extends RSourceSectionNode implements RSyntaxNode, RSy
         return elsePart;
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        state.append("if (");
-        condition.deparse(state);
-        state.append(") ");
-        if (RASTUtils.unwrap(thenPart) instanceof BlockNode && ((BlockNode) RASTUtils.unwrap(thenPart)).getSequence().length > 1) {
-            thenPart.deparse(state);
-            if (elsePart != null) {
-                state.writeline();
-                state.append("else ");
-                elsePart.deparse(state);
-            }
-        } else {
-            state.writeline();
-            state.incIndent();
-            thenPart.deparse(state);
-            state.decIndent();
-            if (elsePart != null) {
-                state.append(" else ");
-                elsePart.deparse(state);
-            }
-        }
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsBuiltin("if");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
index 5815f5d2a4..0fac8ccbed 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
@@ -25,7 +25,6 @@ package com.oracle.truffle.r.nodes.control;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -47,13 +46,6 @@ public final class NextNode extends RSourceSectionNode implements RSyntaxNode, R
         throw NextException.instance;
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        state.append("next");
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsBuiltin("next");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
index 68eeaad4d8..2930f24e59 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
@@ -32,7 +32,6 @@ import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -54,7 +53,8 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 public final class ReplacementNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
 
     /**
-     * This is just the left hand side of the assignment and only used for {@link #deparseImpl} etc.
+     * This is just the left hand side of the assignment and only used when looking at the original
+     * structure of this replacement.
      */
     private final RSyntaxNode syntaxLhs;
     private final boolean isSuper;
@@ -110,17 +110,6 @@ public final class ReplacementNode extends RSourceSectionNode implements RSyntax
         return removeRhs.execute(frame);
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        syntaxLhs.deparseImpl(state);
-        state.append(' ');
-        state.append(getSymbol());
-        state.append(' ');
-        storeRhs.getRhs().asRSyntaxNode().deparseImpl(state);
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsLangType();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
index afe163dda1..4a18e69fde 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
@@ -33,7 +33,6 @@ import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.unary.ConvertBooleanNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
@@ -90,20 +89,6 @@ public final class WhileNode extends AbstractLoopNode implements RSyntaxNode, RS
         return isRepeat;
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        if (isRepeat) {
-            state.append("repeat ");
-        } else {
-            state.append("while (");
-            getCondition().deparse(state);
-            state.append(") ");
-        }
-        getBody().deparse(state);
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsBuiltin(isRepeat ? "repeat" : "while");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index 0eaaa98be0..8c60007897 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -53,7 +53,6 @@ import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RArguments.S4Args;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -392,35 +391,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         return description == null ? "<no source>" : description;
     }
 
-    /*
-     * TODO Decide whether we really care about the braces/no-braces issue for deparse and
-     * serialize, since we do not distinguish them in other nodes at the present time.
-     */
-
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        // TODO linebreaks
-        state.startNodeDeparse(this);
-        state.append("function (");
-        FormalArguments formals = getFormalArguments();
-        int formalsLength = formals.getSignature().getLength();
-        for (int i = 0; i < formalsLength; i++) {
-            RNode defaultArg = formals.getDefaultArgument(i);
-            state.append(formals.getSignature().getName(i));
-            if (defaultArg != null) {
-                state.append(" = ");
-                defaultArg.deparse(state);
-            }
-            if (i != formalsLength - 1) {
-                state.append(", ");
-            }
-        }
-        state.append(") ");
-        state.writeline();
-        body.deparse(state);
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public RSyntaxNode substituteImpl(REnvironment env) {
         FrameDescriptor frameDesc = new FrameDescriptor();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java
index 0ca3806a47..d9258b2a3d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java
@@ -36,7 +36,6 @@ import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseDeoptimizeFr
 import com.oracle.truffle.r.nodes.function.opt.EagerEvalHelper;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.data.FastPathFactory;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -102,13 +101,6 @@ public final class FunctionExpressionNode extends RSourceSectionNode implements
         return callTarget;
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        state.startNodeDeparse(this);
-        ((FunctionDefinitionNode) callTarget.getRootNode()).deparseImpl(state);
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsBuiltin("function");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
index 93af4b7477..9493f0c100 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
@@ -22,11 +22,9 @@ import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.NoGenericMethodException;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RGroupGenerics;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -95,21 +93,6 @@ public final class GroupDispatchNode extends RSourceSectionNode implements RSynt
         return getSourceSection();
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        String name = getGenericName();
-        RDeparse.Func func = RDeparse.getFunc(name);
-        state.startNodeDeparse(this);
-        if (func != null) {
-            // infix operator
-            RASTDeparse.deparseInfixOperator(state, this, func);
-        } else {
-            state.append(name);
-            RCallNode.deparseArguments(state, callArgsNode.getSyntaxArguments(), callArgsNode.signature);
-        }
-        state.endNodeDeparse(this);
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         String name = getGenericName();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
index 7bf0554223..a7aa26bf1a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
@@ -44,7 +44,6 @@ import com.oracle.truffle.r.nodes.function.opt.OptConstantPromiseNode;
 import com.oracle.truffle.r.nodes.function.opt.OptForcedEagerPromiseNode;
 import com.oracle.truffle.r.nodes.function.opt.OptVariablePromiseBaseNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RSerialize.State;
 import com.oracle.truffle.r.runtime.RType;
@@ -326,14 +325,6 @@ public abstract class PromiseNode extends RNode {
             return index;
         }
 
-        @Override
-        public void deparseImpl(RDeparse.State state) {
-            int num = index + 1;
-            state.startNodeDeparse(this);
-            state.append((num < 10 ? ".." : ".") + num);
-            state.endNodeDeparse(this);
-        }
-
         @Override
         public void serializeImpl(State state) {
             throw RInternalError.unimplemented();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
index 47e1711da4..596c3cf5ab 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
@@ -61,7 +61,6 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinRootNode;
 import com.oracle.truffle.r.nodes.function.MatchedArguments.MatchedArgumentsNode;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
 import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
-import com.oracle.truffle.r.nodes.runtime.RASTDeparse;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
@@ -69,7 +68,6 @@ import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.RDeparse.Func;
 import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -83,7 +81,6 @@ import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
@@ -425,81 +422,6 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
         return CallArgumentsNode.create(modeChange, modeChangeAppliesToAll, args, signature);
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        Object fname = RASTUtils.findFunctionName(this);
-        Func func = RASTDeparse.isInfixOperator(fname);
-        try {
-            state.startNodeDeparse(this);
-            if (func != null && arguments.v.length > 0) {
-                RASTDeparse.deparseInfixOperator(state, this, func);
-            } else {
-                if (fname instanceof RSymbol) {
-                    String sfname = ((RSymbol) fname).getName();
-                    if (sfname.equals(":::") || sfname.equals("::")) {
-                        // special infix, could be a:::b() or a:::b
-                        RNode fn = getFunctionNode().unwrap();
-                        RSyntaxNode[] argValues;
-                        if (fn instanceof RCallNode) {
-                            argValues = ((RCallNode) fn).arguments.v;
-                        } else {
-                            argValues = arguments.v;
-                        }
-                        argValues[0].deparseImpl(state);
-                        state.append(sfname);
-                        argValues[1].deparseImpl(state);
-                        if (!(fn instanceof RCallNode)) {
-                            return;
-                        }
-                    } else if (sfname.equals("[<-") || sfname.equals("[[<-")) {
-                        boolean isSubset = sfname.equals("[<-");
-                        arguments.v[0].deparseImpl(state);
-                        state.append(isSubset ? "[" : "[[");
-                        for (int i = 1; i < arguments.v.length - 1; i++) {
-                            if (signature.getName(i) != null && !signature.getName(i).isEmpty()) {
-                                state.append(signature.getName(i));
-                                state.append('=');
-                            }
-                            arguments.v[i].deparseImpl(state);
-                            if (i != arguments.v.length - 2) {
-                                state.append(", ");
-                            }
-                        }
-                        state.append(isSubset ? "]" : "]]");
-                        state.append(" <- ");
-                        arguments.v[arguments.v.length - 1].deparseImpl(state);
-                        return;
-                    }
-                }
-                getFunctionNode().deparse(state);
-
-                deparseArguments(state, arguments.v, signature);
-            }
-        } finally {
-            state.endNodeDeparse(this);
-        }
-    }
-
-    public static void deparseArguments(RDeparse.State state, RSyntaxNode[] arguments, ArgumentsSignature signature) {
-        state.append('(');
-        for (int i = 0; i < arguments.length; i++) {
-            RSyntaxNode argument = arguments[i];
-            String name = signature.getName(i);
-            if (name != null) {
-                state.append(RDeparse.quotify(name, state));
-                state.append(" = ");
-            }
-            if (argument != null) {
-                // e.g. not f(, foo)
-                argument.deparseImpl(state);
-            }
-            if (i != arguments.length - 1) {
-                state.append(", ");
-            }
-        }
-        state.append(')');
-    }
-
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsLangType();
@@ -632,7 +554,7 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
     public static RCallNode createCall(SourceSection src, RNode function, ArgumentsSignature signature, RSyntaxNode... arguments) {
         RCallNode call = new RCallNode(src, function, arguments, signature);
         if (src == RSyntaxNode.EAGER_DEPARSE) {
-            RASTDeparse.ensureSourceSection(call);
+            RDeparse.ensureSourceSection(call);
         }
         return call;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java
deleted file mode 100644
index f77037cb76..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 1995-2012, The R Core Team
- * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.nodes.runtime;
-
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.RASTUtils;
-import com.oracle.truffle.r.nodes.access.ConstantNode;
-import com.oracle.truffle.r.nodes.function.GroupDispatchNode;
-import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.runtime.Arguments;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.RDeparse.Func;
-import com.oracle.truffle.r.runtime.RDeparse.PP;
-import com.oracle.truffle.r.runtime.RDeparse.PPInfo;
-import com.oracle.truffle.r.runtime.RDeparse.State;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
-
-/**
- * Deparse support for AST instances.
- *
- * N.B. We ignore the {@link SourceSection} for GnuR compatibility. E.g. in <a
- * href="https://stat.ethz.ch/R-manual/R-devel/library/base/html/deparse.html">deparse
- * specification</a>: "To avoid the risk of a source attribute out of sync with the actual function
- * definition, the source attribute of a function will never be deparsed as an attribute."
- *
- * Parts transcribed from GnuR deparse.c
- */
-public class RASTDeparse {
-
-    private static final String SQUARE = "[";
-
-    /**
-     * Ensure that {@code node} has a {@link SourceSection} by deparsing if necessary.
-     */
-    public static void ensureSourceSection(RSyntaxNode node) {
-        SourceSection ss = node.getSourceSection();
-        if (ss == RSyntaxNode.EAGER_DEPARSE) {
-            RDeparse.State state = RDeparse.State.createPrintableStateWithSource();
-            node.deparseImpl(state);
-            state.assignSourceSections();
-        }
-    }
-
-    public static void deparse(State state, RLanguage rl) {
-        RBaseNode node = rl.getRep();
-        node.deparse(state);
-    }
-
-    public static void deparse(State state, RFunction f) {
-        ((RSyntaxNode) f.getRootNode()).deparseImpl(state);
-    }
-
-    public static Func isInfixOperator(Object fname) {
-        if (fname instanceof RSymbol) {
-            Func func = RDeparse.getFunc(((RSymbol) fname).getName());
-            if (func == null) {
-                return null;
-            } else {
-                return func.info.kind == PP.RETURN ? null : func;
-            }
-        }
-        return null;
-    }
-
-    private static Func isInfixOperatorNode(RBaseNode node) {
-        if (node instanceof RCallNode || node instanceof GroupDispatchNode) {
-            Object fname = RASTUtils.findFunctionName(node);
-            return isInfixOperator(fname);
-        } else {
-            return null;
-        }
-    }
-
-    public static void deparseInfixOperator(RDeparse.State state, Node node, RDeparse.Func func) {
-        Arguments<RSyntaxNode> args = RASTUtils.findCallArguments(node);
-        RSyntaxNode[] argValues = args.getArguments();
-        PP kind = func.info.kind;
-        if (kind == PP.BINARY && argValues.length == 1) {
-            kind = PP.UNARY;
-        }
-        switch (kind) {
-            case UNARY:
-                state.append(func.op);
-                argValues[0].deparseImpl(state);
-                break;
-
-            case BINARY:
-            case BINARY2: {
-                // TODO lbreak
-                boolean parens = needsParens(func.info, argValues[0], true);
-                if (parens) {
-                    state.append('(');
-                }
-                argValues[0].deparseImpl(state);
-                if (parens) {
-                    state.append(')');
-                }
-                if (kind == PP.BINARY) {
-                    state.append(' ');
-                }
-                state.append(func.op);
-                if (kind == PP.BINARY) {
-                    state.append(' ');
-                }
-                parens = needsParens(func.info, argValues[1], false);
-                if (parens) {
-                    state.append('(');
-                }
-                argValues[1].deparseImpl(state);
-                if (parens) {
-                    state.append(')');
-                }
-                break;
-            }
-            case SUBSET: {
-                boolean parens = needsParens(func.info, argValues[0], true);
-                if (parens) {
-                    state.append('(');
-                }
-                argValues[0].deparseImpl(state);
-                if (parens) {
-                    state.append(')');
-                }
-                state.append(func.op == SQUARE ? "[" : "[[");
-                ArgumentsSignature signature = args.getSignature();
-                // similar to ArgumentsNode.deparse()
-                for (int i = 1; i < argValues.length; i++) {
-                    RSyntaxNode argument = argValues[i];
-                    String name = signature.getName(i);
-                    if (name != null) {
-                        state.append(name);
-                        state.append(" = ");
-                    }
-                    if (argument != null) {
-                        // e.g. not f(, foo)
-                        argument.deparseImpl(state);
-                    }
-                    if (i != argValues.length - 1) {
-                        state.append(", ");
-                    }
-                }
-                state.append(func.op == SQUARE ? "]" : "]]");
-                break;
-            }
-            case DOLLAR:
-                /*
-                 * Experimentally one cannot assume that the call is well formed, i.e arguments may
-                 * be missing.
-                 */
-                if (argValues.length > 0) {
-                    argValues[0].deparseImpl(state);
-                } else {
-                    state.append("NULL");
-                }
-                state.append(func.op);
-                if (argValues.length > 1) {
-                    String fieldName = ConstantNode.getString(argValues[1]);
-                    if (fieldName != null) {
-                        state.append(fieldName);
-                    } else {
-                        // FIXME: this needs to be handled in RCallNode, not here
-                        argValues[1].deparseImpl(state);
-                    }
-                } else {
-                    state.append("NULL");
-                }
-                break;
-            default:
-                assert false;
-        }
-    }
-
-    private static boolean needsParens(PPInfo mainop, RSyntaxNode arg, boolean isLeft) {
-        RBaseNode node = RASTUtils.unwrap(arg);
-        Func func = isInfixOperatorNode(node);
-        if (func != null) {
-            Arguments<RSyntaxNode> args = RASTUtils.findCallArguments(node);
-            PPInfo arginfo = func.info;
-            switch (arginfo.kind) {
-                case BINARY:
-                case BINARY2:
-                    switch (args.getArguments().length) {
-                        case 1:
-                            if (!isLeft) {
-                                return false;
-                            }
-                            if (arginfo.prec == RDeparse.PREC_SUM) {
-                                arginfo = arginfo.changePrec(RDeparse.PREC_SIGN);
-                            }
-                            break;
-                        case 2:
-                            break;
-                        default:
-                            return false;
-                    }
-                    return RDeparse.checkPrec(mainop, arginfo, isLeft);
-
-                    // CheckStyle: stop case fall through check
-                case UNARY:
-                    if (mainop.prec > arginfo.prec || (mainop.prec == arginfo.prec && isLeft == mainop.rightassoc)) {
-                        return true;
-                    }
-                    // CheckStyle: resume case fall through check
-            }
-        } else {
-            // TODO complex
-        }
-        return false;
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
index 6e881b5d1c..1ec20856b7 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
@@ -68,7 +68,7 @@ public abstract class CastStringNode extends CastStringBaseNode {
         for (int i = 0; i < operand.getLength(); i++) {
             Object o = operand.getDataAtAsObject(i);
             if (o instanceof RLanguage) {
-                sdata[i] = RDeparse.deparseForPrint(o);
+                sdata[i] = RDeparse.deparse(o);
             } else {
                 sdata[i] = toString(o);
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java
index c2c87b42fe..6c0435a233 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java
@@ -27,7 +27,6 @@ import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.data.RShareable;
@@ -51,11 +50,6 @@ public abstract class GetNonSharedNode extends RNode implements RSyntaxNode {
         return o;
     }
 
-    @Override
-    public void deparseImpl(RDeparse.State state) {
-        throw RInternalError.unimplemented("deparseImpl");
-    }
-
     @Override
     public RSyntaxNode substituteImpl(REnvironment env) {
         throw RInternalError.unimplemented("substituteImpl");
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
index 7ea3de6394..adcbe3db22 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
@@ -14,9 +14,9 @@ package com.oracle.truffle.r.runtime;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Map;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -27,13 +27,14 @@ import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDataFrame;
+import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RIntSequence;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -42,11 +43,19 @@ import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
 
 /**
  * Deparsing R objects.
@@ -122,12 +131,12 @@ public class RDeparse {
     public static final int PREC_NS = 16;
     public static final int PREC_SUBSET = 17;
 
-    public static class PPInfo {
+    private static class PPInfo {
         public final PP kind;
         public final int prec;
         public final boolean rightassoc;
 
-        public PPInfo(PP kind, int prec, boolean rightassoc) {
+        PPInfo(PP kind, int prec, boolean rightassoc) {
             this.kind = kind;
             this.prec = prec;
             this.rightassoc = rightassoc;
@@ -138,65 +147,63 @@ public class RDeparse {
         }
     }
 
-    public static class Func {
+    private static class Func {
         public final String op;
+        public final String closeOp;
         public final PPInfo info;
 
-        Func(String op, PPInfo info) {
+        Func(String op, String closeOp, PPInfo info) {
             this.op = op;
+            this.closeOp = closeOp;
             this.info = info;
         }
     }
 
-    // TODO COMPLETE THIS!
-    // @formatter:off
     @CompilationFinal private static final Func[] FUNCTAB = new Func[]{
-        new Func("+", new PPInfo(PP.BINARY, PREC_SUM, false)),
-        new Func("-", new PPInfo(PP.BINARY, PREC_SUM, false)),
-        new Func("*", new PPInfo(PP.BINARY, PREC_PROD, false)),
-        new Func("/", new PPInfo(PP.BINARY, PREC_PROD, false)),
-        new Func("^", new PPInfo(PP.BINARY2, PREC_POWER, false)),
-        new Func("%%", new PPInfo(PP.BINARY2, PREC_PERCENT, false)),
-        new Func("%/%", new PPInfo(PP.BINARY2, PREC_PERCENT, false)),
-        new Func("%*%", new PPInfo(PP.BINARY2, PREC_PERCENT, false)),
-        new Func("==", new PPInfo(PP.BINARY, PREC_COMPARE, false)),
-        new Func("!=", new PPInfo(PP.BINARY, PREC_COMPARE, false)),
-        new Func("<", new PPInfo(PP.BINARY, PREC_COMPARE, false)),
-        new Func("<=", new PPInfo(PP.BINARY, PREC_COMPARE, false)),
-        new Func(">=", new PPInfo(PP.BINARY, PREC_COMPARE, false)),
-        new Func(">", new PPInfo(PP.BINARY, PREC_COMPARE, false)),
-        new Func("&", new PPInfo(PP.BINARY, PREC_AND, false)),
-        new Func("|", new PPInfo(PP.BINARY, PREC_OR, false)),
-        new Func("!", new PPInfo(PP.BINARY, PREC_NOT, false)),
-        new Func("&&", new PPInfo(PP.BINARY, PREC_AND, false)),
-        new Func("||", new PPInfo(PP.BINARY, PREC_OR, false)),
-        new Func(":", new PPInfo(PP.BINARY2, PREC_COLON, false)),
-        new Func("~", new PPInfo(PP.BINARY, PREC_TILDE, false)),
-
-        new Func("if", new PPInfo(PP.IF, PREC_FN, true)),
-        new Func("while", new PPInfo(PP.WHILE, PREC_FN, false)),
-        new Func("for", new PPInfo(PP.FOR, PREC_FN, false)),
-        new Func("repeat", new PPInfo(PP.REPEAT, PREC_FN, false)),
-        new Func("break", new PPInfo(PP.BREAK, PREC_FN, false)),
-        new Func("next", new PPInfo(PP.NEXT, PREC_FN, false)),
-        new Func("return", new PPInfo(PP.RETURN, PREC_FN, false)),
-        new Func("function", new PPInfo(PP.FUNCTION, PREC_FN, false)),
-        new Func("{", new PPInfo(PP.CURLY, PREC_FN, false)),
-        new Func("(", new PPInfo(PP.PAREN, PREC_FN, false)),
-        new Func("<-", new PPInfo(PP.ASSIGN, PREC_LEFT, true)),
-        new Func("=", new PPInfo(PP.ASSIGN, PREC_LEFT, true)),
-        new Func("<<-", new PPInfo(PP.ASSIGN, PREC_LEFT, true)),
-        new Func("[", new PPInfo(PP.SUBSET, PREC_SUBSET, false)),
-        new Func("[[", new PPInfo(PP.SUBSET, PREC_SUBSET, false)),
-        new Func("$", new PPInfo(PP.DOLLAR, PREC_DOLLAR, false)),
-        new Func("@", new PPInfo(PP.DOLLAR, PREC_DOLLAR, false)),
+                    new Func("+", null, new PPInfo(PP.BINARY, PREC_SUM, false)),
+                    new Func("-", null, new PPInfo(PP.BINARY, PREC_SUM, false)),
+                    new Func("*", null, new PPInfo(PP.BINARY, PREC_PROD, false)),
+                    new Func("/", null, new PPInfo(PP.BINARY, PREC_PROD, false)),
+                    new Func("^", null, new PPInfo(PP.BINARY2, PREC_POWER, false)),
+                    new Func("%%", null, new PPInfo(PP.BINARY2, PREC_PERCENT, false)),
+                    new Func("%/%", null, new PPInfo(PP.BINARY2, PREC_PERCENT, false)),
+                    new Func("%*%", null, new PPInfo(PP.BINARY2, PREC_PERCENT, false)),
+                    new Func("==", null, new PPInfo(PP.BINARY, PREC_COMPARE, false)),
+                    new Func("!=", null, new PPInfo(PP.BINARY, PREC_COMPARE, false)),
+                    new Func("<", null, new PPInfo(PP.BINARY, PREC_COMPARE, false)),
+                    new Func("<=", null, new PPInfo(PP.BINARY, PREC_COMPARE, false)),
+                    new Func(">=", null, new PPInfo(PP.BINARY, PREC_COMPARE, false)),
+                    new Func(">", null, new PPInfo(PP.BINARY, PREC_COMPARE, false)),
+                    new Func("&", null, new PPInfo(PP.BINARY, PREC_AND, false)),
+                    new Func("|", null, new PPInfo(PP.BINARY, PREC_OR, false)),
+                    new Func("!", null, new PPInfo(PP.BINARY, PREC_NOT, false)),
+                    new Func("&&", null, new PPInfo(PP.BINARY, PREC_AND, false)),
+                    new Func("||", null, new PPInfo(PP.BINARY, PREC_OR, false)),
+                    new Func(":", null, new PPInfo(PP.BINARY2, PREC_COLON, false)),
+                    new Func("~", null, new PPInfo(PP.BINARY, PREC_TILDE, false)),
+
+                    new Func("if", null, new PPInfo(PP.IF, PREC_FN, true)),
+                    new Func("while", null, new PPInfo(PP.WHILE, PREC_FN, false)),
+                    new Func("for", null, new PPInfo(PP.FOR, PREC_FN, false)),
+                    new Func("repeat", null, new PPInfo(PP.REPEAT, PREC_FN, false)),
+                    new Func("break", null, new PPInfo(PP.BREAK, PREC_FN, false)),
+                    new Func("next", null, new PPInfo(PP.NEXT, PREC_FN, false)),
+                    new Func("return", null, new PPInfo(PP.RETURN, PREC_FN, false)),
+                    new Func("function", null, new PPInfo(PP.FUNCTION, PREC_FN, false)),
+                    new Func("{", "}", new PPInfo(PP.CURLY, PREC_FN, false)),
+                    new Func("(", ")", new PPInfo(PP.PAREN, PREC_FN, false)),
+                    new Func("<-", null, new PPInfo(PP.ASSIGN, PREC_LEFT, true)),
+                    new Func("=", null, new PPInfo(PP.ASSIGN, PREC_LEFT, true)),
+                    new Func("<<-", null, new PPInfo(PP.ASSIGN, PREC_LEFT, true)),
+                    new Func("[", "]", new PPInfo(PP.SUBSET, PREC_SUBSET, false)),
+                    new Func("[[", "]]", new PPInfo(PP.SUBSET, PREC_SUBSET, false)),
+                    new Func("$", null, new PPInfo(PP.DOLLAR, PREC_DOLLAR, false)),
+                    new Func("@", null, new PPInfo(PP.DOLLAR, PREC_DOLLAR, false)),
     };
-    // @formatter:on
 
-    public static final PPInfo BUILTIN = new PPInfo(PP.FUNCALL, PREC_FN, false);
     private static final PPInfo USERBINOP = new PPInfo(PP.BINARY2, PREC_PERCENT, false);
 
-    public static Func getFunc(String op) {
+    private static Func getFunc(String op) {
         for (Func func : FUNCTAB) {
             if (func.op.equals(op)) {
                 return func;
@@ -204,185 +211,100 @@ public class RDeparse {
         }
         // user binary op?
         if (isUserBinop(op)) {
-            return new Func(op, USERBINOP);
+            return new Func(op, null, USERBINOP);
         }
         return null;
     }
 
     private static boolean isUserBinop(String op) {
         int len = op.length();
-        return op.charAt(0) == '%' && op.charAt(len - 1) == '%';
+        return len > 0 && op.charAt(0) == '%' && op.charAt(len - 1) == '%';
     }
 
-    public static PPInfo ppInfo(String op) {
-        Func func = getFunc(op);
-        if (func == null) {
-            // must be a builtin that we don't have in FUNCTAB
-            return BUILTIN;
-        } else {
-            return func.info;
+    /**
+     * Ensure that {@code node} has a {@link SourceSection} by deparsing if necessary.
+     */
+    public static void ensureSourceSection(RSyntaxElement node) {
+        SourceSection ss = node.getSourceSection();
+        if (ss == RSyntaxNode.EAGER_DEPARSE) {
+            new DeparseVisitor(true, RDeparse.MAX_Cutoff, false, -1, 0).append(node).fixupSources();
         }
     }
 
-    public static final class State {
-        private final StringBuilder sb = new StringBuilder();
-        private final ArrayList<String> lines;
-        private int linenumber;
-        private int len;
-        private int incurly;
-        private int inlist;
-        private boolean startline;
-        private int indent;
-        private final int cutoff;
-        private final boolean backtick;
-        private int opts;
-        @SuppressWarnings("unused") private int sourceable;
-        @SuppressWarnings("unused") private int longstring;
-        private final int maxlines;
-        private boolean active = true;
-        @SuppressWarnings("unused") private int isS4;
-        private boolean changed;
-
-        private static class NodeSourceInfo {
-            private final int startCharIndex;
-            private int endCharIndex;
-
-            NodeSourceInfo(int startCharIndex) {
-                this.startCharIndex = startCharIndex;
-            }
-        }
-
-        /**
-         * Used when generating {@link SourceSection}s during deparse.
-         */
-        private HashMap<RSyntaxNode, NodeSourceInfo> nodeMap;
-
-        private State(int widthCutOff, boolean backtick, int maxlines, int opts, boolean needVector) {
-            this.cutoff = widthCutOff;
-            this.backtick = backtick;
-            this.maxlines = maxlines == -1 ? Integer.MAX_VALUE : maxlines;
-            this.opts = opts;
-            lines = needVector ? new ArrayList<>() : null;
-        }
-
-        public static State createPrintableState() {
-            return new RDeparse.State(RDeparse.MAX_Cutoff, false, -1, 0, false);
-        }
-
-        public static State createPrintableStateWithSource() {
-            State result = new RDeparse.State(RDeparse.MAX_Cutoff, false, -1, 0, false);
-            result.nodeMap = new HashMap<>();
-            return result;
-        }
-
-        private void preAppend() {
-            if (startline) {
-                startline = false;
-                indent();
-            }
-        }
-
-        public void indent() {
-            for (int i = 1; i <= indent; i++) {
-                if (i <= 4) {
-                    append("    ");
+    private static Func isInfixOperatorNode(RSyntaxElement element) {
+        if (element instanceof RSyntaxCall) {
+            RSyntaxElement lhs = ((RSyntaxCall) element).getSyntaxLHS();
+            if (lhs instanceof RSyntaxLookup) {
+                String name = ((RSyntaxLookup) lhs).getIdentifier();
+                Func func = RDeparse.getFunc(name);
+                if (func == null) {
+                    return null;
                 } else {
-                    append("  ");
+                    return func.info.kind == PP.RETURN ? null : func;
                 }
             }
         }
+        return null;
+    }
 
-        public void mark() {
-            changed = false;
-        }
-
-        public boolean changed() {
-            return changed;
-        }
-
-        public void incIndent() {
-            indent++;
-        }
+    private interface C extends AutoCloseable {
+        // this interface is used to get a shorter name and remove the checked exception
+        void close();
+    }
 
-        public void decIndent() {
-            indent--;
-        }
+    private static final class SourceSectionElement {
+        public final RSyntaxElement element;
+        public final int start;
+        public final int length;
 
-        public void append(String s) {
-            preAppend();
-            sb.append(s);
-            len += s.length();
-            changed = true;
+        SourceSectionElement(RSyntaxElement element, int start, int length) {
+            this.element = element;
+            this.start = start;
+            this.length = length;
         }
+    }
 
-        public void append(char ch) {
-            preAppend();
-            sb.append(ch);
-            len++;
-            changed = true;
-        }
+    private static final class DeparseVisitor {
 
-        private boolean linebreak(boolean lbreak) {
-            boolean result = lbreak;
-            if (len > cutoff) {
-                if (!lbreak) {
-                    result = true;
-                    indent++;
-                }
-                writeline();
-            }
-            return result;
-        }
+        private final Visitor visitor = new Visitor();
 
-        public void writeline() {
-            if (lines == null) {
-                // nl for debugging really, we don't care about format,
-                // although line length could be an issues also.
-                sb.append('\n');
-            } else {
-                lines.add(sb.toString());
-                sb.delete(0, Integer.MAX_VALUE);
-            }
-            linenumber++;
-            if (linenumber >= maxlines) {
-                active = false;
-            }
-            /* reset */
-            len = 0;
-            startline = true;
-            changed = true;
-        }
+        private final StringBuilder sb = new StringBuilder();
 
-        public void writeOpenCurlyNLIncIndent() {
-            append('{');
-            writeline();
-            incIndent();
-        }
+        private final ArrayList<SourceSectionElement> sources;
 
-        public void writeNLOpenCurlyIncIndent() {
-            writeline();
-            append('{');
-            incIndent();
-        }
+        private final int cutoff;
+        private final boolean backtick;
+        private int opts;
+        private final int nlines;
 
-        public void writeNLDecIndentCloseCurly() {
-            writeline();
-            decIndent();
-            append('}');
-        }
+        private int inCurly = 0;
+        private int inList = 0;
+        private int indent = 0;
+        private int lastLineStart = 0;
 
-        public void decIndentWriteCloseCurly() {
-            decIndent();
-            append('}');
+        DeparseVisitor(boolean storeSource, int cutoff, boolean backtick, int opts, int nlines) {
+            this.cutoff = cutoff;
+            this.backtick = backtick;
+            this.opts = opts;
+            this.nlines = nlines;
+            this.sources = storeSource ? new ArrayList<>() : null;
         }
 
-        @Override
-        public String toString() {
-            // assumes needVector == false
+        public String getContents() {
+            // strip surplus newlines
+            int length = sb.length();
+            while (length > 0) {
+                char c = sb.charAt(length - 1);
+                if (c != '\n' && c != ' ') {
+                    break;
+                }
+                length--;
+            }
+            sb.setLength(length);
             return sb.toString();
         }
 
-        boolean showAttributes() {
+        private boolean showAttributes() {
             return (opts & SHOWATTRIBUTES) != 0;
         }
 
@@ -390,1018 +312,740 @@ public class RDeparse {
             return (opts & QUOTEEXPRESSIONS) != 0;
         }
 
-        private int dIndent = 0;
+        private DeparseVisitor append(char ch) {
+            assert ch != '\n';
+            sb.append(ch);
+            return this;
+        }
 
-        private void dIndent() {
-            for (int i = 0; i < dIndent; i++) {
-                System.out.print(' ');
-            }
+        private DeparseVisitor append(String str) {
+            assert !str.contains("\n");
+            sb.append(str);
+            return this;
         }
 
-        @SuppressWarnings("unused")
-        private void trace(boolean enter, RSyntaxNode node) {
-            String ms;
-            if (enter) {
-                ms = "start";
-                dIndent();
-                dIndent += 2;
+        private C withContext(RSyntaxElement... context) {
+            if (sources == null) {
+                return () -> {
+                };
             } else {
-                ms = "end";
-                dIndent -= 2;
-                dIndent();
+                int startIndex = sb.length();
+                return () -> {
+                    for (RSyntaxElement element : context) {
+                        sources.add(new SourceSectionElement(element, startIndex, sb.length() - startIndex));
+                    }
+                };
             }
-            System.out.printf("%sNodeDeparse (%d): %s%n", ms, +sb.length(), node);
         }
 
-        public void startNodeDeparse(RSyntaxNode node) {
-            if (nodeMap != null) {
-                // trace(true, node);
-                nodeMap.put(node, new NodeSourceInfo(sb.length()));
+        public void fixupSources() {
+            Source source = Source.fromText(sb, "deparse");
+            for (SourceSectionElement s : sources) {
+                SourceSection original = s.element.getSourceSection();
+                SourceSection newSource = source.createSection(null, s.start, s.length);
+                s.element.setSourceSection(newSource);
             }
         }
 
-        public void endNodeDeparse(RSyntaxNode node) {
-            if (nodeMap != null) {
-                // trace(false, node);
-                NodeSourceInfo nsi = nodeMap.get(node);
-                nsi.endCharIndex = sb.length();
+        private DeparseVisitor append(String str, RSyntaxElement... context) {
+            try (C c = withContext(context)) {
+                append(str);
             }
+            return this;
         }
 
-        public void assignSourceSections() {
-            assert nodeMap != null;
-            String sourceString = toString();
-            Source source = Source.fromText(sourceString, "deparse");
-            for (Map.Entry<RSyntaxNode, NodeSourceInfo> entry : nodeMap.entrySet()) {
-                RSyntaxNode node = entry.getKey();
-                NodeSourceInfo nsi = entry.getValue();
-                node.setSourceSection(source.createSection("", nsi.startCharIndex, nsi.endCharIndex - nsi.startCharIndex));
+        public DeparseVisitor append(RSyntaxElement element) {
+            try (C c = withContext(element)) {
+                visitor.accept(element);
             }
+            return this;
         }
-    }
-
-    /**
-     * Version for use by {@code RSerialize} to convert a CLOSXP/LANGSXP/PROMSXP into a parseable
-     * string.
-     */
-    @TruffleBoundary
-    public static String deparse(RPairList pl) {
-        State state = new State(80, true, -1, 0, false);
-        return deparse2buff(state, pl).sb.toString();
-    }
-
-    /**
-     * Version to generate a printable string for e.g., error messages.
-     */
-    @TruffleBoundary
-    public static String deparseForPrint(Object expr) {
-        State state = State.createPrintableState();
-        return deparse2buff(state, expr).sb.toString();
-    }
 
-    private static String stateToString(boolean abbrev, State state) {
-        String result;
-        int len = state.lines.size();
-        if (len > 1) {
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < len; i++) {
-                String line = state.lines.get(i);
-                sb.append(line);
-                if (i < len - 1) {
-                    sb.append('\n');
-                }
+        private void printline() {
+            sb.append("\n");
+            lastLineStart = sb.length();
+            for (int i = 0; i < indent; i++) {
+                sb.append(i < 4 ? "    " : "  ");
             }
-            result = sb.toString();
-        } else {
-            result = state.lines.get(0);
-        }
-        if (abbrev && result.length() > 13) {
-            result = result.substring(0, 14);
-        }
-        return result;
-    }
-
-    @TruffleBoundary
-    public static String deparse1Line(Object expr, boolean abbrev) {
-        State state = deparse1WithCutoff(expr, MAX_Cutoff, true, 0, -1);
-        return stateToString(abbrev, state);
-    }
-
-    @TruffleBoundary
-    public static String deparse1Line(Object expr, boolean abbrev, int cutoff, int opts) {
-        State state = deparse1WithCutoff(expr, cutoff, true, opts, -1);
-        return stateToString(abbrev, state);
-    }
-
-    /**
-     * Version for {@code deparse}.
-     */
-    @TruffleBoundary
-    public static String[] deparse(Object expr, int cutoff, boolean backtick, int opts, int nlines) {
-        State state = deparse1WithCutoff(expr, cutoff, backtick, opts, nlines);
-        String[] data = new String[state.lines.size()];
-        state.lines.toArray(data);
-        return data;
-    }
-
-    private static State deparse1WithCutoff(Object expr, int cutoff, boolean backtick, int opts, int nlines) {
-        State state = new State(cutoff, backtick, nlines, opts, true);
-        deparse2buff(state, expr);
-        state.writeline();
-        return state;
-    }
-
-    @TruffleBoundary
-    public static State deparse2buff(State state, Object obj) {
-        boolean lbreak = false;
-        if (!state.active) {
-            return state;
         }
 
-        SEXPTYPE type = typeof(obj);
-        switch (type) {
-            case NILSXP:
-                state.append("NULL");
-                break;
-
-            case MISSINGARG_SXP:
-            case EMPTYARG_SXP:
-                break;
-
-            case SYMSXP: {
-                if (state.quoteExpressions()) {
-                    state.append("quote(");
+        private static boolean isSequence(RSyntaxElement element) {
+            if (element instanceof RSyntaxCall) {
+                RSyntaxElement lhs = ((RSyntaxCall) element).getSyntaxLHS();
+                if (lhs instanceof RSyntaxLookup) {
+                    RSyntaxLookup lookup = (RSyntaxLookup) lhs;
+                    return "{".equals(lookup.getIdentifier());
                 }
-                String name = ((RSymbol) obj).getName();
-                if (state.backtick) {
-                    name = quotify(name, BACKTICK);
-                }
-                state.append(name);
-                if (state.quoteExpressions()) {
-                    state.append(')');
-                }
-                break;
-            }
-
-            case CHARSXP:
-                state.append((String) obj);
-                break;
-
-            case PROMSXP: {
-                RPairList f = (RPairList) obj;
-                deparse2buff(state, f.cdr());
-                break;
             }
+            return false;
+        }
 
-            case CLOSXP: {
-                RPairList f = (RPairList) obj;
-                state.append("function (");
-                if (f.car() instanceof RPairList) {
-                    args2buff(state, f.car(), false, true);
-                }
-                state.append(") ");
-                state.writeline();
-                deparse2buff(state, f.cdr());
-                break;
+        private static String isConstantString(RSyntaxElement element) {
+            if (element instanceof RSyntaxConstant) {
+                return RRuntime.asStringLengthOne(((RSyntaxConstant) element).getValue());
             }
+            return null;
+        }
 
-            case FUNSXP: {
-                RFunction f = (RFunction) obj;
-                if (f.isBuiltin()) {
-                    state.append(".Primitive(");
-                    state.append("\"");
-                    state.append(f.getName());
-                    state.append("\")");
-                } else {
-                    RContext.getRRuntimeASTAccess().deparse(state, f);
+        private boolean linebreak(boolean lbreak) {
+            boolean result = lbreak;
+            if ((sb.length() - lastLineStart) > cutoff) {
+                if (!lbreak) {
+                    result = true;
+                    indent++;
                 }
-                break;
+                printline();
             }
+            return result;
+        }
 
-            case ENVSXP:
-                state.append("<environment>");
-                break;
-
-            case FASTR_FACTOR:
-                deparseList(state, ((RFactor) obj).getVector());
-                break;
-
-            case FASTR_DATAFRAME:
-                deparseList(state, ((RDataFrame) obj).getVector());
-                break;
-
-            case VECSXP:
-                deparseList(state, (RList) obj);
-                break;
-
-            case EXPRSXP:
-                RExpression expr = (RExpression) obj;
-                state.append("expression(");
-                vec2buff(state, expr.getList());
-                state.append(')');
-                break;
-
-            case LISTSXP: {
-                state.append("pairlist(");
-                RPairList s = (RPairList) obj;
-                RPairList t = s;
-                while (t != null && t.cdr() != RNull.instance) {
-                    if (t.getTag() != null && !t.isNullTag()) {
-                        deparse2buff(state, t.getTag());
-                        state.append(" = ");
-                    }
-                    deparse2buff(state, t.car());
-                    state.append(", ");
-                    t = next(t);
-                }
-                if (t.getTag() != null && !t.isNullTag()) {
-                    deparse2buff(state, t.getTag());
-                    state.append(" = ");
+        private C indent() {
+            indent++;
+            return new C() {
+                public void close() {
+                    indent--;
                 }
-                deparse2buff(state, t.car());
-                state.append(')');
-                break;
-            }
+            };
+        }
 
-            case LANGSXP: {
-                if (obj instanceof RLanguage) {
-                    RContext.getRRuntimeASTAccess().deparse(state, (RLanguage) obj);
-                    break;
+        private C inCurly() {
+            inCurly++;
+            return new C() {
+                public void close() {
+                    inCurly--;
                 }
-                RPairList f = (RPairList) obj;
-                Object car = f.car();
-                Object cdr = f.cdr();
-                SEXPTYPE carType = typeof(car);
-                if (carType == SEXPTYPE.SYMSXP) {
-                    RSymbol symbol = (RSymbol) car; // TODO could be a promise according to GnuR
-                    String op = symbol.getName();
-                    boolean userBinop = false;
-                    if (RContext.isPrimitiveBuiltin(op) || (userBinop = isUserBinop(op))) {
-                        RPairList pl = cdr instanceof RPairList ? (RPairList) cdr : null;
-                        PPInfo fop;
-                        if (userBinop) {
-                            // TODO check for named args and deparse as normal function
-                            fop = USERBINOP;
-                        } else {
-                            fop = ppInfo(op);
-                        }
-                        if (fop.kind == PP.BINARY) {
-                            switch (pl.getLength()) {
-                                case 1:
-                                    fop = new PPInfo(PP.UNARY, fop.prec == PREC_SUM ? PREC_SIGN : fop.prec, fop.rightassoc);
-                                    break;
-                                case 2:
-                                    break;
-                                default:
-                                    assert false;
-                            }
-                        } else if (fop.kind == PP.BINARY2) {
-                            if (pl.getLength() != 2) {
-                                fop = new PPInfo(PP.FUNCALL, 0, false);
-                            } else if (userBinop) {
-                                fop = new PPInfo(PP.BINARY, fop.prec, fop.rightassoc);
+            };
+        }
+
+        private final class Visitor extends RSyntaxVisitor<Void> {
+
+            @Override
+            protected Void visit(RSyntaxCall call) {
+                RSyntaxElement lhs = call.getSyntaxLHS();
+                RSyntaxElement[] args = call.getSyntaxArguments();
+                if (lhs instanceof RSyntaxLookup) {
+                    String symbol = ((RSyntaxLookup) lhs).getIdentifier();
+                    RDeparse.Func func = RDeparse.getFunc(symbol);
+                    if (func != null) {
+                        PPInfo info = func.info;
+                        if (args.length == 0) {
+                            switch (info.kind) {
+                                case BREAK:
+                                case NEXT:
+                                    append(func.op, call, lhs);
+                                    return null;
                             }
-                        }
-                        switch (fop.kind) {
-                            case ASSIGN: {
-                                Object left = pl.car();
-                                boolean parens = needsParens(fop, left, true);
-                                if (parens) {
-                                    state.append('(');
-                                }
-                                deparse2buff(state, left);
-                                state.append(' ');
-                                state.append(op);
-                                state.append(' ');
-                                Object right = pl.cadr();
-                                parens = needsParens(fop, right, false);
-                                deparse2buff(state, right);
-                                if (parens) {
-                                    state.append(')');
-                                }
-                                break;
+                        } else if (args.length == 1) {
+                            switch (info.kind) {
+                                case BINARY:
+                                case BINARY2:
+                                    append(func.op, lhs).append(args[0]);
+                                    return null;
+                                case REPEAT:
+                                    append("repeat", lhs).append(' ').append(args[0]);
+                                    return null;
+                                case PAREN:
+                                    append(func.op, lhs).append(args[0]).append(func.closeOp);
+                                    return null;
                             }
-
-                            case IF: {
-                                state.append("if (");
-                                deparse2buff(state, pl.car());
-                                state.append(')');
-                                boolean lookahead = false;
-                                if (state.incurly > 0 && state.inlist == 0) {
-                                    lookahead = curlyahead(pl.cadr());
-                                    if (!lookahead) {
-                                        state.writeline();
-                                        state.indent++;
+                        } else if (args.length == 2) {
+                            switch (info.kind) {
+                                case ASSIGN:
+                                case BINARY:
+                                case BINARY2:
+                                    appendWithParens(args[0], info, true);
+                                    if (info.kind != PP.BINARY2) {
+                                        append(' ').append(func.op, lhs).append(' ');
+                                    } else {
+                                        append(func.op);
                                     }
-                                }
-                                int lenpl = pl.getLength();
-                                if (lenpl > 2) {
-                                    deparse2buff(state, pl.cadr());
-                                    if (state.incurly > 0 && state.inlist == 0) {
-                                        state.writeline();
-                                        if (!lookahead) {
-                                            state.indent--;
-                                        }
+                                    appendWithParens(args[1], info, false);
+                                    return null;
+                                case DOLLAR:
+                                    appendWithParens(args[0], info, true);
+                                    append(func.op, lhs);
+                                    String name = isConstantString(args[1]);
+                                    if (name != null && isValidName(name)) {
+                                        append(name, args[1]);
                                     } else {
-                                        state.append(' ');
+                                        appendWithParens(args[1], info, false);
                                     }
-                                    state.append("else ");
-                                    deparse2buff(state, pl.caddr());
-                                } else {
-                                    deparse2buff(state, pl.cadr());
-                                    if (state.incurly > 0 && !lookahead && state.inlist == 0) {
-                                        state.indent--;
+                                    return null;
+                                case IF:
+                                    append("if", lhs).append(" (").append(args[0]).append(") ");
+                                    if (inCurly > 0 && inList == 0 && !isSequence(args[1])) {
+                                        printline();
+                                        try (C c = indent()) {
+                                            append(args[1]);
+                                        }
+                                    } else {
+                                        append(args[1]);
                                     }
-                                }
-                                break;
-                            }
-
-                            case WHILE: {
-                                state.append("while (");
-                                deparse2buff(state, pl.car());
-                                state.append(") ");
-                                deparse2buff(state, pl.cadr());
-                                break;
+                                    return null;
+                                case WHILE:
+                                    append("while", lhs).append(" (").append(args[0]).append(") ").append(args[1]);
+                                    return null;
                             }
-
-                            case FOR: {
-                                state.append("for (");
-                                deparse2buff(state, pl.car());
-                                state.append(" in ");
-                                deparse2buff(state, pl.cadr());
-                                state.append(") ");
-                                deparse2buff(state, ((RPairList) pl.cdr()).cadr());
-                                break;
-                            }
-
-                            case REPEAT:
-                                state.append("repeat ");
-                                deparse2buff(state, pl.car());
-                                break;
-
-                            case BINARY:
-                            case BINARY2: {
-                                Object left = pl.car();
-                                boolean parens = needsParens(fop, left, true);
-                                if (parens) {
-                                    state.append('(');
-                                }
-                                deparse2buff(state, pl.car());
-                                if (parens) {
-                                    state.append(')');
-                                }
-                                if (fop.kind == PP.BINARY) {
-                                    state.append(' ');
-                                }
-                                state.append(op);
-                                if (fop.kind == PP.BINARY) {
-                                    state.append(' ');
-                                }
-                                if (fop.kind == PP.BINARY) {
-                                    lbreak = state.linebreak(lbreak);
-                                }
-                                Object right = pl.cadr();
-                                parens = needsParens(fop, right, false);
-                                if (parens) {
-                                    state.append('(');
-                                }
-                                deparse2buff(state, right);
-                                if (parens) {
-                                    state.append(')');
-                                }
-                                if (fop.kind == PP.BINARY) {
-                                    if (lbreak) {
-                                        state.indent--;
-                                        lbreak = false;
+                        } else if (args.length == 3) {
+                            switch (symbol) {
+                                case "for":
+                                    append("for", lhs).append(" (").append(args[0]).append(" in ").append(args[1]).append(") ").append(args[2]);
+                                    return null;
+                                case "if":
+                                    append("if", lhs).append(" (").append(args[0]).append(") ");
+                                    if (inCurly > 0 && inList == 0 && !isSequence(args[1])) {
+                                        printline();
+                                        try (C c = indent()) {
+                                            append(args[1]).printline();
+                                        }
+                                    } else {
+                                        append(args[1]);
+                                        if (inCurly > 0 && inList == 0) {
+                                            printline();
+                                        } else {
+                                            append(' ');
+                                        }
                                     }
-                                }
-                                break;
-                            }
-
-                            case UNARY: {
-                                state.append(op);
-                                Object left = pl.car();
-                                boolean parens = needsParens(fop, left, true);
-                                if (parens) {
-                                    state.append('(');
-                                }
-                                deparse2buff(state, left);
-                                if (parens) {
-                                    state.append(')');
-                                }
-                                break;
-                            }
-
-                            case CURLY: {
-                                state.append(op);
-                                state.incurly++;
-                                state.indent++;
-                                state.writeline();
-                                while (pl != null) {
-                                    deparse2buff(state, pl.car());
-                                    state.writeline();
-                                    pl = next(pl);
-                                }
-                                state.indent--;
-                                state.append('}');
-                                state.incurly--;
-                                break;
-                            }
-
-                            case PAREN:
-                                state.append('(');
-                                deparse2buff(state, pl.car());
-                                state.append(')');
-                                break;
-
-                            case SUBSET: {
-                                Object left = pl.car();
-                                boolean parens = needsParens(fop, left, true);
-                                if (parens) {
-                                    state.append('(');
-                                }
-                                deparse2buff(state, left);
-                                if (parens) {
-                                    state.append(')');
-                                }
-                                state.append(op);
-                                args2buff(state, pl.cdr(), false, false);
-                                if (op.equals("[")) {
-                                    state.append(']');
-                                } else {
-                                    state.append("]]");
-                                }
-                                break;
+                                    append("else ").append(args[2]);
+                                    return null;
                             }
-
-                            case FUNCTION:
-                                state.append(op);
-                                state.append('(');
-                                args2buff(state, pl.car(), false, true);
-                                state.append(") ");
-                                deparse2buff(state, pl.cadr());
-                                break;
-
-                            case DOLLAR: {
-                                Object left = pl.car();
-                                boolean parens = needsParens(fop, left, true);
-                                if (parens) {
-                                    state.append('(');
-                                }
-                                deparse2buff(state, left);
-                                if (parens) {
-                                    state.append(')');
-                                }
-                                state.append(op);
-                                Object right = pl.cadr();
-                                if (right instanceof RSymbol) {
-                                    deparse2buff(state, right);
-                                } else {
-                                    parens = needsParens(fop, right, true);
-                                    if (parens) {
-                                        state.append('(');
-                                    }
-                                    deparse2buff(state, right);
-                                    if (parens) {
-                                        state.append(')');
+                        }
+                        switch (info.kind) {
+                            case CURLY:
+                                boolean braces = args.length != 1 || hasBraces(call);
+                                if (braces) {
+                                    append("{", lhs);
+                                    try (C i = indent(); C c = inCurly()) {
+                                        for (RSyntaxElement statement : args) {
+                                            printline();
+                                            append(statement);
+                                        }
                                     }
-                                }
-                                break;
-                            }
-
-                            case FUNCALL:
-                            case RETURN: {
-                                if (state.backtick) {
-                                    state.append(quotify(op, BACKTICK));
+                                    printline();
+                                    append('}');
                                 } else {
-                                    state.append(quotify(op, DQUOTE)); // quote?
+                                    append(args[0]);
                                 }
-                                state.append('(');
-                                state.inlist++;
-                                args2buff(state, cdr, false, false);
-                                state.inlist--;
-                                state.append(')');
-                                break;
-                            }
-
-                            case NEXT:
-                                state.append("next");
-                                break;
-
-                            case BREAK:
-                                state.append("break");
-                                break;
-
-                            default:
-                                throw RInternalError.unimplemented();
+                                return null;
+                            case SUBSET:
+                                appendWithParens(args[0], info, true);
+                                append(func.op, lhs).appendArgs(call.getSyntaxSignature(), args, 1, false).append(func.closeOp);
+                                return null;
                         }
-                    } else {
-                        // TODO promise?
-                        if (op.equals("::") || op.equals(":::")) {
-                            // special case
-                            deparse2buff(state, f.cadr());
-                            state.append(op);
-                            deparse2buff(state, f.caddr());
+                    }
+                    if ("::".equals(symbol) || ":::".equals(symbol)) {
+                        if (args.length == 0) {
+                            append("NULL").append(symbol).append("NULL");
+                        } else if (args.length == 1) {
+                            append(args[0]).append(symbol).append("NULL");
                         } else {
-                            state.append(quotify(op, BACKTICK));
-                            state.append('(');
-                            args2buff(state, cdr, false, false);
-                            state.append(')');
+                            append(args[0]).append(symbol).append(args[1]);
                         }
+                        return null;
                     }
-                } else if (carType == SEXPTYPE.CLOSXP || carType == SEXPTYPE.SPECIALSXP || carType == SEXPTYPE.BUILTINSXP) {
-                    if (parenthesizeCaller(car)) {
-                        state.append('(');
-                        deparse2buff(state, car);
-                        state.append(')');
-                    } else {
-                        deparse2buff(state, car);
-                    }
-                    state.append('(');
-                    args2buff(state, cdr, false, false);
-                    state.append(')');
-                } else {
-                    // lambda
-                    if (parenthesizeCaller(car)) {
-                        state.append('(');
-                        deparse2buff(state, car);
-                        state.append(')');
-                    } else {
-                        deparse2buff(state, car);
-                    }
-                    state.append('(');
-                    args2buff(state, f.cdr(), false, false);
-                    state.append(')');
                 }
-                break;
-            }
 
-            case STRSXP:
-            case LGLSXP:
-            case INTSXP:
-            case REALSXP:
-            case CPLXSXP:
-            case RAWSXP:
-                vector2buff(state, checkScalarVector(obj));
-                break;
-
-            case BCODESXP: {
-                /*
-                 * This should only happen in a call from RSerialize when unserializing a CLOSXP.
-                 * There is no value in following GnuR and appending <bytecode>, as we need the
-                 * source., which is (we expect) in the RPairList cdr (which is an RList).
-                 * Experimentally, only the first element of the list should be deparsed.
-                 */
-                // state.append("<bytecode>");
-                RPairList pl = (RPairList) obj;
-                RList plcdr = (RList) pl.cdr();
-                deparse2buff(state, plcdr.getDataAtAsObject(0));
-                break;
+                PPInfo info = new PPInfo(PP.FUNCALL, PREC_FN, false);
+                appendWithParens(lhs, info, true);
+                append('(').appendArgs(call.getSyntaxSignature(), args, 0, false).append(')');
+                return null;
             }
 
-            case FASTR_DOUBLE:
-            case FASTR_INT:
-            case FASTR_BYTE:
-            case FASTR_COMPLEX:
-                vecElement2buff(state, SEXPTYPE.convertFastRScalarType(type), obj);
-                break;
-
-            case FASTR_CONNECTION:
-                // TODO GnuR deparses this as a structure
-                state.append("NULL");
-                break;
-
-            case S4SXP: {
-                RS4Object s4Obj = (RS4Object) obj;
-                state.append("new(\"");
-                Object clazz = s4Obj.getAttribute("class");
-                state.append(clazz == null ? "S4" : RRuntime.toString(RRuntime.asStringLengthOne(clazz)));
-                state.append('\"');
-                state.incIndent();
-                state.writeline();
-                if (s4Obj.getAttributes() != null) {
-                    for (RAttribute att : s4Obj.getAttributes()) {
-                        if (!"class".equals(att.getName())) {
-                            state.append(", ");
-                            state.append(att.getName());
-                            state.append(" = ");
-                            deparse2buff(state, att.getValue());
-                            state.writeline();
-                        }
-                    }
+            public boolean hasBraces(RSyntaxElement node) {
+                SourceSection ss = node.getSourceSection();
+                if (ss == null || ss == RSyntaxNode.SOURCE_UNAVAILABLE) {
+                    // this is statistical guess
+                    return true;
+                } else {
+                    return ss.getCode().startsWith("{");
                 }
-                state.decIndent();
-                state.append(')');
-                break;
             }
-            case EXTPTRSXP: {
-                RExternalPtr ext = (RExternalPtr) obj;
-                state.append("<pointer: 0x");
-                state.append(Long.toHexString(ext.getAddr()));
-                state.append('>');
-                break;
-            }
-
-            default:
-                throw RInternalError.shouldNotReachHere("unexpected SEXPTYPE: " + type);
-        }
-        return state;
-    }
-
-    private static SEXPTYPE typeof(Object obj) {
-        Class<?> klass = obj.getClass();
-        if (klass == RPairList.class) {
-            return ((RPairList) obj).getType();
-        } else {
-            return SEXPTYPE.typeForClass(klass);
-        }
-    }
-
-    private static RAbstractVector checkScalarVector(Object obj) {
-        if (obj instanceof String) {
-            return RDataFactory.createStringVectorFromScalar((String) obj);
-        } else if (obj instanceof Byte) {
-            return RDataFactory.createLogicalVectorFromScalar((Byte) obj);
-        } else if (obj instanceof Integer) {
-            return RDataFactory.createIntVectorFromScalar((Integer) obj);
-        } else if (obj instanceof Double) {
-            return RDataFactory.createDoubleVectorFromScalar((Double) obj);
-        } else if (obj instanceof RComplex) {
-            return RDataFactory.createComplexVectorFromScalar((RComplex) obj);
-        } else {
-            return (RAbstractVector) obj;
-        }
-    }
 
-    @SuppressWarnings("unused")
-    private static boolean curlyahead(Object obj) {
-        return false;
-    }
+            @Override
+            protected Void visit(RSyntaxConstant constant) {
+                // coerce scalar values to vectors and unwrap data frames and factors:
+                Object value = RRuntime.asAbstractVector(constant.getValue());
+                if (value instanceof RDataFrame) {
+                    value = ((RDataFrame) value).getVector();
+                } else if (value instanceof RFactor) {
+                    value = ((RFactor) value).getVector();
+                }
 
-    /**
-     * Check for whether we need to parenthesize a caller. The unevaluated ones are tricky: We want
-     *
-     * <pre>
-     *  x$f(z)
-     *  x[n](z)
-     *  base::mean(x)
-     * </pre>
-     *
-     * but <pre< (f+g)(z) (function(x) 1)(x) </pre> etc.
-     */
-    private static boolean parenthesizeCaller(Object s) {
-        if (s instanceof RPairList) {
-            RPairList pl = (RPairList) s;
-            if (pl.getType() == SEXPTYPE.CLOSXP) {
-                return true;
-            } else if (pl.getType() == SEXPTYPE.LANGSXP) {
-                Object car = pl.car();
-                if (car instanceof RSymbol) {
-                    String op = ((RSymbol) car).getName();
-                    if (isUserBinop(op)) {
-                        return true;
+                if (value instanceof RExpression) {
+                    append("expression(").appendListContents(((RExpression) value).getList()).append(')');
+                } else if (value instanceof RAbstractListVector) {
+                    RAbstractListVector obj = (RAbstractListVector) value;
+                    try (C c = withAttributes(obj)) {
+                        append("list(").appendListContents(obj).append(')');
+                    }
+                } else if (value instanceof RAbstractVector) {
+                    appendVector((RAbstractVector) value);
+                } else if (value instanceof RNull) {
+                    append("NULL");
+                } else if (value instanceof RFunction) {
+                    RFunction f = (RFunction) value;
+                    if (f.isBuiltin()) {
+                        append(".Primitive(\"").append(f.getName()).append("\")");
+                    } else {
+                        append(RContext.getRRuntimeASTAccess().getSyntaxFunction(f));
                     }
-                    if (RContext.isPrimitiveBuiltin(op)) {
-                        PPInfo info = ppInfo(op);
-                        if (info.prec >= PREC_DOLLAR || info.kind == PP.FUNCALL || info.kind == PP.PAREN || info.kind == PP.CURLY) {
-                            return true;
+                } else if (value instanceof RPairList) {
+                    RPairList pl = (RPairList) value;
+                    assert pl.getType() == SEXPTYPE.LISTSXP;
+                    append("pairlist(");
+                    Arguments<RSyntaxElement> arguments = wrapArguments(pl);
+                    appendArgs(arguments.getSignature(), arguments.getArguments(), 0, false);
+                    append(')');
+                } else if (value instanceof RS4Object) {
+                    RS4Object s4Obj = (RS4Object) value;
+                    Object clazz = s4Obj.getAttribute("class");
+                    String className = clazz == null ? "S4" : RRuntime.toString(RRuntime.asStringLengthOne(clazz));
+                    append("new(\"").append(className).append('\"');
+                    try (C c = indent()) {
+                        printline();
+                        if (s4Obj.getAttributes() != null) {
+                            for (RAttribute att : s4Obj.getAttributes()) {
+                                if (!"class".equals(att.getName())) {
+                                    append(", ").append(att.getName()).append(" = ").process(att.getValue()).printline();
+                                }
+                            }
                         }
                     }
-                    return false;
+                    append(')');
+                } else if (value instanceof RExternalPtr) {
+                    append("<pointer: 0x").append(Long.toHexString(((RExternalPtr) value).getAddr())).append('>');
+                } else if (value instanceof REnvironment) {
+                    append("<environment>");
                 } else {
-                    return true;
+                    throw RInternalError.shouldNotReachHere("unexpected: " + value);
                 }
+                return null;
+            }
 
+            @Override
+            protected Void visit(RSyntaxLookup lookup) {
+                if (!backtick || isValidName(lookup.getIdentifier())) {
+                    append(lookup.getIdentifier());
+                } else {
+                    append(quotify(lookup.getIdentifier(), BACKTICK));
+                }
+                return null;
             }
-        }
-        return false;
-    }
 
-    private static RPairList next(RPairList pairlist) {
-        if (pairlist.cdr() == RNull.instance) {
-            return null;
-        } else {
-            return (RPairList) pairlist.cdr();
-        }
-    }
+            @Override
+            protected Void visit(RSyntaxFunction function) {
+                append("function (");
+                appendArgs(function.getSyntaxSignature(), function.getSyntaxArgumentDefaults(), 0, true);
+                append(") ");
+                RSyntaxElement body = function.getSyntaxBody();
+                boolean newline = true;
+                if (body instanceof RSyntaxCall) {
+                    RSyntaxCall c = (RSyntaxCall) body;
+                    if (c.getSyntaxLHS() instanceof RSyntaxLookup) {
+                        RSyntaxLookup l = (RSyntaxLookup) c.getSyntaxLHS();
+                        if ("{".equals(l.getIdentifier())) {
+                            newline = c.getSyntaxArguments().length == 1 && !hasBraces(c);
+                        }
 
-    /**
-     * Deparse list, dataframe, factor (different representation types in FastR).
-     */
-    private static void deparseList(State state, RVector obj) {
-        if (state.showAttributes()) {
-            attr1(state, obj);
-        }
-        state.append("list(");
-        vec2buff(state, obj);
-        state.append(')');
-        if (state.showAttributes()) {
-            attr2(state, obj);
+                    }
+                }
+                if (newline) {
+                    printline();
+                }
+                append(body);
+                return null;
+            }
         }
-    }
 
-    /**
-     * Check for needing parentheses in expressions. needsparens looks at an arg to a unary or
-     * binary operator to determine if it needs to be parenthesized when deparsed.{@code mainop} is
-     * a unary or binary operator, {@code arg} is an argument to it, on the left if
-     * {@code left == true}.
-     */
-    private static boolean needsParens(PPInfo mainop, Object arg, boolean left) {
-        if (arg instanceof RPairList) {
-            RPairList pl = (RPairList) arg;
-            if (pl.getType() == SEXPTYPE.LANGSXP) {
-                Object car = pl.car();
-                if (car instanceof RSymbol) {
-                    String op = ((RSymbol) car).getName();
-                    if (RContext.isPrimitiveBuiltin(op)) {
-                        PPInfo arginfo = ppInfo(op);
-                        switch (arginfo.kind) {
-                            case BINARY:
-                            case BINARY2: {
-                                Object cdr = pl.cdr();
-                                int length = (cdr instanceof RPairList) ? ((RPairList) cdr).getLength() : 1;
-                                switch (length) {
-                                    case 1:
-                                        if (!left) {
-                                            return false;
-                                        }
-                                        if (arginfo.prec == PREC_SUM) {
-                                            arginfo = arginfo.changePrec(PREC_SIGN);
-                                        }
-                                        break;
-                                    case 2:
-                                        break;
-                                    default:
-                                        return false;
-                                }
-                                return checkPrec(mainop, arginfo, left);
+        private void appendWithParens(RSyntaxElement arg, PPInfo mainOp, boolean isLeft) {
+            Func func = isInfixOperatorNode(arg);
+            boolean needsParens = false;
+            if (func != null) {
+                PPInfo arginfo = func.info;
+                switch (arginfo.kind) {
+                    case BINARY:
+                    case BINARY2:
+                        RSyntaxElement[] subArgs = ((RSyntaxCall) arg).getSyntaxArguments();
+                        if (subArgs.length == 1) {
+                            if (isLeft && arginfo.prec == RDeparse.PREC_SUM) {
+                                arginfo = arginfo.changePrec(RDeparse.PREC_SIGN);
                             }
-
-                            case ASSIGN:
-                            case SUBSET:
-                            case UNARY:
-                            case DOLLAR:
-                                return checkPrec(mainop, arginfo, left);
-
-                            case FOR:
-                            case IF:
-                            case WHILE:
-                            case REPEAT:
-                                return left;
-                            default:
-                                return false;
-                        }
-                    } else if (isUserBinop(op)) {
-                        if (mainop.prec == PREC_PERCENT || (mainop.prec == PREC_PERCENT && left == mainop.rightassoc)) {
-                            return true;
+                        } else if (subArgs.length == 2) {
+                            needsParens = checkPrec(mainOp, arginfo, isLeft);
                         }
-                    }
+                        break;
+                    case UNARY:
+                        needsParens = mainOp.prec > arginfo.prec || (mainOp.prec == arginfo.prec && isLeft == mainOp.rightassoc);
+                        break;
+                    default:
+                        break;
                 }
             }
-        } else if (isComplexLengthOne(arg)) {
-            if (mainop.prec > PREC_SUM || (mainop.prec == PREC_SUM && left == mainop.rightassoc)) {
-                return true;
+            if (needsParens) {
+                append('(');
+                append(arg);
+                append(')');
+            } else {
+                append(arg);
             }
         }
-        return false;
-    }
 
-    public static boolean checkPrec(PPInfo mainop, PPInfo arginfo, boolean left) {
-        return mainop.prec > arginfo.prec || (mainop.prec == arginfo.prec && left == mainop.rightassoc);
-    }
+        private static boolean checkPrec(PPInfo mainop, PPInfo arginfo, boolean left) {
+            return mainop.prec > arginfo.prec || (mainop.prec == arginfo.prec && left == mainop.rightassoc);
+        }
 
-    private static boolean isComplexLengthOne(Object arg) {
-        return ((arg instanceof RComplexVector && ((RComplexVector) arg).getLength() == 1) || arg instanceof RComplex);
-    }
+        private DeparseVisitor appendArgs(ArgumentsSignature signature, RSyntaxElement[] args, int start, boolean formals) {
+            for (int i = start; i < args.length; i++) {
+                RSyntaxElement argument = args[i];
+                if (argument instanceof RSyntaxLookup && ((RSyntaxLookup) argument).getIdentifier().isEmpty()) {
+                    argument = null;
+                }
+                if (argument instanceof RSyntaxConstant && ((RSyntaxConstant) argument).getValue() instanceof REmpty) {
+                    argument = null;
+                }
+                String name = signature.getName(i);
+                if (name != null && name.isEmpty()) {
+                    name = null;
+                }
+                if (name != null) {
+                    if (isValidName(name)) {
+                        append(name);
+                    } else {
+                        append(quotify(name, BACKTICK));
+                    }
+                    if (!formals || argument != null) {
+                        append(" = ");
+                    }
+                }
+                if (argument != null) {
+                    append(argument);
+                }
+                if (i != args.length - 1) {
+                    append(", ");
+                }
+            }
+            return this;
+        }
 
-    @TruffleBoundary
-    /** Handles {@link RList}, (@link RExpression}, {@link RDataFrame} and {@link RFactor}. Method name same as GnuR.
-     */
-    private static State vec2buff(State state, RVector v) {
-        int n = v.getLength();
-        boolean lbreak = false;
-        Object names = v.getNames();
-        RStringVector snames = names == RNull.instance ? null : (RStringVector) names;
-        for (int i = 0; i < n; i++) {
-            if (i > 0) {
-                state.append(", ");
+        private DeparseVisitor process(Object v) {
+            assert RRuntime.asAbstractVector(v) instanceof RTypedValue;
+            assert !(v instanceof RSyntaxElement);
+
+            RSyntaxElement element = wrap(v, false);
+            if (!quoteExpressions() || element instanceof RSyntaxConstant) {
+                append(element);
+            } else {
+                append("quote(");
+                append(element);
+                append(')');
             }
-            lbreak = state.linebreak(lbreak);
-            String sname = snames == null ? null : snames.getDataAt(i);
-            if (snames != null && ((sname = snames.getDataAt(i)) != null)) {
-                state.append(sname);
-                state.append(" = ");
+            return this;
+        }
+
+        private static RSyntaxElement wrap(Object v, boolean isCallLHS) {
+            Object value = RRuntime.asAbstractVector(v);
+            if (value instanceof RSymbol) {
+                return RSyntaxLookup.createDummyLookup(null, ((RSymbol) value).getName(), isCallLHS);
+            } else if (value instanceof RLanguage) {
+                return ((RLanguage) value).getRep().asRSyntaxNode();
+            } else if (value instanceof RPairList) {
+                RPairList pl = (RPairList) value;
+                switch (pl.getType()) {
+                    case LANGSXP:
+                        return wrapCall(pl);
+                    case CLOSXP:
+                        return wrapFunctionExpression(pl);
+                    default:
+                        throw RInternalError.shouldNotReachHere("sexptype: " + pl.getType());
+                }
+            } else if (value instanceof RMissing) {
+                return RSyntaxLookup.createDummyLookup(null, "", false);
+            } else {
+                return RSyntaxConstant.createDummyConstant(null, value);
             }
-            deparse2buff(state, v.getDataAtAsObject(i));
-        }
-        if (lbreak) {
-            state.decIndent();
         }
-        return state;
-    }
 
-    @TruffleBoundary
-    private static State args2buff(State state, Object args, @SuppressWarnings("unused") boolean lineb, boolean formals) {
-        boolean lbreak = false;
-        RPairList arglist;
-        if (args instanceof RNull) {
-            arglist = null;
-        } else {
-            arglist = (RPairList) args;
-        }
-        while (arglist != null) {
-            Object argTag = arglist.getTag();
-            if (argTag != null && argTag != RNull.instance) {
-                String rs = ((RSymbol) arglist.getTag()).getName();
-                if (rs.equals("...")) {
-                    state.append(rs);
+        private static RSyntaxElement wrapCall(RPairList pl) {
+            Object car = pl.car();
+            if (car instanceof RSymbol && ((RSymbol) car).getName().equals("function")) {
+                RPairList fun = (RPairList) pl.cdr();
+                return wrapFunctionExpression(fun);
+            }
+            RSyntaxElement lhs = wrap(car, true);
+
+            Arguments<RSyntaxElement> args = wrapArguments(pl.cdr());
+            return RSyntaxCall.createDummyCall(null, lhs, args.getSignature(), args.getArguments());
+        }
+
+        private static RSyntaxElement wrapFunctionExpression(RPairList fun) {
+            Arguments<RSyntaxElement> args = wrapArguments(fun.car());
+            RSyntaxElement body;
+            Object cdr = fun.cdr();
+            if (cdr instanceof RPairList) {
+                RPairList pl = (RPairList) cdr;
+                if (pl.getType() == SEXPTYPE.BCODESXP) {
+                    RAbstractListVector list = (RAbstractListVector) fun.cddr();
+                    body = wrap(list.getDataAtAsObject(0), false);
+                } else if (pl.getType() == SEXPTYPE.LISTSXP) {
+                    assert pl.cadr() == RNull.instance && pl.cddr() == RNull.instance;
+                    body = wrap(pl.car(), false);
                 } else {
-                    state.append(quotify(rs, state.backtick ? BACKTICK : DQUOTE));
+                    assert pl.getType() == SEXPTYPE.LANGSXP;
+                    body = wrap(pl, false);
                 }
+            } else {
+                body = wrap(cdr, false);
+            }
 
-                if (formals) {
-                    if (arglist.car() != RMissing.instance) {
-                        state.append(" = ");
-                        deparse2buff(state, arglist.car());
-                    }
+            return RSyntaxFunction.createDummyFunction(null, args.getSignature(), args.getArguments(), body);
+        }
+
+        private static Arguments<RSyntaxElement> wrapArguments(Object args) {
+            RPairList arglist = args instanceof RNull ? null : (RPairList) args;
+            ArrayList<RSyntaxElement> argElements = new ArrayList<>();
+            ArrayList<String> argNames = new ArrayList<>();
+            while (arglist != null) {
+                Object argTag = arglist.getTag();
+                if (argTag != null && argTag != RNull.instance) {
+                    String rs = ((RSymbol) arglist.getTag()).getName();
+                    argNames.add(rs);
                 } else {
-                    state.append(" = ");
-                    if (arglist.car() != RMissing.instance) {
-                        deparse2buff(state, arglist.car());
-                    }
+                    argNames.add(null);
                 }
+                argElements.add(wrap(arglist.car(), false));
+                arglist = next(arglist);
+            }
+            RSyntaxElement[] arguments = argElements.toArray(new RSyntaxElement[argElements.size()]);
+            ArgumentsSignature signature = ArgumentsSignature.get(argNames.toArray(new String[argNames.size()]));
+            Arguments<RSyntaxElement> arg = new Arguments<>(arguments, signature);
+            return arg;
+        }
+
+        private static RPairList next(RPairList pairlist) {
+            if (pairlist.cdr() == RNull.instance) {
+                return null;
             } else {
-                deparse2buff(state, arglist.car());
+                return (RPairList) pairlist.cdr();
             }
-            arglist = next(arglist);
-            if (arglist != null) {
-                state.append(", ");
-                lbreak = state.linebreak(lbreak);
+        }
+
+        private void appendVector(RAbstractVector vec) {
+            SEXPTYPE type = SEXPTYPE.typeForClass(vec.getClass());
+            int len = vec.getLength();
+            if (len == 0) {
+                append(vec.getRType().getClazz() + "(0)");
+            } else if (len == 1) {
+                vecElement2buff(type, vec.getDataAtAsObject(0), true);
+            } else {
+                RIntSequence sequence = asIntSequence(vec);
+                if (sequence != null) {
+                    append(RRuntime.intToStringNoCheck(sequence.getStart())).append(':').append(RRuntime.intToStringNoCheck(sequence.getEnd()));
+                    return;
+                } else {
+                    // TODO COMPAT?
+                    append("c(");
+                    for (int i = 0; i < len; i++) {
+                        Object element = vec.getDataAtAsObject(i);
+                        vecElement2buff(type, element, false);
+                        if (i < len - 1) {
+                            append(", ");
+                        }
+                    }
+                    append(')');
+                }
             }
         }
-        if (lbreak) {
-            state.indent--;
+
+        private static RIntSequence asIntSequence(RAbstractVector vec) {
+            if (!(vec instanceof RAbstractIntVector)) {
+                return null;
+            }
+            RAbstractIntVector intVec = (RAbstractIntVector) vec;
+            if (vec instanceof RIntSequence) {
+                return (RIntSequence) vec;
+            }
+            assert vec.getLength() >= 2;
+            int start = intVec.getDataAt(0);
+            if (RRuntime.isNA(start)) {
+                return null;
+            }
+            for (int i = 1; i < vec.getLength(); i++) {
+                int next = intVec.getDataAt(i);
+                if (RRuntime.isNA(next) || next != start + i) {
+                    return null;
+                }
+            }
+            return RDataFactory.createIntSequence(start, 1, intVec.getLength());
         }
-        return state;
-    }
 
-    @TruffleBoundary
-    private static State vector2buff(State state, RAbstractVector vec) {
-        SEXPTYPE type = SEXPTYPE.typeForClass(vec.getClass());
-        boolean surround = false;
-        int len = vec.getLength();
-        if (len == 0) {
+        private DeparseVisitor vecElement2buff(SEXPTYPE type, Object element, boolean singleElement) {
             switch (type) {
-                case LGLSXP:
-                    state.append("logical(0)");
+                case STRSXP:
+                    String s = (String) element;
+                    append(RRuntime.isNA(s) ? (singleElement ? "NA_character_" : "NA") : RRuntime.quoteString((String) element, true));
                     break;
-                case INTSXP:
-                    state.append("integer(0)");
+                case LGLSXP:
+                    append(RRuntime.logicalToString((byte) element));
                     break;
                 case REALSXP:
-                    state.append("numeric(0)");
+                    double d = (double) element;
+                    append(RRuntime.isNA(d) ? (singleElement ? "NA_real_" : "NA") : encodeReal(d));
                     break;
-                case CPLXSXP:
-                    state.append("complex(0)");
-                    break;
-                case STRSXP:
-                    state.append("character(0)");
+                case INTSXP:
+                    int i = (int) element;
+                    if (RRuntime.isNA(i)) {
+                        append((singleElement ? "NA_integer_" : "NA"));
+                    } else {
+                        append(RRuntime.intToStringNoCheck(i)).append('L');
+                    }
                     break;
-                case RAWSXP:
-                    state.append("raw(0)");
+                case CPLXSXP:
+                    RComplex c = (RComplex) element;
+                    if (RRuntime.isNA(c)) {
+                        append((singleElement ? "NA_complex_" : "NA"));
+                    } else {
+                        append(encodeReal(c.getRealPart()));
+                        if (c.getImaginaryPart() >= 0) {
+                            append('+');
+                        }
+                        append(encodeReal(c.getImaginaryPart())).append('i');
+                    }
                     break;
                 default:
-                    assert false;
-            }
-        } else if (type == SEXPTYPE.INTSXP) {
-            // TODO seq detection and COMPAT?
-            if (len > 1) {
-                state.append("c(");
-                surround = true;
+                    throw RInternalError.shouldNotReachHere("unexpected SEXPTYPE: " + type);
             }
-            RAbstractIntVector intVec = (RAbstractIntVector) vec;
-            for (int i = 0; i < len; i++) {
-                int val = intVec.getDataAt(i);
-                if (RRuntime.isNA(val)) {
-                    state.append("NA_integer_");
-                } else {
-                    state.append(Integer.toString(val));
-                    state.append('L');
+            return this;
+        }
+
+        /**
+         * Handles {@link RList}, (@link RExpression}, {@link RDataFrame} and {@link RFactor}.
+         * Method name same as GnuR.
+         */
+        private DeparseVisitor appendListContents(RAbstractListVector v) {
+            int n = v.getLength();
+            boolean lbreak = false;
+            Object names = v.getNames();
+            RStringVector snames = names == RNull.instance ? null : (RStringVector) names;
+            for (int i = 0; i < n; i++) {
+                if (i > 0) {
+                    append(", ");
                 }
-                if (i < len - 1) {
-                    state.append(", ");
+                lbreak = linebreak(lbreak);
+                String sname = snames == null ? null : snames.getDataAt(i);
+                if (snames != null && ((sname = snames.getDataAt(i)) != null)) {
+                    append(sname);
+                    append(" = ");
                 }
+                append(wrap(v.getDataAtAsObject(i), false));
             }
-        } else {
-            // TODO NA checks
-            if (len > 1) {
-                state.append("c(");
-                surround = true;
-            } else if (len == 1 && type == SEXPTYPE.CPLXSXP) {
-                state.append('(');
-                surround = true;
+            if (lbreak) {
+                indent--;
             }
-            for (int i = 0; i < len; i++) {
-                Object element = vec.getDataAtAsObject(i);
-                if (type == SEXPTYPE.REALSXP && RRuntime.isNA((double) element)) {
-                    state.append("NA_real_");
-                } else if (type == SEXPTYPE.STRSXP && RRuntime.isNA((String) element)) {
-                    state.append("NA_character_");
-                } else if (type == SEXPTYPE.CPLXSXP && RRuntime.isNA((RComplex) element)) {
-                    state.append("NA_complex_");
-                } else {
-                    vecElement2buff(state, type, element);
-                }
+            return this;
+        }
 
-                if (i < len - 1) {
-                    state.append(", ");
-                }
+        private static boolean hasAttributes(Object obj) {
+            // TODO check (and ignore) function source attribute
+            if (obj instanceof RAttributable) {
+                RAttributes attrs = ((RAttributable) obj).getAttributes();
+                return attrs != null && !attrs.isEmpty();
+            } else {
+                return false;
             }
         }
-        if (surround) {
-            state.append(')');
-        }
-        return state;
-    }
 
-    private static State vecElement2buff(State state, SEXPTYPE type, Object element) {
-        switch (type) {
-            case STRSXP:
-                // TODO encoding
-                state.append('"');
-                String s = (String) element;
-                for (int i = 0; i < s.length(); i++) {
-                    char ch = s.charAt(i);
-                    int charInt = ch;
-                    switch (ch) {
-                        case '\n':
-                            state.append("\\n");
-                            break;
-                        case '\r':
-                            state.append("\\r");
-                            break;
-                        case '\t':
-                            state.append("\\t");
-                            break;
-                        case '\f':
-                            state.append("\\f");
-                            break;
-                        case '\\':
-                            state.append("\\\\");
-                            break;
-                        case '"':
-                            state.append("\\\"");
-                            break;
-                        case 0x7:
-                            state.append("\\a");
-                            break;
-                        case 0x8:
-                            state.append("\\b");
-                            break;
-                        case 0xB:
-                            state.append("\\v");
-                            break;
-                        default:
-                            if (Character.isISOControl(ch)) {
-                                state.append("\\x" + Integer.toHexString(charInt));
-                            } else if (charInt > 0x7F) {
-                                state.append("\\u" + Integer.toHexString(charInt));
-                            } else {
-                                state.append(ch);
+        private C withAttributes(Object obj) {
+            if (showAttributes() && hasAttributes(obj)) {
+                append("structure(");
+                return () -> {
+                    RAttributes attrs = ((RAttributable) obj).getAttributes();
+                    if (attrs != null) {
+                        Iterator<RAttribute> iter = attrs.iterator();
+                        while (iter.hasNext()) {
+                            RAttribute attr = iter.next();
+                            // TODO ignore function source attribute
+                            String attrName = attr.getName();
+                            append(", ");
+                            String dotName = null;
+                            switch (attrName) {
+                                case "dimnames":
+                                    dotName = ".Dimnames";
+                                    break;
+                                case "dim":
+                                    dotName = ".Dim";
+                                    break;
+                                case "names":
+                                    dotName = ".Names";
+                                    break;
+                                case "tsp":
+                                    dotName = ".Tsp";
+                                    break;
+                                case "levels":
+                                    dotName = ".Label";
+                                    break;
+
+                                default: {
+                                    opts = SIMPLEDEPARSE;
+                                    if (attrName.contains(" ")) {
+                                        append('"');
+                                        append(attrName);
+                                        append('"');
+                                    } else {
+                                        append(attrName);
+                                    }
+                                }
+
+                            }
+                            if (dotName != null) {
+                                append(dotName);
                             }
-                            break;
+                            append(" = ");
+                            process(attr.getValue());
+                            append(')');
+                        }
                     }
-                }
-                state.append('"');
-                break;
-            case LGLSXP:
-                byte lgl = (byte) element;
-                state.append(lgl == RRuntime.LOGICAL_TRUE ? "TRUE" : (lgl == RRuntime.LOGICAL_FALSE ? "FALSE" : "NA"));
-                break;
-            case REALSXP:
-                double d = (double) element;
-                state.append(encodeReal(d));
-                break;
-            case INTSXP:
-                int i = (int) element;
-                state.append(Integer.toString(i));
-                state.append('L');
-                break;
-            case CPLXSXP:
-                RComplex c = (RComplex) element;
-                String reRep = encodeReal(c.getRealPart());
-                String imRep = encodeReal(c.getImaginaryPart());
-                state.append(reRep);
-                state.append('+');
-                state.append(imRep);
-                state.append('i');
-                break;
-            default:
-                assert false;
+                };
+            } else {
+                return () -> {
+                };
+            }
+        }
+    }
+
+    /**
+     * Version for use by {@code RSerialize} to convert a CLOSXP/LANGSXP/PROMSXP into a parseable
+     * string.
+     */
+    @TruffleBoundary
+    public static String deparseDeserialize(Object obj) {
+        Object root = obj;
+        if (root instanceof RPairList) {
+            RPairList pl = (RPairList) root;
+            if (pl.getType() == SEXPTYPE.BCODESXP) {
+                RAbstractListVector list = (RAbstractListVector) pl.cdr();
+                root = list.getDataAtAsObject(0);
+            }
         }
-        return state;
+        return new DeparseVisitor(false, 80, true, 0, -1).process(root).getContents();
+    }
+
+    @TruffleBoundary
+    public static String deparseSyntaxElement(RSyntaxElement element) {
+        return new DeparseVisitor(false, RDeparse.MAX_Cutoff, true, 0, -1).append(element).getContents();
+    }
+
+    @TruffleBoundary
+    public static String deparse(Object expr) {
+        return new DeparseVisitor(false, RDeparse.MAX_Cutoff, true, 0, -1).process(expr).getContents();
+    }
+
+    @TruffleBoundary
+    public static String deparse(Object expr, int cutoff, boolean backtick, int opts, int nlines) {
+        return new DeparseVisitor(false, cutoff, backtick, opts, nlines).process(expr).getContents();
     }
 
+    // TODO: this should use the DoubleVectorPrinter
+
     private static final DecimalFormatSymbols decimalFormatSymbols;
     private static final DecimalFormat decimalFormat;
     private static final DecimalFormat simpleDecimalFormat;
@@ -1415,7 +1059,8 @@ public class RDeparse {
         simpleDecimalFormat = new DecimalFormat("#.##################", decimalFormatSymbols);
     }
 
-    private static String encodeReal(double d) {
+    private static String encodeReal(double x) {
+        double d = RRuntime.normalizeZero(x);
         if (d == 0 || withinSimpleRealRange(d)) {
             return simpleDecimalFormat.format(d);
         } else {
@@ -1432,11 +1077,7 @@ public class RDeparse {
         return (d > 0.0001 || d < -0.0001) && d < 100000 && d > -100000;
     }
 
-    public static String quotify(String name, State state) {
-        return quotify(name, state.backtick ? BACKTICK : DQUOTE);
-    }
-
-    public static String quotify(String name, char qc) {
+    private static String quotify(String name, char qc) {
         if (isValidName(name) || name.length() == 0) {
             return name;
         } else {
@@ -1454,16 +1095,11 @@ public class RDeparse {
         }
     }
 
-    private static final String[] keywords = {"NULL", "NA", "TRUE", "FALSE", "Inf", "NaN", "NA_integer_", "NA_real_", "NA_character_", "NA_complex_", "function", "while", "repeat", "for", "if", "in",
-                    "else", "next", "break", "..."};
+    private static final HashSet<String> keywords = new HashSet<>(Arrays.asList("NULL", "NA", "TRUE", "FALSE", "Inf", "NaN", "NA_integer_", "NA_real_", "NA_character_", "NA_complex_", "function",
+                    "while", "repeat", "for", "if", "in", "else", "next", "break", "..."));
 
-    public static boolean isKeyword(String name) {
-        for (int i = 0; i < keywords.length; i++) {
-            if (name.equals(keywords[i])) {
-                return true;
-            }
-        }
-        return false;
+    private static boolean isKeyword(String name) {
+        return keywords.contains(name);
     }
 
     public static boolean isValidName(String name) {
@@ -1499,71 +1135,4 @@ public class RDeparse {
             return 0;
         }
     }
-
-    private static boolean hasAttributes(Object obj) {
-        // TODO check (and ignore) function source attribute
-        if (obj instanceof RAttributable) {
-            RAttributes attrs = ((RAttributable) obj).getAttributes();
-            return attrs != null && !attrs.isEmpty();
-        } else {
-            return false;
-        }
-    }
-
-    private static void attr1(State state, Object obj) {
-        if (hasAttributes(obj)) {
-            state.append("structure(");
-        }
-    }
-
-    private static void attr2(State state, Object obj) {
-        if (obj instanceof RAttributable) {
-            RAttributes attrs = ((RAttributable) obj).getAttributes();
-            if (attrs != null) {
-                Iterator<RAttribute> iter = attrs.iterator();
-                while (iter.hasNext()) {
-                    RAttribute attr = iter.next();
-                    // TODO ignore function source attribute
-                    String attrName = attr.getName();
-                    state.append(", ");
-                    String dotName = null;
-                    switch (attrName) {
-                        case "dimnames":
-                            dotName = ".Dimnames";
-                            break;
-                        case "dim":
-                            dotName = ".Dim";
-                            break;
-                        case "names":
-                            dotName = ".Names";
-                            break;
-                        case "tsp":
-                            dotName = ".Tsp";
-                            break;
-                        case "levels":
-                            dotName = ".Label";
-                            break;
-
-                        default: {
-                            state.opts = SIMPLEDEPARSE;
-                            if (attrName.contains(" ")) {
-                                state.append('"');
-                                state.append(attrName);
-                                state.append('"');
-                            } else {
-                                state.append(attrName);
-                            }
-                        }
-
-                    }
-                    if (dotName != null) {
-                        state.append(dotName);
-                    }
-                    state.append(" = ");
-                    deparse2buff(state, attr.getValue());
-                    state.append(')');
-                }
-            }
-        }
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
index aa6317c133..1273db4605 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
@@ -33,6 +33,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
@@ -79,15 +80,7 @@ public interface RRuntimeASTAccess {
      */
     void setNames(RLanguage rl, RStringVector names);
 
-    /**
-     * Deparse {@code rl}.
-     */
-    void deparse(RDeparse.State state, RLanguage rl);
-
-    /**
-     * Deparse non-builtin function.
-     */
-    void deparse(RDeparse.State state, RFunction f);
+    RSyntaxFunction getSyntaxFunction(RFunction f);
 
     /**
      * Serialize a runtime value that requires non-standard treatment.
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index 19a3210627..c3c55b054a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -537,7 +537,7 @@ public class RSerialize {
                         if (FastROptions.debugMatches("printUclosure")) {
                             Debug.printClosure(rpl);
                         }
-                        String deparse = RDeparse.deparse(rpl);
+                        String deparse = RDeparse.deparseDeserialize(rpl);
                         try {
                             /*
                              * The tag of result is the enclosing environment (from NAMESPACESEXP)
@@ -563,7 +563,7 @@ public class RSerialize {
                          */
                         if (closureDepth == 0 && langDepth == 0) {
                             RPairList pl = (RPairList) result;
-                            String deparse = RDeparse.deparse(pl);
+                            String deparse = RDeparse.deparseDeserialize(pl);
                             RExpression expr = parse(deparse);
                             assert expr.getLength() == 1;
                             result = expr.getDataAt(0);
@@ -576,7 +576,7 @@ public class RSerialize {
                          * tag: environment for eval (or RNull if evaluated), car: value:
                          * RUnboundValue if not evaluated, cdr: expression
                          */
-                        String deparse = RDeparse.deparse(pl);
+                        String deparse = RDeparse.deparseDeserialize(pl.cdr());
                         RExpression expr = parse(deparse);
                         assert expr.getLength() == 1;
                         RLanguage lang = (RLanguage) expr.getDataAt(0);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java
index 7b650615d0..2724e59029 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java
@@ -25,7 +25,6 @@ package com.oracle.truffle.r.runtime.nodes;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeVisitor;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RSerialize;
@@ -124,11 +123,6 @@ public abstract class RBaseNode extends Node {
         return null;
     }
 
-    public void deparse(State state) {
-        RSyntaxNode syntaxNode = getRSyntaxNode();
-        syntaxNode.deparseImpl(state);
-    }
-
     public RSyntaxNode substitute(REnvironment env) {
         RSyntaxNode syntaxNode = getRSyntaxNode();
         return syntaxNode.substituteImpl(env);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
index 8ae220151a..d76292bd07 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
@@ -53,9 +53,9 @@ import com.oracle.truffle.r.runtime.context.RContext;
  * One particular case is {@link #LAZY_DEPARSE} which indicates that a valid {@link SourceSection}
  * can be produced for the associated node, but it is computed lazily, when requested.
  *
- * Every implementor of this interface must provide an implementation of the {@link #deparseImpl},
- * {@link #serializeImpl}, and {@link #substituteImpl} methods. These are invoked by the
- * corresponding methods on {@link RBaseNode} after the correct {@link RSyntaxNode} is located.
+ * Every implementor of this interface must provide an implementation of the {@link #serializeImpl}
+ * and {@link #substituteImpl} methods. These are invoked by the corresponding methods on
+ * {@link RBaseNode} after the correct {@link RSyntaxNode} is located.
  */
 public interface RSyntaxNode extends RSyntaxNodeSPI, RSyntaxElement {
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNodeSPI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNodeSPI.java
index 1b8c3ac146..47760daff1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNodeSPI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNodeSPI.java
@@ -22,9 +22,8 @@
  */
 package com.oracle.truffle.r.runtime.nodes;
 
-import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.RSerialize;
+import com.oracle.truffle.r.runtime.RSerialize.State;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 /**
@@ -34,11 +33,6 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
  *
  */
 interface RSyntaxNodeSPI {
-    /**
-     * Support for the {@code deparse} builtin function. The source representation should be
-     * appended to the {@link State}.
-     */
-    void deparseImpl(RDeparse.State state);
 
     /**
      * Support for the {@code substitute} builtin function. Assert: {this.isSyntax() == true}. N.B.
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index 9f2dcdf304..7fff3ff75f 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -179,7 +179,6 @@ com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java,gnu_r_gentleman_ihaka.copyright
-com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java,gnu_r.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java,purdue.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/IsFactorNode.java,purdue.copyright
 com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g,purdue.copyright
-- 
GitLab