From 2b6c171f49457cb3cb8df84c0d0e1eeb3ca7074d Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Thu, 11 Dec 2014 08:01:17 -0800
Subject: [PATCH] progress on tools namespace load

---
 .hgignore                                     |   2 +
 .../com/oracle/truffle/r/engine/REngine.java  |   4 +
 .../r/nodes/builtin/RBuiltinPackages.java     |  14 +
 .../truffle/r/nodes/builtin/base/IConv.java   |  40 ++
 .../builtin/base/InfixEmulationFunctions.java |  80 +++-
 .../r/nodes/builtin/base/R/New-Internal.R     |  20 +-
 .../truffle/r/runtime/RBuiltinLookup.java     |   2 +
 .../oracle/truffle/r/runtime/RContext.java    |   5 +
 .../oracle/truffle/r/runtime/RDeparse.java    | 403 +++++++++++-------
 9 files changed, 389 insertions(+), 181 deletions(-)
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java

diff --git a/.hgignore b/.hgignore
index d6a518a2e4..b4e7332a69 100644
--- a/.hgignore
+++ b/.hgignore
@@ -10,6 +10,7 @@
 ^mx.fastr/netbeans-config.zip
 ^com.oracle.truffle.r.test/rpackages/testrlibs_user
 ^com.oracle.truffle.r.test.native/urand/lib/liburand.so
+^library/
 ^build/
 ^build-nograal/
 ^dist/
@@ -88,3 +89,4 @@ R.tokens
 findbugs.html
 com.oracle.truffle.r.native/builtinlibs/lib/*/librfficall.*
 com.oracle.truffle.r.native/builtinlibs/lib/*/libR.dylib
