/* * xtc - The eXTensible Compiler * Copyright (C) 2007 New York University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.typical; import xtc.util.Pair; import xtc.util.Runtime; import xtc.tree.Node; import java.util.ArrayList; /** * A Typical Reduction * * @author Laune Harris * @version $Revision: 1.22 $ */ @SuppressWarnings("unchecked") public class Reduction { /** The runtime */ protected Runtime runtime; /** The location node */ protected Node location; /** The target list */ protected Pair<Node> target; /** The set of patterns. */ protected ArrayList<Analyzer.NodeMatch[]> patterns; /** The set of results. */ protected ArrayList<Object> results; /** The resulting matches */ protected Pair<Object> matches = Pair.empty(); /** This flag indicates that the reduction has a 'singleton' constraint */ protected boolean sing = false; /** This flag indicates that the reduction has a 'required' constraint */ protected boolean req = false; /** This flag indicates that the reduction has a 'duplicate' constraint */ protected boolean dup = false; /** This flag indicates that the reduction has a 'noduplicate' constraint */ protected boolean nodup = false; /** This flag indicates that the reduction has a 'set' constraint */ protected boolean set = false; /** This flag indicates that the reduction has a 'optional' constraint */ protected boolean opt = false; /** This flag indicates that the reduction has a 'list' constraint */ protected boolean list = false; /** Flag indicating that we've seen an error*/ protected boolean error = false; /** This is the string value used for error reporting */ protected String tag; /** * Create a new reduction */ public Reduction(Pair<Node> target, Runtime runtime, Node location) { patterns = new ArrayList<Analyzer.NodeMatch[]>(); results = new ArrayList<Object>(); this.target = target; this.location = location; this.runtime = runtime; //by default don't allow duplicates nodup = true; } /** * add a pattern to this */ public void addPattern(Object result, Analyzer.NodeMatch... pattern) { results.add(result); patterns.add(pattern); } /** * set opt to true */ public void setOpt() { opt = true; } /** * set list to true */ public void setList() { list = true; } /** * set list to true */ public void setSet() { set = true; } /** * set list to true */ public void setSing() { sing = true; } /** * set list to true */ public void setReq() { req = true; } /** * set list to true */ public void setNodup() { nodup = true; } /** * set list to true */ public void setDup() { dup = true; } /** * set the tag * @param tag the tag to set */ public void setTag(String tag) { this.tag = tag; } /** * Apply the reduction. * * @return The given result value if the reduction is sucessful, * null otherwise */ public Object apply() { /* Here's what should be goin on. We've got a list of arrays of patterns * We iterate from the longest array of patterns to the shortets and apply * each array on the list. If there's a successful match we tag all the * matches nodes with 'used = yes' to remove them from the available pool. * we also record the position of the pattern that matched. At the end * we check the constraints against the result and report an error message * if necessary. This is probably not the most efficient way to do this * right now, but it seems to be correct. */ ArrayList<Integer> positions = new ArrayList<Integer>(); //TODO sort the patterns from longest to shortest boolean dupError = false; for (Node n : target) { n.setProperty("used", "no"); } //iterate over the patterns from longest to shortest for (int i = 0; i < patterns.size(); i++) { //this flag indicates that all sub patterns in a pattern array match //the list boolean allMatch = true; int matchCount = 0; //apply the pattern to the list for (Analyzer.NodeMatch nodeMatch : patterns.get(i)) { boolean match = isMatch(nodeMatch); if (match) matchCount++; allMatch = allMatch && match; } //if matched tag all the matching nodes with 'used=yes' if (allMatch && (matchCount == patterns.get(i).length)) { positions.add(i); for (Node n : target) { if ("maybe".equals(n.getProperty("used"))) { n.setProperty("used", "yes"); } } if (nodup) { //check for duplicates //do it all over again if there's another match we give an error allMatch = true; matchCount = 0; //apply the pattern to the list for (Analyzer.NodeMatch nodeMatch : patterns.get(i)) { boolean match = isMatch(nodeMatch); if (match) matchCount++; allMatch = allMatch && match; } if (allMatch && (matchCount == patterns.get(i).length)) { dupError = true; } } } else { //otherwise tag them with 'used=no' for (Node n : target) { if ("maybe".equals(n.getProperty("used"))) { n.setProperty("used", "no"); } } } } int size = positions.size(); if (dupError) { runtime.error("duplicate " + tag + "s defined"); return null; } //constraint and error checks if (sing && size > 1) { runtime.error("multiple " + tag + "s defined", location); return null; } if (req && size == 0) { runtime.error("required " + tag, location); return null; } if (list) { Pair<Object> values = Pair.EMPTY; for (int i : positions) { values = new Pair<Object>(results.get(i), values); } return values.reverse(); } if (positions.size() > 0) { return results.get(positions.get(0)); } return null; } /** * @return <code>true</code> if this pattern matches a node in the list * <code>false</code> otherwise. */ private boolean isMatch(Analyzer.NodeMatch nm) { for (Node n : target) { if ("no".equals(n.getProperty("used")) && nm.apply(n)) { n.setProperty("used", "maybe"); return true; } } return false; } /** * Return the node that matches a given pattern. This is useful for * as pattern. * * @return the matching node */ public Node getMatch(Analyzer.NodeMatch nm) { for (Object n : target) { if (nm.apply((Node)n)) return (Node)n; } return null; } }