Skip to content
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 16 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ set -euxo pipefail

extra_flags=''

while getopts 'f' flag; do
clean="clean"
while getopts 'fd' flag; do
case "${flag}" in
f) extra_flags='-Drascal.compile.skip' ;;
*) printf "Use -f to skip rascal-compile"
d) clean='' ;;
*) printf "incorrect param, valid params:
Use -f to skip rascal-compile
Use -d to skip cleaning the target folder"
exit 1 ;;
esac
done

rm rascal-lsp/target/*.jar

(cd rascal-lsp && mvn clean package $extra_flags )
(cd rascal-lsp && mvn $clean package $extra_flags )
(cd rascal-vscode-extension && npm run lsp4j:package )

Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ public void registerLanguage(IConstructor language) {
((IString) language.get(1)).getValue(),
((IString) language.get(2)).getValue(),
((IString) language.get(3)).getValue(),
((IString) language.get(4)).getValue()
((IString) language.get(4)).getValue(),
null
);

languageClient.receiveRegisterLanguage(param);
Expand All @@ -158,7 +159,8 @@ public void unregisterLanguage(IConstructor language) {
((IString) language.get(1)).getValue(),
((IString) language.get(2)).getValue(),
((IString) language.get(3)).getValue(),
((IString) language.get(4)).getValue()
((IString) language.get(4)).getValue(),
null
);

languageClient.receiveUnregisterLanguage(param);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@
package org.rascalmpl.vscode.lsp.dap;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import org.rascalmpl.debug.IRascalFrame;
import org.rascalmpl.interpreter.Evaluator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public void addContributor(String contribKey, ILanguageContributions contrib) {
* @returns false if the multiplexer is empty, and therefore should not be used anymore
*/
public boolean removeContributor(String contribKey) {
contributions.removeIf(e -> e.key.equals(contribKey));
contributions.removeIf(e -> e.key.equals(contribKey) || e.key.equals(contribKey + "$parser"));
if (contributions.isEmpty()) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
import io.usethesource.vallang.ITuple;
import io.usethesource.vallang.IValue;
import io.usethesource.vallang.IWithKeywordParameters;
import io.usethesource.vallang.exceptions.FactParseError;

public class ParametricTextDocumentService implements IBaseTextDocumentService, LanguageClientAware {
private static final Logger logger = LogManager.getLogger(ParametricTextDocumentService.class);
Expand Down Expand Up @@ -533,6 +534,21 @@ public void registerLanguage(LanguageParameter lang) {
var fact = facts.computeIfAbsent(lang.getExtension(), t ->
new ParametricFileFacts(multiplexer, this::getFile, columns, ownExecuter)
);
if (lang.getPrecompiledParser() != null) {
try {
var location = lang.getPrecompiledParser().getParserLocation();
if (URIResolverRegistry.getInstance().exists(location)) {
logger.debug("Got precompiled definition: {}", lang.getPrecompiledParser());
multiplexer.addContributor(buildContributionKey(lang) + "$parser", new ParserOnlyContribution(lang.getName(), lang.getExtension(), lang.getPrecompiledParser()));
}
else {
logger.error("Defined precompiled parser ({}) does not exist", lang.getPrecompiledParser());
}
}
catch (FactParseError e) {
logger.error("Error parsing location in precompiled parser specification (we expect a rascal loc)", e);
}
}

multiplexer.addContributor(buildContributionKey(lang),
new InterpretedLanguageContributions(lang, this, workspaceService, (IBaseLanguageClient) client, ownExecuter));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
* 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.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CompletableFuture;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.env.GlobalEnvironment;
import org.rascalmpl.interpreter.env.ModuleEnvironment;
import org.rascalmpl.values.IRascalValueFactory;
import org.rascalmpl.values.RascalFunctionValueFactory;
import org.rascalmpl.values.RascalValueFactory;
import org.rascalmpl.values.functions.IFunction;
import org.rascalmpl.values.parsetrees.ITree;
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;
import io.usethesource.vallang.exceptions.FactTypeUseException;

public class ParserOnlyContribution implements ILanguageContributions {
private static final IValueFactory VF = IRascalValueFactory.getInstance();
private final String name;
private final String extension;
private final @Nullable Exception loadingParserError;
private final @Nullable IFunction parser;

public ParserOnlyContribution(String name, String extension, ParserSpecification spec) {
this.name = name;
this.extension = extension;

// we use an entry and a single initialization function to make sure that parser and loadingParserError can be `final`:
Either<IFunction,Exception> result = loadParser(spec);
this.parser = result.getLeft();
this.loadingParserError = result.getRight();
}

@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 (loadingParserError != null || parser != null) {
return CompletableFuture.failedFuture(new RuntimeException("Parser function did not load", loadingParserError));
}

return CompletableFuture.supplyAsync(() -> parser.call(VF.string(input), loc));
}

private static Either<IFunction, Exception> loadParser(ParserSpecification spec) {
// the next two object are scaffolding. we only need them temporarily, and they will not be used by the returned IFunction if the (internal) _call_ methods are not used from ICallableValue.
GlobalEnvironment unusedHeap = new GlobalEnvironment();
Evaluator unusedEvaluator = new Evaluator(VF, InputStream.nullInputStream(), OutputStream.nullOutputStream(), OutputStream.nullOutputStream(), new ModuleEnvironment("***unused***", unusedHeap), unusedHeap);
// this is what we are after: a factory that can load back parsers.
IRascalValueFactory vf = new RascalFunctionValueFactory(unusedEvaluator /*can not be null unfortunately*/);
IConstructor reifiedType = makeReifiedType(spec, vf);

try {
// this hides all the loading and instantiation details of Rascal-generated parsers
return Either.forLeft(vf.loadParser(reifiedType, spec.getParserLocation(), VF.bool(spec.getAllowAmbiguity()), VF.bool(false), VF.bool(false), vf.set()));
}
catch (IOException | ClassNotFoundException | FactTypeUseException e) {
return Either.forRight(e);
}

}

/** convert a non-terminal name into a proper reified type */
private static IConstructor makeReifiedType(ParserSpecification spec, IRascalValueFactory vf) {
String nt = spec.getNonTerminalName();
IConstructor symbol = vf.constructor(RascalValueFactory.Symbol_Sort, VF.string(nt));
symbol = spec.getNonTerminalIsStart() ? vf.constructor(RascalValueFactory.Symbol_Start, symbol) : symbol;
return vf.reifiedType(symbol, vf.map());
}

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

}
Loading
Loading