diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/PipelineToCastNodeTests.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/PipelineToCastNodeTests.java new file mode 100644 index 0000000000000000000000000000000000000000..9692ffc70defc9a6c99c436a263db1e42ac3b857 --- /dev/null +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/PipelineToCastNodeTests.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.test; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.oracle.truffle.r.nodes.builtin.CastBuilder.ArgCastBuilderState; +import com.oracle.truffle.r.nodes.builtin.CastBuilder.PipelineConfigBuilder; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.TypeFilter; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.AsVectorStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.FilterStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.FindFirstStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineToCastNode; +import com.oracle.truffle.r.nodes.unary.BypassNode; +import com.oracle.truffle.r.nodes.unary.CastIntegerNode; +import com.oracle.truffle.r.nodes.unary.CastLogicalNode; +import com.oracle.truffle.r.nodes.unary.CastNode; +import com.oracle.truffle.r.nodes.unary.CastStringNode; +import com.oracle.truffle.r.nodes.unary.ChainedCastNode; +import com.oracle.truffle.r.nodes.unary.FilterNode; +import com.oracle.truffle.r.nodes.unary.FindFirstNode; +import com.oracle.truffle.r.runtime.RType; +import com.oracle.truffle.r.runtime.env.REnvironment; + +public class PipelineToCastNodeTests { + @Test + public void asLogicalVector() { + CastNode pipeline = createPipeline(new AsVectorStep(RType.Logical)); + CastNode castNode = assertBypassNode(pipeline); + assertTrue(castNode instanceof CastLogicalNode); + } + + @Test + public void asStringVector_findFirst() { + CastNode pipeline = createPipeline(new AsVectorStep(RType.Character).setNext(new FindFirstStep("hello", String.class, null))); + CastNode chain = assertBypassNode(pipeline); + assertChainedCast(chain, CastStringNode.class, FindFirstNode.class); + FindFirstNode findFirst = (FindFirstNode) ((ChainedCastNode) chain).getSecondCast(); + assertEquals("hello", findFirst.getDefaultValue()); + } + + @Test + public void mustBeREnvironment_asIntegerVector_findFirst() { + CastNode pipeline = createPipeline(new FilterStep(new TypeFilter(REnvironment.class, x -> x instanceof REnvironment, null)).setNext( + new AsVectorStep(RType.Integer).setNext(new FindFirstStep("hello", String.class, null)))); + CastNode chain = assertBypassNode(pipeline); + assertChainedCast(chain, ChainedCastNode.class, FindFirstNode.class); + CastNode next = ((ChainedCastNode) chain).getFirstCast(); + assertChainedCast(next, FilterNode.class, CastIntegerNode.class); + FindFirstNode findFirst = (FindFirstNode) ((ChainedCastNode) chain).getSecondCast(); + assertEquals("hello", findFirst.getDefaultValue()); + } + + private CastNode assertBypassNode(CastNode node) { + assertTrue(node instanceof BypassNode); + return ((BypassNode) node).getWrappedHead(); + } + + private static void assertChainedCast(CastNode node, Class<?> expectedFirst, Class<?> expectedSecond) { + assertTrue(node instanceof ChainedCastNode); + assertTrue(expectedFirst.isInstance(((ChainedCastNode) node).getFirstCast())); + assertTrue(expectedSecond.isInstance(((ChainedCastNode) node).getSecondCast())); + } + + private CastNode createPipeline(PipelineStep lastStep) { + PipelineConfigBuilder configBuilder = new PipelineConfigBuilder(new ArgCastBuilderState(0, "x", null, null, true)); + return PipelineToCastNode.convert(configBuilder, lastStep); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java index 0f7c93ede71dcb0797ecf2dc6473f88d3ff1eb9b..18262b4a33d604418e07df0011f1d1c1e574fca8 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java @@ -1412,7 +1412,7 @@ public final class CastBuilder { } - static class ArgCastBuilderState { + public static class ArgCastBuilderState { private final DefaultError defaultDefaultError; private final int argumentIndex; @@ -1423,7 +1423,7 @@ public final class CastBuilder { private DefaultError defError; private DefaultError defWarning; - ArgCastBuilderState(int argumentIndex, String argumentName, ArgCastBuilderFactory fact, CastBuilder cb, boolean boxPrimitives) { + public ArgCastBuilderState(int argumentIndex, String argumentName, ArgCastBuilderFactory fact, CastBuilder cb, boolean boxPrimitives) { this.argumentIndex = argumentIndex; this.argumentName = argumentName; this.factory = fact; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStep.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStep.java deleted file mode 100644 index 12b394fbc5a99db6a9bee581479bec1f618445b0..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStep.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.nodes.builtin.casts; - -import com.oracle.truffle.r.nodes.builtin.CastBuilder.PipelineConfigBuilder; -import com.oracle.truffle.r.runtime.RType; - -/** - * Represents a single step in the cast pipeline. - */ -public abstract class CastStep { - - private CastStep next; - - public final CastStep getNext() { - return next; - } - - public final void setNext(CastStep next) { - this.next = next; - } - - public abstract <T> T accept(CastStepVisitor<T> visitor); - - public interface CastStepVisitor<T> { - T visit(PipelineConfStep step); - - T visit(FindFirstStep step); - - T visit(AsVectorStep step); - - T visit(MapStep step); - - T visit(MapIfStep step); - - T visit(FilterStep step); - - T visit(NotNAStep step); - } - - public static class PipelineConfStep extends CastStep { - private final PipelineConfigBuilder pcb; - // TODO??: just remember from the builder: boolean acceptNull, boolean acceptMissing, - // defaultError?, ... - - public PipelineConfStep(PipelineConfigBuilder pcb) { - this.pcb = pcb; - } - - public PipelineConfigBuilder getConfigBuilder() { - return pcb; - } - - @Override - public <T> T accept(CastStepVisitor<T> visitor) { - return visitor.visit(this); - } - } - - public static class NotNAStep extends CastStep { - @Override - public <T> T accept(CastStepVisitor<T> visitor) { - return visitor.visit(this); - } - } - - public static class FindFirstStep extends CastStep { - private final Object defaultValue; - private final Class<?> elementClass; - - public FindFirstStep(Object defaultValue, Class<?> elementClass) { - this.defaultValue = defaultValue; - this.elementClass = elementClass; - } - - public Object getDefaultValue() { - return defaultValue; - } - - public Class<?> getElementClass() { - return elementClass; - } - - @Override - public <T> T accept(CastStepVisitor<T> visitor) { - return visitor.visit(this); - } - } - - public static class AsVectorStep extends CastStep { - private final RType type; - - public AsVectorStep(RType type) { - assert type.isVector() && type != RType.List : "AsVectorStep supports only vector types minus list."; - this.type = type; - } - - public RType getType() { - return type; - } - - @Override - public <T> T accept(CastStepVisitor<T> visitor) { - return visitor.visit(this); - } - } - - public static class MapStep extends CastStep { - @Override - public <T> T accept(CastStepVisitor<T> visitor) { - return visitor.visit(this); - } - } - - public static class MapIfStep extends CastStep { - @Override - public <T> T accept(CastStepVisitor<T> visitor) { - return visitor.visit(this); - } - } - - public static class FilterStep extends CastStep { - private final Filter filter; - - public FilterStep(Filter filter) { - this.filter = filter; - } - - public Filter getFilter() { - return filter; - } - - @Override - public <T> T accept(CastStepVisitor<T> visitor) { - return visitor.visit(this); - } - } -} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStepToCastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStepToCastNode.java deleted file mode 100644 index 4077e66bef8842190bb236e744060cf2af2ef60c..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStepToCastNode.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.nodes.builtin.casts; - -import com.oracle.truffle.r.nodes.builtin.ArgumentFilter; -import com.oracle.truffle.r.nodes.builtin.CastBuilder.PipelineConfigBuilder; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.AsVectorStep; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.CastStepVisitor; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.FilterStep; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.FindFirstStep; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.MapIfStep; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.MapStep; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.NotNAStep; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.PipelineConfStep; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.AndFilter; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.CompareFilter; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.FilterVisitor; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.NotFilter; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.NumericFilter; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.OrFilter; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.RTypeFilter; -import com.oracle.truffle.r.nodes.builtin.casts.Filter.TypeFilter; -import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapByteToBoolean; -import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapDoubleToInt; -import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapToCharAt; -import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapToValue; -import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapperVisitor; -import com.oracle.truffle.r.nodes.unary.BypassNode; -import com.oracle.truffle.r.nodes.unary.CastNode; -import com.oracle.truffle.r.nodes.unary.ChainedCastNode; -import com.oracle.truffle.r.nodes.unary.FilterNode; -import com.oracle.truffle.r.nodes.unary.FindFirstNodeGen; -import com.oracle.truffle.r.nodes.unary.MapNode; -import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RError.Message; -import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.RType; -import com.oracle.truffle.r.runtime.data.RDoubleVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; -import com.oracle.truffle.r.runtime.nodes.RBaseNode; - -/** - * Converts given pipeline into corresponding cast nodes chain. - */ -public final class CastStepToCastNode { - - public static CastNode convert(PipelineConfStep firstStep) { - PipelineConfigBuilder configBuilder = firstStep.getConfigBuilder(); - - CastNodeFactory nodeFactory = new CastNodeFactory(null, null, null, true); // TODO: default - // error instead - // of nulls - CastNode prevCastNode = null; - CastStep currCastStep = firstStep.getNext(); - while (currCastStep != null) { - CastNode node = nodeFactory.create(currCastStep); - if (prevCastNode == null) { - prevCastNode = node; - } else { - CastNode finalPrevCastNode = prevCastNode; - prevCastNode = new ChainedCastNode(() -> node, () -> finalPrevCastNode); - } - - currCastStep = currCastStep.getNext(); - } - return BypassNode.create(configBuilder, prevCastNode); - } - - private static final class CastNodeFactory implements CastStepVisitor<CastNode> { - private final RBaseNode defaultCallObj; - private final RError.Message defaultMessage; - private final Object[] defaultMessageArgs; - private final boolean boxPrimitives; - - public CastNodeFactory(RBaseNode defaultCallObj, Message defaultMessage, Object[] defaultMessageArgs, boolean boxPrimitives) { - this.defaultCallObj = defaultCallObj; - this.defaultMessage = defaultMessage; - this.defaultMessageArgs = defaultMessageArgs; - this.boxPrimitives = boxPrimitives; - } - - public CastNode create(CastStep step) { - return step.accept(this); - } - - @Override - public CastNode visit(PipelineConfStep step) { - throw RInternalError.shouldNotReachHere("There can be only one PipelineConfStep " + - "in pipeline as the first node and it should have been handled by the convert method."); - } - - @Override - public CastNode visit(FindFirstStep step) { - return FindFirstNodeGen.create(step.getElementClass(), step.getDefaultValue()); - } - - @Override - public CastNode visit(FilterStep step) { - ArgumentFilter<Object, Boolean> filter = ArgumentFilterFactory.create(step.getFilter()); - // TODO: check error in step and use it instead of the default one - return FilterNode.create(filter, /* TODO: isWarning?? */false, defaultCallObj, defaultMessage, defaultMessageArgs, boxPrimitives); - } - - @Override - public CastNode visit(NotNAStep step) { - return null; - } - - @Override - public CastNode visit(AsVectorStep step) { - return null; - } - - @Override - public CastNode visit(MapStep step) { - return null; - } - - @Override - public CastNode visit(MapIfStep step) { - return null; - } - } - - private static final class ArgumentFilterFactory implements FilterVisitor<ArgumentFilter<Object, Boolean>> { - - private static final ArgumentFilterFactory INSTANCE = new ArgumentFilterFactory(); - - private ArgumentFilterFactory() { - // singleton - } - - public static ArgumentFilter<Object, Boolean> create(Filter filter) { - return filter.accept(INSTANCE); - } - - @Override - public ArgumentFilter<Object, Boolean> visit(TypeFilter filter) { - return filter.getInstanceOfLambda(); - } - - @Override - public ArgumentFilter<Object, Boolean> visit(RTypeFilter filter) { - if (filter.getType() == RType.Integer) { - return x -> x instanceof Integer || x instanceof RAbstractIntVector; - } else if (filter.getType() == RType.Double) { - return x -> x instanceof Double || x instanceof RDoubleVector; - } else { - throw RInternalError.unimplemented("TODO: more types here"); - } - } - - @Override - public ArgumentFilter<Object, Boolean> visit(NumericFilter filter) { - return x -> x instanceof Integer || x instanceof RAbstractIntVector || x instanceof Double || x instanceof RAbstractDoubleVector || x instanceof Byte || - x instanceof RAbstractLogicalVector; - } - - @Override - public ArgumentFilter<Object, Boolean> visit(CompareFilter filter) { - return null; - } - - @Override - public ArgumentFilter<Object, Boolean> visit(AndFilter filter) { - ArgumentFilter<Object, Boolean> leftFilter = filter.getLeft().accept(this); - ArgumentFilter<Object, Boolean> rightFilter = filter.getRight().accept(this); - // TODO: create and filter... - return null; - } - - @Override - public ArgumentFilter<Object, Boolean> visit(OrFilter filter) { - ArgumentFilter<Object, Boolean> leftFilter = filter.getLeft().accept(this); - ArgumentFilter<Object, Boolean> rightFilter = filter.getRight().accept(this); - // TODO: create or filter... - return null; - } - - @Override - public ArgumentFilter<Object, Boolean> visit(NotFilter filter) { - ArgumentFilter<Object, Boolean> toNegate = filter.accept(this); - // TODO: create not filter - return null; - } - } - - private static final class MapperNodeFactory implements MapperVisitor<MapNode> { - - @Override - public MapNode visit(MapToValue mapper) { - final Object value = mapper.getValue(); - return MapNode.create(x -> value); - } - - @Override - public MapNode visit(MapByteToBoolean mapper) { - return null; - } - - @Override - public MapNode visit(MapDoubleToInt mapper) { - return null; - } - - @Override - public MapNode visit(MapToCharAt mapper) { - return null; - } - } -} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java index 7927c4fc2ddf825bb6a29ce2e22bd80afdf1f112..d9e0d318cb89692aecca0085e1433e01ae682496 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Filter.java @@ -22,8 +22,8 @@ */ package com.oracle.truffle.r.nodes.builtin.casts; -import static com.oracle.truffle.r.nodes.builtin.casts.CastStep.FilterStep; -import static com.oracle.truffle.r.nodes.builtin.casts.CastStep.MapStep; +import static com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.FilterStep; +import static com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.MapStep; import com.oracle.truffle.r.nodes.builtin.ArgumentFilter; import com.oracle.truffle.r.runtime.RType; @@ -33,8 +33,18 @@ import com.oracle.truffle.r.runtime.RType; */ public abstract class Filter { + private final MessageData message; + + protected Filter(MessageData message) { + this.message = message; + } + public abstract <T> T accept(FilterVisitor<T> visitor); + public final MessageData getMessage() { + return message; + } + public interface FilterVisitor<T> { T visit(TypeFilter filter); @@ -58,7 +68,8 @@ public abstract class Filter { private final Class<?> type; private final ArgumentFilter<Object, Boolean> instanceOfLambda; - public TypeFilter(Class<?> type, ArgumentFilter<Object, Boolean> instanceOfLambda) { + public TypeFilter(Class<?> type, ArgumentFilter<Object, Boolean> instanceOfLambda, MessageData message) { + super(message); this.type = type; this.instanceOfLambda = instanceOfLambda; } @@ -87,7 +98,8 @@ public abstract class Filter { public static final class RTypeFilter extends Filter { private final RType type; - public RTypeFilter(RType type) { + public RTypeFilter(RType type, MessageData message) { + super(message); assert type.isVector() && type != RType.List : "RTypeFilter supports only vector types minus list."; this.type = type; } @@ -103,6 +115,10 @@ public abstract class Filter { } public static final class NumericFilter extends Filter { + public NumericFilter(MessageData message) { + super(message); + } + @Override public <T> T accept(FilterVisitor<T> visitor) { return visitor.visit(this); @@ -123,7 +139,8 @@ public abstract class Filter { private final byte operation; private final Object value; - public CompareFilter(byte operation, Object value) { + public CompareFilter(byte operation, Object value, MessageData message) { + super(message); assert operation <= LE : "wrong operation value"; this.operation = operation; this.value = value; @@ -147,7 +164,8 @@ public abstract class Filter { private final Filter left; private final Filter right; - public AndFilter(Filter left, Filter right) { + public AndFilter(Filter left, Filter right, MessageData message) { + super(message); this.left = left; this.right = right; } @@ -170,7 +188,8 @@ public abstract class Filter { private final Filter left; private final Filter right; - public OrFilter(Filter left, Filter right) { + public OrFilter(Filter left, Filter right, MessageData message) { + super(message); this.left = left; this.right = right; } @@ -192,7 +211,8 @@ public abstract class Filter { public static final class NotFilter extends Filter { private final Filter filter; - public NotFilter(Filter filter) { + public NotFilter(Filter filter, MessageData message) { + super(message); this.filter = filter; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Mapper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Mapper.java index 12c032131809b4a59e912bfc8db96e155ed83ad0..46549868ead032a174b349aa4e55f33e94d2fac4 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Mapper.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/Mapper.java @@ -22,7 +22,7 @@ */ package com.oracle.truffle.r.nodes.builtin.casts; -import com.oracle.truffle.r.nodes.builtin.casts.CastStep.MapStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.MapStep; /** * Represents mapping used in {@link MapStep}. @@ -74,15 +74,21 @@ public abstract class Mapper { public static final class MapToCharAt extends Mapper { private final int index; + private final char defaultValue; - public MapToCharAt(int index) { + public MapToCharAt(int index, char defaultValue) { this.index = index; + this.defaultValue = defaultValue; } public int getIndex() { return index; } + public char getDefaultValue() { + return defaultValue; + } + @Override public <T> T accept(MapperVisitor<T> visitor) { return visitor.visit(this); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/MessageData.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/MessageData.java new file mode 100644 index 0000000000000000000000000000000000000000..334a5daa8fd3cd71e33dcecd7b88cc812e48ba41 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/MessageData.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.casts; + +import com.oracle.truffle.api.CompilerDirectives.ValueType; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.nodes.RBaseNode; + +/** + * Value type that holds data necessary for error/warning message from a cast pipeline. + */ +@ValueType +public final class MessageData { + private final RBaseNode callObj; + private final RError.Message message; + private final Object[] messageArgs; + + public MessageData(RBaseNode callObj, Message message, Object[] messageArgs) { + this.callObj = callObj; + this.message = message; + this.messageArgs = messageArgs; + } + + public RBaseNode getCallObj() { + return callObj; + } + + public Message getMessage() { + return message; + } + + public Object[] getMessageArgs() { + return messageArgs; + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java new file mode 100644 index 0000000000000000000000000000000000000000..3a756479054aeb25d9e4ccd6baf71b63a6b8df40 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.casts; + +import com.oracle.truffle.r.runtime.RType; + +/** + * Represents a single step in the cast pipeline. {@code PipelineStep}, {@code Mapper} and + * {@code Filter} are only symbolic representation of the pipeline, these objects can be transformed + * to something useful by using corresponding visitors, e.g. {@linek PipelineStepVisitor}. Steps can + * be chained as a linked list by setting the next step in the chain using + * {@link #setNext(PipelineStep)}. The order of steps should be the same as the order of cast + * pipeline API invocations. + */ +public abstract class PipelineStep { + + private PipelineStep next; + + public final PipelineStep getNext() { + return next; + } + + public final PipelineStep setNext(PipelineStep next) { + this.next = next; + return this; + } + + public abstract <T> T accept(PipelineStepVisitor<T> visitor); + + public interface PipelineStepVisitor<T> { + T visit(FindFirstStep step); + + T visit(AsVectorStep step); + + T visit(MapStep step); + + T visit(MapIfStep step); + + T visit(FilterStep step); + + T visit(NotNAStep step); + + T visit(DefaultErrorStep step); + } + + /** + * Changes the current default error, which is used by steps/filters that do not have error + * message set explicitly. + */ + public static final class DefaultErrorStep extends PipelineStep { + private final MessageData defaultMessage; + + public DefaultErrorStep(MessageData defaultMessage) { + this.defaultMessage = defaultMessage; + } + + public MessageData getDefaultMessage() { + return defaultMessage; + } + + @Override + public <T> T accept(PipelineStepVisitor<T> visitor) { + return visitor.visit(this); + } + } + + /** + * If the replacement is set (!= null), then maps NA values to the replacement, otherwise raises + * given error on NA value of any type. + */ + public static final class NotNAStep extends PipelineStep { + private final MessageData message; + private final Object replacement; + + public NotNAStep(Object replacement) { + this.message = null; + this.replacement = replacement; + } + + public NotNAStep(MessageData message) { + this.message = message; + this.replacement = null; + } + + public MessageData getMessage() { + return message; + } + + public Object getReplacement() { + return replacement; + } + + @Override + public <T> T accept(PipelineStepVisitor<T> visitor) { + return visitor.visit(this); + } + } + + /** + * Takes the first element of a vector. If the vector is empty, null or missing, then either + * raises an error or returns default value if set. + */ + public static final class FindFirstStep extends PipelineStep { + private final MessageData error; + private final Object defaultValue; + private final Class<?> elementClass; + + public FindFirstStep(Object defaultValue, Class<?> elementClass, MessageData error) { + this.defaultValue = defaultValue; + this.elementClass = elementClass; + this.error = error; + } + + public Object getDefaultValue() { + return defaultValue; + } + + public Class<?> getElementClass() { + return elementClass; + } + + public MessageData getError() { + return error; + } + + @Override + public <T> T accept(PipelineStepVisitor<T> visitor) { + return visitor.visit(this); + } + } + + /** + * Converts the value to a vector of given {@link RType}. Null and missing values are forwarded. + */ + public static final class AsVectorStep extends PipelineStep { + private final RType type; + + public AsVectorStep(RType type) { + assert type.isVector() && type != RType.List : "AsVectorStep supports only vector types minus list."; + this.type = type; + } + + public RType getType() { + return type; + } + + @Override + public <T> T accept(PipelineStepVisitor<T> visitor) { + return visitor.visit(this); + } + } + + public static final class MapStep extends PipelineStep { + private final Mapper mapper; + + public MapStep(Mapper mapper) { + this.mapper = mapper; + } + + public Mapper getMapper() { + return mapper; + } + + @Override + public <T> T accept(PipelineStepVisitor<T> visitor) { + return visitor.visit(this); + } + } + + /** + * Allows to execute on of given pipeline chains depending on the condition. + */ + public static final class MapIfStep extends PipelineStep { + private final Filter filter; + private final PipelineStep trueBranch; + private final PipelineStep falseBranch; + + public MapIfStep(Filter filter, PipelineStep trueBranch, PipelineStep falseBranch) { + this.filter = filter; + this.trueBranch = trueBranch; + this.falseBranch = falseBranch; + } + + public Filter getFilter() { + return filter; + } + + public PipelineStep getTrueBranch() { + return trueBranch; + } + + public PipelineStep getFalseBranch() { + return falseBranch; + } + + @Override + public <T> T accept(PipelineStepVisitor<T> visitor) { + return visitor.visit(this); + } + } + + /** + * Raises an error if the value does not conform to the given filter. + */ + public static final class FilterStep extends PipelineStep { + private final Filter filter; + + public FilterStep(Filter filter) { + this.filter = filter; + } + + public Filter getFilter() { + return filter; + } + + @Override + public <T> T accept(PipelineStepVisitor<T> visitor) { + return visitor.visit(this); + } + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java new file mode 100644 index 0000000000000000000000000000000000000000..153686566e40d1a2fe177d98b8cea22b7bab553c --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.casts; + +import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.r.nodes.builtin.ArgumentFilter; +import com.oracle.truffle.r.nodes.builtin.CastBuilder.PipelineConfigBuilder; +import com.oracle.truffle.r.nodes.builtin.ValuePredicateArgumentMapper; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.AndFilter; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.CompareFilter; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.FilterVisitor; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.NotFilter; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.NumericFilter; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.OrFilter; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.RTypeFilter; +import com.oracle.truffle.r.nodes.builtin.casts.Filter.TypeFilter; +import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapByteToBoolean; +import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapDoubleToInt; +import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapToCharAt; +import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapToValue; +import com.oracle.truffle.r.nodes.builtin.casts.Mapper.MapperVisitor; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.AsVectorStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.DefaultErrorStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.FilterStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.FindFirstStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.MapIfStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.MapStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.NotNAStep; +import com.oracle.truffle.r.nodes.builtin.casts.PipelineStep.PipelineStepVisitor; +import com.oracle.truffle.r.nodes.unary.BypassNode; +import com.oracle.truffle.r.nodes.unary.CastComplexNodeGen; +import com.oracle.truffle.r.nodes.unary.CastDoubleNodeGen; +import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen; +import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen; +import com.oracle.truffle.r.nodes.unary.CastNode; +import com.oracle.truffle.r.nodes.unary.CastRawNodeGen; +import com.oracle.truffle.r.nodes.unary.CastStringNodeGen; +import com.oracle.truffle.r.nodes.unary.ChainedCastNode; +import com.oracle.truffle.r.nodes.unary.ConditionalMapNode; +import com.oracle.truffle.r.nodes.unary.FilterNode; +import com.oracle.truffle.r.nodes.unary.FindFirstNodeGen; +import com.oracle.truffle.r.nodes.unary.MapNode; +import com.oracle.truffle.r.nodes.unary.NonNANodeGen; +import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.RType; +import com.oracle.truffle.r.runtime.data.RDoubleVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; +import com.oracle.truffle.r.runtime.ops.na.NACheck; + +/** + * Converts given pipeline into corresponding cast nodes chain. + */ +public final class PipelineToCastNode { + + public static CastNode convert(PipelineConfigBuilder configBuilder, PipelineStep lastStep) { + // TODO: where to get the caller node? argument to this method? and default error? + CastNodeFactory nodeFactory = new CastNodeFactory(new MessageData(null, null, null), true); + CastNode headNode = convert(lastStep, nodeFactory); + return BypassNode.create(configBuilder, headNode); + } + + /** + * Converts chain of pipeline steps to cast nodes. The steps must not contain + * {@code PipelineConfStep} anymore. This method is also invoked when we build mapIf node to + * convert {@code trueBranch} and {@code falseBranch}. + */ + private static CastNode convert(PipelineStep firstStep, CastNodeFactory nodeFactory) { + if (firstStep == null) { + return null; + } + + CastNode prevCastNode = null; + PipelineStep currCastStep = firstStep; + while (currCastStep != null) { + CastNode node = nodeFactory.create(currCastStep); + if (node != null) { + if (prevCastNode == null) { + prevCastNode = node; + } else { + CastNode finalPrevCastNode = prevCastNode; + prevCastNode = new ChainedCastNode(() -> finalPrevCastNode, () -> node); + } + } + + currCastStep = currCastStep.getNext(); + } + return prevCastNode; + } + + private static final class CastNodeFactory implements PipelineStepVisitor<CastNode> { + private MessageData defaultMessage; + private final boolean boxPrimitives; + + public CastNodeFactory(MessageData defaultMessage, boolean boxPrimitives) { + this.defaultMessage = defaultMessage; + this.boxPrimitives = boxPrimitives; + } + + public CastNode create(PipelineStep step) { + return step.accept(this); + } + + @Override + public CastNode visit(DefaultErrorStep step) { + defaultMessage = step.getDefaultMessage(); + return null; + } + + @Override + public CastNode visit(FindFirstStep step) { + return FindFirstNodeGen.create(step.getElementClass(), step.getDefaultValue()); + } + + @Override + public CastNode visit(FilterStep step) { + ArgumentFilter<Object, Boolean> filter = ArgumentFilterFactory.create(step.getFilter()); + MessageData msg = getDefaultIfNull(step.getFilter().getMessage()); + return FilterNode.create(filter, /* TODO: isWarning?? */false, msg.getCallObj(), msg.getMessage(), msg.getMessageArgs(), boxPrimitives); + } + + @Override + public CastNode visit(NotNAStep step) { + Object replacement = step.getReplacement(); + if (replacement != null) { + return NonNANodeGen.create(replacement); + } else { + MessageData message = step.getMessage(); + return NonNANodeGen.create(message.getCallObj(), message.getMessage(), message.getMessageArgs(), null); + } + } + + @Override + public CastNode visit(AsVectorStep step) { + RType type = step.getType(); + if (type == RType.Integer) { + return CastIntegerNodeGen.create(false, false, false); + } else if (type == RType.Double) { + return CastDoubleNodeGen.create(false, false, false); + } else if (type == RType.Character) { + return CastStringNodeGen.create(false, false, false); + } else if (type == RType.Complex) { + return CastComplexNodeGen.create(false, false, false); + } else if (type == RType.Logical) { + return CastLogicalNodeGen.create(false, false, false); + } else if (type == RType.Raw) { + return CastRawNodeGen.create(false, false, false); + } + throw RInternalError.shouldNotReachHere(String.format("Unexpected type '%s' in AsVectorStep.", type.getName())); + } + + @Override + public CastNode visit(MapStep step) { + return MapNode.create(MapperNodeFactory.create(step.getMapper())); + } + + @Override + public CastNode visit(MapIfStep step) { + ArgumentFilter<Object, Boolean> condition = ArgumentFilterFactory.create(step.getFilter()); + CastNode trueCastNode = PipelineToCastNode.convert(step.getTrueBranch(), this); + CastNode falseCastNode = PipelineToCastNode.convert(step.getFalseBranch(), this); + return ConditionalMapNode.create(condition, trueCastNode, falseCastNode); + } + + private MessageData getDefaultIfNull(MessageData message) { + return message == null ? defaultMessage : message; + } + } + + private static final class ArgumentFilterFactory implements FilterVisitor<ArgumentFilter<Object, Boolean>> { + + private static final ArgumentFilterFactory INSTANCE = new ArgumentFilterFactory(); + + private ArgumentFilterFactory() { + // singleton + } + + public static ArgumentFilter<Object, Boolean> create(Filter filter) { + return filter.accept(INSTANCE); + } + + @Override + public ArgumentFilter<Object, Boolean> visit(TypeFilter filter) { + return filter.getInstanceOfLambda(); + } + + @Override + public ArgumentFilter<Object, Boolean> visit(RTypeFilter filter) { + if (filter.getType() == RType.Integer) { + return x -> x instanceof Integer || x instanceof RAbstractIntVector; + } else if (filter.getType() == RType.Double) { + return x -> x instanceof Double || x instanceof RDoubleVector; + } else { + throw RInternalError.unimplemented("TODO: more types here"); + } + } + + @Override + public ArgumentFilter<Object, Boolean> visit(NumericFilter filter) { + return x -> x instanceof Integer || x instanceof RAbstractIntVector || x instanceof Double || x instanceof RAbstractDoubleVector || x instanceof Byte || + x instanceof RAbstractLogicalVector; + } + + @Override + public ArgumentFilter<Object, Boolean> visit(CompareFilter filter) { + return null; + } + + @Override + public ArgumentFilter<Object, Boolean> visit(AndFilter filter) { + ArgumentFilter<Object, Boolean> leftFilter = filter.getLeft().accept(this); + ArgumentFilter<Object, Boolean> rightFilter = filter.getRight().accept(this); + // TODO: create and filter... + return null; + } + + @Override + public ArgumentFilter<Object, Boolean> visit(OrFilter filter) { + ArgumentFilter<Object, Boolean> leftFilter = filter.getLeft().accept(this); + ArgumentFilter<Object, Boolean> rightFilter = filter.getRight().accept(this); + // TODO: create or filter + return null; + } + + @Override + public ArgumentFilter<Object, Boolean> visit(NotFilter filter) { + ArgumentFilter<Object, Boolean> toNegate = filter.accept(this); + // TODO: create not filter + return null; + } + } + + private static final class MapperNodeFactory implements MapperVisitor<ValuePredicateArgumentMapper<Object, Object>> { + private static final MapperNodeFactory INSTANCE = new MapperNodeFactory(); + + private MapperNodeFactory() { + // singleton + } + + public static ValuePredicateArgumentMapper<Object, Object> create(Mapper mapper) { + return mapper.accept(MapperNodeFactory.INSTANCE); + } + + @Override + public ValuePredicateArgumentMapper<Object, Object> visit(MapToValue mapper) { + final Object value = mapper.getValue(); + return ValuePredicateArgumentMapper.fromLambda(x -> value); + } + + @Override + public ValuePredicateArgumentMapper<Object, Object> visit(MapByteToBoolean mapper) { + return ValuePredicateArgumentMapper.fromLambda(x -> RRuntime.fromLogical((Byte) x)); + } + + @Override + public ValuePredicateArgumentMapper<Object, Object> visit(MapDoubleToInt mapper) { + final NACheck naCheck = NACheck.create(); + return ValuePredicateArgumentMapper.fromLambda(x -> { + double d = (Double) x; + naCheck.enable(d); + return naCheck.convertDoubleToInt(d); + }); + } + + @Override + public ValuePredicateArgumentMapper<Object, Object> visit(MapToCharAt mapper) { + final ConditionProfile profile = ConditionProfile.createBinaryProfile(); + final ConditionProfile profile2 = ConditionProfile.createBinaryProfile(); + final char defaultValue = mapper.getDefaultValue(); + final int index = mapper.getIndex(); + return ValuePredicateArgumentMapper.fromLambda(x -> { + String str = (String) x; + if (profile.profile(x == null || str.isEmpty())) { + return defaultValue; + } else { + if (profile2.profile(x == RRuntime.STRING_NA)) { + return RRuntime.INT_NA; + } else { + return (int) str.charAt(index); + } + } + }); + } + } +}