/* * xtc - The eXTensible Compiler * Copyright (C) 2007-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.type; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import xtc.Constants.FuzzyBoolean; import xtc.tree.Locatable; /** * Common type operations for Java ASTs. * * @author Robert Grimm * @version $Revision: 1.9 $ */ public class JavaAST extends AST { /** The set of Java primitive types. */ public static final Set<String> PRIMITIVES; /** The set of Java modifiers. */ public static final Set<String> MODIFIERS; /** The set of Java keywords besides primitive types, modifiers, and void. */ public static final Set<String> KEYWORDS; static { Set<String> primitives = new HashSet<String>(); primitives.add("byte"); primitives.add("short"); primitives.add("char"); primitives.add("int"); primitives.add("long"); primitives.add("float"); primitives.add("double"); primitives.add("boolean"); PRIMITIVES = Collections.unmodifiableSet(primitives); Set<String> modifiers = new HashSet<String>(); modifiers.add("public"); modifiers.add("protected"); modifiers.add("private"); modifiers.add("static"); modifiers.add("abstract"); modifiers.add("final"); modifiers.add("native"); modifiers.add("synchronized"); modifiers.add("transient"); modifiers.add("volatile"); modifiers.add("strictfp"); MODIFIERS = Collections.unmodifiableSet(primitives); Set<String> keywords = new HashSet<String>(); keywords.add("assert"); keywords.add("break"); keywords.add("case"); keywords.add("catch"); keywords.add("class"); keywords.add("const"); keywords.add("continue"); keywords.add("default"); keywords.add("do"); keywords.add("else"); keywords.add("enum"); keywords.add("extends"); keywords.add("finally"); keywords.add("for"); keywords.add("if"); keywords.add("goto"); keywords.add("implements"); keywords.add("import"); keywords.add("instanceof"); keywords.add("interface"); keywords.add("new"); keywords.add("package"); keywords.add("return"); keywords.add("super"); keywords.add("switch"); keywords.add("this"); keywords.add("throw"); keywords.add("throws"); keywords.add("try"); keywords.add("while"); KEYWORDS = Collections.unmodifiableSet(keywords); } // ========================================================================= /** The map from Java class names to their classes. */ protected Map<String,Class<?>> resolvedTypes; /** Create a new Java AST instance. */ public JavaAST() { resolvedTypes = new HashMap<String,Class<?>>(); } // ========================================================================= public void initialize(boolean hasNode, boolean hasToken, boolean hasFormatting, boolean hasAction) { externToIntern.clear(); internToExtern.clear(); externToIntern.put("?", Wildcard.TYPE); externToIntern.put("void", VoidT.TYPE); externToIntern.put("Object", ANY); externToIntern.put("java.lang.Object", ANY); externToIntern.put("Character", CHAR); externToIntern.put("java.lang.Character", CHAR); externToIntern.put("String", STRING); externToIntern.put("java.lang.String", STRING); if (hasToken) externToIntern.put("Token", TOKEN); externToIntern.put("xtc.tree.Token", TOKEN); if (hasNode) externToIntern.put("Node", NODE); externToIntern.put("xtc.tree.Node", NODE); externToIntern.put("generic", GENERIC); if (hasNode) externToIntern.put("GNode", NODE); externToIntern.put("xtc.tree.GNode", NODE); if (hasFormatting) externToIntern.put("Formatting", FORMATTING); externToIntern.put("xtc.tree.Formatting", FORMATTING); externToIntern.put("Pair", WILD_LIST); externToIntern.put("xtc.util.Pair", WILD_LIST); if (hasAction) externToIntern.put("Action", WILD_ACTION); externToIntern.put("xtc.util.Action", WILD_ACTION); internToExtern.put("?", "?"); internToExtern.put("void", "Void"); internToExtern.put("unit", "Void"); internToExtern.put("any", "Object"); internToExtern.put("char", "Character"); internToExtern.put("string", "String"); internToExtern.put("token", "Token"); internToExtern.put("node", "Node"); internToExtern.put("formatting", "Formatting"); internToExtern.put("list", "Pair"); internToExtern.put("action", "Action"); } // ========================================================================== public boolean isVoid(String s) { return "void".equals(s); } public boolean isGenericNode(String s) { return "generic".equals(s); } // ========================================================================== /** The start index for a fully qualified list's element type. */ private static final int QLIST_IDX = "xtc.util.Pair<".length(); /** The start index for a list's element type. */ private static final int LIST_IDX = "Pair<".length(); protected Type internList(String s) { if (s.startsWith("xtc.util.Pair<")) { return new InstantiatedT(intern(s.substring(QLIST_IDX, s.length()-1)), LIST); } else if (s.startsWith("Pair<")) { return new InstantiatedT(intern(s.substring(LIST_IDX, s.length()-1)), LIST); } else { return ErrorT.TYPE; } } /** The start index for a fully qualified action's element type. */ private static final int QACTION_IDX = "xtc.util.Action<".length(); /** The start index for an action's element type. */ private static final int ACTION_IDX = "Action<".length(); protected Type internAction(String s) { if (s.startsWith("xtc.util.Action<")) { return new InstantiatedT(intern(s.substring(QACTION_IDX, s.length()-1)), ACTION); } else if (s.startsWith("Action<")) { return new InstantiatedT(intern(s.substring(ACTION_IDX, s.length()-1)), ACTION); } else { return ErrorT.TYPE; } } protected Type internUser(String s) { if (PRIMITIVES.contains(s)) { throw new IllegalArgumentException("Java primitive type"); } else if (MODIFIERS.contains(s)) { throw new IllegalArgumentException("Java modifier as type"); } else if (KEYWORDS.contains(s)) { throw new IllegalArgumentException("Java keyword as type"); } final int idx = s.indexOf('<'); if (-1 == idx) { return new ClassT(s, null, null, null, null); } else { Type type = new ClassT(s.substring(0, idx), null, null, null, null); String args = s.substring(idx+1, s.length()-1); List<Parameter> parameters = new ArrayList<Parameter>(); List<Type> arguments = new ArrayList<Type>(); int count = 1, start = 0; do { int end = endOfType(args, start); parameters.add(new NamedParameter("T" + count)); arguments.add(intern(args.substring(start, end))); start = end + 1; count++; } while (start < args.length()); return new InstantiatedT(arguments, new ParameterizedT(parameters, type)); } } /** * Determine the last index (exclusive) of the generic type argument * starting at the specified index. * * @param args The generic type arguments. * @param start The start index. * @return The end index (exclusive). */ protected int endOfType(String args, int start) { final int size = args.length(); int end = start, nesting = 0; do { char c = args.charAt(end); switch (c) { case '<': nesting++; break; case '>': nesting--; break; case ',': if (0 == nesting) return end; } end++; } while (end < size); return end; } // ========================================================================== protected String externList(Type type) { return "Pair<" + extern(getArgument(type)) + ">"; } protected String externAction(Type type) { return "Action<" + extern(getArgument(type)) + ">"; } protected String externUser(Type type) { if (type.hasInstantiated() || type.hasParameterized()) { StringBuilder buf = new StringBuilder(); buf.append(extern(type.resolve())); buf.append('<'); Iterator<? extends Type> iter = type.hasInstantiated() ? type.toInstantiated().getArguments().iterator() : type.toParameterized().getParameters().iterator(); do { buf.append(extern(iter.next())); if (iter.hasNext()) buf.append(','); } while (iter.hasNext()); buf.append('>'); return buf.toString(); } else { if (! type.resolve().isClass()) System.out.println(type); return type.resolve().toClass().getQName(); } } // ========================================================================== protected FuzzyBoolean hasLocationUser(Type type) { final Class<?> k = resolve(type.resolve().toClass().getQName()); if ((null == k) || Object.class.equals(k)) { return FuzzyBoolean.MAYBE; } else if (Locatable.class.isAssignableFrom(k)) { return FuzzyBoolean.TRUE; } else { return FuzzyBoolean.FALSE; } } // ========================================================================== protected Type unifyUser(Type t1, Type t2, boolean strict) { Type r1 = t1.resolve(), r2 = t2.resolve(); if (r1.isClass() && r2.isClass() && r1.toClass().getQName().equals(r2.toClass().getQName())) { return t1; } else if (strict) { return ErrorT.TYPE; } else { return ANY; } } // ========================================================================== /** * Resolve the specified type name to its class. This method relies * on the {@link #importedTypes} and {@link #importedModules} data * structures to map incomplete type names to fully qualified type * names. It caches results in the {@link #resolvedTypes} data * structure to speed up future resolutions. * * @param name The type name. * @return The corresponding class or <code>null</code> if the name * cannot be resolved. */ public Class<?> resolve(String name) { if (resolvedTypes.containsKey(name)) return resolvedTypes.get(name); Class<?> k = null; if (importedTypes.containsKey(name)) { // The type has been explicitly imported. try { k = Class.forName(importedTypes.get(name)); } catch (ClassNotFoundException x) { // Ignore. } } else { // The type should have been implicitly imported. for (String module : importedModules) { try { k = Class.forName(module + name); break; } catch (ClassNotFoundException x) { // Ignore. } } if (null == k) { // As a last resort, try the name directly. try { k = Class.forName(name); } catch (ClassNotFoundException x) { // Ignore. } } } // Remember the resolved class. resolvedTypes.put(name, k); return k; } }