diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..5420becd7a23b9ca41c63fa8f75858f9aa833a65
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2018, 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.engine;
+
+import java.util.List;
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage.ContextReference;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrumentation.InstrumentableNode;
+import com.oracle.truffle.api.instrumentation.ProbeNode;
+import com.oracle.truffle.api.instrumentation.Tag;
+import com.oracle.truffle.api.nodes.DirectCallNode;
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.ExitException;
+import com.oracle.truffle.r.runtime.JumpToTopLevelException;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.ReturnException;
+import com.oracle.truffle.r.runtime.Utils.DebugExitException;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+class EngineRootNode extends RootNode {
+    private final SourceSection sourceSection;
+
+    private final MaterializedFrame executionFrame;
+    private final ContextReference<RContext> contextReference;
+
+    @Child private EngineBodyNode bodyNode;
+    @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create();
+
+    EngineRootNode(EngineBodyNode bodyNode, RContext context, SourceSection sourceSection, MaterializedFrame executionFrame) {
+        super(context.getLanguage());
+        this.sourceSection = sourceSection;
+        this.bodyNode = bodyNode;
+        this.executionFrame = executionFrame;
+        this.contextReference = context.getLanguage().getContextReference();
+    }
+
+    public static EngineRootNode createEngineRoot(REngine engine, RContext context, List<RSyntaxNode> statements, SourceSection sourceSection, MaterializedFrame executionFrame) {
+        return new EngineRootNode(new EngineBodyNode(engine, statements, getPrintResult(sourceSection)), context, sourceSection, executionFrame);
+    }
+
+    /**
+     * The normal {@link REngine#doMakeCallTarget} happens first, then we actually run the call
+     * using the standard FastR machinery, saving and restoring the {@link RContext}, since we have
+     * no control over what that might be when the call is initiated.
+     */
+    @Override
+    public Object execute(VirtualFrame frame) {
+        Object actualFrame = executionFrame != null ? executionFrame : contextReference.get().stateREnvironment.getGlobalFrame();
+        try {
+            return r2Foreign.execute(this.bodyNode.execute(actualFrame));
+        } catch (ReturnException ex) {
+            return ex.getResult();
+        } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) {
+            CompilerDirectives.transferToInterpreter();
+            throw e;
+        } catch (RError e) {
+            CompilerDirectives.transferToInterpreter();
+            throw e;
+        } catch (Throwable t) {
+            CompilerDirectives.transferToInterpreter();
+            // other errors didn't produce an output yet
+            RInternalError.reportError(t);
+            throw t;
+        }
+    }
+
+    @Override
+    public SourceSection getSourceSection() {
+        return sourceSection;
+    }
+
+    public static boolean isEngineBody(Object node) {
+        return node instanceof EngineBodyNode;
+    }
+
+    private static boolean getPrintResult(SourceSection sourceSection) {
+        // can't print if initializing the system in embedded mode (no builtins yet)
+        return !sourceSection.getSource().getName().equals(RSource.Internal.INIT_EMBEDDED.string) && sourceSection.getSource().isInteractive();
+    }
+
+    private static final class EngineBodyNode extends Node implements InstrumentableNode {
+
+        private final REngine engine;
+        private final List<RSyntaxNode> statements;
+        @Children protected final DirectCallNode[] calls;
+        private final boolean printResult;
+
+        EngineBodyNode(REngine engine, List<RSyntaxNode> statements, boolean printResult) {
+            this.engine = engine;
+            this.statements = statements;
+            this.calls = new DirectCallNode[statements.size()];
+            this.printResult = printResult;
+        }
+
+        @ExplodeLoop
+        Object execute(Object actualFrame) {
+            Object lastValue = RNull.instance;
+            int lastStatus = 0;
+            for (int i = 0; i < calls.length; i++) {
+                materializeCall(i);
+                lastValue = calls[i].call(new Object[]{actualFrame});
+            }
+            return lastValue;
+        }
+
+        @Override
+        public boolean isInstrumentable() {
+            return false;
+        }
+
+        @Override
+        public WrapperNode createWrapper(ProbeNode probe) {
+            return null;
+        }
+
+        @Override
+        public boolean hasTag(Class<? extends Tag> tag) {
+            return false;
+        }
+
+        @Override
+        public InstrumentableNode materializeInstrumentableNodes(Set<Class<? extends Tag>> materializedTags) {
+            for (int i = 0; i < calls.length; i++) {
+                materializeCall(i);
+            }
+            return this;
+        }
+
+        protected void materializeCall(int i) {
+            if (calls[i] == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                RNode node = statements.get(i).asRNode();
+                calls[i] = insert(Truffle.getRuntime().createDirectCallNode(engine.doMakeCallTarget(node, RSource.Internal.REPL_WRAPPER.string, printResult, true)));
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index 1fb2aa235484794fd54ee50ddc74b8eb1b90377c..8bfbe34c6cad6cb7fdc336d9096ea8256a675d21 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.r.engine;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -33,16 +38,13 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleLanguage.ContextReference;
 import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
 import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.ExecutableNode;
-import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.profiles.ValueProfile;
@@ -99,7 +101,6 @@ import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.interop.R2Foreign;
-import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
@@ -256,7 +257,7 @@ final class REngine implements Engine, Engine.Timings {
 
     @Override
     public Object parseAndEval(Source source, MaterializedFrame frame, boolean printResult) throws ParseException {
-        List<RSyntaxNode> list = parseImpl(source);
+        List<RSyntaxNode> list = parseSource(source);
         try {
             Object lastValue = RNull.instance;
             for (RSyntaxNode node : list) {
@@ -284,21 +285,23 @@ final class REngine implements Engine, Engine.Timings {
         }
     }
 
-    private List<RSyntaxNode> parseImpl(Source source) throws ParseException {
+    List<RSyntaxNode> parseSource(Source source) throws ParseException {
         RParserFactory.Parser<RSyntaxNode> parser = RParserFactory.getParser();
         return parser.script(source, new RASTBuilder(), context.getLanguage());
     }
 
     @Override
     public RExpression parse(Source source) throws ParseException {
-        List<RSyntaxNode> list = parseImpl(source);
+        List<RSyntaxNode> list = parseSource(source);
         Object[] data = list.stream().map(node -> RASTUtils.createLanguageElement(node)).toArray();
         return RDataFactory.createExpression(data);
     }
 
     @Override
     public CallTarget parseToCallTarget(Source source, MaterializedFrame executionFrame) throws ParseException {
-        if (source == Engine.GET_CONTEXT) {
+        if (source.getPath() != null && !source.isInteractive()) {
+            return Truffle.getRuntime().createCallTarget(createRScriptRoot(source, executionFrame));
+        } else if (source == Engine.GET_CONTEXT) {
             /*
              * The "get context" operations should be executed with as little influence on the
              * actual engine as possible, therefore this special case takes care of it explicitly.
@@ -315,14 +318,15 @@ final class REngine implements Engine, Engine.Timings {
                 }
             });
         } else {
-            List<RSyntaxNode> statements = parseImpl(source);
-            return Truffle.getRuntime().createCallTarget(new PolyglotEngineRootNode(statements, createSourceSection(source, statements), executionFrame));
+            List<RSyntaxNode> statements = parseSource(source);
+            EngineRootNode rootNode = EngineRootNode.createEngineRoot(this, context, statements, createSourceSection(source, statements), executionFrame);
+            return Truffle.getRuntime().createCallTarget(rootNode);
         }
     }
 
     @Override
     public ExecutableNode parseToExecutableNode(Source source) throws ParseException {
-        List<RSyntaxNode> list = parseImpl(source);
+        List<RSyntaxNode> list = parseSource(source);
         RNode[] statements = new RNode[list.size()];
         for (int i = 0; i < statements.length; i++) {
             statements[i] = list.get(i).asRNode();
@@ -355,68 +359,49 @@ final class REngine implements Engine, Engine.Timings {
         }
     }
 
-    private final class PolyglotEngineRootNode extends RootNode {
-
-        private final List<RSyntaxNode> statements;
-        private final MaterializedFrame executionFrame;
-        @Children private final DirectCallNode[] calls;
-        private final boolean printResult;
-        private final SourceSection sourceSection;
-        private final ContextReference<RContext> contextReference;
-
-        @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create();
-
-        PolyglotEngineRootNode(List<RSyntaxNode> statements, SourceSection sourceSection, MaterializedFrame executionFrame) {
-            super(context.getLanguage());
-            this.sourceSection = sourceSection;
-            this.contextReference = context.getLanguage().getContextReference();
-            // can't print if initializing the system in embedded mode (no builtins yet)
-            this.printResult = !sourceSection.getSource().getName().equals(RSource.Internal.INIT_EMBEDDED.string) && sourceSection.getSource().isInteractive();
-            this.statements = statements;
-            this.executionFrame = executionFrame;
-            this.calls = new DirectCallNode[statements.size()];
-        }
-
-        @Override
-        public SourceSection getSourceSection() {
-            return sourceSection;
-        }
-
-        /**
-         * The normal {@link #doMakeCallTarget} happens first, then we actually run the call using
-         * the standard FastR machinery, saving and restoring the {@link RContext}, since we have no
-         * control over what that might be when the call is initiated.
-         */
-        @Override
-        @ExplodeLoop
-        public Object execute(VirtualFrame frame) {
-            Object actualFrame = executionFrame != null ? executionFrame : contextReference.get().stateREnvironment.getGlobalFrame();
-            try {
-                Object lastValue = RNull.instance;
-                for (int i = 0; i < calls.length; i++) {
-                    if (calls[i] == null) {
-                        CompilerDirectives.transferToInterpreterAndInvalidate();
-                        RSyntaxNode node = statements.get(i);
-                        calls[i] = insert(Truffle.getRuntime().createDirectCallNode(doMakeCallTarget(node.asRNode(), RSource.Internal.REPL_WRAPPER.string, printResult, true)));
+    private EngineRootNode createRScriptRoot(Source fullSource, MaterializedFrame frame) {
+        URI uri = fullSource.getURI();
+        String file = fullSource.getPath();
+        ArrayList<RSyntaxNode> statements = new ArrayList<>(128);
+        try {
+            try (BufferedReader br = new BufferedReader(new InputStreamReader(fullSource.getInputStream()))) {
+                int lineIndex = 1;
+                int startLine = lineIndex;
+                StringBuilder sb = new StringBuilder();
+                while (true) {
+                    String input = br.readLine();
+                    if (input == null) {
+                        if (sb.length() != 0) {
+                            // end of file, but not end of statement => error
+                            statements.add(new SyntaxErrorNode(null, fullSource.createSection(startLine, 1, sb.length())));
+                        }
+                        break;
+                    }
+                    sb.append(input);
+                    Source src = Source.newBuilder(sb.toString()).mimeType(RRuntime.R_APP_MIME).name(file + "#" + startLine + "-" + lineIndex).uri(uri).build();
+                    lineIndex++;
+                    List<RSyntaxNode> currentStmts = null;
+                    try {
+                        RParserFactory.Parser<RSyntaxNode> parser = RParserFactory.getParser();
+                        currentStmts = parser.statements(src, fullSource, startLine, new RASTBuilder(), context.getLanguage());
+                    } catch (IncompleteSourceException e) {
+                        sb.append('\n');
+                        continue;
+                    } catch (ParseException e) {
+                        statements.add(new SyntaxErrorNode(e, fullSource.createSection(startLine, 1, sb.length())));
                     }
-                    lastValue = calls[i].call(new Object[]{actualFrame});
+                    if (currentStmts != null) {
+                        statements.addAll(currentStmts);
+                    }
+                    // we did not continue on incomplete source exception
+                    sb.setLength(0);
+                    startLine = lineIndex;
                 }
-                return r2Foreign.execute(lastValue);
-            } catch (ReturnException ex) {
-                return ex.getResult();
-            } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) {
-                CompilerDirectives.transferToInterpreter();
-                throw e;
-            } catch (RError e) {
-                CompilerDirectives.transferToInterpreter();
-                throw e;
-            } catch (Throwable t) {
-                CompilerDirectives.transferToInterpreter();
-                // other errors didn't produce an output yet
-                RInternalError.reportError(t);
-                throw t;
             }
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
         }
+        return EngineRootNode.createEngineRoot(this, context, statements, createSourceSection(fullSource, statements), frame);
     }
 
     @Override
@@ -510,7 +495,7 @@ final class REngine implements Engine, Engine.Timings {
      * that the body should be executed in.
      */
     @TruffleBoundary
-    private RootCallTarget doMakeCallTarget(RNode body, String description, boolean printResult, boolean topLevel) {
+    RootCallTarget doMakeCallTarget(RNode body, String description, boolean printResult, boolean topLevel) {
         return Truffle.getRuntime().createCallTarget(new AnonymousRootNode(this, body, description, printResult, topLevel));
     }
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 63c73a1f26c1308818b52569adfca9fdadeb1e82..b4ee6e0c3a8e1ba383f49afd4a08604807b57992 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -308,7 +308,7 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
                 // single statement block: as function body, if/else body, loop body
                 // note: RepeatingNode is not a RSyntaxElement but the body of a loop is
                 // under the repeating node !
-                return parent instanceof RootBodyNode || parent instanceof IfNode || AbstractLoopNode.isLoopBody(node);
+                return parent instanceof RootBodyNode || parent instanceof IfNode || AbstractLoopNode.isLoopBody(node) || EngineRootNode.isEngineBody(parent);
             }
         }
         // TODO: ExpressionTag: (!statement && !loop && !if && !call && !root)??
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..10ecf7472e6ff21daa88ceba29822e7140178c0b
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018, 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.engine;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.nodes.RInstrumentableNode;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+/**
+ * Node that represents piece of AST that would throw syntax error when parser. Used to delay
+ * parsing error to the point when they should be reported.
+ */
+final class SyntaxErrorNode extends RNode implements RSyntaxNode, RInstrumentableNode {
+    private final ParseException exception;
+    private final SourceSection sourceSection;
+
+    SyntaxErrorNode(ParseException exception, SourceSection sourceSection) {
+        this.exception = exception;
+        this.sourceSection = sourceSection;
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        throw exception.throwAsRError();
+    }
+
+    @Override
+    public SourceSection getSourceSection() {
+        return this.sourceSection;
+    }
+
+    @Override
+    public SourceSection getLazySourceSection() {
+        return this.sourceSection;
+    }
+
+    @Override
+    public void setSourceSection(SourceSection source) {
+        throw RInternalError.shouldNotReachHere(String.format("%s should not be serialized/deserialzed.", getClass().getSimpleName()));
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
index 494ff445ab77eaa87417161bce5cccf0f99b4100..50633e95daf86e2f7f48ce313b85859912660432 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.api.instrumentation.ProvidedTags;
 import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.nodes.ExecutableNode;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.engine.interop.RForeignAccessFactoryImpl;
 import com.oracle.truffle.r.nodes.RASTBuilder;
@@ -189,12 +190,13 @@ public final class TruffleRLanguageImpl extends TruffleRLanguage {
     @Override
     protected CallTarget parse(ParsingRequest request) throws Exception {
         CompilerAsserts.neverPartOfCompilation();
+        Source source = request.getSource();
         try {
-            return RContext.getEngine().parseToCallTarget(request.getSource(), null);
+            return RContext.getEngine().parseToCallTarget(source, null);
         } catch (IncompleteSourceException e) {
             throw e;
         } catch (ParseException e) {
-            if (request.getSource().isInteractive()) {
+            if (source.isInteractive()) {
                 throw e.throwAsRError();
             } else {
                 throw e;
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
index 1142e820780741f27816f2bfb1d0c9fa69a62769..99ce2d589b25af2bc8de33625b1ec6e7fe876249 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.launcher;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
@@ -30,6 +31,9 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import org.graalvm.polyglot.PolyglotException;
+import org.graalvm.polyglot.Source;
+
 import com.oracle.truffle.r.launcher.RCmdOptions.Client;
 import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
 
@@ -67,12 +71,36 @@ public final class RscriptCommand extends RAbstractLauncher {
         launch(args);
         if (context != null) {
             String fileOption = options.getString(RCmdOption.FILE);
-            return RCommand.readEvalPrint(context, consoleHandler, fileOption != null ? new File(fileOption) : null);
+            if (fileOption != null) {
+                return executeFile(fileOption);
+            } else {
+                return RCommand.readEvalPrint(context, consoleHandler, null);
+            }
         } else {
             return 0;
         }
     }
 
+    private int executeFile(String fileOption) {
+        Source src;
+        try {
+            src = Source.newBuilder("R", new File(fileOption)).interactive(false).build();
+        } catch (IOException ex) {
+            System.err.printf("IO error while reading the source file '%s'.\nDetails: '%s'.", fileOption, ex.getLocalizedMessage());
+            return 1;
+        }
+        try {
+            context.eval(src);
+            return 0;
+        } catch (Throwable ex) {
+            if (ex instanceof PolyglotException && ((PolyglotException) ex).isExit()) {
+                return ((PolyglotException) ex).getExitStatus();
+            }
+            // Internal exceptions are reported by the engine already
+            return 1;
+        }
+    }
+
     // CheckStyle: stop system..print check
 
     private static String[] preprocessRScriptOptions(RCmdOptions options) throws PrintHelp {
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java
index f92d3697ba5833dfbe6f37da88c4e8f578bb0a56..d05bcab85c9f53cf7b617f2441c72612acdf43b5 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java
@@ -62,6 +62,26 @@ public class DefaultRParserFactory extends RParserFactory {
             }
         }
 
+        @Override
+        public List<T> statements(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException {
+            try {
+                try {
+                    RContext context = language.getContextReference().get();
+                    RParser<T> parser = new RParser<>(source, fullSource, startLine, builder, language, context.sourceCache);
+                    return parser.script();
+                } catch (IllegalArgumentException e) {
+                    // the lexer will wrap exceptions in IllegalArgumentExceptions
+                    if (e.getCause() instanceof RecognitionException) {
+                        throw (RecognitionException) e.getCause();
+                    } else {
+                        throw e;
+                    }
+                }
+            } catch (RecognitionException e) {
+                throw handleRecognitionException(source, e);
+            }
+        }
+
         @Override
         public RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException {
             RContext context = language.getContextReference().get();
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
index c54448034f70222006046e7952cb8762e17034b6..be131a7a7da9e1fd430fb2d8b5abf352fb2f2f89 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
@@ -98,6 +98,7 @@ public class ParserGeneration {
         "allow everything but newlines in %<ident>% operators",
         "allow strings in :: and :::",
         "use file for interactive single-line source",
-        ":: and ::: do not set argument names"
+        ":: and ::: do not set argument names",
+        "refactored: use file for interactive single-line source"
     };
 }
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
index daab0541ae8da9e5eb327ad196c7950d7165a873..3dbf82f7b36a8bf3370f549c291ca6d8004327b8 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
@@ -75,90 +75,25 @@ import com.oracle.truffle.r.runtime.RError;
     private TruffleRLanguage language;
     private int fileStartOffset = 0;
     private Map<String, Source> sourceCache;
-    
-    /**
-     * Always use this constructor to initialize the R specific fields.
-     */
+
     public RParser(Source source, RCodeBuilder<T> builder, TruffleRLanguage language, Map<String, Source> sourceCache) {
         super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString()))));
         assert source != null && builder != null;
         this.initialSource = source;
         this.builder = builder;
         this.language = language;
-        this.sourceCache = sourceCache;
-        if (source.getURI() != null && source.getName().contains("#")) {
-        	this.source = createFullSource(source);
-        } else {
-        	this.source = source;
-        }
+        this.source = source;
     }
-    
-    private Source createFullSource(Source original) {
-	    String originalName = original.getName();
 
-	    // check if source name is like 'path/to/source.R#45-54'
-	    int hash_idx = originalName.lastIndexOf("#");
-	    if (hash_idx == -1) {
-	        return original;
-	    }
-
-	    String fileName = originalName.substring(0, hash_idx);
-	    String lineRange = originalName.substring(hash_idx + 1);
-
-	    try {
-	        // check for line range, e.g. '45-54'
-	        int startLine = -1;
-	        int endLine = -1;
-	        int dashIdx = lineRange.indexOf('-');
-	        if (dashIdx != -1) {
-	            startLine = Integer.parseInt(lineRange.substring(0, dashIdx));
-	            endLine = Integer.parseInt(lineRange.substring(dashIdx + 1));
-	        } else {
-	            startLine = Integer.parseInt(lineRange);
-	            endLine = startLine;
-	        }
-	        File f = new File(fileName);
-	        Source fullSource;
-	        String canonicalName;
-	        try {
-	            canonicalName = f.getAbsoluteFile().getCanonicalPath();
-	            fullSource = sourceCache != null ? sourceCache.get(canonicalName) : null;
-	        } catch(IOException e) {
-	            // ignore an freshly load file
-	            fullSource = null;
-	            canonicalName = null;
-	        }
-            if(fullSource == null) {
-    	        Builder<IOException, RuntimeException, RuntimeException> newBuilder = Source.newBuilder(f);
-    	        if (original.isInteractive()) {
-    	            newBuilder.interactive();
-    	        }
-    	        fullSource = newBuilder.build();
-    	        
-    	        if (sourceCache != null && canonicalName != null) {
-    	        	sourceCache.put(canonicalName, fullSource);
-    	        }
-            }
-
-	        // verify to avoid accidentally matching file names
-	        for (int i = 0; i < endLine - startLine + 1; i++) {
-	            if (!original.getCharacters(i + 1).equals(fullSource.getCharacters(startLine + i))) {
-	                return original;
-	            }
-	        }
-
-	        fileStartOffset = -fullSource.getLineStartOffset(startLine);
-	        return fullSource;
-	    } catch (NumberFormatException e) {
-	        // invalid line number
-	    } catch (IllegalArgumentException e) {
-            // file name is accidentally named in the expected scheme
-	    } catch (IOException e) {
-	    } catch (RuntimeException e) {
-	    	assert rethrow(e);
-	    }
-	    return original;
-	}
+    public RParser(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language, Map<String, Source> sourceCache) {
+        super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString()))));
+        assert source != null && builder != null;
+        this.initialSource = source;
+        this.builder = builder;
+        this.language = language;
+        this.source = fullSource;
+        fileStartOffset = -fullSource.getLineStartOffset(startLine);
+    }
         
     private <T extends Throwable> boolean rethrow(T e) throws T {
     	throw e;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java
index f42227d82e276a80c92f214e516232225a6cdf2b..e49e36e7f82ef7ad5af92d6ec050330bad2bbd12 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -37,6 +37,8 @@ public abstract class RParserFactory {
     public interface Parser<T> {
         List<T> script(Source source, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException;
 
+        List<T> statements(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException;
+
         RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException;
 
         boolean isRecognitionException(Throwable t);