/*
* xtc - The eXTensible Compiler
* Copyright (C) 2009-2012 New York University
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.lang.cpp;
import java.util.List;
import java.util.LinkedList;
import java.util.Arrays;
import java.io.StringReader;
import xtc.lang.cpp.Syntax.Kind;
import xtc.lang.cpp.Syntax.Language;
import xtc.parser.Result;
import xtc.tree.GNode;
import xtc.tree.Node;
import xtc.tree.Token;
public class TreeComparator {
/** A stack to hold the pending nodes of the first AST. */
private LinkedList<Object> s1;
/** A stack to hold the pending nodes of the second AST. */
private LinkedList<Object> s2;
/** The array of symbols indexed by symbol id. */
List symbols;
/** Create a new comparator. This object is a singleton. */
private TreeComparator() {
this.symbols = Arrays.asList(CParseTables.getInstance().yytname);
}
/**
* Compare two ASTs. This method checks whether the pre-order
* traversal of both trees is the same. This is done because
* SuperC's AST has extra nodes, e.g., Conditional and *List nodes
* that the Rats! AST doesn't have. Comparing pre-order makes it
* easy to skip these nodes.
*
* @param o1 The SuperC AST.
* @param o2 The Rats AST.
* @return true if their pre-order traversals are equal.
*/
public boolean traverse(Object o1, Object o2) {
s1 = new LinkedList<Object>();
s2 = new LinkedList<Object>();
s1.push(o1);
s2.push(o2);
while (traverse()) {
if (s1.isEmpty() && s2.isEmpty()) return true;
}
return false;
}
/**
* Perform the actual comparison. This assumes s1 and s2 are stacks
* containing the top-level node of both trees.
*
* @return true if the ASTs' pre-order traversals are equal.
*/
private boolean traverse() {
// Skip nulls in Rats! ASTs
if (! s2.isEmpty() && null == s2.peek()) {
if (s1.peek() instanceof GNode) {
GNode g = (GNode) s1.peek();
if (g.getName().endsWith("List") && g.size() == 0) {
// Equate empty lists with null
s1.pop();
}
}
s2.pop();
return true;
}
if (s1.isEmpty() && s2.isEmpty()) return true;
if (s1.isEmpty() || s2.isEmpty()) {
System.err.println("sizes differ");
return false;
}
Object o1 = s1.pop();
Object o2 = s2.pop();
if (null == o1 && null == o2) return true;
// Move past SuperC's FunctionProtoype nodes.
if (o1 instanceof GNode
&& ((GNode)o1).getName().equals("FunctionPrototype")) {
Node a = (Node) o1;
GNode g = (GNode) a;
System.err.println("is function prototype " + a.getName());
for (int i = g.size() - 1; i >= 0; i--) {
s1.push(g.get(i));
}
s2.push(o2);
return true;
}
// Check for mismatched null.
if (null == o1 || null == o2) {
System.err.println("mismatched null");
System.err.println("a: " + o1 + "\n");
System.err.println("b: " + o2 + "\n");
return false;
}
if (o1 instanceof String && o2 instanceof Syntax
|| o1 instanceof Syntax && o2 instanceof String) {
// Equate a SuperC token with a Rats!' string
return o1.toString().equals(o2.toString());
} else if (o1 instanceof Syntax.Text && o2 instanceof GNode) {
// Equate SuperC's tokens with scannerless Rats!'s AST nodes
Syntax.Text s = (Syntax.Text) o1;
GNode r = (GNode) o2;
System.err.println(s.tag() + ":" + s.toString());
System.err.println(r.getName() + ":" + r.toString());
return o1.toString().equals(((GNode) o2).get(0).toString());
}
Node a = (Node) o1;
Node b = (Node) o2;
if (a.isToken() && b.isToken()) {
return a.getTokenText().equals(b.getTokenText());
}
// Compare type names. SuperC has tokens, Rats! has AST nodes.
if (a instanceof Language && b.size() == 0
&& (a.toString() + "()").equals(b.toString().toLowerCase())) {
return true;
}
if (! (a.isGeneric() && b.isGeneric())) {
System.err.println("node types differ");
System.err.println("a: " + a + "\n");
System.err.println("b: " + b + "\n");
return false;
}
GNode g = (GNode) a;
GNode h = (GNode) b;
// Skip Conditional nodes
if (g.getName().equals("Conditional")) {
s1.push(g.get(1));
s2.push(o2);
return true;
}
// Expand SuperC AST's lists, but not ExpressionList, which is a
// Node in the Rats! grammar.
if (! g.getName().equals("ExpressionList")
&& CValues
.getInstance().getValueType(symbols.indexOf(g.getName()))
== SemanticValues.ValueType.LIST) {
System.err.println("is list " + a.getName());
for (int i = g.size() - 1; i >= 0; i--) {
s1.push(g.get(i));
}
s2.push(o2);
return true;
}
// TODO: SuperC should have a unified DeclarationSpecifiers
// production is possible.
if (h.getName().equals("DeclarationSpecifiers")) {
if (g.getName().equals("DeclarationSpecifier")
|| g.getName().equals("TypeSpecifier")
|| g.getName().equals("DeclarationQualifierList")
|| g.getName().equals("TypeQualifierList")) {
}
} else if (! g.getName().equals(h.getName())) {
System.err.println("node names differ");
System.err.println("a: " + a + "\n");
System.err.println("b: " + b + "\n");
return false;
}
for (int i = g.size() - 1; i >= 0; i--) {
s1.push(g.get(i));
}
for (int i = h.size() - 1; i >= 0; i--) {
s2.push(h.get(i));
}
return true;
}
private static TreeComparator ref = null;
public static TreeComparator getInstance() {
if (null == ref) {
ref = new TreeComparator();
}
return ref;
}
/**
* Check whether two xtc ASTs are equivalent.
*
* @param o1 The first tree.
* @param o2 The second tree.
* @return true if the tress are equivalent.
*/
public static boolean compare(Object o1, Object o2) {
if (null == o1 && null == o2) return true;
if (null == o1 || null == o2) return false;
if (o1 instanceof String && o2 instanceof Syntax
|| o1 instanceof Syntax && o2 instanceof String) {
return o1.toString().equals(o2.toString());
}
Node a = (Node) o1;
Node b = (Node) o2;
if (a.isToken() && b.isToken()) {
return a.getTokenText().equals(b.getTokenText());
} else if (a.isGeneric() && b.isGeneric()) {
if (a.getName().equals(b.getName())) {
GNode g = (GNode) a;
GNode h = (GNode) b;
for (int i = 0; i < Math.max(g.size(), h.size()); i++) {
if (i >= g.size() || i >= h.size()) {
System.err.println("sizes differ");
System.err.println("a: " + g + "\n");
System.err.println("b: " + h + "\n");
return false;
}
if (!compare(g.get(i), h.get(i))) {
return false;
}
}
return true;
} else if (a.getName().equals("Conditional")) {
return compare(a.get(1), b);
} else if (b.getName().equals("Conditional")) {
return compare(a, b.get(1));
} else {
System.err.println("node names differ");
System.err.println("a: " + a + "\n");
System.err.println("b: " + b + "\n");
return false;
}
} else {
System.err.println("node types differ");
System.err.println("a: " + a + "\n");
System.err.println("b: " + b + "\n");
return false;
}
}
}