
javarepl.Evaluator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javarepl Show documentation
Show all versions of javarepl Show documentation
Java REPL is a simple Read-Eval-Print-Loop for Java language.
package javarepl;
import com.googlecode.totallylazy.*;
import com.googlecode.totallylazy.Option;
import com.googlecode.totallylazy.annotations.multimethod;
import com.googlecode.totallylazy.functions.Reducer;
import javarepl.expressions.*;
import javarepl.expressions.Type;
import javarepl.expressions.Value;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.regex.MatchResult;
import static com.googlecode.totallylazy.Either.left;
import static com.googlecode.totallylazy.Either.right;
import static com.googlecode.totallylazy.Files.*;
import static com.googlecode.totallylazy.Option.*;
import static com.googlecode.totallylazy.Sequences.empty;
import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.totallylazy.predicates.Not.not;
import static com.googlecode.totallylazy.predicates.Predicates.equalTo;
import static com.googlecode.totallylazy.predicates.Predicates.where;
import static java.io.File.pathSeparator;
import static javarepl.Evaluation.evaluation;
import static javarepl.EvaluationClassLoader.evaluationClassLoader;
import static javarepl.EvaluationContext.evaluationContext;
import static javarepl.Result.noResult;
import static javarepl.Utils.randomIdentifier;
import static javarepl.Utils.urlAsFilePath;
import static javarepl.expressions.Patterns.*;
import static javarepl.rendering.EvaluationClassRenderer.renderExpressionClass;
import static javarepl.rendering.EvaluationClassRenderer.renderMethodSignatureDetection;
import static javarepl.rendering.ExpressionSourceRenderer.renderExpressionSource;
import static javarepl.rendering.ExpressionTokenRenderer.EXPRESSION_TOKEN;
import static javax.tools.ToolProvider.getSystemJavaCompiler;
public class Evaluator {
private EvaluationClassLoader classLoader;
private EvaluationContext context;
public Evaluator(EvaluationContext context, EvaluationClassLoader classLoader) {
initializeEvaluator(context, classLoader);
}
public Either evaluate(final String expr) {
return parseExpression(expr).flatMap(
expression -> {
Either resultForValue = evaluate(expression);
if (resultForValue.isLeft() && resultForValue.left() instanceof ExpressionCompilationException && expression instanceof Value) {
Either resultForStatement = evaluate(new Statement(expr));
return resultForStatement.isLeft() && resultForStatement.left() instanceof ExpressionCompilationException
? left(new ExpressionCompilationException(sequence(resultForStatement.left().getMessage(), resultForValue.left().getMessage()).unique().toString("\n\n")))
: resultForStatement;
}
return resultForValue;
});
}
public Either parseExpression(String expression) {
if (isValidImport(expression))
return createImport(expression);
if (isValidType(expression))
return createTypeExpression(expression);
if (isValidMethod(expression))
return createMethodExpression(expression);
if (isValidAssignmentWithType(expression))
return createAssignmentWithType(expression);
if (isValidAssignment(expression))
return createAssignmentExpression(expression);
return createValueExpression(expression);
}
public void addResults(Sequence result) {
context = context.addResults(result);
}
private Either createImport(String expression) {
return right((Expression) new Import(expression, importPattern.match(expression).group(1)));
}
private Either createAssignmentWithType(String expression) {
try {
MatchResult match = Patterns.assignmentWithTypeNamePattern.match(expression);
java.lang.reflect.Method declaredMethod = detectMethod(match.group(1) + " " + randomIdentifier("method") + "(){}");
return right((Expression) new AssignmentWithType(expression, declaredMethod.getGenericReturnType(), match.group(2), match.group(3)));
} catch (Exception e) {
return left(Utils.unwrapException(e));
}
}
private Either createAssignmentExpression(String expression) {
MatchResult match = Patterns.assignmentPattern.match(expression);
return right((Expression) new Assignment(expression, match.group(1), match.group(2)));
}
private Either createTypeExpression(String expression) {
MatchResult match = typePattern.match(expression);
return right((Expression) new Type(expression, option(match.group(1)), match.group(2)));
}
private Either createValueExpression(String expression) {
return right((Expression) new Value(expression));
}
public Option lastSource() {
return context.lastSource();
}
public Sequence results() {
return context.results();
}
public Option result(String name) {
return context.result(name);
}
public Sequence expressionsOfType(Class type) {
return context.expressionsOfType(type);
}
public Sequence expressions() {
return context.expressions();
}
public EvaluationContext context() {
return context;
}
public void reset() {
clearOutputDirectory();
EvaluationContext evaluationContext = evaluationContext();
initializeEvaluator(evaluationContext, evaluationClassLoader(evaluationContext));
}
private void initializeEvaluator(EvaluationContext evaluationContext, EvaluationClassLoader evaluationClassLoader) {
context = evaluationContext;
classLoader = evaluationClassLoader;
}
public final Either tryEvaluate(String expression) {
Evaluator localEvaluator = new Evaluator(context, evaluationClassLoader(context));
return localEvaluator.evaluate(expression);
}
public final Option typeOfExpression(String expression) {
Either extends Throwable, Evaluation> evaluation = tryEvaluate(expression);
Option expressionType = none();
if (evaluation.isRight()) {
Option result = evaluation.right().result();
if (!result.isEmpty()) {
expressionType = some(result.get().type());
}
}
return expressionType;
}
public final Option classFrom(String expression) {
try {
return some(detectClass(expression));
} catch (Throwable e) {
return none();
}
}
public void clearOutputDirectory() {
deleteFiles(context.outputDirectory());
delete(context.outputDirectory());
}
public void addClasspathUrl(URL classpathUrl) {
classLoader.registerURL(classpathUrl);
}
public File outputDirectory() {
return context.outputDirectory();
}
@multimethod
private Either evaluate(Expression expression) {
return new multi() {
}.>methodOption(expression).getOrElse(evaluateExpression(expression));
}
@multimethod
private Either evaluate(Type expression) {
if (getSystemJavaCompiler() == null) {
return left((Throwable) new FileNotFoundException("Java compiler not found." +
"This can occur when JavaREPL was run with JRE instead of JDK or JDK is not configured correctly."));
}
try {
File outputPath = directory(context.outputDirectory(), expression.typePackage().getOrElse("").replace('.', File.separatorChar));
File outputJavaFile = file(outputPath, expression.type() + ".java");
String sources = renderExpressionClass(context, expression.type(), expression)
.replace(EXPRESSION_TOKEN, renderExpressionSource(expression));
Files.write(sources.getBytes(), outputJavaFile);
compile(outputJavaFile);
classLoader.loadClass(expression.canonicalName());
context = context.addExpression(expression).lastSource(sources);
return right(evaluation(expression, noResult()));
} catch (Exception e) {
return left(Utils.unwrapException(e));
}
}
private Either createMethodExpression(String expression) {
try {
java.lang.reflect.Method declaredMethod = detectMethod(expression);
return right((Expression) new Method(expression, declaredMethod.getGenericReturnType(), declaredMethod.getName(), sequence(declaredMethod.getParameterTypes())));
} catch (Exception e) {
return left(Utils.unwrapException(e));
}
}
private java.lang.reflect.Method detectMethod(String expression) throws Exception {
final String className = randomIdentifier("Method");
final File outputJavaFile = file(context.outputDirectory(), className + ".java");
final String sources = renderMethodSignatureDetection(context, className, expression);
Files.write(sources.getBytes(), outputJavaFile);
compile(outputJavaFile);
Class> expressionClass = classLoader.loadClass(className);
return expressionClass.getDeclaredMethods()[0];
}
private Class detectClass(String expression) throws Exception {
final String className = randomIdentifier("Class");
final File outputJavaFile = file(context.outputDirectory(), className + ".java");
final String sources = renderMethodSignatureDetection(context, className, "public " + expression + " detectClass(){return null;}");
Files.write(sources.getBytes(), outputJavaFile);
compile(outputJavaFile);
Class expressionClass = classLoader.loadClass(className);
return expressionClass.getDeclaredMethods()[0].getReturnType();
}
private Either evaluateExpression(final Expression expression) {
final String className = randomIdentifier("Evaluation");
try {
EvaluationContext newContext = context.removeExpressionWithKey(expression.key());
File outputJavaFile = file(context.outputDirectory(), className + ".java");
final String sources = renderExpressionClass(newContext, className, expression)
.replace(EXPRESSION_TOKEN, renderExpressionSource(expression));
Files.write(sources.getBytes(), outputJavaFile);
compile(outputJavaFile);
Class> expressionClass = classLoader.loadClass(className);
Constructor> constructor = expressionClass.getDeclaredConstructor(EvaluationContext.class);
final Object expressionInstance = constructor.newInstance(newContext);
java.lang.reflect.Method method = expressionClass.getMethod("evaluate");
Object resultObject = method.invoke(expressionInstance);
Sequence modifiedResults = modifiedResults(expressionInstance);
if (resultObject != null || !method.getReturnType().equals(void.class)) {
Result result = Result.result(nextResultKeyFor(expression), resultObject, typeFor(expression));
context = newContext.addExpression(expression).addResults(modifiedResults.append(result)).lastSource(sources);
return right(evaluation(expression, some(result)));
} else {
context = newContext.addExpression(expression).addResults(modifiedResults).lastSource(sources);
return right(evaluation(expression, noResult()));
}
} catch (Throwable e) {
return left(Utils.unwrapException(e));
}
}
private Sequence modifiedResults(final Object expressionInstance) {
return sequence(expressionInstance.getClass().getDeclaredFields())
.reduceLeft(new Reducer>() {
public Sequence call(Sequence results, Field field) throws Exception {
Option result = result(field.getName()).filter(where(Result::value, not(equalTo(field.get(expressionInstance)))));
if (result.isEmpty())
return results;
return results.append(Result.result(field.getName(), field.get(expressionInstance)));
}
public Sequence identity() {
return empty(Result.class);
}
});
}
private String nextResultKeyFor(Expression expression) {
return (expression instanceof Value)
? context.nextResultKey()
: expression.key();
}
private Option typeFor(Expression expression) {
return (expression instanceof AssignmentWithType)
? Option.some(((AssignmentWithType) expression).type())
: Option.none();
}
private void compile(File file) throws Exception {
String classpath =
sequence(System.getProperty("java.class.path"))
.join(sequence(classLoader.getURLs()).map(urlAsFilePath()))
.toString(pathSeparator);
JavaCompiler compiler = getSystemJavaCompiler();
DiagnosticCollector diagnostics = new DiagnosticCollector();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sequence(file));
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, sequence("-cp", classpath), null, compilationUnits);
try {
if (!task.call())
throw new ExpressionCompilationException(file, diagnostics.getDiagnostics());
} finally {
fileManager.close();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy