Skip to content

Commit

Permalink
Switch to using refactored dynsem entrypoint
Browse files Browse the repository at this point in the history
This allows us to select our rules for ourselves and apply them with
program terms and custom environments
  • Loading branch information
Balletie committed Jun 2, 2016
1 parent 62771a6 commit 3cbf567
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.metaborg.spoofax.shell.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;

import org.apache.commons.lang3.ClassUtils;
Expand All @@ -10,8 +12,10 @@
import org.metaborg.meta.lang.dynsem.interpreter.DynSemContext;
import org.metaborg.meta.lang.dynsem.interpreter.DynSemEntryPoint;
import org.metaborg.meta.lang.dynsem.interpreter.DynSemLanguage;
import org.metaborg.spoofax.shell.core.DynSemEvaluationStrategy.NonParser;
import org.metaborg.meta.lang.dynsem.interpreter.ITermRegistry;
import org.metaborg.meta.lang.dynsem.interpreter.nodes.rules.RuleRegistry;

import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.vm.PolyglotEngine;

/**
Expand All @@ -21,15 +25,7 @@
* the supported {@link DynSemLanguage#PARSER configuration parameter}.
*/
public class ClassPathInterpreterLoader implements IInterpreterLoader {
private NonParser nonParser;

/**
* @param nonParser
* The {@link NonParser} to inject as configuration parameter to the VM Builder.
*/
public ClassPathInterpreterLoader(NonParser nonParser) {
this.nonParser = nonParser;
}
private String targetPackage;

@Override
public PolyglotEngine loadInterpreterForLanguage(ILanguageImpl langImpl)
Expand All @@ -42,10 +38,22 @@ public PolyglotEngine loadInterpreterForLanguage(ILanguageImpl langImpl)

DynSemEntryPoint entryPoint = getEntryPoint(dynSemProperties);

targetPackage = dynSemProperties.getProperty("target.package");
RuleRegistry ruleRegistry = entryPoint.getRuleRegistry();
ITermRegistry termRegistry = entryPoint.getTermRegistry();

String mimeType = entryPoint.getMimeType();
return PolyglotEngine.newBuilder().config(mimeType, DynSemLanguage.PARSER, nonParser)
.config(mimeType, DynSemLanguage.RULE_REGISTRY, entryPoint.getRuleRegistry())
.config(mimeType, DynSemLanguage.TERM_REGISTRY, entryPoint.getTermRegistry()).build();
PolyglotEngine builtEngine =
PolyglotEngine.newBuilder().config(mimeType, DynSemLanguage.RULE_REGISTRY, ruleRegistry)
.config(mimeType, DynSemLanguage.TERM_REGISTRY, termRegistry).build();
try {
builtEngine
.eval(Source.fromReader(new InputStreamReader(entryPoint.getSpecificationTerm()),
"Evaluate to interpreter.").withMimeType(mimeType));
} catch (IOException e) {
throw new InterpreterLoadException(e);
}
return builtEngine;
}

private DynSemLanguage getDynSemLanguageSingleton(Properties dynSemProperties)
Expand All @@ -71,6 +79,11 @@ private DynSemEntryPoint getEntryPoint(Properties dynSemProperties)
}
}

@Override
public String getTargetPackage() {
return targetPackage;
}

