package translating; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import configuration.ASPSolver; import configuration.Settings; import parser.ASTaggregateElement; import parser.ASTatom; import parser.ASTbody; import parser.ASTchoice_element; import parser.ASTdisplay; import parser.ASTextendedNonRelAtom; import parser.ASTextendedSimpleAtomList; import parser.ASTnonRelAtom; import parser.ASTpredSymbol; import parser.ASTprogram; import parser.ASTprogramRule; import parser.ASTprogramRules; import parser.ASTsimpleAtom; import parser.ASTsortDefinitions; import parser.ASTsortExpression; import parser.ASTsymbolicConstant; import parser.ASTsymbolicFunction; import parser.ASTsymbolicTerm; import parser.ASTterm; import parser.ASTtermList; import parser.ASTunlabeledProgramCrRule; import parser.ASTunlabeledProgramRule; import parser.Node; import parser.ParseException; import parser.SimpleNode; import parser.SparcTranslator; import parser.SparcTranslatorTreeConstants; import sorts.BuiltIn; import sorts.CurlyBracketsExpander; import translating.InstanceGenerator.GSort; import utilities.Pair; import warnings.ExpandSolve; import warnings.Formula; import warnings.RuleReducer; import warnings.StringListUtils; import warnings.WarningRuleCreator; public class Translator { // mapping from sort names to sort expressions assigned to the sorts private HashMap<String, ASTsortExpression> sortNameToExpression; // mapping from predicate names to a list of names of sorts describing private HashMap<String, ArrayList<String>> predicateArgumentSorts; // consistency restoring rule labels private HashSet<String> ruleLabels; // renaming of original sort names to their ASP translation private HashMap<String,String> sortRenaming; private Writer out;// output private String inputFileName;// name of file being parsed( for error // reporting) private SparcTranslator mainTranslator; // name of atoms to be added for generating combinations of cr-rules final String crAuxAtomName = "appl"; // id of cr-rule label, used for generating unique labels int labelId; private InstanceGenerator gen; // count of choice rules and aggregate elements private int localElemCount = 0; private StringBuilder translatedOutput; private LocalVariableRenamer renamer; // flags indicating whether or not warnings need to be generated private boolean generateASPWarnings; private boolean generateClingconWarnings; private RuleReducer ruleReducer; /** * Constructor * * @param sortNameToExpression * @param predicateArgumentSorts * @param ruleLabels * @param out * @param mainTranslator */ public Translator( Writer out, SparcTranslator mainTranslator, InstanceGenerator gen, boolean generateASPWarnings, boolean generateClingconWarnings) { this.mainTranslator = mainTranslator; this.sortNameToExpression = mainTranslator.sortNameToExpression; this.predicateArgumentSorts = mainTranslator.predicateArgumentSorts; this.ruleLabels = mainTranslator.crRuleLabels; this.out = out; this.gen = gen; renamer = new LocalVariableRenamer(); this.generateASPWarnings = generateASPWarnings; this.generateClingconWarnings = generateClingconWarnings; if(generateClingconWarnings) { ruleReducer=new RuleReducer(sortNameToExpression, predicateArgumentSorts, gen); } } /** * Input file name setter (file name is used for error reporting) * * @param inputFileName */ public void setInputFileName(String inputFileName) { this.inputFileName = inputFileName; } /** * Translate program given by means of Abstract syntax tree node * * @param program * to be translated. * @param generatingSorts * sorts from which all instances will be generating(those which * occur in program rules explicitly). * @param writeWarningsToSTDERR * flag, errors are written to stderr if it is true and ignored * otherwise. * @throws ParseException * if sort of some unsafe variable cannot be determined (may be * the case in aggregate or choice rules where there is no * constraints for variable in the body). */ public String translateProgram(ASTprogram program, HashSet<String> generatingSorts, HashMap<String,String> sortsRenaming, boolean writeWarningsToSTDERR) throws ParseException { this.sortRenaming = sortsRenaming; translatedOutput = new StringBuilder(); localElemCount = 0; labelId = 0; // if we need warnings, we need to shift curlyBrackets: if(generateClingconWarnings) { CurlyBracketsExpander cExpander= new CurlyBracketsExpander(sortNameToExpression); cExpander.ExpandCurlyBrackets((ASTsortDefinitions)program.jjtGetChild(0)); } // generate sorts for (String s : generatingSorts) { gen.addSort(s, sortNameToExpression.get(s), true); } writeDirectives(program); translateRules((ASTprogramRules) program.jjtGetChild(2),writeWarningsToSTDERR); // translate display: HashSet<String> usedNames = new HashSet<String>(); usedNames.addAll(sortRenaming.values()); usedNames.addAll(predicateArgumentSorts.keySet()); // append instances of generating sorts to the resulting program store for (GSort sort : gen.generatedSorts) { for (String instance : sort.instances) { String name = sort.sortName; if(sortsRenaming.containsKey(name)) name = sortRenaming.get(name); appendStringToTranslation(name); appendStringToTranslation("("); appendStringToTranslation(instance); appendStringToTranslation(")."); appendNewLineToTranslation(); } } if(program.jjtGetNumChildren()>3) translateDisplay(usedNames, (ASTdisplay) program.jjtGetChild(3)); // write warnings if the flag was set to true if (writeWarningsToSTDERR) { for (String warning : mainTranslator.getWarnings()) { System.err.println("%WARNING: " + warning); } } else { StringBuilder warningStrings = new StringBuilder(); // warningsStrings. warningStrings.append("%WARNINGS"); for (String warning : mainTranslator.getWarnings()) { warningStrings.append(" %WARNING: " + warning); } if (mainTranslator.getWarnings().size() > 0) { throw new ParseException(warningStrings.toString()); } } //System.out.println(translatedOutput.toString()); if(Settings.getSolver()== ASPSolver.Clingo) { addClingoOptimizations(); } return translatedOutput.toString(); } /** * The method extends translatedOutput with some show statements * so that the output of the solver is restricted */ private void addClingoOptimizations() { String commandLineQuery = Settings.getCommandLineQuery(); if(commandLineQuery != null) { // todo: compute the arity later translatedOutput.append("#show " + commandLineQuery + "/0." ); appendNewLineToTranslation(); } //translatedOutput.append(b) } /** * Takes display section and expands shorthands #s. to #s(X) and p. to p(X1,...,Xn), * where n is the arity of the predicate p * @param display (note, this input is being modified) */ private void ExpandShortHands(ASTdisplay display) { for(int i=0 ; i< display.jjtGetNumChildren(); i++) { ASTnonRelAtom atom = (ASTnonRelAtom) display.jjtGetChild(i); ASTpredSymbol pred = (ASTpredSymbol)atom.jjtGetChild(0); // if atom is a sort name, and it does not have any arguments, we append an argument "X" to it: if(pred.hasPoundSign()) { if(atom.jjtGetNumChildren() == 1) { ASTtermList tlist= new ASTtermList(SparcTranslatorTreeConstants.JJTTERMLIST); tlist.jjtAddChild(new ASTterm("X"), 0); atom.jjtAddChild(tlist, 1); } } if(!pred.hasPoundSign() && atom.jjtGetNumChildren()==1 && predicateArgumentSorts.get(pred.image).size()>0) { ASTtermList tlist= new ASTtermList(SparcTranslatorTreeConstants.JJTTERMLIST); for(int j=0; j<predicateArgumentSorts.get(pred.image).size(); j++) { tlist.jjtAddChild(new ASTterm("X"+Integer.toString(j+1)), j); } atom.jjtAddChild(tlist, 1); } } } /** * * @param usedNames - predicate names already used in the program * @param display - the display section of the program (represented by ast) * consisting of the display keyword * followed by a sequence of statements of the form [-]p(t_1,...,t_n). * or of the form p, where [-]p is a shorthand for [-]p(X_1,...,X_n), * and both [-]p(t_1,...,t_n) and [-] p(X_1,...,X_n) are literals of P * @result function extends translatedOutput with a sequence of rules of the form * p'(t_1,...,t_n) :- p(t_1,...,t_n), where p' is a fresh predicate name * for each p(t_1,...,t_n) in the display * followed by a comment % p1',....,pn', where p1',...,pn' are all fresh predicates used */ private void translateDisplay(HashSet<String> usedNames, ASTdisplay display) { ExpandShortHands(display); ArrayList<String> displayPredicateNames = new ArrayList<String>(); for(int i=0 ; i< display.jjtGetNumChildren(); i++) { ASTnonRelAtom atom = (ASTnonRelAtom) display.jjtGetChild(i); // create a rule p'(t_1,...,t_n) :- p(t_1,...,t_n) ASTpredSymbol pred = (ASTpredSymbol)atom.jjtGetChild(0); String body = null; if(pred.hasPoundSign() && sortRenaming.containsKey(pred.image)) { body = atom.toStringWithPredicateRenamed(sortRenaming.get(pred.image)); } else { body = atom.toString(); } String newName =""; boolean prevNegative = pred.negative; boolean prevPoundSign = pred.hasPoundSign(); pred.negative = false; if(pred.hasPoundSign() && sortRenaming.containsKey(pred.image)) { newName = extendtoUniqueName(sortRenaming.get(pred.image), usedNames); } else { newName = extendtoUniqueName(pred.image, usedNames); } usedNames.add(newName); displayPredicateNames.add(newName); pred.setPoundSign(false); pred.negative = false; String head = atom.toStringWithPredicateRenamed(newName); appendStringToTranslation(head.toString() + ":-" + body.toString() + "."); appendNewLineToTranslation(); // restore the pound sign and negativity: pred.setPoundSign(prevPoundSign); pred.negative = prevNegative; } // add the comment StringBuilder sb = new StringBuilder(); sb.append("%"); for(String s: displayPredicateNames) { sb.append(" " + s); } appendStringToTranslation(sb.toString()); appendNewLineToTranslation(); } /** * * @param s a string * @param usedNames - a set of strings * @return a new string s' such that s' = s + x, where x is a shortest sequence of underscores * such that s + x is not in usedNames */ private String extendtoUniqueName(String s, HashSet<String> usedNames) { while (usedNames.contains(s)) { s = s + "_"; } return s; } /** * Rewrite #const and #maxint directives from SPARC to DLV program * * @param root * of program abstract syntax tree */ private void writeDirectives(ASTprogram program) { //add #maxint if the solver is DLV: if(Settings.getSolver()==ASPSolver.DLV) { appendStringToTranslation("#maxint="+BuiltIn.getMaxInt()+"."); } appendNewLineToTranslation(); //add other directives for (String s : program.getdirectives()) { appendStringToTranslation(s); appendNewLineToTranslation(); } } /** * Unique cr-rule label generator * * @return string containing unique label */ private String generateUniqueRuleLabel() { while (ruleLabels.contains("r_" + labelId)) ++labelId; ruleLabels.add("r_" + labelId); return "r_" + labelId; } /** * Translate a collection of program rules and append the new content to * translatedOutput * * @param rules * to translate * @throws ParseException * if sort of some unsafe variable cannot be determined (may be * the case in aggregate or choice rules where there is no * constraints for variable in the body). */ private void translateRules(ASTprogramRules rules,boolean writeWarningsToSTDERR) throws ParseException { for (int i = 0; i < rules.jjtGetNumChildren(); i++) { translateRule((ASTprogramRule) rules.jjtGetChild(i),writeWarningsToSTDERR); } } public String translateAndWriteRules(ASTprogramRules rules, boolean writeWarningsToSTDERR) throws ParseException { translatedOutput = new StringBuilder(); translateRules(rules,writeWarningsToSTDERR); writeTranslatedProgram(); if (writeWarningsToSTDERR) { for (String warning : mainTranslator.getWarnings()) { System.err.println("%WARNING: " + warning); } } return translatedOutput.toString(); } /** * Add atoms to the body of the rule given by AST node * * @param rule * AST node * @param atoms * collections of atoms to add */ private void addAtomsToRulesBody(ASTprogramRule rule, ArrayList<ASTatom> atoms) { if (atoms.size() == 0) return; SimpleNode child = (SimpleNode) rule.jjtGetChild(0); if (child.getId() == SparcTranslatorTreeConstants.JJTUNLABELEDPROGRAMRULE) { addAtomsToRulesBody((ASTunlabeledProgramRule) child, atoms); } else { addAtomsToRulesBody((ASTunlabeledProgramCrRule) child, atoms); } } /** * Add atoms to the body given by AST node * * @param body * AST node * @param atoms * collection of atoms to add */ private void addAtomsToBody(ASTbody body, ArrayList<ASTatom> atoms) { HashSet<String> addedAtoms = new HashSet<String>(); for(int i=0;i<body.jjtGetNumChildren();i++) { addedAtoms.add(((ASTatom)body.jjtGetChild(i)).toString(sortRenaming)); } for (int i = 0; i < atoms.size(); i++) { if (!addedAtoms.contains(atoms.get(i).toString(sortRenaming))) { body.jjtAddChild(atoms.get(i), body.jjtGetNumChildren()); addedAtoms.add((atoms.get(i).toString(sortRenaming))); } } } /** * Create an abstract syntax tree body node from given list of atoms * * @param atoms * components of created body * @return abstract syntax tree body node */ private ASTbody createBody(ArrayList<ASTatom> atoms) { ASTbody body = new ASTbody(SparcTranslatorTreeConstants.JJTBODY); for (int i = 0; i < atoms.size(); i++) { body.jjtAddChild(atoms.get(i), i); } return body; } /** * Add atoms to the body of cr-rule given by AST node * * @param rule * AST node * @param atoms * collections of atoms to add */ private void addAtomsToRulesBody(ASTunlabeledProgramCrRule rule, ArrayList<ASTatom> atoms) { if (rule.jjtGetNumChildren() == 1 && ((SimpleNode) rule.jjtGetChild(0)).getId() == SparcTranslatorTreeConstants.JJTBODY) { addAtomsToBody((ASTbody) rule.jjtGetChild(0), atoms); } else if (rule.jjtGetNumChildren() > 1) { addAtomsToBody((ASTbody) rule.jjtGetChild(1), atoms); } else { ASTbody createdBody = createBody(atoms); rule.jjtAddChild(createdBody, rule.jjtGetNumChildren()); } } /** * Add atoms to the body of standard rule given by AST node * * @param rule * AST node * @param atoms * collections of atoms to add */ private void addAtomsToRulesBody(ASTunlabeledProgramRule rule, ArrayList<ASTatom> atoms) { if (((SimpleNode) rule.jjtGetChild(0)).getId() == SparcTranslatorTreeConstants.JJTPREDSYMBOL) { ASTbody createdBody = createBody(atoms); rule.jjtAddChild(createdBody, rule.jjtGetNumChildren()); } else if (((SimpleNode) rule.jjtGetChild(0)).getId() == SparcTranslatorTreeConstants.JJTBODY) { addAtomsToBody(((ASTbody) rule.jjtGetChild(0)), atoms); } else if (rule.jjtGetNumChildren() > 1 && ((SimpleNode) rule.jjtGetChild(1)).getId() == SparcTranslatorTreeConstants.JJTBODY) { addAtomsToBody(((ASTbody) rule.jjtGetChild(1)), atoms); } else { ASTbody createdBody = createBody(atoms); rule.jjtAddChild(createdBody, rule.jjtGetNumChildren()); } } /** * Add atoms to choice element's body example: by adding p(Y) to choice * element X: p(X) we get X:p(X),p(Y). * * @param node * choice_element * @param newAtoms * atoms to be added */ private void addAtomsToChoiceElement(ASTchoice_element node, ArrayList<ASTatom> newAtoms) { if (newAtoms.size() == 0) return; if (node.jjtGetNumChildren() == 2) { addAtomsToList((ASTextendedSimpleAtomList) node.jjtGetChild(1), newAtoms); } else { ASTextendedSimpleAtomList newList = new ASTextendedSimpleAtomList( SparcTranslatorTreeConstants.JJTEXTENDEDSIMPLEATOMLIST); node.jjtAddChild(newList, 1); addAtomsToList(newList, newAtoms); } } /** * Convert atom to simpleAtom (before calling this method make sure that * atom a follows the grammar given for simple atoms, otherwise results are * unpredictable) * * @param a * atom to be converted * @return converted atom */ private ASTsimpleAtom ConvertAtomToSimple(ASTatom a) { ASTsimpleAtom res = new ASTsimpleAtom( SparcTranslatorTreeConstants.JJTSIMPLEATOM); res.image = a.image; for (int i = 0; i < a.jjtGetNumChildren(); i++) { res.jjtAddChild(a.jjtGetChild(i), i); } return res; } /** * Add atoms to aggregate element's body example: by adding p(Y) to * aggregate element X: p(X) we get X:p(X),p(Y). * * @param node * aggregate element * @param newAtoms * atoms to be added */ private void addAtomsToAggregateElement(ASTaggregateElement node, ArrayList<ASTatom> newAtoms) { if (newAtoms.size() == 0) return; ASTextendedSimpleAtomList simpleList = null; for (int i = 0; i < node.jjtGetNumChildren(); i++) { if (((SimpleNode) (node.jjtGetChild(i))).getId() == SparcTranslatorTreeConstants.JJTEXTENDEDSIMPLEATOMLIST) { simpleList = ((ASTextendedSimpleAtomList) (node.jjtGetChild(i))); break; } } if (simpleList == null) { simpleList = new ASTextendedSimpleAtomList( SparcTranslatorTreeConstants.JJTEXTENDEDSIMPLEATOMLIST); node.jjtAddChild(simpleList, node.jjtGetNumChildren()); } addAtomsToList(simpleList, newAtoms); } /** * Add all of the atoms from ArrayList of atoms as children to * simpleList(given by AST node). * * @param simpleList * @param newAtoms */ private void addAtomsToList(ASTextendedSimpleAtomList simpleList, ArrayList<ASTatom> newAtoms) { HashSet<String> addedAtoms = new HashSet<String>(); for (ASTatom a : newAtoms) { if (!addedAtoms.contains(a.toString(sortRenaming))) { simpleList.jjtAddChild(ConvertAtomToSimple(a), simpleList.jjtGetNumChildren()); addedAtoms.add(a.toString(sortRenaming)); } } } /** * 1.Move expressions from predicate arguments to the body of the rule * Example: p(X+1):-q(X+2). becomes p(Y):-p(Z),Y=X+1,Z=X+2, where Y and Z * are new variables in the rule 2. Add all newly added atoms of the form * [variable]=[expression] to newBodyAtoms list * * @param rule * to be processed * @param newBodyAtoms * the list where all newly added atoms are stored */ private void fetchGlobalExpressions(ASTprogramRule rule, ArrayList<ASTatom> newBodyAtoms) { ArrayList<ASTatom> newAtoms = new ArrayList<ASTatom>(); VariableFetcher vf = new VariableFetcher(); ExpressionFetcher ef = new ExpressionFetcher(vf.fetchVariables(rule)); newAtoms.addAll(ef.fetchGlobalExpressions(rule)); //addAtomsToRulesBody(rule, newAtoms); newBodyAtoms.addAll(newAtoms); } /** * Move local expressions from predicates from aggregates and choice rules * arguments to bodies Example: p(X+1):q(X+2). becomes * p(Y):p(Z),Y=X+1,Z=X+2, where Y and Z are new variables in the rule * * @param rule * to be processed */ private void fetchLocalExpressions(ASTprogramRule rule) { VariableFetcher vf = new VariableFetcher(); fetchLocalExpressions(rule, vf.fetchVariables(rule)); } /** * Recursively search for expressions in aggregates and choice rules and * move them from predicates from aggregates and choice rules arguments to * bodies Example: p(X+1):q(X+2). becomes p(Y):p(Z),Y=X+1,Z=X+2, where Y and * Z are new variables in the rule * * @param rule * to be processed */ private void fetchLocalExpressions(SimpleNode node, HashSet<String> variables) { if (node.getId() == SparcTranslatorTreeConstants.JJTAGGREGATEELEMENT || node.getId() == SparcTranslatorTreeConstants.JJTCHOICE_ELEMENT) { // add expressions to the set of new atoms which will be added to // the node's body ExpressionFetcher ef = new ExpressionFetcher(variables); if (node.getId() == SparcTranslatorTreeConstants.JJTAGGREGATEELEMENT) { addAtomsToAggregateElement((ASTaggregateElement) node, ef.fetchLocalExpressions((ASTaggregateElement) node)); } else { addAtomsToChoiceElement((ASTchoice_element) node, ef.fetchLocalExpressions((ASTchoice_element) node)); } } // recursively search for aggregate and choice elements for (int i = 0; i < node.jjtGetNumChildren(); i++) { fetchLocalExpressions((SimpleNode) node.jjtGetChild(i), variables); } } // find all global variables occurring in the body of the rule private HashSet<String> findBodyGlobalVariables(SimpleNode node) { HashSet<String> vars = new HashSet<String>(); if(node.getId() == SparcTranslatorTreeConstants.JJTBODY) { for(int i=0;i<node.jjtGetNumChildren();i++) { SimpleNode atom = (SimpleNode)node.jjtGetChild(i); SimpleNode atomChild = (SimpleNode) atom.jjtGetChild(0); if(atomChild.getId() != SparcTranslatorTreeConstants.JJTAGGREGATE) { VariableFetcher vf = new VariableFetcher(); vf.fetchVariables(atomChild,vars); } } } for(int i=0;i<node.jjtGetNumChildren();i++) { vars = findBodyGlobalVariables((SimpleNode)node.jjtGetChild(i)); if(vars!= null) return vars; } return null; } private void renameLocalVariables(SimpleNode n, HashMap<String, String> originalNameMapping) { if(n.getId() == SparcTranslatorTreeConstants.JJTPROGRAMRULE) { HashSet<String> bodyVariables = findBodyGlobalVariables(n); if(bodyVariables == null) bodyVariables = new HashSet<String>(); renamer.setBodyVariables(bodyVariables); } if (n.getId() == SparcTranslatorTreeConstants.JJTAGGREGATEELEMENT) { renamer.renameLocalVariables((ASTaggregateElement) n, localElemCount++, originalNameMapping); } else { for (int i = 0; i < n.jjtGetNumChildren(); i++) { renameLocalVariables((SimpleNode) n.jjtGetChild(i), originalNameMapping); } } } /** * Recursively fetch terms with variables from aggregates and choice rules * of the rule and add atoms, specifying sort of the terms to the body of * the corresponding aggregates and choice rules. * * @param rule * @param fetchedTerms * mapping between string representation of terms and arrayList * of all sort the term must belong to. */ private void fetchLocalTerms(SimpleNode node) throws ParseException { boolean isAggregateElement = node.getId() == SparcTranslatorTreeConstants.JJTAGGREGATEELEMENT; boolean isChoiceRuleElement = node.getId() == SparcTranslatorTreeConstants.JJTCHOICE_ELEMENT; TermFetcher tf = null; if (isAggregateElement || isChoiceRuleElement) { tf = new TermFetcher(predicateArgumentSorts,sortNameToExpression); HashMap<ASTterm, String> localFetchedTerms = null; if (isAggregateElement) { localFetchedTerms = tf .fetchTermSorts((ASTaggregateElement) node); } else { localFetchedTerms = tf.fetchTermSorts((ASTchoice_element) node); } ArrayList<ASTatom> newAtoms = new ArrayList<ASTatom>(); for (ASTterm term : localFetchedTerms.keySet()) { String sortName = localFetchedTerms.get(term); newAtoms.add(createSortAtom(sortName, term)); gen.addSort(sortName, sortNameToExpression.get(sortName), true); } if (isAggregateElement) { addAtomsToAggregateElement((ASTaggregateElement) node, newAtoms); } else { addAtomsToChoiceElement((ASTchoice_element) node, newAtoms); } } else { for (int i = 0; i < node.jjtGetNumChildren(); i++) { fetchLocalTerms((SimpleNode) node.jjtGetChild(i)); } } } /** * Fetch terms with variables from aggregates and choice rules of the rule * and add atoms, specifying sort of the terms to the body of the * corresponding aggregates and choice rules. * * @param rule * @param fetchedTerms * mapping between string representation of terms and arrayList * of all sort the term must belong to. */ private void fetchLocalTerms(ASTprogramRule rule) throws ParseException { fetchLocalTerms((SimpleNode) rule); } /** * Fetch terms with variables from the rule and add atoms, specifying sort * of the terms to the body of the rule. * * @param rule * @param fetchedTerms * mapping between string representation of terms and arrayList * of all sort the term must belong to. */ private void fetchGlobalTerms(ASTprogramRule rule, ArrayList<ASTatom> newBodyAtoms) throws ParseException { TermFetcher tf = new TermFetcher(predicateArgumentSorts, sortNameToExpression); HashMap<ASTterm, String> globalFetchedTerms = tf.fetchTermSorts(rule); ArrayList<ASTatom> newAtoms = new ArrayList<ASTatom>(); for (ASTterm term : globalFetchedTerms.keySet()) { String sortName = globalFetchedTerms.get(term); newAtoms.add(createSortAtom(sortName, term)); gen.addSort(sortName, sortNameToExpression.get(sortName), true); } //addAtomsToRulesBody(rule, newAtoms); newBodyAtoms.addAll(newAtoms); } /** * Go over the AST node and fill sets of unbounded and bounded variables * * @param unboundedVariables * set of found unbounded variables * @param boundedVariables * set of found bounded variables * @param node * node to explore * @param scope * true if node is a child of a simpleAtom(either */ private void classifyVariables(HashSet<String> allVariables, HashSet<String> simpleOccurVariables, HashSet<String> arithmeticVariables, SimpleNode node, boolean predicateScope, boolean arithmeticScope) { if (node.getId() == SparcTranslatorTreeConstants.JJTVAR) { if(predicateScope && ! arithmeticScope) simpleOccurVariables.add(node.toString()); if(arithmeticScope) arithmeticVariables.add(node.toString()); allVariables.add(node.toString()); } if (node.getId() == SparcTranslatorTreeConstants.JJTEXTENDEDNONRELATOM || node.getId() == SparcTranslatorTreeConstants.JJTNONRELATOM) { predicateScope = true;// root of predicate; } if(node.getId()==SparcTranslatorTreeConstants.JJTARITHMETICTERM) { String nodeImage=node.toString(); if(nodeImage.indexOf('+')!=-1 || nodeImage.indexOf('-')!=-1 || nodeImage.indexOf('*')!=-1) { arithmeticScope=true; } } for (int i = 0; i < node.jjtGetNumChildren(); i++) { classifyVariables(allVariables, simpleOccurVariables, arithmeticVariables, (SimpleNode) node.jjtGetChild(i),predicateScope,arithmeticScope); } } /** * Translate program rule given by means of AST node * * @param rule * rule to be translated * @throws ParseException * if sort of some variable cannot be detected */ private void translateRule(ASTprogramRule rule,boolean writeWarningsToSTDERR) throws ParseException { String originalRule = rule.toString(new HashMap<String,String>()); int lineNumber = rule.getBeginLine(); int columnNumber = rule.getBeginColumn(); // write warnings to STDERR if the flag was set to true if (generateClingconWarnings) { Formula ruleF=ruleReducer.reduceRule(rule); // System.err.println(ruleF.toString()); if(ruleF!=null && !ExpandSolve.run(ruleF)) { if(writeWarningsToSTDERR) { System.err.println("%WARNING: Rule "+originalRule+" at line "+lineNumber+ ", column "+columnNumber+" is an empty rule"); } else { mainTranslator.addWarning("Rule "+originalRule+" at line "+lineNumber+ ", column "+columnNumber+" is an empty rule"); } } } // renameLocalVariables HashMap<String, String> originalNameMapping = new HashMap<String, String>(); appendToVariableNamesIn(rule, "_G", originalNameMapping); renameLocalVariables(rule,originalNameMapping); ArrayList<ASTatom> newSortAtoms=new ArrayList<ASTatom>(); ensureVariableSafety(rule, originalRule, originalNameMapping, newSortAtoms); // fetch expressions: fetchGlobalExpressions(rule,newSortAtoms); fetchLocalExpressions(rule); // fetch terms: fetchGlobalTerms(rule,newSortAtoms); fetchLocalTerms(rule); addAtomsToRulesBody(rule,newSortAtoms); // add rules for warnings if(generateASPWarnings) { ArrayList<String> warningRules= WarningRuleCreator.createWarningRules(originalRule, lineNumber, columnNumber, newSortAtoms); for(String warningRule:warningRules) { appendStringToTranslation(warningRule); appendNewLineToTranslation(); } } RuleAnalyzer ra = new RuleAnalyzer(rule); // add new weak constraints and rules for a CR-rule if (ra.isCrRule()) { String ruleName = getRuleName(rule); ASTatom applyAtom = getApplAtom(ruleName); ASTbody body = ra.getBody(); appendStringToTranslation(applyAtom.toString(sortRenaming)); appendStringToTranslation("|-"); appendStringToTranslation(applyAtom.toString(sortRenaming)); if (body != null) { appendStringToTranslation(":-"); appendStringToTranslation(body.toString(sortRenaming)); } appendStringToTranslation("."); appendNewLineToTranslation(); appendStringToTranslation(":~"); appendStringToTranslation(applyAtom.toString(sortRenaming)); if (body != null) { appendStringToTranslation(","); appendStringToTranslation(body.toString(sortRenaming)); } appendStringToTranslation("."); appendNewLineToTranslation(); ArrayList<ASTatom> newAtoms = new ArrayList<ASTatom>(); newAtoms.add(applyAtom); addAtomsToRulesBody(rule, newAtoms); } appendStringToTranslation(rule.toString(sortRenaming)); appendNewLineToTranslation(); } private void ensureVariableSafety(ASTprogramRule rule, String originalRule, HashMap<String, String> originalNameMapping, ArrayList<ASTatom> newSortAtoms) throws ParseException { HashSet<String> simpleOccurVariables =new HashSet<String>(); HashSet<String> allVariables=new HashSet<String>(); HashSet<String> arithmeticVariables=new HashSet<String>(); final boolean arithmeticScope=false; final boolean predicateScope=false; classifyVariables(allVariables, simpleOccurVariables, arithmeticVariables, rule, predicateScope, arithmeticScope); allVariables.removeAll(simpleOccurVariables); allVariables.removeAll(arithmeticVariables); if(!allVariables.isEmpty()) { Pair<ArrayList<String>,ArrayList<String>> unrestrictedVariablesLists=splitLocalGlobalVariables(allVariables); renameVariables(unrestrictedVariablesLists.first, originalNameMapping); renameVariables(unrestrictedVariablesLists.second,originalNameMapping); throw new ParseException(inputFileName + ": " + "program rule "+originalRule + " at line " + rule.getBeginLine() + ", column " + rule.getBeginColumn() + " contains " +(unrestrictedVariablesLists.first.size()>0? "unrestricted global variables "+ StringListUtils.getSeparatedList(unrestrictedVariablesLists.first, ","):"") + (unrestrictedVariablesLists.second.size()>0 ? ((unrestrictedVariablesLists.first.size()>0)? " and ":"")+ "unrestricted local variables "+ StringListUtils.getSeparatedList(unrestrictedVariablesLists.second, ","):"")); } arithmeticVariables.removeAll(simpleOccurVariables); if(!arithmeticVariables.isEmpty()) { gen.addSort("nat", sortNameToExpression.get("nat"), true); Pair<ArrayList<String>,ArrayList<String>> unrestrictedArithmVariablesLists=splitLocalGlobalVariables(arithmeticVariables); //add some #nat atoms to the body: for(int i=0;i<unrestrictedArithmVariablesLists.first.size();i++) { newSortAtoms.add(createSortAtom(sortRenaming.get("nat"), new ASTterm(unrestrictedArithmVariablesLists.first.get(i)))); } if(!unrestrictedArithmVariablesLists.second.isEmpty()) { //add some nat atoms to the body of local elements addNatAtomsForLocalVariables(rule,unrestrictedArithmVariablesLists.second); } } } /** * Split the set of variables into two array lists of local and global variables * @return Pair<ArrayList<String>, ArrayList<String> > * the pair of lists. the first list will contain global variables * and the second list will contain local variables. */ Pair<ArrayList<String>, ArrayList<String>> splitLocalGlobalVariables(HashSet<String> variables) { Pair<ArrayList<String>,ArrayList<String>> result=new Pair<ArrayList<String>,ArrayList<String>>(new ArrayList<String>(), new ArrayList<String>()); for(String varName:variables) { if(varName.endsWith("_G")) result.first.add(varName); else result.second.add(varName); } return result; } /** * Recursively search for aggregates and choice rules and add #nat atoms for corresponding * bodies * @param n */ private void addNatAtomsForLocalVariables(SimpleNode n,ArrayList<String> variablesToAdd) { if(n.getId()==SparcTranslatorTreeConstants.JJTAGGREGATEELEMENT || n.getId()==SparcTranslatorTreeConstants.JJTCHOICE_ELEMENT) { VariableFetcher vf=new VariableFetcher(); HashSet<String> variablesInElement=new HashSet<String>(); vf.fetchVariables(n,variablesInElement); for(String varName:variablesToAdd) { if(variablesInElement.contains(varName)) { ASTatom atomToAdd=createSortAtom(sortRenaming.get("nat"),new ASTterm(varName)); ArrayList<ASTatom> atomListToAdd=new ArrayList<ASTatom>( Arrays.asList(atomToAdd)); if(n.getId()==SparcTranslatorTreeConstants.JJTAGGREGATEELEMENT) { addAtomsToAggregateElement((ASTaggregateElement)n, atomListToAdd); } else { addAtomsToChoiceElement((ASTchoice_element)n, atomListToAdd); } } } } for(int i=0;i<n.jjtGetNumChildren();i++) { addNatAtomsForLocalVariables((SimpleNode)n.jjtGetChild(i), variablesToAdd); } } /** * Rename variables in the list according to provided mapping */ private void renameVariables(ArrayList<String> variables, HashMap<String,String> nameMapping) { for(int i=0;i<variables.size();i++) { variables.set(i,nameMapping.get(variables.get(i))); } } /** * Create sort atom (consisting of sort name and one argument) * * @param name * name of the new atom * @param term * term to be inserted as an argument of the new atom * @return created Atom */ ASTatom createSortAtom(String name, ASTterm term) { ASTatom atom = new ASTatom(SparcTranslatorTreeConstants.JJTATOM); ASTextendedNonRelAtom exatom = new ASTextendedNonRelAtom( SparcTranslatorTreeConstants.JJTEXTENDEDNONRELATOM); exatom.image = ""; atom.jjtAddChild(exatom, 0); ASTpredSymbol pred = new ASTpredSymbol( SparcTranslatorTreeConstants.JJTPREDSYMBOL); pred.image = name; pred.setPoundSign(true); exatom.jjtAddChild(pred, 0); ASTtermList termList = new ASTtermList( SparcTranslatorTreeConstants.JJTTERMLIST); exatom.jjtAddChild(termList, 1); termList.jjtAddChild(term, 0); return atom; } /** * Retrieve appl atom for given rule name. * * @param ruleName * of the form r_id(Var_1,Var_2,..Var_n). * @return AST node for the new atom of the form appl(ruleName). */ ASTatom getApplAtom(String ruleName) { ASTatom atom = new ASTatom(SparcTranslatorTreeConstants.JJTATOM); ASTextendedNonRelAtom exatom = new ASTextendedNonRelAtom( SparcTranslatorTreeConstants.JJTEXTENDEDNONRELATOM); exatom.image = ""; atom.jjtAddChild(exatom, 0); ASTpredSymbol pred = new ASTpredSymbol( SparcTranslatorTreeConstants.JJTPREDSYMBOL); pred.image = crAuxAtomName; exatom.jjtAddChild(pred, 0); ASTtermList mainList = new ASTtermList( SparcTranslatorTreeConstants.JJTTERMLIST); exatom.jjtAddChild(mainList, 1); ASTterm mainTerm = new ASTterm(SparcTranslatorTreeConstants.JJTTERM); mainList.jjtAddChild(mainTerm, 0); ASTsymbolicTerm mainSymbTerm = new ASTsymbolicTerm( SparcTranslatorTreeConstants.JJTSYMBOLICTERM); mainTerm.jjtAddChild(mainSymbTerm, 0); if (ruleName.indexOf('(') != -1) { // there are variables in the rule // name // create term list containing the variables ASTtermList tlist = new ASTtermList( SparcTranslatorTreeConstants.JJTTERMLIST); // exatom.jjtAddChild(tlist, 1); String[] vars = ruleName.substring(ruleName.indexOf('(') + 1, ruleName.indexOf(')')).split(","); for (String var : vars) { ASTterm term = new ASTterm(SparcTranslatorTreeConstants.JJTTERM); term.image = var; tlist.jjtAddChild(term, tlist.jjtGetNumChildren()); } ASTsymbolicFunction func = new ASTsymbolicFunction( SparcTranslatorTreeConstants.JJTSYMBOLICFUNCTION); func.image = ruleName.substring(0, ruleName.indexOf('(') + 1); mainSymbTerm.jjtAddChild(func, 0); mainSymbTerm.jjtAddChild(tlist, 1); } else { ASTsymbolicConstant cons = new ASTsymbolicConstant( SparcTranslatorTreeConstants.JJTSYMBOLICCONSTANT); cons.image = ruleName; mainSymbTerm.jjtAddChild(cons, 0); } return atom; } /** * Get rule name for the rule given by AST node * * @param rule * @return String, containing rule name of the form * r_id(Var_1,Var_2,...Var_n). */ private String getRuleName(ASTprogramRule rule) { String label = rule.getLabel(); if (label.equals("")) { label = generateUniqueRuleLabel(); rule.setLabel(label); } VariableFetcher vf = new VariableFetcher(); HashSet<String> vars = vf.fetchVariables(rule); HashSet<String> varsToRemove = new HashSet<String>(); for (String var : vars) { // remove local variables if (!var.endsWith("_G")) { varsToRemove.add(var); } } vars.removeAll(varsToRemove); String ruleName = label; if (vars.size() != 0) { ruleName += "("; } boolean first = true; for (String var : vars) { if (!first) ruleName += ","; first = false; ruleName += var; } if (vars.size() != 0) { ruleName += ")"; } return ruleName; } /** * Write new line symbol to output */ private void appendNewLineToTranslation() { String eol = System.getProperty("line.separator"); appendStringToTranslation(eol); } /** * Write new string to output * * @param s * string to be written */ private void appendStringToTranslation(String s) { this.translatedOutput.append(s); } /** * Write program from internal string buffer to output */ public void writeTranslatedProgram() { try { if (out != null && this.translatedOutput!=null) { out.write(this.translatedOutput.toString()); out.flush(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Append suffix to all variables occurring in Abstract syntax subtree with * root n. * * @param n * root of the tree to be explored * @param suffix * string to be added */ void appendToVariableNamesIn(SimpleNode n, String suffix, HashMap<String, String> originalNameMapping) { if (n.getId() == SparcTranslatorTreeConstants.JJTVAR) { originalNameMapping.put(n.image + suffix, n.image); n.image += suffix; } else for (int i = 0; i < n.jjtGetNumChildren(); i++) { appendToVariableNamesIn((SimpleNode) (n.jjtGetChild(i)), suffix, originalNameMapping); } if (n.getId() == SparcTranslatorTreeConstants.JJTTERM) { originalNameMapping.put(n.toString(false), n.toString(true)); } } }