/* * 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(); } }