/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille * * ludovic.apvrille AT enst.fr * * This software is a computer program whose purpose is to allow the * edition of TURTLE analysis, design and deployment diagrams, to * allow the generation of RT-LOTOS or Java code from this diagram, * and at last to allow the analysis of formal validation traces * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP * from INRIA Rhone-Alpes. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ package tmltranslator; import compiler.tmlparser.ParseException; import compiler.tmlparser.SimpleNode; import compiler.tmlparser.TMLExprParser; import compiler.tmlparser.TokenMgrError; import myutil.Conversion; import myutil.TraceManager; import java.io.StringReader; import java.util.ArrayList; /** * Class TMLSyntaxChecking * Used verifying the syntax of a TML specification * Creation: 12/09/2007 * @version 1.0 12/09/2007 * @author Ludovic APVRILLE */ public class TMLSyntaxChecking { private final String WRONG_ORIGIN_CHANNEL = "is not declared as an origin channel of the task"; private final String WRONG_DESTINATION_CHANNEL = "is not declared as a destination channel of the task"; private final String WRONG_ORIGIN_EVENT = "is not declared as an origin event of the task"; private final String WRONG_DESTINATION_EVENT = "is not declared as a destination event of the task"; private final String WRONG_ORIGIN_REQUEST = "is not declared as an origin request of the task"; private final String SYNTAX_ERROR = "syntax error"; private final String WRONG_VARIABLE_IDENTIFIER = "forbidden variable's name"; private final String VARIABLE_ERROR = "variable is not used according to its type"; private final String UNDECLARED_VARIABLE = "unknown variable"; private final String SYNTAX_ERROR_VARIABLE_EXPECTED = "syntax error (variable expected)"; private final String TIME_UNIT_ERROR = "unknown time unit"; private final String NO_NEXT_OPERATOR_ERROR = "No next operator"; private ArrayList<TMLError> errors; private ArrayList<TMLError> warnings; private TMLModeling<?> tmlm; private TMLMapping<?> mapping; public TMLSyntaxChecking(TMLModeling<?> _tmlm) { tmlm = _tmlm; } public TMLSyntaxChecking(TMLMapping<?> _mapping) { mapping = _mapping; tmlm = mapping.getTMLModeling(); } public void checkSyntax() { errors = new ArrayList<TMLError>(); warnings = new ArrayList<TMLError>(); //System.out.println("Checking syntax"); checkReadAndWriteInChannelsEventsAndRequests(); checkActionSyntax(); checkNextActions(); } public int hasErrors() { if (errors == null) { return 0; } return errors.size(); } public int hasWarnings() { if (warnings == null) { return 0; } return warnings.size(); } public ArrayList<TMLError> getErrors() { return errors; } public ArrayList<TMLError> getWarnings() { return warnings; } public void addError(TMLTask t, TMLActivityElement elt, String message, int type) { TMLError error = new TMLError(type); error.message = message; error.task = t; error.element = elt; errors.add(error); } public void checkNextActions() { for(TMLTask t: tmlm.getTasks()) { TMLActivity tactivity = t.getActivityDiagram(); int n = tactivity.nElements(); for(int i=0; i<n; i++) { TMLActivityElement elt = tactivity.get(i); if (!(elt instanceof TMLStopState)) { if(elt.getNbNext() == 0) { addError(t, elt, elt.getName() + ": " + NO_NEXT_OPERATOR_ERROR, TMLError.ERROR_BEHAVIOR); } } } } } public void checkReadAndWriteInChannelsEventsAndRequests() { TMLChannel ch; TMLEvent evt; TMLRequest request; for(TMLTask t: tmlm.getTasks()) { TMLActivity tactivity = t.getActivityDiagram(); TMLActivityElement elt; int n = tactivity.nElements(); for(int i=0; i<n; i++) { elt = tactivity.get(i); //System.out.println("Task= " + t.getName() + " element=" + elt); if (elt instanceof TMLWriteChannel) { for(int j=0; j<((TMLWriteChannel)elt).getNbOfChannels(); j++) { ch = ((TMLWriteChannel)elt).getChannel(j); if (ch.isBasicChannel()) { //System.out.println("Write in channel" + ch.getName()); if (ch.getOriginTask() != t) { //System.out.println("Origin task=" + ch.getOriginTask().getName() + " / task = " + t.getName() + "tch=" + ch.getOriginTask() + " t=" + t); //System.out.println("tml:" + tmlm.toString()); // TMLTextSpecification tmlt = new TMLTextSpecification("toto"); //System.out.println("tml:" + tmlt.toTextFormat(tmlm)); addError(t, elt, ch.getName() + ": " + WRONG_ORIGIN_CHANNEL, TMLError.ERROR_BEHAVIOR); } } } } if (elt instanceof TMLReadChannel) { ch = ((TMLReadChannel)elt).getChannel(0); if (ch.isBasicChannel()) { //System.out.println("Read channel"); if (ch.getDestinationTask() != t) { addError(t, elt, ch.getName() + ": " + WRONG_DESTINATION_CHANNEL, TMLError.ERROR_BEHAVIOR); } } } if (elt instanceof TMLSendEvent) { evt = ((TMLSendEvent)elt).getEvent(); if (evt.isBasicEvent()) { //TraceManager.addDev("send evt= " + evt.getName() + " task=" + t.getName() + " origin=" + evt.getOriginTask().getName()); if (evt.getOriginTask() != t) { addError(t, elt, evt.getName() + ": " + WRONG_ORIGIN_EVENT, TMLError.ERROR_BEHAVIOR); } } } if (elt instanceof TMLWaitEvent) { evt = ((TMLWaitEvent)elt).getEvent(); if (evt.isBasicEvent()) { try { TraceManager.addDev("wait evt= " + evt.getName()); } catch (Exception e) { TraceManager.addDev("Error on evt = " + evt); } if (evt.getDestinationTask() == null) { TraceManager.addDev("Null destination task"); } TraceManager.addDev("wait evt= " + evt.getName() + " task=" + t.getName() + " destination=" + evt.getDestinationTask().getName()); if (evt.getDestinationTask() != t) { addError(t, elt, evt.getName() + ": " + WRONG_DESTINATION_EVENT, TMLError.ERROR_BEHAVIOR); } } } if (elt instanceof TMLNotifiedEvent) { evt = ((TMLNotifiedEvent)elt).getEvent(); //System.out.println("Write channel"); if (evt.getDestinationTask() != t) { addError(t, elt, evt.getName() + ": " + WRONG_DESTINATION_EVENT, TMLError.ERROR_BEHAVIOR); } } if (elt instanceof TMLSendRequest) { request = ((TMLSendRequest)elt).getRequest(); //System.out.println("Write channel"); if (!request.isAnOriginTask(t)) { addError(t, elt, request.getName() + ": " + WRONG_ORIGIN_REQUEST, TMLError.ERROR_BEHAVIOR); } } } } } public void checkActionSyntax() { TMLWaitEvent tmlwe; TMLSendEvent tmlase; TMLSendRequest tmlsr; TMLChoice choice; TMLForLoop loop; TMLEvent evt; TMLRequest req; TMLType type; TMLRandom random; int j; int elseg, afterg; TMLAttribute attr; // StringReader toParse; String action; for(TMLTask t: tmlm.getTasks()) { TMLActivity tactivity = t.getActivityDiagram(); TMLActivityElement elt; // Checking names of atrributes for(TMLAttribute attri: t.getAttributes()) { if (!TMLTextSpecification.isAValidId(attri.getName())) { addError(t, null, WRONG_VARIABLE_IDENTIFIER + ": invalid identifier", TMLError.ERROR_STRUCTURE); } } int n = tactivity.nElements(); //System.out.println("Task" + t.getName()); for(int i=0; i<n; i++) { elt = tactivity.get(i); //System.out.println("elt=" + elt); if (elt instanceof TMLActionState) { action = ((TMLActivityElementWithAction)elt).getAction(); parsingAssignment(t, elt, action); } else if (elt instanceof TMLActivityElementWithAction) { action = ((TMLActivityElementWithAction)elt).getAction(); parsing(t, elt, "actionnat", action); } else if (elt instanceof TMLActivityElementWithIntervalAction) { //System.out.println("Parsing TMLActivityElementWithIntervalAction"); action = ((TMLActivityElementWithIntervalAction)elt).getMinDelay(); parsing(t, elt, "actionnat", action); action = ((TMLActivityElementWithIntervalAction)elt).getMaxDelay(); parsing(t, elt, "actionnat", action); if (elt instanceof TMLDelay) { action = ((TMLDelay)elt).getUnit().trim(); if (!(TMLDelay.isAValidUnit(action))) { addError(t, elt, TIME_UNIT_ERROR + "in expression " + action, TMLError.ERROR_BEHAVIOR); } } } else if (elt instanceof TMLActivityElementChannel) { action = ((TMLActivityElementChannel)elt).getNbOfSamples(); parsing(t, elt, "actionnat", action); } else if (elt instanceof TMLSendEvent) { tmlase = (TMLSendEvent)elt; evt = tmlase.getEvent(); for(j=0; j<tmlase.getNbOfParams(); j++) { action = tmlase.getParam(j); if ((action != null) && (action.length() > 0)){ type = evt.getType(j); if ((type == null) || (type.getType() == TMLType.NATURAL)) { parsing(t, elt, "actionnat", action); } else { parsing(t, elt, "actionbool", action); } } } } else if (elt instanceof TMLWaitEvent) { tmlwe = (TMLWaitEvent)elt; evt = tmlwe.getEvent(); //System.out.println("Nb of params of wait event:" + tmlwe.getNbOfParams()); for(j=0; j<tmlwe.getNbOfParams(); j++) { action = tmlwe.getParam(j).trim(); if ((action != null) && (action.length() > 0)) { if (!(Conversion.isId(action))) { addError(t, elt, SYNTAX_ERROR_VARIABLE_EXPECTED + " in expression " + action, TMLError.ERROR_BEHAVIOR); } else { // Declared variable? attr = t.getAttributeByName(action); if (attr == null ) { addError(t, elt, UNDECLARED_VARIABLE + " :" + action + " in expression " + action, TMLError.ERROR_BEHAVIOR); } else { //System.out.println("Nb of params:" + tmlwe.getEvent().getNbOfParams() + " j:" + j); if (tmlwe.getEvent().getType(j).getType() == 0) { System.out.println("0"); } if (attr.getType().getType() != tmlwe.getEvent().getType(j).getType()) { TraceManager.addDev("Type0:" + attr.getType().getType() + " type1:" + tmlwe.getEvent().getType(j).getType()); addError(t, elt, VARIABLE_ERROR + " :" + action + " in expression " + action, TMLError.ERROR_BEHAVIOR); } } } } } } else if (elt instanceof TMLSendRequest) { tmlsr = (TMLSendRequest)elt; req = tmlsr.getRequest(); for(j=0; j<tmlsr.getNbOfParams(); j++) { action = tmlsr.getParam(j); if ((action != null) && (action.length() > 0)){ type = req.getType(j); if ((type == null) || (type.getType() == TMLType.NATURAL)) { parsing(t, elt, "actionnat", action); } else { parsing(t, elt, "actionbool", action); } } } } else if (elt instanceof TMLChoice) { choice = (TMLChoice)elt; elseg = choice.getElseGuard(); afterg = choice.getAfterGuard(); for(j=0; j<choice.getNbGuard(); j++) { if (!choice.isNonDeterministicGuard(j) && !choice.isStochasticGuard(j)) { if ((j!= elseg) && (j!=afterg)) { action = choice.getGuard(j); parsing(t, elt, "guard", action); } } } } else if (elt instanceof TMLForLoop) { loop = (TMLForLoop)elt; if (loop.getInit().trim().length() > 0) { parsing(t, elt, "assnat", loop.getInit()); } if (loop.getCondition().trim().length() > 0) { parsing(t, elt, "actionbool", loop.getCondition()); } if (loop.getIncrement().trim().length() > 0) { parsing(t, elt, "assnat", loop.getIncrement()); } } else if (elt instanceof TMLRandom) { random = (TMLRandom)elt; parsing(t, elt, "actionnat", random.getMinValue()); parsing(t, elt, "actionnat", random.getMaxValue()); parsing(t, elt, "natid", random.getVariable()); parsing(t, elt, "natnumeral", ""+random.getFunctionId()); } } } } public void parsingAssignment(TMLTask t, TMLActivityElement elt, String action) { int index = action.indexOf("="); if (index == -1) { addError(t, elt, SYNTAX_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR); return; } String var = action.substring(0, index).trim(); TMLAttribute attrFound = null; for(TMLAttribute attr: t.getAttributes()) { if (attr.getName().compareTo(var) == 0) { attrFound = attr; break; } } if (attrFound == null) { addError(t, elt, UNDECLARED_VARIABLE + " :" + var + " in expression " + action, TMLError.ERROR_BEHAVIOR); return; } if (attrFound.isNat()) { parsing(t, elt, "assnat", action); } else { parsing(t, elt, "assbool", action); } } /** * Parsing in two steps: * 1. Parsing the expression with no variable checking * 2. Parsing the expression with variables values to see whether variables are well-placed or not * The second parsing is performed iff the first one succeeds */ public void parsing(TMLTask t, TMLActivityElement elt, String parseCmd, String action) { if (action==null){ return; } TMLExprParser parser; SimpleNode root; // First parsing parser = new TMLExprParser(new StringReader(parseCmd + " " + action)); try { //System.out.println("\nParsing :" + parseCmd + " " + action); root = parser.CompilationUnit(); //root.dump("pref="); //System.out.println("Parse ok"); } catch (ParseException e) { //System.out.println("ParseException --------> Parse error in :" + parseCmd + " " + action); addError(t, elt, SYNTAX_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR); return; } catch (TokenMgrError tke ) { //System.out.println("TokenMgrError --------> Parse error in :" + parseCmd + " " + action); addError(t, elt, SYNTAX_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR); return; } // Second parsing // We only replace variables values after the "=" sign if (parseCmd.compareTo("natnumeral") == 0) { return; } int index = action.indexOf('='); String modif = action; if ((parseCmd.compareTo("assnat") ==0) || (parseCmd.compareTo("assbool") ==0)) { if (index != -1) { modif = action.substring(index+1, action.length()); } if (parseCmd.compareTo("assnat") ==0) { parseCmd = "actionnat"; } else { parseCmd = "actionbool"; } } if (parseCmd.compareTo("natid") == 0) { parseCmd = "natnumeral"; } for(TMLAttribute attr: t.getAttributes()) { modif = tmlm.putAttributeValueInString(modif, attr); } parser = new TMLExprParser(new StringReader(parseCmd + " " + modif)); try { //System.out.println("\nParsing :" + parseCmd + " " + modif); root = parser.CompilationUnit(); //root.dump("pref="); //System.out.println("Parse ok"); } catch (ParseException e) { //System.out.println("ParseException --------> Parse error in :" + parseCmd + " " + action); addError(t, elt, VARIABLE_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR); return; } catch (TokenMgrError tke ) { //System.out.println("TokenMgrError --------> Parse error in :" + parseCmd + " " + action); addError(t, elt, VARIABLE_ERROR + " in expression " + action, TMLError.ERROR_BEHAVIOR); return; } // Tree analysis: if the tree contains a variable, then, this variable has not been declared ArrayList<String> vars = root.getVariables(); for(String s: vars) { addError(t, elt, UNDECLARED_VARIABLE + " :" + s + " in expression " + action, TMLError.ERROR_BEHAVIOR); } } public String printSummary() { String ret = ""; if (errors.size() == 0) { ret += printWarnings(); ret += "Syntax checking: successful\n"; ret += "No error, " + warnings.size() + " warning(s)\n"; } else { ret += printErrors() + printWarnings(); ret += "Syntax checking: failed\n"; ret += errors.size() + " error(s), "+ warnings.size() + " warning(s)\n"; } return ret; } public String printErrors() { String ret = "*** ERRORS:"; for(TMLError error: errors) { ret += "ERROR / task " + error.task.getName() + " / element " + error.element.getName() + ": " + error.message + "\n"; } return ret; } public String printWarnings() { String ret = ""; for(TMLError error: warnings) { ret += "ERROR / task " + error.task.getName() + " / element: " + error.element.getName() + ": " + error.message + "\n"; } return ret; } }