From 85ab441c01f2586b7a4c65593d36252398fea67a Mon Sep 17 00:00:00 2001 From: stepan <stepan.sindelar@oracle.com> Date: Fri, 6 Apr 2018 16:15:31 +0200 Subject: [PATCH] Rscript parses the whole file before executing in order to be able to respond to `materializeInstrumentableNodes` --- .../truffle/r/engine/EngineRootNode.java | 172 ++++++++++++++++++ .../com/oracle/truffle/r/engine/REngine.java | 125 ++++++------- .../r/engine/RRuntimeASTAccessImpl.java | 2 +- .../truffle/r/engine/SyntaxErrorNode.java | 65 +++++++ .../r/engine/TruffleRLanguageImpl.java | 6 +- .../truffle/r/launcher/RscriptCommand.java | 30 ++- .../r/parser/DefaultRParserFactory.java | 20 ++ .../truffle/r/parser/ParserGeneration.java | 3 +- .../src/com/oracle/truffle/r/parser/R.g | 87 ++------- .../truffle/r/runtime/RParserFactory.java | 4 +- 10 files changed, 362 insertions(+), 152 deletions(-) create mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java create mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java 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 0000000000..5420becd7a --- /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 1fb2aa2354..8bfbe34c6c 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 63c73a1f26..b4ee6e0c3a 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 0000000000..10ecf7472e --- /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 494ff445ab..50633e95da 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 1142e82078..99ce2d589b 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 f92d3697ba..d05bcab85c 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 c54448034f..be131a7a7d 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 daab0541ae..3dbf82f7b3 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 f42227d82e..e49e36e7f8 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); -- GitLab