/** Is there a priority between high and low? Helper function for checkAndApplyLexerPrecedence. */ private static boolean hasPriority( Map<Acceptation, Set<Acceptation>> priorities, Acceptation high, Acceptation low) { Set<Acceptation> set; set = priorities.get(high); return set != null && set.contains(low); }
/* * Check and apply implicit and explicit lexical precedence rules. Display * errors and infos for the human user during the process. * * @param automaton * is the automaton to check. In order to have the explicit * priorities applied, it is required that the automaton is * tagged with the acceptation of the LexerExpression. * @return a new automaton where only the right acceptation tags remains. */ public Automaton checkAndApplyLexerPrecedence( Automaton automaton, Trace trace, Strictness strictness) { automaton = automaton.minimal(); Map<State, String> words = automaton.collectShortestWords(); Map<Acceptation, Set<State>> accepts = automaton.collectAcceptationStates(); // Associate each acceptation with the ones it share at least a common // state. Map<Acceptation, Set<Acceptation>> conflicts = new HashMap<Acceptation, Set<Acceptation>>(); // Associate each acceptation with the ones it supersedes. Map<Acceptation, Set<Acceptation>> priorities = new HashMap<Acceptation, Set<Acceptation>>(); // Fill the priorities structure with the implicit inclusion rule for (Acceptation acc1 : automaton.getAcceptations()) { if (acc1 == Acceptation.ACCEPT) { continue; } // FIXME: empty LexerExpressions are not detected here since // their acceptation tag is not in the automaton. // Collect all the conflicts Set<State> set1 = accepts.get(acc1); Set<Acceptation> confs = new TreeSet<Acceptation>(); for (State s : set1) { confs.addAll(s.getAcceptations()); } conflicts.put(acc1, confs); // Check for implicit priority for each conflict for (Acceptation acc2 : confs) { if (acc2 == Acceptation.ACCEPT) { continue; } if (acc1 == acc2) { continue; } Set<State> set2 = accepts.get(acc2); if (set2.equals(set1)) { if (!conflicts.containsKey(acc2)) { throw SemanticException.genericError( "The " + acc1.getName() + " and " + acc2.getName() + " tokens are equivalent."); } } else if (set2.containsAll(set1)) { addPriority(priorities, acc1, acc2); State example = null; for (State s : set2) { if (!set1.contains(s)) { example = s; break; } } // Note: Since set1 is strictly included in set2, example // cannot be null trace.verboseln( " The " + acc1.getName() + " token is included in the " + acc2.getName() + " token. (Example of divergence: '" + words.get(example) + "'.)"); } } } // Collect new acceptation states and see if a conflict still exists Map<State, Acceptation> newAccepts = new HashMap<State, Acceptation>(); for (State s : automaton.getStates()) { if (s.getAcceptations().isEmpty()) { continue; } Acceptation candidate = s.getAcceptations().first(); for (Acceptation challenger : s.getAcceptations()) { if (candidate == challenger) { continue; } if (hasPriority(priorities, candidate, challenger)) { // nothing. keep the candidate } else if (hasPriority(priorities, challenger, candidate)) { candidate = challenger; } else { throw SemanticException.genericError( "The " + candidate.getName() + " token and the " + challenger.getName() + " token conflict on the string '" + words.get(s) + "'. You should specify a precedence between them."); } } newAccepts.put(s, candidate); } // Ask for a new automaton with the correct acceptation states. return automaton.resetAcceptations(newAccepts); }