-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow DSLs deployments to load a parser from the precompiled file without waiting for evaluators to load #297
Merged
Merged
Changes from 6 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
379a40f
Sketching out how we can have an extra parameter that loads a parser …
DavyLandman 2d4abdc
Improved interface specification
DavyLandman c44ce90
Implemented initial support for preloading syntax highlighting
DavyLandman 8e11cb0
Removed 2 code smells
DavyLandman 9f8a803
Fixed bugs and renamed fields
DavyLandman fc8fe11
Improved error reporting
DavyLandman dbb25b9
Merge remote-tracking branch 'origin/main' into preloaded-grammar-for…
DavyLandman b19c284
Reuse loc parsing code
DavyLandman 15b0381
Merge branch 'main' into preloaded-grammar-for-dsls
jurgenvinju 6d45dc3
shorter implementation for parser loading that hides the implementation
jurgenvinju 1063221
organized imports
jurgenvinju c1e151b
removed dead variable
jurgenvinju a4e5765
switched parameters to parse function
jurgenvinju 7f88d50
changed Entry to Either
jurgenvinju be5f6a5
organized imports
jurgenvinju 7a13ce4
Fixed warnings and made sure not to have null pointers
DavyLandman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
254 changes: 254 additions & 0 deletions
254
rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParserOnlyContribution.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
/* | ||
* Copyright (c) 2018-2023, NWO-I CWI and Swat.engineering | ||
* All rights reserved. | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
* | ||
* 1. Redistributions of source code must retain the above copyright notice, | ||
* this list of conditions and the following disclaimer. | ||
* | ||
* 2. Redistributions in binary form must reproduce the above copyright notice, | ||
* this list of conditions and the following disclaimer in the documentation | ||
* and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
* POSSIBILITY OF SUCH DAMAGE. | ||
*/ | ||
package org.rascalmpl.vscode.lsp.parametric; | ||
|
||
import java.io.IOException; | ||
import java.util.Collections; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.CompletionException; | ||
import org.checkerframework.checker.nullness.qual.Nullable; | ||
import org.rascalmpl.exceptions.RuntimeExceptionFactory; | ||
import org.rascalmpl.interpreter.Configuration; | ||
import org.rascalmpl.interpreter.Evaluator; | ||
import org.rascalmpl.interpreter.asserts.Ambiguous; | ||
import org.rascalmpl.interpreter.staticErrors.UndeclaredNonTerminal; | ||
import org.rascalmpl.interpreter.utils.JavaBridge; | ||
import org.rascalmpl.parser.gtd.IGTD; | ||
import org.rascalmpl.parser.gtd.exception.ParseError; | ||
import org.rascalmpl.parser.gtd.exception.UndeclaredNonTerminalException; | ||
import org.rascalmpl.parser.gtd.recovery.IRecoverer; | ||
import org.rascalmpl.parser.gtd.result.out.DefaultNodeFlattener; | ||
import org.rascalmpl.parser.uptr.UPTRNodeFactory; | ||
import org.rascalmpl.parser.uptr.action.NoActionExecutor; | ||
import org.rascalmpl.uri.URIResolverRegistry; | ||
import org.rascalmpl.values.IRascalValueFactory; | ||
import org.rascalmpl.values.parsetrees.ITree; | ||
import org.rascalmpl.values.parsetrees.SymbolAdapter; | ||
import org.rascalmpl.values.parsetrees.TreeAdapter; | ||
import org.rascalmpl.vscode.lsp.parametric.model.ParametricSummaryBridge; | ||
import org.rascalmpl.vscode.lsp.terminal.ITerminalIDEServer.ParserSpecification; | ||
import org.rascalmpl.vscode.lsp.util.concurrent.InterruptibleFuture; | ||
import io.usethesource.vallang.IConstructor; | ||
import io.usethesource.vallang.IList; | ||
import io.usethesource.vallang.ISet; | ||
import io.usethesource.vallang.ISourceLocation; | ||
import io.usethesource.vallang.IValue; | ||
import io.usethesource.vallang.IValueFactory; | ||
|
||
public class ParserOnlyContribution implements ILanguageContributions { | ||
|
||
private final String name; | ||
private final String extension; | ||
private final @Nullable Exception loadingParserError; | ||
private final @Nullable Class<IGTD<IConstructor, ITree, ISourceLocation>> parserClass; | ||
private final String parserMethodName; | ||
private final boolean allowAmbiguity; | ||
|
||
public ParserOnlyContribution(String name, String extension, ParserSpecification spec) { | ||
this.name = name; | ||
this.extension = extension; | ||
Class<IGTD<IConstructor, ITree, ISourceLocation>> clzz = null; | ||
Exception err = null; | ||
try { | ||
clzz = loadParserClass(spec); | ||
} catch (ClassNotFoundException | IOException e) { | ||
err = e; | ||
} | ||
this.parserClass = clzz; | ||
this.loadingParserError = err; | ||
this.parserMethodName = (spec.getNonTerminalIsStart() ? "start__" : "") + spec.getNonTerminalName(); | ||
this.allowAmbiguity = spec.getAllowAmbiguity(); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public String getExtension() { | ||
return extension; | ||
} | ||
|
||
@Override | ||
public CompletableFuture<ITree> parseSourceFile(ISourceLocation loc, String input) { | ||
DavyLandman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (parserClass == null || loadingParserError != null) { | ||
return CompletableFuture.failedFuture(new RuntimeException("ParserClass did not load", loadingParserError)); | ||
} | ||
return CompletableFuture.supplyAsync(() -> { | ||
try { | ||
IGTD<IConstructor, ITree, ISourceLocation> parser | ||
= parserClass.getDeclaredConstructor().newInstance(); | ||
return (ITree)parser.parse( | ||
parserMethodName, loc.getURI(), input.toCharArray(), new NoActionExecutor(), | ||
new DefaultNodeFlattener<>(), | ||
new UPTRNodeFactory(allowAmbiguity), | ||
(IRecoverer<IConstructor>) null | ||
); | ||
} | ||
catch (ParseError pe) { | ||
ISourceLocation errorLoc = pe.getLocation(); | ||
throw RuntimeExceptionFactory.parseError(errorLoc); | ||
} | ||
catch (Ambiguous e) { | ||
ITree tree = e.getTree(); | ||
IValueFactory vf = IRascalValueFactory.getInstance(); | ||
throw RuntimeExceptionFactory.ambiguity(e.getLocation(), vf.string(SymbolAdapter.toString(TreeAdapter.getType(tree), false)), vf.string(TreeAdapter.yield(tree))); | ||
} | ||
catch (UndeclaredNonTerminalException e){ | ||
throw new UndeclaredNonTerminal(e.getName(), e.getClassName(), loc); | ||
} | ||
catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { | ||
throw new CompletionException("Error with loaded parser", e); | ||
} | ||
}); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private static Class<IGTD<IConstructor, ITree, ISourceLocation>> loadParserClass(ParserSpecification spec) throws ClassNotFoundException, IOException { | ||
DavyLandman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var bridge = new JavaBridge( | ||
Collections.singletonList(Evaluator.class.getClassLoader()), | ||
IRascalValueFactory.getInstance(), | ||
new Configuration()); | ||
return (Class<IGTD<IConstructor, ITree, ISourceLocation>>)bridge.loadClass(URIResolverRegistry.getInstance().getInputStream(spec.getParserLocation())); | ||
} | ||
|
||
|
||
|
||
private static final IValueFactory VF = IRascalValueFactory.getInstance(); | ||
|
||
@Override | ||
public InterruptibleFuture<IList> outline(ITree input) { | ||
return InterruptibleFuture.completedFuture(VF.list()); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<IConstructor> summarize(ISourceLocation loc, ITree input) { | ||
return InterruptibleFuture.completedFuture(ParametricSummaryBridge.emptySummary(loc)); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<ISet> lenses(ITree input) { | ||
return InterruptibleFuture.completedFuture(VF.set()); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<@Nullable IValue> executeCommand(String command) { | ||
return InterruptibleFuture.completedFuture(VF.bool(false)); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<IList> inlayHint(@Nullable ITree input) { | ||
return InterruptibleFuture.completedFuture(VF.list()); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<ISet> documentation(ISourceLocation loc, ITree input, ITree cursor) { | ||
return InterruptibleFuture.completedFuture(VF.set()); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<ISet> defines(ISourceLocation loc, ITree input, ITree cursor) { | ||
return InterruptibleFuture.completedFuture(VF.set()); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<ISet> references(ISourceLocation loc, ITree input, ITree cursor) { | ||
return InterruptibleFuture.completedFuture(VF.set()); | ||
} | ||
|
||
@Override | ||
public InterruptibleFuture<ISet> implementations(ISourceLocation loc, ITree input, ITree cursor) { | ||
return InterruptibleFuture.completedFuture(VF.set()); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasDedicatedDocumentation() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasDedicatedDefines() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasDedicatedReferences() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasDedicatedImplementations() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasOutline() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasSummarize() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasLenses() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasExecuteCommand() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> hasInlayHint() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> askSummaryForDocumentation() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> askSummaryForDefinitions() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> askSummaryForReferences() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Boolean> askSummaryForImplementations() { | ||
return CompletableFuture.completedFuture(false); | ||
} | ||
|
||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why leak these implementation details if Prelude.loadParsers hides all of that? All you have to do is call that function with the right source location. You receive an
IFunction
that can becalled
directly with an input source location or string (first parameter), then the location used for the@loc
annotations. The options are provided with the loadParsers method. If you don't want to instantiate a Prelude, a RascalFunctionValueFactory instance with an empty Evaluator for the constructor will also do fine.With this you have saved 130 lines of code, and also you can hide behind the interface in case future parser implementations change the way they are implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the IFunction is acquired, there is no more need for the RascalFunctionValueFactory and its empty Evaluator, or a Prelude instance. The function instance will lock on the evaluator only when/if it is called from the evaluator, and not if called manually via the
IFunction.call
interface.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to copy that code since it had a hard dependency on Evaluator. I specificaly wanted not to load an evaluator.