/*
* xtc - The eXTensible Compiler
* Copyright (C) 2008 Robert Grimm
*
* 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;
import xtc.Constants;
import xtc.tree.GNode;
import xtc.tree.Node;
import xtc.tree.Token;
import xtc.tree.Visitor;
import xtc.type.ArrayT;
import xtc.type.StringReference;
import xtc.type.Tagged;
import xtc.type.Type;
import xtc.util.Runtime;
import xtc.util.SymbolTable;
import xtc.util.Utilities;
/**
* A visitor to extract C features. This visitor assumes that the AST
* is well-typed and annotated with the correct types.
*
* @author Robert Grimm
* @version $Revision: 1.6 $
*/
public class CFeatureExtractor extends Visitor {
/** The runtime. */
protected final Runtime runtime;
/** The symbol table. */
protected SymbolTable table;
/** The type of the current construct. */
protected Type type;
/** The name of the variable being declared. */
protected String name;
/**
* Create a new C feature extractor.
*
* @param runtime The runtime.
*/
public CFeatureExtractor(Runtime runtime) {
this.runtime = runtime;
}
/**
* Process the specified AST.
*
* @param node The type-checked and marked AST.
* @param table The corresponding symbol table.
*/
public void process(Node node, SymbolTable table) {
this.table = table;
type = null;
name = null;
runtime.console().sep();
dispatch(node);
runtime.console().sep().flush();
}
/**
* Start processing the specified node.
*
* @param node The node to process.
* @return The type of the surrounding construct.
*/
public Type enter(GNode node) {
Type old = type;
type = toType(node);
return old;
}
/**
* Stop processing the current node.
*
* @param old The type of the surrounding construct.
*/
public void exit(Type old) {
type = old;
}
/** Visit the specified initialized declarator. */
public void visitInitializedDeclarator(GNode n) {
final String oldName = name;
final Type oldType = type;
name = CAnalyzer.getDeclaredId(n.getGeneric(1)).getString(0);
type = (Type)table.lookup(name);
visit(n);
if (isZeroLength(type)) {
runtime.console().loc(n).p(": zero length array '").p(name).pln("'");
}
if (null != n.get(2)) {
final String alt = toText(n.getGeneric(2).getGeneric(0));
runtime.console().loc(n).p(": assembly name '").p(name).p("' -> '").
p(alt).pln("'");
}
name = oldName;
type = oldType;
}
/** Visit the specified thread specifier. */
public void visitThreadSpecifier(GNode n) {
runtime.console().loc(n).pln(": thread-local");
}
/** Visit the specified enumertion type definition. */
public void visitEnumerationTypeDefinition(GNode n) {
final Type old = enter(n);
visit(n);
exit(old);
}
/** Visit the specified enumeration type reference. */
public void visitEnumerationTypeReference(GNode n) {
final Type old = enter(n);
visit(n);
exit(old);
}
/** Visit the specified structure type definition. */
public void visitStructureTypeDefinition(GNode n) {
final Type old = enter(n);
visit(n);
if (0 == type.toTagged().getMemberCount()) {
runtime.console().loc(n).p(": empty ").pln(toDescription(type));
}
exit(old);
}
/** Visit the specified structure type reference. */
public void visitStructureTypeReference(GNode n) {
final Type old = enter(n);
visit(n);
exit(old);
}
/** Visit the specified union type definition. */
public void visitUnionTypeDefinition(GNode n) {
final Type old = enter(n);
visit(n);
if (0 == type.toTagged().getMemberCount()) {
runtime.console().loc(n).p(": empty ").pln(toDescription(type));
}
exit(old);
}
/** Visit the specified union type reference. */
public void visitUnionTypeReference(GNode n) {
final Type old = enter(n);
visit(n);
exit(old);
}
/** Visit the specified structure declaration. */
public void visitStructureDeclaration(GNode n) {
if (null == n.get(2)) runtime.console().loc(n).pln(": unnamed field");
visit(n);
}
/** Visit the specified structure declarator. */
public void visitStructureDeclarator(GNode n) {
final GNode id = CAnalyzer.getDeclaredId(n);
if (null != id) {
final String s = id.getString(0);
final Type t = type.toTagged().lookup(s);
if (isZeroLength(t)) {
runtime.console().loc(n).p(": zero length array '").p(s).pln("'");
}
}
}
/** Visit the specified parameter type list. */
public void visitParameterTypeList(GNode n) {
final Type old = type;
type = null;
visit(n);
type = old;
}
/** Visit the specified parameter declaration. */
public void visitParameterDeclaration(GNode n) {
final String oldName = name;
final GNode id = CAnalyzer.getDeclaredId(n.getGeneric(1));
name = null == id ? null : id.getString(0);
visit(n);
name = oldName;
}
/** Visit the specified typeof specifier. */
public void visitTypeofSpecifier(GNode n) {
runtime.console().loc(n).pln(": typeof");
visit(n);
}
/** Visit the specified var-arg list specifier. */
public void visitVarArgListSpecifier(GNode n) {
runtime.console().loc(n).pln(": var-arg list");
}
/** Visit the specified designator. */
public void visitDesignator(GNode n) {
if (3 == n.size()) runtime.console().loc(n).pln(": array range");
visit(n);
}
/** Visit the specified obsolete array designation. */
public void visitObsoleteArrayDesignation(GNode n) {
if (3 == n.size()) runtime.console().loc(n).pln(": array range");
visit(n);
}
/** Visit the specified attribute list entry. */
public void visitAttributeListEntry(GNode n) {
runtime.console().loc(n).p(": ");
if (null != type && type.resolve().isFunction()) {
runtime.console().p("function attribute '").p(name).p("' -> ");
} else if (null != type && type.hasTagged()) {
runtime.console().p("type attribute '").p(toDescription(type)).p("' -> ");
} else {
runtime.console().p("variable attribute ");
if (null != name) runtime.console().p("'").p(name).p("' -> ");
}
runtime.console().p("'").p(n.getString(0)).pln("'");
dispatch(n.getNode(1));
}
/** Visit the specified local label declaration. */
public void visitLocalLabelDeclaration(GNode n) {
runtime.console().loc(n).p(": local label");
if (1 == n.size()) {
runtime.console().p(' ');
} else {
runtime.console().p("s ");
}
boolean first = true;
for (Object o : n) {
if (first) {
first = false;
} else {
runtime.console().p(", ");
}
runtime.console().p("'").p((String)o).p("'");
}
runtime.console().pln();
}
/** Visit the specified case label. */
public void visitCaseLabel(GNode n) {
if (2 == n.size()) runtime.console().loc(n).pln(": case range");
visit(n);
}
/** Visit the specified goto statement. */
public void visitGotoStatement(GNode n) {
if (null != n.get(0)) runtime.console().loc(n).pln(": computed goto");
visit(n);
}
/** Visit the specified alignof expression. */
public void visitAlignofExpression(GNode n) {
runtime.console().loc(n).pln(": alignof");
visit(n);
}
/** Visit the specified offsetof expression. */
public void visitOffsetofExpression(GNode n) {
runtime.console().loc(n).pln(": offsetof");
visit(n);
}
/** Visit the specified type compatibility expression. */
public void visitTypeCompatibilityExpression(GNode n) {
runtime.console().loc(n).pln(": types_compatible_p");
visit(n);
}
/** Visit the specified label address expression. */
public void visitLabelAddressExpression(GNode n) {
runtime.console().loc(n).pln(": label address");
visit(n);
}
/** Visit the specified function call. */
public void visitFunctionCall(GNode n) {
if (n.getGeneric(0).hasName("PrimaryIdentifier")) {
final String s = n.getGeneric(0).getString(0);
if (s.startsWith("__builtin_") || s.startsWith("__sync_")) {
runtime.console().loc(n).p(": call to built-in function '").p(s).
pln("'");
}
}
visit(n);
}
/** Visit the specified statement as expression. */
public void visitStatementAsExpression(GNode n) {
runtime.console().loc(n).pln(": statement expression");
visit(n);
}
/** Visit the specified variable argument access. */
public void visitVariableArgumentAccess(GNode n) {
runtime.console().loc(n).
pln(": call to built-in function '__builtin_va_arg'");
visit(n);
}
/** Visit the specified assembly argument. */
public void visitAssemblyArgument(GNode n) {
if (1 < n.size()) runtime.console().loc(n).pln(": extended asm");
visit(n);
}
/** Visit the specified node. */
public void visit(Node n) {
table.enter(n);
for (Object o : n) {
if (o instanceof Node) dispatch((Node)o);
}
table.exit(n);
}
/**
* Get the specified node's type from the corresponding property.
*
* @param node The node.
* @return The corresponding type.
*/
public static final Type toType(Node node) {
Type t = (Type)node.getProperty(Constants.TYPE);
assert null != t;
return t;
}
/**
* Determine whether the specified type represents a zero-length
* array.
*
* @param type The type.
* @return <code>true</code> if the type is a zero-length array.
*/
public static boolean isZeroLength(Type type) {
type = type.resolve();
if (! type.isArray()) return false;
ArrayT array = type.toArray();
return array.hasLength() && 0 == array.getLength();
}
/**
* Get the specified type's description.
*
* @param type The type.
* @return The corresponding description or <code>null</code> if
* there is no description.
*/
public static String toDescription(Type type) {
String s = null;
if (type.hasTagged()) {
Tagged tagged = type.toTagged();
if (tagged.isEnum()) {
s = "enum";
} else if (tagged.isStruct()) {
s = "struct";
} else {
s = "union";
}
if (! tagged.isUnnamed()) {
s = s + ' ' + tagged.getName();
}
}
return s;
}
/**
* Get the specified string constant's text.
*
* @param node The string constant node.
* @return The corresponding text.
*/
public final String toText(GNode node) {
assert node.hasName("StringConstant");
return ((StringReference)toType(node).getShape()).getLiteral();
}
}