package org.overture.codegen.trans; import org.overture.codegen.ir.INode; import org.overture.codegen.ir.IRInfo; import org.overture.codegen.ir.SDeclIR; import org.overture.codegen.ir.analysis.AnalysisException; import org.overture.codegen.ir.analysis.DepthFirstAnalysisAdaptor; import org.overture.codegen.ir.declarations.AFieldDeclIR; import org.overture.codegen.ir.declarations.AFuncDeclIR; import org.overture.codegen.ir.declarations.AMethodDeclIR; import org.overture.codegen.ir.declarations.AStateDeclIR; import org.overture.codegen.ir.expressions.AFieldExpIR; import org.overture.codegen.ir.expressions.AIdentifierVarExpIR; import org.overture.codegen.ir.statements.AFieldStateDesignatorIR; import org.overture.codegen.ir.statements.AIdentifierStateDesignatorIR; import org.overture.codegen.ir.types.ARecordTypeIR; import org.overture.codegen.trans.assistants.TransAssistantIR; public class SlStateAccessTrans extends DepthFirstAnalysisAdaptor { private final AStateDeclIR stateDecl; private IRInfo info; private TransAssistantIR transAssistant; private boolean inPreOrPost; public SlStateAccessTrans(AStateDeclIR stateDecl, IRInfo info, TransAssistantIR transAssistant) { this.stateDecl = stateDecl; this.info = info; this.transAssistant = transAssistant; this.inPreOrPost = false; } @Override public void caseAFuncDeclIR(AFuncDeclIR node) throws AnalysisException { handleMethodOrFunc(node.getBody(), node.getPreCond(), node.getPostCond()); } @Override public void caseAMethodDeclIR(AMethodDeclIR node) throws AnalysisException { handleMethodOrFunc(node.getBody(), node.getPreCond(), node.getPostCond()); } @Override public void caseAIdentifierVarExpIR(AIdentifierVarExpIR node) throws AnalysisException { if (isOldFieldRead(stateDecl, node) /* 1 */ || isFieldRead(stateDecl, node) /* 2 */ || isFieldReadInPreOrPost(stateDecl, node) /* 3 */) { // Given a state named 'St' with a field named 'f' // Note: If a variable is 'old' then it is local since state is passed // as an argument to the post condition function // Also note that the IR uses underscore to denote old names: _St corresponds to St~ (in VDM) // and _f corresponds to f~ (in VDM) // /*1*/ In case we are in a post condition the variable expression may represent a // field of the old state, i.e._f (or f~ in VDM) // // /*2*/ Any other place in the model: // The variable expression represents a field of the current state // // /*3*/ Another possibility is that the variable expression represents // a field of the current state in a pre or post condition of a function or // an operation. This is a special case since the state field can be // read as 'f' although it is only the entire state that is being passed as an argument // to the function. Below is a Java example showing this (result, old state, // current state): // // public static Boolean post_op(final Number RESULT, final St _St, final St St) // If /*1*/ or /*2*/ or /*3*/ is true we make the field access explicit since IR modules // use a single field to represent state. // AFieldExpIR fieldExp = new AFieldExpIR(); fieldExp.setSourceNode(node.getSourceNode()); fieldExp.setTag(node.getTag()); fieldExp.setType(node.getType().clone()); setFieldNames(fieldExp, stateDecl, node.getName()); transAssistant.replaceNodeWith(node, fieldExp); } } @Override public void caseAIdentifierStateDesignatorIR( AIdentifierStateDesignatorIR node) throws AnalysisException { if (!node.getIsLocal() && !node.getName().equals(stateDecl.getName())) { ARecordTypeIR stateType = transAssistant.getRecType(stateDecl); AIdentifierStateDesignatorIR idState = new AIdentifierStateDesignatorIR(); idState.setClassName(null); idState.setExplicit(false); idState.setIsLocal(false); idState.setName(stateDecl.getName()); idState.setType(stateType); AFieldStateDesignatorIR field = new AFieldStateDesignatorIR(); field.setField(node.getName()); field.setObject(idState); for (AFieldDeclIR f : stateDecl.getFields()) { if (f.getName().equals(node.getName())) { field.setType(f.getType().clone()); } } transAssistant.replaceNodeWith(node, field); } } private void handleMethodOrFunc(INode body, SDeclIR preCond, SDeclIR postCond) throws AnalysisException { if (body != null) { body.apply(this); } handleCond(preCond); handleCond(postCond); } private void handleCond(SDeclIR cond) throws AnalysisException { if (cond != null) { inPreOrPost = true; cond.apply(this); inPreOrPost = false; } } private boolean isFieldReadInPreOrPost(final AStateDeclIR stateDecl, AIdentifierVarExpIR node) { if (!inPreOrPost) { return false; } boolean matches = false; for (AFieldDeclIR f : stateDecl.getFields()) { if (f.getName().equals(node.getName())) { matches = true; break; } } return matches; } private boolean isFieldRead(final AStateDeclIR stateDecl, AIdentifierVarExpIR node) { return !info.isSlStateRead(node) && !node.getName().equals(stateDecl.getName()); } private boolean isOldFieldRead(final AStateDeclIR stateDecl, AIdentifierVarExpIR node) { return info.getExpAssistant().isOld(node.getName()) && !node.getName().equals("_" + stateDecl.getName()); } private void setFieldNames(AFieldExpIR field, AStateDeclIR stateDecl, String name) { ARecordTypeIR recType = transAssistant.getRecType(stateDecl); String stateName = stateDecl.getName(); if (info.getExpAssistant().isOld(name)) { field.setObject(transAssistant.getInfo().getExpAssistant().consIdVar("_" + stateName, recType)); field.setMemberName(info.getExpAssistant().oldNameToCurrentName(name)); } else { field.setObject(transAssistant.getInfo().getExpAssistant().consIdVar(stateName, recType)); field.setMemberName(name); } } }