/* * xtc - The eXTensible Compiler * Copyright (C) 2006-2007 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.io.IOException; import java.math.BigInteger; /** * Representation of a reference. A reference represents a specific * region of memory. It has a type, which determines the layout of * the referenced memory region. Furthermore, it may optionally have * a base and an offset, indicating a memory region relative to * another. The optional base is a reference, and the optional offset * is either an index or a field. * * @author Robert Grimm * @version $Revision: 1.13 $ */ public abstract class Reference { /** The type. */ protected final Type type; /** * Create a new reference. Note that this constructor resolves the * specified type and strips any arrays. * * @param type The type. */ public Reference(Type type) { type = type.resolve(); while (type.isArray()) { type = type.toArray().getType().resolve(); } this.type = type; } /** * Get this reference's type. * * @return The type. */ public Type getType() { return type; } /** * Determine whether this reference is a null reference. Note that * a reference other than {@link NullReference#NULL} may still * return <code>true</code>; notably, an index reference with a null * base and a zero offset still is null. * * @see NullReference * * @return <code>true</code> if this reference is a null * reference. */ public boolean isNull() { return false; } /** * Determine whether this reference is a string reference. * * @see StringReference * * @return <code>true</code> if this reference is a string * reference. */ public boolean isString() { return false; } /** * Determine whether this reference is a variable reference. * * @see VariableReference * * @return <code>true</code> if this reference is a variable * reference. */ public boolean isVariable() { return false; } /** * Determine whether this reference is a static reference. * * @see StaticReference * * @return <code>true</code> if this reference is a static * reference. */ public boolean isStatic() { return false; } /** * Determine whether this reference is a dynamic reference. * * @see DynamicReference * * @return <code>true</code> if this reference is a dynamic * reference. */ public boolean isDynamic() { return false; } /** * Determine whether this reference represents a prefix operator in * C syntax. * * @return <code>true</code> if this reference represents a * prefix operator. */ public boolean isPrefix() { return false; } /** * Determine whether this reference is a cast reference. * * @return <code>true</code> if this reference is a cast * reference. */ public boolean isCast() { return false; } /** * Determine whether this reference is an indirect reference. * * @return <code>true</code> if this reference is an indirect * reference. */ public boolean isIndirect() { return false; } /** * Determine whether this reference has a base. * * @see RelativeReference * * @return <code>true</code> if this reference has a base. */ public boolean hasBase() { return false; } /** * Get this reference's base. * * @see RelativeReference * * @return The base. * @throws IllegalStateException Signals that this reference does * not have a base. */ public Reference getBase() { throw new IllegalStateException("not a relative reference"); } /** * Determine whether this reference has an index. * * @see IndexReference * * @return <code>true</code> if this reference has an index. */ public boolean hasIndex() { return false; } /** * Get this reference's index. * * @return The index. * @throws IllegalStateException Signals that this reference does * not have an index. */ public BigInteger getIndex() { throw new IllegalStateException("not an index reference"); } /** * Determine whether this reference has a field. * * @see FieldReference * * @return <code>true</code> if this reference has a field. */ public boolean hasField() { return false; } /** * Get this reference's field. * * @return The field. * @throws IllegalStateException Signals that this reference does * not have a field. */ public String getField() { throw new IllegalStateException("not a field reference"); } /** * Determine whether this reference has an absolute memory location. * * @return <code>true</code> if this reference has an absolute * memory location. */ public boolean hasLocation() { return false; } /** * Get this reference's absolute memory location. * * @param ops The C operations. * @return The memory location. * @throws IllegalStateException Signals that this reference does * not have an absolute memory location. */ public BigInteger getLocation(C ops) { throw new IllegalStateException(); } /** * Determine whether this reference represents a compile-time * constant memory location. * * @return <code>true</code> if this reference represents a * compile-time memory location. */ public boolean isConstant() { return false; } /** * Indirect this reference. This method determines the appropriate * reference when using a pointer-decayed type. For arrays and * functions, it simly returns this reference. For all other types, * it returns an indirect reference, with this reference as the * base. * * @param type The reference's declared type (before pointer decay). */ public Reference indirect(Type type) { Type resolved = type.resolve(); if (resolved.isArray() || resolved.isFunction()) { return this; } else { return new IndirectReference(this); } } /** * Add the specified value to this reference. * * @param val The value. * @return A reference with an index increased by <code>val</code>. */ public Reference add(long val) { if (0 == val) { return this; } else { return add(BigInteger.valueOf(val)); } } /** * Add the specified value to this reference. * * @param val The value. * @return A reference with an index increased by <code>val</code>. */ public Reference add(BigInteger val) { if (val.signum() == 0) { return this; } else { return new IndexReference(this, val); } } /** * Subtract the specified value from this reference. * * @param val The value. * @return A reference with an index decreased by <code>val</code>. */ public Reference subtract(long val) { if (0 == val) { return this; } else { return subtract(BigInteger.valueOf(val)); } } /** * Subtract the specified value from this reference. * * @param val The value. * @return A reference with an index decreased by <code>val</code>. */ public Reference subtract(BigInteger val) { if(val.signum() == 0) { return this; } else { return new IndexReference(this, val.negate()); } } /** * Determine the difference between the two references. * * @param ref The other reference. * @return The difference or <code>null</code> if the difference * cannot be statically determined. */ public BigInteger difference(Reference ref) { if (hasIndex() && ref.hasIndex()) { IndexReference r1 = (IndexReference)this; IndexReference r2 = (IndexReference)ref; if (r1.base.equals(r2.base)) { return r1.index.subtract(r2.index); } } else if (hasIndex()) { IndexReference r1 = (IndexReference)this; if (r1.base.equals(ref)) { return r1.index; } } else if (ref.hasIndex()) { IndexReference r2 = (IndexReference)ref; if (this.equals(r2.base)) { return r2.index.negate(); } } return null; } /** * Write a human readable representation to the specified * appendable. * * @param out The appendable. * @throws IOException Signals an I/O error. */ public abstract void write(Appendable out) throws IOException; public String toString() { StringBuilder buf = new StringBuilder(); try { write(buf); } catch (IOException x) { assert false; } return buf.toString(); } }