From 83ee6291e21ee13dd778719822e9d6b867be076b Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Fri, 9 Sep 2016 19:09:18 +0200
Subject: [PATCH] Intermediate representation of the cast pipeline

There are simple classes that represent steps in the cast pipeline,
those can be converted to nodes or any other useful representation
by provided visitors.
---
 .../r/nodes/test/PipelineToCastNodeTests.java |  93 ++++++
 .../truffle/r/nodes/builtin/CastBuilder.java  |   4 +-
 .../r/nodes/builtin/casts/CastStep.java       | 158 ---------
 .../builtin/casts/CastStepToCastNode.java     | 233 -------------
 .../truffle/r/nodes/builtin/casts/Filter.java |  36 +-
 .../truffle/r/nodes/builtin/casts/Mapper.java |  10 +-
 .../r/nodes/builtin/casts/MessageData.java    |  56 ++++
 .../r/nodes/builtin/casts/PipelineStep.java   | 241 ++++++++++++++
 .../builtin/casts/PipelineToCastNode.java     | 307 ++++++++++++++++++
 9 files changed, 735 insertions(+), 403 deletions(-)
 create mode 100644 com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/PipelineToCastNodeTests.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStep.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastStepToCastNode.java
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/MessageData.java
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineToCastNode.java

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 0000000000..9692ffc70d
--- /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 0f7c93ede7..18262b4a33 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 12b394fbc5..0000000000
--- 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 4077e66bef..0000000000
--- 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 7927c4fc2d..d9e0d318cb 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 12c0321318..46549868ea 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 0000000000..334a5daa8f
--- /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 0000000000..3a75647905
--- /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 0000000000..153686566e
--- /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);
+                    }
+                }
+            });
+        }
+    }
+}
-- 
GitLab