package net.sf.orcc.tools.merger.actor; import static net.sf.orcc.ir.IrFactory.eINSTANCE; import java.util.ArrayList; import java.util.List; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import net.sf.orcc.OrccRuntimeException; import net.sf.orcc.df.Action; import net.sf.orcc.df.Actor; import net.sf.orcc.df.DfFactory; import net.sf.orcc.df.FSM; import net.sf.orcc.df.Network; import net.sf.orcc.df.Port; import net.sf.orcc.df.State; import net.sf.orcc.df.util.BinOpSeqParser; import net.sf.orcc.graph.Vertex; import net.sf.orcc.ir.Expression; import net.sf.orcc.ir.ExprBinary; import net.sf.orcc.ir.Instruction; import net.sf.orcc.ir.IrFactory; import net.sf.orcc.ir.OpBinary; import net.sf.orcc.ir.OpUnary; import net.sf.orcc.ir.Var; import net.sf.orcc.util.DomUtil; import net.sf.orcc.util.OrccLogger; import org.eclipse.emf.ecore.util.EcoreUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * This class parses a given superactor definition in an XML file * * @author Jani Boutellier * @author Ghislain Roquier * */ public class SuperactorParser { private List<Schedule> scheduleList; private Network network; private Actor superActor; private FSM fsm; private List<Action> guardList; /** * This class defines a parser of XDF expressions. * * @author Matthieu Wipliez * */ private class ExprParser { private Action thisGuard; /** * Parses the given node as an expression and returns the matching * Expression expression. * * @param node * a node whose expected to be, or whose sibling is expected * to be, a DOM element named "Expr". * @return an expression */ public Expression parseExpr(Action thisGuard, Node node) { this.thisGuard = thisGuard; ParseContinuation<Expression> cont = parseExprCont(node); Expression expr = cont.getResult(); if (expr == null) { throw new OrccRuntimeException("Expected an Expr element"); } else { if(expr.isExprBinary()) { ((ExprBinary)expr).setType(IrFactory.eINSTANCE.createTypeBool()); } return expr; } } /** * Parses the given node as a binary operator and returns a parse * continuation with the operator parsed. * * @param node * a node that is expected, or whose sibling is expected, to * be a DOM element named "Op". * @return a parse continuation with the operator parsed */ private ParseContinuation<OpBinary> parseExprBinaryOp(Node node) { while (node != null) { if (node.getNodeName().equals("Op")) { Element op = (Element) node; String name = op.getAttribute("name"); return new ParseContinuation<OpBinary>(node, OpBinary.getOperator(name)); } node = node.getNextSibling(); } return new ParseContinuation<OpBinary>(node, null); } /** * Parses the given node and its siblings as a sequence of binary * operations, aka "BinOpSeq". A BinOpSeq is a sequence of expr, op, * expr, op, expr... * * @param node * the first child node of a Expr kind="BinOpSeq" element * @return a parse continuation with a BinaryExpr */ private ParseContinuation<Expression> parseExprBinOpSeq(Node node) { List<Expression> expressions = new ArrayList<Expression>(); List<OpBinary> operators = new ArrayList<OpBinary>(); ParseContinuation<Expression> contE = parseExprCont(node); expressions.add(contE.getResult()); while (node != null) { ParseContinuation<OpBinary> contO = parseExprBinaryOp(node); OpBinary op = contO.getResult(); node = contO.getNode(); if (op != null) { operators.add(op); contE = parseExprCont(node); Expression expr = contE.getResult(); if (expr == null) { throw new OrccRuntimeException( "Expected an Expr element"); } expressions.add(expr); node = contE.getNode(); } } Expression expr = BinOpSeqParser.parse(expressions, operators); return new ParseContinuation<Expression>(node, expr); } /** * Parses the given node as an expression and returns the matching * Expression expression. * * @param node * a node whose sibling is expected to be a DOM element named * "Expr". * @return an expression */ private ParseContinuation<Expression> parseExprCont(Node node) { Expression expr = null; while (node != null) { if (node.getNodeName().equals("Expr")) { Element elt = (Element) node; String kind = elt.getAttribute("kind"); if (kind.equals("BinOpSeq")) { return parseExprBinOpSeq(elt.getFirstChild()); } else if (kind.equals("Literal")) { expr = parseExprLiteral(elt); break; } else if (kind.equals("List")) { List<Expression> exprs = parseExprs(node .getFirstChild()); expr = IrFactory.eINSTANCE.createExprList(exprs); break; } else if (kind.equals("UnaryOp")) { ParseContinuation<OpUnary> cont = parseExprUnaryOp(node .getFirstChild()); OpUnary op = cont.getResult(); Expression unaryExpr = parseExpr(thisGuard, cont.getNode()); expr = IrFactory.eINSTANCE.createExprUnary(op, unaryExpr, null); break; } else if (kind.equals("Var")) { String name = elt.getAttribute("name"); // look up variable, in variables scope, and if not // found in parameters scope Var var = findStateVar(network, name, thisGuard); if (var == null) { var = findPortVariable(network, name, thisGuard); } if (var == null) { throw new OrccRuntimeException("In superactor \"" + network.getName() + "\": unknown variable: \"" + name + "\""); } expr = IrFactory.eINSTANCE.createExprVar(var); break; } else { throw new OrccRuntimeException("In network \"" + network.getName() + "\": Unsupported Expr kind: \"" + kind + "\""); } } node = node.getNextSibling(); } return new ParseContinuation<Expression>(node, expr); } /** * Parses the given "Expr" element as a literal and returns the matching * Expression expression. * * @param elt * a DOM element named "Expr" * @return an expression */ private Expression parseExprLiteral(Element elt) { String kind = elt.getAttribute("literal-kind"); String value = elt.getAttribute("value"); if (kind.equals("Boolean")) { return IrFactory.eINSTANCE.createExprBool(Boolean .parseBoolean(value)); } else if (kind.equals("Character")) { throw new OrccRuntimeException("Characters not supported yet"); } else if (kind.equals("Integer")) { return IrFactory.eINSTANCE.createExprInt(Long.parseLong(value)); } else if (kind.equals("Real")) { return IrFactory.eINSTANCE.createExprFloat(Float .parseFloat(value)); } else if (kind.equals("String")) { return IrFactory.eINSTANCE.createExprString(value); } else { throw new OrccRuntimeException("Unsupported Expr " + "literal kind: \"" + kind + "\""); } } private List<Expression> parseExprs(Node node) { List<Expression> exprs = new ArrayList<Expression>(); while (node != null) { if (node.getNodeName().equals("Expr")) { exprs.add(parseExpr(thisGuard, node)); } node = node.getNextSibling(); } return exprs; } /** * Parses the given node as a unary operator and returns a parse * continuation with the operator parsed. * * @param node * a node that is expected, or whose sibling is expected, to * be a DOM element named "Op". * @return a parse continuation with the operator parsed */ private ParseContinuation<OpUnary> parseExprUnaryOp(Node node) { while (node != null) { if (node.getNodeName().equals("Op")) { Element op = (Element) node; String name = op.getAttribute("name"); return new ParseContinuation<OpUnary>(node, OpUnary.getOperator(name)); } node = node.getNextSibling(); } throw new OrccRuntimeException("Expected an Op element"); } } /** * This class defines a parse continuation, by storing the next node that * shall be parsed along with the result already computed. * * @author Matthieu Wipliez * */ private static class ParseContinuation<T> { final private Node node; final private T result; /** * Creates a new parse continuation with the given DOM node and result. * The constructor stores the next sibling of node. * * @param node * a node that will be used to resume parsing after the * result has been stored * @param result * the result */ public ParseContinuation(Node node, T result) { if (node == null) { this.node = null; } else { this.node = node.getNextSibling(); } this.result = result; } /** * Returns the node stored in this continuation. * * @return the node stored in this continuation */ public Node getNode() { return node; } /** * Returns the result stored in this continuation. * * @return the result stored in this continuation */ public T getResult() { return result; } } public SuperactorParser(Network network, Actor superActor) { this.network = network; this.superActor = superActor; this.scheduleList = new ArrayList<Schedule>(); this.fsm = DfFactory.eINSTANCE.createFSM(); this.guardList = new ArrayList<Action>(); } public void parse(String definitionFile) { try { InputStream is = new FileInputStream(definitionFile); Document document = DomUtil.parseDocument(is); parseSuperactorList(document); is.close(); } catch (IOException e) { e.printStackTrace(); } } public List<Schedule> getScheduleList() { return scheduleList; } public FSM getFSM() { return fsm; } public List<Action> getGuardList() { return guardList; } private void parseSuperactorList(Document doc) { Element root = doc.getDocumentElement(); Node node = root.getFirstChild(); while (node != null) { if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; if (node.getNodeName().equals("superactor") && element.getAttribute("name").equals(network.getName())) { parseSuperactor(element, element.getAttribute("name")); parseFSM(element); } } node = node.getNextSibling(); } } private void parseSuperactor(Element element, String superactor) { Node node = element.getFirstChild(); while (node != null) { if (node instanceof Element) { Element elt = (Element) node; if (elt.getTagName().equals("superaction")) { Action guard = DfFactory.eINSTANCE.createAction(); guard.setBody(eINSTANCE.createProcedure("isSchedulable_" + elt.getAttribute("name"), 0, eINSTANCE.createTypeBool())); guard.setPeekPattern(DfFactory.eINSTANCE.createPattern()); Schedule schedule = new Schedule(); schedule.setName(elt.getAttribute("name")); schedule.setOwner(superactor); parseSuperaction(guard, schedule, elt); scheduleList.add(schedule); guard.addAttribute("actorName"); guard.setAttribute("actorName", network.getName()); guard.addAttribute("actionName"); guard.setAttribute("actionName", elt.getAttribute("name")); guardList.add(guard); } if (elt.getTagName().equals("regfeedback")) { superActor.addAttribute("RegisterFeedback"); } } node = node.getNextSibling(); } } private void parseSuperaction(Action guard, Schedule schedule, Element element) { Expression guardExpression = null; Node node = element.getFirstChild(); while (node != null) { if (node instanceof Element) { Element elt = (Element) node; if (elt.getTagName().equals("iterand")) { Vertex vertex = findActor(elt.getAttribute("actor")); if (vertex != null) { Actor actor = vertex.getAdapter(Actor.class); addActorActionToSchedule(schedule, actor, findAction(actor, elt.getAttribute("action")), Integer.parseInt(elt.getAttribute("repetitions"))); } else { OrccLogger.warnln("unknown actor " + elt.getAttribute("actor") + " in schedule"); } } if (node.getNodeName().equals("guard")) { Expression expr = parseGuard(guard, elt); if (expr != null) { Var resultVar = guard.getBody().newTempLocalVariable( IrFactory.eINSTANCE.createTypeBool(), "result"); guard.getBody().getLast().add( IrFactory.eINSTANCE.createInstAssign( resultVar, expr)); guardExpression = IrFactory.eINSTANCE.createExprVar(resultVar); } else { guardExpression = null; } } } node = node.getNextSibling(); } if (guardExpression == null) { guardExpression = IrFactory.eINSTANCE.createExprBool(true); } guard.getBody().getLast().add( IrFactory.eINSTANCE.createInstReturn(guardExpression)); } /* * Schedule parsing specific methods */ private Vertex findActor(String id) { for (Vertex vertex : network.getVertices()) { if(vertex.getLabel().equals(id)) return vertex; } return null; } private Action findAction(Actor actor, String id) { for(Action action : actor.getActions()) { if (action.getName().equals(id)) { return action; } } for(Action action : actor.getInitializes()) { if (action.getName().equals(id)) { return action; } } OrccLogger.warnln("action " + id + " not found in actor " + actor.getName()); return null; } private void addActorActionToSchedule(Schedule schedule, Actor actor, Action action, int rep) { if ((actor != null) && (action != null)) { for (int i = 0; i < rep; i++) { schedule.add(new Iterand(action)); } } } /* * FSM parsing specific methods */ private void parseFSM(Element element) { String initialStateName = null; Node node = element.getFirstChild(); while (node != null) { if (node instanceof Element) { Element elt = (Element) node; if (node.getNodeName().equals("fsm")) { initialStateName = elt.getAttribute("initial"); parseTransitions(elt); } } node = node.getNextSibling(); } setInitialState(initialStateName); } private void setInitialState(String initialState) { State initial = MergerUtil.findState(fsm, initialState); if (initial != null) { fsm.setInitialState(initial); } else { throw new OrccRuntimeException("Trying to set " + initialState + " as initial state, but the state does not exist"); } } private void parseTransitions(Element element) { Node node = element.getFirstChild(); while (node != null) { if (node instanceof Element) { Element elt = (Element) node; if (elt.getTagName().equals("transition")) { fsm.addTransition( addStateIfMissing(fsm, elt.getAttribute("src")), addSuperactionIfMissing(superActor, elt.getAttribute("action")), addStateIfMissing(fsm, elt.getAttribute("dst"))); } } node = node.getNextSibling(); } } private Action addSuperactionIfMissing(Actor actor, String actionName) { Action action = MergerUtil.findAction(actor, actionName); if (action == null) { action = DfFactory.eINSTANCE.createAction(); action.setTag(DfFactory.eINSTANCE.createTag(actionName)); superActor.getActions().add(action); } return action; } private State addStateIfMissing(FSM fsm, String stateName) { State state = MergerUtil.findState(fsm, stateName); if(state == null) { state = (State) DfFactory.eINSTANCE.createState(); state.setName(stateName); fsm.getStates().add(state); } return state; } /* * Guard parser specific methods */ private Var createIndexVar(Port port) { return IrFactory.eINSTANCE.createVar(0, EcoreUtil.copy(port.getType()), new String("index_" + port.getName()), true, 0); } private Instruction createPeekInstruction(Var target, Port port) { Var indexVar = createIndexVar(port); return IrFactory.eINSTANCE.createInstLoad(target, IrFactory.eINSTANCE .createVar(0, EcoreUtil.copy(port.getType()), "tokens_" + port.getName(), true, 0), MergerUtil.createModuloIndex(port, indexVar)); } private Expression parseGuard(Action guard, Element element) { Node node = element.getFirstChild(); while (node != null) { if (node.getNodeType() == Node.ELEMENT_NODE) { return new ExprParser().parseExpr(guard, node); } node = node.getNextSibling(); } return null; } private Var findStateVar(Network network, String name, Action guard) { for (Actor actor : network.getAllActors()) { for (Var var : actor.getStateVars()) { if (var.getName().equals(name)) { Var peekVar = guard.getBody().newTempLocalVariable( EcoreUtil.copy(var.getType()), name + "_peek"); guard.getBody().getLast().add(IrFactory.eINSTANCE.createInstLoad(peekVar, var)); return peekVar; } } } return null; } private Var findPortVariable(Network network, String name, Action guard) { for (Actor actor : network.getAllActors()) { for (Port port : actor.getInputs()) { if (port.getName().equals(name)) { return addToPeekList(guard, port); } } } return null; } private Var addToPeekList(Action guard, Port port) { createVariableIntroduction(guard, port); Var peekVar = IrFactory.eINSTANCE.createVar(0, EcoreUtil.copy(port.getType()), new String(port.getName() + "_peek"), true, 0); guard.getBody().getLast().add( createPeekInstruction(peekVar, port)); guard.getPeekPattern().setNumTokens(port, 1); guard.getPeekPattern().setVariable(port, peekVar); return peekVar; } private void createVariableIntroduction(Action guard, Port port) { guard.getBody().newTempLocalVariable(IrFactory.eINSTANCE.createTypeInt(32), port.getName() + "_peek"); } }