package org.overture.codegen.vdm2jml.trans; import org.apache.log4j.Logger; import org.overture.ast.definitions.SFunctionDefinition; import org.overture.ast.definitions.SOperationDefinition; import org.overture.ast.expressions.AVariableExp; import org.overture.ast.node.INode; import org.overture.codegen.ir.STypeIR; import org.overture.codegen.ir.SourceNode; import org.overture.codegen.ir.analysis.AnalysisException; import org.overture.codegen.ir.analysis.DepthFirstAnalysisAdaptor; import org.overture.codegen.ir.declarations.ADefaultClassDeclIR; import org.overture.codegen.ir.declarations.AFieldDeclIR; import org.overture.codegen.ir.declarations.AFormalParamLocalParamIR; import org.overture.codegen.ir.declarations.AMethodDeclIR; import org.overture.codegen.ir.declarations.ARecordDeclIR; import org.overture.codegen.ir.expressions.AExplicitVarExpIR; import org.overture.codegen.ir.expressions.AFieldExpIR; import org.overture.codegen.ir.expressions.AIdentifierVarExpIR; import org.overture.codegen.ir.patterns.AIdentifierPatternIR; import org.overture.codegen.ir.types.ABoolBasicTypeIR; import org.overture.codegen.ir.types.AClassTypeIR; import org.overture.codegen.ir.types.AMethodTypeIR; import org.overture.codegen.trans.assistants.TransAssistantIR; import org.overture.codegen.vdm2java.JavaCodeGen; import org.overture.codegen.vdm2java.JavaCodeGenUtil; public class RecInvTransformation extends DepthFirstAnalysisAdaptor { private JavaCodeGen javaGen; private String paramName; private ARecordDeclIR rec; private Logger log = Logger.getLogger(this.getClass().getName()); public RecInvTransformation(JavaCodeGen javaGen, ARecordDeclIR rec) throws AnalysisException { this.javaGen = javaGen; this.rec = rec; changeRecInvSignature(); } private void changeRecInvSignature() throws AnalysisException { if (!(rec.getInvariant() instanceof AMethodDeclIR)) { log.error("Expected invariant to be a method declaration. Got: " + rec.getInvariant()); terminate(); } AMethodDeclIR invMethod = (AMethodDeclIR) rec.getInvariant(); if (invMethod.getFormalParams().size() != 1) { log.error("Expected invariant to take a single argument. Instead it takes " + invMethod.getFormalParams().size()); if (invMethod.getFormalParams().isEmpty()) { terminate(); } } AFormalParamLocalParamIR param = invMethod.getFormalParams().getFirst(); if (!(param.getPattern() instanceof AIdentifierPatternIR)) { log.error("Expected pattern of formal parameter to be an identifier pattern at this point. Got " + param.getPattern()); terminate(); } // First update the signature of the invariant method to take the fields invMethod.setMethodType(null); invMethod.getFormalParams().clear(); AMethodTypeIR newMethodType = new AMethodTypeIR(); newMethodType.setResult(new ABoolBasicTypeIR()); invMethod.setMethodType(newMethodType); for (AFieldDeclIR f : rec.getFields()) { newMethodType.getParams().add(f.getType().clone()); AFormalParamLocalParamIR nextParam = new AFormalParamLocalParamIR(); nextParam.setPattern(javaGen.getInfo().getPatternAssistant().consIdPattern(consUniqueName(f.getName()))); nextParam.setType(f.getType().clone()); invMethod.getFormalParams().add(nextParam); } this.paramName = ((AIdentifierPatternIR) param.getPattern()).getName(); } private void terminate() throws AnalysisException { throw new AnalysisException("Invalid record invariant - transformation cannot be applied. See error log."); } @Override public void caseAExplicitVarExpIR(AExplicitVarExpIR node) throws AnalysisException { String pack = javaGen.getJavaSettings().getJavaRootPackage(); if (JavaCodeGenUtil.isValidJavaPackage(pack)) { STypeIR type = node.getClassType(); if (type instanceof AClassTypeIR) { AClassTypeIR classType = (AClassTypeIR) type; classType.setName(pack + "." + classType.getName()); } else { log.error("Expected type of explicit variable to be a class type at this point. Got: " + type); } } } @Override public void caseAIdentifierVarExpIR(AIdentifierVarExpIR node) throws AnalysisException { if (node.parent() instanceof AFieldExpIR && node.getName().equals(paramName)) { AFieldExpIR field = (AFieldExpIR) node.parent(); TransAssistantIR assistant = javaGen.getTransAssistant(); AIdentifierVarExpIR replField = javaGen.getInfo().getExpAssistant().consIdVar(consUniqueName(field.getMemberName()), field.getType().clone()); assistant.replaceNodeWith(field, replField); } else { SourceNode sourceNode = node.getSourceNode(); if (sourceNode != null) { INode vdmNode = sourceNode.getVdmNode(); if (vdmNode instanceof AVariableExp) { AVariableExp varExp = (AVariableExp) vdmNode; if (varExp.getVardef() instanceof SFunctionDefinition || varExp.getVardef() instanceof SOperationDefinition) { ADefaultClassDeclIR encClass = rec.getAncestor(ADefaultClassDeclIR.class); if (encClass != null) { String defClass = ""; if (JavaCodeGenUtil.isValidJavaPackage(encClass.getPackage())) { defClass += encClass.getPackage() + "."; } defClass += encClass.getName(); AExplicitVarExpIR func = new AExplicitVarExpIR(); AClassTypeIR classType = new AClassTypeIR(); classType.setName(defClass); func.setClassType(classType); func.setIsLambda(false); func.setIsLocal(false); func.setSourceNode(sourceNode); func.setName(node.getName()); javaGen.getTransAssistant().replaceNodeWith(node, func); } else { log.error("Could not find enclosing class of record " + rec.getName()); } } } } } } /** * TODO: Constructing names like this will work since names on the form _<name> cannot appear in a VDM model. What * is not so nice about this approach is that it uses the naming conventions of an old name in the IR. Please note * that since this method is used to construct names that appear inside an invariant no old name will ever appear. A * better solution than the current one would be to pick a name that is not already used in the scope the name is * constructed for. */ private String consUniqueName(String name) { return "_" + name; } }