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