/******************************************************************************* * Copyright (c) 2008 SAP * see https://research.qkal.sap.corp/mediawiki/index.php/CoMONET * * Date: $Date: 2010-05-20 15:12:26 +0200 (Do, 20 Mai 2010) $ * @version $Revision: 9718 $ * @author: $Author: c5106462 $ *******************************************************************************/ package com.sap.furcas.parsergenerator.tcs.t2m.grammar; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import com.sap.furcas.metamodel.FURCAS.TCS.Associativity; import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.Keyword; import com.sap.furcas.metamodel.FURCAS.TCS.Literal; import com.sap.furcas.metamodel.FURCAS.TCS.Operator; import com.sap.furcas.metamodel.FURCAS.TCS.OperatorList; import com.sap.furcas.metamodel.FURCAS.TCS.OperatorTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.Priority; import com.sap.furcas.metamodel.FURCAS.TCS.PropertyReference; import com.sap.furcas.parsergenerator.tcs.t2m.grammar.rules.ClassProductionRule; import com.sap.furcas.parsergenerator.util.VarStringBuffer; import com.sap.furcas.runtime.common.exceptions.MetaModelLookupException; import com.sap.furcas.runtime.common.exceptions.SyntaxElementException; import com.sap.furcas.runtime.parser.exceptions.SyntaxParsingException; import com.sap.furcas.runtime.tcs.TemplateNamingHelper; /** * The Class OperatorHandler. */ public class OperatorHandler { /** The writer. */ ANTLR3GrammarWriter writer; private final TemplateNamingHelper<?> namingHelper; private final SemanticErrorBucket errorBucket; /** * Instantiates a new operator handler. * * @param writer the writer * @param syntaxLookup the syntax lookup * @param metaLookup * @param namingHelper * @param errorBucket */ protected OperatorHandler(ANTLR3GrammarWriter writer, TemplateNamingHelper<?> namingHelper, SemanticErrorBucket errorBucket) { super(); this.writer = writer; this.namingHelper = namingHelper; this.errorBucket = errorBucket; } /** * @param handlerConfig */ public OperatorHandler(SyntaxElementHandlerConfigurationBean<?> handlerConfig) { this(handlerConfig.getWriter(), handlerConfig.getNamingHelper(), handlerConfig.getErrorBucket()); } /** * Adds production rules to the grammar for this classtemplate and the given operatorList. * Adding operator lists is complex, because operators in grammars are complex. For each operator, * we need to consider * priority (i.e. * higher than +), * arity (i.e. NOT(x) is unary, (x OR y) is binary, * associativity ((8/2)/2=2) vs (8/(2/2)=8), while 8/2/2 is ambiguous without associativity * * The general idea is that for each priority a production rule ({prefix}_)?priority_{value} is created, where the * highest vale means highest priority, and this production rule is called first during parsing. Any production rule * then either is present by means of the operator symbol, or it calls the next priority operator rule. Only after no * operator in this chain is present does the production rule for the operatored class production rule get called. So * as an example, parsing "3" with 3 being of operatored class integer having 4 priorities of operators would invoke these rules: * integer -> priority_3 -> priority_2 -> priority_1 -> priority_0 -> primary_integer * * Priority rules for binary operators will look like this ( ( ret=primary_element -- first element to be parsed, can be recursive operatored expression (in brackets) or atomic expression without operator ((SYMBOL {opName = "*";} ((ret=atomicElement[opName, ret, firstToken]))))* -- Loop (implicit recursion over variable ret!): Any next element will have the previous "ret" as left side ) ) * * * * Apart from that there are technical concerns about TCS to consider here, i.e. references between operators and OperatorTemplates. * * @param opList the op list * @param classTemplateName the class template name * @param hasPrimaries * @param ruleBodyBufferFactory * @param template * @throws SyntaxParsingException */ public void addOperatorList(OperatorList opList, String classTemplateName, boolean hasPrimaries, RuleBodyBufferFactory ruleBodyBufferFactory, ClassTemplate template) { Collection<Priority> prios = opList.getPriorities(); if (prios == null) { throw new IllegalArgumentException("OperatorList has null as Priorities List"); } String prefix = getPriorityPrefix(opList); // we can assume the following (need to check validity for better errors?) // Priorities begin with 0 and go up always +1. We cannot assume the order here, but there won't be missing numbers, // else the TCS definition is invalid. // add one rule per priority in list for (Priority priority : prios) { VarStringBuffer rulebody = new VarStringBuffer(); String calledRule = null; if (priority.getValue() > 0) { calledRule = prefix + "priority_" + (priority.getValue() - 1); } else { if (hasPrimaries) { calledRule = "primary_"; if (classTemplateName != null) { calledRule += classTemplateName.toLowerCase(); } else { // the syntax is invalid in that case, but we dealt with that earlier, so let is pass here } } } String associatedCallRule; boolean isLeftAssociative; if (priority.getAssociativity() == null || priority.getAssociativity() == Associativity.LEFT) { isLeftAssociative = true; associatedCallRule = calledRule; } else { isLeftAssociative = false; associatedCallRule = prefix + "priority_" + (priority.getValue()); // call same priority again } appendOperatorAlternatives(rulebody, priority, calledRule, associatedCallRule, isLeftAssociative, ruleBodyBufferFactory, template); rulebody.append("\n{\n").append("this.setLocationAndComment(ret, firstToken);\n").append("ret2=ret;\n }"); String initString = "java.lang.String opName=null; org.antlr.runtime.Token firstToken=input.LT(1); Object semRef=null;"; ClassProductionRule rule = ClassProductionRule.getClassTemplateProductionRule(prefix + "priority_" + priority.getValue(), "Object ret2", initString, rulebody.toString(), false, true); writer.addRule(rule); } } /** * negotiates the same usage of prefixes between classTemplateHandler and operatorHandler * @param opList * @return */ public static String getPriorityPrefix(OperatorList opList) { String prefix; if (opList != null && opList.getName() != null) { prefix = opList.getName().toLowerCase() + '_'; } else { prefix = ""; } return prefix; } /** * within the priority production rule, operators rule calls and injector calls are required, this method deals with that. * * @param rulebody the rulebody * @param priority the priority * @param nextCalledRule the rule to call in case there is no operator of this priority (fall through) * @param associatedCalledRule the rule to call to allow for associativity (a + b + c + ...) * @param isLeftAssociative the is left associative * @param ruleBodyBufferFactory * @param template * @throws SyntaxParsingException */ private void appendOperatorAlternatives(VarStringBuffer rulebody, Priority priority, String nextCalledRule, String associatedCalledRule, boolean isLeftAssociative, RuleBodyBufferFactory ruleBodyBufferFactory, ClassTemplate template) { rulebody.append(" ("); // b1 Collection<Operator> operators = priority.getOperators(); if (operators == null || operators.size() == 0) { rulebody.append("ret=").append(nextCalledRule); // fallthrough for priorities without operators } else { // sort operators according to their arity, first treat arity=1, then arity=2 (ignore other arities as done in ATL) List<Operator> unaryOperators = new ArrayList<Operator>(operators.size()); List<Operator> binaryOperators = new ArrayList<Operator>(operators.size()); splitOperators(operators, unaryOperators, binaryOperators); // binary operators have a different rulebody than unary ones, as they can repeat (a + b + c + d...) if (unaryOperators.size() > 0) { List<Operator> nonPostFixUnaryOperators = new ArrayList<Operator>(operators.size()); for (Operator operator : unaryOperators) { if (!operator.isPostfix()) { nonPostFixUnaryOperators.add(operator); } } if (nonPostFixUnaryOperators.size() > 0) { appendOperatorsList(rulebody, nonPostFixUnaryOperators, associatedCalledRule, isLeftAssociative, 1, ruleBodyBufferFactory, template); rulebody.append("\n|\n"); } } // in any case we need a reference to the nextCalled Rule. // In case of unary operators, this is an alternative in itself, // in case of binary operators, this would be the left side. rulebody.append(" ( ret=", nextCalledRule); // b2 //now all postfix unary operators if (unaryOperators.size() > 0) { List<Operator> postFixUnaryOperators = new ArrayList<Operator>(operators.size()); for (Operator operator : unaryOperators) { if (operator.isPostfix()) { postFixUnaryOperators.add(operator); } } if (postFixUnaryOperators.size() > 0) { rulebody.append("\n)(\n"); //b2 appendOperatorsList(rulebody, postFixUnaryOperators, associatedCalledRule, isLeftAssociative, 1, ruleBodyBufferFactory, template); rulebody.append("\n|\n"); } } // now append all the possible operators + right sides to rule body, if any if (binaryOperators.size() > 0) { appendOperatorsList(rulebody, binaryOperators, associatedCalledRule, isLeftAssociative, 2, ruleBodyBufferFactory, template); // '*' or '?' depends on associativity of Priority } rulebody.append(')'); // b2 } // end if more than zero operators in priority rulebody.append(')'); // b1 } /** * splits operators in two lists according to their arity * @param operators * @param unaryOperators * @param binaryOperators */ private void splitOperators(Collection<Operator> operators, List<Operator> unaryOperators, List<Operator> binaryOperators) { for (Operator operator : operators) { if (operator.getArity() == 1) { unaryOperators.add(operator); } else if (operator.getArity() == 2) { binaryOperators.add(operator); } else { errorBucket.addError("Operator with illegal arity: " + operator.getName() + " has arity " + operator.getArity(), operator); } if (operator.isPostfix() && operator.getArity() > 1) { errorBucket.addError("Postfix notation for arity > 1 not implemented yet.", operator); } } } /** * this method adds to the rulebody for each operator in the given list. * * @param rulebody the rulebody * @param operators the operators * @param associativityCalledRule the associativity called rule * @param isLeftAssociative * @param ruleBodyBufferFactory * @param template * @throws SyntaxParsingException */ private void appendOperatorsList(VarStringBuffer targetrulebody, List<Operator> operators, String associativityCalledRule, boolean isLeftAssociative, int arity, RuleBodyBufferFactory ruleBodyBufferFactory, ClassTemplate template) { try { VarStringBuffer rulebody = new VarStringBuffer(); boolean hasAddedOperator = false; // used to decide about parentheses, none if nothing is added for (Operator operator : operators) { // don't create anything if operator has no operator Template (non critical case? Maybe warning would be good.) Collection<OperatorTemplate> opTemplateList //= (Collection<OperatorTemplate>) operator.refGetValue("templates"); = operator.getTemplates(); if (opTemplateList.size() == 0) { // operator is in list, but never used in any operatorTemplate, so ignore it errorBucket.addWarning("Operator not used by any template.", operator); continue; } if (hasAddedOperator) { rulebody.append("\n| "); } hasAddedOperator = true; Literal literal = operator.getLiteral(); if (literal == null) { throw new SyntaxElementException("Operator does not have a literal " + operator, operator); // literal = syntaxLookup.getLiteralForOperator(operator); } if (arity == 2 || isLeftAssociative) { // don't add here if unary and right associative String literalValue = null; if (literal instanceof Keyword) { // Keywords being used by their value, as they are not declared as named symbols //add synpred to cope with recursion and the empty alternative literalValue = "'" + literal.getValue() + "'"; } else { //add synpred to cope with recursion and the empty alternative literalValue = literal.getName().toUpperCase(); } rulebody.append("(", literalValue); boolean first = true; boolean addedAtLeastOne = false; List<String> synpreds = new ArrayList<String>(opTemplateList.size()); for (OperatorTemplate operatorTemplate : opTemplateList) { if (operatorTemplate.getDisambiguateV3() != null && !synpreds.contains(operatorTemplate.getDisambiguateV3())) { if (first) { rulebody.append("("); } addedAtLeastOne = true; synpreds.add(operatorTemplate.getDisambiguateV3()); if (!first) { rulebody.append("|"); } else { first = false; } rulebody.append("(", operatorTemplate.getDisambiguateV3(), ")"); } } if (addedAtLeastOne) { rulebody.append(")"); } rulebody.append(")=>"); } rulebody.append('('); // b2 required to separate operators, if many rulebody.append(ObservationDirectivesHelper.getEnterOperatorSequenceNotification( operator.getLiteral().getValue(), arity, operator.isPostfix())); if (arity == 2 || isLeftAssociative) { // don't add here if unary and right associative //FIXME: have a seperate notification for operators? Or is OperatorSequence enough? rulebody.append(ObservationDirectivesHelper.getEnterSequenceElementNotification(null)); appendOperatorLiteralBit(rulebody, literal); rulebody.append(ObservationDirectivesHelper.getExitSequenceElementNotification()); } rulebody.append('('); // b3 / required to separate templates if many SemanticDisambiguateHandler semanticHandler = new SemanticDisambiguateHandler(opTemplateList, errorBucket, namingHelper); boolean addedSemanticDisambiguateRule = false; for (Iterator<OperatorTemplate> iterator2 = opTemplateList.iterator(); iterator2.hasNext();) { OperatorTemplate opTemplate = iterator2.next(); // if (opTemplate == null || opTemplate.getNames() == null || namingHelper.getRuleName(opTemplate) == null) { // // TODO: Can this ever happen after parsing the syntax? Probably not. // throw new RuntimeException("Inconsistent OperatorTemplate either null or name == null " + opTemplate); // } /* * if there are 2 operands (arity = 2), the expression for both operands is first created with * the first operand, then the second operand is parsed and set. For unary operators, there * is just one operand, so the containig expression is created with null as first operand, and * then the operand is set. */ if (arity == 2) { if (semanticHandler.shouldUseSemanticDisambiguate(opTemplate)) { addedSemanticDisambiguateRule = semanticHandler.addSemanticDisambiguateRule(opTemplate, rulebody, ruleBodyBufferFactory, template, namingHelper.getMetaTypeListParameter(template), addedSemanticDisambiguateRule); if (iterator2.hasNext()) { rulebody.append("\n| "); } } else { if (opTemplate.getDisambiguateV3() != null) { // add disambiguation rule rulebody.append("(" + opTemplate.getDisambiguateV3() + ")=>"); } rulebody.append("(ret=", namingHelper.getRuleName(opTemplate), "[opName, ret, firstToken]"); // auch f�r sem pr�dikat am ende erzeugen String storeRightTo = getRightSideStorageName(opTemplate); if (storeRightTo != null) { // is this ever // possible? rulebody.append("right=", associativityCalledRule); rulebody.append(" {setProperty(ret, \"", storeRightTo, "\", right);\n", "this.setLocationAndComment(ret, firstToken);\n", "}"); } rulebody.append(')'); // if (iterator2.hasNext()) { // rulebody.append("\n| "); // } } } else { // arity == 1, unary templates don't have a StoreRightTo attribute, because they are unary. if (opTemplate.getDisambiguateV3() != null) { // add disambiguation rule rulebody.append("(" + opTemplate.getDisambiguateV3() + ")=>"); } if (operator.isPostfix()) { rulebody.append("(ret=", namingHelper.getRuleName(opTemplate), "[opName, ret, firstToken]"); } else { rulebody.append("(ret=", namingHelper.getRuleName(opTemplate), "[opName, null, firstToken]"); } if (!isLeftAssociative) { // add here if unary and right associative rulebody.append(ObservationDirectivesHelper.getEnterSequenceElementNotification(null)); appendOperatorLiteralBit(rulebody, literal); rulebody.append(ObservationDirectivesHelper.getExitSequenceElementNotification()); } if (!operator.isPostfix()) { rulebody.append("right=", associativityCalledRule); rulebody.append(" {setProperty(ret, \"", getSourceStorageName(opTemplate), "\", right);\n"); } else { rulebody.append("{"); } rulebody.append("this.setLocationAndComment(ret, firstToken);\n", "})"); } if (iterator2.hasNext()) { rulebody.append("\n| "); } } // end for operatorTemplates of operator rulebody.append(')'); // b3 rulebody.append(ObservationDirectivesHelper.getExitOperatorSequenceNotification()); rulebody.append(')'); // b2 // if (iterator.hasNext()) { // rulebody.append("\n| "); // } } // end for operators if (hasAddedOperator) { targetrulebody.append('('); // bx targetrulebody.append(rulebody); if (arity == 2) { if (isLeftAssociative) { targetrulebody.append(")*"); // bx } else { targetrulebody.append(")?"); // bx } } else { targetrulebody.append(")"); // bx } } // end if added anything } catch (SyntaxElementException e) { errorBucket.addException(e); } catch (MetaModelLookupException e) { errorBucket.addException(new SyntaxElementException(e.getMessage(), null, e)); } } private static void appendOperatorLiteralBit(VarStringBuffer rulebody, Literal literal) { if (literal instanceof Keyword) { // Keywords being used by their value, as they are not declared as named symbols rulebody.append('\'', literal.getValue(), '\''); } else { rulebody.append(literal.getName().toUpperCase(), ' '); } rulebody.append("{opName = \"", literal.getValue(), "\";}"); } /** * @param template * @return */ private static String getRightSideStorageName(OperatorTemplate template) { if (template != null) { PropertyReference propRef = template.getStoreRightSideTo(); if (propRef != null) { if (propRef.getStrucfeature() != null) { return propRef.getStrucfeature().getName(); } else { return propRef.getName(); } } } return null; } /** * @param template * @return */ private static String getSourceStorageName(OperatorTemplate template) { if (template != null) { PropertyReference propRef = template.getStoreLeftSideTo(); if (propRef != null) { // may happen with stubs if (propRef.getStrucfeature() != null) { return propRef.getStrucfeature().getName(); } else { return propRef.getName(); } } } return null; } }