0001: /*
0002: * xtc - The eXTensible Compiler
0003: * Copyright (C) 2005-2007 Robert Grimm
0004: *
0005: * This library is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU Lesser General Public License
0007: * version 2.1 as published by the Free Software Foundation.
0008: *
0009: * This library is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: * Lesser General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU Lesser General Public
0015: * License along with this library; if not, write to the Free Software
0016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0017: * USA.
0018: */
0019: package xtc.util;
0020:
0021: import java.util.ArrayList;
0022: import java.util.HashMap;
0023: import java.util.Iterator;
0024: import java.util.List;
0025: import java.util.Map;
0026:
0027: import xtc.Constants;
0028:
0029: import xtc.tree.Node;
0030: import xtc.tree.Printer;
0031:
0032: /**
0033: * A symbol table. This class implements a symbol table, which maps
0034: * symbols represented as strings to values of any type. The mapping
0035: * is organized into hierarchical {@link Scope scopes}, which allows
0036: * for multiple definitions of the same symbol across different
0037: * scopes. Additionally, a symbol may have multiple definitions
0038: * within the same scope: if the corresponding value is a Java
0039: * collections framework list, it is recognized as a multiply defined
0040: * symbol. Scopes are named, with names being represented as strings.
0041: * Both scope names and symbols can be unqualified — that is,
0042: * they need to be resolved relative to the {@link #current() current
0043: * scope} — or qualified by the {@link Constants#QUALIFIER
0044: * qualification character} '<code>.</code>' — that is, they are
0045: * resolved relative to the symbol table's {@link #root() root}. Once
0046: * {@link #enter(String) created}, a scope remains in the symbol table
0047: * and the corresponding AST node should be associated with that scope
0048: * by setting the corresponding {@link Constants#SCOPE property} to
0049: * the scope's qualified name. Subsequent traversals over that node
0050: * can then automatically {@link #enter(Node) enter} and {@link
0051: * #exit(Node) exit} that scope. Alternatively, if traversing out of
0052: * tree order, the current scope can be set {@link
0053: * #setScope(SymbolTable.Scope) explicitly}.
0054: *
0055: * <p />To support different name spaces within the same scope, this
0056: * class can optionally {@link #toNameSpace mangle} and {@link
0057: * #fromNameSpace unmangle} unqualified symbols. By convention, a
0058: * name in any name space besides the default name space is prefixed
0059: * by the name of the name space and an opening parenthesis
0060: * '<code>(</code>' and suffixed by a closing parenthesis
0061: * '<code>)</code>'.
0062: *
0063: * @author Robert Grimm
0064: * @version $Revision: 1.34 $
0065: */
0066: public class SymbolTable {
0067:
0068: /**
0069: * A symbol table scope. A scope has a name and may have a parent
0070: * (unless it is the root scope), one or more nested scopes, and one
0071: * or more definitions.
0072: */
0073: public static class Scope {
0074:
0075: /** The name. */
0076: String name;
0077:
0078: /** The fully qualified name. */
0079: String qName;
0080:
0081: /** The parent scope. */
0082: Scope parent;
0083:
0084: /** The nested scopes, if any. */
0085: Map<String, Scope> scopes;
0086:
0087: /** The map from symbols to values, if any. */
0088: Map<String, Object> symbols;
0089:
0090: /**
0091: * Create a new root scope with the specified name, which may be
0092: * the empty string.
0093: *
0094: * @param name The name.
0095: */
0096: Scope(String name) {
0097: this .name = name;
0098: this .qName = name;
0099: }
0100:
0101: /**
0102: * Create a new nested scope with the specified unqualified name
0103: * and parent.
0104: *
0105: * @param name The unqualified name.
0106: * @param parent The parent.
0107: * @throws IllegalArgumentException
0108: * Signals that the specified parent already has a nested scope
0109: * with the specified name.
0110: */
0111: Scope(String name, Scope parent) {
0112: if ((null != parent.scopes)
0113: && parent.scopes.containsKey(name)) {
0114: throw new IllegalArgumentException("Scope "
0115: + parent.qName + " already contains scope "
0116: + name);
0117: }
0118: this .name = name;
0119: this .qName = Utilities.qualify(parent.qName, name);
0120: this .parent = parent;
0121: if (null == parent.scopes) {
0122: parent.scopes = new HashMap<String, Scope>();
0123: }
0124: parent.scopes.put(name, this );
0125: }
0126:
0127: /**
0128: * Get this scope's unqualfied name.
0129: *
0130: * @return This scope's unqualified name.
0131: */
0132: public String getName() {
0133: return name;
0134: }
0135:
0136: /**
0137: * Get this scope's qualified name.
0138: *
0139: * @return This scope's qualified name.
0140: */
0141: public String getQualifiedName() {
0142: return qName;
0143: }
0144:
0145: /**
0146: * Update this scope's qualified name relative to the parent
0147: * scope's qualified name. This method also requalifies any
0148: * nested scopes' qualified names. It must not be called on the
0149: * root scope.
0150: */
0151: void requalify() {
0152: qName = Utilities.qualify(parent.qName, name);
0153:
0154: if (null != scopes) {
0155: for (Scope scope : scopes.values()) {
0156: scope.requalify();
0157: }
0158: }
0159: }
0160:
0161: /**
0162: * Determine whether this scope is the root scope.
0163: *
0164: * @return <code>true</code> if this scope is the root scope.
0165: */
0166: public boolean isRoot() {
0167: return (null == parent);
0168: }
0169:
0170: /**
0171: * Get this scope's parent.
0172: *
0173: * @return This scope's parent scope or <code>null</code> if this
0174: * scope does not have a parent (i.e., is the root scope).
0175: */
0176: public Scope getParent() {
0177: return parent;
0178: }
0179:
0180: /**
0181: * Determine whether this scope has any nested scopes.
0182: *
0183: * @return <code>true</code> if this scope has any nested scopes.
0184: */
0185: public boolean hasNested() {
0186: return ((null != scopes) && (0 < scopes.size()));
0187: }
0188:
0189: /**
0190: * Get an iterator over the names of all nested scopes.
0191: *
0192: * @return An iterator over the nested scopes.
0193: */
0194: public Iterator<String> nested() {
0195: if (null == scopes) {
0196: return EmptyIterator.value();
0197: } else {
0198: return scopes.keySet().iterator();
0199: }
0200: }
0201:
0202: /**
0203: * Determine whether this scope has the specified unqualified
0204: * nested scope.
0205: *
0206: * @param name The nested scope's unqualified name.
0207: * @return <code>true</code> if the corresponding scope exists.
0208: */
0209: public boolean hasNested(String name) {
0210: return (null != getNested(name));
0211: }
0212:
0213: /**
0214: * Get the nested scope with the specified unqualified name.
0215: *
0216: * @param name The nested scope's unqualified name.
0217: * @return The corresponding scope or <code>null</code> if there is
0218: * no such scope.
0219: */
0220: public Scope getNested(String name) {
0221: return (null == scopes) ? null : scopes.get(name);
0222: }
0223:
0224: /**
0225: * Determine whether the scope with the specified unqualified name
0226: * can be merged into this scope. A nested scope can be merged if
0227: * it (1) does not contain any bindings with the same names as
0228: * this scope's bindings and (2) does not have any children with
0229: * the same names as this scope's children.
0230: *
0231: * @param name The nested scope's unqualified name.
0232: * @return <code>true</code> if the scope can be merged.
0233: * @throws IllegalArgumentException Signals that this scope does
0234: * not have a nested scope with the specified name.
0235: */
0236: public boolean isMergeable(String name) {
0237: Scope nested = getNested(name);
0238:
0239: if (null == nested) {
0240: throw new IllegalArgumentException("Scope " + qName
0241: + " does not " + " contain scope " + name);
0242: }
0243:
0244: if (null != nested.scopes) {
0245: // Note that this scope must have nested scopes, since we just
0246: // looked one up.
0247: for (String s : nested.scopes.keySet()) {
0248: if ((!s.equals(name)) && this .scopes.containsKey(s)) {
0249: return false;
0250: }
0251: }
0252: }
0253:
0254: if ((null != this .symbols) && (null != nested.symbols)) {
0255: for (String s : nested.symbols.keySet()) {
0256: if (this .symbols.containsKey(s)) {
0257: return false;
0258: }
0259: }
0260: }
0261:
0262: return true;
0263: }
0264:
0265: /**
0266: * Merge the nested scope with the specified unqualified name into
0267: * this scope.
0268: *
0269: * @param name The nested scope's unqualified name.
0270: * @throws IllegalArgumentException Signals that (1) this scope
0271: * does not have a nested scope with the specified name, (2) any
0272: * of the nested scope's children has the same name as one of
0273: * this scope's children, or (3) any of the nested scope's
0274: * bindings has the same name as one of this scope's bindings.
0275: */
0276: public void merge(String name) {
0277: final Scope nested = getNested(name);
0278:
0279: // Make sure the nested scope is mergeable. Note that the
0280: // nested scope must exist in the consequence of the
0281: // if-statement, since isMergeable signals an exception for
0282: // non-existent scopes.
0283: if (!isMergeable(name)) {
0284: throw new IllegalArgumentException("Scope "
0285: + nested.qName
0286: + " cannot be merged into the parent");
0287: }
0288:
0289: // Remove the nested scope.
0290: this .scopes.remove(name);
0291:
0292: // Add the nested scope's children.
0293: if (null != nested.scopes) {
0294: this .scopes.putAll(nested.scopes);
0295:
0296: for (Scope s : nested.scopes.values()) {
0297: s.parent = this ;
0298: s.requalify();
0299: }
0300: }
0301:
0302: // Add the nested scope's bindings.
0303: if (null != nested.symbols) {
0304: if (null == this .symbols) {
0305: this .symbols = nested.symbols;
0306: } else {
0307: this .symbols.putAll(nested.symbols);
0308: }
0309: }
0310:
0311: // Invalidate the nested scope.
0312: nested.parent = null;
0313: nested.name = null;
0314: nested.qName = null;
0315: nested.scopes = null;
0316: nested.symbols = null;
0317: }
0318:
0319: /**
0320: * Determine whether this scope has any local definitions.
0321: *
0322: * @return <code>true</code> if this scope has any local
0323: * definitions.
0324: */
0325: public boolean hasSymbols() {
0326: return ((null != symbols) && (0 < symbols.size()));
0327: }
0328:
0329: /**
0330: * Get an iterator over the all locally defined symbols.
0331: *
0332: * @return An iterator over the locally defined symbols.
0333: */
0334: public Iterator<String> symbols() {
0335: if (null == symbols) {
0336: return EmptyIterator.value();
0337: } else {
0338: return symbols.keySet().iterator();
0339: }
0340: }
0341:
0342: /**
0343: * Determine whether the specified symbol is defined in this
0344: * scope.
0345: *
0346: * @param symbol The unqualified symbol.
0347: * @return <code>true</code> if the symbol is defined in this
0348: * scope.
0349: */
0350: public boolean isDefinedLocally(String symbol) {
0351: return (null == symbols) ? false : symbols
0352: .containsKey(symbol);
0353: }
0354:
0355: /**
0356: * Determine whether the specified unqualified symbol is defined
0357: * in this scope or any of its ancestors.
0358: *
0359: * @param symbol The unqualified symbol.
0360: * @return <code>true</code> if the symbol is defined in this scope
0361: * or any of its ancestors.
0362: */
0363: public boolean isDefined(String symbol) {
0364: return (null != lookupScope(symbol));
0365: }
0366:
0367: /**
0368: * Determine whether the specified symbol is defined multiple
0369: * times in this scope or any of its ancestors.
0370: *
0371: * @param symbol The unqualified symbol.
0372: * @return <code>true</code> if the symbol is defined multiple
0373: * times.
0374: */
0375: public boolean isDefinedMultiply(String symbol) {
0376: Scope scope = lookupScope(symbol);
0377: return (null == scope) ? false
0378: : scope.symbols.get(symbol) instanceof List;
0379: }
0380:
0381: /**
0382: * Get the scope defining the specified unqualified symbol. This
0383: * method searches this scope and all its ancestors, returning the
0384: * first defining scope.
0385: *
0386: * @param symbol The unqualified symbol.
0387: * @return The definining scope or <code>null</code> if there is
0388: * no such scope.
0389: */
0390: public Scope lookupScope(String symbol) {
0391: Scope scope = this ;
0392: do {
0393: if ((null != scope.symbols)
0394: && (scope.symbols.containsKey(symbol))) {
0395: return scope;
0396: }
0397: scope = scope.parent;
0398: } while (null != scope);
0399: return null;
0400: }
0401:
0402: /**
0403: * Get the value for the specified unqualified symbol. This
0404: * method searches this scope and all its ancestors, returning the
0405: * value of the first definition.
0406: *
0407: * @param symbol The unqualified symbol.
0408: * @return The corresponding value or <code>null</code> if there is
0409: * no definition.
0410: */
0411: public Object lookup(String symbol) {
0412: Scope scope = lookupScope(symbol);
0413: return (null == scope) ? null : scope.symbols.get(symbol);
0414: }
0415:
0416: /**
0417: * Get the scope named by the specified unqualified symbol, which
0418: * is nested in the scope defining the symbol. This method
0419: * searches this scope and all its ancestors, up to the first
0420: * defining scope. It then looks for the nested scope with the
0421: * same name.
0422: *
0423: * @param symbol The unqualified symbol.
0424: * @return The bound scope or <code>null</code> if there is no
0425: * definition or nested scope with the same name.
0426: */
0427: public Scope lookupBoundScope(String symbol) {
0428: Scope scope = lookupScope(symbol);
0429: return (null == scope) ? null : scope.getNested(symbol);
0430: }
0431:
0432: /**
0433: * Get the local value for the specified unqualified symbol.
0434: *
0435: * @param symbol The unqualified symbol.
0436: * @return The corresponding value or <code>null</code> if there is
0437: * no local definition.
0438: */
0439: public Object lookupLocally(String symbol) {
0440: return (null == symbols) ? null : symbols.get(symbol);
0441: }
0442:
0443: /**
0444: * Set the specified symbol's value to the specified value in this
0445: * scope.
0446: *
0447: * @param symbol The unqualified symbol.
0448: * @param value The value.
0449: */
0450: public void define(String symbol, Object value) {
0451: if (null == symbols) {
0452: symbols = new HashMap<String, Object>();
0453: }
0454: symbols.put(symbol, value);
0455: }
0456:
0457: /**
0458: * Add the specified value to the specified symbol's values in
0459: * this scope.
0460: *
0461: * @param symbol The unqualified symbol.
0462: * @param value The value.
0463: */
0464: @SuppressWarnings("unchecked")
0465: public void addDefinition(String symbol, Object value) {
0466: if (null == symbols) {
0467: symbols = new HashMap<String, Object>();
0468: }
0469:
0470: if (symbols.containsKey(symbol)) {
0471: Object o = symbols.get(symbol);
0472:
0473: if (o instanceof List) {
0474: ((List<Object>) o).add(value);
0475:
0476: } else {
0477: List<Object> l = new ArrayList<Object>();
0478: l.add(o);
0479: l.add(value);
0480: symbols.put(symbol, l);
0481: }
0482:
0483: } else {
0484: symbols.put(symbol, value);
0485: }
0486: }
0487:
0488: /**
0489: * Undefine the specified unqualified symbol. If the symbol is
0490: * defined in this scope, this method removes all its values.
0491: *
0492: * @param symbol The unqualified symbol.
0493: */
0494: public void undefine(String symbol) {
0495: if (null != symbols) {
0496: symbols.remove(symbol);
0497: }
0498: }
0499:
0500: /**
0501: * Qualify the specified unqualified symbol with this scope's
0502: * name.
0503: *
0504: * @param symbol The unqualified symbol.
0505: * @return The qualified symbol.
0506: */
0507: public String qualify(String symbol) {
0508: return Utilities.qualify(qName, symbol);
0509: }
0510:
0511: /**
0512: * Dump the contents of this scope. This method pretty prints the
0513: * contents of this scope and all nested scopes with the specified
0514: * printer. If the printer is registered with a visitor, that
0515: * visitor is used for formatting any node values.
0516: *
0517: * @param printer The printer, which need not be registered with a
0518: * visitor.
0519: */
0520: public void dump(Printer printer) {
0521: boolean hasVisitor = (null != printer.visitor());
0522:
0523: printer.indent().p('.').p(name).pln(" = {").incr();
0524:
0525: if (null != symbols) {
0526: for (Map.Entry<String, Object> entry : symbols
0527: .entrySet()) {
0528: String symbol = entry.getKey();
0529: Object value = entry.getValue();
0530:
0531: printer.indent().p(symbol).p(" = ");
0532: if (null == value) {
0533: printer.p("null");
0534: } else if (hasVisitor && (value instanceof Node)) {
0535: printer.p((Node) value);
0536: } else if (value instanceof String) {
0537: printer.p('"').escape((String) value,
0538: Utilities.JAVA_ESCAPES).p('"');
0539: } else {
0540: try {
0541: printer.p(value.toString());
0542: } catch (final Exception e) {
0543: printer
0544: .p(value.getClass().getName()
0545: + "@?");
0546: }
0547: }
0548: printer.pln(';');
0549:
0550: Scope nested = getNested(symbol);
0551: if (null != nested) {
0552: nested.dump(printer);
0553: }
0554: }
0555: }
0556:
0557: if (null != scopes) {
0558: for (Scope nested : scopes.values()) {
0559: if ((null == symbols)
0560: || (!symbols.containsKey(nested.name))) {
0561: nested.dump(printer);
0562: }
0563: }
0564: }
0565:
0566: printer.decr().indent().pln("};");
0567: }
0568:
0569: }
0570:
0571: // =========================================================================
0572:
0573: /** The root scope. */
0574: protected Scope root;
0575:
0576: /** The current scope. */
0577: protected Scope current;
0578:
0579: /** The fresh name count. */
0580: protected int freshNameCount;
0581:
0582: /** The fresh identifier count. */
0583: protected int freshIdCount;
0584:
0585: // =========================================================================
0586:
0587: /**
0588: * Create a new symbol table with the empty string as the root
0589: * scope's name.
0590: */
0591: public SymbolTable() {
0592: this ("");
0593: }
0594:
0595: /**
0596: * Create a new symbol table.
0597: *
0598: * @param root The name of the root scope.
0599: */
0600: public SymbolTable(String root) {
0601: this .root = new Scope(root);
0602: current = this .root;
0603: freshNameCount = 0;
0604: freshIdCount = 0;
0605: }
0606:
0607: // =========================================================================
0608:
0609: /**
0610: * Clear this symbol table. This method deletes all scopes and
0611: * their definitions from this symbol table.
0612: */
0613: public void reset() {
0614: root.scopes = null;
0615: root.symbols = null;
0616: current = root;
0617: freshNameCount = 0;
0618: freshIdCount = 0;
0619: }
0620:
0621: /**
0622: * Get the root scope.
0623: *
0624: * @return The root scope.
0625: */
0626: public Scope root() {
0627: return root;
0628: }
0629:
0630: /**
0631: * Get the current scope.
0632: *
0633: * @return The current scope.
0634: */
0635: public Scope current() {
0636: return current;
0637: }
0638:
0639: /**
0640: * Get the scope with the specified qualified name.
0641: *
0642: * @param name The qualified name.
0643: * @return The corresponding scope or <code>null</code> if no such
0644: * scope exits.
0645: */
0646: public Scope getScope(String name) {
0647: // Optimize for the common case where the specified name denotes a
0648: // scope directly nested in the current scope.
0649: Scope scope = current;
0650: if (name.startsWith(scope.qName)
0651: && (name.lastIndexOf(Constants.QUALIFIER) == scope.qName
0652: .length())) {
0653: return scope.getNested(Utilities.getName(name));
0654: }
0655:
0656: String[] components = Utilities.toComponents(name);
0657: scope = root.name.equals(components[0]) ? root : null;
0658: int index = 1;
0659:
0660: while ((null != scope) && (index < components.length)) {
0661: scope = scope.getNested(components[index]);
0662: index++;
0663: }
0664:
0665: return scope;
0666: }
0667:
0668: /**
0669: * Set the current scope to the specified scope.
0670: *
0671: * @param scope The new current scope.
0672: * @throws IllegalArgumentException Signals that this symbol table's
0673: * root is not the specified scope's root.
0674: */
0675: public void setScope(Scope scope) {
0676: // Check the specified scope.
0677: Scope s = scope;
0678: while (null != s.parent)
0679: s = s.parent;
0680: if (s != root) {
0681: throw new IllegalArgumentException("Scope " + scope.qName
0682: + " not " + "in this symbol table " + this );
0683: }
0684:
0685: // Make the scope the current scope.
0686: current = scope;
0687: }
0688:
0689: /**
0690: * Determine whether the specified symbol is defined. If the symbol
0691: * is qualified, this method checks whether the symbol is defined in
0692: * the named scope. Otherwise, it checks whether the symbol is
0693: * defined in the current scope or one of its ancestors.
0694: *
0695: * @param symbol The symbol.
0696: * @return <code>true</code> if the specified symbol is defined.
0697: */
0698: public boolean isDefined(String symbol) {
0699: Scope scope = lookupScope(symbol);
0700: if ((null == scope) || (null == scope.symbols)) {
0701: return false;
0702: } else {
0703: return scope.symbols.containsKey(Utilities
0704: .unqualify(symbol));
0705: }
0706: }
0707:
0708: /**
0709: * Determine whether the specified symbol is define multiple times.
0710: * If the symbol is qualified, this method checks whether the symbol
0711: * has multiple definitions in the named scope. Otherwise, it
0712: * checks whether the symbol has multiple definitions in the current
0713: * scope or one of its ancestors.
0714: *
0715: * @param symbol The symbol.
0716: * @return <code>true</code> if the specified symbol is multiply
0717: * defined.
0718: */
0719: public boolean isDefinedMultiply(String symbol) {
0720: Scope scope = lookupScope(symbol);
0721: if ((null == scope) || (null == scope.symbols)) {
0722: return false;
0723: } else {
0724: return scope.symbols.get(Utilities.unqualify(symbol)) instanceof List;
0725: }
0726: }
0727:
0728: /**
0729: * Get the scope for the specified symbol. If the symbol is
0730: * qualified, this method returns the named scope (without checking
0731: * whether the symbol is defined in that scope). Otherwise, it
0732: * searches the current scope and all its ancestors, returning the
0733: * first defining scope.
0734: *
0735: * @param symbol The symbol.
0736: * @return The corresponding scope or <code>null</code> if no such
0737: * scope exits.
0738: */
0739: public Scope lookupScope(String symbol) {
0740: if (Utilities.isQualified(symbol)) {
0741: return getScope(Utilities.getQualifier(symbol));
0742:
0743: } else {
0744: return current.lookupScope(symbol);
0745: }
0746: }
0747:
0748: /**
0749: * Get the value for the specified symbol. If the symbol is
0750: * qualified, this method returns the definition within the named
0751: * scope. Otherwise, it searches the current scope and all its
0752: * ancestors, returning the value of the first definition.
0753: *
0754: * @param symbol The symbol.
0755: * @return The corresponding value or <code>null</code> if no such
0756: * definition exists.
0757: */
0758: public Object lookup(String symbol) {
0759: Scope scope = lookupScope(symbol);
0760: if ((null == scope) || (null == scope.symbols)) {
0761: return null;
0762: } else {
0763: return scope.symbols.get(Utilities.unqualify(symbol));
0764: }
0765: }
0766:
0767: /**
0768: * Enter the scope with the specified unqualified name. If the
0769: * current scope does not have a scope with the specified name, a
0770: * new scope with the specified name is created. In either case,
0771: * the scope with that name becomes the current scope.
0772: *
0773: * @param name The unqualified name.
0774: */
0775: public void enter(String name) {
0776: Scope parent = current;
0777: Scope child = parent.getNested(name);
0778: if (null == child) {
0779: child = new Scope(name, parent);
0780: }
0781: current = child;
0782: }
0783:
0784: /**
0785: * Exit the current scope.
0786: *
0787: * @throws IllegalStateException
0788: * Signals that the current scope is the root scope.
0789: */
0790: public void exit() {
0791: if (null == current.parent) {
0792: throw new IllegalStateException("Unable to exit root scope");
0793: }
0794: current = current.parent;
0795: }
0796:
0797: /**
0798: * Delete the scope with the specified unqualified name. If the
0799: * current scope contains a nested scope with the specified name,
0800: * this method deletes that scope and <em>all its contents</em>,
0801: * including nested scopes.
0802: *
0803: * @param name The unqualified name.
0804: */
0805: public void delete(String name) {
0806: if (null != current.scopes) {
0807: current.scopes.remove(name);
0808: }
0809: }
0810:
0811: /**
0812: * Determine whether the specified node has an associated {@link
0813: * Constants#SCOPE scope}.
0814: *
0815: * @param n The node.
0816: * @return <code>true</code> if the node has an associated scope.
0817: */
0818: public static boolean hasScope(Node n) {
0819: return n.hasProperty(Constants.SCOPE);
0820: }
0821:
0822: /**
0823: * Mark the specified node. If the node does not have an associated
0824: * {@link Constants#SCOPE scope}, this method set the property with
0825: * the current scope's qualified name.
0826: *
0827: * @param n The node.
0828: */
0829: public void mark(Node n) {
0830: if (!n.hasProperty(Constants.SCOPE)) {
0831: n.setProperty(Constants.SCOPE, current.getQualifiedName());
0832: }
0833: }
0834:
0835: /**
0836: * Enter the specified node. If the node has an associated {@link
0837: * Constants#SCOPE scope}, this method tries to enter the scope.
0838: * Otherwise, it does not change the scope.
0839: *
0840: * @param n The node.
0841: * @throws IllegalStateException Signals that the node's scope is
0842: * invalid or not nested within the current scope.
0843: */
0844: public void enter(Node n) {
0845: if (n.hasProperty(Constants.SCOPE)) {
0846: String name = n.getStringProperty(Constants.SCOPE);
0847: Scope scope = getScope(name);
0848:
0849: if (null == scope) {
0850: throw new IllegalStateException("Invalid scope " + name);
0851: } else if (scope.getParent() != current) {
0852: throw new IllegalStateException("Scope " + name
0853: + " not nested in "
0854: + current.getQualifiedName());
0855: }
0856:
0857: current = scope;
0858: }
0859: }
0860:
0861: /**
0862: * Exit the specified node. If the node has an associated {@link
0863: * Constants#SCOPE scope}, the current scope is exited.
0864: *
0865: * @param n The node.
0866: */
0867: public void exit(Node n) {
0868: if (n.hasProperty(Constants.SCOPE)) {
0869: exit();
0870: }
0871: }
0872:
0873: /**
0874: * Create a fresh name. The returned name has
0875: * "<code>anonymous</code>" as it base name.
0876: *
0877: * @see #freshName(String)
0878: *
0879: * @return A fresh name.
0880: */
0881: public String freshName() {
0882: return freshName("anonymous");
0883: }
0884:
0885: /**
0886: * Create a fresh name incorporating the specified base name. The
0887: * returned name is of the form
0888: * <code><i>name</i>(<i>count</i>)</code>.
0889: *
0890: * @param base The base name.
0891: * @return The corresponding fresh name.
0892: */
0893: public String freshName(String base) {
0894: StringBuilder buf = new StringBuilder();
0895: buf.append(base);
0896: buf.append(Constants.START_OPAQUE);
0897: buf.append(freshNameCount++);
0898: buf.append(Constants.END_OPAQUE);
0899: return buf.toString();
0900: }
0901:
0902: /**
0903: * Create a fresh C identifier. The returned identifier has
0904: * "<code>tmp</code>" as its base name.
0905: *
0906: * @see #freshCId(String)
0907: *
0908: * @return A fresh C identifier.
0909: */
0910: public String freshCId() {
0911: return freshCId("tmp");
0912: }
0913:
0914: /**
0915: * Create a fresh C identifier incorporating the specified base
0916: * name. The returned name is of the form
0917: * <code>__<i>name</i>_<i>count</i></code>.
0918: *
0919: * @param base The base name.
0920: * @return The corresponding fresh C identifier.
0921: */
0922: public String freshCId(String base) {
0923: StringBuilder buf = new StringBuilder();
0924: buf.append("__");
0925: buf.append(base);
0926: buf.append('_');
0927: buf.append(freshIdCount++);
0928: return buf.toString();
0929: }
0930:
0931: /**
0932: * Create a fresh Java identifier. The returned identifier has
0933: * "<code>tmp</code>" as its base name.
0934: *
0935: * @see #freshJavaId(String)
0936: *
0937: * @return A fresh Java identifier.
0938: */
0939: public String freshJavaId() {
0940: return freshJavaId("tmp");
0941: }
0942:
0943: /**
0944: * Create a fresh Java identifier incorporating the specified base
0945: * name. The returned name is of the form
0946: * <code><i>name</i>$<i>count</i></code>.
0947: *
0948: * @param base The base name.
0949: * @return The corresponding fresh Java identifier.
0950: */
0951: public String freshJavaId(String base) {
0952: StringBuilder buf = new StringBuilder();
0953: buf.append(base);
0954: buf.append('$');
0955: buf.append(freshIdCount++);
0956: return buf.toString();
0957: }
0958:
0959: /**
0960: * Convert the specified unqualified symbol to a symbol in the
0961: * specified name space.
0962: *
0963: * @param symbol The symbol
0964: * @param space The name space.
0965: * @return The mangled symbol.
0966: */
0967: public static String toNameSpace(String symbol, String space) {
0968: return space + Constants.START_OPAQUE + symbol
0969: + Constants.END_OPAQUE;
0970: }
0971:
0972: /**
0973: * Determine whether the specified symbol is in the specified name
0974: * space.
0975: *
0976: * @param symbol The symbol.
0977: * @param space The name space.
0978: * @return <code>true</code> if the symbol is mangled symbol in the
0979: * name space.
0980: */
0981: public static boolean isInNameSpace(String symbol, String space) {
0982: try {
0983: return (symbol.startsWith(space)
0984: && (Constants.START_OPAQUE == symbol.charAt(space
0985: .length())) && symbol.endsWith(END_OPAQUE));
0986: } catch (IndexOutOfBoundsException x) {
0987: return false;
0988: }
0989: }
0990:
0991: /** The end of opaqueness marker as a string. */
0992: private static final String END_OPAQUE = Character
0993: .toString(Constants.END_OPAQUE);
0994:
0995: /**
0996: * Convert the specified unqualified symbol within a name space to a
0997: * symbol without a name space.
0998: *
0999: * @param symbol The mangled symbol within a name space.
1000: * @return The corresponding symbol without a name space.
1001: */
1002: public static String fromNameSpace(String symbol) {
1003: int start = symbol.indexOf(Constants.START_OPAQUE);
1004: int end = symbol.length() - 1;
1005: if ((0 < start) && (Constants.END_OPAQUE == symbol.charAt(end))) {
1006: return symbol.substring(start + 1, end);
1007: } else {
1008: throw new IllegalArgumentException("Not a mangled symbol '"
1009: + symbol + "'");
1010: }
1011: }
1012:
1013: /**
1014: * Conver the specified C macro identifier into a symbol table scope
1015: * name.
1016: *
1017: * @param id The macro identifier.
1018: * @return The corresponding symbol table scope name.
1019: */
1020: public static String toMacroScopeName(String id) {
1021: return toNameSpace(id, "macro");
1022: }
1023:
1024: /**
1025: * Determine whether the specified scope name represents a macro's
1026: * scope.
1027: *
1028: * @param name The name.
1029: * @return <code>true</code> if the name denotes a macro scope.
1030: */
1031: public static boolean isMacroScopeName(String name) {
1032: return isInNameSpace(name, "macro");
1033: }
1034:
1035: /**
1036: * Convert the specified C function identifier into a symbol table
1037: * scope name.
1038: *
1039: * @param id The function identifier.
1040: * @return The corresponding symbol table scope name.
1041: */
1042: public static String toFunctionScopeName(String id) {
1043: return toNameSpace(id, "function");
1044: }
1045:
1046: /**
1047: * Determine whether the specified scope name represents a
1048: * function's scope.
1049: *
1050: * @param name The name.
1051: * @return <code>true</code> if the name denotes a function scope.
1052: */
1053: public static boolean isFunctionScopeName(String name) {
1054: return isInNameSpace(name, "function");
1055: }
1056:
1057: /**
1058: * Convert the specified C struct, union, or enum tag into a symbol
1059: * table name.
1060: *
1061: * @param tag The tag.
1062: * @return The corresponding symbol table name.
1063: */
1064: public static String toTagName(String tag) {
1065: return toNameSpace(tag, "tag");
1066: }
1067:
1068: /**
1069: * Convert the specified label identifier into a symbol table name.
1070: *
1071: * @param id The identifier.
1072: * @return The corresponding symbol table name.
1073: */
1074: public static String toLabelName(String id) {
1075: return toNameSpace(id, "label");
1076: }
1077:
1078: /**
1079: * Convert the specified method identifier into a symbol table name.
1080: *
1081: * @param id The method identifier.
1082: * @return The corresponding symbol table name.
1083: */
1084: public static String toMethodName(String id) {
1085: return toNameSpace(id, "method");
1086: }
1087:
1088: }
|