private Class<?> getGeneratedClass(Properties dynSemProperties, String className)
throws InterpreterLoadException {
String targetPackage = dynSemProperties.getProperty("target.package");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,48 @@
package org.metaborg.spoofax.shell.core;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.metaborg.core.MetaborgException;
import org.metaborg.core.context.IContext;
import org.metaborg.core.language.ILanguageImpl;
import org.metaborg.meta.lang.dynsem.interpreter.IDynSemLanguageParser;
import org.metaborg.meta.lang.dynsem.interpreter.nodes.rules.RuleRegistry;
import org.metaborg.meta.lang.dynsem.interpreter.nodes.rules.RuleResult;
import org.metaborg.meta.lang.dynsem.interpreter.terms.ITerm;
import org.metaborg.spoofax.core.shell.ShellFacet;
import org.metaborg.spoofax.shell.core.IInterpreterLoader.InterpreterLoadException;
import org.metaborg.spoofax.shell.output.AnalyzeResult;
import org.metaborg.spoofax.shell.output.ParseResult;
import org.spoofax.interpreter.core.Tools;
import org.spoofax.interpreter.terms.IStrategoAppl;
import org.spoofax.interpreter.terms.IStrategoConstructor;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.spoofax.jsglr.client.imploder.ImploderAttachment;
import org.spoofax.terms.StrategoString;
import org.spoofax.terms.TermFactory;

import com.oracle.truffle.api.source.Source;
import com.github.krukow.clj_lang.PersistentHashMap;
import com.oracle.truffle.api.vm.PolyglotEngine;
import com.oracle.truffle.api.vm.PolyglotEngine.Value;

/**
* An {@link IEvaluationStrategy} for DynSem-based languages.
*/
public class DynSemEvaluationStrategy implements IEvaluationStrategy {
private IInterpreterLoader interpLoader;
private final IInterpreterLoader interpLoader;
private PolyglotEngine polyglotEngine;
private NonParser nonParser;
private String shellStartSymbol;

/**
* Construct a new {@link DynSemEvaluationStrategy}. This does not yet load the interpreter for
* the language. Rather, this is done when first invoking
* {@link #evaluate(AnalyzeResult, IContext)} or {@link #evaluate(ParseResult, IContext)}.
*/
public DynSemEvaluationStrategy() {
nonParser = new NonParser();
interpLoader = new ClassPathInterpreterLoader(nonParser);
interpLoader = new ClassPathInterpreterLoader();
}

@Override
Expand All @@ -44,86 +52,82 @@ public String name() {

@Override
public IStrategoTerm evaluate(ParseResult parsed, IContext context) throws MetaborgException {
return evaluate(parsed.unit().input().text(), parsed.ast().get(), context.language());
return evaluate(parsed.ast().get(), context.language());
}

@Override
public IStrategoTerm evaluate(AnalyzeResult analyzed, IContext context)
throws MetaborgException {
return evaluate(analyzed.unit().input().input().text(), analyzed.ast().get(),
context.language());
return evaluate(analyzed.ast().get(), context.language());
}

private IStrategoTerm evaluate(String origSource, IStrategoTerm input, ILanguageImpl langImpl)
private IStrategoTerm evaluate(IStrategoTerm input, ILanguageImpl langImpl)
throws MetaborgException {
if (uninitialized()) {
initialize(langImpl);
}
nonParser.setCurrentTerm(input);
Value eval = null;

ITerm programTerm = null;
try {
Source source = Source.fromNamedText(origSource, Integer.toHexString(input.hashCode()))
.withMimeType("application/x-simpl");
eval = polyglotEngine.eval(source);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
programTerm = getProgramTerm(input);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
| InvocationTargetException cause) {
throw new MetaborgException("Error constructing program term from input.", cause);
}

Value prog = polyglotEngine.findGlobalSymbol("INIT");
IStrategoConstructor inputCtor = ((IStrategoAppl) input).getConstructor();
String ctorName = inputCtor.getName();
int arity = inputCtor.getArity();

if (!Tools.isTermAppl(input)) {
throw new MetaborgException("Expected a StrategoAppl, but a \""
+ input.getClass().getName() + "\" was found: "
+ input.toString(1));
}
Value rule;
if (getSortForTerm(input) == shellStartSymbol) {
// Look up "-shell->" rule.
rule = polyglotEngine.findGlobalSymbol(RuleRegistry.makeKey("shell", ctorName, arity));
} else {
// Look up a "-shell_init->" rule.
rule = polyglotEngine.findGlobalSymbol(RuleRegistry.makeKey("init", ctorName, arity));
}
try {
Callable<RuleResult> callable = new Callable<RuleResult>() {
@Override
public RuleResult call() throws Exception {
return prog.execute().as(RuleResult.class);
}
};
RuleResult ruleResult = callable.call();
RuleResult ruleResult = rule.execute(programTerm).as(RuleResult.class);
return new StrategoString(ruleResult.result.toString(), TermFactory.EMPTY_LIST,
IStrategoTerm.IMMUTABLE);
} catch (Exception e) {
throw new RuntimeException(e);

} catch (IOException e) {
throw new MetaborgException("Input/output error while evaluating.", e);
}
}

private boolean uninitialized() {
return polyglotEngine == null;
@SuppressWarnings("unchecked")
private ITerm getProgramTerm(IStrategoTerm input) throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
String termSort = getSortForTerm(input);
// Get the abstract class for the sort of the term.
Class<? extends ITerm> generatedTermClass = (Class<? extends ITerm>) ClassUtils
.getClass(interpLoader.getTargetPackage() + ".terms.I" + termSort + "Term");
return (ITerm) MethodUtils.invokeStaticMethod(generatedTermClass, "create", input);
}

private void initialize(ILanguageImpl langImpl) throws InterpreterLoadException {
polyglotEngine = interpLoader.loadInterpreterForLanguage(langImpl);
private String getSortForTerm(IStrategoTerm input) {
ImploderAttachment termAttachment = input.getAttachment(ImploderAttachment.TYPE);
if (termAttachment == null) {
return null;
}
return termAttachment.getElementSort();
}

/**
* An {@link IDynSemLanguageParser} which returns just the {@link IStrategoTerm} which is set by
* the evaluation strategy. Since Truffle only supports programs parsed directly from source
* code, we need this workaround to avoid having to parse source code to {@link IStrategoTerm
* ASTs} again. Hence this class is called a {@link NonParser "non parser"}.
*/
static class NonParser implements IDynSemLanguageParser {
private IStrategoTerm currentTerm;

/**
* @return the current term.
*/
public IStrategoTerm getCurrentTerm() {
return currentTerm;
}
private boolean uninitialized() {
return polyglotEngine == null && shellStartSymbol == null;
}

/**
* Sets the term to be returned the next time the parser is called through
* {@link PolyglotEngine#eval(Source)}.
*
* @param currentTerm
* the current term to set.
*/
public void setCurrentTerm(IStrategoTerm currentTerm) {
this.currentTerm = currentTerm;
}
private void initialize(ILanguageImpl langImpl) throws InterpreterLoadException {
polyglotEngine = interpLoader.loadInterpreterForLanguage(langImpl);

@Override
public IStrategoTerm parse(Source src) {
return getCurrentTerm();
}
ShellFacet shellFacet = langImpl.facet(ShellFacet.class);
shellStartSymbol = shellFacet.getShellStartSymbol();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public interface IInterpreterLoader {
PolyglotEngine loadInterpreterForLanguage(ILanguageImpl langImpl)
throws InterpreterLoadException;

/**
* @return The target package of the files generated by DynSem.
*/
String getTargetPackage();

/**
* Exception thrown when loading results in an error.
*/
Expand Down

0 comments on commit 3cbf567

Please sign in to comment.