package it.xsemantics.dsl.generator;
import it.xsemantics.dsl.typing.XsemanticsTypeSystem;
import it.xsemantics.dsl.util.XsemanticsNodeModelUtils;
import it.xsemantics.dsl.util.XsemanticsUtils;
import it.xsemantics.dsl.xsemantics.AuxiliaryDescription;
import it.xsemantics.dsl.xsemantics.EmptyEnvironment;
import it.xsemantics.dsl.xsemantics.EnvironmentAccess;
import it.xsemantics.dsl.xsemantics.EnvironmentComposition;
import it.xsemantics.dsl.xsemantics.EnvironmentMapping;
import it.xsemantics.dsl.xsemantics.EnvironmentReference;
import it.xsemantics.dsl.xsemantics.EnvironmentSpecification;
import it.xsemantics.dsl.xsemantics.ErrorSpecification;
import it.xsemantics.dsl.xsemantics.Fail;
import it.xsemantics.dsl.xsemantics.JudgmentDescription;
import it.xsemantics.dsl.xsemantics.OrExpression;
import it.xsemantics.dsl.xsemantics.RuleInvocation;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.common.types.JvmExecutable;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XBlockExpression;
import org.eclipse.xtext.xbase.XClosure;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XMemberFeatureCall;
import org.eclipse.xtext.xbase.XVariableDeclaration;
import org.eclipse.xtext.xbase.XbasePackage;
import org.eclipse.xtext.xbase.compiler.Later;
import org.eclipse.xtext.xbase.compiler.XbaseCompiler;
import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable;
import org.eclipse.xtext.xbase.impl.FeatureCallToJavaMapping;
import com.google.inject.Inject;
public class XsemanticsXbaseCompiler extends XbaseCompiler {
@Inject
private FeatureCallToJavaMapping featureCallToJavaMapping;
@Inject
protected XsemanticsNodeModelUtils nodeModelUtils;
@Inject
protected XsemanticsUtils xsemanticsUtils;
@Inject
protected XsemanticsGeneratorExtensions generatorExtensions;
@Inject
protected XsemanticsTypeSystem typeSystem;
@Override
protected void doInternalToJavaStatement(XExpression obj,
ITreeAppendable appendable, boolean isReferenced) {
if (obj instanceof RuleInvocation) {
_toJavaStatement((RuleInvocation) obj, appendable, isReferenced);
} else if (obj instanceof OrExpression) {
_toJavaStatement((OrExpression) obj, appendable, isReferenced);
} else if (obj instanceof EnvironmentAccess) {
_toJavaStatement((EnvironmentAccess) obj, appendable, isReferenced);
} else if (obj instanceof Fail) {
_toJavaStatement((Fail) obj, appendable, isReferenced);
} else
super.doInternalToJavaStatement(obj, appendable, isReferenced);
}
@Override
protected void internalToConvertedExpression(XExpression obj,
ITreeAppendable appendable) {
if (obj instanceof RuleInvocation) {
_toJavaExpression((RuleInvocation) obj, appendable);
} else if (obj instanceof OrExpression) {
_toJavaExpression((OrExpression) obj, appendable);
} else if (obj instanceof EnvironmentAccess) {
_toJavaExpression((EnvironmentAccess) obj, appendable);
} else if (obj instanceof Fail) {
_toJavaExpression((Fail) obj, appendable);
} else
super.internalToConvertedExpression(obj, appendable);
}
@Override
protected void _toJavaStatement(XBlockExpression expr, ITreeAppendable b,
boolean isReferenced) {
if (insideClosure(expr)) {
// make sure it is referenced if there's only one expression in the
// block otherwise we might generate
// an invalid Java statement
super._toJavaStatement(
expr,
b,
isReferenced
|| (expr.getExpressions().size() == 1 && typeSystem
.isBooleanPremise(expr.getExpressions()
.get(0))));
} else {
if (expr.getExpressions().isEmpty())
return;
if (expr.getExpressions().size() == 1) {
compileBooleanXExpression(expr.getExpressions().get(0), b,
isReferenced);
return;
}
if (isReferenced)
declareSyntheticVariable(expr, b);
boolean needsBraces = isReferenced;
if (needsBraces) {
b.newLine().append("{").increaseIndentation();
b.openPseudoScope();
}
final EList<XExpression> expressions = expr.getExpressions();
for (int i = 0; i < expressions.size(); i++) {
XExpression ex = expressions.get(i);
boolean hasToBeReferenced = isReferenced;
if (i < expressions.size() - 1) {
hasToBeReferenced = false;
}
compileBooleanXExpression(ex, b, hasToBeReferenced);
if (hasToBeReferenced) {
b.append("\n").append(getVarName(expr, b)).append(" = (");
internalToConvertedExpression(ex, b, null);
b.append(");");
}
}
if (needsBraces) {
b.closeScope();
b.decreaseIndentation().newLine().append("}");
}
}
}
@Override
protected void appendFeatureCall(XAbstractFeatureCall call, ITreeAppendable b) {
JvmIdentifiableElement feature = call.getFeature();
AuxiliaryDescription auxiliaryDescription = generatorExtensions.associatedAuxiliaryDescription(feature);
if (auxiliaryDescription == null) {
super.appendFeatureCall(call, b);
return;
}
CharSequence name = generatorExtensions.entryPointInternalMethodName(auxiliaryDescription);
// copied from FeatureCallCompiler
b.trace(call, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE, 0).append(name);
if (feature instanceof JvmExecutable) {
b.append("(");
// add the trace argument
b.append(generatorExtensions.ruleApplicationTraceName());
List<XExpression> arguments = featureCallToJavaMapping.getActualArguments(call);
if (!arguments.isEmpty()) {
b.append(", ");
XExpression receiver = (call instanceof XMemberFeatureCall) ? ((XMemberFeatureCall)call).getMemberCallTarget() : null;
boolean shouldBreakFirstArgument = receiver == null || arguments.get(0) != receiver;
appendArguments(arguments, b, shouldBreakFirstArgument);
}
b.append(")");
}
}
protected boolean insideClosure(XBlockExpression expr) {
return expr.eContainer() instanceof XClosure;
}
/**
* If it's boolean, wrapes in an if with throw RuleFailedException
*
* @param expression
* @param b
* @param hasToBeReferenced
*/
protected void compileBooleanXExpression(XExpression expression,
ITreeAppendable b, boolean hasToBeReferenced) {
if (expression instanceof XBlockExpression) {
// original generation
// no need to handle XBlock as a boolean expression:
// its expressions will be handled accordingly
internalToJavaStatement(expression, b, hasToBeReferenced);
return;
}
boolean isBoolean = typeSystem.isBooleanPremise(expression);
if (isBoolean)
hasToBeReferenced = true;
// original generation
internalToJavaStatement(expression, b, hasToBeReferenced);
if (isBoolean) {
generateCommentWithOriginalCode(expression, b);
newLine(b);
b.append("if (!");
toJavaExpression(expression, b);
b.append(") {");
b.increaseIndentation();
newLine(b);
throwNewRuleFailedException(expression, b);
closeBracket(b);
}
}
/**
* Does nothing: rule invocation is compiled into a statement
*
* @param ruleInvocation
* @param b
*/
public void _toJavaExpression(RuleInvocation ruleInvocation,
ITreeAppendable b) {
b.append("null");
}
public void _toJavaExpression(OrExpression orExpression, ITreeAppendable b) {
b.append("null");
}
public void _toJavaStatement(RuleInvocation ruleInvocation,
ITreeAppendable b, boolean isReferenced) {
generateCommentWithOriginalCode(ruleInvocation, b);
JudgmentDescription judgmentDescription = xsemanticsUtils
.judgmentDescription(ruleInvocation,
ruleInvocation.getJudgmentSymbol(),
ruleInvocation.getRelationSymbols());
final EList<XExpression> ruleInvocationExpressions = ruleInvocation
.getExpressions();
ruleInvocationExpressionsToJavaStatements(b, ruleInvocationExpressions);
generateEnvironmentSpecificationAsStatements(
ruleInvocation.getEnvironment(), b);
boolean hasOutputParams = xsemanticsUtils
.hasOutputParams(ruleInvocation);
newLine(b);
String resultVariable = "";
if (hasOutputParams) {
generatorExtensions.resultType(judgmentDescription, b);
space(b);
resultVariable = generateResultVariable(ruleInvocation, b);
assign(b);
}
b.append(generatorExtensions.entryPointInternalMethodName(
judgmentDescription).toString());
b.append("(");
generateEnvironmentSpecificationAsExpression(
ruleInvocation.getEnvironment(), b);
comma(b);
b.append(generatorExtensions.additionalArgsForRuleInvocation(
ruleInvocation).toString());
comma(b);
ruleInvocationExpressionsToJavaExpressions(b, ruleInvocation);
b.append(");");
if (hasOutputParams) {
reassignResults(b, ruleInvocation, resultVariable, true);
}
}
public void _toJavaStatement(final EnvironmentAccess environmentAccess,
final ITreeAppendable b, boolean isReferenced) {
generateCommentWithOriginalCode(environmentAccess, b);
toJavaStatement(environmentAccess.getArgument(), b, true);
if (isReferenced) {
Later later = new Later() {
public void exec(ITreeAppendable b) {
compileEnvironmentAccess(environmentAccess, b);
}
};
declareFreshLocalVariable(environmentAccess, b, later);
} else {
newLine(b);
compileEnvironmentAccess(environmentAccess, b);
b.append(";");
}
}
protected void _toJavaExpression(EnvironmentAccess environmentAccess,
ITreeAppendable b) {
b.append(b.getName(environmentAccess));
}
protected void compileEnvironmentAccess(
EnvironmentAccess environmentAccess, ITreeAppendable b) {
b.append(generatorExtensions.environmentAccessMethod());
b.append("(");
b.append(environmentAccess.getEnvironment().getName());
comma(b);
toJavaExpression(environmentAccess.getArgument(), b);
comma(b);
generateJavaClassReference(environmentAccess.getType(),
environmentAccess, b);
b.append(")");
}
protected void _toJavaStatement(final OrExpression orExpression,
final ITreeAppendable b, boolean isReferenced) {
generateCommentWithOriginalCode(orExpression, b);
final XExpression left = orExpression.getBranches().get(0);
final XExpression right = orExpression.getBranches().get(1);
tryStmnt(b);
// make it referenced
compileBooleanXExpression(left, b, false);
catchStmnt(b, orExpression);
// don't need to make it referenced
compileBooleanXExpression(right, b, false);
closeBracket(b);
}
public void throwNewRuleFailedException(final XExpression expression,
final ITreeAppendable b) {
b.append(generatorExtensions.sneakyThrowRuleFailedException());
b.append("(");
generateStringWithOriginalCode(expression, b);
b.append(");");
}
protected void ruleInvocationExpressionsToJavaStatements(ITreeAppendable b,
final EList<XExpression> ruleInvocationExpressions) {
for (XExpression ruleInvocationExpression : ruleInvocationExpressions) {
toJavaStatement(ruleInvocationExpression, b, true);
}
}
protected void ruleInvocationExpressionsToJavaExpressions(
ITreeAppendable b, final RuleInvocation ruleInvocation) {
ruleInvocationExpressionsToJavaExpressions(b,
xsemanticsUtils.inputArgsExpressions(ruleInvocation));
}
protected void ruleInvocationExpressionsToJavaExpressions(
ITreeAppendable b,
final List<XExpression> inputArgsExpressions) {
Iterator<XExpression> expIt = inputArgsExpressions
.iterator();
while (expIt.hasNext()) {
toJavaExpression(expIt.next(), b);
if (expIt.hasNext())
comma(b);
}
}
protected void reassignResults(ITreeAppendable b,
RuleInvocation ruleInvocation, String resultVariable,
boolean checkAssignable) {
List<XExpression> expIt = xsemanticsUtils
.outputArgsExpressions(ruleInvocation);
if (expIt.isEmpty())
return;
newLine(b);
Iterator<String> getMethods = XsemanticsGeneratorConstants
.getResultGetMethods().iterator();
for (XExpression expression : expIt) {
final JvmTypeReference expressionType = typeSystem.getType(
expression);
final String getMethod = getMethods.next();
if (checkAssignable) {
b.append("checkAssignableTo");
b.append("(");
b.append(resultVariable + "." + getMethod);
comma(b);
generateJavaClassReference(expressionType, expression, b);
b.append(");");
newLine(b);
}
// assignment with cast
if (expression instanceof XVariableDeclaration) {
// this is not contemplated by xbase compiler
XVariableDeclaration varDecl = (XVariableDeclaration) expression;
b.append(b.getName(varDecl));
} else {
toJavaExpression(expression, b);
}
assign(b);
b.append("(");
serialize(expressionType, expression, b);
b.append(")");
space(b);
b.append(resultVariable + "." + getMethod);
b.append(";");
newLine(b);
}
}
protected void generateJavaClassReference(
final JvmTypeReference expressionType,
final XExpression expression, ITreeAppendable b) {
b.append(expressionType.getType());
b.append(".class");
}
public void _toJavaExpression(Fail fail, ITreeAppendable b) {
b.append("null");
}
public void _toJavaStatement(Fail fail, ITreeAppendable b,
boolean isReference) {
generateCommentWithOriginalCode(fail, b);
final XExpression errorSpecification = fail.getError();
if (errorSpecification == null) {
newLine(b);
b.append("throwForExplicitFail();");
} else {
toJavaStatement(errorSpecification, b, isReference);
}
}
public void _toJavaStatement(ErrorSpecification errorSpecification, ITreeAppendable b,
boolean isReference) {
String errorMessageVar = compileErrorOfErrorSpecification(
errorSpecification, b);
String sourceVar = compileSourceOfErrorSpecification(
errorSpecification, b);
String featureVar = compileFeatureOfErrorSpecification(
errorSpecification, b);
newLine(b);
b.append("throwForExplicitFail(");
b.append(errorMessageVar);
comma(b);
b.append("new ");
b.append(generatorExtensions.errorInformationType(errorSpecification).getType());
b.append("(");
b.append(sourceVar);
comma(b);
b.append(featureVar);
b.append(")");
b.append(");");
}
protected void generateCommentWithOriginalCode(EObject modelElement,
ITreeAppendable b) {
b.append("\n").append("/* ")
.append(nodeModelUtils.getProgramText(modelElement))
.append(" */");
}
protected void generateStringWithOriginalCode(EObject modelElement,
ITreeAppendable b) {
b.append("\"")
.append(generatorExtensions.javaString(nodeModelUtils
.getProgramText(modelElement))).append("\"");
}
protected void newLine(ITreeAppendable b) {
b.append("\n");
}
protected void space(ITreeAppendable b) {
b.append(" ");
}
protected void comma(ITreeAppendable b) {
b.append(", ");
}
protected void assign(ITreeAppendable b) {
space(b);
b.append("=");
space(b);
}
/**
* Also declares a RuleFailedException variable for the passed expressions
*
* @param b
* @param expression
* @return
*/
protected String catchStmnt(final ITreeAppendable b, XExpression expression) {
b.decreaseIndentation();
newLine(b);
b.append("} catch (");
b.append(generatorExtensions.exceptionType(expression).getType());
b.append(" ");
final String declareExceptionVariable = declareExceptionVariable(
expression, b);
b.append(declareExceptionVariable);
b.append(") {");
b.increaseIndentation();
return declareExceptionVariable;
}
protected void tryStmnt(final ITreeAppendable b) {
newLine(b);
b.append("try {");
b.increaseIndentation();
}
protected void closeBracket(final ITreeAppendable b) {
b.decreaseIndentation();
newLine(b);
b.append("}");
}
protected String generateResultVariable(RuleInvocation ruleInvocation,
ITreeAppendable b) {
final String declareResultVariable = declareResultVariable(
ruleInvocation, b);
b.append(declareResultVariable);
return declareResultVariable;
}
public String declareResultVariable(RuleInvocation ruleInvocation,
ITreeAppendable b) {
return b.declareSyntheticVariable(ruleInvocation, "result");
}
public String declareExceptionVariable(XExpression expression,
ITreeAppendable b) {
return b.declareSyntheticVariable(expression, "e");
}
public String compileErrorOfErrorSpecification(
final ErrorSpecification errorSpecification, final ITreeAppendable b) {
return compileAndAssignToLocalVariable(
errorSpecification.getError(),
b,
getTypeReferences().getTypeForName(String.class,
errorSpecification), "error");
}
public String compileSourceOfErrorSpecification(
final ErrorSpecification errorSpecification, final ITreeAppendable b) {
return compileAndAssignToLocalVariable(
errorSpecification.getSource(),
b,
getTypeReferences().getTypeForName(EObject.class,
errorSpecification), "source");
}
public String compileFeatureOfErrorSpecification(
final ErrorSpecification errorSpecification, final ITreeAppendable b) {
return compileAndAssignToLocalVariable(
errorSpecification.getFeature(),
b,
getTypeReferences().getTypeForName(EStructuralFeature.class,
errorSpecification), "feature");
}
protected String compileAndAssignToLocalVariable(
final XExpression expression, final ITreeAppendable b,
JvmTypeReference expectedType, String proposedVariable) {
if (expression == null)
return "null";
toJavaStatement(expression, b, true);
final Object syntheticObject = new Object();
final String varName = b.declareSyntheticVariable(syntheticObject,
proposedVariable);
b.append("\n");
serialize(expectedType, expression, b);
b.append(" ").append(varName).append(" = ");
toJavaExpression(expression, b);
b.append(";");
return b.getName(syntheticObject);
}
public void generateEnvironmentSpecificationAsExpression(
EnvironmentSpecification environmentSpecification, ITreeAppendable b) {
if (environmentSpecification instanceof EmptyEnvironment) {
b.append(generatorExtensions.emptyEnvironmentInvocation());
} else if (environmentSpecification instanceof EnvironmentReference) {
b.append(((EnvironmentReference) environmentSpecification)
.getEnvironment().getName());
}
if (environmentSpecification instanceof EnvironmentMapping) {
EnvironmentMapping mapping = (EnvironmentMapping) environmentSpecification;
b.append(generatorExtensions.environmentEntryInvocation());
b.append("(");
toJavaExpression(mapping.getKey(), b);
comma(b);
toJavaExpression(mapping.getValue(), b);
b.append(")");
} else if (environmentSpecification instanceof EnvironmentComposition) {
EnvironmentComposition composition = (EnvironmentComposition) environmentSpecification;
b.append(generatorExtensions.environmentCompositionInvocation());
b.append("(");
b.increaseIndentation();
newLine(b);
generateEnvironmentSpecificationAsExpression(
composition.getCurrentEnvironment(), b);
comma(b);
generateEnvironmentSpecificationAsExpression(
composition.getSubEnvironment(), b);
b.decreaseIndentation();
newLine(b);
b.append(")");
}
}
protected void generateEnvironmentSpecificationAsStatements(
EnvironmentSpecification environmentSpecification, ITreeAppendable b) {
if (environmentSpecification instanceof EnvironmentMapping) {
EnvironmentMapping mapping = (EnvironmentMapping) environmentSpecification;
toJavaStatement(mapping.getKey(), b, true);
toJavaStatement(mapping.getValue(), b, true);
} else if (environmentSpecification instanceof EnvironmentComposition) {
EnvironmentComposition composition = (EnvironmentComposition) environmentSpecification;
generateEnvironmentSpecificationAsStatements(
composition.getCurrentEnvironment(), b);
generateEnvironmentSpecificationAsStatements(
composition.getSubEnvironment(), b);
}
}
}