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);
+                    }
+                }
+            });
+        }
+    }
+}