/* * 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 xtc.Constants; import xtc.Limits; import xtc.tree.Attribute; import xtc.tree.GNode; import xtc.tree.Location; import xtc.tree.Node; import xtc.tree.Token; import xtc.tree.Visitor; import xtc.type.AliasT; import xtc.type.ArrayT; import xtc.type.BooleanT; import xtc.type.C; import xtc.type.CastReference; import xtc.type.Constant; import xtc.type.DynamicReference; import xtc.type.EnumT; import xtc.type.EnumeratorT; import xtc.type.ErrorT; import xtc.type.FieldReference; import xtc.type.FunctionT; import xtc.type.InternalT; import xtc.type.LabelT; import xtc.type.NullReference; import xtc.type.NumberT; import xtc.type.PointerT; import xtc.type.Reference; import xtc.type.StaticReference; import xtc.type.StringReference; import xtc.type.StructOrUnionT; import xtc.type.StructT; import xtc.type.Tagged; import xtc.type.Type; import xtc.type.Type.Tag; import xtc.type.UnionT; import xtc.type.VariableT; import xtc.type.VoidT; import xtc.util.Pair; import xtc.lang.cpp.Syntax.Kind; import xtc.lang.cpp.Syntax.LanguageTag; import xtc.lang.cpp.Syntax.ConditionalTag; import xtc.lang.cpp.Syntax.DirectiveTag; import xtc.lang.cpp.Syntax.Layout; import xtc.lang.cpp.Syntax.Language; import xtc.lang.cpp.Syntax.Text; import xtc.lang.cpp.Syntax.Directive; import xtc.lang.cpp.Syntax.Conditional; import xtc.lang.cpp.PresenceConditionManager.PresenceCondition; import xtc.lang.cpp.ForkMergeParser.StackFrame; import xtc.lang.cpp.ForkMergeParser.Subparser; /** * This class implements the generated CActionsInterface. * * @author Paul Gazzillo * @version $Revision: 1.3 $ */ public class CTypeChecker extends CActionsInterface { /** True when statistics should be output. */ private boolean languageStatistics; /** Construct a new C semantic actions plugin. */ private CTypeChecker() { } /** The instance of this class */ private static CTypeChecker ref; /** Get the instance of this class */ public static CTypeChecker getInstance() { if (null == ref) { ref = new CTypeChecker(); } return ref; } /** * Turn language statistics collection on. Default is off. * * @param b True is on. */ public void collectStatistics(boolean b) { languageStatistics = b; } protected static class Specifiers { /** Presence condition. */ public PresenceCondition pc; /** True if there is a type error. */ public boolean error; /** The type-error message. */ public String error_message; /** The flag for whether a tag reference is a declaration. */ public boolean refIsDecl; /** The type, if any. */ public Type type; /** The storage class attribute, if any. */ public Attribute storage; /** The thread-local attribute, if any. */ public Attribute threadlocal; /** The inline attribute, if any. */ public Attribute function; /** Any other attributes. */ public List<Attribute> attributes; // The internal state for tracking type specifiers. public boolean seenSigned; public boolean seenUnsigned; public boolean seenBool; public boolean seenChar; public boolean seenShort; public boolean seenInt; public int longCount; public boolean seenFloat; public boolean seenDouble; public boolean seenComplex; public Specifiers(PresenceCondition pc) { this.pc = pc; } public Specifiers(PresenceCondition pc, Specifiers s) { this(pc); this.error = s.error; this.error_message = s.error_message; this.refIsDecl = s.refIsDecl; this.type = s.type; this.storage = s.storage; this.threadlocal = s.threadlocal; this.function = s.function; this.attributes = s.attributes; this.seenSigned = s.seenSigned; this.seenUnsigned = s.seenUnsigned; this.seenBool = s.seenBool; this.seenChar = s.seenChar; this.seenShort = s.seenShort; this.seenInt = s.seenInt; this.longCount = s.longCount; this.seenFloat = s.seenFloat; this.seenDouble = s.seenDouble; this.seenComplex = s.seenComplex; } /** Test for previous type. */ protected boolean hasType() { return (seenBool || seenChar || seenShort || seenInt || (0 < longCount) || seenFloat || seenDouble || seenComplex || (null != type)); } /** Report error indicating multiple types. */ protected void multipleTypes() { this.error = true; this.error_message = "multiple data types in declaration specifiers"; type = ErrorT.TYPE; } public void setError(String msg) { this.error = true; this.error_message = msg; System.err.println("error in specifiers: " + msg); } // Get specs. Gets list of specifiers, checking for type errors. // Also need to hoist conditionals around complete type // specifiers. Is it possible to make a hoisting tree-walker? // Walk tree, when encountering conditional, fork the spec state // and walk both. No need for merge, since there are two // different declarations. } protected void typeDispatch(Visitor v, Object o) { if (o instanceof Syntax) { v.dispatch((Syntax) o); } else if (o instanceof GNode) { v.dispatch((GNode) o); } else { System.err.println("internal error: invalid AST type. shouldn't happen. " + o.getClass()); } } /** * For a given tree node containing declaration specificers, return * a list of Specifiers classes hoisted around conditionals. * * @param spec_tree Type specifier subtree, potentially with * conditional nodes. * @param specs The current Specifiers object to continue analyzing * from. * @return A list of Specifier objects. */ protected List<Specifiers> getSpecifiers(Object spec_tree, final Specifiers specs) { final List<Specifiers> hoisted_specs = new LinkedList<Specifiers>(); final Visitor v = new Visitor() { public void visit(GNode n) { for (Object o : n) { typeDispatch(this, o); } } public void visitConditional(GNode n) { // stop if there is a type error // filter out infeasible configurations // update pc of spec object, create new spec object // recursively call getSpecifiers on both objects, passing // the child System.err.println("TODO, fork"); typeDispatch(this, n.get(1)); } public void visitStorageClass(GNode n) { LanguageTag tag = (((Syntax) n.get(0)).toLanguage()).tag(); String token = CParseTables.getInstance().yytname[tag.getID()]; String name = tag.getText(); Attribute storage; if (token.equals("AUTO")) { storage = Constants.ATT_STORAGE_AUTO; } else if (token.equals("EXTERN")) { storage = Constants.ATT_STORAGE_EXTERN; } else if (token.equals("REGISTER")) { storage = Constants.ATT_STORAGE_REGISTER; } else if (token.equals("STATIC")) { storage = Constants.ATT_STORAGE_STATIC; } else if (token.equals("TYPEDEF")) { storage = Constants.ATT_STORAGE_TYPEDEF; } else { storage = null; error("unsupported storage class found " + name); } if (null == specs.storage) { specs.storage = storage; } else if (specs.storage == storage) { specs.setError("duplicate '" + name + "'"); } else { specs.setError("multiple storage classes in declaration specifiers"); } System.err.println(specs.storage); } public void visitBasicType(GNode n) { LanguageTag tag = (((Syntax) n.get(0)).toLanguage()).tag(); String token = CParseTables.getInstance().yytname[tag.getID()]; String name = tag.getText(); if (token.equals("VOID")) { if (specs.hasType()) { specs.multipleTypes(); } else { specs.type = VoidT.TYPE; } } else if (token.equals("CHAR")) { if (specs.hasType()) { specs.multipleTypes(); } else { specs.seenChar = true; } } else if (token.equals("SHORT")) { if (specs.seenBool || specs.seenChar || specs.seenShort || (0 < specs.longCount) || specs.seenFloat || specs.seenDouble || specs.seenComplex || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenShort = true; } } else if (token.equals("INT")) { if (specs.seenBool || specs.seenChar || specs.seenInt || specs.seenFloat || specs.seenDouble || specs.seenComplex || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenInt = true; } } else if (token.equals("LONG")) { if (specs.seenBool || specs.seenChar || specs.seenShort || (1 < specs.longCount) || specs.seenFloat || ((specs.seenDouble || specs.seenComplex) && (0 < specs.longCount)) || (null != specs.type)) { specs.multipleTypes(); } else { specs.longCount++; } } else if (token.equals("FLOAT")) { if (specs.seenBool || specs.seenChar || specs.seenShort || specs.seenInt || (0 < specs.longCount) || specs.seenDouble || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenFloat = true; } } else if (token.equals("DOUBLE")) { if (specs.seenBool || specs.seenChar || specs.seenShort || specs.seenInt || (1 < specs.longCount) || specs.seenFloat || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenDouble = true; } } else if (token.equals("UNSIGNED")) { if (specs.seenSigned) { specs.seenUnsigned = true; specs.setError("both 'signed' and 'unsigned' in declaration specifiers"); } else if (specs.seenUnsigned) { specs.setError("duplicate 'unsigned'"); } else { specs.seenUnsigned = true; } } else if (token.equals("_BOOL")) { if (specs.hasType()) { specs.multipleTypes(); } else { specs.seenBool = true; } } else { error("unsupported basic type found " + name); } } public void visitComplexKeyword(GNode n) { if (specs.seenBool || specs.seenChar || specs.seenShort || specs.seenInt || (1 < specs.longCount) || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenComplex = true; } } public void visitSignedKeyword(GNode n) { if (specs.seenUnsigned) { specs.seenSigned = true; specs.setError("both 'signed' and 'unsigned' in declaration specifiers"); } else if (specs.seenSigned) { specs.setError("duplicate 'signed'"); } else { specs.seenSigned = true; } } public void visit(Syntax s) { System.err.println("TODO unchecked syntax object: " + s); } }; typeDispatch(v, spec_tree); // if (spec_tree instanceof Syntax) { // if (((Syntax) spec_tree).kind() == Syntax.Kind.LANGUAGE) { // System.err.println(spec_tree); // } else { // System.err.println("internal error: invalid Syntax type. shouldn't happen"); // } // } else if (spec_tree instanceof GNode) { // Visitor v = new Visitor() { // public void visit(GNode n) { // dispatch(n.getGeneric(0)); // } // }; // v.dispatch((GNode) spec_tree); // } else { // System.err.println("internal error: invalid AST type. shouldn't happen"); // } // for each configuration, verify that it has a valid set of // specifiers return null; } public void BindIdentifier(Subparser subparser) { StackFrame stack = subparser.stack; PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); CTypeContext scope = (CTypeContext) subparser.scope; boolean typedef; Language ident; // Get the AST nodes that contain the type specifier and the // declarator. Object a = stack.get(3).value; Object b = stack.get(2).value; List<Specifiers> hoisted_specs = getSpecifiers(a, new Specifiers(presenceCondition)); System.err.println("decl: " + b); // c-decl.c, grokdeclarator and decl_context // Check whether the declaration is a typedef. This assumes that // the typedef keyword is the first token of the declaration. Language t; while (true) { if (a instanceof Node && ! (a instanceof Syntax)) { Node n = (Node) a; if (n.hasName(ForkMergeParser.CHOICE_NODE_NAME)) { // When it's a conditional node, the first child is a // presence condition, the second is the first AST child. a = n.get(1); } else { a = n.get(0); } } else if (a instanceof Pair) { a = ((Pair) a).head(); } else { break; } } t = (Language) a; // Get the identifier in the declarator. ident = getident(b); if (CTag.TYPEDEF == t.tag()) { // Bind a typedef name. typedef = true; if (languageStatistics) { if (typedef) { Location location = subparser.lookahead.token.syntax.getLocation(); System.err.println(String.format("typedef %s %s", ident, location)); } } } else { // Binding a variable name. typedef = false; } scope.bind(ident.getTokenText(), typedef, presenceCondition); } public void BindIdentifierInList(Subparser subparser) { StackFrame stack = subparser.stack; PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); CTypeContext scope = (CTypeContext) subparser.scope; boolean typedef; Language ident; // Get the AST nodes that contain the type specifier and the // declarator. Object a = stack.get(5).value; Object b = stack.get(2).value; // Check whether the declaration is a typedef. This assumes that // the typedef keyword is the first token of the declaration. Language t; while (true) { if (a instanceof Node && ! (a instanceof Syntax)) { Node n = (Node) a; if (n.hasName(ForkMergeParser.CHOICE_NODE_NAME)) { // When it's a conditional node, the first child is a // presence condition, the second is the first AST child. a = n.get(1); } else { a = n.get(0); } } else if (a instanceof Pair) { a = ((Pair) a).head(); } else { break; } } t = (Language) a; // Get the identifier in the declarator. ident = getident(b); if (CTag.TYPEDEF == t.tag()) { // Bind a typedef name. typedef = true; if (languageStatistics) { if (typedef) { Location location = subparser.lookahead.token.syntax.getLocation(); System.err.println(String.format("typedef %s %s", ident, location)); } } } else { // Bind a variable name. typedef = false; } scope.bind(ident.getTokenText(), typedef, presenceCondition); } public void BindVar(Subparser subparser) { StackFrame stack = subparser.stack; PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); CTypeContext scope = (CTypeContext) subparser.scope; // Get the identifier in the declarator. Object b = stack.get(2).value; Language<?> ident = getident(b); // Bind variable name. scope.bind(ident.getTokenText(), false, presenceCondition); } public void BindEnum(Subparser subparser) { StackFrame stack = subparser.stack; PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); CTypeContext scope = (CTypeContext) subparser.scope; // Get the identifier in the declarator. The identifier must // occur after an IdentifierOrTypedefName token. Object b = stack.get(2).value; String ident = getident(b).getTokenText(); // Bind variable name. scope.bind(ident, false, presenceCondition); } public void EnterScope(Subparser subparser) { PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); subparser.scope = ((CTypeContext) subparser.scope).enterScope(presenceCondition); } public void ExitScope(Subparser subparser) { PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); subparser.scope = ((CTypeContext) subparser.scope).exitScope(presenceCondition); } public void ExitReentrantScope(Subparser subparser) { PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); subparser.scope = ((CTypeContext) subparser.scope).exitReentrantScope(presenceCondition); } public void ReenterScope(Subparser subparser) { PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); subparser.scope = ((CTypeContext) subparser.scope).reenterScope(presenceCondition); } public void KillReentrantScope(Subparser subparser) { PresenceConditionManager.PresenceCondition presenceCondition = subparser.getPresenceCondition(); subparser.scope = ((CTypeContext) subparser.scope).killReentrantScope(presenceCondition); } /** * Find the identifier or typedef name in a declarator. Assume * the first identifier in the subtree is the var or typedef name. * * @param o The semantic value. * @return The first identifier in the subtree or null if there is * none. */ private static Language getident(Object o) { if (o instanceof Language) { Language token = ((Language) o); if (CTag.IDENTIFIER == token.tag() || CTag.TYPEDEFname == token.tag()) { return token; } else { return null; } } else if (o instanceof Pair) { Pair<?> b = (Pair<?>) o; while (b != Pair.empty()) { Object child = b.head(); if (null != child) { Language ident = getident(child); if (null != ident) { return ident; } } b = b.tail(); } return null; } else if (o instanceof PresenceConditionManager.PresenceCondition) { return null; } else { Node b = (Node) o; for (int i = 0; i < b.size(); i++) { Object child = b.get(i); if (null != child) { Language ident = getident(child); if (null != ident) { return ident; } } } return null; } } public void Declaration(Subparser parser) { System.err.println("encountered declaration"); } public void CompoundStatement(Subparser parser) { System.err.println("encountered compoundstatement"); } public void FunctionCompoundStatement(Subparser parser) { System.err.println("encountered functioncompoundstatement"); } private void error(String msg) { System.err.println("error: " + msg); System.exit(1); } // Temporary Specifiers specs = new Specifiers(null); public void StorageClass(Subparser parser) { GNode n = (GNode) parser.stack.value; LanguageTag tag = (((Syntax) n.get(0)).toLanguage()).tag(); String token = CParseTables.getInstance().yytname[tag.getID()]; String name = tag.getText(); Attribute storage; if (token.equals("AUTO")) { storage = Constants.ATT_STORAGE_AUTO; } else if (token.equals("EXTERN")) { storage = Constants.ATT_STORAGE_EXTERN; } else if (token.equals("REGISTER")) { storage = Constants.ATT_STORAGE_REGISTER; } else if (token.equals("STATIC")) { storage = Constants.ATT_STORAGE_STATIC; } else if (token.equals("TYPEDEF")) { storage = Constants.ATT_STORAGE_TYPEDEF; } else { storage = null; error("unsupported storage class found " + name); } if (null == specs.storage) { specs.storage = storage; } else if (specs.storage == storage) { specs.setError("duplicate '" + name + "'"); } else { specs.setError("multiple storage classes in declaration specifiers"); } System.err.println(specs.storage); } public void BasicType(Subparser parser) { GNode n = (GNode) parser.stack.value; LanguageTag tag = (((Syntax) n.get(0)).toLanguage()).tag(); String token = CParseTables.getInstance().yytname[tag.getID()]; String name = tag.getText(); if (token.equals("VOID")) { if (specs.hasType()) { specs.multipleTypes(); } else { specs.type = VoidT.TYPE; } } else if (token.equals("CHAR")) { if (specs.hasType()) { specs.multipleTypes(); } else { specs.seenChar = true; } } else if (token.equals("SHORT")) { if (specs.seenBool || specs.seenChar || specs.seenShort || (0 < specs.longCount) || specs.seenFloat || specs.seenDouble || specs.seenComplex || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenShort = true; } } else if (token.equals("INT")) { if (specs.seenBool || specs.seenChar || specs.seenInt || specs.seenFloat || specs.seenDouble || specs.seenComplex || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenInt = true; } } else if (token.equals("LONG")) { if (specs.seenBool || specs.seenChar || specs.seenShort || (1 < specs.longCount) || specs.seenFloat || ((specs.seenDouble || specs.seenComplex) && (0 < specs.longCount)) || (null != specs.type)) { specs.multipleTypes(); } else { specs.longCount++; } } else if (token.equals("FLOAT")) { if (specs.seenBool || specs.seenChar || specs.seenShort || specs.seenInt || (0 < specs.longCount) || specs.seenDouble || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenFloat = true; } } else if (token.equals("DOUBLE")) { if (specs.seenBool || specs.seenChar || specs.seenShort || specs.seenInt || (1 < specs.longCount) || specs.seenFloat || (null != specs.type)) { specs.multipleTypes(); } else { specs.seenDouble = true; } } else if (token.equals("UNSIGNED")) { if (specs.seenSigned) { specs.seenUnsigned = true; specs.setError("both 'signed' and 'unsigned' in declaration specifiers"); } else if (specs.seenUnsigned) { specs.setError("duplicate 'unsigned'"); } else { specs.seenUnsigned = true; } } else if (token.equals("_BOOL")) { if (specs.hasType()) { specs.multipleTypes(); } else { specs.seenBool = true; } } else { error("unsupported basic type found " + name); } } // public void ComplexKeyword(Subparser parser) { // if (specs.seenBool || specs.seenChar || specs.seenShort || specs.seenInt || (1 < specs.longCount) || // (null != specs.type)) { // specs.multipleTypes(); // } else { // specs.seenComplex = true; // } // } // public void SignedKeyword(Subparser parser) { // if (specs.seenUnsigned) { // specs.seenSigned = true; // specs.setError("both 'signed' and 'unsigned' in declaration specifiers"); // } else if (specs.seenSigned) { // specs.setError("duplicate 'signed'"); // } else { // specs.seenSigned = true; // } // } }