+com.oracle.truffle.r.native/library/tools/lib/*/*.*
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index fab4e25b44..0c82cef2bc 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -160,6 +160,10 @@ public final class REngine implements RContext.Engine {
         RBuiltinPackages.load(name, frame, envForFrame);
     }
 
+    public boolean isBuiltin(String name) {
+        return builtinLookup.isBuiltin(name);
+    }
+
     public RFunction lookupBuiltin(String name) {
         return builtinLookup.lookup(name);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
index 1923c1603c..8d6810e612 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
@@ -136,4 +136,18 @@ public final class RBuiltinPackages implements RBuiltinLookup {
         return null;
     }
 
+    /**
+     * Used by {@link RDeparse} to detect whether a symbol is a builtin (or special). N.B. special
+     * functions are not explicitly denoted currently, only by virtue of the
+     * {@link RBuiltin#nonEvalArgs} attribute.
+     */
+    public boolean isBuiltin(String name) {
+        for (RBuiltinPackage pkg : packages.values()) {
+            if (pkg.lookupByName(name) != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java
new file mode 100644
index 0000000000..49011dc21b
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.base;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+@RBuiltin(name = "iconv", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "from", "to", "sub", "mark", "toRaw"})
+public abstract class IConv extends RBuiltinNode {
+    @SuppressWarnings("unused")
+    @Specialization
+    protected RStringVector doIConv(RAbstractStringVector x, Object from, Object to, Object sub, byte mark, byte toRaw) {
+        // TODO implement
+        RStringVector xv = x.materialize();
+        return RDataFactory.createStringVector(xv.getDataCopy(), RDataFactory.COMPLETE_VECTOR);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
index 3a5c3f7c7c..19f80da75d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
@@ -32,6 +32,9 @@ import com.oracle.truffle.r.runtime.*;
  * These definitions create the illusion that the definitions exist, even if they are not actually
  * bound to anything useful.
  *
+ * One important reason that these must exist as {@link RBuiltin}s is that they occur when deparsing
+ * packages and the deparse logic depends on them being found as builtins. See {@link RDeparse}.
+ *
  * N.B. These could be implemented by delegating to the equivalent nodes, e.g.
  * {@link AccessArrayNode}.
  */
@@ -44,7 +47,7 @@ public class InfixEmulationFunctions {
     }
 
     @RBuiltin(name = "[", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
-    public abstract static class AccessArrayNodeBuiltin extends ErrorAdapter {
+    public abstract static class AccessArrayBuiltin extends ErrorAdapter {
         @SuppressWarnings("unused")
         @Specialization
         protected Object doIt(Object x, Object i) {
@@ -53,7 +56,7 @@ public class InfixEmulationFunctions {
     }
 
     @RBuiltin(name = "[[", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
-    public abstract static class AccessArrayNodeSubsetBuiltin extends ErrorAdapter {
+    public abstract static class AccessArraySubsetBuiltin extends ErrorAdapter {
         @SuppressWarnings("unused")
         @Specialization
         protected Object doIt(Object x, Object i) {
@@ -62,7 +65,7 @@ public class InfixEmulationFunctions {
     }
 
     @RBuiltin(name = "[<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
-    public abstract static class UpdateArrayNodeBuiltin extends ErrorAdapter {
+    public abstract static class UpdateArrayBuiltin extends ErrorAdapter {
         @SuppressWarnings("unused")
         @Specialization
         protected Object doIt(Object x, Object i) {
@@ -79,8 +82,26 @@ public class InfixEmulationFunctions {
         }
     }
 
+    @RBuiltin(name = "<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
+    public abstract static class AssignBuiltin extends ErrorAdapter {
+        @SuppressWarnings("unused")
+        @Specialization
+        protected Object doIt(Object x, Object i) {
+            throw nyi();
+        }
+    }
+
+    @RBuiltin(name = "<<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
+    public abstract static class AssignOuterBuiltin extends ErrorAdapter {
+        @SuppressWarnings("unused")
+        @Specialization
+        protected Object doIt(Object x, Object i) {
+            throw nyi();
+        }
+    }
+
     @RBuiltin(name = "$", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
-    public abstract static class AccessFieldNodeBuiltin extends ErrorAdapter {
+    public abstract static class AccessFieldBuiltin extends ErrorAdapter {
         @SuppressWarnings("unused")
         @Specialization
         protected Object doIt(Object x, Object i) {
@@ -89,7 +110,7 @@ public class InfixEmulationFunctions {
     }
 
     @RBuiltin(name = "$<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
-    public abstract static class UpdateFieldNodeBuiltin extends ErrorAdapter {
+    public abstract static class UpdateFieldBuiltin extends ErrorAdapter {
         @SuppressWarnings("unused")
         @Specialization
         protected Object doIt(Object x, Object i) {
@@ -98,7 +119,7 @@ public class InfixEmulationFunctions {
     }
 
     @RBuiltin(name = "{", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
-    public abstract static class BraceNodeBuiltin extends ErrorAdapter {
+    public abstract static class BraceBuiltin extends ErrorAdapter {
         @SuppressWarnings("unused")
         @Specialization
         protected Object doIt(Object x) {
@@ -107,7 +128,52 @@ public class InfixEmulationFunctions {
     }
 
     @RBuiltin(name = "(", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
-    public abstract static class ParenNodeBuiltin extends ErrorAdapter {
+    public abstract static class ParenBuiltin extends ErrorAdapter {
+        @SuppressWarnings("unused")
+        @Specialization
+        protected Object doIt(Object x) {
+            throw nyi();
+        }
+    }
+
+    @RBuiltin(name = "if", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    public abstract static class IfBuiltin extends ErrorAdapter {
+        @SuppressWarnings("unused")
+        @Specialization
+        protected Object doIt(Object x) {
+            throw nyi();
+        }
+    }
+
+    @RBuiltin(name = "while", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    public abstract static class WhileBuiltin extends ErrorAdapter {
+        @SuppressWarnings("unused")
+        @Specialization
+        protected Object doIt(Object x) {
+            throw nyi();
+        }
+    }
+
+    @RBuiltin(name = "for", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    public abstract static class ForBuiltin extends ErrorAdapter {
+        @SuppressWarnings("unused")
+        @Specialization
+        protected Object doIt(Object x) {
+            throw nyi();
+        }
+    }
+
+    @RBuiltin(name = "break", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    public abstract static class BreakBuiltin extends ErrorAdapter {
+        @SuppressWarnings("unused")
+        @Specialization
+        protected Object doIt(Object x) {
+            throw nyi();
+        }
+    }
+
+    @RBuiltin(name = "next", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    public abstract static class NextBuiltin extends ErrorAdapter {
         @SuppressWarnings("unused")
         @Specialization
         protected Object doIt(Object x) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/New-Internal.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/New-Internal.R
index fb0d909552..ab4ee53305 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/New-Internal.R
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/New-Internal.R
@@ -89,7 +89,7 @@ cbind <- function(..., deparse.level = 1)
   .Internal(cbind.internal(deparse.level, ...))
 
 rbind <- function(..., deparse.level = 1)
-	# TODO: if the name of the internal and of R function is the same then R function is not picked up	
+	# TODO: if the name of the internal and of R function is the same then R function is not picked up
   .Internal(rbind.internal(deparse.level, ...))
 
 ### for methods:::bind_activation
@@ -240,15 +240,15 @@ encodeString <- function(x, width = 0L, quote = "", na.encode = TRUE,
 	.Internal(encodeString(x, width, quote, justify, na.encode))
 }
 
-#l10n_info <- function() .Internal(l10n_info())
-#
-#iconv <- function(x, from = "", to = "", sub = NA, mark = TRUE, toRaw = FALSE)
-#{
-#  if(! (is.character(x) || (is.list(x) && is.null(oldClass(x)))))
-#    x <- as.character(x)
-#  .Internal(iconv(x, from, to, as.character(sub), mark, toRaw))
-#}
-#
+l10n_info <- function() .Internal(l10n_info())
+
+iconv <- function(x, from = "", to = "", sub = NA, mark = TRUE, toRaw = FALSE)
+{
+  if(! (is.character(x) || (is.list(x) && is.null(oldClass(x)))))
+    x <- as.character(x)
+  .Internal(iconv(x, from, to, as.character(sub), mark, toRaw))
+}
+
 #iconvlist <- function()
 #{
 #  int <- .Internal(iconv(NULL, "", "", "", TRUE, FALSE))
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookup.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookup.java
index 3a1efb2f7c..73b412f534 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookup.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookup.java
@@ -26,6 +26,8 @@ import com.oracle.truffle.r.runtime.data.*;
 
 public interface RBuiltinLookup {
 
+    boolean isBuiltin(String name);
+
     RFunction lookup(String methodName);
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
index 10fb1e6da5..d1cfae1e99 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
@@ -156,6 +156,11 @@ public final class RContext extends ExecutionContext {
          */
         void loadDefaultPackage(String name, MaterializedFrame frame, REnvironment envForFrame);
 
+        /**
+         * Is {@code name} a builtin function?
+         */
+        boolean isBuiltin(String name);
+
         /**
          * Return the {@link RFunction} for the builtin {@code name}.
          *
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 200e19a771..eee693736f 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
@@ -133,6 +133,7 @@ public class RDeparse {
         new Func("{", new PPInfo(PP.CURLY, 0, false)),
         new Func("(", new PPInfo(PP.PAREN, 0, false)),
         new Func("<-", new PPInfo(PP.ASSIGN, 1, true)),
+        new Func("<<-", new PPInfo(PP.ASSIGN, 1, true)),
         new Func("[", new PPInfo(PP.SUBSET, 17, false)),
         new Func("[[", new PPInfo(PP.SUBSET, 17, false)),
         new Func("$", new PPInfo(PP.DOLLAR, 15, false)),
@@ -357,7 +358,7 @@ public class RDeparse {
                 RPairList f = (RPairList) obj;
                 state.append("function (");
                 if (f.car() instanceof RPairList) {
-                    args2buff(state, (RPairList) f.car(), false, true);
+                    args2buff(state, f.car(), false, true);
                 }
                 state.append(") ");
                 state.writeline();
@@ -429,196 +430,208 @@ public class RDeparse {
                 if (carType == SEXPTYPE.SYMSXP) {
                     RSymbol symbol = (RSymbol) car;
                     String op = symbol.getName();
-                    RPairList pl = (RPairList) cdr;
-                    // TODO BUILTINSXP, SPECIALSXP, userBinOp
-                    PPInfo 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 */false) {
-                            // TODO
-                            fop = new PPInfo(PP.BINARY, fop.prec, fop.rightassoc);
-                        }
-                    }
-                    switch (fop.kind) {
-                        case ASSIGN: {
-                            // TODO needsparens
-                            deparse2buff(state, pl.car());
-                            state.append(' ');
-                            state.append(op);
-                            state.append(' ');
-                            deparse2buff(state, ((RPairList) pl.cdr()).car());
-                            break;
+                    if (RContext.getEngine().isBuiltin(op)) {
+                        RPairList pl = (RPairList) cdr;
+                        // TODO BUILTINSXP, SPECIALSXP, userBinOp
+                        PPInfo 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 */false) {
+                                // TODO
+                                fop = new PPInfo(PP.BINARY, fop.prec, fop.rightassoc);
+                            }
                         }
-
-                        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++;
-                                }
+                        switch (fop.kind) {
+                            case ASSIGN: {
+                                // TODO needsparens
+                                deparse2buff(state, pl.car());
+                                state.append(' ');
+                                state.append(op);
+                                state.append(' ');
+                                deparse2buff(state, ((RPairList) pl.cdr()).car());
+                                break;
                             }
-                            int lenpl = pl.getLength();
-                            if (lenpl > 2) {
-                                deparse2buff(state, pl.cadr());
+
+                            case IF: {
+                                state.append("if (");
+                                deparse2buff(state, pl.car());
+                                state.append(')');
+                                boolean lookahead = false;
                                 if (state.incurly > 0 && state.inlist == 0) {
-                                    state.writeline();
+                                    lookahead = curlyahead(pl.cadr());
                                     if (!lookahead) {
-                                        state.indent--;
+                                        state.writeline();
+                                        state.indent++;
                                     }
+                                }
+                                int lenpl = pl.getLength();
+                                if (lenpl > 2) {
+                                    deparse2buff(state, pl.cadr());
+                                    if (state.incurly > 0 && state.inlist == 0) {
+                                        state.writeline();
+                                        if (!lookahead) {
+                                            state.indent--;
+                                        }
+                                    } else {
+                                        state.append(' ');
+                                    }
+                                    state.append("else ");
+                                    deparse2buff(state, pl.caddr());
                                 } else {
-                                    state.append(' ');
+                                    deparse2buff(state, pl.cadr());
+                                    if (state.incurly > 0 && !lookahead && state.inlist == 0) {
+                                        state.indent--;
+                                    }
                                 }
-                                state.append("else ");
-                                deparse2buff(state, pl.caddr());
-                            } else {
+                                break;
+                            }
+
+                            case WHILE: {
+                                state.append("while (");
+                                deparse2buff(state, pl.car());
+                                state.append(") ");
                                 deparse2buff(state, pl.cadr());
-                                if (state.incurly > 0 && !lookahead && state.inlist == 0) {
-                                    state.indent--;
-                                }
+                                break;
                             }
-                            break;
-                        }
 
-                        case WHILE: {
-                            state.append("while (");
-                            deparse2buff(state, pl.car());
-                            state.append(") ");
-                            deparse2buff(state, pl.cadr());
-                            break;
-                        }
+                            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 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 REPEAT:
-                            state.append("repeat ");
-                            deparse2buff(state, pl.car());
-                            break;
+                            case BINARY:
+                            case BINARY2: {
+                                // TODO parens
+                                deparse2buff(state, pl.car());
+                                state.append(' ');
+                                state.append(op);
+                                state.append(' ');
+                                if (fop.kind == PP.BINARY) {
+                                    lbreak = state.linebreak(lbreak);
+                                }
+                                deparse2buff(state, pl.cadr());
+                                if (fop.kind == PP.BINARY) {
+                                    if (lbreak) {
+                                        state.indent--;
+                                        lbreak = false;
+                                    }
+                                }
+                                break;
+                            }
 
-                        case BINARY:
-                        case BINARY2: {
-                            // TODO parens
-                            deparse2buff(state, pl.car());
-                            state.append(' ');
-                            state.append(op);
-                            state.append(' ');
-                            if (fop.kind == PP.BINARY) {
-                                lbreak = state.linebreak(lbreak);
+                            case UNARY: {
+                                state.append(op);
+                                deparse2buff(state, pl.car());
+                                break;
                             }
-                            deparse2buff(state, pl.cadr());
-                            if (fop.kind == PP.BINARY) {
-                                if (lbreak) {
-                                    state.indent--;
-                                    lbreak = false;
+
+                            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;
                             }
-                            break;
-                        }
 
-                        case UNARY: {
-                            state.append(op);
-                            deparse2buff(state, pl.car());
-                            break;
-                        }
+                            case PAREN:
+                                state.append('(');
+                                deparse2buff(state, pl.car());
+                                state.append(')');
+                                break;
 
-                        case CURLY: {
-                            state.append(op);
-                            state.incurly++;
-                            state.indent++;
-                            state.writeline();
-                            while (pl != null) {
+                            case SUBSET: {
                                 deparse2buff(state, pl.car());
-                                state.writeline();
-                                pl = next(pl);
+                                state.append(op);
+                                args2buff(state, pl.cdr(), false, false);
+                                if (op.equals("[")) {
+                                    state.append(']');
+                                } else {
+                                    state.append("]]");
+                                }
+                                break;
                             }
-                            state.indent--;
-                            state.append('}');
-                            state.incurly--;
-                            break;
-                        }
 
-                        case PAREN:
-                            state.append('(');
-                            deparse2buff(state, pl.car());
-                            state.append(')');
-                            break;
+                            case FUNCTION:
+                                state.append(op);
+                                state.append('(');
+                                args2buff(state, pl.car(), false, true);
+                                state.append(')');
+                                deparse2buff(state, pl.cadr());
+                                break;
 
-                        case SUBSET: {
-                            deparse2buff(state, pl.car());
-                            state.append(op);
-                            args2buff(state, (RPairList) pl.cdr(), false, false);
-                            if (op.equals("[")) {
-                                state.append(']');
-                            } else {
-                                state.append("]]");
+                            case DOLLAR: {
+                                // TODO needparens, etc
+                                deparse2buff(state, pl.car());
+                                state.append(op);
+                                deparse2buff(state, pl.cadr());
+                                break;
                             }
-                            break;
-                        }
 
-                        case FUNCTION:
-                            state.append(op);
-                            state.append('(');
-                            args2buff(state, (RPairList) pl.car(), false, true);
-                            state.append(')');
-                            deparse2buff(state, pl.cadr());
-                            break;
+                            case FUNCALL:
+                            case RETURN: {
+                                if (state.backtick) {
+                                    state.append('`');
+                                    state.append(op);
+                                    state.append('`');
+                                } else {
+                                    state.append(op);
+                                }
+                                state.append('(');
+                                state.inlist++;
+                                args2buff(state, cdr, false, false);
+                                state.inlist--;
+                                state.append(')');
+                                break;
+                            }
 
-                        case DOLLAR: {
-                            // TODO needparens, etc
-                            deparse2buff(state, pl.car());
-                            state.append(op);
-                            deparse2buff(state, pl.cadr());
-                            break;
+                            default:
+                                assert false;
                         }
-
-                        case FUNCALL:
-                        case RETURN: {
-                            if (state.backtick) {
-                                state.append('`');
-                                state.append(op);
-                                state.append('`');
-                            } else {
-                                state.append(op);
-                            }
+                    } else {
+                        // TODO promise?
+                        if (op.equals("::") || op.equals(":::")) {
+                            // special case
+                        } else {
+                            state.append(quotify(op));
                             state.append('(');
-                            state.inlist++;
-                            args2buff(state, (RPairList) cdr, false, false);
-                            state.inlist--;
+                            args2buff(state, cdr, false, false);
                             state.append(')');
-                            break;
                         }
-
-                        default:
-                            assert false;
                     }
                 } else if (carType == SEXPTYPE.CLOSXP || carType == SEXPTYPE.SPECIALSXP || carType == SEXPTYPE.BUILTINSXP) {
                     // TODO needparens, etc
                     deparse2buff(state, car);
                     state.append('(');
-                    args2buff(state, (RPairList) cdr, false, false);
+                    args2buff(state, cdr, false, false);
                     state.append(')');
                 } else {
                     // lambda
@@ -630,7 +643,7 @@ public class RDeparse {
                         deparse2buff(state, car);
                     }
                     state.append('(');
-                    args2buff(state, (RPairList) f.cdr(), false, false);
+                    args2buff(state, f.cdr(), false, false);
                     state.append(')');
                 }
                 break;
@@ -649,12 +662,12 @@ public class RDeparse {
                 /*
                  * 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 RPaieLits cdr
+                 * 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();
-                assert plcdr.getLength() == 1;
                 deparse2buff(state, plcdr.getDataAtAsObject(0));
                 break;
             }
@@ -734,9 +747,14 @@ public class RDeparse {
     }
 
     @TruffleBoundary
-    private static State args2buff(State state, RPairList args, @SuppressWarnings("unused") boolean lineb, boolean formals) {
+    private static State args2buff(State state, Object args, @SuppressWarnings("unused") boolean lineb, boolean formals) {
         boolean lbreak = false;
-        RPairList arglist = args;
+        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) {
@@ -796,7 +814,26 @@ public class RDeparse {
                     assert false;
             }
         } else if (type == SEXPTYPE.INTSXP) {
-            // TODO
+            // TODO seq detection and COMPAT?
+            if (len > 1) {
+                state.append("c(");
+            }
+            RIntVector intVec = (RIntVector) 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');
+                }
+                if (i < len - 1) {
+                    state.append(", ");
+                }
+            }
+            if (len > 1) {
+                state.append(')');
+            }
         } else {
             // TODO NA checks
             if (len > 1) {
@@ -826,7 +863,40 @@ public class RDeparse {
             case STRSXP:
                 // TODO encoding
                 state.append('"');
-                state.append((String) element);
+                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;
+                        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);
+                            }
+                            break;
+                    }
+                }
                 state.append('"');
                 break;
             case LGLSXP:
@@ -848,4 +918,9 @@ public class RDeparse {
         return state;
     }
 
+    private static String quotify(String name) {
+        // TODO implement
+        return name;
+    }
+
 }
-- 
GitLab